Лучшее решение для __autoload - PullRequest
       21

Лучшее решение для __autoload

10 голосов
/ 11 декабря 2008

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

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

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

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

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

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

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

Есть ли другие решения?

Ответы [ 11 ]

2 голосов
/ 11 декабря 2008

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

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

Типичный код выглядит так:

<?php

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

    $sc = new SomeClass();

?>

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

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

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

1 голос
/ 31 июля 2009

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

Вот оно

1 голос
/ 10 января 2009

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

1 голос
/ 12 декабря 2008

Есть 2 общих подхода, которые хорошо работают.
Во-первых, используется структура имен стандартного класса PEAR, поэтому вам просто нужно заменить '_' на /, чтобы найти класс.

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

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

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

0 голосов
/ 14 декабря 2014
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!");
   }
}
0 голосов
/ 16 июня 2010

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

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

Примечание: это было для PHP 5 <5.3, поэтому для PHP 5.3 вы можете использовать пространства имен, причина, по которой я использовал 3 _ в качестве разделителя, заключается в том, что это простая замена для использования пространства имен 5.3 </p>

0 голосов
/ 15 сентября 2009

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

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 голосов
/ 12 декабря 2008
<code>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 ).'
'); } }

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

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

0 голосов
/ 11 декабря 2008

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

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

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

0 голосов
/ 11 декабря 2008

Вы исследовали использование Zend_Loader registerAutoload () ) вместо только нативного __autoload()? Я использовал Zend_Loader и был счастлив с ним, но не смотрел на это с точки зрения производительности. Тем не менее, этот пост , похоже, провел некоторый анализ производительности; Вы можете сравнить эти результаты с внутренним тестированием производительности на своем текущем автозагрузчике, чтобы проверить, соответствует ли оно вашим ожиданиям.

...