International PHP Conference Berlin 2023

类的自动加载

在编写面向对象(OOP) 程序时,很多开发者为每个类新建一个 PHP 文件。 这会带来一个烦恼:每个脚本的开头,都需要包含(include)一个长长的列表(每个类都有个文件)。

spl_autoload_register() 函数可以注册任意数量的自动加载器,当使用尚未被定义的类(class)和接口(interface)时自动去加载。通过注册自动加载器,脚本引擎在 PHP 出错失败前有了最后一个机会加载所需的类。

像 class 一样的结构都可以以相同方式自动加载。包括类、接口、trait 和枚举。

警告

PHP 8.0.0 之前,可以使用 __autoload() 自动加载类和接口。然而,它是 spl_autoload_register() 的一种不太灵活的替代方法,并且 __autoload() 在 PHP 7.2.0 起弃用,在 PHP 8.0.0 起移除。

注意:

spl_autoload_register() 可以多次调用以便注册多个自动加载器。但从自动加载函数中抛出异常会中断该过程并且禁止继续执行。因此强烈建议不要从自动加载函数中抛出异常。

示例 #1 自动加载示例

本例尝试分别从 MyClass1.phpMyClass2.php 文件中加载 MyClass1MyClass2 类。

<?php
spl_autoload_register
(function ($class_name) {
    require_once 
$class_name '.php';
});

$obj  = new MyClass1();
$obj2 = new MyClass2();
?>

示例 #2 另一个例子

本例尝试加载接口 ITest

<?php

spl_autoload_register
(function ($name) {
    
var_dump($name);
});

class 
Foo implements ITest {
}

/*
string(5) "ITest"

Fatal error: Interface 'ITest' not found in ...
*/
?>

add a note

User Contributed Notes 6 notes

up
95
jarret dot minkler at gmail dot com
13 years ago
You should not have to use require_once inside the autoloader, as if the class is not found it wouldn't be trying to look for it by using the autoloader.

Just use require(), which will be better on performance as well as it does not have to check if it is unique.
up
61
str at maphpia dot com
6 years ago
This is my autoloader for my PSR-4 clases. I prefer to use composer's autoloader, but this works for legacy projects that can't use composer.

<?php
/**
* Simple autoloader, so we don't need Composer just for this.
*/
class Autoloader
{
    public static function
register()
    {
       
spl_autoload_register(function ($class) {
           
$file = str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
            if (
file_exists($file)) {
                require
$file;
                return
true;
            }
            return
false;
        });
    }
}
Autoloader::register();
up
4
info at atomosmaestoso dot com
5 months ago
Autoloading Classes with  spl_autoload_register() or spl_autoload() is the best and most modern way to securely code for API integration.

It restricts the various attacks that can be faced by using a "polyfill" or framework that is subject to data injection. Low level attacks, polyfill and framework vulnerabilities are some exploitations limited in using the core functionalities of your host programming language.

Your loop-holes and target endpoints are vastly removed to the level of programming experience of the developer - in not exposing the threats espoused to your programming language and its security protocols.

Each event you transfer data from one program to the next reveals another threat and another attack endpoint. When you are production, it is at this point composer and other tools that gather requirements specific secure integration should limit its use, such as PCI-DSS, HIPAA, or GDPR.

The use of a framework or polyfill gives an attacker hints at what point a function will access memory to produce intended results. Visiting the late L1-Cache Terminal Fault - attacks that use machine language to access memory and read what actually is happening will have all the details of what process is taking place and when.

Not to mention, when a product is open-source, the code is editable and easily compiled. Using access to machine level integrations a simply 10 second loss of time to process could well as infer the entire application has experienced an overhaul.

To deter this, and ensure maximum security for piece of mind and money-wise. The embedded resources of a programming language should be utilized at maximal capacity to prevent an overhaul on multiple endpoints. Visiting a system in use is not deletable or easily moved, removed or altered.
up
18
toi]n[enkayt[attaat]gmaal.com
2 years ago
Autoloading plain functions is not supported by PHP at the time of writing. There is however a simple way to trick the autoloader to do this. The only thing that is needed is that the autoloader finds the searched class (or any other autoloadable piece of code) from the files it goes through and the whole file will be included to the runtime.

Let's say you have a namespaced file for functions you wish to autoload. Simply adding a class of the same name to that file with a single constant property is enough to trigger the autoloader to seek for the file. Autoloading can then be triggered by accessing the constant property.

The constant could be replaced by any static property or method or by default constructor. However, I personally find a constant named 'load' elegant and informative. After all this is a workaround. Another thing to keep in mind is that this introduces an unnecessary class to the runtime. The benefit of this is that there is no need to manually include or require files containing functions by path which in turn makes code maintaining easier. Such behaviour makes it easier to alter the project structure since manual includes need not to be fixed. Only the autoloader needs to be able to locate the moved files which can be automated.

A code file containing functions.
/Some/Namespace/Functions.php
<?php
namespace Some\Namespace;

class
Functions { const load = 1; }

function
a () {
}

function
b () {
}
?>

Triggering autoloading of the file containing functions.
main.php
<?php
\Some\Namespace\Functions::load;

a ();
b ();
?>
up
12
Anonymous
12 years ago
It's worth to mention, if your operating system is case-sensitive you need to name your file with same case as in source code eg. MyClass.php instead of myclass.php
up
4
kalkamar at web dot de
14 years ago
Because static classes have no constructor I use this to initialize such classes.
The function init will (if available) be called when you first use the class.
The class must not be included before, otherwise the init-function wont be called as autoloading is not used.

<?php
function __autoload($class_name)
{
    require_once(
CLASSES_PATH.$class_name.'.cls.php');
    if(
method_exists($class_name,'init'))
       
call_user_func(array($class_name,'init'));
    return
true;
}
?>

I use it for example to establish the mysql-connection on demand.

It is also possilbe do add a destructor by adding this lines to the function:
<?php
if(method_exists($class_name,'destruct'))
   
register_shutdown_function(array($class_name,'destruct'));
?>
To Top