Jak używać przestrzeni nazw PHP z automatycznym ładowaniem?


104

Otrzymuję ten błąd, gdy próbuję użyć automatycznego ładowania i przestrzeni nazw:

Błąd krytyczny: nie znaleziono klasy „Class1” w /usr/local/www/apache22/data/public/php5.3/test.php w linii 10

Czy ktoś może mi powiedzieć, co robię źle?

Oto mój kod:

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();

?>

Odpowiedzi:


119

Klasa 1 nie należy do zakresu globalnego.

Poniżej znajduje się działający przykład:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Edycja (2009-12-14):

Dla wyjaśnienia, użycie wyrażenia „użyj ... jako” miało na celu uproszczenie przykładu.

Alternatywa była następująca:

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

lub

use Person\Barnes\David\Class1;

// ...

$class = new Class1();

1
Nie musisz używać AS. Nie dlatego to rozwiązanie działa. Możesz równie łatwo zrobić: use Person\Barnes\David\Class1;(co jest równoważne use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse

1
Dzięki, to działa. Ale nie mogę zrozumieć, dlaczego możemy po prostu użyć $ class = new Class1 (); kiedy już zdefiniowaliśmy "użyj Person \ Barnes \ David;" wcześniej?
user345602

4
@ user346665 musisz użyć use Person\Barnes\David\Class1;, aby to zrobić $class = new Class1();. Z use Person\Barnes\David;musisz zrobić $class = new David\Class1();. Słowo usekluczowe samo w sobie jest odpowiednikiem use Person\Barnes\David\Class1 as Class1;lub use Person\Barnes\David as David;, odpowiednio dla każdego przykładu.
Justin C

Dla tych, którzy czytają w 2018, użyj rozwiązania @ prince-billy-graham z spl_autoload_register
Bruno de Oliveira

26

Jak wspomniano Pascal MARTIN, należy zamienić „\” na DIRECTORY_SEPARATOR, na przykład:

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

Sugerowałbym również przeorganizowanie struktury katalogu, aby kod był bardziej czytelny. To może być alternatywa:

Struktura katalogów:

ProjectRoot
 |- lib

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

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Utwórz podkatalog dla każdej zdefiniowanej przestrzeni nazw.

Plik: /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();
  • Użyłem rekomendacji php 5 do deklaracji autoloadera. Jeśli nadal korzystasz z PHP 4, zamień go na starą składnię: function __autoload ($ class)

18

Twoja __autoloadfunkcja otrzyma pełną nazwę klasy, w tym nazwę przestrzeni nazw.

Oznacza to, że w Twoim przypadku __autoloadfunkcja otrzyma „ Person\Barnes\David\Class1”, a nie tylko „ Class1”.

Więc musisz zmodyfikować swój kod do automatycznego ładowania, aby poradzić sobie z tego rodzaju „bardziej skomplikowaną” nazwą; często używanym rozwiązaniem jest organizowanie plików przy użyciu jednego poziomu katalogu na „poziom” przestrzeni nazw i podczas automatycznego ładowania zastąp „ \” w nazwie przestrzeni nazw znakiem DIRECTORY_SEPARATOR.


1
To nie jest to, co znalazłem. Kiedy umieściłem instrukcję die ($ class); w funkcji __autoload wypisał „Class1”, a nie „Person \ Barnes \ David \ Class1”
David Barnes

Prawdziwe. Parametr $ class autoloadu to nazwa klasy zapisana w wywołaniu konstruktora.
tishma

1
Głosuj przeciw „Twoja __autoloadfunkcja otrzyma pełną nazwę klasy, w tym nazwę przestrzeni nazw” - jest to prawdą tylko wtedy, gdy masz jawnie usedaną klasę, do której próbujesz się odwołać, a nie, jeśli po prostu usedałeś przestrzeń nazw, do której należy. Błąd OP polegał na tym, że useutworzył przestrzeń nazw zawierającą klasę, a następnie spodziewał się, że jego funkcja automatycznego ładowania w magiczny sposób przejdzie przez pełną ścieżkę klas. Ta odpowiedź tak naprawdę nie odnosi się do błędu PO.
Mark Amery,

15

Robię coś takiego: Zobacz ten przykład GitHub

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___
}

3
Ładnie i prosto. Gdyby tego szukać.)
dennis

3

Znalazłem ten klejnot od Flysystem

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);
    }
});

3

Widzę, że funkcje automatycznego ładowania otrzymują „pełną” nazwę klasy - ze wszystkimi przestrzeniami nazw ją poprzedzającymi - w dwóch następujących przypadkach:

