PSR4 Autoloader findet Klasse nicht

  • Hey,


    ich entwickle gerade wieder ein größeres Projekt und brauche dafür den Autoloader. Als Autoloader nutze ich den PSR4 Autoloader.


    Nun wird mir die Fehlermeldung angezeigt, dass die gewünschte Klasse Authentification nicht gefunden werden kann. Der Autoloader bricht immer wieder bei dem if-Statement mit der Funktion strcncmp() ab.


    In $class steckt Authentification.


    Woran kann es liegen?


    Hier ist der Code:

    PHP: index.php
    1. <?php
    2. require_once "autoload.php";
    3. $auth = new Authentification();
  • Das solltest du eigentlich kennen:


    Richtig debuggen

    1. Man bemerkt, dass ein Skript nicht das tut, was es soll.
    2. Man schreibt an den Anfang des Scriptes die Zeile: error_reporting(-1);
    3. Man verwendet ini_set('display_errors', true); damit die Fehler auch angezeigt werden.
    4. Man versucht, die Stelle die daran Schuld sein kann, schonmal einzugrenzen. Falls dies nicht geht, wird zunächst das komplette Skript als fehlerhaft angesehen.
    5. An markanten Stellen im Skript lässt man sich wichtige Variableninhalte ausgeben und ggf. auch in bedingten Anweisungen eine kurze Ausgabe machen, um zu überprüfen, welche Bedingung ausgeführt wurde. Wichtig bei MySQL Fehlern (...not a valid MySQL result resource...): mysqli_error() verwenden oder Abfrage ausgeben und zb mit phpmyadmin testen.
    6. Schritt 5 wird so lange wiederholt, bis Unstimmigkeiten im Skript auffallen
    7. Damit hat man das Problem (Unstimmigkeit) gefunden und kann versuchen diese zu beheben. Hierzu dienen dann die PHP-Dokumentation und andere Quellen als Ratgeber.
    8. Lässt sich das konkrete Problem trotzdem nicht beheben, kann man in Foren um Rat fragen.
    9. Das Programm läuft und man kann die Debug-Ausgaben wieder entfernen.

    Sind die Werte denn OK? Verzeichnis- und Dateinamen korrekt, auch was Groß/Kleinschreibung betrifft.

  • Hey m.scatello,


    ja. Dies habe ich auch schon gemacht und bin an Punkt 8 angekommen:)


    Ich habe mit namespaces schon lange nichts mehr gemacht und finde da erstmal rein.


    So sieht der Authentification Controller aus:

    PHP
    1. namespace App\Controller\Authentification;
    2. class Authentification{
    3. ...
    4. }


    Wenn ich diesen in der index.php über App\Controller\Authentification(); aufrufen möchte wird es immer noch nicht gefunden. Der Autoloader bricht immer wieder bei dem if-Statement mit der Funktion strcncmp() ab. Warum weiß ich nicht. Mir kommt da nichts falsch vor.

  • Wenn ich diesen in der index.php über App\Controller\Authentification(); aufrufen möchte wird es immer noch nicht gefunden.

    Klar, der ist ja unter App\Controller\Authentification\Authentification zu finden. Und irgendwie sieht das auch jetzt alles ganz anders aus als oben, da war noch davon die Rede dass in $class nur Authentification stehen würde. Was du mit dem $prefix und dem strncmp genau vorhast erschließt sich mir auch nicht so ganz …

  • Hey,


    tk1234 : Daran liegt es nicht.


    Ich habe einfach den PSR4-Autoloader von php-fig.org kopiert: https://www.php-fig.org/psr/psr-4/examples/. Der Prefix wird beim Erstellen des Pfads sowieso entfernt.


    Nochmal ein projektbezogenes Beispiel


    Dies ist die Ordnerstruktur zur Klasse Router die ich mit dem Autoloader laden möchte: app/core/Router.php

    Nun steht in der Router.php dieser Namespace: namespace App\Core;


    Wenn ich im Autoloader mir die $class direkt zu Beginn ausgeben lasse, wird einfach nur den Klassenname ausgegeben. In diesem Fall also Router. Ohne jeglichen Namespace. Wenn ich den Namespace nun in der index.php bei der Instanziierung der Klasse nochmal explizit angebe ändert sich auch nichts daran.


    Irgendwie erkennt der Autoloader den Namespace nicht.


    Wenn ich vom Autoloader folgenden Codeblock entferne kann ich mir zumindest den Pfad zur Datei ausgeben lassen, welche eingebunden wird, wenn diese existiert:

    PHP
    1. if (strncmp($prefix, $class, $len) !== 0) {
    2. return;
    3. }

    Der Pfad lautet: C:\xampp\htdocs\project/app/Router.php


    Dann habe ich mir gedacht, dass es an dem direkten Aufruf über C:\ liegt und habe direkt http://localhost/project anstelle von __DIR__ bei $base_dir genommen und zum Testen auch den direkten Pfad angeben, damit nurnoch die $class angehangen werden muss: http://localhost/project/app/core/. Jetzt wird diese selbst damit immernoch nicht gefunden obwohl der Pfad, der eingebunden wird folgender ist: http://localhost/project/app/core/Router.php.


    Mit einem relativen Pfad klappt es auch nicht.

  • Vorweg: wenn du ein Posting einmal abgeschickt hast, änder es doch bitte nicht mehr. Es ist sehr verwirrend wenn man den Code zum testen kopieren will und auf einmal ist der weg.

    Ich habe einfach den PSR4-Autoloader von php-fig.org kopiert: https://www.php-fig.org/psr/psr-4/examples/.

    Ok, jetzt verstehe ich was der Code macht - das kann aber natürlich nicht funktionieren wenn der Namespace fehlt (wie in #1, dort wird in der index.php kein Namespace verwendet!).

    Dies ist die Ordnerstruktur zur Klasse Router die ich mit dem Autoloader laden möchte: app/core/Router.php

    Nun steht in der Router.php dieser Namespace: namespace App\Core;


    Wenn ich im Autoloader mir die $class direkt zu Beginn ausgeben lasse, wird einfach nur den Klassenname ausgegeben. In diesem Fall also Router.

    Dann machst du irgendwas falsch, bei mir funktioniert der Code von der angegebenen Quelle. Auch passt die Ordnerstruktur nicht zum Namespace bzw. dem $prefix im Autoloader. Bleib doch bitte mal bei einer Klasse und einem Namespace und ändere den nicht ständig - das ist sehr verwirrend und macht es quasi unmöglich den Fehler zu finden. Poste bitte auch mal den Code mit dem sich das Problem nachvollziehen lässt (einschließlich der Pfade wo was liegt).


    Wenn ich den Namespace nun in der index.php bei der Instanziierung der Klasse nochmal explizit angebe ändert sich auch nichts daran.

    Den Namespace musst du in der index.php natürlich auf jeden Fall angeben, woher soll PHP sonst wissen welchen Namespace du meinst? Ob du da am Anfang der Datei namespace …; verwendest, beim Aufruf der Klasse mit new … den Namespace voranstellst oder mit use …; arbeitest ist dabei egal.


    Zitat

    Wenn ich vom Autoloader folgenden Codeblock entferne kann ich mir zumindest den Pfad zur Datei ausgeben lassen, welche eingebunden wird, wenn diese existiert:

    Das kommt daher dass im Autoloader der Namespace nicht ankommt - der Autoloader ist nur für den angegebenen Namespace zuständig, ohne diesen bricht er natürlich ab.

    Dann habe ich mir gedacht, dass es an dem direkten Aufruf über C:\ liegt und habe direkt http://localhost/project anstelle von __DIR__ bei $base_dir genommen

    Das ist natürlich Käse. Du brauchst auf jeden Fall lokale, absolute Pfade - mit einer URL kommst du nicht weit: probier mal das PHP-Script per URL aufzurufen, was siehst du? Richtig, nichts - genau das gleiche sieht PHP wenn es die Datei per require einbindet. Ganz schlechte Idee, am besten sofort wieder vergessen.


    Aber: warum liegen die Klassen überhaupt in einem Verzeichnis das per HTTP erreichbar ist? Das einzige was in dem per http erreichbaren Verzeichnis liegt ist die index.php (sowie Bilder/JS/CSS), alle weiteren Scripte liegen in einem Verzeichnis weiter oben.

  • Guten Morgen,


    danke für deine ausführliche Antwort & die Ratschläge. Ich habe einfach mal die Klasse Router mit seinem Namespace über use integriert use App\Core\Router as Router;, dann instanziiert und plötzlich hat es, obwohl der Namespace der gleiche war, funktioniert. Ich weiß nicht warum.


    Der Autoloader funktioniert jetzt aber ohne weitere Probleme.


    Zitat

    Aber: warum liegen die Klassen überhaupt in einem Verzeichnis das per HTTP erreichbar ist? Das einzige was in dem per http erreichbaren Verzeichnis liegt ist die index.php (sowie Bilder/JS/CSS), alle weiteren Scripte liegen in einem Verzeichnis weiter oben.

    Wie meinst du das?


    Schöne Grüße,
    Stef

  • Wie meinst du das?

    Wenn du http://localhost aufrufst, wird vmtl. der Inhalt des Verzeichnisses C:/xampp/htdocs aufgerufen. Damit Scripte und auch z.B. Cache-Dateien o.ä. nicht über den Browser erreichbar sind, gehören die in C:/xampp/scripte (oder so, wie genau ist eigentlich völlig egal, nur halt nicht unterhalb von htdocs).

    Wobei ich eher ganz außerhalb von C:/xampp arbeiten würde aber zum Glück muss überhaupt nicht mit Windows arbeiten und habe damit hier überhaupt kein c:/ (geschweige denn XAMPP) :-)

  • okay und warum empfiehlst du dies so zu tun?


    Ich sehe da aktuell noch keinen Grund dies so zu tun. Selbst große Shopsysteme haben all ihre Scripte innerhalb des Root-Verzeichnisses.

    Wenn Dateien nicht per http erreichbar sind, können sich auch nicht durch einen Konfigurationsfehler ausgeliefert werden. Dass der PHP-Interpreter mal nicht funktioniert (und damit PHP-Dateien im Klartext ausgeliefert werden) ist natürlich extrem unwahrscheinlich aber bei anderen Dateien kann es schon mal vorkommen dass man mal vergisst ein Verzeichnis zu sperren. Bei größeren Systemen schauen idR viele Leute drüber und Fehlkonfigurationen fallen da normal auf - bei selbst geschriebenen Scripten ist das nicht so. Zudem sind die großen Systeme meist über lange Zeit gewachsen und können nicht mal eben einfach komplett umgekrempelt werden außerdem funktioniert das auch nur wenn die Systeme im obersten Verzeichnis einer Domain installiert werden müssen, sobald eine Installation auch in tieferen Verzeichnissen möglich sein soll (z.B. example.com/shop/), ist das mit fertigen Scripten nicht mehr möglich. Bei Eigenenwicklungen ist es imho aber nicht verkehrt nur die Dateien direkt erreichbar zu machen die auch erreichbar sein müssen.

  • Hey,


    danke für deine ausführliche Antwort. :)

    Ich habe es ausprobiert und es hat aber nicht funktioniert. Der Pfad kann nicht gefunden werden, weil die Daten außerhalb des Root-Verzeichnisses der Webseite liegen.


    Beim Ausprobieren ist es mir auf dem FTP-Server mit dieser Vorgehensweise auch sehr unübersichtlich erschienen.


    Schöne Grüße,
    Stef

  • Hey,


    ich muss dieses Thema nochmal aufgreifen.


    Ich habe mein Projekt gerade ohne jegliche Änderungen auf mein Webserver hochgeladen und über den Browser aufgerufen. Nun kommt der gleiche Fehler wie zu Beginn: Die Klasse kann nicht gefunden werden, obwohl der Pfad zur PHP-Datei stimmt.


    Lokal unter Xampp funktioniert es. Online nicht.


    Ich kann mich noch daran erinnern, dass es zu Beginn bei Xampp nicht funktioniert hat und nach Tagen plötzlich ohne Codeänderung funktioniert hatte.


    Ich bin ratlos wie das sein kann.


    Habt ihr Vorschläge?

  • Dateiname groß, klein passt?
    Unix verhält sich hier anders als Windows.
    Möglicherweise können auch irgendwelche (unsichtbaren) Steuerzeichen entweder im Dateinamen oder im Klassennamen befinden.
    Zudem würde ich mit einem kleinen `die('foo')` im Kopf der Zieldatei überprüfen, ob die Datei durch den Interpreter läuft (also erfolgreich geladen wird) oder ob es bereits hier scheitert.

  • Ich habe mein Projekt gerade ohne jegliche Änderungen auf mein Webserver hochgeladen und über den Browser aufgerufen. Nun kommt der gleiche Fehler wie zu Beginn: Die Klasse kann nicht gefunden werden, obwohl der Pfad zur PHP-Datei stimmt.

    Da der Webserver vmtl. unter Linux läuft könnte es an einem Klassiker liegen: Groß- und Kleinschreibung bei Dateinamen und Verzeichnissen nicht konsequent beachtet (Linux unterscheidet hier, Windows nicht) - mehr lässt sich so völlig ohne Kenntnis von Code und Datenstruktur nicht sagen.


    Zum Thema Dateien außerhalb vom document-root ablegen muss ich auch noch was nachtragen: dass große Shop-System alle ihre Dateien innerhalb des Root-Verzeichnisses hätten (-> #9) stimmt nicht: Shopware z.B. hat da mit der Version 6 umgestellt, wenn die Domain nicht auf das public-Verzeichnis zeigt sondern auf die Ebene darüber, bekommt man lediglich eine Fehlermeldung. Das muss natürlich nicht repräsentativ sein, oft wird es aber historische Gründe haben da eine Umstellung der Verzeichnisstruktur immer einen gewissen Aufwand mitbringt.

  • Hey,


    danke für eure Hilfestellung :) Nach langem rumtesten, recherchieren funktioniert nun alles. Es lag unter anderem an der Groß- und Kleinschreibung und den verschiedenen Rechten, die benötigt wurden um z.B. Aktionen im Verzeichnissystem auszuführen.


    tk1234 : Danke für die nochmalige Erwähnung dieser Thematik. Ich werde mir dies wenn ich dafür Zeit finde, nochmal in Ruhe anschauen.

  • Stef , tk1234 der Vorteil Dateien außerhalb des Docroots abzulegen ist, niemand kann darauf über die URI zugreifen (//example.com/.../config.php aufzurufen ist quasi nie möglich, //example.com/config.php dagegen viel eher).
    Dies hilft bei (Webserver-)Konfigurations & Menschlichen Fehlern Datenleaks zu vermeiden. Natürlich ist das keine Garantie aber eben eine Maßnahme mehr.

    Das ganze ist nicht relevant wenn alles richtig konfiguriert ist & keine Fehler passieren, überall die benötigten deny Einträge liegen und directiory indexing ausgeschaltet ist. Wir wissen aber das in der echten Welt fehler passieren und Dinge übersehen werden oder auch Maschinen wie Webserver manchmal nicht mehr so funktionieren wie vorgesehen.

    Zum Beispiel:

    1. In dem Moment wo sich aber z.B. dein Webserver verschluckt und z.B.*.phpdateien nicht mehr über den php parser ausliefert sondern direkt könnten Configurations-Dateien plaintext an den Nutzer augeliefert werden.
    2. Irgendwelche debug ausgaben mit kritischen Informationen in einer Datei übersehen/vergessen werden, die zwar im normalen einsatz der applikation nicht eingebunden werden, durchaus aber noch über die URL aufrufbar sind
    3. Ein Fehler in einer von dir genutzen composer dependencie vorliegt der dir nicht bekannt ist, welcher ausgenutzt werden kann, weil die composer dateien über die URL aufrufbar sind
    4. -- hier kann man noch ne Menge ergänzen, u.A. auch komplizierte Exploits --

    tl;dr
    Wenn es bei deinem Hoster möglich ist den document root anzupassen, ist das zu empfehlen & nur wirklich öffentliche Dateien (index.php, css, bild und js assets) im public Ordner abzulegen.