douju6651 2013-10-06 07:18
浏览 45

注册的自动加载器函数中的`include`语句在哪个脚本的上下文中运行?

Directory structure

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

3 directories, 3 files

First code example

~/bin/php/main.php:

<?php
spl_autoload_register(function ($class) {
    $path = str_replace('\\', DIRECTORY_SEPARATOR, $class) . '.php';
    echo "----- Autoload $class from $path -----
";
    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!
";
    }
}
?>

I am able to execute main.php from my home directory in the following manner and it works fine:

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!

I am surprised that this works. I expect it to fail when $this->brain = new Neuron(); is executed in ~/bin/php/Body/Brain.php. When this line is encountered, the autoloader tries to execute include 'Body/Cell/Neuron.php' but there is no such subdirectory called Body inside ~/bin/php/Body.

Second code example

Let me show you why I expect the first code example to fail by showing another code example that is using include statements instead of autoloader.

~/bin/php/main.php modified to:

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

See that the autoloader is missing now from the above code, and the same include statement is being used now which was being executed by the autoloader earlier.

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

<?php
namespace Body;

include 'Body/Cell/Neuron.php';

use Body\Cell\Neuron;

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

Note that the same include statement has been added to this code that was being executed by the autoloader earlier to load Body\Cell\Neuron.

Trying to execute this code results in failure.

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

This failure is expected because while executing include 'Body/Cell/Neuron.php'; in ~/bin/php/Body/Brain.php, it doesn't find a subdirectory called Body in ~/bin/php/Body.

Question

I know that I can easily fix the second code example by editing ~/bin/php/Body/Brain.php to use include 'Cell/Neuron.php'; instead of include 'Body/Cell/Neuron.php';. However, my question is not about why the second code example doesn't work, but about why the first code example works.

  1. When the PHP interpreter is unable to include 'Body/Cell/Neuron.php' from ~/bin/php/Body/Brain.php in the second code example, how does the autoloader succeed in doing the same include from the same PHP file in the first code example?
  2. Or am I mistaken? Could it be that in the first code example the autoloader is always executing the include statements from ~/bin/php/main.php regardless of where the classes are first being used, so include 'Body/Cell/Neuron.php' is done at ~/bin/php/main.php and it succeeds because there indeed is a subdirectory called Body in ~/bin/php? If this is the case, where can I read about it in the official documentation?
  3. To summarize our understanding, could you please tell me where the registered autoloader function is executed from, in general? From the script where the function is defined? From the script where the function is registered? Or from the script where a new class is encountered?
  • 写回答

2条回答 默认 最新

  • doufan2541 2013-10-06 08:16
    关注

    Okay it's so:

    1. In your first code example you are defining the autoloader. It will include every needed class with include. Imagine it just copy and pastes the code in currently running php script.

      Now in the Brain.php file (which has been included, let me say: actually it's running in main.php because it has been "copy and pasted" into main.php) the class Neuron is needed. In main.php the autoloader is denfined -> the autoloader is called and loads the Neuron class. I think you understand.

    2. Your second example doesn't work because when you are going to include Brain.php the PHP parser will before including Brain.php into main.php, try to include Body/Cell/Neuron.php into Brain.php. And you know that this can't work.

    3. The autloader is excuted from/in the script you placed it. In your example in main.php

    (I hope I hasn't confused you more)

    评论

报告相同问题?

悬赏问题

  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号
  • ¥15 基于单片机的靶位控制系统
  • ¥15 真我手机蓝牙传输进度消息被关闭了,怎么打开?(关键词-消息通知)
  • ¥15 装 pytorch 的时候出了好多问题,遇到这种情况怎么处理?
  • ¥20 IOS游览器某宝手机网页版自动立即购买JavaScript脚本
  • ¥15 手机接入宽带网线,如何释放宽带全部速度