PHP 成员重载与实战

人盡茶涼 提交于 2020-01-25 18:18:49

一、静态成员

应用场景:

1、如果一个对象,我们仅仅只用一次,还有必要创建对象吗, 直接将类看成对象,岂不是更方便
2、如果多个对象之间, 需要共享一些属性和方法, 而他们必须通过一个个独立对象调用的,无法共享,怎么办?
这时候使用静态成员就能解决。

成员分为:成员可分为类成员和实例成员,成员又包含属性和方法
实例属性定义public $product;初始化用构造函数
实例方法定义public function getInfo1(){}
静态属性定义public static $price; 可以在构造函数中进行初始化,但不建议,最好是定义时进行初始化。访问: 用类直接访问,例如双冒号[范围解析符] echo Demo::$price;
静态方法定义public static function getInfo2(){}

二、常量

类常量定义: const 常量名=常量值;
普通常量定义: define('常量名','常量值');
变量普通常量的区别:

  • 变量是有作用域限制,通过_GLOBAL数组输出;普通常量是是全局的,无作用域的限制但受命名空间限制,可以直接输出。

类常量普通常量的区别:

  • 声明的方式不同:类常量用const;普通常量可以const也可以define,一般用define,因为define灵活点允许值是一个表达式(abc/实际的值)和字面量(实际的值),而const只允许值为字面量;但define在类里面不能用。
  • 类常量不能被重复声明和赋值且不能被改写,普通常量不能进行改写(更新)和删除。

类常量类属性的区别:

  • 类常量是类属性的一种,属于类。类常量不允许修改(更新),类属性可以进行修改(更新)。
  • 类常量定义后必须进行初始化,类属性定义后可以通过构造函数初始化。

类常量类属性的相同:

  • 在类的内部使用:self::常量名/$属性名;在类的外部使用:类名::常量名/$属性名;

三、以回调的方式调用函数

以回调的方式调用已定义的方法:

call_user_func($callback,[$parameter...]): 以函数参数的方式,执行一个函数,其实就是以回调的方式执行函数
call_user_func_array($callback,$array): 功能与call_user_func()相同, 仅仅是参数以数组形式提供

①、普通函数回调

函数回调:函数以回调的方式被调用
将要调用地函数作为call_user_func_array($callback,$array)函数的第一个参数,参数组成一个数组作为第二参数。
代码如下:

<?php
namespace _1008;
function sum($a, $b) {
	return $a . ' + ' . $b . ' = ' . ($a + $b);
}

// 正常函数调用
echo sum(20, 40);
echo '<br>';
// 以回调的方式执行该函数
// __NAMESPACE__: 命名空间魔术常量,它的值始终是表示当前命名空间的字符串
// 因为当前脚本使用了命名空间, 所以函数使用使用命名空间做为前缀,否则会报错
//__namespace__===_1008;
// echo call_user_func('_1008\sum', 50, 20);
echo call_user_func('\_1008\sum', 50, 20);
echo '<br>';

// call_user_func_array(), 第二个参数是数组格式,如果没参数就传空数据,不能省略。一般用这个函数
echo call_user_func_array(__NAMESPACE__ . '\sum', [30, 80]);
echo '<hr>';

打印结果如下:
在这里插入图片描述

②、对象方法回调
<?php
class Test1
{
	// 对象方法
	public function sum($a, $b){
		return $a . ' + ' . $b . ' = ' . ($a+$b);
	}
}

// 如果以回调方式执行对象方法呢?
//$obj = new namespace\Test1();
$obj = new Test1(); // 等价

// 仅以call_user_func_array([对象名,方法名],[参数数组])举例, call_user_func()原理一样
echo call_user_func_array([$obj,'sum'], [10,30]);
echo '<br>';
// 如果仅调用一次,可以简化一下对象创建方式
echo call_user_func_array([new Test1(),'sum'], [10,30]);
echo '<hr>';

打印结果如下:
在这里插入图片描述

③、静态方法回调
<?php
class Test2{
	// 对象方法 (乘法运算)
	public static function mul($a, $b){
		return $a . ' * ' . $b . ' = ' . ($a*$b);
	}
}

// 直接将类名与方法写在一个字符串即可
echo call_user_func_array(__NAMESPACE__.'\Test2::mul', [10,30]);
echo '<br>';

