Ankündigung

Einklappen
Keine Ankündigung bisher.

Composer PSR-4 Verständnisproblem

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • Composer PSR-4 Verständnisproblem

    Hallo,

    ich bin seit kurzem in meinen großen Projekt auf namespaces umgestiegen und muss dazu composer mit PSR-4 verwenden. Dementsprechend folge ich der Konvention pro Subnamespace einen gleichnamigen Ordner anzulegen.

    Die Ordnerstruktur sieht so aus:
    Code:
    <root-folder>
      app
        -> mvc
           -> controller
           -> model
           -> routes
           -> views
        -> src
      lib
        -> core
            -> Mvc
                -> Init
            -> Persistence
        -> modules
    [...]
    Ich möchte im Prinzip mit der use Anweisung einfach einen Namespace verwenden können, ohne jegliches weiteres manuelles Einbinden von PHP-Dateien. Composer soll hier auch Subnamespaces finden können. Meine autoload Sektion in der composer.json sieht bisher folgendermaßen aus:
    Code:
    "autoload":
                    {
                        "psr-4": { "Diana\\": "lib/core/" }
                    },
    Demnach soll <root-folder>/app/mvc/controller/IndexController.php folgendes finden können:
    PHP-Code:
    namespace App\Mvc\Controller
    {
        use 
    Diana\Core\Mvc\BaseController;
        
        class 
    IndexController extends BaseController
        
    {
            public function 
    __construct()
            {
                
    // ...
            
    }
        }
        

    Die autoload Datei von composer wird natürlich an zentraler Stelle eingebunden. Ich verstehe nur nicht, warum so mit dieser Zusammenstellung eine weiße Seite kommt.


    MFG derwunner


    Edit: Mit dem qualifizierten Aufruf klappt es. Wie kann ich aber solche Subnamespaces unqualifiziert aufrufen? Ich müsste sonst meinen kompletten Code ändern. Wenn ich die Klasse String aufrufen möchte, dann klappt das mit den normalen magischen autoload so:
    PHP-Code:
    new String('test'); 
    Mit dem autoload von composer klappt das aber nicht. Der qualifizierte Aufruf mit vorheriger use Anweisung wäre dann
    PHP-Code:
    new Diana\Std\String('test'); 

  • #2
    Hallöchen,

    ich denke dein Mapping passt nicht. Das müsste meiner Meinung nach so aussehen:

    Code:
    "psr-4": { "Diana\": "lib/" }
    oder

    Code:
    "psr-4": { "Diana\\Core\": "lib/core/" }
    Viele Grüße
    lotti
    [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

    Kommentar


    • #3
      Ja, stimmt, das war ein Fehler. Ersteres ist natürlich richtig, also
      Code:
      "psr-4": { "Diana\\": "lib/" }
      ist richtig. Weiterhin sind aber leider unqualifizierte Aufrufe von Klassen nicht möglich. Woran könnte das noch liegen?

      Kommentar


      • #4
        Hallöchen,

        im Normalfall läuft das so:

        PHP-Code:
        <?php

        namespace My;

        use 
        My\Namespace\SomeClass;
        use 
        My\Namespace\OtherClass as AliasName;

        // ..

        $obj = new SomeClass;
        $other = new AliasName;
        Viele Grüße
        lotti
        [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

        Kommentar


        • #5
          Ja, das geht auch. Nur was nicht geht, ist eine Klasse aufzurufen, die erstmal nicht existiert. Das alte autoload, also
          PHP-Code:
          function __autoload 
          hätte die dann einfach geladen. Das funktioniert jetzt aber nicht mehr, selbst wenn ich es in files in der autoload section von composer angebe. Ist das irgendwie noch mit composer abbildbar oder geht das gar nicht mehr? Im Web fand ich auch nicht viel dazu.

          Edit: Und ja, an den dump-autoload habe ich gedacht. Weiterhin trägt die Klasse um die es sich handelt auch einen eigenen namespace, der aber nicht unbedingt nötig ist.

          Kommentar


          • #6
            Hallöchen,

            damit der Autoloader weiß welche Klasse er für dich laden soll, muss er wissen um welche Klasse es sich dabei handelt resp. wo er diese findet. Dazu ist die Deklaration via use zwingend notwendig. Eine Klasse "String" kann ja schließlich in allen möglichen Verzeichnissen / Namespaces herumliegen. Auch für deine IDE oder andere Entwickler ist das eine sehr wertvolle Information, die bereitgestellt werden sollte. Alles andere ist Mist.

            Auch wenn das Logo von Composer anderes vermuten lässt, kann Composer wider allen Erwartens tatsächlich nicht zaubern.

            Viele Grüße
            lotti
            [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

            Kommentar


            • #7
              Ok, soweit so gut.Es war etwas Arbeit alles daraufhin abzuändern, aber nun geht es.

              Nur noch eine Frage: Wie kann ich eine Datei mit namespace variabel einbinden? Konkret geht es mir hier um den Controller, der je nach Seitenaufruf ein anderer sein kann, also kann ich nicht alle mit use in der vorherigen Klasse einbinden, die dann den Controller aufruft.
              Nur ein require scheint aber auch nicht zu reichen. Und soviel ich weiß geht ja die use Anweisung innerhalb einer Methode nicht.

              Kommentar


              • #8
                PHP-Code:
                $variablerControllerName "MeinNamspace\\MeinController";

                $controller = new $variablerControllerName(); 

                Kommentar


                • #9
                  Hallöchen,

                  Zitat von derwunner Beitrag anzeigen
                  Nur noch eine Frage: Wie kann ich eine Datei mit namespace variabel einbinden? Konkret geht es mir hier um den Controller, der je nach Seitenaufruf ein anderer sein kann, also kann ich nicht alle mit use in der vorherigen Klasse einbinden, die dann den Controller aufruft.
                  Da du konkret Controller angesprochen hast, gehe ich davon aus, dass du irgendwo deine Routen konfigurierst und die dort festgelegten Klassen später instanziieren willst. Bei mir sieht das meistens so aus:

                  routes.php
                  PHP-Code:
                  <?php

                  use App\Controller\UserController;

                  return[
                      
                  'GET /users' => [UserController::class, 'list']
                  ];
                  index.php
                  PHP-Code:
                  $route $match(..); // returns [UserController::class, 'list']
                  list($class$method) = $route;
                  $controller = new $class;
                  $controller->$method(..);
                  // do something 
                  Viele Grüße
                  lotti
                  [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

                  Kommentar


                  • #10
                    Schon richtig, aber bei mir ist der Code deiner index.php in einer Methode innerhalb der Dispatcher Klasse. Der Dispatcher ist eine Klasse die den Kontrollfluss der Anwendung von Anfang bis Ende inne hält (ähnlich wie bei ASP .NET). Von dort aus wird eine Funktion aufgerufen, die im zentralen Scope die Klasse einbindet. Danach wird ein Objekt von dem Controller angelegt wie in deiner index.php (mit preExec und afterExec noch). Ohne namespaces war diese Einbidung mithilfe von Reflections kein Problem, aber wie sieht das mit namespaces aus? Ich kann ja nicht einfach in der Dispatcher Methode eine use-Anweisung reinschreiben. Auch waren solche Gebilde nicht möglich:
                    PHP-Code:
                    new App\Mvc\Controller\$sFullCtlName(); 
                    @hellbringer:
                    Nein, so ging es auch nicht. Leider...

                    Mein Code sieht bisher so in der Dispatcher.php aus:
                    PHP-Code:
                            private static function _doBaseStuff()
                            {
                                
                    $sFullCtlName ucfirst(self::$_sController 'Controller');
                                
                    require_file(ROOT_PATH 'app/mvc/controller/' $sFullCtlName '.php');
                                
                    $request = new Request();
                                
                    $response = new Response();
                                
                    $sFullCtlName "App\\Mvc\\Controller\\$sFullCtlName";
                                
                    $controller = new $sFullCtlName();
                                
                    // TODO: fertig machen hier
                                
                    die('geht');

                                try
                                {
                                    
                    $controller->setRequest($request);
                                    
                    $controller->setResponse($response);
                                    
                    $controller->setControllerName(self::$_sController);
                                    
                    $controller->setActionName(self::$_sAction);
                                    
                    $bRet $controller->preExec();
                                    if (
                    $bRet)
                                    {
                                        
                    $sLocalAction self::$_sAction->__toString();
                                        
                    $controller->$sLocalAction();
                                    }
                                    
                    $controller->afterExec();
                                }
                                catch (
                    Exception $ex)
                                {
                                    
                    $controller->_errorHandler($ex);
                                }
                            } 
                    etwas vorher in der index.php:
                    PHP-Code:
                    function require_file($sFile)
                    {
                        require(
                    $sFile);

                    Auch die schönere Variante mit der string Verkettung ging nicht. Ich vermute die double quoutes sind wegen den zwei Backslashes, damit die geparst werden.

                    Kommentar


                    • #11
                      Hallöchen,

                      Zitat von derwunner Beitrag anzeigen
                      Schon richtig, aber bei mir ist der Code deiner index.php in einer Methode innerhalb der Dispatcher Klasse. Der Dispatcher ist eine Klasse die den Kontrollfluss der Anwendung von Anfang bis Ende inne hält (ähnlich wie bei ASP .NET). Von dort aus wird eine Funktion aufgerufen, die im zentralen Scope die Klasse einbindet. Danach wird ein Objekt von dem Controller angelegt wie in deiner index.php (mit preExec und afterExec noch).
                      Das mit der index.php war natürlich nur ein Beispiel. In richtigen Projekten sind alle Aufgaben nach SRP in entsprechende Klassen ausgelagert und zentral gesteuert. Zudem nutze ich fertige Komponenten wie nikic/FastRoute und PHP-DI um das Rad nicht ständig neu erfinden zu müssen.

                      Zitat von derwunner Beitrag anzeigen
                      Ohne namespaces war diese Einbidung mithilfe von Reflections kein Problem, aber wie sieht das mit namespaces aus?
                      In dem von mir gezeigten Beispiel tue ich doch genau das. In routes.php werden Controller-Klassen für Routen festgelegt, die in index.php instanziiert und ausgeführt werden. In der index.php ist auch kein weiteres use-Statement mehr notwendig.

                      Zitat von derwunner Beitrag anzeigen
                      Ich kann ja nicht einfach in der Dispatcher Methode eine use-Anweisung reinschreiben. Auch waren solche Gebilde nicht möglich:
                      Musst du auch nicht, denn die Controller-Klasse legst du für gewöhnlich in deiner Routen-Konfiguration fest.

                      Zitat von derwunner Beitrag anzeigen
                      @hellbringer:
                      Nein, so ging es auch nicht. Leider...
                      Doch, genau das geht:

                      PHP-Code:
                      <?php

                      namespace My\Funny\Space;

                      class 
                      MyClass{
                          public function 
                      run(){
                              echo 
                      'yes, it works';
                          }
                      }

                      $classRef 'My\Funny\Space\MyClass';

                      $obj = new $classRef;
                      $obj->run();
                      Mein Code sieht bisher so in der Dispatcher.php aus:
                      PHP-Code:
                      // ..
                      require_file(ROOT_PATH 'app/mvc/controller/' $sFullCtlName '.php');
                      // .. 
                      Das hat mit Autoloading nix mehr zu tun. Wenn du das Autoloading richtig nutzt, dann solltest du in deiner gesamten Anwendung ** keine ** Klassen mehr manuell (also via require) laden müssen. Siehe dazu nochmal mein Beispiel. Wenn der Autoloader korrekt konfiguriert ist, dann funktioniert mein Beispiel ohne ein require-Statement innerhalb des gesamten Prozesses (ausgenommen das Laden von Konfigurationsdateien).

                      PHP-Code:
                      function require_file($sFile)
                      {
                      require(
                      $sFile);

                      Was soll der Wrapper um require? Überlasse das Autoloading am besten Composer.

                      Ich vermute die double quoutes sind wegen den zwei Backslashes, damit die geparst werden.
                      Jep.

                      Viele Grüße
                      lotti
                      [SIZE="1"]Atwood's Law: any application that can be written in JavaScript, will eventually be written in JavaScript.[/SIZE]

                      Kommentar


                      • #12
                        Abend,

                        mittlerweile weis ich, dass der Aufruf mit dem qualifizierten namespace auch bei mir geht. Das Problem ist eher, dass er eine Model Klasse nicht findet. Scheint aber alles richtig zu sein. So kommt es mir jedenfalls vor, denn der Debugger springt jedes Mal beim new Schlüsselwort von den Model in den composer autoloader Code. Die nachfolgenden Code Auszüge führen zu den Problem:
                        PHP-Code:
                        namespace App\Src
                        {
                            
                        // ...
                            
                        use App\Mvc\Model\ProfileModel;
                            class 
                        TestController extends BaseController
                            
                        {
                                
                        // ...
                                
                        protected function _fetchFrontendUser()
                                {
                                    
                        // ...
                                    
                        $profileMdl = new ProfileModel();
                                    die(
                        'asdf');
                                }
                            }

                        ProfileModel.php:
                        PHP-Code:
                        namespace App\Mvc\Model
                        {
                        // use ...
                            
                        class ProfileModel extends BaseModel
                            
                        {
                                
                        // ...
                            
                        }

                        Meine PSR-4 config im composer.json:
                        Code:
                            "autoload":
                                        {
                                            "psr-4": {
                                                        "Diana\\": "lib/",
                                                        "App\\": "app/"
                                                    }
                                        },
                        Wegen der Ordnerstruktur siehe Thread Eröffnung. Kann es eventuell auch an der unterschiedlichen Groß- und Kleinschreibung der Ordner im Dateisystem liegen?

                        Kommentar


                        • #13
                          Wenn du eh siehst, dass er in den Autoloader springt, warum schaust du dann nicht gleich nach, wo der Autoloader die Datei sucht?

                          Die geschweiften Klammern bei namespace sind übrigens unüblich und bringen keinen Vorteil. Sie sind nur für den Fall gedacht, dass man in einer Datei mehrere Namespaces verwendet, was aber eher ein Sonderfall ist.

                          Kommentar


                          • #14
                            Wegen der Ordnerstruktur siehe Thread Eröffnung. Kann es eventuell auch an der unterschiedlichen Groß- und Kleinschreibung der Ordner im Dateisystem liegen?
                            Ja, wenn Deine Ordnerstruktur z.Z. so aussieht app/mvc/... dann kann das nicht funktionieren. Versuch mal die Ordner wie folgt umzubenennen: app/Mvc/Model/ oder App/Mvc/Model/ (im letzteren Fall müßtest Du noch den Path im Composer anpassen "App\\": "App/").

                            VG
                            Jack
                            -

                            Kommentar


                            • #15
                              hellbringer Ja, Danke das war der richtige Tipp! Ich hatte Persistence mit 2 c geschrieben. Das war der große Fehler warum das Einbinden nicht ging.

                              Ansonsten musste ich nur noch den Backslash vor allen PHP Standardklassen hinzufügen, damit es ging. Seltsam fand ich, dass eclipse eine Datei unsichtbar kaputt gemacht hatte. Ich hatte mir whitespaces und Zeilenenden anzeigen lassen in einem anderen Editor, nichts gefunden. Erst nachdem ich den kompletten Inhalt der Klasse in eine neue Datei kopiert hatte und diese dann wieder in die Ursprungsdatei umbenannt hatte, ging es wieder. Das hat mir knapp zwei Stunden gekostet

                              Danke an alle, es geht nun so wie sollte.

                              Kommentar

                              Lädt...
                              X