# :-: 一、類屬性與類方法(靜態成員)
* 類屬性: 靜態屬性
* 類方法: 靜態方法
* 靜態成員屬于類,而不屬于對象
* 靜態成員不需要通過對象訪問,所以不必實例化
* 使用`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依賴管理工具