Как использовать пространства имен PHP с автозагрузкой?

104

Я получаю эту ошибку, когда пытаюсь использовать автозагрузку и пространства имен:

Неустранимая ошибка: класс Class1 не найден в /usr/local/www/apache22/data/public/php5.3/test.php в строке 10

Может ли кто-нибудь сказать мне, что я делаю не так?

Вот мой код:

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

?>
Дэвид Барнс
источник

Ответы:

119

Class1 не входит в глобальную область видимости.

См. Ниже рабочий пример:

<?php

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

use Person\Barnes\David as MyPerson;

$class = new MyPerson\Class1();

Изменить (2009-12-14):

Чтобы уточнить, я использовал «использовать ... как» для упрощения примера.

Альтернатива была следующая:

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

или

use Person\Barnes\David\Class1;

// ...

$class = new Class1();
Танеркай
источник
1
Вам не нужно использовать AS. Это решение работает не поэтому. Вы можете так же легко сделать: use Person\Barnes\David\Class1;(что эквивалентно use Person\Barnes\David\Class1 as Class1;).
cartbeforehorse 02
1
Спасибо, это работает. Но я не могу понять, почему мы можем просто использовать $ class = new Class1 (); когда мы уже определили "use Person \ Barnes \ David;" раньше?
user345602
4
@ user346665 вы должны использовать use Person\Barnes\David\Class1;для этого $class = new Class1();. С use Person\Barnes\David;тобой надо делать $class = new David\Class1();. Само useпо себе ключевое слово является эквивалентом use Person\Barnes\David\Class1 as Class1;или use Person\Barnes\David as David;соответственно для каждого примера.
Justin C,
Для тех, кто читает в 2018 году, используйте решение @ prince-billy-graham с spl_autoload_register
Бруно де Оливейра
26

Как упоминал Паскаль МАРТИН, вы должны заменить '\' на DIRECTORY_SEPARATOR, например:

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

Также я бы посоветовал вам реорганизовать структуру каталога, чтобы сделать код более читабельным. Это может быть альтернатива:

Структура каталога:

ProjectRoot
 |- lib

Файл: /ProjectRoot/lib/Person/Barnes/David/Class1.php

<?php
namespace Person\Barnes\David
class Class1
{
    public function __construct()
    {
        echo __CLASS__;
    }
}
?>
  • Создайте подкаталог для каждого определенного вами пространства имен.

Файл: /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();
  • Я использовал рекомендацию php 5 для объявления автозагрузчика. Если вы все еще используете PHP 4, замените его старым синтаксисом: function __autoload ($ class)
Костанос
источник
18

Ваша __autoloadфункция получит полное имя класса, включая имя пространства имен.

Это означает, что в вашем случае __autoloadфункция получит ' Person\Barnes\David\Class1', и не только ' Class1'.

Итак, вы должны изменить свой код автозагрузки, чтобы иметь дело с таким «более сложным» именем; часто используемое решение - организовать ваши файлы, используя один уровень каталогов для каждого «уровня» пространств имен, и при автозагрузке заменить ' \' в имени пространства имен на DIRECTORY_SEPARATOR.

Паскаль МАРТИН
источник
1
Это не то, что я нашел. Когда я поставил выражение die ($ class); в функции __autoload он распечатал «Class1», а не «Person \ Barnes \ David \ Class1»
Дэвид Барнс
Правда. Параметр $ class автозагрузки - это имя класса, записанное при вызове конструктора.
tishma 06
1
Голосовать против «Ваша __autoloadфункция получит полное имя класса, включая имя пространства имен» - это верно только в том случае, если вы явно useуказали класс, на который пытаетесь сослаться, а не если вы просто указали useпространство имен, которому он принадлежит. Ошибка OP заключалась в том, что он создал useпространство имен, содержащее класс, а затем ожидал, что его функция автозагрузки каким-то образом волшебным образом передаст полный путь к классам. Этот ответ на самом деле не касается ошибки OP.
Марк Эмери
15

Я делаю что-то вроде этого: см. Этот пример на 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
Красиво и просто. Если кто-то должен это искать.)
Деннис
3

