Wie verwende ich PHP-Namespaces mit Autoload?

104

Ich erhalte diese Fehlermeldung, wenn ich versuche, Autoload und Namespaces zu verwenden:

Schwerwiegender Fehler: Klasse 'Klasse1' nicht in /usr/local/www/apache22/data/public/php5.3/test.php in Zeile 10 gefunden

Kann mir jemand sagen, was ich falsch mache?

Hier ist mein Code:

Class1.php:

<?php

namespace Person\Barnes\David
{
    class Class1
    {
        public function __construct()
        {
            echo __CLASS__;
        }
    }
}

?>

test.php:

<?php

function __autoload($class)
{
    require $class . '.php';
}

use Person\Barnes\David;

$class = new Class1();

?>
David Barnes
quelle

Antworten:

119

Klasse 1 ist nicht im globalen Bereich.

Unten finden Sie ein Arbeitsbeispiel:

<?php

function __autoload($class)
{
    $parts = explode('\\', $class);
    require end($parts) . '.php';
}

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Bearbeiten (14.12.2009):

Zur Verdeutlichung diente meine Verwendung von "use ... as" dazu, das Beispiel zu vereinfachen.

Die Alternative war die folgende:

$class = new Person\Barnes\David\Class1();

oder

use Person\Barnes\David\Class1;

// ...

$class = new Class1();
Tanerkay
quelle
1
Sie müssen nicht verwenden AS. Deshalb funktioniert diese Lösung nicht. Sie könnten genauso gut tun: use Person\Barnes\David\Class1;(was gleichbedeutend ist mit use Person\Barnes\David\Class1 as Class1;).
Cartbeforehorse
1
Danke, das funktioniert. Aber ich kann nicht verstehen, warum wir nur $ class = new Class1 () verwenden können; wenn wir bereits vorher "use Person \ Barnes \ David;" definiert haben?
user345602
4
@ user346665 müssen Sie verwenden, use Person\Barnes\David\Class1;um zu tun $class = new Class1();. Mit use Person\Barnes\David;muss man machen $class = new David\Class1();. Das useSchlüsselwort für sich entspricht use Person\Barnes\David\Class1 as Class1;bzw. entspricht use Person\Barnes\David as David;jedem Beispiel.
Justin C
Verwenden Sie für diejenigen, die 2018 lesen, die @ prince-billy-graham-Lösung mit spl_autoload_register
Bruno de Oliveira
26

Wie Pascal MARTIN erwähnt, sollten Sie das '\' durch DIRECTORY_SEPARATOR ersetzen, zum Beispiel:

$filename = BASE_PATH . DIRECTORY_SEPARATOR . str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
include($filename);

Ich würde Ihnen auch empfehlen, die Verzeichnisstruktur neu zu organisieren, um den Code besser lesbar zu machen. Dies könnte eine Alternative sein:

Verzeichnisaufbau:

ProjectRoot
 |- lib

Datei: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Erstellen Sie das Unterverzeichnis für jeden von Ihnen definierten Namespace.

Datei: /ProjectRoot/test.php

define('BASE_PATH', realpath(dirname(__FILE__)));
function my_autoloader($class)
{
    $filename = BASE_PATH . '/lib/' . str_replace('\\', '/', $class) . '.php';
    include($filename);
}
spl_autoload_register('my_autoloader');

use Person\Barnes\David as MyPerson;
$class = new MyPerson\Class1();
  • Ich habe die PHP 5-Empfehlung für die Autoloader-Deklaration verwendet. Wenn Sie immer noch mit PHP 4 arbeiten, ersetzen Sie es durch die alte Syntax: function __autoload ($ class)
Kostanos
quelle
18

Ihre __autoloadFunktion erhält den vollständigen Klassennamen einschließlich des Namespace-Namens.

Dies bedeutet in Ihrem Fall, dass die __autoloadFunktion ' Person\Barnes\David\Class1' und nicht nur ' Class1' erhält .

Sie müssen also Ihren Autoloading-Code ändern, um mit dieser Art von "komplizierterem" Namen fertig zu werden. Eine häufig verwendete Lösung besteht darin, Ihre Dateien mit einer Verzeichnisebene pro "Ebene" von Namespaces zu organisieren und beim automatischen Laden ' \' im Namespace-Namen durch zu ersetzen DIRECTORY_SEPARATOR.

