it-swarm.com.de

Verschachtelte oder innere Klasse in PHP

Ich baue eine User Class für meine neue Website, diesmal habe ich mir aber vorgenommen, sie etwas anders zu bauen ...

Ich weiß, dass C++ , Java und Even Ruby (und wahrscheinlich andere Programmiersprachen) verschachtelte/innere Klassen innerhalb der Hauptklasse zulassen, wodurch der Code objektorientierter und organisierter gestaltet werden kann.

In PHP möchte ich so etwas machen:

<?php
    public class User {
        public $userid;
        public $username;
        private $password;

        public class UserProfile {
            // Some code here
        }

        private class UserHistory {
            // Some code here
        }
    }
?>

Ist das in PHP möglich? Wie kann ich das erreichen?


UPDATE

Wenn dies nicht möglich ist, werden zukünftige PHP -Versionen möglicherweise geschachtelte Klassen unterstützen?

87
Lior Elrom

Intro:

Verschachtelte Klassen beziehen sich auf andere Klassen ein wenig anders als äußere Klassen. Am Beispiel von Java:

Nicht statische verschachtelte Klassen haben Zugriff auf andere Mitglieder der umgebenden Klasse, auch wenn sie als privat deklariert sind. Für nicht statische verschachtelte Klassen muss auch eine Instanz der übergeordneten Klasse instanziiert werden.

OuterClass outerObj = new OuterClass(arguments);
outerObj.InnerClass innerObj = outerObj.new InnerClass(arguments);

Es gibt mehrere zwingende Gründe für ihre Verwendung:

  • Es ist eine Möglichkeit, Klassen logisch zu gruppieren, die nur an einer Stelle verwendet werden. 

Wenn eine Klasse nur für eine andere Klasse nützlich ist, ist sie für .__ logisch. beziehen und in diese Klasse einbetten und die beiden zusammenhalten.

  • Es erhöht die Einkapselung.

Betrachten Sie zwei Klassen der obersten Ebene, A und B, bei denen B Zugriff auf .__ benötigt. Mitglieder von A, die sonst als privat deklariert würden. Durch Verstecken der Klasse Innerhalb der Klasse A können die Mitglieder von A als privat deklariert werden und B kann auf .__ zugreifen. Sie. Außerdem kann B selbst vor der Außenwelt verborgen werden.

  • Verschachtelte Klassen können zu lesbarerem und wartungsfähigerem Code führen.

Eine verschachtelte Klasse bezieht sich normalerweise auf ihre übergeordnete Klasse und bildet zusammen ein "Paket".

In PHP

Sie können ähnliches Verhalten in PHP ohne verschachtelte Klassen haben. 

Wenn Sie lediglich eine Struktur/Organisation erreichen möchten, können PHP -Namensräume als Package.OuterClass.InnerClass ausreichen. Sie können sogar mehr als einen Namespace in derselben Datei deklarieren (dies ist jedoch aufgrund der Standardfunktionen für das automatische Laden möglicherweise nicht ratsam).

namespace;
class OuterClass {}

namespace OuterClass;
class InnerClass {}

Wenn Sie andere Merkmale wie die Sichtbarkeit der Mitglieder nachahmen möchten, ist dies ein wenig mehr Aufwand.

Definieren der Klasse "Paket"

namespace {

    class Package {

        /* protect constructor so that objects can't be instantiated from outside
         * Since all classes inherit from Package class, they can instantiate eachother
         * simulating protected InnerClasses
         */
        protected function __construct() {}

        /* This magic method is called everytime an inaccessible method is called 
         * (either by visibility contrains or it doesn't exist)
         * Here we are simulating shared protected methods across "package" classes
         * This method is inherited by all child classes of Package 
         */
        public function __call($method, $args) {

            //class name
            $class = get_class($this);

            /* we check if a method exists, if not we throw an exception 
             * similar to the default error
             */
            if (method_exists($this, $method)) {

                /* The method exists so now we want to know if the 
                 * caller is a child of our Package class. If not we throw an exception
                 * Note: This is a kind of a dirty way of finding out who's
                 * calling the method by using debug_backtrace and reflection 
                 */
                $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 3);
                if (isset($trace[2])) {
                    $ref = new ReflectionClass($trace[2]['class']);
                    if ($ref->isSubclassOf(__CLASS__)) {
                        return $this->$method($args);
                    }
                }
                throw new \Exception("Call to private method $class::$method()");
            } else {
                throw new \Exception("Call to undefined method $class::$method()");
            }
        }
    }
}

