Библиотека PHP автозагрузчика


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

Предпосылка заключается в том, что все разбивается на пакеты в несколько слоев суб-пакетов. Занятия с использованием CamelCasing. Итак, класс название связано с его пакета следующим образом: PackageSubpackageSubpackageName. Теперь, каждый пакет может иметь особые интерфейсы, определенные isPackageName для интерфейсы, исключения PackageNameExceptionи т. д. Я старался сделать его достаточно гибким для повторного использования.

/**
 * A class for lazy-loading other classes
 *
 * This class enables lazy-loading of php classes.  The benefit of this is
 * three-fold.  First, there is a memory benefit, since not all classes are
 * loaded until they are needed.  Second, there is a time benefit, since not all
 * classes are loaded.  Third, it produces cleaner code, since there is no need
     * to litter files with require_once() calls.
 *
 * @category Libraries
 * @package  Libraries
 * @author   Me
 */
abstract class Loader
{

    /**
     * @var array An array of class to path mappings
     */
    protected static $classes = array();

    /**
     * @var boolean Has the loader been initialized already
     */
    protected static $initialized = false;

    /**
     * @var array An array of auto-search paths
     */
    protected static $namedPaths = array(
        'exception',
        'interface',
        'iterator',
    );

    /**
     * @var array An array of include paths to search
     */
    protected static $paths = array(
        PATH_LIBS,
    );

    /**
     * Tell the auto-loader where to find an un-loaded class
     *
     * This can be used to "register" new classes that are unknown to the
     * system.  It can also be used to "overload" a class (redefine it
     * elsewhere)
     *
     * @param string $class The class name to overload
     * @param string $path  The path to the new class
     *
     * @throws InvalidArgumentException Upon an Invalid path submission
     * @return void
     */
    public static function _($class, $path)
    {
        $class = strtolower($class);
        if (!file_exists($path)) {
            throw new InvalidArgumentException('Invalid Path Specified');
        }
        self::$classes[$class] = $path;
    }

    /**
     * Add a path to the include path list
     *
     * This adds a path to the list of paths to search for an included file.
     * This should not be used to overload classes, since the default include
     * directory will always be searched first.  This can be used to extend
     * the search path to include new parts of the system
     *
     * @param string $path The path to add to the search list
     *
     * @throws InvalidArgumentException when an invalid path is specified
     * @return void
     */
    public static function addPath($path)
    {
        if (!is_dir($path)) {
            throw new InvalidArgumentException('Invalid Include Path Added');
        }
        $path = rtrim($path, DS);
        if (!in_array($path, self::$paths)) {
                self::$paths[] = $path;
        }
    }

    /**
     * Add a path to the auto-search paths (for trailing extensions)
     *
     * The path should end with an 's'.  Default files should not.
     *
     * @param string $path The name of the new auto-search path
     *
     * @return void
     */
    public static function addNamedPath($path)
    {
        $path = strtolower($path);
        if (substr($path, -1) == 's') {
            $path = substr($path, 0, -1);
        }
        if (!in_array($path, self::$namedPaths)) {
            self::$namedPaths[] = $path;
        }
    }

    /**
     * Initialize and register the autoloader.
     *
     * This method will setup the autoloader.  This should only be called once.
     *
     * @return void
     */
    public static function initialize()
    {
        if (!self::$initialized) {
            self::$initialized = true;
            spl_autoload_register(array('Loader', 'load'));
        }
    }

    /**
     * The actual auto-loading function.
     *
     * This is automatically called by PHP whenever a class name is used that
     * doesn't exist yet.  There should be no need to manually call this method.
     *
     * @param string $class The class name to load
     *
     * @return void
     */
    public static function load($class)
    {
        $className = strtolower($class);
        if (isset(self::$classes[$className])) {
            $file = self::$classes[$className];
        } else {
            $file = self::findFile($class);
        }
        if (file_exists($file)) {
            include_once $file;
        }
    }

    /**
     * Find the file to include based upon its name
     *    
     * This splits the class name by uppercase letter, and then rejoins them
     * to attain the file system path.  So FooBarBaz will be turned into
     * foo/bar/baz.  It then searches the include paths for that chain.  If baz
     * is a directory, it searches that directory for a file called baz.php.
     * Otherwise, it looks for baz.php under the bar directory.
     *
     * @param string $class The name of the class to find
     *
     * @return string The path to the file defining that class
     */
    protected static function findFile($class)
    {
        $regex = '#([A-Z]{1}[a-z0-9_]+)#';
        $options = PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE;
        $parts = preg_split($regex, $class, null, $options);

        $subpath = '';
        $file = strtolower(end($parts));
        $test = strtolower(reset($parts));
        if ($test == 'is') {
            array_shift($parts);
            return self::findNamedFile($class, $parts, 'interface');
        }
        foreach ($parts as $part) {
            $subpath .= DS . strtolower($part);
        }    
        foreach (self::$paths as $path) {
            $newpath = $path . $subpath;
            if (is_file($newpath . '.php')) {
                return $newpath . '.php';
            } elseif (is_file($newpath . DS . $file . '.php')) {
                return $newpath . DS . $file . '.php';
            }
        }
        if (in_array($file, self::$namedPaths)) {
            //Get rid of the trailing part
            array_pop($parts);
            return self::findNamedFile($class, $parts, $file);
        }    
        return '';
    }

