Использование Composer для генерации автозагрузчика, поддерживающего legacy-код

    В настоящий момент во многих проектах с богатым наследием код представляет собой смесь из различных подходов, от PHP 3 до PHP 5.

    В PHP 3 мы использовали библиотеки функций, файлы подключения к базе данных, файлы переопределения настроек php.ini и так далее.

    В PHP 4 и 5 мы создавали классы, именование файлов которых было сильно кастомизировано, от package/class.myclass.inc до Package/MyClass.php.

    В эпоху PHP 5 мы получили стандарт PSR-0, описывающий стандарт именования файлов классов PHP для повышения совместимости их автозагрузки.

    Код постепенно начал превращаться в не очень приятное месиво из require_once и spl_autoload_register. Прямо скажем, месиво не сильно управляемое и время от времени приводящее к фатальным ошибкам «функция не существует» или «класс не существует» при разработке нового кода.

    И вот, внезапно к нам на помощь пришёл менеджер зависимостей Composer (github-репозиторий).

    Хочу подчеркнуть, что Composer не является только лишь генератором автозагрузчика. Задачи, которые он выполняет намного более широки и интересны.

    Давайте посмотрим, как мы можем применить Composer для генерации автозагрузчика для нашего legacy-кода.

    Компоненты Composer'а


    Composer состоит из PHP-пакета composer.phar и json-схемы composer.json (помещается в ту же папку, в которой находится composer.phar).

    Схема, описываемая в composer.json, имеет несколько разделов. Нас же интересует только раздел «autoload».

    Раздел autoload схемы composer.json


    Раздел может включать один или несколько подразделов: psr-0, classmap, files — и отвечает за генерацию автозагрузчика описанных в подразделе классов и файлов.

    Подраздел psr-0


    Подраздел psr-0 отвечает за добавление в автозагрузчик пакетов, именование в которых удовлетворяет требованиям стандарта PSR-0.

    Подраздел представляет собой объект, свойствами которого является имя пакета или поставщика, а значением свойств — директория, в которой находится директория пакета или директория поставщика с поддиректориями пакетов этого поставщика (см. PSR-0).

    Предположим, мы имеем поставщика MyVendor и отдельный пакет MyPackage:
    composer.json
    composer.phar
    lib/
        MyVendor/
    includes/
        MyPackage/
    

    Для генерации автозагрузчика, нам необходимо добавить следующий код в composer.json:
    {
        "autoload": {
            "psr-0": {
                "MyVendor": "lib/",
                "MyPackage": "includes/"
            }
        }
    }
    

    Подраздел classmap


    Подраздел classmap отвечает за описание файлов классов, именование которых не соответствует стандарту PSR-0.

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

    Существует возможность описания отдельного файла класса и описания директории для последующего создания описания всех файлов классов, находящихся в директории (рекурсивно).

    Предположим, у нас есть директория MyClasses с набором классов с различным именованием и файл class.myclass.php, содержащий какой-то отдельный класс, который нам необходимо загружать:
    composer.json
    composer.phar
    MyClasses/
    SomeDir/
        class.myclass.php
    

    Для генерации автозагрузчика, нам необходимо добавить следующий код в composer.json:
    {
        "autoload": {
            "classmap": [
                "MyClasses/",
                "SomeDir/class.myclass.php"
            ]
        }
    }
    

    Подраздел files


    Подраздел files отвечает за описание файлов, которые нам необходимо подключать в самом начале исполнения приложения.

    Подраздел представляет собой массив файлов, которые должны быть подключены.

    Предположим, что у нас есть файлы functions.php и db_connect.php, которые должны быть последовательно подключены перед исполнением приложения:
    composer.json
    composer.phar
    functions/
        functions.php
        db_connect.php
    

    Для генерации автозагрузчика, нам необходимо добавить следующий код в composer.json:
    {
        "autoload": {
            "files": [
                "functions/functions.php",
                "functions/db_connect.php"
            ]
        }
    }
    


    Собираем всё в кучу


    Давайте напишем файл, который позволит нам иметь автозагрузчик, обслуживающий все описанные выше ситуации:
    {
        "autoload": {
            "psr-0": {
                "MyVendor": "lib/",
                "MyPackage": "includes/"
            }, 
            "classmap": [
                "MyClasses/",
                "SomeDir/class.myclass.php"
            ],
            "files": [
                "functions/functions.php",
                "functions/db_connect.php"
            ]
        }
    }
    

    Генерируем автозагрузчик


    После того, как мы описали все необходимые нам пакеты и файлы, генерируем автозагрузчик:
    php composer.phar install
    

    Если всё прошло успешно, подключаем сгенерированный нами файл в bootstrap-файл (предполагаем, что bootstrap-файл лежит в той же директории, в которой лежит composer.phar) своего проекта:
    $loader = require_once __DIR__.'/vendor/autoload.php';
    

    Всё, требуемые файлы загружаются автоматически.

    Объект автозагрузчика, сохранённый в $loader, можем использовать для ручного добавления файлов и пакетов в автозагрузчик.

    Заключение


    Composer — это гораздо больше, чем просто генератор автозагрузчика. Рекомендую более пристально присмотреться к этому проекту.

    Всем удачи на пути к построению надёжных приложений, построенных на красивых решениях.
    • +11
    • 39,5k
    • 6
    Поделиться публикацией
    Похожие публикации
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее
    Реклама
    Комментарии 6
    • +3
      Статья похожа на краткий перевод документации с опущенными тонкими моментами, а как мы знаем, весь дьявол в деталях!

      Например, как с помощью инструкции autoload psr-0 подключить класс находящийся в глобальном неймспейсе?
      Можно ли указать одну директорию в которой искать все psr-0 пакеты, не прописывая каждый неймспейс в отдельности? Стоило упомянуть, что «MyVendor» и «MyPackage» (в ваших примерах) могут быть не только такими простыми, но и вложенными нейсмпейсами, например «Vendor\\Namespace». Наконец, самая крутая инструкция, без которой порой нельзя сделать правильный autoload: «target-dir» — это самое интересное!
      • 0
        Для этого есть документация и ссылки на неё в посте. Я всего лишь даю идею, как composer можно использовать для генерации автозагрузчика. Цели сделать перевод документаций я перед собой не ставил.

        target-dir прописывается в composer.json пакета, чтобы composer знал, куда его установить. В посте не рассказывается про установку пакетов и composer.json'ы отдельных пакетов.

        Вы пишите:
        Стоило упомянуть, что «MyVendor» и «MyPackage» (в ваших примерах) могут быть не только такими простыми, но и вложенными нейсмпейсами, например «Vendor\\Namespace»

        Я пишу:
        а значением свойств — директория, в которой находится директория пакета или директория поставщика с поддиректориями пакетов этого поставщика (см. PSR-0).

        Цитата из документации по PSR-0:
        A fully-qualified namespace and class must have the following structure \VendorName\(Namespace\)*Class Name

        Для использования подраздела PSR-0, конечно, нужно знать, что это такое. Это же касается и «классов в глобальном неймспейсе» (как я понимаю, под этим Вы подразумеваете именование классов с разделенем псевдо неймспейсов подчёркиванием). Цитата из документации по PSR-0:
        Each "_" character in the CLASS NAME is converted to a DIRECTORY_SEPARATOR. The "_" character has no special meaning in the namespace.
        • 0
          > Можно ли указать одну директорию в которой искать все psr-0 пакеты, не прописывая каждый неймспейс в отдельности?

          Да:

          {
              "autoload": {
                  "psr-0" {
                      "": "src"
                  }
              }
          }
          
          • 0
            Я как раз стараюсь этого избегать, т.к. для меня composer так же в каком-то смысле является высокоуровневой «картой» моего проекта — всегда видно, что мы используем, и всегда можно задать вопрос, нужно ли нам оно.
            • 0
              Это ответ на вопрос, а не лучшая практика :)
              • 0
                Да, я Вас понял. Просто уточняю для тех, кто будет читать Ваш комментарий. Вдруг им это поможет :)

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