PHP Conference Japan 2024

spl_autoload

(PHP 5 >= 5.1.0, PHP 7, PHP 8)

spl_autoload__autoload() 的默认实现

描述

spl_autoload(string $class, ?string $file_extensions = null): void

此函数旨在用作 __autoload() 的默认实现。如果没有其他指定,并且 spl_autoload_register() 没有参数调用,则 spl_autoload() 将用于以后对 __autoload() 的任何调用。

参数

class

正在实例化的类的名称。调用函数时,带命名空间的类名将传递给参数。 class 不包含完全限定标识符的开头反斜杠。

file_extensions

默认情况下,它检查所有 include_path 是否包含通过小写类名附加文件名扩展名 .inc.php 构建的文件名。

返回值

不返回任何值。

错误/异常

当找不到类且没有注册其他自动加载器时,抛出 LogicException

变更日志

版本 描述
8.0.0 file_extensions 现在可以为空。

添加注释

用户贡献的注释 9 条注释

simast at gmail dot com
15 年前
请注意,默认的自动加载实现是用 C 编写的,始终比您本地的 PHP 实现略快。

这是一个使用任何配置的默认实现的技巧

<?php

// 自定义类目录
define('CLASS_DIR', 'class/')

// 将您的类目录添加到 include path
set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR);

// 您可以使用此技巧使自动加载器查找常用的“My.class.php”类型文件名
spl_autoload_extensions('.class.php');

// 使用默认自动加载实现
spl_autoload_register();
?>

这也开箱即用地支持命名空间。因此,您可以编写类似“use My\Name\Object”的代码,它将映射到“class/My/Name/Object.class.php”文件路径!
theking2 at king dot ma
2 年前
命名空间和类名都不区分大小写。这可能会导致在_区分大小写_的操作系统/文件系统中自动加载出现问题。

考虑这段代码
<?php declare(strict_types=1);
set_include_path(get_include_path() . PATH_SEPARATOR . 'class/');
spl_autoload_extensions('.class.php');
spl_autoload_register();

$foobar = new \foo\bar();
?>

和一个类文件 /class/foo/bar.class.php
<?php declare(strict_types=1);
namespace
Foo;

class
Bar {
public function
__construct() {
echo
'Map constructed';
}
}
?>

这将正常工作,无论操作系统/文件系统是否区分大小写。

名为 ./class/Foo/Bar.class.php 的文件仅在不区分大小写的情况下才能找到,因为默认的类加载器将使用 mb_strtolower() 将类名转换为小写以查找类文件名。

顺便说一句,类文件的内容将等同于上面的内容
<?php declare(strict_types=1);
namespace
foo;

class
bar {
public function
__construct() {
echo
'Map constructed';
}
}
?>

类 /foo/bar 和 /Foo/Bar 是同一个。
EVODelavega
10 年前
我只是想对 simast at gmail dot com 的注释做出回应:虽然他说的 C 的性能优于 PHP 是对的,但他的建议是微优化。我不完全反对微优化代码,但如果你这样做,就彻底地做。

<?php

// 自定义类目录
define('CLASS_DIR', 'class/')

// 将您的类目录添加到 include path
set_include_path(get_include_path().PATH_SEPARATOR.CLASS_DIR);

这会将 include path 添加到 PHP 将扫描类文件的路径的末尾,导致大量未命中 (file-not-found) 才能真正查看 CLASS_DIR。
更合理的方法是编写

set_include_path(
CLASS_DIR.
PATH_SEPARATOR,
get_include_path()
);
Luke Scott
13 年前
如果你想充分利用带有 APC 缓存的自动加载,请不要使用 spl_autoload。它使用相对路径,因此即使使用 apc.stat=0 也将执行 stat(或者根本不起作用)。

相反,创建一个自定义函数并使用带有绝对路径的 require/include(使用 spl_autoload_register 注册它)。

不要使用 *_once 函数或相对路径。这将比 spl_autoload 失败得更厉害。

还要避免使用 file_exists 和 is_file。这也会执行 stat。