    /**
     * Find a file for named directories (interfaces, exceptions, iterators, etc)
     *
     * @param string $class The class name of the exception to find
     * @param array  $parts The parts of the class name pre-split
     * @param string $name  The name of the named directory to search in
     *    
     * @return string The found path, or '' if not found
     */
    protected static function findNamedFile($class, array $parts, $name)
    {    
        if (empty($parts)) {
            return '';
        }
        $name = strtolower($name);
        //Add a trailing s, since individual files are not plural
        $filename = $name;
        $name .= 's';
        //Try the global path first
        $subpath = DS . $name . DS . strtolower(implode('', $parts)) . '.php';
        foreach (self::$paths as $path) {
            $newpath = $path . $subpath;
            if (is_file($newpath)) {
                return $newpath;
            }
        }
        //Try to build a full sub path for package specific named files
        $package = array_shift($parts);
        $subpath = DS . strtolower($package) . DS . $name . DS;
        if (!empty($parts)) {
            $subpath .= strtolower(implode('', $parts)) . '.php';
        } else {
            $subpath .= $filename . '.php';
        }
        foreach (self::$paths as $path) {
            $newpath = $path . $subpath;
            if (is_file($newpath)) {
                return $newpath;
            }
        }
        return '';
    }
}

Он также полностью модульного тестирования.

Каковы ваши мысли? Это-комплекс?



1656
24
задан 19 января 2011 в 09:01 Источник Поделиться
Комментарии
2 ответа

Первая проблема, которую я вижу, заключается в том, что есть много случаев, когда кто-то захочет создать класс с более чем одно слово в названии (DataMapper), и автозагрузчик, который вы предоставили не допустит. Я бы рекомендовал использовать другой символ для разделения между имени пакета. В Zend Framework использует Package_SubPackage_SubPackage_Class и что работает очень хорошо.

Кроме того, я не уверен, что ваши конкретные причины для написания собственных автозагрузчик (будь то производство, образование и т. д.), но если вы планируете использовать его для производства, я бы рекомендовал класс zend_loader из Zend Framework, а она поддерживает, полностью протестированы, и постоянно разрабатываются новые. Вы можете прочитать это краткое руководство пользователя вот

14
ответ дан 19 января 2011 в 09:01 Источник Поделиться

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

Есть предложение, что государства на структуру классов, структуру каталогов, пространства имен, где автопогрузчиков работают очень хорошо.

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


  • Обязательные:


    • Полностью определенное пространство имен и класс должен иметь следующую структуру:

      \<Vendor Name>\(<Namespace>\)*<Class Name>

    • Каждое пространство имен должно иметь пространство имен верхнего уровня ("имя поставщика").

    • Каждое пространство имен может иметь несколько суб-пространства имен, как он хочет.

    • Каждое пространство имен сепаратор преобразуется в DIRECTORY_SEPARATOR при загрузке из файловой системы.

    • Каждый "" символ в имя класса преобразуется в DIRECTORY_SEPARATOR. В "" персонаж не имеет особого значения в пространстве имен.

    • Полностью определенное пространство имен и класс suffixed с ".РНР" при загрузке из файловой системы.

    • Буквенные символы в названиях поставщика, пространства имен и имена классов могут быть в любой комбинации верхнего и нижнего регистра.


  • Примеры:

  • \Учение\общие\IsolatedClassLoader => /path/to/project/lib/vendor/Doctrine/Common/IsolatedClassLoader.php

  • \В Symfony\ядро\запрос => /path/to/project/lib/vendor/Symfony/Core/Request.php

  • \Зенд\ОБК => /path/to/project/lib/vendor/Zend/Acl.php

  • \Зенд\почты\сообщений => /path/to/project/lib/vendor/Zend/Mail/Message.php

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

@Ссылка

Есть очень хороший класс, который я использую около 6 проектов, и я считаю, что это идеальный вариант, и вы должны изучить и посмотреть, что можно сделать с ним.

Класс Ссылке

Пример использования будет вот так:

$classLoader = new SplClassLoader('Doctrine\Common', '/libs/doctrine');
$classLoader->register();

$classLoader = new SplClassLoader('Ircmexell\MyApplication', 'libs/internal');
$classLoader->register();

5
ответ дан 19 января 2011 в 11:01 Источник Поделиться