Я нашел эту жемчужину от 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

Я вижу, что функции автозагрузки получают «полное» имя класса со всеми предшествующими ему пространствами имен только в следующих двух случаях:

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

Я вижу, что функции автозагрузки НЕ получают полное имя класса в следующем случае:

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

ОБНОВЛЕНИЕ: [c] - это ошибка, и в любом случае это не так, как работают пространства имен. Я могу сообщить, что вместо [c] также хорошо работают следующие два случая:

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

Надеюсь это поможет.

Дэниел Родс
источник
Кстати, useключевое слово некорректно работает в интерактивном интерфейсе командной строки PHP ( php --interactive);
Эндрю Ларссон
3

Я использую этот простой прием в одну строчку:

spl_autoload_register(function($name){
        require_once 'lib/'.str_replace('\\','/',$name).'.php';
    });
princebillyGK
источник
1

была такая же проблема, и я только что нашел это:

Когда вы создаете структуру подпапок, соответствующую пространствам имен содержащихся классов, вам даже не придется определять автозагрузчик.

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

Оно работало завораживающе

Подробнее здесь: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

РЕДАКТИРОВАТЬ: это вызывает проблему в Linux из-за обратной косой черты ... См. Здесь рабочее решение от immeëmosol

Автозагрузка пространства имен работает под Windows, но не под Linux

JohnWolf
источник
1

Использование имеет подводный камень, хотя это, безусловно, самый быстрый метод, он также ожидает, что все ваши имена файлов будут в нижнем регистре.

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

Например:

Файл, содержащий класс SomeSuperClass, должен быть назван somesuperclass.php, это ошибка при использовании чувствительной к регистру файловой системы, такой как Linux, если ваш файл называется SomeSuperClass.php, но не проблема в Windows.

Использование __autoload в вашем коде может по-прежнему работать с текущими версиями PHP, но ожидается, что эта функция станет устаревшей и будет окончательно удалена в будущем.

Итак, какие варианты остались:

Эта версия будет работать с PHP 5.3 и выше и позволяет использовать имена файлов SomeSuperClass.php и somesuperclass.php. Если вы используете версию 5.3.2 и выше, этот автозагрузчик будет работать еще быстрее.

<?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
в качестве примечания, str_replace ([ '_','\\'] '/', $className );в два раза быстрее, чем два str_replace
Итай Моав -Малимовка
Пока не имеет значения, имеет ли файл php верхний / нижний регистр, каталоги по-прежнему чувствительны к регистру
Майк
1

Я недавно нашел ответ tanerkuc очень полезным! Просто хотел добавить, что использование strrpos()+ substr()немного быстрее, чем explode()+ end():

spl_autoload_register( function( $class ) {
    $pos = strrpos( $class, '\\' );
    include ( $pos === false ? $class : substr( $class, $pos + 1 ) ).'.php';
});
Алан
источник
1

Я добавлю свои два цента для относительных новичков или еще чего-то, кто не хочет простой установки spl_autoload_register () без всякой теории: просто создайте один файл php для каждого класса, назовите этот файл php таким же, как имя вашего класса, и сохраните файлы классов в том же каталоге, что и ваш файл php, тогда это будет работать:

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

Погуглите детали внутри этой функции, чтобы понять, как она работает. PS: Я использую Linux, и это работает в Linux. Пользователи Windows должны сначала это проверить.


источник
1

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

Вы захотите поместить файлы классов в папку с именем Classes , которая находится в том же каталоге, что и точка входа в ваше приложение PHP. Если классы используют пространства имен, они будут преобразованы в структуру каталогов.

В отличие от многих других автозагрузчиков, подчеркивания не будут преобразованы в структуры каталогов (сложно создать псевдопространства имен PHP <5.3 вместе с реальными пространствами имен 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');

Вы захотите поместить следующий код в свой основной скрипт PHP (точку входа):

require_once("Classes/Autoloader.php");

Вот пример макета каталога:

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
Хотя этот код может ответить на вопрос, предоставление дополнительного контекста относительно того, почему и / или как этот код отвечает на вопрос, улучшает его долгосрочную ценность.
Итан