Как автоматически загружать классы PHP?

Я использую классы с Wordpress и пытаюсь автоматически загрузить их в свой файл functions.php:

spl_autoload_register(function($class) {
    include('classes/'.$class.'.php');
});

Вот как выглядит мой каталог классов:

  • classes/
    • project/
      • Application.php
      • Core.php
      • Сайт.php
      • Helpers.php
    • utils/
      • Helpers.php
      • Twig.php
    • views/
      • Layout.php
      • Модули.php
      • pages/
        • Home.php

Каждый класс имеет пространство имен в зависимости от каталога, в котором он находится. Например:

$homeClass = new \views\pages\Home();

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

Предупреждение PHP: include(classes/project\Application.php): не удалось открыть поток: нет такого файла или каталога

Очевидно, что обратная косая черта, являющаяся частью пространства имен, не работает в пути. Я мог бы обновить свою функцию, чтобы заменить обратную косую черту прямой косой чертой следующим образом:

spl_autoload_register(function($class) {
    include('classes/'.str_replace("\\", "/", $class).'.php');
});

Но кажется странным, что это необходимо. Я что-то упускаю?


person Michael Lynch    schedule 07.07.2015    source источник
comment
Почему это кажется странным? Если это хост Unix/Linux, обратная косая черта не является разделителем каталогов, поэтому вы не можете использовать ее для разделения каталогов в пути.   -  person IMSoP    schedule 07.07.2015
comment
Мне это кажется странным, потому что spl_autoload_register() встроен в PHP, но требует дополнительной функции (str_replace()) для размещения пространств имен. Я бы предположил, что spl_autoload_register() сможет добиться этого самостоятельно.   -  person Michael Lynch    schedule 07.07.2015
comment
spl_autoload_register вообще ничего не знает о путях к файлам. Он даже не запускает include для вас, он просто дает вам полное имя класса, и вы можете делать все, что хотите, чтобы этот класс появился. Так что совсем не странно, что вам нужно манипулировать одной строкой (именем класса), чтобы превратить ее в другую строку (путь на диске в соответствии с вашим соглашением об именах).   -  person IMSoP    schedule 08.07.2015


Ответы (4)


У меня есть пример здесь.
По сути, это лучшая версия spl_autoload_register, поскольку она пытается запросить файл класса только всякий раз, когда вы инициализируете класс.
Здесь он автоматически получает каждый файл в папке вашего класса, требует файлы и инициализирует их. . Все, что вам нужно сделать, это назвать класс так же, как и файл.
index.php

<?php
require_once __DIR__ . '/app/autoload.php';

$loader = new Loader(false);

User::dump(['hello' => 'test']);

autoload.php

<?php
class Loader 
{

    public static $library;

    protected static $classPath = __DIR__ . "/classes/";

    protected static $interfacePath = __DIR__ . "/classes/interfaces/";

    public function __construct($requireInterface = true) 
    {
        if(!isset(static::$library)) {
            // Get all files inside the class folder
            foreach(array_map('basename', glob(static::$classPath . "*.php", GLOB_BRACE)) as $classExt) {
                // Make sure the class is not already declared
                if(!in_array($classExt, get_declared_classes())) {
                    // Get rid of php extension easily without pathinfo
                    $classNoExt = substr($classExt, 0, -4); 
                    $file = static::$path . $classExt;

                    if($requireInterface) {
                        // Get interface file
                        $interface = static::$interfacePath . $classExt;
                        // Check if interface file exists
                        if(!file_exists($interface)) {
                            // Throw exception
                            die("Unable to load interface file: " . $interface);
                        }

                        // Require interface
                        require_once $interface;
                        //Check if interface is set
                        if(!interface_exists("Interface" . $classNoExt)) {
                            // Throw exception
                            die("Unable to find interface: " . $interface);
                        }
                    }

                    // Require class
                    require_once $file;
                    // Check if class file exists
                    if(class_exists($classNoExt)) {
                        // Set class        // class.container.php
                        static::$library[$classNoExt] = new $classNoExt();
                    } else {
                        // Throw error
                        die("Unable to load class: " . $classNoExt);
                    }

                }
            }
        }
    }

    /*public function get($class) 
    {
        return (in_array($class, get_declared_classes()) ? static::$library[$class] : die("Class <b>{$class}</b> doesn't exist."));
    }*/
}

Вы можете легко справиться с небольшим количеством кода, чтобы также требовать классы в разных папках.
Надеюсь, это может быть вам полезно.

person anttwuan    schedule 17.02.2017

В этом нет ничего странного!

Регистрация ваших автозагрузчиков с использованием пространств имен в качестве средства обозначения структуры каталогов является довольно распространенной практикой. Я бы рекомендовал следовать соглашениям PSR-4, но если вы слишком Чтобы реорганизовать ваши соглашения об именах, я бы рекомендовал использовать константу DIRECTORY_SEPARATOR, чтобы помочь PHP решить, использовать ли '/' или '\', поскольку серверы Windows используют последнее, а серверы Linux используют первое.

person maiorano84    schedule 07.07.2015

Я сделал класс для этого требования, совместимый с PSR-4.

Вы можете найти его здесь: https://github.com/pablo-pacheco/wp-namespace-autoloader

Объяснение есть, но в основном это зависимость от композитора. Вам просто нужно потребовать это в своем проекте:

"require": {    
    "pablo-pacheco/wp-namespace-autoloader": "dev-master"
}

А затем вызовите класс

<?php
new \WP_Namespace_Autoloader( array(    
    'directory'   => __DIR__,       // Directory of your project. It can be your theme or plugin. __DIR__ is probably your best bet.    
    'namespace'   => __NAMESPACE__, // Main namespace of your project. E.g My_Project\Admin\Tests should be My_Project. Probably if you just pass the constant __NAMESPACE__ it should work     
    'classes_dir' => 'src',         // (optional). It is where your namespaced classes are located inside your project. If your classes are in the root level, leave this empty. If they are located on 'src' folder, write 'src' here 
) );

Я ценю любую обратную связь!

person Pablo S G Pacheco    schedule 17.02.2017

Попробуйте этот класс, он автоматически загружает с пространствами имен или без них

person Community    schedule 16.08.2019