Laravel框架源码解析之反射的使用详解


Posted in PHP onMay 14, 2020

本文实例讲述了Laravel框架源码解析之反射的使用。分享给大家供大家参考,具体如下:

前言

PHP的反射类与实例化对象作用相反,实例化是调用封装类中的方法、成员,而反射类则是拆封类中的所有方法、成员变量,并包括私有方法等。就如“解刨”一样,我们可以调用任何关键字修饰的方法、成员。当然在正常业务中是建议不使用,比较反射类已经摒弃了封装的概念。

本章讲解反射类的使用及Laravel对反射的使用。

反射

反射类是PHP内部类,无需加载即可使用,你可以通过实例化 ReflectionClass 类去使用它。

方法

这里列举下PHP反射类常用的方法

方法名 注释
ReflectionClass::getConstant 获取定义过的一个常量
ReflectionClass::getConstants 获取一组常量
ReflectionClass::getConstructor 获取类的构造函数
ReflectionClass::getDefaultProperties 获取默认属性
ReflectionClass::getDocComment 获取文档注释
ReflectionClass::getEndLine 获取最后一行的行数
ReflectionClass::getFileName 获取定义类的文件名
ReflectionClass::getInterfaceNames 获取接口(interface)名称
ReflectionClass::getMethods 获取方法的数组
ReflectionClass::getModifiers 获取类的修饰符
ReflectionClass::getName 获取类名
ReflectionClass::getNamespaceName 获取命名空间的名称
ReflectionClass::getParentClass 获取父类

等等等等.... 所有关于类的方法、属性及其继承的父类、实现的接口都可以查询到。
详细文档请参考官网: http://php.net/manual/zh/class.reflectionclass.php

栗子

<?php
 namespace A\B;
 
 class Foo { }
 
 $function = new \ReflectionClass('stdClass');
 
 var_dump($function->inNamespace());
 var_dump($function->getName());
 var_dump($function->getNamespaceName());
 var_dump($function->getShortName());
 
 $function = new \ReflectionClass('A\\B\\Foo');
 
 var_dump($function->inNamespace());
 var_dump($function->getName());
 var_dump($function->getNamespaceName());
 var_dump($function->getShortName());
?>

输出结果

bool(false)
string(8) "stdClass"
string(0) ""
string(8) "stdClass"

bool(true)
string(7) "A\B\Foo"
string(3) "A\B"
string(3) "Foo"

Laravel

Laravel在实现服务容器加载时使用了反射类。现在我们开启“解刨”模式

入口文件

index.php

$app = require_once __DIR__.'/../bootstrap/app.php';

/*
|--------------------------------------------------------------------------
| Run The Application
|--------------------------------------------------------------------------
|
| Once we have the application, we can handle the incoming request
| through the kernel, and send the associated response back to
| the client's browser allowing them to enjoy the creative
| and wonderful application we have prepared for them.
|
*/

$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);

$response = $kernel->handle(
 $request = Illuminate\Http\Request::capture()
);

$response->send();

$kernel->terminate($request, $response);

是引用语句发生的下一行调用了make方法。各位很清楚,make方法用于解析类,所有make方法的实现一定是在引用的文件内。

bootstrap\app.php

$app = new Illuminate\Foundation\Application(
 realpath(__DIR__.'/../')
);

laravel开始加载它的核心类,所有的实现从 Illuminate\Foundation\Application 开始。

Illuminate\Foundation\Application

public function make($abstract, array $parameters = [])
{
  $abstract = $this->getAlias($abstract);

  if (isset($this->deferredServices[$abstract]) && ! isset($this->instances[$abstract])) {
   $this->loadDeferredProvider($abstract);
  }

  return parent::make($abstract, $parameters);
}

在核心类中你可能准确的查找到make方法的存在,它加载了服务提供者随后调用了父类的方法make,要知道作为独立的模块 “服务容器”是绝对不能写在核心类的。懂点设计模式的都很清楚。

Illuminate\Container\Container

$api = $this->app->make('HelpSpot\API',['id'=>1]); 为例来讲解

// 真正的make方法,它直接调用了resolve继续去实现make的功能
// $abstract = 'HelpSpot\API'
public function make($abstract, array $parameters = [])
{
 // $abstract = 'HelpSpot\API'
 return $this->resolve($abstract, $parameters);
}

...

protected function resolve($abstract, $parameters = [])
{
 ...
 // 判断是否可以合理反射
 // $abstract = 'HelpSpot\API'
 if ($this->isBuildable($concrete, $abstract)) {
  // 实例化具体实例 (实际并不是实例化,而是通过反射“解刨”了)
  $object = $this->build($concrete);
 } else {
  $object = $this->make($concrete);
 }
 ...
}

