PHP

Несколько функций __autoload в одном коде на PHP

С полгода назад я писал про нюансы автоматической загрузки классов с использованием __autoload(), и там указывал на проблему использования данного способа, заключающуюся в том, что функция с магическим именем “__autoload” может быть объявлена только одна. Поэтому, если Вы пишете свой фреймворк и использовали в нем функцию с этим именем, то другой фреймворк, использующий аналогичную функцию уже не сможет работать в паре с Вашим.

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

Так было полгода назад. В последствии я ближе познакомился со Standard PHP Library (SPL) и осознал, что эта проблема давно решена при помощи функции spl_autoload_register, которая активизирует стэк из функций автозагрузки классов.

Разумеется, Вы должны использовать уникальное имя для своей функции автозагрузки, поэтому от имени “__autoload” лучше отказаться сразу. Можете использовать уникальный префикс типа “myframework_autoload” или завернуть эту функцию в отдельный класс автозагрузки. А еще лучше (в рассчете на будущее) использовать нэймспэйсы в PHP, хотя это сразу ограничит минимальную версию PHP до 5.3.0.

Когда Вы первый раз вызываете функцию spl_autoload_register(), она активизирует стэк функций автозагрузки классов и с этого момента функция с именем “__autoload” уже не вызывается автоматически, если только Вы не зарегистрируете и ее. Т.е. с момента вызова spl_autoload_register функция с именем “__autoload” перестает быть магической. И даже разрегистрирование всех функций автозагрузки при помощи spl_autoload_unregister не возвращает ее магическое предназначение.

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

Вот такой дизайн может иметь класс автозагрузки в Вашем собственном фреймворке (для примера используется нэймспэйс “MyFramework”):
<?php

namespace MyFramework;

class Autoload {

  public static function register() {
    if (!spl_autoload_register(__NAMESPACE__.'::Autoload::load')) {
    	throw new Exception('Could not register '.__NAMESPACE__.''s class autoload function');
    }
  }

  public static function unregister() {
    if (!spl_autoload_unregister(__NAMESPACE__.'::Autoload::load')) {
    	throw new Exception('Could not unregister '.__NAMESPACE__.''s class autoload function');
    }
  }

  public static function load($fullClassName) {
    $namespaces = explode('::', $fullClassName);
    $className = array_pop($namespaces);
    if (count($namespaces) != 1 || $namespaces[0] != __NAMESPACE__)
      return;
    $basePath = dirname(__FILE__).'/';
    @include_once($basePath . self::getFileByClass($className));
  }

  private static function getFileByClass($class) {
    return str_replace('_', '/', $class).'.php';
  }

}

?>

Теперь все, что нужно для того, чтобы использовать классы из этого фреймворка, не заботясь о их подключении через (require_once или include_once) — это вызвать статический метод register() класса Autoload в нэймспэйсе MyFramework:
require_once 'path/to/myframework/Autoload.php';
MyFramework::Autoload::register();

Можете еще больше упростить предложенный пример, создав в каталоге с Вашим фреймворком файл init.php вида:
<?php
require_once dirname(__FILE__).'/classes/Autoload.php';
MyFramework::Autoload::register();
?>

И тогда для использования фреймворка достаточно заинклудить этот init.php и использовать его классы напрямую.

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

Напомню, что функция автозагрузки должна отрабатывать максимально “тихо”, не вызывая в случае неуспеха каких-либо исключений, приводящих к завершению скрипта, т.к. в стэке SPL может находиться несколько функций автозагрузки.

Применяя предложенный паттерн Вы сможете избежать конфликта с другим фреймворками, так же использующими автозагрузку классов через SPL.
Теги:
autoload
Добавлено: 20 Мая 2018 21:08:21 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...