静态延迟绑定
在未使用静态延迟绑定的时候,如果父类使用self时,子类继承了父类,在子类中重新赋值的时候,是继承父类的,也可以说是不生效的,但是在父类将self更改为static时,将改为延时绑定模式,开启延时绑定模式时,子类在重新赋值时,子类已延时绑定,最终的结果将是子类的结果。
### 单例模式
单例模式感觉是在数据库链接的时候经常用到,意思就是先设置一个静态属性,这个静态属性使用这个静态方法,静态方法内判断静态属性是否进行了实例化,如果没有进行实例化,就进行实例化一次,在子类中使用时,防止父类中已经调用(实例化了一次了),并不会在子类中重新调用时,需要将静态属性在子类中重新赋值,或者重新调用一遍,让他为空即可。
<?php
/**
*
* 后期静态绑定 延迟静态绑定 static
*
*/
/**
* 在创建类层次结构时,self关键字在编译时就已经确定了他的作用域范围,而不是在运行时(后期),
* self不能动态的与调用类进行绑定,我们后期(延迟)静态绑定
*
* $this能动态的与调用类进行绑定。
*
*/
class Employee
{
public static $favSport = 'football';
public static function watchTV()
{
echo 'watching ' . static::$favSport . '<br>';
}
}
class Execute extends Employee
{
public static $favSport = 'polo';
}
echo Execute::watchTV();
echo Execute::$favSport;
// 延迟绑定时将 输出的部分,也就是最终的部分 self替换static
class tbody
{
public $favSsport = 'football';
public function ccTV()
{
echo 'cctv' . $this->favSsport . '节目';
}
}
class td extends tbody
{
public $favSsport = '2';
}
$a = new td;
$a->ccTV();
// 非静态类既不需要进行延迟绑定,他是调用最终的部分
//单例模式 只允许类被实例化一次
class Father
{
// 将构造器设置成私有的
private function __construct()
{
}
private function __clone()
{
}
// 设置一个受保护的属性 存储father类的实例
protected static $_instance;
// 获取father类的实例 唯一实例
// static function getInstance()
// {
// if (self::$_instance === null)
// // 将值赋值给静态属性
// self::$_instance = new self;
// // 返回的是一个对象
// return self::$_instance;
// }
// }
/////////////下面更改为延迟绑定//////////////
static function getInstance()
{
if (static::$_instance === null)
// 将值赋值给静态属性
static::$_instance = new static;
// 返回的是一个对象
return static::$_instance;
}
}
class son extends Father
{
protected static $_instance;
}
var_dump(Father::getInstance());
var_dump(son::getInstance());
// 自己的见解,单例模式只允许实例化一次,其实就是在var_dump第一次执行的时候,已经执行了一次静态延迟绑定,所以在第二次调用子类的时候,
// 实际上父类就已经调用一次了,所以在if判断分支的时候,并不会进入static::$_instance === null,而是跳过,直接输出结果,如果想他
// 继续执行一次,需要在子类中,重新给static::$_instance === null赋值为空,也就是重新定义一次
单例模式链接数据库(非延迟静态)
单例链接(数据库操作链接演示,操作其他也类似)其实是将数据库的增删改查链接数据库封装到接口中,封装到接口中后,后面的由抽象类来完成一部分的工作,工作类也就是普通类继承一部分抽象类来完成所有的工作,接口提供一个方法,抽象类完成一个最主要的部分,工作类来完成所有的工作,下面的工作类都的 where判断感觉也可以直接写到类的里面,还没有测试,等测试下,完成了所有的类方法后封装成类属性或者方法,直接可以在最后进行调用
代码部分:
<?php
/***
* 单例模式链接数据库 应用程序跟设计库交互
*
*/
namespace app\laolu;
use PDO;
interface idbBase
{
///增删改查
//插入
static function insert($db, $data);
//查询
static function select($db, $where = []);
// 更新
static function update($db, $where = []);
//删除
static function delete($db, $data, $where = []);
//数据库链接
static function doconnect($dsn, $user, $password);
}
abstract class adb implements idbBase
{
private static $_instance;
private function __construct()
{
}
private function __clone()
{
}
static function doconnect($dsn, $user, $password)
{
//创建adb类的唯一实例 获取唯一的pdo对象
if (is_null(self::$_instance)) {
self::$_instance = new PDO($dsn, $user, $password);
}
return self::$_instance;
}
}
//工作类
class Db extends adb
{
///增删改查
//插入
static function insert($db, $data)
{
}
//查询
// static function select($db, $where = [])
// {
// $dbo = $db->query("SELECT * FROM `mj_user` LIMIT 3")->fetchAll(PDO::FETCH_ASSOC);
// return $dbo;
// }
static function select($db, $where = ['uid' => 1])
{
foreach ($where as $key => $value) {
$sql = $key . '>' . $value;
// 组装循环条件 这里装成字符后再query语句里面填入where循环条件
}
$dbo = $db->query("SELECT * FROM `mj_user` WHERE " . $sql . " LIMIT 3")->fetchAll(PDO::FETCH_ASSOC);
return $dbo;
}
// 更新
static function update($db, $where = [])
{
}
//删除
static function delete($db, $data, $where = [])
{
}
}
$config = [
'type' => $type ?? 'mysql',
'host' => $host ?? 'localhost',
'dbname' => $dbname ?? 'ad',
'username' => $username ?? 'root',
'password' => $password ?? 'H9MvYSqY3JmAC4aj',
'port' => $port ?? '3306',
'charset' => $charset ?? 'utf8'
];
extract($config);
$dsn = sprintf('%s:host=%s;port=%s;charset=%s;dbname=%s', $type, $host, $port, $charset, $dbname);
$pdo = Db::doconnect($dsn, $username, $password);
$db1 = Db::select($pdo);
var_dump($db1);
var_dump(Db::select($pdo));
// foreach ($db1 as $key => $value) {
// echo $key
// }
重载就是php类中无该类属性成员(或私有属性)时,将自动创建类属性成员,也就是通过_set方法来创建的类属性成员,当访问这个属性是,使用的魔术方法__get 方法来访问这个类成员,类属性重载是将新类属性成员通过set get魔术方法来新建类属性,但魔术方法都是公开的,所以新建的类属性成员,私有属性成员都成为公开成员,违反了也忘了叫啥了。
<?php
/**
* php重载 overload
* php重载 方法拦截器 是指动态的创建类属性和方法,我们是通过魔术方法 来实现的, _get _set _callstatic _call
*
* 当访问类中不存在或者不可见成员时,会自动调用魔术方法__set __get
* 因为魔术方法都是公开的,所以一些私有成员的不可见性就不会生效
*
*/
class Credit
{
public $name;
private $idNum;
public function __construct($name, $idNum)
{
$this->name = $name;
$this->idNum = $idNum;
var_dump($this->idNum);
echo "<br>上面输出了<br>";
}
public function __set($name, $value)
{
// echo '<hr>';
// echo $name . '<br>';
// echo $value . '<br>';
$this->$name = $value;
// var_dump($this->$name);
echo '<br>521314php<br>';
return $this->$name;
}
public function __get($name)
{
return $this->$name;
}
}
$a = new Credit('胡歌', '341621199905015488');
echo $a->name;
echo '<hr>';
$ab = $a->name;
echo $ab . '这里输出了<br>';
$a->age = 20; //__set
echo $a->age; //__get
// echo $a->name;
echo $a->idNum;//私有属性是通过__get魔术标签输出的
重载的私有属性或新类成员方法时解决方法:
在私有属性新赋值时,也是走的set 在进入set方法自动调用时,将set方法return出去的数据拦截,拦截组装出一个新的私有类成员方法,将在私有方法中输出结果,这样就将私有属性继续私有化,并不会公开化,当echo输出时,使用的__get魔术方法,但是传入的值,只是属性名,同样也是将属性私有化处理,新建一个私有方法,在get魔术方法中组装一个私有类成员方法
在下面使用中的新函数:
函数名 | 函数说明 | 使用方法 |
---|---|---|
ucfirst() | 将传入的值首字母大写 | ucfirst($name)传入的name值将首字母大写处理 |
method_exists() | 检查本作用域中是否有这个类成员方法 | method_exists($this, $method) $this是本作用域,$method检查的变量 |
property_exists() | 检查本类方法中是否存在这个类成员,属性类成员 | property_exists($this, $name)前面是作用域,后面是检查的类成员 |
<?php
class Credit
{
private $name;
private $idNum;
public function __construct($name, $idNum)
{
$this->name = $name;
$this->idNum = $idNum;
}
//下面设置了一个私有的方法 这里的set相当于一个中转站
public function __set($name, $value) //name是拦截的属性的名,value是拦截的属性的值
{
// $this->$name = $value;
// return $this->$name; //这里将返回的属性值取消,暂时先不return出去
// 拼接实际调用的方法名称 ucfirst是将函数的第一个字母大写
$method = 'set' . ucfirst($name);
//下面判断 this本作用域中, 有没有$method这个类成员方法,也就是setidnum和setAge这个方法
if (method_exists($this, $method)) {
//这里相当于访问的是本作用域中的idnum方法,传入的值也是idnum的值
return $this->$method($name, $value); //传入name是拦截的属性的名,value是拦截的属性的值 这里return出去 其中这里类属性已经变为了set*,比如下面的setIdNum
} else {
return null;
}
}
private function setAge($name, $value) //setIdNum这里相当于是未来私有方法的名称 $name是拦截的属性的名,value是拦截的属性的值
{
//设置信息
$ad = '改变为私有属性';
echo $ad;
}
private function setIdNum($name, $value) //setIdNum这里相当于是未来私有方法的名称
{
//设置信息 身份证号 做一些验证
// property_exists检查类中是否存在该属性 类属性 不是方法,$idNum 私有属性,是有的
if (property_exists($this, $name)) {
// strlen检查值是不是18位
return $this->$name = strlen($value) == 18 ? $value : null;
}
}
private function getIdNum($name)
{
// 只返回后6位
var_dump($name); //这里传入的是idNum也就是属性,其中下面this代表的是本类,也就是作用域.
$flag = property_exists($this, $name) && !empty($this->$name); //empty判断是否为空
var_dump($flag);
if ($flag) {
return '*********' . mb_substr($this->$name, -6, 6);
} else {
return '操作违规信息不合法';
}
}
public function __get($name) //getIdNum 最终的值还是来到了魔术方法__get方法中
{
// return $this->$name;
$method = 'get' . ucfirst($name);
//下面判断 this本作用域中, 有没有$method这个类成员方法,也就是getIdNum这个方法
if (method_exists($this, $method)) {
//这里相当于访问的是本作用域中的idnum方法,传入的值也是idnum的值
return $this->$method($name); //传入name是拦截的属性的名,value是拦截的属性的值 这里return出去的是getIdNum
} else {
return null;
}
}
}
$a = new Credit('胡歌', '341621199905015488');
// echo $a->name
$a->age = 20; //__set
// echo $a->age; //__get
$a->idNum = '341621199905015488';
echo $a->idNum;//私有属性是通过__get魔术标签输出的