Pascal MARTIN
quelle
1
Das habe ich nicht gefunden. Als ich die Aussage gestorben habe ($ class); In der Funktion __autoload wurde 'Class1 "' gedruckt, nicht 'Person \ Barnes \ David \ Class1'
David Barnes
Wahr. Der Parameter $ class von autoload ist der Klassenname, wie er im Konstruktoraufruf geschrieben wurde.
Tishma
1
Downvote für "Ihre __autoloadFunktion erhält den vollständigen Klassennamen einschließlich des Namespace-Namens" - dies gilt nur, wenn Sie explizit usedie Klasse angegeben haben, auf die Sie verweisen möchten , und nicht, wenn Sie lediglich useden Namespace angegeben haben, zu dem sie gehört. Der Fehler des OP war, dass er useden Namespace mit einer Klasse erstellt hatte und dann erwartete, dass seine Autoload-Funktion auf magische Weise den vollständigen Klassenpfad passieren würde. Diese Antwort spricht den Fehler des OP nicht wirklich an.
Mark Amery
15

Ich mache so etwas: Siehe dieses GitHub-Beispiel

spl_autoload_register('AutoLoader');

function AutoLoader($className)
{
    $file = str_replace('\\',DIRECTORY_SEPARATOR,$className);

    require_once 'classes' . DIRECTORY_SEPARATOR . $file . '.php'; 
    //Make your own path, Might need to use Magics like ___DIR___
}
Tika
quelle
3
Schön und einfach. Wenn man danach suchen sollte.)
Dennis
3

Ich habe dieses Juwel von Flysystem gefunden

spl_autoload_register(function($class) {
    $prefix = 'League\\Flysystem\\';

    if ( ! substr($class, 0, 17) === $prefix) {
        return;
    }

    $class = substr($class, strlen($prefix));
    $location = __DIR__ . 'path/to/flysystem/src/' . str_replace('\\', '/', $class) . '.php';

    if (is_file($location)) {
        require_once($location);
    }
});
Boksiora
quelle
3

Ich sehe, dass die Autoload-Funktionen in den folgenden beiden Fällen nur den "vollständigen" Klassennamen erhalten - mit allen vorangegangenen Namespaces:

[a] $a = new The\Full\Namespace\CoolClass();

[b] use The\Full\Namespace as SomeNamespace; (at the top of your source file) followed by $a = new SomeNamespace\CoolClass();

Ich sehe, dass die Autoload-Funktionen im folgenden Fall NICHT den vollständigen Klassennamen erhalten:

[c] use The\Full\Namespace; (at the top of your source file) followed by $a = new CoolClass();

UPDATE: [c] ist ein Fehler und funktioniert sowieso nicht mit Namespaces. Ich kann berichten, dass anstelle von [c] auch die folgenden zwei Fälle gut funktionieren:

[d] use The\Full\Namespace; (at the top of your source file) followed by $a = new Namespace\CoolClass();

[e] use The\Full\Namespace\CoolClass; (at the top of your source file) followed by $a = new CoolClass();

Hoffe das hilft.

Daniel Rhodes
quelle
Nebenbei bemerkt, das useSchlüsselwort funktioniert in der interaktiven PHP-Befehlszeilenschnittstelle ( php --interactive) nicht richtig .
Andrew Larsson
3

Ich benutze diesen einfachen Hack in einer Zeile:

spl_autoload_register(function($name){
        require_once 'lib/'.str_replace('\\','/',$name).'.php';
    });
princebillyGK
quelle
1

hatte das gleiche Problem und fand gerade Folgendes:

Wenn Sie eine Unterordnerstruktur erstellen, die mit den Namespaces der enthaltenen Klassen übereinstimmt, müssen Sie nicht einmal einen Autoloader definieren.

    spl_autoload_extensions(".php"); // comma-separated list
    spl_autoload_register();

Es funktionierte wie ein Zauber

Weitere Informationen hier: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

BEARBEITEN: Dies verursacht unter Linux ein Problem aufgrund eines Backslashs ... Hier finden Sie eine funktionierende Lösung von immeëmosol

Namespace Autoload funktioniert unter Windows, jedoch nicht unter Linux

JohnWolf
quelle
1

Die Verwendung von gotcha ist zwar bei weitem die schnellste Methode, es wird jedoch auch erwartet, dass alle Dateinamen in Kleinbuchstaben geschrieben werden.

spl_autoload_extensions(".php");
spl_autoload_register();

Beispielsweise:

Eine Datei, die die Klasse SomeSuperClass enthält, muss somesuperclass.php heißen. Dies ist ein Problem, wenn Sie ein Dateisystem mit Groß- und Kleinschreibung wie Linux verwenden, wenn Ihre Datei SomeSuperClass.php heißt, aber unter Windows kein Problem darstellt.

Die Verwendung von __autoload in Ihrem Code funktioniert möglicherweise weiterhin mit aktuellen PHP-Versionen. Erwarten Sie jedoch, dass diese Funktion in Zukunft veraltet und endgültig entfernt wird.

Also, welche Optionen bleiben übrig:

