Битва стандартов: PSR-0 против PSR-4

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

PSR-0

PSR-0 смотрит на пространств имен класса и на его основе генерирует путь к файлу. Например, класс \Zend\Mail\Message соответствует файлу /path/to/project/lib/vendor/Zend/Mail/Message.php.

PSR-0 также поддерживает символы подчеркивания в имени класса в качестве альтернативы неймспейсам для версий PHP < 5.3. Таким образом класс Zend_Mail_Message так же будет загружен из /path/to/project/lib/vendor/Zend/Mail/Message.php.

Composer

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

vendor
└── vendor_name
    └── package_name
        ├── src
        │   └── Vendor_Name
        │       └── Package_Name
        │           └── ClassName.php  # Vendor_Name\Package_Name\ClassName
        └── test
            └── Vendor_Name
                └── Package_Name
                    └── ClassNameTest.php  # Vendor_Name\Package_Name\ClassNameTest

Это в лучшем случае бардак и избыточность. Директории src и test обязательно должны содержать директорию с именем вендора и названием пакета. По этой причине некоторые головастые PHP разработчики собрались и написали новый стандарт, который должен решить проблему.

PSR-4

PSR-4 разрабатывался с расчетом возможности работы вместе с PSR-0, если это необходимо, не заменяя его. Он конечно может, но не обязан. Основная цель PSR-4 это выбросить устаревшие решения, использовавшиеся до выхода PHP 5.3 и построить более простую структуру директорий пакета. С PSR-4, описанная выше структура пакета будет выглядеть так:

vendor
└── vendor_name
    └── package_name
        ├── src
        │   └── ClassName.php  # Vendor_Name\Package_Name\ClassName
        └── tests
            └── ClassNameTest.php  # Vendor_Name\Package_Name\ClassNameTest

Выбранный подход

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

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

Кроме того, стандарт явно заявляет, что автозагрузчик PSR-4 никогда не должен выбрасывать исключения и вызывать ошибки просто потому, что в проекте может быть зарегистрировано несколько автозагрузчиков. Если не удалось загрузить класс одним загрузчиком, нужно оставить возможность сделать это другим. Остановка процесса при исключении в PSR-0 доставляло неприятности. Если же требуется сбор информации о сбое, следует использовать PSR-3 совместимый логгер или любые другие средства.

Согласно примеру, загрузка классов с помощью PSR-4 из следующей структуры:

/path/to/packages/foo-bar/
├── src
│   ├── Baz.php  # Foo\Bar\Baz
│   └── Qux
│       └── Quux.php  # Foo\Bar\Qux\Quux
└── test
    ├── BazTest.php  # Foo\Bar\BazTest
    └── Qux
        └── Quuxtest.php  # Foo\Bar\Qux\QuuxTest

будет выглядеть так:

// instantiate the loader
$loader = new \Example\Psr4AutoloaderClass;

// register the autoloader
$loader->register();

// register the base directories for the namespace    prefix
$loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/src');
$loader->addNamespace('Foo\Bar', '/path/to/packages/foo-bar/tests');

При вызове new \Foo\Bar\Qux\Quux; класс будет загружен из первого зарегистрированного каталога, в то время как new Foo\Bar\Qux\QuuxTest; будет загружен из второго.

Заключение

Ни один способ не является серебряной пулей в вопросах автозагрузки. Каждый подход имеет свои плюсы и минусы. PSR-4 использует более простую структуру каталогов, но иногда не совсем очевидную, например, мы не сможем узнать точный путь к файлу, просто взглянув на имя класса. PSR-0 в свою очередь создает избыточную, но при этом очевидную структуру директорий. Он так же поддерживает старые добрые имена классов с подчеркиваниями, за что любим некоторыми разработчиками, застрявшими на старых версиях PHP.

Источник: http://www.sitepoint.com/battle-autoloaders-psr-0-vs-psr-4/