// 将类名与类方法分开,放在一个数组中
echo call_user_func_array([__NAMESPACE__.'\Test2','mul'], [10,30]);
echo '<br>';

// ::class的功能: 用于类名解析, 返回一个带有命名空间的类名
echo '类名是: '. Test2::class;  // 返回一个类名字符串
echo '<br>';
// 所以这样写,也是正确的
echo call_user_func_array([Test2::class,'mul'], [10,30]);
echo '<hr>';

打印结果如下:
在这里插入图片描述

四、成员重载

①属性重载

应用场景:

当我们在类的外部访问一个不存在或者没有权限访问的一个实例成员或者类成员的时候,我们应该提供一种机制来检查和处理,这种机制就叫属性重载。

  • 重载: 动态的创建属性和方法
  • 当访问未定义或不可见的属性/方法时, 重载方法会自动调用
  • “当访问未定义或不可见”, 统称为: “不可访问”
  • PHP中的重载,是通过”魔术方法”实现
  • “魔术方法”是特指客户端不能访问,而只能是系统根据一定条件自动调用
  • 所有重载方法必须声明为: public (前两个方法(__get()、__set())用的比较多)
  • __get($name): 当获取不可访问属性时触发
  • __set($name, $value):当给不可访问属性赋值时触发
  • __isset($name): 当检测不可访问属性时触发,重载了isset()函数
  • __unset($name): 当注销不可访问属性时触发,重载了usset()函数
<?php
//属性重载
namespace admin;
class Demo {
	private $name;
	private $salary;
	protected $secret = '秘密就是没有秘密';
	// 构造方法
	public function __construct($name, $salary) {
		$this->name = $name;
		$this->salary = $salary;
	}
	// __get($name):当获取不可访问的属性时触发
	public function __get($a) {
		echo '要重载的属性名是:' . $a . ';初始化后,$name的属性值是:' . $this->name;
		echo '<br>';
		//判断要访问的属性名是不是secret
		if ($a === 'secret') {
			// 仅允许(属性值为)name=='admin'的用户可以查看secret字段内容
			return ($this->name === 'admin') ? $this->$a : '无权查看!';
		}
		return $this->$a; //通过当前对象访问  这条语句等价于$this->salary/$this->secret
	}
}
$obj = new Demo('admin', 6666);
echo $obj->salary . '<hr>';
echo $obj->secret, '<br>';
?>

输出如图:
在这里插入图片描述

②方法重载

调用未定义的方法,通过方法重载实现:

__call($方法名,$方法参数列表): 访问未定义的对象方法时会自动调用它
__callStatic($方法名,$方法参数列表): 访问未定义的静态类方法时会自动调用它

<?php
class Demo{
	// __call(): 访问不存在/不可见对象方法时触发
	public function __call($name, $arguments){
		return '方法名: '.$name.'<br>方法参数列表: ' . '<pre>'.print_r($arguments, true);
	}

	// __callStatic(): 访问不存在/不可见的类方法(静态)方法时触发
	public static function __callStatic($name, $arguments){
		return '方法名: '.$name.'<br>方法参数列表: ' . '<pre>'.print_r($arguments, true);//以源码的方式打印出来
	}
}

$obj = new Demo();

// 访问不存在或无权访问的对象方法
echo $obj->getInfo1(10,20,30);

echo '<hr>';

// 访问不存在或无权访问的静态类方法
echo Demo4::getInfo2('html','css', 'javascript');
echo '<hr>';

打印结果如下:
在这里插入图片描述

五、方法重载实例(框架中的数据库查询案例)

  • 类方法的跨类调用的实现
  • 链式调用的原理分析

PDO执行SQL操作的流程:

  • 连接数据库 (Db.php)
  • 创建SQL语句模板(SQL语句拼接)(query.php)
  • 创建预处理对象()
  • 变量绑定
  • 执行SQL语句
  • 关闭连接
