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生成静态页
Nov 25 PHP
PHP5 安装方法
Jan 15 PHP
php中截取字符串支持utf-8
Jan 18 PHP
php Static关键字实用方法
Jun 04 PHP
php 截取字符串并以零补齐str_pad() 函数
May 07 PHP
PHP缓存技术的使用说明
Aug 06 PHP
解析用PHP读写音频文件信息的详解(支持WMA和MP3)
May 10 PHP
CodeIgniter图像处理类的深入解析
Jun 17 PHP
8个必备的PHP功能开发
Oct 02 PHP
CodeIgniter分页类pagination使用方法示例
Mar 28 PHP
详解php中空字符串和0之间的关系
Oct 23 PHP
PHP实现微信提现(企业付款到零钱)
Aug 01 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修改密码方法总结
2008/03/25 PHP
PHP Header用于页面跳转要注意的几个问题总结
2008/10/03 PHP
19个Android常用工具类汇总
2014/12/30 PHP
php输出图像的方法实例分析
2017/02/16 PHP
php post json参数的传递和接收处理方法
2018/05/31 PHP
Laravel自动生成UUID,从建表到使用详解
2019/10/24 PHP
详解Laravel服务容器的绑定与解析
2019/11/05 PHP
use jscript List Installed Software
2007/06/11 Javascript
浅析Cookie中的Path与domain
2013/12/18 Javascript
javascript组合使用构造函数模式和原型模式实例
2015/06/04 Javascript
js实现的简洁网页滑动tab菜单效果代码
2015/08/24 Javascript
浅谈angular懒加载的一些坑
2016/08/20 Javascript
JavaScript基础——使用Canvas绘图
2016/11/02 Javascript
jQuery UI Grid 模态框中的表格实例代码
2017/04/01 jQuery
vue组件生命周期详解
2017/11/07 Javascript
浅谈React中组件间抽象
2018/01/27 Javascript
VUE.js实现动态设置输入框disabled属性
2019/10/28 Javascript
Webpack设置环境变量的一些误区详解
2019/12/19 Javascript
vue实现分页加载效果
2019/12/24 Javascript
微信小程序:报错(in promise) MiniProgramError
2020/10/30 Javascript
[04:52]2015国际邀请赛LGD战队晋级之路
2015/08/14 DOTA
Pycharm学习教程(3) 代码运行调试
2017/05/03 Python
python爬虫中get和post方法介绍以及cookie作用
2018/02/08 Python
对Python中的@classmethod用法详解
2018/04/21 Python
使用实现XlsxWriter创建Excel文件并编辑
2018/05/04 Python
Python中一些不为人知的基础技巧总结
2018/05/19 Python
在Pycharm中设置默认自动换行的方法
2019/01/16 Python
Django1.11自带分页器paginator的使用方法
2019/10/31 Python
遇到的Mysql的面试题
2014/06/29 面试题
若通过ObjectOutputStream向一个文件中多次以追加方式写入object,为什么用ObjectInputStream读取这些object时会产生StreamCorruptedException?
2016/10/17 面试题
财务与信息服务专业推荐信
2013/11/28 职场文书
庆祝新中国成立65周年“向国旗敬礼”网上签名寄语
2014/09/27 职场文书
2015年服务员个人工作总结
2015/05/27 职场文书
2016学校先进集体事迹材料
2016/02/29 职场文书
有趣的二维码:使用MyQR和qrcode来制作二维码
2021/05/10 Python
PostgreSQL自动更新时间戳实例代码
2021/11/27 PostgreSQL