主要内容:
- 静态绑定应用场景-调用上下文
- 魔术方法
- 重载(访问拦截器) - 属性、方法
- 事件委托:方法重新定向(o:让别的类去完成任务)
- 实战:链式查询(数据库查询构造器)
1. 静态绑定应用场景-调用上下文
<?php
// 后期静态绑应用场景: 静态继承的上下文
// 调用上下文 ==== 调用者
// 动态绑定静态成员的调用者/调用上下文
// 父类
class Base
{
public static function index()
{
return __METHOD__;
}
public static function fetch()
{
// return self::index();
return static::index();
}
// 在父类中, 尽可能少写或不写self,而是用static来代替,以提升代码的通用性
}
echo Base::fetch();
// 静态方法调用用:: 普通方法用->
echo '<hr>';
// 子类
class User extends Base
{
public static function index()
{
return __METHOD__;
}
}
// 子类调用
echo User::fetch();
2. 魔术方法
- 魔术方法: 由某一个事件/行为来自动的触发,不允许用户主动调用
- 魔术方法的前面是两个下划线__
<?php
class Product
{
public $name = '手机';
public $price = 5800;
}
// 类的实例被创建出来的时候,应该有一个确定的初始状态
$product = new Product();
echo $product->name . ': ' . $product->price . '<hr>';
$product->name = '电脑';
$product->price = 9000;
echo $product->name . ': ' . $product->price . '<hr>';
/* 应该有一个方法,可以在类中实现对类的实例的状态进行初始化,对象的初始状态
这个方法就是构造方法,并且这个方法还必须且只能在对象被new的时候自动调用
*/
class Product1
{
public $name;
public $price;
// 构造方法: 魔术方法, 公共方法
public function __construct($name, $price)
{
// 1. 生成一个对象。这个命令及第3个命令都是默认生成的,不需要写。
// $this = new self($name, $price);
// 2. 给这个对象的属性初始化
$this->name = $name;
$this->price = $price;
// 实例化的同时,可以自动调用一些方法(例如这个里面自定义的方法getInfo)
echo $this->getInfo();
// 3. 返回这个新对象
// return $this;
}
// 输出
public function getInfo()
{
return $this->name . ': ' . $this->price;
}
}
new Product1('显示器', 3500);
//因为里面有直接返回及echo,所以直接new就可以展示出来。
3. 重载(访问拦截器) - 属性
- 重载: “访问拦截器”
- 类: 二类成员, 属性和方法,所以也有二种重载: 属性重载和方法重载
- 换个叫法: 属性拦截器 和 方法拦截器
get(), set(), isset(), unset()
四个步骤:
- 从类的外部访问类中不存在或者无权限访问的属性的时候,会被拒绝
- 如果类中存在__get($propName)魔术方法的时候会调用它来处理这个非法访问
- 属性访问拦截器就是借助了__get()方法,拦截了用户对类内部的属性的访问,动态生成访问接口/方法
- 再创建一个属性的访问方法,如getName(), getPrice(),这个方法名称应该有一定的规律
总的如下:
$product->price ====> __get() ===> 动态生成getPrice()类方法名称===>调用getPrice()方法来处理
<?php
class Product
{
private $name;
private $price;
// 不含税价
private $taxRate = 0.06;
// 构造方法
public function __construct($name, $price)
{
$this->name = $name;
$this->price = $price;
}
/*__get($name):
当外部访问一个不存在或者无权限访问的属性的时候会自动调用__get
- 调用一个类内部的方法对外部的属性访问进行过滤(更安全)
1. 根据属性名称生成对应的属性访问接口方法
name ===> get+ Name, price ===> get+ Price(首字母大写)
2. 调用这个处理属性访问的方法
*/
public function __get($name)
{
$method = 'get' . ucfirst($name);//首字母大写
return method_exists($this, $method) ? $this->$method() : null;
}
/* 使用__set()魔术方法可以将用户对属性的更新操作进行重定向
调用一个类内部的方法对外部的属性更新修改进行过滤
1. 根据属性名称生成对应的属性访问接口方法
// name ===> set+ Name, price ===> set+ Price
2. 调用这个处理属性访问的方法
*/
public function __set($name, $value) //多了value进行赋值
{
$method = 'set' . ucfirst($name);
return method_exists($this, $method) ? $this->$method($value) : null;
}
// 类中为私有成员 提供了一个访问接口来过滤外部的访问请求
// o: 这个地方应该是服务于上面的__get和__set
private function getName()
{
return mb_substr($this->name, 0, 10); //从0开始取name的前10位字节
}
private function getPrice()
{
return $this->price + $this->price * $this->taxRate;
}
// 属性修改接口
private function setName($value)
{
return $this->name = trim($value);
}
private function setPrice($value)
{
return $this->price = $value *(1-$this->taxRate);
}
}
$product = new Product('显示器 27, 2020款, Dell, 144Hz', 3500);
// echo $product->name . ': ' . $product->price;
// 这个方法实际项目中应该在类的内部 调用
// echo $product->getName();
// 这种方法更直观
echo $product->name , '... : 含税价: ' , $product->price, ' 元';
$product->name = '外套';
$product->price = 780;
echo '<hr>';
echo $product->name, ' : 含税价: ' , $product->price, ' 元';
4. 重载(访问拦截器) - 方法
<?php
// 方法拦截器,当从类的外部,访问类中不存在或无权限访问的方法的时候,会自动调用它
class User
{
// 方法拦截器也是一个魔术方法__call(方法名称, 参数数组)
public function __call(string $name, array $args)
{
printf('方法名: %s(), 参数 [%s]', $name, implode(', ', $args));
//implode为将array转化为字符串。
}
// 静态方法拦截器: __callSatic(方法名称, 参数数组)
public static function __callStatic(string $name, array $args)
{
printf('方法名: %s(), 参数 [%s]', $name, implode(', ', $args));
}
}
$user = new User;
$user->hello('a', 'b', 'c');
echo '<hr>';
User::demo(1,2,3,4);
5. 事件委托:方法重新定向
<?php
class Base
{
public function write(...$args) //...为展开
{
printf('方法名: %s(), 参数 [%s]',__METHOD__, implode(', ', $args));
}
public static function fetch(...$args)
{
printf('方法名: %s(), 参数 [%s]',__METHOD__, implode(', ', $args));
}
}
// 工作类
class Work
{
// 事件委托时,重定向到的目标类
private $base;
// 将$base初始化
public function __construct(Base $base)
{
$this->base = $base;
}
// 方法拦截器,将$this->write()重定向到$this->base->write()
public function __call($name, $args)
{
// 将$this->$name()重定向到$this->base->$name()
if (method_exists($this->base, $name))
// return $this->base->$name($name, $args); 这个也可以,但基本上不用
// 用回调的方式来调用: call_user_func_array(函数, 参数数组)
// 如果是一个对象的方法,应该使用数组的方式: [对象, '方法名']
return call_user_func_array([$this->base, 'write'], $args);
}
// 方法拦截器,将self::fetch()重定向到Base::fetch()
public static function __callStatic($name, $args)
{
if (method_exists('Base', $name))
return call_user_func_array(['Base', 'fetch'], $args);
// call_user_func_array()的介绍见下面
}
}
$base = new Base();
$work = new Work($base);
$work->write(1,2,3);
$work::fetch('abc', 'ppp', 'www');
- call_user_func_array
call_user_func_array — 调用回调函数,并把一个数组参数作为回调函数的参数
call_user_func_array ( callable $callback , array $param_arr ) : mixed
把第一个参数作为回调函数(callback)调用,把参数数组作(param_arr)为回调函数的的参数传入。
callback
被调用的回调函数。
param_arr
要被传入回调函数的数组,这个数组得是索引数组。
返回值
返回回调函数的结果。如果出错的话就返回FALSE
6. 实战:链式查询(数据库查询构造器)
- 方法委托/方法拦截器 实战: 数据库查询构造器(链式查询)
<?php
// 查询类
class Query
{
// 连接对象
protected $db;
// 数据表
protected $table;
// 字段列表
protected $field;
// 记录数量
protected $limit;
// 构造方法: 连接数据库
public function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
// 设置默认的数据表名称
public function table($table)
{
$this->table = $table;
return $this;
}
// 设置默认的字段名称
public function field($field)
{
$this->field = $field;
return $this;
}
// 链式方法: 设置查询数量
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
// 生成查询语句
protected function getSql()
{
return sprintf('SELECT %s FROM %s LIMIT %s', $this->field, $this->table, $this->limit);
}
// 执行查询
public function select()
{
return $this->db->query($this->getSql())->fetchAll(PDO::FETCH_ASSOC);
}
}
// 数据据操作类
class DB
{
// 静态方法委托
public static function __callStatic($name, $args)
{
// 获取到查询类的对象: new Query()
$dsn = 'mysql:host=localhost;dbname=phpedu';
$username = 'root';
$password = 'root';
$query = new Query($dsn, $username, $password);
// 直接跳到Query类中的具体方法来调用
return call_user_func([$query, $name], ...$args);
}
}
$result = DB::table('users')
// 字段列表
->field('id,name,email')
->limit(5)
// 开始查询
->select();
print_r($result);
7. 实战
- select
<?php
// 0727作业
// 1. 实例演示方法拦截器的__isset(), __unset()//时间问题,这个职能后面再做了。
// 2. 实例演示查询构造器中的inset(),update(),delete()方法
// 查询类
class Query
{
protected $db;
protected $table;
protected $field;
protected $limit;
public function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
public function table($table)
{
$this->table = $table;
return $this;
}
public function field($field)
{
$this->field = $field;
return $this;
}
public function limit($limit)
{
$this->limit = $limit;
return $this;
}
// 生成查询语句
protected function getSql()
{
return sprintf('SELECT %s FROM %s LIMIT %s', $this->field, $this->table, $this->limit);
}
// 执行查询
public function select()
{
return $this->db->query($this->getSql())->fetchAll(PDO::FETCH_ASSOC);
}
}
// 数据据操作类
class DB
{
// 静态方法委托
public static function __callStatic($name, $args)
{
// 获取到查询类的对象: new Query()
$dsn = 'mysql:host=localhost;dbname=liangtest';
$username = 'liang';
$password = '123456';
$query = new Query($dsn, $username, $password);
// 直接跳到Query类中的具体方法来调用
return call_user_func([$query, $name], ...$args);
}
}
$result = DB::table('my_list')
// 字段列表
->field('id,name,age,phone')
->limit(6)
// 开始查询
->select();
print_r($result);
- insert
<?php
// 0727作业
// 1. 实例演示方法拦截器的__isset(), __unset()
// 2. 实例演示查询构造器中的inset(),update(),delete()方法
// 查询类
class Query
{
protected $db;
protected $table;
protected $field;
protected $limit;
public function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
public function table($table)
{
$this->table = $table;
return $this;
}
public function field($field)
{
$this->field = $field;
return $this;
}
public function values($values)
{
$this->values = $values;
return $this;
}
// 生成查询语句
protected function getSql()
{
return sprintf('INSERT INTO %s (%s) VALUES(%s)', $this->table, $this->field, $this->values);
}
// 执行查询
public function insert()
{
return $this->db->query($this->getSql());
}
}
class DB
{
public static function __callStatic($name, $args)
{
$dsn = 'mysql:host=localhost;dbname=liangtest';
$username = 'liang';
$password = '123456';
$query = new Query($dsn, $username, $password);
return call_user_func([$query, $name], ...$args);
}
}
$result = DB::table('my_list')
->field('id,name,age,phone')
->values("'202','王三元','19','234321'")//这个地方是大括号套小括号,好不容易试错试出来的。
->insert();
print_r($result);
- update
<?php
// 0727作业
// 1. 实例演示方法拦截器的__isset(), __unset()
// 2. 实例演示查询构造器中的inset(),update(),delete()方法
// 查询类
class Query
{
protected $db;
protected $table;
protected $field;
protected $limit;
public function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
public function table($table)
{
$this->table = $table;
return $this;
}
public function field($field)
{
$this->field = $field;
return $this;
}
public function values($values)
{
$this->values = $values;
return $this;
}
// 生成查询语句
protected function getSql()
{
return sprintf('UPDATE %s SET %s = %s', $this->table, $this->field, $this->values);
}
// 执行查询
public function update()
{
return $this->db->query($this->getSql());
}
}
class DB
{
public static function __callStatic($name, $args)
{
$dsn = 'mysql:host=localhost;dbname=liangtest';
$username = 'liang';
$password = '123456';
$query = new Query($dsn, $username, $password);
return call_user_func([$query, $name], ...$args);
}
}
$result = DB::table('my_list')
->field('phone')
->values('234322')
->update();
print_r($result);
- delete
<?php
// 0727作业
// 1. 实例演示方法拦截器的__isset(), __unset()
// 2. 实例演示查询构造器中的inset(),update(),delete()方法
// 查询类
class Query
{
protected $db;
protected $table;
protected $field;
protected $limit;
public function __construct($dsn, $username, $password)
{
$this->connect($dsn, $username, $password);
}
// 连接数据库
private function connect($dsn, $username, $password)
{
$this->db = new PDO($dsn, $username, $password);
}
public function table($table)
{
$this->table = $table;
return $this;
}
public function field($field)
{
$this->field = $field;
return $this;
}
public function values($values)
{
$this->values = $values;
return $this;
}
// 生成查询语句
protected function getSql()
{
return sprintf('DELETE FROM %s WHERE %s = %s', $this->table, $this->field, $this->values);
}
// 执行查询
public function delete()
{
return $this->db->query($this->getSql());
}
}
class DB
{
public static function __callStatic($name, $args)
{
$dsn = 'mysql:host=localhost;dbname=liangtest';
$username = 'liang';
$password = '123456';
$query = new Query($dsn, $username, $password);
return call_user_func([$query, $name], ...$args);
}
}
$result = DB::table('my_list')
->field('phone')
->values('234321')
->delete();
print_r($result);