public function build($concrete)
{
  // $concrete = 'HelpSpot\API'
  if ($concrete instanceof Closure) {
   return $concrete($this, $this->getLastParameterOverride());
  }
  // 实例化反射类
  $reflector = new ReflectionClass($concrete);

  // 检查类是否可实例化
  if (! $reflector->isInstantiable()) {
   return $this->notInstantiable($concrete);
  }

  $this->buildStack[] = $concrete;

  // 获取类的构造函数
  $constructor = $reflector->getConstructor();
  
  if (is_null($constructor)) {
   array_pop($this->buildStack);

   return new $concrete;
  }

  $dependencies = $constructor->getParameters();

  $instances = $this->resolveDependencies(
   $dependencies
  );

  array_pop($this->buildStack);
   
  // 从给出的参数创建一个新的类实例。
  return $reflector->newInstanceArgs($instances);
}

可见一个服务容器就加载成功了。

希望本文所述对大家基于Laravel框架的PHP程序设计有所帮助。

PHP 相关文章推荐
PHP Ajax实现页面无刷新发表评论
Jan 02 PHP
php代码把全角数字转为半角数字
Dec 10 PHP
计算一段日期内的周末天数的php代码(星期六,星期日总和)
Nov 12 PHP
windows环境下php配置memcache的具体操作步骤
Jun 09 PHP
php版小黄鸡simsimi聊天机器人接口分享
Jan 26 PHP
ThinkPHP3.1查询语言详解
Jun 19 PHP
PHP使用GIFEncoder类处理gif图片实例
Jul 01 PHP
功能强大的php分页函数
Jul 20 PHP
Linux平台PHP5.4设置FPM线程数量的方法
Nov 09 PHP
php实现不通过扩展名准确判断文件类型的方法【finfo_file方法与二进制流】
Apr 18 PHP
基于laravel缓冲cache的用法详解
Oct 23 PHP
php中使用array_filter()函数过滤数组实例讲解
Mar 03 PHP
PHP 数组操作详解【遍历、指针、函数等】
May 13 #PHP
ThinkPHP5 框架引入 Go AOP,PHP AOP编程项目详解
May 12 #PHP
php中用unset销毁变量并释放内存
May 10 #PHP
php屏蔽错误及提示的方法
May 10 #PHP
php判断数组是否为空的实例方法
May 10 #PHP
通过PHP实现获取访问用户IP
May 09 #PHP
如何通过PHP实现Des加密算法代码实例
May 09 #PHP
You might like
关于mysql 字段的那个点为是定界符
2007/01/15 PHP
PHP5中虚函数的实现方法分享
2011/04/20 PHP
php 截取字符串并以零补齐str_pad() 函数
2011/05/07 PHP
解析thinkphp import 文件内容变量失效的问题
2013/06/20 PHP
PHP二维数组排序的3种方法和自定义函数分享
2014/04/09 PHP
javascript+php实现根据用户时区显示当地时间的方法
2015/03/11 PHP
laravel 时间格式转时间戳的例子
2019/10/11 PHP
基于Jquery的温度计动画效果
2010/06/18 Javascript
利用腾讯的ip地址库做ip物理地址定位
2010/07/24 Javascript
基于Jquery的回车成tab焦点切换效果代码(Enter To Tab )
2010/11/14 Javascript
jQuery对于显示和隐藏等常用状态的判断方法
2014/12/13 Javascript
javascript实现给定半径求出圆的面积
2015/06/26 Javascript
jQuery+Ajax实现无刷新分页
2015/10/30 Javascript
动态加载js、css的简单实现代码
2016/05/26 Javascript
HTML5 实现的一个俄罗斯方块实例代码
2016/09/19 Javascript
微信小程序 JS动态修改样式的实现代码
2017/02/10 Javascript
解决Vue调用springboot接口403跨域问题
2019/09/02 Javascript
微信小程序3D轮播实现代码
2019/09/19 Javascript
[01:03:13]VG vs Pain 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
python中的实例方法、静态方法、类方法、类变量和实例变量浅析
2014/04/26 Python
Python下载懒人图库JavaScript特效
2015/05/28 Python
mvc框架打造笔记之wsgi协议的优缺点以及接口实现
2018/08/01 Python
浅谈python3发送post请求参数为空的情况
2018/12/28 Python
PyQt5 在label显示的图片中绘制矩形的方法
2019/06/17 Python
tensorflow之变量初始化(tf.Variable)使用详解
2020/02/06 Python
Python爬虫爬取百度搜索内容代码实例
2020/06/05 Python
Python读取yaml文件的详细教程
2020/07/21 Python
python实现每天自动签到领积分的示例代码
2020/08/18 Python
详解HTML5将footer置于页面最底部的方法(CSS+JS)
2018/10/11 HTML / CSS
草莓网化妆品加拿大网站:Strawberrynet Canada
2016/09/20 全球购物
Sarenza德国:法国最大的时尚鞋和包包网上商店
2019/06/08 全球购物
2014小学语文教师个人工作总结
2014/12/03 职场文书
《小蝌蚪找妈妈》教学反思
2016/02/23 职场文书
2019自荐信范文集锦!
2019/07/03 职场文书
MySQL锁机制
2021/04/05 MySQL
Redis集群节点通信过程/原理流程分析
2022/03/18 Redis