Как обрабатывать, включая необходимые классы в PHP - PullRequest
3 голосов
/ 23 августа 2008

Мне интересно, как лучше всего решить проблему с необходимостью «включать» так много файлов в мои PHP-скрипты, чтобы гарантировать, что все классы, которые мне нужно использовать, доступны для моего скрипта.

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

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

Есть ли более простой способ справиться с этим?

Или PHP просто не подходит для приложений типа " enterprisey " с множеством различных объектов, расположенных в отдельных файлах, которые могут находиться в разных каталогах.

Ответы [ 7 ]

6 голосов
/ 23 августа 2008

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

Каждый раз, когда добавляется новый класс, я запускаю скрипт построителя командной строки, который сканирует все дерево каталогов в поисках классов моделей, а затем создает ассоциативный массив с именами классов в качестве ключей и путями в качестве значений. Затем функция __autoload ищет имя класса в этом массиве и получает путь включения. Вот код:

autobuild.php

define('MAP', 'var/cache/autoload.map');
error_reporting(E_ALL);
require 'setup.php';
print(buildAutoloaderMap() . " classes mapped\n");

function buildAutoloaderMap() {
    $dirs = array('lib', 'view', 'model');
    $cache = array();
    $n = 0;
    foreach ($dirs as $dir) {
        foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir)) as $entry) {
            $fn = $entry->getFilename();
            if (!preg_match('/\.class\.php$/', $fn))
                continue;
            $c = str_replace('.class.php', '', $fn);
            if (!class_exists($c)) {
                $cache[$c] = ($pn = $entry->getPathname());
                ++$n;
            }
        }
    }
    ksort($cache);
    file_put_contents(MAP, serialize($cache));
    return $n;
}

autoload.php

define('MAP', 'var/cache/autoload.map');

function __autoload($className) {
    static $map;
    $map or ($map = unserialize(file_get_contents(MAP)));
    $fn = array_key_exists($className, $map) ? $map[$className] : null;
    if ($fn and file_exists($fn)) {
        include $fn;
        unset($map[$className]);
    }
}

Обратите внимание, что соглашение об именовании файлов должно быть [class_name] .class.php. Изменения каталогов классов будут смотреться в autobuild.php. Вы также можете запускать autobuilder из функции автозагрузки, когда класс не найден, но это может привести вашу программу в бесконечный цикл.

Сериализованные массивы чертовски быстры.

@ JasonMichael: PHP 4 мертв. Преодолей это.

2 голосов
/ 23 августа 2008

Вы можете определить несколько функций автозагрузки с помощью spl_autoload_register:

spl_autoload_register('load_controllers');
spl_autoload_register('load_models');

function load_models($class){
    if( !file_exists("models/$class.php") )
        return false;

    include "models/$class.php";
    return true;
}
function load_controllers($class){
    if( !file_exists("controllers/$class.php") )
        return false;

    include "controllers/$class.php";
    return true;
}
1 голос
/ 23 августа 2008

Вы также можете программно определить местоположение файла класса, используя соглашения о структурированных именах, которые сопоставляются с физическими каталогами. Вот как Zend делает это в Zend Framework . Поэтому, когда вы вызываете Zend_Loader::loadClass("Zend_Db_Table");, он разбивает имя класса на массив каталогов путем разделения на подчеркивания, а затем класс Zend_Loader идет для загрузки требуемого файла.

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

Но были проблемы с производительностью при загрузке, когда вы используете какую-либо динамическую загрузку классов, например, смотрите в этом блоге сравнение Zend_Loader с жесткой загрузкой файлов классов.

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

При использовании ЛЮБОГО динамического загрузчика классов APC не может полностью кэшировать эти файлы, так как не уверен, какие файлы будут загружаться при любом отдельном запросе. Жестко загружая файлы, APC может их полностью кэшировать.

0 голосов
/ 23 августа 2008

@ Кевин:

Я просто пытался указать, что spl_autoload_register - лучшая альтернатива __autoload, поскольку вы можете определить несколько загрузчиков, и они не будут конфликтовать друг с другом. Удобно, если вам нужно включить библиотеки, которые также определяют функцию __autoload.

Ты уверен? Документация гласит:

Если ваш код имеет существующую функцию __autoload, то эта функция должна быть явно зарегистрирована в стеке __autoload. Это связано с тем, что spl_autoload_register () будет эффективно заменять кэш механизма для функции __autoload либо spl_autoload () или spl_autoload_call ().

=> Вы также должны явно зарегистрировать любую библиотеку __autoload. Но кроме этого вы, конечно, правы, эта функция - лучшая альтернатива.

0 голосов
/ 23 августа 2008

Из предложений на данный момент я неравнодушен к Кевину, но он не должен быть абсолютным. Я вижу несколько разных вариантов использования с __autoload.

  1. Поместите все файлы классов в один каталог. Назовите файл после класса, то есть classes/User.php или classes/User.class.php.
  2. Идея Кевина поместить модели в один каталог, контроллеры в другой и т. Д. Хорошо работает, если все ваши классы хорошо вписываются в инфраструктуру MVC, но иногда все становится не так.
  3. Включить каталог в имя класса. Например, класс Model_User будет фактически расположен в classes/Model/User.php. Ваша функция __autoload могла бы преобразовать подчеркивание в разделитель каталогов, чтобы найти файл.
  4. Просто проанализируйте всю структуру каталогов один раз. Либо в функции __autoload, либо даже просто в том же PHP-файле, где он определен, переберите содержимое каталога classes и кешируйте, где находятся файлы. Таким образом, если вы попытаетесь загрузить класс User, не имеет значения, находится он в classes/User.php или classes/Models/User.php или classes/Utility/User.php. Как только он найдет User.php где-нибудь в каталоге classes, он будет знать, какой файл включить, когда класс User должен быть автоматически загружен.
0 голосов
/ 23 августа 2008

__autoload будет работать, но только в PHP 5.

0 голосов
/ 23 августа 2008

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

Либо сохраните ассоциативный массив имен в расположениях файлов для вашего класса и разрешите __autoload запросить этот массив.

...