Anwendungsfall

namespace Package {
    class MyParent extends \Package {
        public $publicChild;
        protected $protectedChild;

        public function __construct() {
            //instantiate public child inside parent
            $this->publicChild = new \Package\MyParent\PublicChild();
            //instantiate protected child inside parent
            $this->protectedChild = new \Package\MyParent\ProtectedChild();
        }

        public function test() {
            echo "Call from parent -> ";
            $this->publicChild->protectedMethod();
            $this->protectedChild->protectedMethod();

            echo "<br>Siblings<br>";
            $this->publicChild->callSibling($this->protectedChild);
        }
    }
}

namespace Package\MyParent
{
    class PublicChild extends \Package {
        //Makes the constructor public, hence callable from outside 
        public function __construct() {}
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
    class ProtectedChild extends \Package { 
        protected function protectedMethod() {
            echo "I'm ".get_class($this)." protected method<br>";
        }

        protected function callSibling($sibling) {
            echo "Call from " . get_class($this) . " -> ";
            $sibling->protectedMethod();
        }
    }
}

Testen

$parent = new Package\MyParent();
$parent->test();
$pubChild = new Package\MyParent\PublicChild();//create new public child (possible)
$protChild = new Package\MyParent\ProtectedChild(); //create new protected child (ERROR)

Ausgabe:

Call from parent -> I'm Package protected method
I'm Package protected method

Siblings
Call from Package -> I'm Package protected method
Fatal error: Call to protected Package::__construct() from invalid context

HINWEIS:

Ich glaube wirklich nicht, dass es eine gute Idee ist, innerClasses in PHP zu emulieren. Ich denke, der Code ist weniger sauber und lesbar. Es gibt wahrscheinlich auch andere Möglichkeiten, ähnliche Ergebnisse mit einem gut etablierten Muster wie dem Observer, Decorator oder COmposition Pattern zu erzielen. Manchmal reicht schon eine einfache Vererbung aus.

121
Tivie

Echte verschachtelte Klassen mit public/protected/private-Zugänglichkeit wurden 2013 für PHP 5.6 als RFC vorgeschlagen, jedoch nicht erstellt (Noch keine Abstimmung, kein Update seit 2013 - Stand vom 29.12.2016):

https://wiki.php.net/rfc/nested_classes

class foo {
    public class bar {

    }
}

Zumindest haben anonyme Klassen es in PHP 7 geschafft

https://wiki.php.net/rfc/anonymous_classes

Von dieser RFC-Seite:

Zukünftiger Umfang

Die durch diesen Patch vorgenommenen Änderungen bedeuten, dass benannte verschachtelte Klassen (um ein kleines bisschen) einfacher implementiert werden können.

In einer zukünftigen Version erhalten wir möglicherweise verschachtelte Klassen, aber es ist noch nicht entschieden.

19

Sie können dies in PHP nicht tun. Es gibt jedoch funktionale Möglichkeiten, um dies zu erreichen.

Weitere Details finden Sie in diesem Beitrag: Wie mache ich eine PHP verschachtelte Klasse oder verschachtelte Methoden?

Diese Art der Implementierung wird als flüssige Schnittstelle bezeichnet: http://en.wikipedia.org/wiki/Fluent_interface

12
Sumoanand

Seit PHP Version 5.4 können Sie das Erstellen von Objekten mit privatem Konstruktor durch Reflektion erzwingen. Es kann verwendet werden, um geschachtelte Java-Klassen zu simulieren. Beispielcode:

class OuterClass {
  private $name;

  public function __construct($name) {
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function forkInnerObject($name) {
    $class = new ReflectionClass('InnerClass');
    $constructor = $class->getConstructor();
    $constructor->setAccessible(true);
    $innerObject = $class->newInstanceWithoutConstructor(); // This method appeared in PHP 5.4
    $constructor->invoke($innerObject, $this, $name);
    return $innerObject;
  }
}

class InnerClass {
  private $parentObject;
  private $name;

  private function __construct(OuterClass $parentObject, $name) {
    $this->parentObject = $parentObject;
    $this->name = $name;
  }

  public function getName() {
    return $this->name;
  }

