PHP - 为什么代码中对象实例化的不同位置会产生不同的结果?
PHP - Why do different position of object instiation in code give different results?
请注意以下所有示例中include_once
语句的位置:
这个有效:
include_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
echo $request->getMethod();
我认为这行不通,但确实如此:
use Symfony\Component\HttpFoundation\Request;
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
但这失败了:
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
这也是:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
use Symfony\Component\HttpFoundation\Request;
echo $request->getMethod();
我猜只要代码中 use
和 include_once
在 class 是 used/instantiated 之前,代码就可以正常工作。
对吗?
流程的详细解释将不胜感激。
use
语句不调用自动加载器,或检查 class 是否存在,它只是将 "name" 拉入当前命名空间。在您尝试使用它之前,不会调用自动加载器,PHP 也不会查找 class 以查看它是否存在。
PHP 脚本的执行分为两个阶段:首先编译脚本,如果没有语法错误,编译器会生成一系列所谓的 "opcodes"。然后,解释器启动并开始执行脚本;这意味着它一个一个地获取操作码,解释它们并执行由它们编码的操作。
只有主脚本在开始执行前编译,包含的文件(使用 include
函数之一)在执行期间编译。
让我们看看您的脚本会发生什么。
include_once
include/include_once/require/require_once
语句生成的代码告诉解释器:"stop the execution of the current script here, load and compile this file and, if it doesn't contain syntax errors, execute its code before continuing this script".
就是这样,包含的文件不会在主脚本的编译阶段被读取,而只有在运行时需要时(并且如果)才被读取。在这段代码中:
if (false) {
include 'a.php';
}
从未读取文件 a.php
的内容;更重要的是,解释器甚至不关心文件是否存在。它仅在 include
语句即将执行时才开始关心 a.php
但由于 if (false)
测试,这从未发生过。
使用
use
语句用于创建别名。这样的别名只是程序员和编译器之间的约定,编译器不会为其生成任何代码。
通过使用语句:
use Symfony\Component\HttpFoundation\Request;
程序员告诉编译器:"hey, I want you to know that later in this file when you find the token Request
I mean \Symfony\Component\HttpFoundation\Request
but I prefer to use only Request
instead because it's shorter".
use
语句不会在生成的代码中留下任何痕迹。它们在运行时不存在。代码:
use Symfony\Component\HttpFoundation\Request;
$request = new Request();
等同于:
$request = new \Symfony\Component\HttpFoundation\Request();
为他们生成相同的代码;编译时,第一种形式"transformed"变成第二种形式
自动加载器
文件vendor/autoload.php
的内容(可能)是Composer生成的自动加载器。当代码到达对未知 class 的引用时由解释器执行的 autoloader is a callable (function or class method) registered using spl_autoload_register()
。自动加载器接收完整的 class 名称(带有命名空间),其目的是使 class 可用。它通常知道在哪里可以找到 class(在文件中)。可以注册多个自动加载器,解释器一个接一个地调用它们,直到 class 可用或全部失败。
它是如何运行的
让我们替换您的代码变体中的别名,看看我们得到了什么。
变体 #1 和 #2 产生相同的代码:
include_once __DIR__ . '/vendor/autoload.php';
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
echo $request->getMethod();
运行期间:
include_once
语句加载并注册自动加载器;
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals()
- class \Symfony\Component\HttpFoundation\Request
未知,解释器调用包含声明 class 的文件的自动加载器;然后 class 的方法 createFromGlobals
被执行并且它 returns 一个 \Symfony\Component\HttpFoundation\Request
; 类型的对象
$request
不是 null
并且它的 class 实现了方法 getMethod()
;该方法被调用,它的值returns被echo
显示,大家都很高兴。
变体 #3:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
运行方式:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
- class \Symfony\Component\HttpFoundation\Request
尚未声明并且没有注册自动加载器 - 解释器无法找到 class,那里没办法继续了。
PHP Fatal error: Class 'Symfony\Component\HttpFoundation\Request' not found.
变体#4:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
别名没有被替换,因为它是在使用后声明的。
运行期间:
include_once
语句加载并注册自动加载器;
$request = Request::createFromGlobals();
- class Request
尚未定义;解释器调用自动加载器,但自动加载器找不到它;口译员无法继续。
Fatal error: Class 'Request' not found
请注意以下所有示例中include_once
语句的位置:
这个有效:
include_once __DIR__ . '/vendor/autoload.php';
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
echo $request->getMethod();
我认为这行不通,但确实如此:
use Symfony\Component\HttpFoundation\Request;
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
但这失败了:
use Symfony\Component\HttpFoundation\Request;
$request = Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
这也是:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
use Symfony\Component\HttpFoundation\Request;
echo $request->getMethod();
我猜只要代码中 use
和 include_once
在 class 是 used/instantiated 之前,代码就可以正常工作。
对吗?
流程的详细解释将不胜感激。
use
语句不调用自动加载器,或检查 class 是否存在,它只是将 "name" 拉入当前命名空间。在您尝试使用它之前,不会调用自动加载器,PHP 也不会查找 class 以查看它是否存在。
PHP 脚本的执行分为两个阶段:首先编译脚本,如果没有语法错误,编译器会生成一系列所谓的 "opcodes"。然后,解释器启动并开始执行脚本;这意味着它一个一个地获取操作码,解释它们并执行由它们编码的操作。
只有主脚本在开始执行前编译,包含的文件(使用 include
函数之一)在执行期间编译。
让我们看看您的脚本会发生什么。
include_once
include/include_once/require/require_once
语句生成的代码告诉解释器:"stop the execution of the current script here, load and compile this file and, if it doesn't contain syntax errors, execute its code before continuing this script".
就是这样,包含的文件不会在主脚本的编译阶段被读取,而只有在运行时需要时(并且如果)才被读取。在这段代码中:
if (false) {
include 'a.php';
}
从未读取文件 a.php
的内容;更重要的是,解释器甚至不关心文件是否存在。它仅在 include
语句即将执行时才开始关心 a.php
但由于 if (false)
测试,这从未发生过。
使用
use
语句用于创建别名。这样的别名只是程序员和编译器之间的约定,编译器不会为其生成任何代码。
通过使用语句:
use Symfony\Component\HttpFoundation\Request;
程序员告诉编译器:"hey, I want you to know that later in this file when you find the token Request
I mean \Symfony\Component\HttpFoundation\Request
but I prefer to use only Request
instead because it's shorter".
use
语句不会在生成的代码中留下任何痕迹。它们在运行时不存在。代码:
use Symfony\Component\HttpFoundation\Request;
$request = new Request();
等同于:
$request = new \Symfony\Component\HttpFoundation\Request();
为他们生成相同的代码;编译时,第一种形式"transformed"变成第二种形式
自动加载器
文件vendor/autoload.php
的内容(可能)是Composer生成的自动加载器。当代码到达对未知 class 的引用时由解释器执行的 autoloader is a callable (function or class method) registered using spl_autoload_register()
。自动加载器接收完整的 class 名称(带有命名空间),其目的是使 class 可用。它通常知道在哪里可以找到 class(在文件中)。可以注册多个自动加载器,解释器一个接一个地调用它们,直到 class 可用或全部失败。
它是如何运行的
让我们替换您的代码变体中的别名,看看我们得到了什么。
变体 #1 和 #2 产生相同的代码:
include_once __DIR__ . '/vendor/autoload.php';
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
echo $request->getMethod();
运行期间:
include_once
语句加载并注册自动加载器;$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals()
- class\Symfony\Component\HttpFoundation\Request
未知,解释器调用包含声明 class 的文件的自动加载器;然后 class 的方法createFromGlobals
被执行并且它 returns 一个\Symfony\Component\HttpFoundation\Request
; 类型的对象
$request
不是null
并且它的 class 实现了方法getMethod()
;该方法被调用,它的值returns被echo
显示,大家都很高兴。
变体 #3:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
include_once __DIR__ . '/vendor/autoload.php';
echo $request->getMethod();
运行方式:
$request = \Symfony\Component\HttpFoundation\Request::createFromGlobals();
- class\Symfony\Component\HttpFoundation\Request
尚未声明并且没有注册自动加载器 - 解释器无法找到 class,那里没办法继续了。PHP Fatal error: Class 'Symfony\Component\HttpFoundation\Request' not found.
变体#4:
include_once __DIR__ . '/vendor/autoload.php';
$request = Request::createFromGlobals();
echo $request->getMethod();
别名没有被替换,因为它是在使用后声明的。
运行期间:
include_once
语句加载并注册自动加载器;$request = Request::createFromGlobals();
- classRequest
尚未定义;解释器调用自动加载器,但自动加载器找不到它;口译员无法继续。Fatal error: Class 'Request' not found