Db.php
<?php
require 'query.php';
class Db {
	// 数据库连接对象
	protected static $pdo = null;
	// 数据库连接方法, 每次查询时再连接, 实现真正的惰性连接,节省系统开销
	public static function connection() {
		// 为简化,这里直接使用字面量参数连接数据库,真实项目中应该将参数放在配置文件中
		self::$pdo = new \PDO('mysql:host=localhost;dbname=php', 'root', 'root');
	}
	// 这是查询类(query)操作的入口, 通过静态魔术方法\方法重载,进行跳转到query类,实现对象方法的跨类调用
	// 本类(Db类)对象访问(调用)不可访问的静态(类)方法时触发该魔术方法__callStatic,__callStatic($调用的静态方法名字,$调用的方法里面的参数)
	public static function __callStatic($name, $arguments) {
		// 创建pdo对象,并连接数据库
		self::connection();//如果connection方法不是静态的方法,就是静态方法调用实例方法,这是不行的,此时需要new一个对象来调用connection方法
		// 实例化查询类,将连接对象(pdo)做为参数
		$query = new Query(self::$pdo);
		// 执行查询类Query中的对象方法, 注意参数是数组,我只需要第一个参数:表名, 所以加了索引键名
		// 跨Db类链式调用query类的实例方法,完成SQL语句的拼接,例:Db::table('user')->field('uid,name,phone,sex');
		return call_user_func_array([$query, $name], [$arguments[0]]);
	}                              //[$arguments[0]] 可以写成$arguments
}
// 客户端的链式调用
// 以Db类做入整数数据库操作的入口, SQL语句的各个部分用对象方法提供
// 链式操作是现代PHP框架的基础,非常有用
// Db是一个入口类,通过静态方法的重定向,定向到另外一个查询类在另外一个类中定义了很多方法,这些方法叫链式调用方法,也叫链式方法

// 传入用户自定义参数
$staffs = Db::table('user')//返回值是当前类query类的一个对象,然后继续调用下一个query类的方法
	->field('uid,name,phone,sex')
	->where('uid > 2') // = 2,只会输出一条
	->limit(5)
	->select();

	// 测试查询, 先测试默认值,在query类里面的方法开启die方法
//$staffs = Db::table('staff')->select();
//echo  $staffs;
//打印结果:SQL:[18] SELECT *FROM staff Params:0

// 遍历查询结果
foreach ($staffs as $user) {
	print_r($user);
	echo '<br>';
}
?>
query.php
<?php
namespace _0801;
// 数据库查询类

class Query{
	// 连接对象
	public $pdo = null;

	// 数据表名
	public $table;

	// 字段列表
	public $field = '*';

	// 查询条件
	public $where;

	// 显示数量
	public $limit;

	// 构造方法,初始化连接对象
	public function __construct($pdo){
		// 实例化时自动连接数据库
		$this->pdo = $pdo;
	}

	// 调用表名
	public function table($tableName){
		$this->table = $tableName;
		// 返回当前对象,便于链式调用该对象的其它方法
		return $this;
	}

	// 设置查询字段
	public function field($fields = '*'){
		$this->field = empty($fields) ? '*' : $fields;
		return $this;// 返回当前对象,便于链式调用该对象的其它方法
	}
	// 设置查询条件
	public function where($where = ''){
		$this->where = empty($where) ? $where : ' WHERE '. $where;
		return $this;// 返回当前对象,便于链式调用该对象的其它方法
	}

	// 设置显示数量
	public function limit($limit){
		$this->limit = empty($limit) ? $limit : ' LIMIT '.$limit;
		return $this;// 返回当前对象,便于链式调用该对象的其它方法
	}

	// 创建SQL查询语句对象,并返回查询结果
	public function select(){
		// 拼装SQL语句 SELECT *FROM Table WHERE条件 LIMIT n;
		$sql = 'SELECT '
			. $this->field  //字段列表
			. ' FROM '
			. $this->table  // 数据表
			. $this->where  // 查询条件
			. $this->limit; // 显示数量

		// 测试预处理查询 pdo对象调用prepare方法,返回一个sql语句对象
		$stmt = $this->pdo->prepare($sql);
		//执行sql语句
		$stmt->execute();
		//die($stmt->debugDumpParams()); 查看生成的SQL语句
		return $stmt->fetchAll(\PDO::FETCH_ASSOC);//返回查询结果 	
	}//\PDO::FETCH_ASSOC :是一个常量:只获取到结果集中的关联数组
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!