1

目录结构

learner@debian:~$ tree ~/bin
/home/learner/bin
└── php
    ├── Body
    │   ├── Brain.php
    │   └── Cell
    │       └── Neuron.php
    └── main.php

3 directories, 3 files

第一个代码示例

~/bin/php/main.php:

<?php
spl_autoload_register(function ($class) {
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
    echo "----- Autoload $class from $path -----\n";
    include $path;
});

use Body\Brain;
$brain = new Brain();
?>

~/bin/php/Body/Brain.php:

<?php
namespace Body;

use Body\Cell\Neuron;

class Brain
{
    public function __construct()
    {
        $this->brain = new Neuron();
        $this->brain->talk();
    }
}
?>

~/bin/php/Body/Cell/Neuron.php:

<?php
namespace Body\Cell;
class Neuron
{
    public function talk()
    {
        echo "I am Neuron!\n";
    }
}
?>

我可以通过以下方式从我的主目录执行 main.php 并且它工作正常:

learner@debian:~$ php ~/bin/php/main.php 
----- Autoload Body\Brain from Body/Brain.php -----
----- Autoload Body\Cell\Neuron from Body/Cell/Neuron.php -----
I am Neuron!

我很惊讶这行得通。我希望它 $this->brain = new Neuron();~/bin/php/Body/Brain.php. 当遇到这一行时,自动加载器会尝试执行,但里面include 'Body/Cell/Neuron.php'没有调用这样的子目录。Body~/bin/php/Body

第二个代码示例

让我通过展示另一个使用include语句而不是自动加载器的代码示例来向您展示为什么我希望第一个代码示例失败。

~/bin/php/main.php 修改为:

<?php
include 'Body/Brain.php';
use Body\Brain;
$brain = new Brain();
?>

看到上面的代码现在缺少自动加载器,并且现在正在使用相同的包含语句,该语句之前由自动加载器执行。

~/bin/php/Body/Brain.php 修改为:

<?php
namespace Body;

include 'Body/Cell/Neuron.php';

use Body\Cell\Neuron;

class Brain
{
    public function __construct()
    {
        $this->brain = new Neuron();
        $this->brain->talk();
    }
}
?>

请注意,相同的 include 语句已添加到此代码中,该语句之前由自动加载器执行以加载Body\Cell\Neuron

尝试执行此代码会导致失败。

learner@debian:~$ php ~/bin/php/main.php 
PHP Warning:  include(Body/Cell/Neuron.php): failed to open stream: No such file or directory in /home/learner/bin/php/Body/Brain.php on line 4
PHP Warning:  include(): Failed opening 'Body/Cell/Neuron.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/learner/bin/php/Body/Brain.php on line 4
PHP Fatal error:  Class 'Body\Cell\Neuron' not found in /home/learner/bin/php/Body/Brain.php on line 12

预计会出现此故障,因为在执行 include 'Body/Cell/Neuron.php';~/bin/php/Body/Brain.php,它没有找到调用的Body子目录~/bin/php/Body

问题

我知道我可以通过编辑 ~/bin/php/Body/Brain.php使用 include 'Cell/Neuron.php';而不是 include 'Body/Cell/Neuron.php';. 但是,我的问题不是关于为什么第二个代码示例不起作用,而是关于为什么第一个代码示例起作用。

  1. 当 PHP 解释器无法include 'Body/Cell/Neuron.php'~/bin/php/Body/Brain.php第二个代码示例中执行时,自动加载器如何成功地从第一个代码示例中的同一个 PHP 文件执行相同的包含?
  2. 还是我弄错了?是不是在第一个代码示例中,自动加载器总是从哪里执行包含语句, ~/bin/php/main.php而不管类首先在哪里被使用,所以在哪里include 'Body/Cell/Neuron.php'完成 ~/bin/php/main.php并且它成功了,因为确实有一个调用的Body子目录~/bin/php?如果是这种情况,我在哪里可以阅读官方文档中的相关信息?
  3. 总结一下我们的理解,您能告诉我注册的自动加载器功能通常是从哪里执行的吗?从定义函数的脚本?从注册函数的脚本?或者来自遇到新类的脚本?
4

2 回答 2

1

好吧是这样的:

  1. 在您的第一个代码示例中,您正在定义自动加载器。它将包括所有需要的类include。想象一下,它只是将代码复制并粘贴到当前正在运行的 php 脚本中。

    现在在Brain.php文件中(它已被包含,让我说:实际上它正在运行,main.php因为它已“复制并粘贴”到main.php)中需要该类Neuron。在main.php自动加载器中定义 -> 自动加载器被调用并加载Neuron类。我想你明白了。

  2. 您的第二个示例不起作用,因为当您要包含Brain.phpPHP 解析器时,将包含Brain.phpinto之前main.php尝试包含Body/Cell/Neuron.phpinto Brain.php。你知道这是行不通的。

  3. 自动加载器是从您放置它的脚本中执行的。在你的例子中main.php

(我希望我没有让你更困惑)

于 2013-10-06T08:16:50.653 回答
0

我在这里回答我自己的问题。通过一些实验,在我看来,注册的自动加载器函数是在定义它的脚本的上下文中执行的,而不是在它被注册为自动加载器的地方,也不是在实例化或使用新类的地方。

