# :-: 一、类属性与类方法(静态成员)
* 类属性: 静态属性
* 类方法: 静态方法
* 静态成员属于类,而不属于对象
* 静态成员不需要通过对象访问,所以不必实例化
* 使用`static`关键字定义
* 类外使用类名访问,类内使用`self`访问
* 类外部, 类属性不能用实例访问,但类方法可以
```php
namespace admin;
class Demo{
// 实例属性
public $product;
// 静态属性, 使用关键字 static 定义静态成员
public static $price;
// 构造方法
public function __construct($product, $price){
$this->product = $product;
// 静态属性的初始化
self::$price = $price;
// 尽管可以在构造方法中初始化静态属性,但不建议这样做,否则静态属性,无法在对象之间共享
}
// 实例方法
public function getInfo1(){
// 实例方法, 即可以访问实例属性,也可以访问静态属性
return $this->product . '价格是: ' . self::$price;
}
// 类方法: 静态方法
public static function getInfo2(){
// 静态方法是类方法, 不能用对象调用,所以内部也不允许使用对象引用$this
// 如果静态方法中,一定要用到对象属性或方法,可以用参数传入
// return $this->product . '价格是: ' . self::$price;
}
// 在静态方法中, 使用实例属性的实现手段
// 将实例属性以访问参数的形式, 注入到录前的静态方法中即可
public static function getInfo3($product){
return $product . '价格是: ' . self::$price;
}
}
$obj = new Demo('衣服', 300);
// 实例属性: 用类的实例访问
echo $obj->product, '<br>';
// 静态属性: 用类直接访问,例如双冒号[范围解析符]
echo Demo::$price;
echo '<hr>';
echo $obj->getInfo1(), '<br>';
//echo $obj->getInfo2(), '<br>';
echo '<hr>';
// 在静态方法中访问实例属性
echo Demo::getInfo3($obj->product);
echo '<br>';
// 对象不能访问静态属性,但是可以访问静态方法,应该避免这样去做
echo $obj->getInfo3($obj->product);
```
--------------------------------------------
# :-: 二、类常量
* 类常量也类属性一样,也是属于类的, 必须用类访问,不能用对象访问
* 类常量与类属性的区别是: 类常量不允许修改,而类属性可以修改
* 类常量与普通常量的命名规则是一致的, 推荐使用大写字母或大写字母+下划线
* 类常量不需要设置访问限制符,默认行为与`public`是一样的
```php
namespace admin;
class Demo
{
// 类常量也类属性一样,也是属于类的, 必须用类访问,不能用对象访问
const NATION = '中国';
// 类常量与类属性的区别是: 类常量不允许修改,而类属性可以修改
// 类常量必须初始化, 而且必须是字面量, 不允许使用表达式或变量对象等
public static $sex = '男';
private $name;
public function __construct($name){
$this->name = $name;
}
public function getInfo(){
// 类常量在类的内部,访问方式与类属性是一样的
return $this->name.'的性别是:' . self::$sex.',国籍是: ' . self::NATION;
}
}
$obj = new Demo('欧阳克');
// 访问类属性
echo Demo::$sex, '<br>';
// 访问类常量
echo Demo::NATION, '<br>';
// 访问对象方法: 该方法又访问了类属性与类常量
echo $obj->getInfo();
echo '<hr>';
// 修改类属性
Demo::$sex = '保密';
// 修改类常量: 报错
//Demo::NATION = '中国';
// 可以看到类属性:$sex发生了变化
echo $obj->getInfo();
```
--------------------------------------------
# :-: 三、属性重载
* 重载: 动态的创建属性和方法
* 当访问未定义或不可见的属性/方法时, 重载方法会自动调用
* "当访问未定义或不可见", 统称为: "不可访问"
* PHP中的重载,是通过"魔术方法"实现
* "魔术方法"是特指客户端不能访问,而只能是系统根据一定条件自动调用
* 所有重载方法必须声明为: `public`
* `__get($name)`: 当获取未定义可不见属性时触发
* `__set($name, $value)` :当给未定义可不见属性赋值时触发
* `__isset($name)`: 当检测未定义可不见属性时触发
* `__unset($name)`: 当注销未定义可不见属性时触发
```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($name){
if ($name === 'secret') {
// 仅允许name=='admin'的用户可以查看secret字段内容
return ($this->name === 'admin') ? $this->$name : '无权查看';
}
return $this->$name;
}
// __set($name, $value):当给未定义可不见属性赋值时触发
public function __set($name, $value){
// 直接返回, 极少这样做
// $this->$name = $value;
// 添加过滤机制
if ($name === 'salary') {
return $this->name === 'admin' ? $this->$name = $value : '无权修改';
}
return $this->$name = $value;
}
// __isset($name): 当检测未定义可不见属性时
public function __isset($name){
if ($this->name === 'admin') {
if (isset($this->$name)){
echo '存在该属性';
} else {
echo '没有该属性';
}
} else {
echo '无权检测';
}
}
//__unset($name): 当注销未定义可不见属性时触发
public function __unset($name){
if ($this->name === 'admin') {
unset($this->$name);
} else {
echo '无法删除';
}
}
}
$obj = new Demo('欧阳克', 6666);
echo $obj->name, '<br>';
echo $obj->secret, '<br>';
// 怎么才能查看 secret, 只能用'admin'来实例化
$obj = new Demo('admin', 8888);
echo $obj->secret, '<br>';
// 直接修改 salary, 类中没有__set()会报错
$obj->salary = 9999;
// 查看salary字段值
echo $obj->salary, '<br>';
echo '<hr>';
// 检测是否存在salary字段
isset($obj->salary);
echo '<br>';
// 删除salary属性
unset($obj->salary);
echo '<br>';
isset($obj->salary);
```
--------------------------------------------
# :-: 四、方法重载
* call_user_func($callback[,$parameter...]): 以函数参数的方式,执行一个函数,其实就是以回调的方式执行函数
* call_user_func_array($callback,$array): 功能与call_user_func()相同, 仅仅是参数以数组形式提供
>[info] 函数回掉
```php
function sum($a, $b) {
return $a . ' + ' . $b . ' = ' . ($a+$b);
}
// 正常函数调用
echo sum(20, 40);
echo '<br>';
// 以回调的方式执行该函数
// __NAMESPACE__: 命名空间魔术常量,它的值始终是表示当前命名空间的字符串
// 因为当前脚本使用了命名空间, 所以函数使用使用命名空间做为前缀,否则会报错
echo call_user_func(__NAMESPACE__.'\sum', 50, 20);
echo '<br>';
// call_user_func_array(), 第二个参数是数组格式,如果没参数就传空数据,不能省略
echo call_user_func_array(__NAMESPACE__.'\sum', [30, 80]);
echo '<hr>';
```
>[info] 方法回掉
```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>';
```
>[info] 静态方法回掉
```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>';
```
* `__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>';
```
--------------------------------------------
# :-: 五、方法重载实例演示
* 类方法的跨类调用的实现
* 链式调用的原理分析
```php
namespace admin;
require 'Query.php';
class Db{
// 数据库连接对象
protected static $pdo = null;
// 数据库连接方法, 每次查询时再连接, 实现真正的惰性连接,节省系统开销
public static function connection(){
// 为简化,这里直接使用字面量参数连接数据库,真实项目中应该将参数放在配置文件中
self::$pdo = new \PDO('mysql:host=localhost;dbname=php','root','root');
}
// 这是查询类操作的入口, 通过静态魔术方法进行跳转,实现对象方法的跨类调用
public static function __callStatic($name, $arguments){
// 创建pdo对象,并连接数据库
self::connection();
// 实例化查询类,将连接对象做为参数
$query = new Query(self::$pdo);
// 执行查询类Query中的对象方法, 注意参数是数组,我只需要第一个参数:表名, 所以加了索引键名
return call_user_func_array([$query,$name],[$arguments[0]]);
}
}
// 客户端的链式调用
// 以Db类做入整数数据库操作的入口, SQL语句的各个部分用对象方法提供
// 链式操作是现代PHP框架的基础,非常有用
// 测试查询, 先测试默认值
$users = Db::table('user')->select();
// 遍历查询结果
foreach ($users as $user) {
print_r($user); echo '<br>';
}
echo '<hr>';
// 传入用户自定义参数
$staffs = Db::table('user')
->field('uid,name,phone,sex')
->where('uid > 2') // = 2,只会输出一条
->limit(5)
->select();
// 遍历查询结果
foreach ($users as $user) {
print_r($user); echo '<br>';
}
```
>[info] Query.php 文件
```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($tablName){
$this->table = $tablName;
// 返回当前对象,便于链式调用该对象的其它方法
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语句
$sql = 'SELECT '
. $this->field //字段列表
. ' FROM '
. $this->table // 数据表
. $this->where // 查询条件
. $this->limit; // 显示数量
// 测试预处理查询
$stmt = $this->pdo->prepare($sql);
$stmt->execute();
return $stmt->fetchAll(\PDO::FETCH_ASSOC);
}
}
```
- 序言
- PHP基础
- 认识PHP
- 环境安装
- PHP语法
- 流程控制
- PHP数组
- PHP函数
- PHP类与对象
- PHP命名空间
- PHP7新特性
- PHP方法库
- PHP交互
- 前后端交互
- 项目常规开发流程
- MySQL数据库
- 会话控制
- Ajax分页技术
- 细说函数
- 类与对象
- 对象进阶
- 类与对象进阶
- OOP面向对象
- 设计模式
- 路由与模板引擎
- 异常类
- PHP爬虫
- PHP抓取函数
- PHP匹配函数
- 正则表达式
- PHP字符串函数
- 抓取实战
- PHP接口
- 了解接口
- PHP插件
- PHPSpreadsheet
- ThinkPHP6
- 安装
- 架构
- 数据库
- 数据库操作
- 视图
- 模版
- 模型
- 杂项
- 命令行
- 交互
- 微信小程序
- 介绍
- 配置
- 组件
- 交互
- API
- 其他知识
- 百度小程序
- 介绍
- 配置
- 组件
- 交互
- API
- 其他知识
- Linux
- 服务器上线流程
- 安装svn
- MySQL
- 认识MySQL
- MySQL函数
- 杂项
- composer依赖管理工具