Статья была написана во времена Zend Framework 1.6.0
Статья связана с Zend Framework (далее ZF). Но так же она будет полезна, если вы используете похожие с ZF соглашения по именованию классов.
Автору всегда нравилась в PHP5 возможность автозагрузки классов, поэтому в своих приложениях обычно использует ее.
Давайте посмотрим, что ZF предлагает по данному поводу.
Сначала заглянем в стандарты кодирования, B.3. Соглашения по именованию:
Zend Framework использует схему именования классов, в соответствии с которой имена классов напрямую указывают на директории, где они находятся. Корневой директорией Zend Framework'а является директория "Zend/", в которой иерархически расположены все классы.
Имена классов могут содержать только буквенно-числовые символы. Числа допустимы в именах классов, но не приветствуются. Символы нижнего подчеркивания допустимы в местах разделителей пути - имя файла "Zend/Db/Table.php" должно указывать на класс с именем "
Zend_Db_Table
".
Автору нравится это соглашение и он старается использовать его в своих приложениях (само соглашение не ново :). Приходится привыкать к длинным названиям классов. Используя такое соглашение проще искать классы и следить за их пространством имен (уникальности имени класса).
Теперь взглянем, что предлагает ZF по загрузке файлов Глава 26. Zend_Loader:
Класс Zend_Loader включает в себя методы, помогающие производить динамическую загрузку файлов.
Zend_Loader::loadClass('Container_Tree')
;Строка, задающая класс, преобразуется в относительный путь посредством замены знаков подчеркивания разделителями директорий и добавления расширения '.php' в конец. В примере выше '
Container_Tree
' преобразуется в 'Container/Tree.php'.
26.1.4. Использование автозагрузчика:
После регистрации метода обратного вызова автозагрузки вы можете ссылаться на классы из Zend Framework без их явной загрузки. Метод
autoload()
автоматически запускает методZend_Loader::loadClass()
, когда вы ссылаетесь на класс.
При этом сам фреймворк должен лежать в одной из папок указанных в include_path
.
Разработчики ZF оставляют право выбора, использовать автозагрузчик классов или нет, поэтому в самой библиотеке используется явно require_once
. Если с автозагрузкой классов из ZF все, в принципе, понятно, включаем описанный автозагрузчик и добавляем путь к ZF в include_path
, то со своими классами дело обстоит немного интереснее.
Что нам предлагают?
8.11. Использование определенной соглашением модульной структуры директорий:
Определенная соглашением модульная структура директорий позволяет разделять различные приложения MVC в автономные единицы и повторно использовать их с различными фронт-контроллерами.
В общем модули - это хорошо, распыляться на эту тему не стоит :).
Выделим кусок структуры, что касается модуля:
core/
controllers/
IndexController.php
FooController.php
models/
SubBar
Foo.php
Bar.php
views/
scripts/
index/
foo/
helpers/
filters/
Вот и подвох, а касается он того, что в модуле появилось много папок, где будут находиться наши классы:
core/controllers
core/models
core/views/helpers
Насчет контроллеров позаботился ZF, следуем рекомендациям:
$front->setControllerDirectory(array(
'default' => '/path/to/application/controllers',
'blog' => '/path/to/application/blog/controllers'
));
или
/**
* Предполагается следующая структура директорий:
* application/
* modules/
* default/
* controllers/
* foo/
* controllers/
* bar/
* controllers/
*/
$front->addModuleDirectory('/path/to/application/modules');
Насчет "вьюверных хелперов" ZF тоже позаботился, добавив плагин-загрузчик 26.2. Loading Plugins
$loader = new Zend_Loader_PluginLoader(array(
'Zend_View_Helper' => 'Zend/View/Helper/',
'Foo_View_Helper' => 'application/modules/foo/views/helpers',
'Bar_View_Helper' => 'application/modules/bar/views/helpers'
));
То, что приходится работать с разными загрузчиками классов, это автору не понравилось.
Модели остаются на нашей совести. Хочется, чтоб классы моделей Core_Bar
и Core_SubBar_Foo
находились в core/models/Bar.php
и core/models/SubBar/Foo.php
сответсвенно, но тут автолоадер из ZF ничего поделать не может. Вот тут и началось раздолье. Что же делать с моделями, как сделать так, чтобы автозагрузчик в приложении был одним единственным, а еще лучше чтоб загружал он все по одному принципу? Автор сделал много проб, например, был такой вариант:
ModuleName/
_configs/
_controllers/
_views/
MainClass/
SubClassName.php
ClassName.php
...
Автолоадер остается из ZF, в include_path
добавляется директория с модулями. Но эта структура не понравилась, может подчеркиванием, может заглавной буквой имени модуля(в этом случае приходилось переопределять лишние вещи в фронт-контроллере из ZF), а может еще чем…
Из вышеупомянутого плагина-загрузчика понравилась идея: определенному префиксу соответствует определенная директория. А почему бы эту идею не использовать для приложения в целом?
Итого:
Zend -> 'path/to/library/Zend'
Core -> 'path/to/module/core/models'
Core -> 'path/to/module/core/controllers'
...
Тогда если класс Zend_Controller_Front
, то наш автозагрузчик найдет префикс Zend
, дальше от класса останется Controller_Front
, который преобразуется в Controller/Front.php
(по принципу Zend_Loader
), аналогично и для наших моделей: по Core_SubBar_Foo
находит префикс Core
, далее остается SubBar_Foo
, который преобразуется SubBar/Foo.php
. В результате получили отличный механизм для автозагрузки как классов из ZF, так и классов из модулей. Сам механизм очень простой, но достаточно гибкий, те же вьювер-хелперы и контроллеры хорошо вписываются в эту структуру и не только.
Остается подумать, как сделать так, чтоб не регистрировать все существующие префиксы. Не забываем про упомянутое выше соглашение по именованию классов из стандартов кодирования ZF. Тогда задача очень легко решается: регистрировать нужно папки с наборами классов, а потом эти папки просматривать и в соответствии с содержимым регистрировать префиксы. Папок с классами в хорошо продуманной файловой структуре проекта не так уж много, или описываются довольно простыми правилами.
Например: регистрируем папку с библиотеками path/to/library
, по содержимому ее найдется папка Zend
и остальные библиотеки.
Подумаем о модулях. Модули могут постоянно добавляться, но разница добавленных папок с классами будет в названии самого модуля, если предположить, что у модулей одинаковая файловая структура (а оно так и должно быть, по идее). Приходит на ум добавить папку с модулями, а при нахождении модуля добавлять необходимый суффикс.
Например, из вышеприведенной структуры модуля:
Loader->addDir('path/to/modules', 'suffix/to/models');
Loader->addDir('path/to/modules', 'suffix/to/views/helpers');
...
После того как идея есть, дело остается за малым - реализовать :)
P.S. Автор знаком с ZF начиная с 0.1.x версий. Первое знакомство: PHPIns!de #18, Июль'2006.