为什么 stat 不好?因为它们访问文件系统。PHP 有一个 stat 缓存可以提供帮助,但它会破坏 apc.stat = 0 的目的。

记住保持自定义自动加载函数简单也很重要。这是我的 Loader 类

<?php

class Loader
{
public static function
registerAutoload()
{
return
spl_autoload_register(array(__CLASS__, 'includeClass'));
}

public static function
unregisterAutoload()
{
return
spl_autoload_unregister(array(__CLASS__, 'includeClass'));
}

public static function
includeClass($class)
{
require(
PATH . '/' . strtr($class, '_\\', '//') . '.php');
}
}

?>

另需指出,如果`require/include`(而非`*_once`)在全局作用域中进行(并且不是条件性的),APC 会对使用相对路径的 `require/include` 进行优化。因此,显式包含您知道每次请求都会使用的文件是个好主意(但不要使用`*_once`)。例如,您可以向上面的类中添加一个“`registerProfiledAutoload`”函数,并跟踪您正在包含的内容,以帮助您确定可以显式包含的内容(在开发期间,而不是生产期间)。关键是尽量不要过度依赖自动加载。

如果您必须使用相对路径,并且不关心必须小写文件名,那么 `spl_autoload` 非常有效。
daniel
14年前
注意,此函数会将查找的类名转换为小写,如果找不到 `Foo_Bar.php`,请不要困惑。

此外,与大多数其他自动加载代码片段不同,此函数不会将下划线转换为斜杠。

class Foo_Bar {}
将加载 `foo_bar.php`,不会尝试加载 `foo/bar.php`。

您可以使用以下方法解决此问题:
spl_autoload_register(function($class) { return spl_autoload(str_replace('_', '/', $class));});
Philip
11年前
文档在说明“正在实例化的类(和命名空间)的小写名称”时有点不清楚。

它的实际含义是,参数可以采用任何大小写,但在 PHP 开始查找文件之前,它将转换为小写。这可能是因为在 PHP 中,类名是不区分大小写的(函数名和命名空间也是如此),因此它需要转换为某种规范格式。
safak_ozpinar at NOSPAM dot yahoo dot com
17年前
请注意,文件扩展名的顺序对于性能很重要。您应该将您最喜欢的文件扩展名的优先级设置最高,或者只为您的类文件使用一个扩展名。查看此示例

一些类文件

ClassA.php
<?php class ClassA { var $val = 'Hello from class "ClassA"'; } ?>
ClassB.php
<?php class ClassB { var $val = 'Hello from class "ClassB"'; } ?>
ClassC.php
<?php class ClassC { var $val = 'Hello from class "ClassC"'; } ?>
ClassD.php
<?php class ClassD { var $val = 'Hello from class "ClassD"'; } ?>
ClassE.php
<?php class ClassE { var $val = 'Hello from class "ClassE"'; } ?>

1. 简单示例
<?php
// 默认优先级:.inc .php
for($n=65; $n<70; $n++) {
$className = 'Class'.chr($n);
spl_autoload($className);
$ins = new $className;
echo
$ins->val.'<br>';
}
// 4.2 毫秒
?>

2. 更改优先级
<?php
spl_autoload_extensions
('.php,.inc');
// 新优先级:.php .inc
for($n=65; $n<70; $n++) {
$className = 'Class'.chr($n);
spl_autoload($className);
$ins = new $className;
echo
$ins->val.'<br>';
}
// 1.4 毫秒
?>

或者,您可以使用此简单的函数,它对优先级较低的扩展名运行速度更快 :)
<?php
function my_autoload($className, $extList='.inc,.php') {
$ext = explode(',',$extList);
foreach(
$ext as $x) {
$fname = $className.$x;
if(@
file_exists($fname)) {
require_once(
$fname);
return
true;
}
}
return
false;
}