  public function getParent() {
    return $this->parentObject;
  }
}

$outerObject = new OuterClass('This is an outer object');
//$innerObject = new InnerClass($outerObject, 'You cannot do it');
$innerObject = $outerObject->forkInnerObject('This is an inner object');
echo $innerObject->getName() . "\n";
echo $innerObject->getParent()->getName() . "\n";
4
Pascal9x

In PHP ist das nicht möglich. PHP unterstützt "include", aber Sie können dies nicht einmal innerhalb einer Klassendefinition tun. Nicht viele tolle Möglichkeiten hier.

Dies beantwortet Ihre Frage nicht direkt, aber Sie könnten an "Namespaces" interessiert sein, einem furchtbar hässlichen\Syntax\hacked\auf\top\von PHP OOP: http: // www. php.net/manual/de/language.namespaces.rationale.php

3
dkamins

Laut Xenons Kommentar zu Anıl Özselgins Antwort wurden anonyme Klassen in PHP 7.0 implementiert, das so nahe an verschachtelten Klassen liegt, wie Sie es jetzt gerade bekommen. Hier sind die relevanten RFCs:

Verschachtelte Klassen (Status: zurückgezogen)

Anonyme Klassen (Status: implementiert in PHP 7.0)

Ein Beispiel für den ursprünglichen Beitrag: So würde Ihr Code aussehen:

<?php
    public class User {
        public $userid;
        public $username;
        private $password;

        public $profile;
        public $history;

        public function __construct() {
            $this->profile = new class {
                // Some code here for user profile
            }

            $this->history = new class {
                // Some code here for user history
            }
        }
    }
?>

Dies ist jedoch mit einem sehr unangenehmen Vorbehalt verbunden. Wenn Sie eine IDE wie PHPStorm oder NetBeans verwenden, und dann eine solche Methode der User-Klasse hinzufügen:

public function foo() {
  $this->profile->...
}

... tschüss tschüs autovervollständigung. Dies ist auch der Fall, wenn Sie mit einem Muster wie dem folgenden in Schnittstellen schreiben (das I in SOLID):

<?php
    public class User {
        public $profile;

        public function __construct() {
            $this->profile = new class implements UserProfileInterface {
                // Some code here for user profile
            }
        }
    }
?>

Wenn Ihre einzigen Aufrufe von $this->profile nicht von der __construct()-Methode stammen (oder in welcher Methode $this->profile definiert ist), erhalten Sie keine Art von Typhinweisen. Ihre Eigenschaft ist im Wesentlichen für Ihre IDE "verborgen". Das macht das Leben sehr schwer, wenn Sie sich auf Ihre IDE für die automatische Vervollständigung, das Schnüffeln von Code-Gerüchen und das Refactoring verlassen.

2
e_i_pi

Ich denke, ich habe eine elegante Lösung für dieses Problem geschrieben, indem ich Namespaces verwendet. In meinem Fall muss die innere Klasse ihre übergeordnete Klasse nicht kennen (wie die statische innere Klasse in Java). Als Beispiel habe ich eine Klasse namens 'User' und eine Unterklasse namens 'Type' erstellt, die in meinem Beispiel als Referenz für die Benutzertypen (ADMIN, OTHERS) verwendet wird. Grüße.

User.php (Benutzerklassendatei)

<?php
namespace
{   
    class User
    {
        private $type;

        public function getType(){ return $this->type;}
        public function setType($type){ $this->type = $type;}
    }
}

namespace User
{
    class Type
    {
        const ADMIN = 0;
        const OTHERS = 1;
    }
}
?>

Using.php (Ein Beispiel für das Aufrufen der 'Unterklasse')

<?php
    require_once("User.php");

    //calling a subclass reference:
    echo "Value of user type Admin: ".User\Type::ADMIN;
?>
1
Rogerio Souza

Es wartet auf die Abstimmung als RFC https://wiki.php.net/rfc/anonymous_classes

1
Anıl Özselgin

Sie können wie folgt:

class User{
  public $id;
  public $name;
  public $password;
  public $Profile;
  public $History;  /*  (optional declaration, if it isn't public)  */
  public function __construct($id,$name,$password){
    $this->id=$id;
    $this->name=$name;
    $this->name=$name;
    $this->Profile=(object)[
        get=>function(){
          return 'Name: '.$this->name.''.(($this->History->get)());
        }
      ];
    $this->History=(object)[
        get=>function(){
          return ' History: '.(($this->History->track)());
        }
        ,track=>function(){
          return (lcg_value()>0.5?'good':'bad');
        }
      ];
  }
}
echo ((new User(0,'Lior','nyh'))->Profile->get)();
0
Arlon Arriola