Статьи

Каков твой план для __autoload ()?

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


  function __autoload($name) {
    require_once('classes/'.$name.'.php'); 
  }

Однако это решение негибко и имеет некоторые недостатки. Наиболее очевидным является имя класса -> ограничение имени файла. Кроме того, это означает, что все файлы классов должны храниться в одной папке. Это не вариант для проектов с несколькими классами hundert, естественно упорядоченными в каталогах пакетов. В целом, эти реализации кажутся не совсем зрелыми, а скорее доказательством концепции __autoload (). Это требует лучшего решения.

Загрузка шустро

Что, если бы у нас был маленький «искатель классов», который рекурсивно искал в каталогах PHP-скрипты и анализировал каждый из них на предмет определения классов? Во-первых, он будет знать обо всех классах в этих папках и сообщать нам, в каком файле их искать. Мы могли бы объединить это с __autoload (), чтобы помочь ему найти любой необходимый класс сам по себе. «Это глупо, это привело бы к большим накладным расходам», — скажете вы. Правильно! Так что, если мы будем кэшировать результаты после каждого поиска, зная, что файловая структура редко изменяется, если разработчик не работает над этим? Конечно, я говорю о лучшем способе кеширования, таким образом генерируя список классов в виде PHP-кода и сохраняя его для дальнейшего использования.

Сказано — сделано. Я написал класс, который реализует эту идею и претенциозно назвал его «SmartLoader», так как он достаточно умен, чтобы найти любой класс вашего PHP-приложения без посторонней помощи. Вы можете скачать его здесь под лицензией Lesser General Public License . Теперь давайте подробнее рассмотрим, как это работает:

За кулисами

Шаг 1: SmartLoader рекурсивно ищет PHP-скрипты и анализирует их на наличие классов со следующим регулярным выражением:

 (interface|class)s+(w+)s+(extendss+(w+)s+)?(implementss+w+s*(,s*w+s*)*)?{

Шаг 2: Теперь у нас есть список всех доступных классов и где их найти. Этот список будет преобразован в код PHP и записан в файл кэша. Его содержимое будет выглядеть примерно так:

 
// this is a automatically generated cache file.
$GLOBALS['smartloader_classes']['Main'] = 'classes/main.class.php';
$GLOBALS['smartloader_classes']['Iterable'] = 'classes/containers/iterable.class.php';
$GLOBALS['smartloader_classes']['ActiveRecord'] = 'classes/database/activerecord.class.php';
/* etc. */

Шаг 3: Всякий раз, когда необходимо загрузить класс, SmartLoader проверяет кэш на его имя и включает соответствующий файл PHP. В случае, если класс не может быть найден или загружен, кэш воссоздается, и будет предпринята другая попытка включения.

Готовиться

smartloader.class.php содержит функцию автозагрузки, а также сам класс SmartLoader. Единственное, что нужно сделать, это настроить метод автозагрузки:

 
  function __autoload($class_name) {
    /* using a static loader object rather than a singleton to reduce overhead */
    static $ldr = null;
 
    /* initializing loader */
    if(!$ldr) {
      $ldr = new SmartLoader();
    }
 
      /* defining cache file, make sure write permissions */
      $ldr->setCacheFilename('cache/smartloader_cache.php');
 
      /* adding directories to parse. better use absolute paths. */
      $ldr->addDir("classes");
 
      /* what are the endings of your class files? */
      $ldr->setClassFileEndings(array('.php', '.class'));
 
      /* should SmartLoader follow symbolic links? */
      $ldr->setfollowSymlinks(false);
 
      /* it should probably ignore hidden dirs/files */
      $ldr->setIgnoreHiddenFiles(true);
    }
 
    /* load the class or trigger some fatal error on failure */
    if(!$ldr->loadClass($class_name)) {
      trigger_error("Cannot load class '".$class_name."'", E_USER_ERROR);
    }
  }

После этого вам просто нужно включить smartloader.class.php в ваши скрипты и больше не беспокоиться о включении классов.

преимущества

  • Удобство: SmartLoader упрощает управление файлами классов. Вы можете переименовать их, переместить их или реорганизовать их в (пакет) папки. Пока они находятся в вашем веб-пространстве, SmartLoader может их найти.
  • Скорость: с SmartLoader файлы классов загружаются только тогда, когда они действительно необходимы. Такой подход, называемый «отложенной загрузкой» или «как раз вовремя» (не путать с интерактивным программированием), может сэкономить PHP много разборов и компиляции, вызванных избыточными включениями.
  • Портативность, обратная совместимость: вы можете легко использовать SmartLoader, не нарушая существующие приложения. __autoload () включается только тогда, когда по умолчанию создается экземпляр несуществующего класса.

Потенциальные ловушки

  • Обработка ошибок: вы можете использовать любой вариант обработки ошибок, который вам нравится. Ну, почти: исключения не могут быть вызваны через __autoload (). Помните, что это особенность, а не ошибка . Это возможно, хотя и со страшным взломом eval (), но я не буду вдаваться в подробности.
  • Системы контроля версий: если ваше веб-пространство является рабочей копией какой-либо системы контроля версий, вы, вероятно, должны сказать SmartLoader игнорировать скрытые файлы. Это улучшит его производительность и предотвратит сканирование тех устаревших устаревших копий файлов, которые находятся в папках .SVN / .CVS.
  • Я не должен вам этого говорить, но не позволяйте SmartLoader сканировать циклы символьных ссылок.
  • Профилирование: Zend Profiler запутывается в SmartLoader и начинает вычислять недопустимые результаты для своего времени выполнения.
  • Кэши опкодов: я недостаточно хорошо знаю внутреннюю работу APC, Zend Optimizer и им подобных, но могу представить, что динамические включения, используемые в SmartLoader, сводят на нет производительность, получаемую от кеширования опкодов. Я не смог проверить это, поскольку Zend Profiler отказывается правильно работать с SmartLoader (см. Выше). Я был бы рад получить любые отзывы по этому вопросу.