但是,由于这只是一个试图证明我的理论的实验,如果有人可以通过引用官方文档来回答我的问题,我会将其标记为正确答案。

实验设置

目录结构:

learner@debian:~$ tree ~/bin
/home/learner/bin
└── php
    ├── AutoLoader
    │   ├── AutoLoader.php
    │   └── ClassLoader
    │       └── ClassLoader.php
    ├── Body
    │   ├── Brain.php
    │   └── Cell
    │       └── Neuron.php
    └── main.php

5 directories, 5 files

~/bin/php/AutoLoader/ClassLoader/ClassLoader.php包含名为 的自动加载函数ClassLoader::loadClass

~/bin/php/AutoLoader/AutoLoader.php通过调用注册自动加载功能spl_autoload_register('Autoloader\\ClassLoader\\ClassLoader::loadClass');

源代码

~/bin/php/AutoLoader/AutoLoader.php

<?php
namespace AutoLoader;

include 'ClassLoader/ClassLoader.php';

class AutoLoader
{
    static public function registerAutoloader()
    {
        spl_autoload_register(
                'Autoloader\\ClassLoader\\ClassLoader::loadClass');
    }
}
?>

~/bin/php/AutoLoader/ClassLoader/ClassLoader.php

~/bin/php/Body/Brain.php

<?php
namespace Body;

use Body\Cell\Neuron;

class Brain
{
    public function __construct()
    {
        $this->brain = new Neuron();
        $this->brain->talk();
    }
}
?>

~/bin/php/Body/Cell/Neuron.php

<?php
namespace Body\Cell;
class Neuron
{
    public function talk()
    {
        echo "I am Neuron!\n";
    }
}
?>

~/bin/php/main.php

<?php
include 'AutoLoader/AutoLoader.php';

use AutoLoader\AutoLoader;
use Body\Brain;

AutoLoader::registerAutoloader();
$brain = new Brain();
?>

实验一

执行上面的代码会报错:

learner@debian:~$ php ~/bin/php/main.php 
----- Autoload Body\Brain from Body/Brain.php -----
PHP Warning:  include(Body/Brain.php): failed to open stream: No such file or directory in /home/learner/bin/php/AutoLoader/ClassLoader/ClassLoader.php on line 9
PHP Warning:  include(): Failed opening 'Body/Brain.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/learner/bin/php/AutoLoader/ClassLoader/ClassLoader.php on line 9
PHP Fatal error:  Class 'Body\Brain' not found in /home/learner/bin/php/main.php on line 8

观察:自动加载器无法包含Body/Brain.php来自~/bin/php/Body/Brain.php.

实验二

Body目录移动到~/bin/php/AutoLoader并执行代码仍然会导致错误:

learner@debian:~$ mv ~/bin/php/Body ~/bin/php/AutoLoader
learner@debian:~$ tree ~/bin
/home/learner/bin
└── php
    ├── AutoLoader
    │   ├── AutoLoader.php
    │   ├── Body
    │   │   ├── Brain.php
    │   │   └── Cell
    │   │       └── Neuron.php
    │   └── ClassLoader
    │       └── ClassLoader.php
    └── main.php

5 directories, 5 files
learner@debian:~$ php ~/bin/php/main.php 
----- Autoload Body\Brain from Body/Brain.php -----
PHP Warning:  include(Body/Brain.php): failed to open stream: No such file or directory in /home/learner/bin/php/AutoLoader/ClassLoader/ClassLoader.php on line 9
PHP Warning:  include(): Failed opening 'Body/Brain.php' for inclusion (include_path='.:/usr/share/php:/usr/share/pear') in /home/learner/bin/php/AutoLoader/ClassLoader/ClassLoader.php on line 9
PHP Fatal error:  Class 'Body\Brain' not found in /home/learner/bin/php/main.php on line 8

观察:自动加载器无法包含Body/Brain.php来自~/bin/php/AutoLoader/Body/Brain.php.

实验三

Body目录移动到~/bin/php/AutoLoader/ClassLoader并执行代码会成功。

learner@debian:~$ mv ~/bin/php/AutoLoader/Body ~/bin/php/AutoLoader/ClassLoader/Body
learner@debian:~$ tree ~/bin
/home/learner/bin
└── php
    ├── AutoLoader
    │   ├── AutoLoader.php
    │   └── ClassLoader
    │       ├── Body
    │       │   ├── Brain.php
    │       │   └── Cell
    │       │       └── Neuron.php
    │       └── ClassLoader.php
    └── main.php

5 directories, 5 files
learner@debian:~$ php ~/bin/php/main.php 
----- Autoload Body\Brain from Body/Brain.php -----
----- Autoload Body\Cell\Neuron from Body/Cell/Neuron.php -----
I am Neuron!

观察:自动加载器可能包括Body/Brain.php来自~/bin/php/AutoLoader/ClassLoader/Body/Brain.php.

结论

三个实验的结论似乎表明自动加载器包含与~/bin/php/AutoLoader/ClassLoader/目录相关的文件,即包含定义自动加载器功能的脚本的目录。

于 2013-10-06T09:09:52.460 回答