[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();

Widzę, że funkcje automatycznego ładowania NIE otrzymują pełnej nazwy klasy w następującym przypadku:

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

AKTUALIZACJA: [c] jest błędem, a zresztą tak nie jest, jak działają przestrzenie nazw. Mogę zgłosić, że zamiast [c], następujące dwa przypadki również działają dobrze:

[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();

Mam nadzieję że to pomoże.


Na marginesie, usesłowo kluczowe nie działa poprawnie w interaktywnym interfejsie wiersza poleceń PHP ( php --interactive);
Andrew Larsson

3

Używam tego prostego hacka w jednej linii:

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

1

miał ten sam problem i właśnie znalazłem to:

Kiedy tworzysz strukturę podfolderu pasującą do przestrzeni nazw klas zawierających ją, nigdy nie będziesz musiał nawet definiować autoloadera.

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

Zadziałało jak urok

Więcej informacji tutaj: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDYCJA: powoduje to problem w Linuksie z powodu odwrotnego ukośnika ... Zobacz tutaj działające rozwiązanie immeëmosol

Namespace Autoload działa w systemie Windows, ale nie w systemie Linux


1

Używanie ma problem, chociaż jest to zdecydowanie najszybsza metoda, oczekuje również, że wszystkie nazwy plików będą pisane małymi literami.

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

Na przykład:

Plik zawierający klasę SomeSuperClass musiałby nazywać się somesuperclass.php, jest to problem w przypadku korzystania z systemu plików z rozróżnianiem wielkości liter, takiego jak Linux, jeśli plik nosi nazwę SomeSuperClass.php, ale nie jest to problem w systemie Windows.

Używanie __autoload w kodzie może nadal działać z aktualnymi wersjami PHP, ale spodziewaj się, że ta funkcja zostanie wycofana i ostatecznie usunięta w przyszłości.

Więc jakie opcje zostały:

Ta wersja będzie działać z PHP 5.3 i nowszymi wersjami i pozwala na używanie nazw plików SomeSuperClass.php i somesuperclass.php. Jeśli używasz wersji 5.3.2 i nowszych, ten autoloader będzie działał jeszcze szybciej.

<?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;
});

2
na marginesie, str_replace ([ '_','\\'] '/', $className );jest dwa razy szybsze niż dwa str_replace
Itay Moav -Malimovka

O ile nie ma znaczenia, czy plik php jest pisany dużymi / małymi literami, katalogi nadal uwzględniają wielkość liter
Mike

1

Niedawno uznałam, że odpowiedź Tanerkuca jest bardzo pomocna! Chciałem tylko dodać, że użycie strrpos()+ substr()jest nieco szybsze niż explode()+ end():

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

1

Dorzucę moje dwa centy dla względnie początkujących lub czegokolwiek, kto chce prostej konfiguracji spl_autoload_register () bez całej teorii: po prostu utwórz jeden plik php dla każdej klasy, nazwij ten plik php tak samo jak nazwa Twojej klasy i zachowaj pliki swoich klas w tym samym katalogu co dany plik php, to zadziała:

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

Wyszukiwanie elementów w tej funkcji powinno odpowiedzieć na pytanie, jak to działa. PS: Używam Linuksa, a to działa na Linuksie. Użytkownicy systemu Windows powinni najpierw to przetestować.


1

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

Będziesz chciał umieścić swoje pliki klas w folderze o nazwie Classes, który znajduje się w tym samym katalogu, co punkt wejścia do aplikacji PHP. Jeśli klasy używają przestrzeni nazw, przestrzenie nazw zostaną przekonwertowane na strukturę katalogów.

W przeciwieństwie do wielu innych programów ładujących, podkreślenia nie będą konwertowane na struktury katalogów (trudno jest zrobić pseudo-przestrzenie nazw PHP <5.3 razem z rzeczywistymi przestrzeniami nazw PHP> = 5.3).

<?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');

Będziesz chciał umieścić następujący kod w swoim głównym skrypcie PHP (punkcie wejścia):

require_once("Classes/Autoloader.php");

Oto przykładowy układ katalogu:

index.php
Classes/
  Autoloader.php
  ClassA.php - class ClassA {}
  ClassB.php - class ClassB {}
  Business/
    ClassC.php - namespace Business; classC {}
    Deeper/
      ClassD.php - namespace Business\Deeper; classD {}

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();
}
?>

1
Chociaż ten kod może odpowiedzieć na pytanie, zapewnia dodatkowy kontekst dotyczący tego, dlaczego i / lub jak ten kod odpowiada, poprawia jego długoterminową wartość.
Ethan
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.