本文来自pilishen.com----原文链接; 欢迎作客我们的php&Laravel学习群:109256050
该篇属于《Laravel底层核心技术实战揭秘》这一课程《laravel底层核心概念解析》这一章的扩展阅读。由于要真正学好laravel底层,有些PHP相关的知识必须得了解,考虑到学员们的基础差异,为了避免视频当中过于详细而连篇累牍,故将一些laravel底层实现相关的PHP知识点以文章形式呈现,供大家预习和随时查阅。
php传参类型声明(type declaration)
今天,我们不在laravel里,自己搞个PHP文件测试:
<?php
class Bar {}
function injection($bar)
{
var_dump($bar);
}
injection();
?>
复制代码
我们想的是把Class Bar
的一个实例传进去,但是这样肯定会报错:
Fatal error: Uncaught ArgumentCountError: Too few arguments to function injection(), 0 passed in
复制代码
好吧,那就给你传个参数进去:
$bar = new Bar;
injection($bar);
复制代码
这样就可以了:
object(Bar)[1]
复制代码
问题是,这个时候,我怎么确保你传递进去的就是Class Bar
的实例呢?我们完全可以传个别的进去啊:
class Foo {}
$bar = new Foo;
injection($bar);
复制代码
这个时候也不会报错:
object(Foo)[1]
复制代码
由于这个时候我们的function injection($bar)
期望传入的只是一个变量$bar
,所以如果我们需要根据这个变量$bar
的类型来操作,我们就得这样:
<?php
class Bar {}
class Foo {}
function injection($bar)
{
if($bar instanceof Bar){
echo 'Bar type';
}elseif($bar instanceof Foo){
echo 'Foo type';
}
}
$bar = new Foo;
injection($bar);
?>
复制代码
这样的话就会输出:
Foo type
复制代码
看到这儿,相信你应该能想起我们在《Laravel底层核心技术实战揭秘》自定义常见的系统Exception时用过:
public function render($request, Exception $e)
{
if($e instanceof TokenMismatchException){
...
}
if($e instanceof ModelNotFoundException){
...
}
if($e instanceof FatalErrorException){
...
}
...
}
复制代码
但是,更经常的是,我们在往一个方法里传参的时候,我们只期望是某一种类型(type),比如你传进来的必须是一个User
Model的实例,传其他的进来就没有意义,这个时候就需要用到PHP的类型声明(type declaration),允许函数在调用时要求参数为特定类型。 如果给出的值类型不对,那么将会产生一个错误: 在PHP 5中,这将是一个可恢复的致命错误,而在PHP 7中将会抛出一个TypeError异常。
好吧,这没什么稀奇的,相信很多人都已经知道了,所以之前的例子现在就可以写成:
<?php
class Bar {}
class Foo {}
function injection(Bar $bar)
{
echo get_class($bar);
}
$bar = new Foo;
injection($bar);
?>
复制代码
此时就会输出:
Fatal error: Uncaught TypeError: Argument 1 passed to injection() must be an instance of Bar, instance of Foo given,
复制代码
如果我们将$bar = new Foo;
改成$bar = new Bar;
就会输出:
Bar
复制代码
这样我们就限定了依赖注入(dependency injection)
的类型(type hinted
),嗯,没啥高科技。
常见的依赖注入(dependency injection)——方法注入(method injection)
这里需要注意的是,我们在执行injection($bar);
前,手动实例化了一个Bar
Class,所以这个时候相当于我们执行的是:injection(new Bar);
,如果这个injection()
方法是在一个class当中,因为这样更符合实际情况,例如:
<?php
class Bar {}
class Foo {}
class Baz {
function injection(Bar $bar)
{
echo get_class($bar);
}
}
$baz = new Baz;
$bar = new Bar;
$result = $baz->injection($bar);
//相当于$result = (new Baz)->injection(new Bar);
?>
复制代码
这就是一个(单一)方法注入(method injection)
。
常见的依赖注入(dependency injection)——构造函数依赖注入(constructor injection)
如果这个object参数还经常用在其他同类(class)方法里,那么我们通常放在一个构造函数(constructor)
里,就成了这样子:
class Baz {
protected $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
public function check()
{
echo get_class($this->bar);
}
}
复制代码
这个时候我们就不能直接$baz = new Baz;
了,得是这样调用:
$baz = new Baz(new Bar);
$result = $baz->check();
复制代码
看上去没错,挺简单,这就是构造函数依赖注入(constructor injection)
,这些我们laravel实战优雅入门:任务管理系统(一)都讲烂了。
层层依赖的问题
但如果Class Bar
还有它自己的依赖呢?比如说:
class Foo {}
class Bar {
protected $foo;
public function __construct(Foo $foo)
{
$this->foo = $foo;
}
}
复制代码
稍微复杂点的类有几个依赖很正常,那么现在我们的调用就得这样了:
$baz = new Baz(new Bar(new Foo));
$result = $baz->check();
复制代码
看出问题来了吗?也就是说现在我们还只是个简单的示例,实际当中,当你的依赖逐渐增多,这个class依赖那个,那个又依赖另一个的时候,层层依赖,稍微大点的系统很常见的事儿。
但是如果我们每次实例化一个对象,就得手动去传递或者说注入一大堆依赖,就得不断地去new
来new
去,那还了得?
如何自动地去引入相应地依赖,不论我们的依赖有多少层?
或者说,Laravel能不能做到这一点呢?都说Laravel IOC Container
,又称Service Container(服务容器)
,很强大,那么是否强大到自动给我们构建依赖呢?
限于篇幅,我们下一节讲~