php autoloading

Keywords: PHP

Automatic loading of php

Principle of Automatic Loading

When we use a class defined by another file, we traditionally require XXX.php

//Good.php
<?php
  class Good{
    //......
  }
 
//use.php
<?php
  require 'Good.php';   //Included in the document    
  $good = new Good();   //Now you can use the Good class

But it's really annoying to write require!! Now you can use an auto-loading function to automatically help us to require the required files according to the rules we defined when we found that the class was not defined.

//Good.php
<?php
  class Good{
    //......
  }

//use.php
<?php
function __autoload($className){
  require $className . '.php';  //Define the rules contained
}

$good = new Good(); //You can use the Good class and find that it is undefined and will automatically call the _autoload function. Now'Good'is passed in as a parameter to the _autoload function to contain the required files.

spl_autoload_register

_ The autoload function can't solve all the problems. Now the project is very complex. An autoload function alone can't meet the needs, because different modules may use different automatic loading rules.

spl_autoload_register registers functions into the SPL autoload function queue, which actually creates the queue of _autoload functions and executes them one by one in the order in which they are defined. In contrast, _ autoload() can be defined only once.

Function prototype

bool spl_autoload_register ([ callable $autoload_function [, bool $throw = true [, bool $prepend = false ]]] )
  • autoload_function

Autoload function to be registered. If no parameters are provided, the default implementation function spl_autoload() of autoload is automatically registered.

  • throw

This parameter sets whether spl_autoload_register() throws an exception when autoload_function cannot be registered successfully.

  • prepend

If true, spl_autoload_register() adds a function to the top of the queue, not to the end of the queue.

Function usage

sql_autoload_resister('load_function'); //Function name
sql_autoload_resister(array('load_object', 'load_function')); //Classes and static methods
sql_autoload_resister('load_object::load_function'); //Static calls to classes and methods

//After php 5.3, you can also support anonymous functions like this.
spl_autoload_register(function($className){
    if (is_file('./lib/' . $className . '.php')) {
        require './lib/' . $className . '.php';
    }
});

note: The original _autoload function fails after spl_autoload_register is used, and if you need to continue using it, you need to register it as shown.

 if (function_exists('__autoload')) {
   spl_autoload_register('__autoload');
 }

//. . . . . . 

 spl_autoload_register('your_autoload_function');   //Now register another

Whether or not to continue searching in the auto-loading function queue does not depend on the return value of the loading function and whether or not the file is required, only the file that the real require needs will stop.

//Good.php is located under the app folder
<?php
  class Good{
    //......
  }


//Another.php, located under the use peer folder
<?php
class Another{
}


//use.php
<?php
function load1($className)
{
    echo "load1\n";
    if (file_exists($className.'.php')) {
        echo 'found: '.$className.PHP_EOL;
        require $className;
    } else {
        echo 'not found: '.$className.PHP_EOL;
    }
}

function load2($className)
{
    echo "load2\n";
    require 'Another.php';
    echo 'require another class'.PHP_EOL;
    return true;
}

function load3($className)
{
    echo "load3\n";
    if (file_exists('app/'.$className.'.php')) {
        echo 'found '.$className.PHP_EOL;
        require 'app/'.$className.'.php';
    } else {
        echo 'not found: '.$className.PHP_EOL;
    }
}


spl_autoload_register('load1');
spl_autoload_register('load2');
spl_autoload_register('load3');

$a = new Good();

The output is as follows

load1
not found: Good
load2
require another class
load3
found Good

All registered functions can be obtained using spl_autoload_functions().

Array
(
    [0] => load1
    [1] => load2
    [2] => load3
)

Unregistered autoload function spl_autoload_unregister

spl_autoload_unregister('load1');

spl_autoload() is the default implementation of the _autoload() function

A default implementation of autoload() is provided. If the spl_autoload_register() function is not invoked with any parameters, it will be automatically used in future autoload() calls.

Automatic loading and namespace

PSR-4 Automatic Loading Specification

For complete specifications, please refer to PSR-4: Autoloader

Standard class names should have the following forms

 \<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>

It needs to be emphasized that:

  • The sliding line in any part of a complete class name has no special meaning.
  • All class names must be case sensitive
  • All class names must be case sensitive

When a file is loaded according to a namespace:

  • In a complete class name, the first namespace separator is removed, and one or more consecutive namespaces and sub-namespaces in front must correspond to at least one "file base directory" as a "namespace prefix".
  • Subnamespaces immediately after the namespace prefix must match the corresponding "file base directory", where the namespace separator will act as a directory separator.
  • The class name at the end must be the same name as the corresponding file suffixed with. php.
  • The implementation of autoloader must not throw exceptions, must not trigger error information at any level, and should not have a return value.

Example

complete class name namespace prefix File base directory File path
\Acme\Log\Writer\File_Writer Acme\Log\Writer ./acme-log-writer/lib/ ./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\Status Aura\Web /path/to/aura-web/src/ /path/to/aura-web/src/Response/Status.php
\Symfony\Core\Request Symfony\Core ./vendor/Symfony/Core/ ./vendor/Symfony/Core/Request.php
\Zend\Acl Zend /usr/includes/Zend/ /usr/includes/Zend/Acl.php

Implementation examples

Using anonymous functions

<?php
/**
 * An example of a project-specific implementation.
 *
 * After registering this autoload function with SPL, the following line
 * would cause the function to attempt to load the \Foo\Bar\Baz\Qux class
 * from /path/to/project/src/Baz/Qux.php:
 *
 *      new \Foo\Bar\Baz\Qux;
 *
 * @param string $class The fully-qualified class name.
 * @return void
 */
spl_autoload_register(function ($class) {
    // project-specific namespace prefix
    $prefix = 'Foo\\Bar\\';

    // base directory for the namespace prefix
    $base_dir = __DIR__ . '/src/';

    // does the class use the namespace prefix?
    $len = strlen($prefix);
    if (strncmp($prefix, $class, $len) !== 0) {
        // no, move to the next registered autoloader
        return;
    }

    // get the relative class name
    $relative_class = substr($class, $len);

    // replace the namespace prefix with the base directory, replace namespace
    // separators with directory separators in the relative class name, append
    // with .php
    //The next line is crucial.
    $file = $base_dir . str_replace('\\', '/', $relative_class) . '.php';

    // if the file exists, require it
    if (file_exists($file)) {
        require $file;
    }

});

Reference material:

Posted by rrijnders on Sat, 06 Jul 2019 17:49:41 -0700