Diese Version funktioniert mit PHP 5.3 und höher und ermöglicht die Dateinamen SomeSuperClass.php und somesuperclass.php. Wenn Sie 5.3.2 und höher verwenden, arbeitet dieser Autoloader noch schneller.

<?php

if ( function_exists ( 'stream_resolve_include_path' ) == false ) {
    function stream_resolve_include_path ( $filename ) {
        $paths = explode ( PATH_SEPARATOR, get_include_path () );
        foreach ( $paths as $path ) {
            $path = realpath ( $path . PATH_SEPARATOR . $filename );
            if ( $path ) {
                return $path;
            }
        }
        return false;
    }
}

spl_autoload_register ( function ( $className, $fileExtensions = null ) {
    $className = str_replace ( '_', '/', $className );
    $className = str_replace ( '\\', '/', $className );
    $file = stream_resolve_include_path ( $className . '.php' );
    if ( $file === false ) {
        $file = stream_resolve_include_path ( strtolower ( $className . '.php' ) );
    }
    if ( $file !== false ) {
        include $file;
        return true;
    }
    return false;
});
Michael Bush
quelle
2
als Randnotiz, str_replace ([ '_','\\'] '/', $className );ist doppelt so schnell wie zwei str_replace
Itay Moav -Malimovka
Solange es keine Rolle spielt, ob die PHP-Datei in Groß- / Kleinschreibung geschrieben ist, bleiben die Verzeichnisse immer noch in Groß- und Kleinschreibung
Mike
1

Ich fand kürzlich Tanerkucs Antwort sehr hilfreich! Ich wollte das nur mit hinzufügenstrrpos() + substr()etwas schneller ist als explode()+ end():

spl_autoload_register( function( $class ) {
    $pos = strrpos( $class, '\\' );
    include ( $pos === false ? $class : substr( $class, $pos + 1 ) ).'.php';
});
Alan
quelle
1

Ich werde meine zwei Cent für relative Anfänger oder was auch immer ein einfaches spl_autoload_register () Setup ohne die ganze Theorie wollen einwerfen: Erstellen Sie einfach eine PHP-Datei für jede Klasse, benennen Sie diese PHP-Datei mit Ihrem Klassennamen und behalten Sie Ihre Klassendateien im selben Verzeichnis wie Ihre betreffende PHP-Datei, dann funktioniert dies:

spl_autoload_register(function ($class_name) {
    require_once dirname(__FILE__) . DIRECTORY_SEPARATOR . $class_name . '.php';
});

Das Googeln der Teile in dieser Funktion sollte die Funktionsweise beantworten. PS: Ich benutze Linux und das funktioniert unter Linux. Windows-Leute sollten es zuerst testen.


quelle
1

https://thomashunter.name/blog/simple-php-namespace-friendly-autoloader-class/

Sie möchten Ihre Klassendateien in einem Ordner namens Classes ablegen, der sich im selben Verzeichnis befindet wie der Einstiegspunkt in Ihre PHP-Anwendung. Wenn Klassen Namespaces verwenden, werden die Namespaces in die Verzeichnisstruktur konvertiert. Im Gegensatz zu vielen anderen Auto-Loadern werden Unterstriche nicht in Verzeichnisstrukturen konvertiert (es ist schwierig, PHP <5.3 Pseudo-Namespaces zusammen mit PHP> = 5.3 echte Namespaces zu erstellen).

<?php
class Autoloader {
    static public function loader($className) {
        $filename = "Classes/" . str_replace("\\", '/', $className) . ".php";
        if (file_exists($filename)) {
            include($filename);
            if (class_exists($className)) {
                return TRUE;
            }
        }
        return FALSE;
    }
}
spl_autoload_register('Autoloader::loader');

Sie möchten den folgenden Code in Ihr Haupt-PHP-Skript (Einstiegspunkt) einfügen:

require_once("Classes/Autoloader.php");

Hier ist ein Beispiel für ein Verzeichnislayout:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business classC {}
    Deeper/
      ClassD.php - namespace BusinessDeeper classD {}
Yuvraj Singh Shekhawat
quelle
0
<?php
spl_autoload_register(function ($classname){
   // for security purpose
   //your class name should match the name of your class "file.php"
   $classname = str_replace("..", "", $classname);
   require_once __DIR__.DIRECTORY_SEPARATOR.("classes/$classname.class.php");
});
try {
  $new = new Class1();
} catch (Exception $e) {
   echo "error = ". $e->getMessage();
}
?>
Nevil Saul Blemuss
quelle
1
Während dieser Code die Frage möglicherweise beantwortet, verbessert die Bereitstellung eines zusätzlichen Kontexts darüber, warum und / oder wie dieser Code die Frage beantwortet, ihren langfristigen Wert.
Ethan