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

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

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

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

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

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

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

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

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

11.12.2008 15:57:32
11 ОТВЕТОВ

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


 load_class($class_name, $instansiate);

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


 load_class('Customer', TRUE);

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

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

0
11.12.2008 16:14:42

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

-1
11.12.2008 16:27:22

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

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

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

<?php

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

    $sc = new SomeClass();

?>

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

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

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

2
11.12.2008 16:50:53
Два больших преимущества автозагрузки: 1) отсутствие необходимости вручную загружать классы перед их использованием и 2) нетрудно кодировать их расположение в файловой системе. Вы отменили оба.
Preston 10.01.2009 03:09:00
Вы можете быть правы, если ваш проект использует несколько классов, но для больших ОО-проектов вам придется перейти к другому решению. Представьте себе, что php ищет один класс во всех каталогах огромного фреймворка. Жаль, без всяких импортных подсказок.
azkotoki 10.01.2009 18:45:41

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

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

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

0
11.12.2008 17:49:22
Ну, конечно, я использую кеширование кода операции. Просто APC не мешает вам вызывать stat () несколько раз, так как он вмешивается только по требованию ().
tpk 12.12.2008 17:15:39
Что касается второго пункта, учитывая количество классов, преимущество отменяется тем, что мы загружаем больше, чем нужно. Кроме того, действительно неудобно перестраивать его каждый раз, когда вы хотите что-то развернуть.
tpk 12.12.2008 17:17:03
Вы измерили производительность и сравнили результаты, или вы просто делаете обоснованное предположение?
Bill Karwin 12.12.2008 20:45:06

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

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

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

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

1
12.12.2008 01:36:49
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), чтобы найти класс или интерфейс.

-3
12.12.2008 14:32:16
О боже, это ужасно. Вы вызываете findкаждый dir в INCLUDE_PATH, для каждого загружаемого вами класса при каждом запросе PHP?!?
Bill Karwin 12.12.2008 17:15:39
И ваш разделитель пути это ';' (что означает, что этот код работает только в Windows), но вы полагаетесь на инструмент posix, как find?
Bill Karwin 12.12.2008 17:18:29
Я согласен ... это ужасно неэффективно. Почему не file_exists ()? Это то, что мы имеем на данный момент.
tpk 12.12.2008 17:20:32
Проверьте RecursiveDirectoryIterator.
Preston 10.01.2009 03:12:00
Извините, Билл, вы ошибаетесь в обоих аккаунтах. Я не работаю на Windows, но я кеширую расположение файлов в базе данных, это используется только тогда, когда их там нет. Помните, что это просто пример умных людей ...
Kris 11.01.2009 15:17:24

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

1
10.01.2009 03:28:12

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

Вот

1
31.07.2009 19:46:16

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

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
14.09.2009 23:55:27

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

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

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

/ Путь / к / корневой / PHP
  /Приложения
    /Веб-сайт
      Server.php
  / Модель
    User.php
  / Библиотеки
    / 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
16.06.2010 13:19:36
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
14.12.2014 18:08:50