Пользовательская автозагрузка конфликтует с автозагрузкой Composer?

У меня есть проект PHP, который загружает загрузочный файл с именем custom_funcs.php, который находится в корневом веб-каталоге. Этот файл содержит кучу функций, определяет несколько констант и выполняет следующие действия:

    require dirname( __DIR__ ) . '/lib/php/vendor/autoload.php';
    spl_autoload_register( function ($class_name) { include __DIR__ ."/classes/$class_name.php"; });
    set_include_path( get_include_path() . PATH_SEPARATOR . SITEROOT );

Эта первая строка загружает автозагрузчик Composer. (Обратите внимание, что библиотеки композитора расположены вне корневого веб-каталога). Вторая строка сообщает моему коду, где искать нераспознанные классы, чтобы мне не приходилось постоянно вручную загружать какой-либо файл класса, который я когда-либо использовал. Третья строка добавляет веб-корень в PATH PHP.

Это прекрасно работает вместе с несколькими пакетами Composer уже пару лет.

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

  1. Если ошибок нет, то работает нормально.
  2. Если есть ошибка, я также получаю предупреждения PHP:
Warning: include(C:\...path_to_web_root.../classes/SebastianBergmann\Invoker\Invoker.php): failed to open stream: No such file or directory in C:\...path_to_web_root...\common_funcs.php on line 14
Warning: include(): Failed opening 'C:\...path_to_web_root.../classes/SebastianBergmann\Invoker\Invoker.php' for inclusion (include_path='xxxxxxx') in C:\...path_to_web_root...\common_funcs.php on line 14

Итак, PhpUnit пытается автоматически загрузить класс Invoker, но код использует мой собственный путь автозагрузки, заданный spl_autoload_register.

Есть ли способ исправить это? Это ошибка в PhpUnit?

Я могу скрыть ошибку, используя «@» перед строкой включения, но я стараюсь любой ценой избегать @hiding ошибок

Примечание. Я загружаю custom_funcs.php перед тестированием через:

    public static function setUpBeforeClass(): void {
        require 'common_funcs.php';
    }

Структура файла может сделать это более понятным:

c:/some_path/
..lib/
....php/
......vendor/
........(third-party Composer libraries)
..webroot/
....custom_funcs.php
....classes/
......(namespace)/
........(my custom classes)

Я использую пространство имен: MyCompany\Portal. Итак, мои пользовательские классы находятся в <webroot>/classes/MyCompany/Portal/


person Stephen R    schedule 26.07.2019    source источник
comment
Сделайте свой custom_func классом и добавьте его в автозагрузчик PSR4. См. getcomposer.org/doc/01-basic-usage.md#autoloading   -  person paskl    schedule 26.07.2019
comment
Это очень похоже на переписывание кодовой базы ввода. За несколькими заметными (и недавними) исключениями, это устаревшая система, которая полностью процедурна. custom_funcs.php — это не что иное, как связный осмысленный класс Object — это набор многократно используемых функций, которые я использую на всем сайте.   -  person Stephen R    schedule 26.07.2019
comment
Вторая строка сообщает моему коду, где искать нераспознанные классы, чтобы мне не приходилось постоянно вручную загружать какой-либо файл класса, который я когда-либо использовал. это именно то, для чего нужен композитор. Я не совсем понимаю вопрос здесь? Добавьте свои собственные классы в автозагрузчик композитора и удалите эту строку.   -  person paskl    schedule 26.07.2019
comment
Если есть способ заменить мой вызов spl_autoload_register автозагрузчиком Composer, я открыт для этой идеи, но понимаю, что Composer и пользовательские классы находятся в двух разных местах. Или вы имеете в виду, что я должен взять свои пользовательские классы и вставить их в папку поставщика Composer?   -  person Stephen R    schedule 26.07.2019
comment
Используете ли вы пространства имен в своих собственных файлах классов? Если да, вы можете указать композитору, где найти эти пространства имен. (см. ссылку из моего первого комментария) Они могут сидеть где угодно (при условии, что они читабельны).   -  person paskl    schedule 26.07.2019
comment
уточнение пространства имен добавлено в нижнюю часть OP   -  person Stephen R    schedule 29.07.2019


Ответы (2)


Возможно, вы сможете заменить свою пользовательскую функцию автозагрузчика classmap автозагрузчиком в вашем composer.json:

{
    "autoload": {
        "classmap": ["classes/"],
        "files": ["custom_funcs.php"]
    }
}

Эта карта создается путем сканирования классов во всех файлах .php и .inc в заданных каталогах/файлах.

Всякий раз, когда вы создаете новый класс, вам может потребоваться обновить автозагрузчик композитора, используя composer dump-autoload, чтобы он был выбран. Я не уверен, что это так по умолчанию или только при оптимизации автозагрузчика. В любом случае это можно решить, приняв соглашения об именах PSR-0 или PSR-4 для новых классов.

редактировать: поскольку все файлы будут автоматически загружаться для вас с помощью автозагрузчика композитора, больше не нужно require их вручную. Поэтому вы также можете удалить setupBeforeClass-метод

person dbrumann    schedule 27.07.2019
comment
Я пробовал это, но мне нужно знать, к чему относится карта классов и пути к файлам. Корень сайта? composer.json? phpunit.xml? Каталог с исполняемым файлом phpunit? Не удалось заставить его работать, и каждый пример, который я могу найти в Интернете, предполагает очень специфическую структуру каталогов. - person Stephen R; 29.07.2019
comment
Примечание. Я не могу удалить метод setupBeforeClass, потому что он загружает файл с функциями, которые я тестирую в первую очередь. Но я мог бы удалить вызов класса вручную из этого файла (common_funcs.php). - person Stephen R; 29.07.2019
comment
Пути должны указываться относительно вашего composer.json, который в большинстве случаев совпадает с корнем проекта (не корнем сайта). Так что, возможно, вам придется делать webroot/classes вместо занятий. Вы можете выполнить composer dump-autoload -vvv, что даст вам подробный вывод, какие классы он собрал. - person dbrumann; 29.07.2019
comment
dump-autoload стал решающим фактором. Кажется, теперь все работает. Я смог удалить руководство spl_autoload_register; хотя я оставил файл common_funcs, потому что в нем есть мои стандартные функции. Спасибо - person Stephen R; 31.07.2019
comment
Обратите внимание, что вам нужно будет запускать composer dump-autoload каждый раз, когда вы переименовываете или добавляете новый класс. Использование classmap может подойти для устаревшей кодовой базы, но если это живая вещь, вам лучше использовать psr-4 вместо этого. - person rob006; 31.07.2019

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

spl_autoload_register(function ($class_name) {
    if (file_exists(__DIR__ . "/classes/$class_name.php")) {
        include __DIR__ . "/classes/$class_name.php";
        return true;
    }
});
person rob006    schedule 27.07.2019
comment
Элегантный и простой обходной путь, но я выбрал тот, который правильно заставляет все работать - person Stephen R; 31.07.2019
comment
@StephenR Это не обходной путь, в этом случае вы должны реализовать свой собственный автозагрузчик. Хотя использование автозагрузчика композитора может быть более простым и эффективным решением, могут быть случаи, когда вам может потребоваться собственная реализация, и вы не можете использовать автозагрузчик из композитора. - person rob006; 31.07.2019
comment
Хорошая точка зрения. Я действительно реализовал оба предложения, так что спасибо. - person Stephen R; 31.07.2019
comment
@ rob006 хорошо! Как насчет: if (is_readable(__DIR__ . "/classes/$class_name.php")) ? - person LanreSmith; 08.12.2020