Як використовувати простори імен 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

Клас1 не входить у світову сферу.

Нижче див. Робочий приклад:

<?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

1
Дякую, це працює. Але я не можу зрозуміти, чому ми можемо просто використовувати $ class = new Class1 (); коли ми вже визначили "використовувати Person \ Barnes \ David;" раніше?
користувач345602

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;, відповідно , для кожного прикладу.
Джастін С

Для тих, хто читає у 2018 році, використовуйте рішення @ Prince-billy-Graham з spl_autoload_register
Бруно де Олівейра,

26

Як згадував Pascal MARTIN, вам слід замінити "\" на DIRECTORY_SEPARATOR, наприклад:

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

Також я б запропонував вам реорганізувати структуру dirrectory, щоб зробити код читабельнішим. Це може бути альтернативою:

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

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, замініть його на старий синтаксис: функція __autoload ($ class)

18

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

Це означає, що у вашому випадку __autoloadфункція отримає ' Person\Barnes\David\Class1', а не тільки ' Class1'.

Отже, ви повинні змінити код автозавантаження, щоб мати справу з такою "більш складною" назвою; рішення, яке часто використовується, - це організувати свої файли, використовуючи один рівень каталогу на "рівень" просторів імен, а при автозавантаженні замінити " \" у назві простору імен на DIRECTORY_SEPARATOR.


1
Це не те, що я знайшов. Коли я поставив оператор die ($ class); у функції __autoload він надрукував "Class1" ", а не" Person \ Barnes \ David \ Class1 "
David Barnes

Правда. Параметр $ class автозавантаження - це ім'я класу, написане у виклику конструктора.
tishma

1
Downvote для "Ваша __autoloadфункція отримає повне ім'я класу, включаючи ім'я простору імен" - це справедливо лише в тому випадку, якщо ви чітко used класу, на який ви намагаєтеся посилатися, а не якщо ви просто used простору імен, до якого належить. Помилка OP полягала в тому, що він визначив useпростір імен, що містить клас, а потім очікував, що його функція автозавантаження магічним чином пройде повний класний шлях. Ця відповідь насправді не стосується помилки ОП.
Марк Амері

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
Приємно і просто. Якщо цього потрібно шукати.)
dennis

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

1

був той самий випуск, і щойно це було знайдено:

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

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

Це спрацювало як шарм

Більше інформації тут: http://www.php.net/manual/en/function.spl-autoload-register.php#92514

EDIT: це спричиняє проблеми в Linux через зворотну косу рису ... Дивіться тут робоче рішення від immeëmosol

Автоматичне завантаження простору імен працює під Windows, але не в Linux


1

Використання має "gotcha", хоча це далеко не найшвидший метод, але він також очікує, що всі ваші імена файлів будуть малими літерами.

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
Itay Moav -Malimovka,

Поки це не має значення, якщо файл 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
Хоча цей код може відповісти на питання, надаючи додатковий контекст стосовно того, чому та / або як цей код відповідає на питання, покращує його довгострокове значення.
Етан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.