for(
$n=65; $n<70; $n++) {
$className = 'Class'.chr($n);
my_autoload($className);
$ins = new $className;
echo
$ins->val.'<br>';
}
// 2.6 毫秒
?>
---
Safak Ozpinar - 伊斯坦布尔大学,计算机工程
Ivan Stojmenovic
12年前
一个简单的例子,展示了如何在 MVC、框架应用程序中使用 `spl_autoload` 函数。例如,将使用 Loader 类。


<?php

class Loader
{

/**
* 控制器目录路径
*
* @var Array
* @access protected
*/
protected $_controllerDirectoryPath = array();

/**
* 模型目录路径
*
* @var Array
* @access protected
*/
protected $_modelDirectoryPath = array();

/**
* 库目录路径
*
* @var Array
* @access protected
*/
protected $_libraryDirectoryPath = array();


/**
* 构造函数
* 常量包含模型、视图、控制器和库目录的完整路径。
*
* @Constant MPATH,VPATH,CPATH,LPATH
*/

public function __construct()
{
$this->modelDirectoryPath = MPATH;
$this->viewDirectoryPath = VPATH;
$this->controllerDirectoryPath = CPATH;
$this->libraryDirectoryPath = LPATH;

spl_autoload_register(array($this,'load_controller'));
spl_autoload_register(array($this,'load_model'));
spl_autoload_register(array($this,'load_library'));

log_message('debug',"Loader Class Initialized");
}

/**
*-----------------------------------------------------
* 加载库
*-----------------------------------------------------
* 加载库的方法。
* 此方法返回类对象。
*
* @library String
* @param String
* @access public
*/
public function load_library($library, $param = null)
{
if (
is_string($library)) {
return
$this->initialize_class($library);
}
if (
is_array($library)) {
foreach (
$library as $key) {
return
$this->initialize_class($library);
}
}
}

/**
*-----------------------------------------------------
* 初始化类
*-----------------------------------------------------
* 初始化类的方法
* 此方法返回新对象。
* 此方法可以使用 (数组) 初始化多个类
*
* @library String|Array
* @param String
* @access public
*/
public function initialize_class($library)
{
try {
if (
is_array($library)) {
foreach(
$library as $class) {
$arrayObject = new $class;
}
return
$this;
}
if (
is_string($library)) {
$stringObject = new $library;
}else {
throw new
ISException('类名必须是字符串。');
}
if (
null == $library) {
throw new
ISException('您必须输入类的名称。');
}
} catch(
Exception $exception) {
echo
$exception;
}
}

/**
* 自动加载控制器类
*
* @param string $class
* @return object
*/

public function load_controller($controller)
{
if (
$controller) {
set_include_path($this->controllerDirectoryPath);
spl_autoload_extensions('.php');
spl_autoload($class);
}
}


/**
* 自动加载模型类
*
* @param string $class
* @return object
*/

public function load_models($model)
{
if (
$model) {
set_include_path($this->modelDirectoryPath);
spl_autoload_extensions('.php');
spl_autoload($class);
}
}

/**
* 自动加载库类
*
* @param string $class
* @return object
*/

public function load_library($library)
{
if (
$library) {
set_include_path($this->libraryDirectoryPath);
spl_autoload_extensions('.php');
spl_autoload($class);
}
}



}

?>
contato at felipebarth dot com dot br
12年前
<?php
/*
* 定义负责加载类的函数,
* 替换旧的 __autoload。
* ROOT 是系统根路径的常量
*/
spl_autoload_extensions('.class.php');
spl_autoload_register('loadClasses');

function
loadClasses($className)
{

if(
file_exists(ROOT_DIR.DS.'controller/'.$className.'.class.php' ) ){
set_include_path(ROOT_DIR.DS.'controller'.DS);
spl_autoload($className);
}
elseif(
file_exists('model/'.$className.'.class.php' ) ){
set_include_path(ROOT_DIR.DS.'model'.DS);
spl_autoload($className);
}elseif(
file_exists('view/'.$className.'.class.php' ) ){
set_include_path(ROOT_DIR.DS.'view'.DS);
spl_autoload($className );
}else
{
set_include_path(ROOT_DIR.DS.'lib'.DS);
spl_autoload($className );
}
}
?>
To Top