PHP - most lightweight psr-0 compliant autoloader

不羁的心 提交于 2019-11-27 07:51:46
Adrien Gibrat

You ask extremely lightweight, let's do so ;)

Timothy Boronczyk wrote a nice minimal SPL autoloader : http://zaemis.blogspot.fr/2012/05/writing-minimal-psr-0-autoloader.html

I condensed the code like this:

function autoload1( $class ) {
    preg_match('/^(.+)?([^\\\\]+)$/U', ltrim( $class, '\\' ), $match ) );
    require str_replace( '\\', '/', $match[ 1 ] )
        . str_replace( [ '\\', '_' ], '/', $match[ 2 ] )
        . '.php';
}

Then compare (minified versions of) this [autoload3] with short @Alix Axel code [autoload4] :

function autoload3($c){preg_match('/^(.+)?([^\\\\]+)$/U',ltrim($c,'\\'),$m);require str_replace('\\','/',$m[1]).str_replace(['\\','_'],'/',$m[2]).'.php';}
function autoload4($c){require (($n=strrpos($c=ltrim($c,'\\'),'\\'))!==false?str_replace('\\','/',substr($c,0,++$n)):null).str_replace('_','/',substr($c,$n)).'.php';}

autoload3 is the shortest !

Let's use stable & extremely lightweight (175b !) autoloader file :

<?php spl_autoload_register(function ($c){preg_match('/^(.+)?([^\\\\]+)$/U',ltrim($c,'\\'),$m);require str_replace('\\','/',$m[1]).str_replace(['\\','_'],'/',$m[2]).'.php';});

Maybe i'm crazy but you Asked for extreme, no?

EDIT: Thanks to Alix Axel, i've shorten the code (only 100b !) and used include instead of require in case you have various autoloading strategy for old libs (and then various autoloader in spl autoload stack...).

<?php spl_autoload_register(function($c){@include preg_replace('#\\\|_(?!.+\\\)#','/',$c).'.php';});

If you want to make it shorter / better, please use this gist.

The PSR-0 specification document has an examplary compatible autoloader function that is already pretty short:

function autoload($className)
{
    $className = ltrim($className, '\\');
    $fileName  = '';
    $namespace = '';
    if ($lastNsPos = strripos($className, '\\')) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName  = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';

    require $fileName;
}

It's usage is pretty straight forward:

spl_autoload_register('autoload');

The shortcoming with it is, that you need to configure base-directories it works on with the include_path directive. To support a hybrid PSR-0 autoloaders leaned onto SPL semantics, the following one supportss include path and spl autoload extensions:

$spl_autoload_register_psr0 = function ($extensions = null) 
{
    $callback = function ($className, $extensions = null) 
    {
        if (!preg_match('~^[a-z0-9\\_]{2,}$~i', $className)) {
            return;
        }
        null !== $extensions || $extensions = spl_autoload_extensions();
        $extensions = array_map('trim', explode(',', $extensions));
        $dirs = array_map('realpath', explode(PATH_SEPARATOR, get_include_path()));

        $classStub = strtr($className, array('_' => '/', '\\' => '/'));

        foreach ($dirs as $dir) {
            foreach ($extensions as $extension) {
                $file = sprintf('%s/%s%s', $dir, $classStub, $extension);
                if (!is_readable($file)) {
                    continue;
                }
                include $file;
                return;
            }
        }
    };

    return spl_autoload_register($callback);
};

The The Symfony2 ClassLoader Component has the benefit to allow more configuration here. You can install it easily via Pear or Composer (symfony/class-loader on Packagist). It is a component on it's own that is used by many and fairly well tested and supported.

SplClassLoader seems like a right choice. It's an implementation proposed by PSR-0 itself.

Alix Axel

An exact equivalent of the answer @hakre provided, just shorter:

function autoload($class) {
  $path = null;

  if (($namespace = strrpos($class = ltrim($class, '\\'), '\\')) !== false) {
    $path .= strtr(substr($class, 0, ++$namespace), '\\', '/');
  }

  require($path . strtr(substr($class, $namespace), '_', '/') . '.php');
}

You can also set the base directory by changing $path = null; to another value, or just do like this:

$paths = array
(
    __DIR__ . '/vendor/',
    __DIR__ . '/vendor/phunction/phunction.php',
);

foreach ($paths as $path)
{
    if (is_dir($path) === true)
    {
        spl_autoload_register(function ($class) use ($path)
        {
            if (($namespace = strrpos($class = ltrim($class, '\\'), '\\')) !== false)
            {
                $path .= strtr(substr($class, 0, ++$namespace), '\\', '/');
            }

            require($path . strtr(substr($class, $namespace), '_', '/') . '.php');
        });
    }

    else if (is_file($path) === true)
    {
        require($path);
    }
}

The doctrine classloader is another good choice. You can easily install it with composer

This is not a direct answer to the question, but I found that the above answers worked great on standalone PHP scripts, but were causing issues when used in certain frameworks, such as Joomla.

For anyone using Joomla, it turns out that there is a compatible autoloader already built into the framework, therefore you won't need to use the above functions. In that instance, just call JLoader::registerNamespace().... for example:

JLoader::registerNamespace('Solarium', JPATH_LIBRARIES . DS . 'solarium-3.2.0' . DS . 'library');
function autoload($fullClassName) {
    $name_elems = explode('\\', $fullClassName);
    require __DIR__.DIRECTORY_SEPARATOR.implode(DIRECTORY_SEPARATOR, $name_elems).'.php';
}

This even supports things like: $transformerContstraint = new \Recurr\Transformer\Constraint\AfterConstraint(new DateTime());

Just put it in /vendor/Recurr/Transformer/Constraint/AfterConstraint.php

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!