Сен 29 2008

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

Категория: PHPgugglegum @ 05:35

С полгода назад я писал про нюансы автоматической загрузки классов с использованием __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.

3 отзыва на “Несколько функций __autoload в одном коде на PHP”

  1. Степан says:

    Скажу что на данный момент у хостеров нет php 5.3 :) Только на отдельных серверах.

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

    У меня такой вопрос есть.

    Если файлы зазендованы, то какая настройка отвечает за то, чтобы можно было пользоваться обычными именами функций в некоторых файлах - чтобы php их переименовывал сам? Например из шаблонов смарти. (у меня периодически возникает такая проблема на некоторых хостерах)

  2. навигатор says:

    Спасибо большое поставил на vpn php 5.3 и радуюсь жизни….

  3. fly_fly says:

    А как на счет области видимости? Например если использовать __autoload то можно сказать что подключение идет внутри функции.

Напишите что Вы об этом думаете


*


*


rel=nofollow включен, спам не поднимет Page Rank Вашего сайта



Anti-Spam Image
Введите этот код, чтобы подтвердить, что вы человек
*

* — Обязательно для заполнения

Перейти на главную страницу