2008-12-11 3 views
10

Поскольку наше приложение PHP5 OO выросло (как по размеру, так и по трафику), мы решили пересмотреть стратегию __autoload().Лучшее решение для __autoload

Мы всегда называем файл определением класса, который он содержит, поэтому класс Customer будет содержаться в Customer.php. Мы использовали список каталогов, в которых файл потенциально может существовать, пока не будет найден правильный .php-файл.

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

Решения, которые приходят на ум ...

-use в именовании, которые диктуют имя каталога (похожее на PEAR). Недостатки: не слишком большой масштаб, что приводит к ужасным именам классов.

-доступайте с заранее подготовленным массивом местоположений (propel делает это для своей __autoload). Недостаток: требуется пересоздание до развертывания нового кода.

-строить массив «на лету» и кешировать его. Это, по-видимому, лучшее решение, так как оно позволяет вам использовать любые имена классов и структуру каталогов и полностью гибко в том, что новые файлы просто добавляются в список. Вызывает озабоченность: где хранить его и что делать с удаленными/перемещенными файлами. Для хранения мы выбрали APC, поскольку у него нет служебных данных ввода-вывода на диске. Что касается удаления файлов, это не имеет значения, так как вы, вероятно, не захотите их нигде в любом случае. Что касается ходов ... это нерешено (мы игнорируем его, поскольку исторически это не случалось очень часто для нас).

Любые другие решения?

ответ

0

CodeIgniter делает что-то подобное с функцией load_class. Если я правильно помню, это статическая функция, которая содержит массив объектов. Вызов функции:


load_class($class_name, $instansiate); 

так в вашем случае


load_class('Customer', TRUE); 

и это будет загрузить экземпляр класса Customer в массив объектов.

Функция была довольно прямой. Извините, я не могу вспомнить имя класса, в котором он был. Но я помню, что есть несколько классов, которые загружаются, например, я считаю класс Routing, класс Benchmark и класс URI.

-1

Вы изучили использование Zend_LoaderregisterAutoload()) вместо родного __autoload()? Я использовал Zend_Loader и был доволен этим, но не смотрел на него с точки зрения производительности. Однако this blog post, по-видимому, провел анализ производительности; вы можете сравнить эти результаты с вашим собственным тестированием производительности на вашем текущем автозагрузчике, чтобы убедиться, что он может оправдать ваши ожидания.

2

Я также играл с автозагрузкой в ​​течение некоторого времени, и я закончил реализацию своего рода автозагрузчика с именами (да, он также работает и для PHP5.2).

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

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

<?php 

    loader::import('foo::bar::SomeClass'); 
    loader::import('foo::bar::OtherClass'); 

    $sc = new SomeClass(); 

?> 

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

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

Я мог бы предоставить вам несколько рабочих примеров, но я слишком застенчив, чтобы показать мой дрянной код для публики. Надеюсь, что приведенное выше объяснение было полезным ...

+6

Два больших преимущества автозагрузки: 1) отсутствие необходимости вручную загружать классы перед их использованием и 2) не жесткое кодирование их местоположений в файловой системе. Вы отменили оба. – Preston 2009-01-10 03:09:00

+0

Возможно, вы правы, если ваш проект использует несколько классов, но для больших проектов OO вам придется перейти на другое решение. Представьте, что php ищет один класс через все каталоги огромной структуры. Слишком плохо без каких-либо импортных подсказок. – azkotoki 2009-01-10 18:45:41

0

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

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

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

+0

Ну, конечно, я использую кеширование кода операции. Это просто, что APC не мешает вам вызывать stat() несколько раз, поскольку он вмешивается только в require(). – tpk 2008-12-12 17:15:39

1

Существует 2 общих подхода, которые хорошо работают.
Сначала использует иерархическую структуру имен классов PEAR, поэтому вам просто нужно заменить «_» на /, чтобы найти класс.

http://pear.php.net/manual/en/pear2cs.rules.php

