Недавно я начал разрабатывать очередной php проект, который использует множество классов, так как полностью построен на ООП. Следовательно я решил использовать автозагрузчик классов, однако мне нужен был такой, который мог бы найти любой класс в проекте, и при этом не слишком нагружал систему. Был написан следующий код:
<?php

class autoload {
    
    //Список директорий, которые следует исключить из поиска
    private static $exception_list = [];
   
   //Список ранее найденных директорий
    private static $founded_list = [];
   

    public static function search($dir, $file_to_search) {
       //Если попали на директорию, исключенную из поиска, или уже подключали требуемый класс то игнорируем ее
        if(in_array($dir, static::$exception_list) || isset(static::$founded_list[$file_to_search])) {
            return;
        }
        //Сканируем текущую директорию в поисках класса
        $scan = glob("$dir/*");
        foreach ($scan as $path) {
            if (preg_match('/\.php$/', $path) && is_file($path)) {
             //Если нашли, то "запоминаем" директорию и подключаем файл
                static::$founded_list[basename($path)] = $path;
                if(basename($path) == $file_to_search) {
                    include_once $path;
                    return;
                }
            }
            elseif (is_dir($path)) {
                self::search($path, $file_to_search);
            }
        }
    }
}

function __autoload(string $class){
    Autoload::search(ROOT, $class . '.php');
}

Надеюсь кому-нибудь пригодится данный код в его работе.

Комментарии (17)


  1. NorthDakota
    17.09.2018 11:18
    +4

    Бегите…
    Просто оставлю эти слова здесь
    2018, composer, deprecated, psr-0, psr-1, psr-2, psr-4


    1. Raserad Автор
      17.09.2018 11:32
      -2

      Хорошо, пожалуйста, перечислите здесь нарушения данных стандартов, чтобы я знал как писать код в будущем, и чтобы другие тоже видели.
      А так же насчет composer. Я понимаю, что его использует сейчас огромное количество современных проектов, однако, как ни странно, он не на всех серверах есть, и мне кажется что гораздо проще написать один маленький класс, который автоматически будет находить нужные классы и подгружать их, чем тащить composer. Но все зависит от конкретных требований проекта))


      1. NorthDakota
        17.09.2018 11:53
        +3

        Хорошо. немного конструктива
        1. Магический метод __autoload уже давно никто не использует, так же он обьявлен deprecated начиная с версии 7.2
        Вместо этого используйте spl_autoload_register

        2. Статика, старайтесь избегать использование статики, ибо она оправдана в очень редких и специфичных местах, если же вы хотели добится чего то типа «кеша» то нужно курить в сторону Singleton

        3. Излишние коментарии. Вместо того чтобы комментировать каждую строчку комментарием «это код», «это переменная», постарайтесь придумать название для переменной/метода чтобы название говорило всё о нём.
        Например exception_list — во первых list явно лишнее, так как видно что это массив, приходим логически к exceptions — сбивает с толку, кажется что это будет массив пхп исключений. Заменим на excluded, уже более нормально но нужно добавить ещё что именно excluded.
        excludedFolders, excluededDirs — уже более подходящие названия

        4. По качеству кода ещё очень много мелких замечаний, лутше перейду к psr'ам
        psr-0, psr-4 собственно описывают как написать autoload, обязательно к прочтению
        psr-1, psr-2 стиль кода собственно, старайтесь использовать camelCase и тд (то что укололо глаз)

        Ну и напоследок, если взялись что то делать, проверьте, поищите как это сделано было уже до вас. Например glob и рекурсивность можно с легкостью заменить использованием RecursiveDirectoryIterator. Придумывать свой велосипед это весело и полезно, но юзать в продакшене очень рисковано


        1. NorthDakota
          17.09.2018 12:00
          +1

          А ну и про композер забыл,
          Его не дураки писали, и я на 99% уверен что его автозагрузка будет на 99% быстрее и «рекурсивней» вашего подхода,
          По поводу того что его нету на всех серверах. Композер — это пхп апликейшн, и поставить его даже не имея рут прав раз плюнуть


          PS. Только заметил что ваш скрипт загружает все файлы, даже если они не используются в текущем реквесте, так что ваш автозагрузчик очеееень медленный


          1. m03r
            17.09.2018 12:06

            Да вроде бы нет — include происходит только при совпадении basename.


            1. NorthDakota
              17.09.2018 12:14

              Я немного о другом. Если в проекте 100500 файлов, а при реквесте на один контроллер реально будет использоваться только 10 классов, то в память будут загружены все 100500 вместо 10. Но фиг с ней с памятью, а вот диск при чтении этих 100500 файлов немножко будет напрягаться.

              Также благодаря этому basename если у нас будет несколько класов с одинаковыми названиеми но разными неймспейсами то последний не будет загружён


              1. m03r
                17.09.2018 12:18

                Да вроде бы не будет — каждый раз читаются только оглавления директорий (впрочем, смотрю код с телефона и могу чего-то не заметить). А вот с одинаковыми названиями возможны хитрые глюки, когда в разных средах порядок перечисления будет разный.


      1. m03r
        17.09.2018 11:58
        +1

        По PSR-4 классы раскладываются по директориям в соответствии со своими неймспейсами. Поэтому вычисление имени файла и пути к нему по классу производится простой заменой. А у вас каждый раз при поиске файла происходит glob по всем директориям, что может быть весьма небыстро. Кроме этого, держать в памяти список найденных файлов бессмысленно: автозагрузка работает один раз для каждого класса.


        В вашем случае (как я понимаю, просто классы по файлам в разных директориях) эффективнее всего просто заранее проходить по всем файлам и составлять кеш соответствий в виде статического массива в данном классе: он ещё и ляжет в опкеш и не будет перечитываться с диска каждый раз. Композер, собственно, ровно так и делает в одном из вариантов.


        И да, код оформлен не по PSR-2.


      1. PaulZi
        17.09.2018 11:59
        +1

        в чём сложности с установкой composer?


        1. m03r
          17.09.2018 12:03
          +1

          Я бы не стал ставить composer только для автозагрузки. Всё-таки в первую очередь это менеджер пакетов, а автозагрузка пользовательских классов — так, приятный бонус.


          1. PaulZi
            17.09.2018 12:08

            А что вас пугает? Используйте только `composer dumpautoload`, это лучше чем такие лесопеды писать.


            1. m03r
              17.09.2018 12:15

              Тянуть большие зависимости ради маленького кусочка функционала редко оправданно, и для этого всегда нужны серьёзные обоснования. А лесапед для PSR-4 пишется в несколько строк, и его сложно написать плохо.


      1. dumistoklus
        17.09.2018 12:01
        +1

        Если composer нет на сервере, значит можно установить локально и залить проект на сервер вместе с папкой vendor, где и лежит загрузчик классов.

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


  1. FanatPHP
    17.09.2018 11:52
    +3

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


    И неймспейсы, как я понимаю, в проекте не используются?


  1. peresada
    17.09.2018 12:07

    Заминусуют еще сильнее, даже если бы код был идеальным и работал бы он быстрее стандартных автозагрузчиков, все равно простая публикация кода на хабре не приветствуется — используйте для этого gist в github'е


  1. AlexLeonov
    17.09.2018 12:12

    Напишите на доске сто раз слово «PSR»


  1. Raserad Автор
    17.09.2018 12:37

    Хорошо, все понятно)) Для этого я и опубликовал здесь данный код, чтобы мне указали на ошибки. Этого я и хотел — критики)) Так что всем спасибо.))))