单利模式
解决场景就是如何在整个项目创建一个唯一对象实例
拥有一个构造函数,并且为privare
拥有一个静态成员变量用来保持类的实例
拥有一个访问这个实例的静态方法
注册树模式
注册树模式通过将对象实例注册到一棵全局的对象树上
需要的时候从对象树上采摘下来使用
把一些常用的基础类库或者对象统一挂载这个树上,不用new直接用树上取出来
后续把对象挂载到$objects上
#A.php A类
class A {
public function abc() {
return "abcd";
}
}
A类挂载到数下面
class SingwaRegister {
/*
注册树池子
*/
protected static $object = null;
/*
将对象挂到树上
*/
#第一个参数是key,第二个参数是对象
public static function set($key,$object){
self::$object[$key] = $object;
}
/*
从树上获取对象,如果没有的时候 注册
*/
public static function get($key) {
if(!isset(self::$object[$key])){
#挂载
self::$object[$key] = new $key;
}
return self::$object[$key];
}
public static function __unset($key) {
unset(self::$object[$key]);
}
}
public function register() {
$a = new \A();
\SingwaRegister::set("singwa",$a);
$abc = \SingwaRegister::get("singwa")->abc();
var_dump($abc);
}
如何理解依赖注入和控制反转
依赖注入和控制反转是同一个东西,就是编程思想不同
依赖注入主要用来减少代码间的耦合
有效分离对象和它所需的外部资源
注入谁注入谁呢
class Person {
/*
依赖 Person类依赖于 Car
注入 car类注入到Person
*/
#传入对象
public function buy($obj) {
#$bmw = new Car();
#return $bmw->pay();
return $obj->pay();
}
}
#M.php 或者Car.php
namespace di;
class M {
public function pay() {
return 345;
}
}
public function personbuy() {
$signwa = new \di\Person();
$bmw = new \di\M;
echo $singwa->buy($bmw);
}
PHP反射机制深入学习
一、什么是反射?
它是指在PHP运行状态中,扩展分析PHP程序,导出或提取出关于类、方法、属性、参数等的详细信息,包括注释。这种动态获取的信息以及动态调用对象的方法的功能称为反射API。
在PHP中,反射是指在PHP运行状态中,扩展分析PHP程序,导出或者提取出关于类、属性、方法、参数等的详细信息,包括注释。这种动态获取信息以及动态调用对象方法的功能,被称为反射API。
#文章
https://www.php.net/manual/zh/book.reflection.php
class A {
public $ab =1;
private $bc =2;
public static $instance = null;
public function abc() {
return "abcd";
}
public function abcd($a,$b) {
return $a."|".$b;
}
//
public function mf($type = 1) {
return "type:".$type;
}
public function ddd() {
return "ddd";
}
}
public function rel() {
$obj = new \A();
##建立一个A类的反射类 是反射ReflectionClass
$obje2 = new \ReflectionClass($obj);
$instance = $obj2->newInstance(); //相当于实例化这个类
//dump($instance);
#获取类当中的所有方法
$methods = $obj2->getMethods();
#获取方法的注释
foreach($methods as $method){
dump($method->getDocComment());
}
#类下的所有属性
$properties = $obj2->getProperties();
//方法一
#echo $instance->abc(1,2);
//方法二
#$method = $obj2->getMethod("abcd");
#echo $method->invokeArgs($instance,["mmm","4444m"]);
//方法三
#$method = $obj2->getMethod("ddd");
#echo $method->invoke($instance);
#判断某个方法是私有方法和共有方法
$method = new \ReflectionMethod($obj,"ddd");
if ($method->isPublic()) {
echo "ddd方法是公共";
}
}
#基于A类的反射类
##建立一个A类的反射类 是反射ReflectionClass
$obje2 = new \ReflectionClass($obj);
$instance = $obj2->newInstance(); //相当于实例化这个类
//dump($instance);
#获取类当中的所有方法
$methods = $obj2->getMethods();
#获取方法的注释
foreach($methods as $method){
dump($method->getDocComment());
}
#类下的所有属性
$properties = $obj2->getProperties();
var_dump($method->getParameters());
传入几个参数
var_dump($method->getNumberOfParameters())
方法一、不使用反射的写法
public function initialize(array $config = array(), $reset = TRUE)
{
$reflection = new ReflectionClass($this);
...
#遍历config,如果存在set_方法调用set方法,没有则直接赋值
foreach ($config as $key => $v) {
if (method_exists($this, 'set_' . $key)) {
$this->{'set_' . $key}($v);
}else{
$this->$key =$v;
}
}
这种方法有个弊端:无法判断config数组的所有key是否合法,是否是类的成员属性
方法二 框架内的实现
反射在这里的体现的好处是:可以判断类是否存在属性(hasProperty),和判断类是否存在方法(hasMethod)。
public function initialize(array $config = array(), $reset = TRUE)
{
$reflection = new ReflectionClass($this);
...
foreach ($config as $key => &$value)
{
if ($key[0] !== '_' && $reflection->hasProperty($key))
{
if ($reflection->hasMethod('set_'.$key))
{
$this->{'set_'.$key}($value);
}
else
{
$this->$key = $value;
}
}
}
容器类
Container.php
namespace di;
class Container {
/*存放容器的数据
把我们实例化的类放到这里面去 $instances
*/
public $instances = [];
/*容器中的对象实例*/
protected static $instance;
private function __construct() {
}
/*获取当前容器的实例 单利模式*/
public static function getInstance() {
if (is_null(static::$instance)) {
#new static 都是new 一个对象
static::$instance = new static;
}
return static::$instance;
}
public function set($key,$value) {
$this->instances[$key] = $value;
}
//反射机制 获取容器里面的实例
public function get($key) {
if(!empty($this->instances[$key])) {
$key = $this->instances[$key];
}
$reflect = new \ReflectionClass($key);
//获取类的构造函数 没有构造函数直接new 类就行了
$c = $reflect->getConstructor();
if(!$c) {
return new $key;
}
//获取构造函数里面的参数 $obj
$params = $c->getParameters();
if(empty($params)) {
return new $key;
}
#反射机制
foreach($prams as $param) {
#不加Car判断不到是对象形式
$class = $params->getClass();
if(!$class){
}else{
#name 就是 di\Car
$args[] = $this->get($class->name);
#var_dump($class);exit;
}
}
#再用反射
return $reflect->newInstanceArgs($args);
}
}
构造函数里面有这个类,需要转换成对象,做个反射把它实例化
到时候再去实例化某些类的时候(例如Person),把数据传递给参数就可以了
var_dump($class);exit; 打印的是
Person类下构造方法,就会把Car $obj ,场景转换成实例,通过实例调用newInstanceArgs做成实例化,它的参数就传递过来了 —下面的代码
return $reflect->newInstanceArgs($args)
Car.php
namespace di;
class Car {
public function pay() {
return 123;
}
}
Person.php
namespace di;
class Person{
#加Car 满足是对象形式 默认的是Car的类
public function __construct(Car $obj , $a = 12){
$this->obj = $obj; #是123
$this->a = $a;
}
public function buy() {
#return $this->$obj->pay();
return $this->a ."|".$this->obj->pay();
}
}
index.php
这是应用层
把实例set进去,用的时候get
键名 =>person 对象和实例 new \di\Person()
public function personbuy() {
#\di\Container::getInstance()->set("person", new \di\Person());
#\di\Container::getInstance()->set("car", new \di\Car());
\di\Container::getInstance()->set("person", "\di\Person");
\di\Container::getInstance()->set("car", "\di\Car");
#这传的事类名字 person类的
$obj = \di\Container::getInstance()->get('person');
#var_dump($obj->buy(\di\Container::getInstance()->get("car")));
dump($obj->buy());
}
use ArrayAccess; 数组一样使用对象
#接口
interface Countable {
public function count();
}
#SingwaCount.php
class SingwaCount implements Countable {
public function count() {
return 234;
}
}
#index.php
public function acount() {
$obj = new \SingwaCount;
echo count($obj);
}
获取容器里面实例分析
app 就是类名和标识
public static function get($abstract, $vars = [], $newInstance = false)
{
#本类调用make方法
return static::getInstance()->make($abstract, $vars, $newInstance);
}
if (true === $vars) {
// 总是创建新的实例化对象
$newInstance = true; #为true 每次都要实例化这个类
$vars = [];
}
#是反射
use ReflectionClass;
$this->instances 容器里面
/**
* 创建类的实例
* @access public
* @param string $abstract 类名或者标识
* @param array|true $vars 变量
* @param bool $newInstance 是否每次创建新的实例
* @return object
*/
public function make($abstract, $vars = [], $newInstance = false)
{
if (true === $vars) {
// 总是创建新的实例化对象
$newInstance = true;
$vars = [];
}
#默认第一次值都是 $this->name里面是app
$abstract = isset($this->name[$abstract]) ? $this->name[$abstract] : $abstract;
//第二次 就成为 $abstract => think\App;
#看$this->instances有没有这个key 第三次再走就会走到这,因为容器里面有this->instances[think\App]
if (isset($this->instances[$abstract]) && !$newInstance) {
#有值在容器里面就返回 可能是实例或者可能是app或者其他的
return $this->instances[$abstract];
}
#如果没有就走下面的逻辑创建,创建并放到容器在返回
if (isset($this->bind[$abstract])) {
#绑定 concrete => think\App
$concrete = $this->bind[$abstract];
#instanceof (1)判断一个对象是否是某个类的实例,(2)判断一个对象是否实现了某个接口。
#如果是命名函数走这里或者闭包时候
if ($concrete instanceof Closure) {//闭包场景
$object = $this->invokeFunction($concrete, $vars);
} else {
//$this->name['app'] = think\App;
$this->name[$abstract] = $concrete;
return $this->make($concrete, $vars, $newInstance);
}
} else {
//返回对象,没有实例
$object = $this->invokeClass($abstract, $vars);
}
if (!$newInstance) {
#是对象 $object
$this->instances[$abstract] = $object;
// $this->instances[think\App] = object;
}
return $object;
}
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 参数
* @return mixed
*/
public function invokeClass($class, $vars = [])
{
try {
//实现这个反射类
$reflect = new ReflectionClass($class);
//当中是否存在__make方法
if ($reflect->hasMethod('__make')) {
#反射这个类实现这个方法
$method = new ReflectionMethod($class, '__make');
if ($method->isPublic() && $method->isStatic()) {
$args = $this->bindParams($method, $vars);
#就实例化了
return $method->invokeArgs(null, $args);
}
}
$constructor = $reflect->getConstructor();
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
return $reflect->newInstanceArgs($args);
} catch (ReflectionException $e) {
throw new ClassNotFoundException('class not exists: ' . $class, $class);
}
}
/**
* 调用反射执行类的实例化 支持依赖注入
* @access public
* @param string $class 类名
* @param array $vars 参数
* @return mixed
*/
public function invokeClass($class, $vars = [])
{
try {
//实现这个反射类
$reflect = new ReflectionClass($class);
//当中是否存在__make方法
if ($reflect->hasMethod('__make')) {
#反射这个类实现这个方法
$method = new ReflectionMethod($class, '__make');
if ($method->isPublic() && $method->isStatic()) {
$args = $this->bindParams($method, $vars);
#就实例化了
return $method->invokeArgs(null, $args);
}
}
$constructor = $reflect->getConstructor();
$args = $constructor ? $this->bindParams($constructor, $vars) : [];
#实例化这个类
return $reflect->newInstanceArgs($args);
} catch (ReflectionException $e) {
throw new ClassNotFoundException('class not exists: ' . $class, $class);
}
}
门面模式Facade
门面为容器中的类提供了一个静态调用接口
相比于传统的静态方法调用,带来了更好的可测试性和扩展性
thinkphp/library/think/facade/Config.php
public function facade() {
dump(\think\facade\Config::get("app."));
}
thinkphp/library/think/facade/Config.php 根本没有get方法,包括父类Facade也没有get方法就会执行下面的方法
//调用实际类的方法 也就说在应用层调用一个不存在静态方法的时候就会走到__callStatic
public static function __callStatic($method, $params)
{
return call_user_func_array([static::createFacade(), $method], $params);
}
打印出
如果加一个 abcd 方法
应用层在调用 输出就下面的
/**
* 创建Facade实例
* @static
* @access protected
* @param string $class 类名或标识
* @param array $args 变量
* @param bool $newInstance 是否每次创建新的实例
* @return object
*/
protected static function createFacade($class = '', $args = [], $newInstance = false)
{
$class = $class ?: static::class;
$facadeClass = static::getFacadeClass();
if ($facadeClass) {
$class = $facadeClass;
#否则看上层self::$bind的属性,如果有就把值付给它$class
} elseif (isset(self::$bind[$class])) {
$class = self::$bind[$class];
}
if (static::$alwaysNewInstance) {
$newInstance = true;
}
#容器拿到我们想要的这么一个类,比如容器有,我就返回容器里面的实例,容器理由config实例我就返回,如果没有走容器里的创建的实例逻辑
return Container::getInstance()->make($class, $args, $newInstance);
}
好处:我们能够通过静态的方式去调用类的一些方法
facade\Singwa.php
namespace app\facade;
use think\Facade;
class Singwa extends Facade {
/**
* 获取当前Facade对应类名(或者已经绑定的容器对象标识)
* @access protected
* @return string
*/
protected static function getFacadeClass(){
return "singwa";#没有绑定Singwa
return '\app\common\Singwa';
}
}
application\common\Singwa.php
namespace app\common;
class Singwa {
public function abcd() {
return "abcd";
}
}
通过静态的方式去调用实际方法里面的内容 门面模式的思想
public function facade() {
// 实际的 singwa调用
#$obj = new \app\common\Singwa();
#echo $obj->abcd();
//需要把上面的内容转换为门面模式思想来调用
echo \app\facade\Singwa::abcd();
}
第二种 如何绑定呢
public function facade(){
\think\Faade::bind('app\facade\Sing','app\common\Sing');
dump(\app\facade\Sing::abcd());
}