Klaus: Verständnisproblem namespaces

Hey! In PHP gibt es ja mittlerweile auch namespaces nur leider komme ich damit noch nicht so richtig zurecht.

Nehmen wir mal folgende Verzeichnis-Struktur an.

Projekt
   |
   |-> Module
      |
      |-> foo
         |
         |-> constants
            |
            |-> constants.php
         |
         |-> foo.php
      |
      |-> bar
         |
         |-> constants
            |
            |-> constants.php
         |
         |-> bar.php
   |
   |-> autoload.php
   |-> index.php

Ist es richtig, dass foo.php im namespace foo landet, bar.php in bar und index.php in Projekt? Oder sollten foo und bar im namespace "Module" sein"?
Und wenn index.php im namespace "Projekt" ist, wie erkläre ich meinem autoloader denn wo er z.B. foo findet? Und wenn ich von foo aus eine Instanz von bar erstellen will?

Puh, das ist für mich ganz schön harter Tobak und mir leuchtet das alles noch nicht so richtig ein. Auch nicht so wirklich der Sinn dahinter...

Kann mir da bitte jemand ein bisschen helfen?

  1. Tach!

    In PHP gibt es ja mittlerweile auch namespaces [...].
    Nehmen wir mal folgende Verzeichnis-Struktur an.

    Beides hat nichts miteinander zu tun. Man bildet jedoch gern eine Namespace-Struktur auf einer Verzeichnissstruktur ab, damit man eine logische Ordung auch physisch vorliegen hat. Das hilft, sich in diesem komplexen Gebilde zurechtzufinden.

    Projekt
       |-> Module
          |-> foo
             |-> constants
                |-> constants.php
             |-> foo.php
          |-> bar
             |-> constants
                |-> constants.php
             |-> bar.php
       |-> autoload.php
       |-> index.php

    Ist es richtig, dass foo.php im namespace foo landet, bar.php in bar und index.php in Projekt? Oder sollten foo und bar im namespace "Module" sein"?

    Unter der Voraussetzung, dass jeder Namespace-Teil einem Verzeichnisnamen entspricht, würden sich Elemente in foo.php sich im Namespace \Projekt\Module\foo befinden, Elemente in bar.php sind in \Projekt\Module\bar und Dinge in index.php unter \Projekt. Aber - und dass wäre die Antwort auf die zweite Frage - du kannst dir das selbst so einrichten, wie du das gern hättest.

    Und wenn index.php im namespace "Projekt" ist, wie erkläre ich meinem autoloader denn wo er z.B. foo findet? Und wenn ich von foo aus eine Instanz von bar erstellen will?

    Das ist dein Problem. Namespaces in einer Verzeichnisstruktur nachzubilden, hilft jedenfalls beim Finden von Dateien, wenn man das 1:1 übersetzen kann. Ansonsten braucht es da einen anderen Zuordnungsmechanismus.

    dedlfix.

    1. Hey dedlfix danke für deine Hilfe!

      Beides hat nichts miteinander zu tun. Man bildet jedoch gern eine Namespace-Struktur auf einer Verzeichnissstruktur ab, damit man eine logische Ordung auch physisch vorliegen hat. Das hilft, sich in diesem komplexen Gebilde zurechtzufinden.

      Ah ok ich verstehe. Ich habe also versucht das Pferd von hinten aufzuzäumen.

      Unter der Voraussetzung, dass jeder Namespace-Teil einem Verzeichnisnamen entspricht, würden sich Elemente in foo.php sich im Namespace \Projekt\Module\foo befinden, Elemente in bar.php sind in \Projekt\Module\bar und Dinge in index.php unter \Projekt. Aber - und dass wäre die Antwort auf die zweite Frage - du kannst dir das selbst so einrichten, wie du das gern hättest.

      Aber wie wäre es denn am sinnvollsten? Meine Überlegung war, dass in dem Fall jedes Modul eine Datei namens "constants.php" hat und ich denen gern den namespace des jeweiligen Moduls geben wollte damit sich die Konstanten nicht "beissen" falls sie gleiche Namen haben.

      Das ist dein Problem. Namespaces in einer Verzeichnisstruktur nachzubilden, hilft jedenfalls beim Finden von Dateien, wenn man das 1:1 übersetzen kann. Ansonsten braucht es da einen anderen Zuordnungsmechanismus.

      Du meinst also im Idealfall tauscht mein autoloader nur die "" gegen "/", hängt ein ".php" an und findet so die jeweilige Klasse? Oder etwas komplexer die Zeichen tauschen und das jeweilige Verzeichnis inkl. Unterverzeichnisse nach einer Datei mit dem Klassennamen durchsuchen?

      Und was wäre in dem Fall der Nutzen von "use"? Mir leuchtet nicht ganz ein was es für einen Sinn ergibt wenn ich "use" nach "namespace" benutze. Auch das ist mir noch nicht so ganz klar.

      1. Moin!

        Und was wäre in dem Fall der Nutzen von "use"? Mir leuchtet nicht ganz ein was es für einen Sinn ergibt wenn ich "use" nach "namespace" benutze. Auch das ist mir noch nicht so ganz klar.

        Damit importierst du eine Klasse eines anderen Namespaces in deinen aktuellen, kannst dabei auch den Zielnamen verändern (Alias-Namen), damit er sich nicht beißt, und kannst die Klasse dann mit diesem kurzen Bezeichner benutzen, anstatt immer den kompletten Namespace anzugeben.

        - Sven Rautenberg

      2. Tach!

        Aber wie wäre es denn am sinnvollsten? Meine Überlegung war, dass in dem Fall jedes Modul eine Datei namens "constants.php" hat und ich denen gern den namespace des jeweiligen Moduls geben wollte damit sich die Konstanten nicht "beissen" falls sie gleiche Namen haben.

        Du kannst das machen, wie du das selbst für sinnvoll erachtest.

        Du meinst also im Idealfall tauscht mein autoloader nur die "" gegen "/", hängt ein ".php" an und findet so die jeweilige Klasse?

        Das wäre die einfachste Vorgehensweise nach einer simplen Regel ohne Ausnahmen.

        dedlfix.

        1. Sorry wenn ich irgendwo nicht ganz mitkomme aber wo ist der praktische Nutzen der namespaces wenn ich damit eh machen kann was ich will?

          1. Tach!

            Sorry wenn ich irgendwo nicht ganz mitkomme aber wo ist der praktische Nutzen der namespaces wenn ich damit eh machen kann was ich will?

            Ich versteh die Frage nicht. Auch mit PHP kannst du machen, was du willst. PHP schreibt nicht vor, welchen praktichen Nutzen du damit haben sollst. Den musst du selbst finden. Warum es Namespaces gibt, ist in der Einleitung des Kapitels im PHP-Handbuch angeführt.

            dedlfix.

            1. Ich versteh die Frage nicht.

              Und ich verstehe die namespaces nicht^^

              Nehmen wir mal folgende index.php

              <?php  
              namespace Projekt;  
              use Projekt\Module\foo;  
              require_once('autoload.php');  
              $foo = new foo(); // Projekt\Module\foo  
              $bar = new bar(); // Projekt\bar  
              ?>
              

              autoload.php

              <?php  
              function __autoload($strClassName){  
                  echo $strClassName;  
              }  
              ?>
              

              foo.php

              <?php  
              namespace Projekt\Module\foo;  
              class foo{  
                  public function run(){ }  
              }  
              ?>
              

              bar.php

              <?php  
              namespace Projekt\Module\foo;  
              class bar{  
                  public function run(){ }  
              }  
              ?>
              

              Wieso erhalte ich da völlig verschiedene Angaben?
              Mir ist überhaupt nicht klar wie ich das nutzen kann, dass sich gleiche Klassennamen nicht beissen. Wenn ich 2 identische Dateien/Klassen in die Ordner Projekt\Module\foo und Projekt\Module\bar kopiere, wie sprech ich die denn überhaupt an? Beim autoloader kommt offenbar nur noch der namespace und nicht mehr der Klassenname an. Ich verstehe es nicht :(

              1. Moin!

                autoload.php

                <?php

                function __autoload($strClassName){
                    echo $strClassName;
                }
                ?>

                  
                Dein Autoloader wird verhindern, dass der obere Code funktioniert, weil er die Klasse nicht lädt... Das nur am Rande.  
                  
                
                > foo.php  
                  
                Diese Klasse heißt in Langform: \Projekt\Module\foo\foo  
                  
                
                > ~~~php
                
                <?php  
                
                > namespace Projekt\Module\foo;  
                > class foo{  
                >     public function run(){ }  
                > }  
                > ?>
                
                

                bar.php

                Und diese heißt in Langform: \Projekt\Module\foo\bar

                <?php

                namespace Projekt\Module\foo;
                class bar{
                    public function run(){ }
                }
                ?>

                  
                Und mit diesen Definitionen schauen wir den Code an:  
                
                > ~~~php
                
                <?php  
                
                > namespace Projekt;  
                > use Projekt\Module\foo;  
                > require_once('autoload.php');  
                > $foo = new foo(); // Projekt\Module\foo  
                > $bar = new bar(); // Projekt\bar  
                > ?>
                
                

                Du gehst in den Namespace \Projekt - alle Klassen, die du jetzt benutzt, müssen entweder voll qualifiziert sein mit Backslash, oder relativ adressiert sein ausgehend von diesem Namespace, oder importiert sein mit "use", oder echt in diesem Namespace definiert werden.

                "use Projekt\Module\foo" -> ist eine Fehlerquelle. Du importierst den ganzen Namespace als Alias-Namespace "foo" in den aktuellen Namespace.

                Da deine Klasse \Projekt\Module\foo\foo heißt, kannst du sie nach diesem Use als "foo\foo" ansprechen, aber nicht als "foo". Denn der Alias vom "use" muss verwendet werden.

                Würdest du die Klasse direkt als "foo" ansprechen wollen, müsstest du sie mit einem "use \Projekt\Module\foo\foo" importieren. Dann importierst du aber die Klasse "bar" nicht mehr.

                Wieso erhalte ich da völlig verschiedene Angaben?

                Wäre schön, wenn du deine Ausgaben deutlich kennzeichnen würdest.

                Mir ist überhaupt nicht klar wie ich das nutzen kann, dass sich gleiche Klassennamen nicht beissen. Wenn ich 2 identische Dateien/Klassen in die Ordner Projekt\Module\foo und Projekt\Module\bar kopiere, wie sprech ich die denn überhaupt an? Beim autoloader kommt offenbar nur noch der namespace und nicht mehr der Klassenname an. Ich verstehe es nicht :(

                Der Autoloader kriegt immer den kompletten Namespace plus Klassennamen. Aber wenn du dein Use falsch benutzt und dann die falsche Klasse instanziierst, sieht es natürlich so aus, als würde der Autoloader nur den Namespace nutzen.

                Wenn du was testest, ist es immer eine ganz schlechte Idee, zwei unterschiedliche Dinge mit dem gleichen String zu versehen. Hier also Namespace-Namensteil "foo" und Klassenname "foo". Hättest du das mit "NSfoo" und "KlasseFoo" probiert, wäre es dir sofort aufgefallen.

                - Sven Rautenberg

                1. Hey Sven!

                  Danke für deine Ausführung! Ich habe es endlich begriffen!

                  Es gefällt mir aber überhaupt nicht, dass ich jetzt beim Instanzieren einer Klasse den namespace mit angeben muss. Es dürfte ziemlich viel Arbeit sein wenn ich versuchen würde ältere Projekte entsprechend anzupassen. Aber naja, das ist ja ein anderes Thema.

                  1. Moin!

                    Danke für deine Ausführung! Ich habe es endlich begriffen!

                    Es gefällt mir aber überhaupt nicht, dass ich jetzt beim Instanzieren einer Klasse den namespace mit angeben muss. Es dürfte ziemlich viel Arbeit sein wenn ich versuchen würde ältere Projekte entsprechend anzupassen. Aber naja, das ist ja ein anderes Thema.

                    Musst du ja nicht zwingend.

                    Die alte Form des "Namespacing" waren ellenlange Klassennamen, sowas wie "Zend_Form_Element_Checkbox". Das wäre in Namespaces "\Zend\Form\Element\Checkbox", also nichts anderes.

                    Aber bei Namespaces kannst du halt importieren: use \Zend\Form\Element\Checkbox as Box; Und ab dann geht new Box();.

                    Alte Projekte umstellen ist aber tatsächlich nicht sonderlich effizient. Muss man auch nicht machen, der Code funktioniert auch ohne Namespaces.

                    - Sven Rautenberg