Laravel Real-Time Facades 内部实现机制的分解
我已经很久没有写过技术文章了。我刚查过,已经一年多了!¯_(ツ)_/¯
我从来没有那么忙过,但在我能用文字写作的同时,我却学不到任何新东西。我宁愿在其他文章中解释我在那几个月里所做的事情。让我们看一看今天的文章。
Real-Time Facades
Laravel的 Real-Time facades 可以像处理facade一样处理常规类,而无需定义facade类。但如果你不熟悉facade,那么你可以看看下面的文章,我相信你会了解Laravel的facade,以及如何创建自己的外观。
Real-time 门面代理就像魔术方法一样,要了解魔术,你需要了解自动加载类在 PHP 中是如何工作的。
自动加载类
在编写 PHP 应用程序时,一开始文件的数量很少。可以使用 include 或 require 表达式来引用类文件。 但是随着项目的增长,类也会增长,并且编写 require 表达式来加载这些文件没有任何意义。在这种情况下,你应该使用 SPL 自动加载。有没有想过为什么我们在使用 composer 时在我们的脚本中需要 vendor/autoload.php
? 如果你以前从未想过,让我告诉你,它会为我们自动加载文件。
现在,让我们动手吧!这就是我们项目简单的样子。
项目结构
- app 目录包含
App
类。 - src 目录包含
Source
类。 - 基础/工作目录包含
Current
类。
Current
类扩展了 Source
类,Source
类扩展了 App
类。 查看以下代码库。 只是几个简单的类。
现在,要实例化 Current
类,我们需要该文件以及相关文件。 在不使用 SPL 自动加载时,我们的 index.php 应该如下所示。
<?php
require './app/App.php';
require './src/Source.php';
require './Current.php';
echo (new Current())->sayHello('anik');
这里,require 表达式的顺序很重要。除此以外,以任何顺序交换第 3、4、5 行都不起作用。因此,你必须要注意类的导入顺序。
现在,让我们看看下面的例子。
<?php
function autoload_src($file): bool
{
echo sprintf('autoload_src: Looking for "%s".%s', $file, PHP_EOL);
if (strtolower(($extract = explode('\\', $file))[0]) === 'source') {
return loadFileIfExists('./src', $extract[1] ?? '');
}
return false;
}
function autoload_app($file): bool
{
echo sprintf('autoload_app: looking for "%s".%s', $file, PHP_EOL);
if (strtolower(($extract = explode('\\', $file))[0]) === 'app') {
return loadFileIfExists('./app', $extract[1] ?? '');
}
return false;
}
function autoload_current($file): bool
{
echo sprintf('autoload_current: looking for "%s".%s', $file, PHP_EOL);
return loadFileIfExists('.', $file);
}
function loadFileIfExists(string $directory, string $filename): bool
{
if (file_exists($path = sprintf('%s/%s.php', rtrim($directory, '/'), $filename))) {
include($path);
return true;
}
return false;
}
spl_autoload_register('autoload_app');
spl_autoload_register('autoload_src');
spl_autoload_register('autoload_current', prepend: true);
echo sprintf('autoloader order: %s.%s', json_encode(spl_autoload_functions()), PHP_EOL);
//spl_autoload_unregister('autoload_two');
//var_dump(spl_autoload_functions());
echo (new Current())->sayHello('anik');
第 3、14 和 25 行 声明了三个方法。 这些方法中的每一个都需要引用来自正确目录的正确文件。
autoload_src
方法负责从 src 目录中 include 文件。autoload_app
方法负责从 app 目录中 include 文件。autoload_current
方法负责从 current 目录中 include 文件。
第 43、44、45 行 注册我们定义的方法。 [**spl_autoload_register**](https://www.php.net/manual/en/function.spl-autoload-register.php)
接受第一个参数,一个 回调。 第二个参数 $throw
不应该被触及 如果使用PHP ≥8.0,最后, $prepend
第三个参数,决定回调应该放在队列的顶部还是队列的底部。
第 47 行,打印出可用的注册自动加载函数。
第 51 行, echo 类方法的返回值。
现在,如果我们执行脚本,它将产生以下输出。
❯ php index.php
autoloader order:["autoload_current","autoload_app","autoload_src"].
autoload_current: looking for "Current".
autoload_current: looking for "Source\Source".
autoload_app: looking for "Source\Source".
autoload_src: Looking for "Source\Source".
autoload_current: looking for "App\App".
autoload_app: looking for "App\App".
Current says: "Hello anik."
如果我们在输出的第一行检查自动加载器的顺序,第 43、44、45 行 现在就有意义了。
当我们实例化 Current
类时,该文件由 autoload_current
方法自动加载。Current
类扩展了 Source
类。它也应该在我们的脚本中是必需的,并且是自动完成的。只有 autoload_src
可以加载 Source
类,并且加载器方法位于自动加载器队列的第三位。这就是为什么 autoload_current,autoload_app 在加载 Source
类时在 autoload_src 之前运行。App
类也会发生同样的事情,因为 Source
类需要它。
所以,现在我相信你已经了解了 PHP 是如何自动加载文件的。
回到 Real-Time 门面代理
-
Laravel 使用 Composer 和 PSR-4 自动加载。
-
在 基于 cli 的 入口点 artisan 和 基于网络请求的 入口点 public/index .php,需要
vendor/autoload.php
,然后注册一个自动加载器,负责加载所有类文件。 -
对于 artisan 和 index.php,它 注册所需的内核
-
根据你使用的 SAPI,它调用 [HTTP内核的引导程序](github.com/laravel/framework /blob/538bc3f0d7787a728f0d5f153c2fa5effb65bf7b/src/Illuminate/Foundation/Http/Kernel.php#L150-L155) 或 控制台内核的引导程序
-
两个内核都在某一时刻调用 RegisterFacades 类的 引导方法。
-
反过来,这会调用 AliasLoader::prependToLoaderStack。 这是
spl_autoload_register
方法注册一个新的自动加载器并将其推送到自动加载器队列头部。 -
然后,当调用带有
Facade
前缀命名空间的类时,它会调用 AliasLoader::load 方法,如果可以,它会检查并加载门面类。
最后,您将非Facade类作为Facade实例处理。
也许你可以争论一下,这样做有什么意义?有什么特权吗?我不知道。我从来没有像facade那样使用任何常规类。此外,在过去的6年里,我一直在与门面被禁用的 Lumen 合作。 所以,我回答不了你的问题。 ¯_(ツ)_/¯
这就是本文的全部内容。 希望您了解它的实际工作原理。 但我更希望你自己去练习一下。 也应该可以使用 MyFacade\
代替 Facade\
前置命名空间。 你能想出怎么做吗?
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。