Или вы можете искать каталоги для классов PHP и имени класса в файл. Вы можете сохранить карту класса в кеш, чтобы сохранить каталоги поиска на каждой странице.
Структура Symfony использует этот подход.

Как правило, лучше следовать стандартной структуре, так как ее проще, и вам не нужно ничего кэшировать, плюс вы следуете рекомендациям.

-3
function __autoload($class) 
{ 
    $patterns = array('%s.class.php', '%s.interface.php'); 

    foreach(explode(';', ini_get('include_path')) as $dir) 
    { 
     foreach($patterns as $pattern) 
     { 
      $file = sprintf($pattern, $class); 
      $command = sprintf('find -L %s -name "%s" -print', $dir, $file); 
      $output = array(); 
      $result = -1; 

      exec($command, $output, $result); 

      if (count($output) == 1) 
      { 
       require_once($output[ 0 ]); 
       return; 
      } 
     } 
    } 

    if (is_integer(strpos($class, 'Exception'))) 
    { 
     eval(sprintf('class %s extends Exception {}', $class)); 
     return; 
    } 

    if (! class_exists($class, false)) 
    { 
     // no exceptions in autoload :(
     die(sprintf('Failure to autoload class: "%s"', $class)); 
     // or perhaps: die ('<pre>'.var_export(debug_backtrace(), true).'</pre>');   
    } 
} 

Вы также можете найти другие, менее зависимые от posix пути для каталогов итераций, но это то, что я использовал.

Он обходит все каталоги в include_path (установленном в php.ini или .htaccess), чтобы найти класс или интерфейс.

1

Мы используем что-то похожее на последний вариант, за исключением проверки file_exists() перед требованием. Если он не существует, перестройте кеш и попробуйте еще раз. Вы получаете дополнительный stat за файл, но он обрабатывает движения прозрачно. Очень удобно для быстрого развития, когда я часто перемещаю или переименовываю.

1

Я использовал это решение в прошлом, я писал об этом для справки и может быть интересен некоторым из вас ...

Here it is

0

У меня есть конкретные соглашения об именах для каждого «типа» класса (контроллеры, модели, библиотечные файлы, и так далее ...), так что в настоящее время я сделать что-то похожее на:

function __autoload($class){ 
    if($class matches pattern_1 and file_exists($class.pattern_1)){ 
     //include the file somehow 
    } elseif($class matches pattern_2 and file_exists($class.pattern_2)){ 
     //include the file somehow 
    } elseif(file_exists($class.pattern_3)){ 
     //include the file somehow 
    } else { 
     //throw an error because that class does not exist? 
    } 
} 
0

Старая нить, но я думал, что могу вообще разоблачить свой метод здесь, возможно, это может помочь кому-то. Это, как я определить __autoload() в моем сайте точки входа /path/to/root/www/index.php, например:

function __autoload($call) { 
    require('../php/'.implode('/', explode('___', $call)).'.php'); 
} 

Все PHP файлы в организованных в виде дерева

 
/path/to/root/php 
    /Applications 
    /Website 
     Server.php 
    /Model 
    User.php 
    /Libraries 
    /HTTP 
     Client.php 
    Socket.php 

И имя классы:

 
Applications___Website___Server 
Model___User 
Libraries___HTTP___Client 
Libraries___Socket 

Быстро, и если файл отсутствует, он будет аварийно завершен, и ваш журнал ошибок скажет вам, какой файл отсутствует. Это может показаться немного суровым, но если вы попытаетесь использовать неправильный класс, это ваша проблема.

NB: это было для PHP 5 < 5.3, поэтому для PHP 5.3 вы можете использовать пространства имен, причина, почему я использовал 3 _ в качестве разделителя является то, что это легкая замена сделать для 5.3 пространства имен использовать

0
function __autoload($class_name) { 
    $class_name = strtolower($class_name); 
    $path  = "../includes/{$class_name}.php"; 
    if (file_exists($path)) { 
     require_once($path); 
    } else { 
     die("The file {$class_name}.php could not be found!"); 
    } 
} 
Смежные вопросы