Статьи

Автозагрузка в PHP и стандарт PSR-0

Допустим, у вас есть файл Rectangle.php, который содержит определение для класса Rectangle . Прежде чем вы сможете создать экземпляр объекта в другом месте вашего кода, вам сначала нужно Rectangle.php файл Rectangle.php , возможно, написав что-то вроде этого:

 <?php require "Rectangle.php"; $rect = new Rectangle(42, 25); 

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

В этой статье я познакомлю вас с «историей автозагрузки», от более раннего до нынешнего стандартного подхода автозагрузчика PSR-0, который можно найти во многих средах PHP, таких как Lithium, Symfony, Zend и т. Д. Затем я познакомлю вас с Компонент ClassLoader из проекта Symfony2 для PHP 5.3, который соответствует стандарту PSR-0 .

Предупреждение. Большинство примеров кода в начале этой статьи демонстрируют устаревшие подходы. Было бы неразумно использовать их в производстве. Я рекомендую вместо этого использовать один из стандартных автозагрузчиков PSR-0.

Автозагрузка в «Старые дни»

В PHP 5 появилась волшебная функция __autoload() которая автоматически вызывается, когда ваш код ссылается на класс или интерфейс, который еще не загружен. Это предоставляет среде выполнения последний шанс загрузить определение до того, как PHP завершится с ошибкой.

Вот пример чрезвычайно простой реализации __autoload() :

 <?php function __autoload($className) { $filename = $className . ".php"; if (is_readable($filename)) { require $filename; } } 

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

Основным недостатком функции __autoload() является то, что вы можете использовать только один автозагрузчик. В PHP 5.1.2 введена spl_autoload() которая позволяет регистрировать несколько функций автозагрузчика, и в будущем __autoload() будет устаревшей.

Введение в spl_autoload_register() дало программистам возможность создавать цепочку автозагрузки, серию функций, которые можно вызывать, чтобы попытаться загрузить класс или интерфейс. Например:

 <?php function autoloadModel($className) { $filename = "models/" . $className . ".php"; if (is_readable($filename)) { require $filename; } } function autoloadController($className) { $filename = "controllers/" . $className . ".php"; if (is_readable($filename)) { require $filename; } } spl_autoload_register("autoloadModel"); spl_autoload_register("autoloadController"); 

Обычно функции вызываются в том порядке, в котором они зарегистрированы, но на порядок также могут влиять дополнительные аргументы, передаваемые в spl_autoload_register() .

Важно помнить, что после регистрации функции с spl_autoload_register() __autoload() функция __autoload() больше не будет вызываться. Если у вас есть __autoload() вы хотите запустить как часть цепочки автозагрузчика, вам придется зарегистрировать ее с помощью spl_autoload_register() .

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

До того, как в PHP 5.3 была введена реальная поддержка пространства имен, разработчики разработали свои собственные подходы для предотвращения конфликтов имен. Стандарт кодирования PEAR использовал подчеркивания для добавления префиксов к именам классов; например, класс Zend_Translate будет определен в файле Zend/Translate.php . Автозагрузчик должен был заменить подчеркивания разделителями каталогов, чтобы найти определение.

Кроме того, разные разработчики приняли разные соглашения, когда дело .class.php до именования их файлов классов, например, файлы могут заканчиваться на .php , .class.php , .inc и т. Д. Некоторые библиотеки также могут быть установлены по разным путям. Загрузчик должен был искать их в разных местах, поэтому теперь загрузчик начинает выглядеть так:

 <?php function __autoload($className) { $extensions = array(".php", ".class.php", ".inc"); $paths = explode(PATH_SEPARATOR, get_include_path()); $className = str_replace("_" , DIRECTORY_SEPARATOR, $className); foreach ($paths as $path) { $filename = $path . DIRECTORY_SEPARATOR . $className; foreach ($extensions as $ext) { if (is_readable($filename . $ext)) { require_once $filename . $ext; break; } } } } 

Автозагрузка — это полезная идея, но она нуждалась в стандартизации.

Стандарт ПСР-0

После того, как в PHP 5.3 была введена настоящая поддержка пространства имен, группа людей из сообщества PHP решила создать рабочую группу по стандартам PHP в 2009 году (позже переименованную в Framework Interoperability Group ) и установить стандарт PSR-0, в котором изложены различные практики и ограничения, которые должны соблюдаться для совместимости автозагрузчика. Ниже приведены требования к соответствию PSR-0:

  • Полное пространство имен и класс должны иметь следующую структуру <Vendor Name>(<Namespace>)*<Class Name> .
  • Каждое пространство имен должно иметь пространство имен верхнего уровня («Имя поставщика»).
  • Каждое пространство имен может иметь столько подпространств, сколько пожелает.
  • Каждый разделитель пространства имен преобразуется в DIRECTORY_SEPARATOR при загрузке из файловой системы.
  • Каждое подчеркивание в имени класса преобразуется в DIRECTORY_SEPARATOR . Подчеркивание не имеет особого значения в пространстве имен.
  • При загрузке из файловой системы к полному пространству имен и классу добавляется .php .
  • Буквенные символы в именах поставщиков, пространствах имен и именах классов могут состоять из любой комбинации строчных и прописных букв.

В соответствии со стандартом PSR-0 должен существовать каталог верхнего уровня с именем поставщика и именем пакета, поэтому дерево каталогов будет выглядеть так:

example component path

Классы тогда будут соответственно распределены по пространству имен:

 <?php namespace VendorPackage; class Example { } 

Таким образом, определение класса для DoctrineCommonConnections можно найти в /path/to/project/lib/Doctrine/Common/Connections.php , а SymfonyCoreRequest в /path/to/project/lib/Symfony/Core/Request.php . Стандарт PSR-0 не предписывает, что представляет собой часть /path/to/project/lib base /path/to/project/lib , и соответствующие автозагрузчики предлагают различные методы для его разрешения. Некоторые позволят вам зарегистрировать каталог, некоторые будут искать PHP по include_path , а некоторые предлагают вам оба. Ниже приведен пример из принятого стандарта PSR-0.

 <?php function autoload($className) { $className = ltrim($className, '\'); $fileName = ''; $namespace = ''; if ($lastNsPos = strripos($className, '\')) { $namespace = substr($className, 0, $lastNsPos); $className = substr($className, $lastNsPos + 1); $fileName = str_replace('\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR; } $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php'; require $fileName; } 

Эта идея Джонатана SplClassLoader является SplClassLoader реализации SplClassLoader которая может загружать ваши классы, если вы следуете стандартам совместимости автозагрузчика. В настоящее время это рекомендуемый способ загрузки классов PHP 5.3, соответствующих этим стандартам.

Вы можете использовать любой из PSR-0-совместимых автозагрузчиков из таких сред, как Symfony, Pear2, AuraPHP (для PHP 5.4+) и т. Д., И придерживаться приведенных выше правил с собственным кодом, чтобы воспользоваться преимуществами автозагрузки без неопределенностей Я обсуждал ранее.

Использование автозагрузчика Symfony

Проект Symfony2 — это основанная на компонентах инфраструктура для PHP 5.3 и выше, которую вы можете использовать как библиотеку компонентов или как среду полного стека. Вы можете загрузить автозагрузчик Symfony, компонент ClassLoader, с помощью различных средств — pear.symfony.com , packagist или с GitHub .

Вот структура каталогов компонента Symfony ClassLoader:

Symfony ClassLoader Component

Использование компонента выглядит следующим образом:

 <?php require_once "/path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php"; use Symfony\Component\ClassLoader\UniversalClassLoader; $loader = new UniversalClassLoader(); $loader->registerNamespace("Symfony\Component" => "/path/to/symfony/components"); $loader->registerNamespace("Monolog" => "path/to/monolog/src/"); $loader->registerPrefix("Zend_", "path/to/zend/library"); $loader->register(); 

Метод registerNamespace() используется для информирования автозагрузчика, где отображается базовый каталог данного пространства имен в файловой системе, и принимает пространство имен в качестве первого аргумента и путь в качестве второго значения. Вы также можете зарегистрировать несколько пространств имен за один вызов с помощью метода registerNamespaces() .

 <?php $loader->registerNamespaces(array( "Symfony\Component" => "/path/to/symfony/components", "Monolog' => "path/to/monolog/src")); 

Метод registerPrefix() используется для регистрации псевдо-пространств имен, которые использовались Pear, Zend и другими библиотеками и инфраструктурами до того, как в PHP была реализована поддержка реального пространства имен, как мы уже рассмотрели выше. Вы также можете зарегистрировать несколько из них с помощью метода registerPrefixes() и передать его в виде ассоциативного массива.

 <?php $loader->registerPrefixes(array( "Zend_" => "/path/to/zend/library", "Twig_" => "path/to/twig/library")); 

Если вы используете альтернативный PHP Cache (APC), бесплатный кэш кода операции с открытым исходным кодом для PHP, то вы можете рассмотреть возможность использования класса ApcUniversalClassLoader . ApcUniversalClassLoader расширяет UniversalClassLoader но использует apc_store() и apc_fetch() для хранения информации о поиске в кеше APC. Стандартный UniversalClassLoader , конечно, будет работать с APC, но дополнительное поведение класса ApcUniversalClassLoader дает дополнительное преимущество в производительности.

 <?php require_once "path/to/Symfony/Component/ClassLoader/UniversalClassLoader.php"; require_once "path/to/Symfony/Component/ClassLoader/ApcUniversalClassLoader.php"; use Symfony\Component\ClassLoader\ApcUniversalClassLoader; $loader = new ApcUniversalClassLoader("apc.prefix."); 

ApcUniversalClassLoader принимает префикс с его конструктором. Для получения дополнительной информации о APC, я предлагаю прочитать документацию APC .

Резюме

В этой статье мы обсудили автозагрузку, начиная с ее ранних времен и заканчивая текущим стандартом PSR-0, который стал широко распространенным во многих средах PHP. Недавно Дэвид Колалье попытался SplClassloader класс SplClassloader в PHP 5.4, чтобы предложить встроенную функциональность автозагрузки, совместимую с PSR-0, но по разным причинам этого не произошло. Возможно, в будущем мы увидим это добавлено. (См. Расширение C на gist.github.com/1310352 .)

Теперь текущая горячая дискуссия в рабочей группе сосредоточена на кэшировании. Если вы хотите стать частью этого, присоединяйтесь к обсуждению!

Изображение через Matyas Szabo / Shutterstock

И если вам понравилось читать этот пост, вы полюбите Learnable ; место, чтобы узнать новые навыки и приемы у мастеров. Участники получают мгновенный доступ ко всем электронным книгам SitePoint и интерактивным онлайн-курсам, таким как Jump Start PHP .

Комментарии к этой статье закрыты. Есть вопрос по PHP? Почему бы не спросить об этом на наших форумах ?