> PHP 使用了一段時間, 從對OOP的不了解, 再到使用, 覺得挺好, 特寫下
OOP并不是為了面向對象而面向對象, 而是為了達到代碼的`重用性`、`靈活性`、`擴展性`
# 對象和類
從先有對象才有人類, 這個來說, 有點不合理的合理
類:具有相同屬性的一組對象的集合
對象:類實例化之后就是對象
看下一般的類的定義
```php
<?php
class Person{
// 成員變量
private $name;
private $sex;
private $age;
// 構造函數
function __construct($name="",$sex="",$age=""){
if($name===""||$sex===""||$age===""){
throw new Exception("must set name , sex, age");
}
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
// 析構函數
function __destruct(){
echo "byebye\n";
}
// 成員方法
function eat(){
echo "my name is ". $this->name;
}
function sleep(){
echo "i am sleeping";
}
}
// 類的實例化
$jaime = new Person("jaime", "man", 24);
$jaime->eat();
?>
```
保存為index.php, 在命令窗口輸入
```shell
$ php index.php
my name is jaime
byebye
```
如果
```php
$jaime = new Person("jaime", "man", 24);
```
改為
```php
$jaime = new Person("jaime", "man");
```
則會觸發異常, 會有以下的消息出來, 包括錯誤信息, 錯誤行數, 和堆棧信息
```shell
$ php index.php
Fatal error: Uncaught exception 'Exception' with message 'must set name , sex, age' in E:\net\index.php on line 15
Exception: must set name , sex, age in E:\net\index.php on line 15
Call Stack:
0.0025 242440 1. {main}() E:\net\index.php:0
0.0025 243016 2. Person->__construct() E:\net\index.php:30
```
# 對象和內存
說點理論上的東西吧,
內存的分類:
堆(heap): 不可直接存取, 存儲占有空間很大的數據類型的數據
棧(stack): 可以直接存取, 存儲占用相同空間長度并且占用空間小的數據, 如存放局部變量,函數參數,當前狀態,函數調用信息等
```php
$jaime = new Person("jaime", "man", 24);
```
$jaime是存放在棧內存里面的引用變量, 即存儲在堆中對象的首地址, 一個指針
new Person 實例化出來的對象是存放在堆內存里面的, 是真正的對象實例
# 對象和成員
變量,方法(內部成員函數)的前綴:
private: 私有成員
public: ?公有成員(外部接口),沒有加修飾, 默認就是public
protected: 保護型成員, 繼承的類可以調用
訪問private修飾的變量
```shell
Fatal error: Cannot access private property Person::$name in E:\net\index.php on line 36
```
如果想訪問private, protected修飾的成員:
1. 把private改為public
2. 使用__get(), ___set()魔術方法, 但是還是寫出代碼來看看根據實際情況使用
```php
<?php
class Person{
private $name;
private $sex;
private $age;
function __construct($name="",$sex="",$age=""){
if($name===""||$sex===""||$age===""){
throw new Exception("must set name , sex, age");
}
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
function __destruct(){
echo "byebye\n";
}
function eat(){
echo "my name is ". $this->name."\n";
}
function sleep(){
echo "i am sleeping\n";
}
function __get($property_name){
$access_array = ['age','name'];// 只允許訪問age,name兩個私有成員
if(in_array($property_name, $access_array)){
return ($this->$property_name);
}
else{
return NULL;
}
}
function __set($property_name, $value){
$access_array = ['age'];// 只允許訪問age這個私有成員
if(in_array($property_name, $access_array)){
$this->$property_name = $value;
}
}
}
$jaime = new Person("jaime", "man", 24);
$jaime->eat();
echo ($jaime->age === NULL)? "NULL":$jaime->age;
echo "\n";
echo ($jaime->sex === NULL)? "NULL":$jaime->sex;
$jaime->age = 80;
echo "\n";
echo ($jaime->age === NULL)? "NULL":$jaime->age;
echo "\n";
$jaime->name = "lll";
echo ($jaime->name === NULL)? "NULL":$jaime->name;
echo "\n";
?>
```
執行結果如下
```shell
$ php index.php
my name is jaime
24
NULL
80
jaime
byebye
```
# 類的繼承
```php
<?php
class Person{
private $name;
private $sex;
private $age;
function __construct($name="",$sex="",$age=""){
if($name===""||$sex===""||$age===""){
throw new Exception("must set name , sex, age");
}
$this->name = $name;
$this->age = $age;
$this->sex = $sex;
}
function __destruct(){
echo "byebye\n";
}
function hello(){
echo "my name is ". $this->name."\n";
}
function sleep(){
echo "i am sleeping\n";
}
}
class Student extends Person
{
private $school;
function __construct($name, $sex, $age, $school)
{
// 調用父類方法, 構造函數
parent::__construct($name, $sex, $age);
$this->school = $school;
}
// 重載了父類方法
function sleep(){
echo "afternoon sleep\n";
// 調用父類方法
parent::sleep();
}
}
$jaime = new Student("jaime", "man", 24,"zh");
$jaime->hello();
$jaime->sleep();
?>
```
執行后輸出
```shell
$ php index.php
my name is jaime
afternoon sleep
i am sleeping
byebye
```
調用父類的方法需要用parent
# 靜態成員和常量
no bb, show code
```php
<?php
class Person
{
// 靜態成員屬性
public static $contry = "China";
public static function say(){
// 靜態成員方法, 通過self訪問其它靜態成員
// 類里面的靜態方法只能訪問類的靜態的屬性
echo "I live in ".self::$contry."\n";
}
public function show(){
echo "xxx".self::$contry."\n";
}
}
// 輸出靜態屬性
echo Person::$contry."\n";
// 調用靜態方法, 外部調用用類名::靜態方法
Person::say();
// 給靜態屬性重新賦值
Person::$contry = "American";
Person::say();
(new Person())->show();
?>
```
結果
```shell
$ php is.php
China
en
I live in China
I live in American
xxxAmerican en
```
類的靜態變量,類似于全局變量,能夠被所有類的實例共享,類的靜態方法也是一樣的,類似于全局函數, 靜態成員被這個類的每個實例對象所共享
訪問靜態方法訪問靜態成員不能用`$this`, 需要用`self`
`$this`表示了此方法的對象
'self'表示此靜態方法所在的類, self::成員
# 抽象方法和抽象類
什么叫抽象?不具體的就叫抽象! so
抽象方法 : 類里面沒有具體方法體的方法(其實就是不具體的方法)
抽象類: 含有抽象方法的類,
抽象類不能實例化會報錯"Cannot instantiate abstract class <classname>", 有點像C里面的函數聲明, 僅僅只是一個聲明
```php
<?php
abstract class AbstractClass{
// 抽象類里面可以有不是抽象的成員
public $variable;
// 抽象方法
abstract function fun1();
abstract function fun2();
function fun3{
echo "我是抽象類中的非抽象方法";
}
}
class demo0 extends AbstractClass{
// 子類必須把父類中的抽象方法全部都實現, 否則子類中還存在抽象方法,仍是抽象類
function fun1(){
echo "call ".__FUNCTION__."\n";
}
function fun2(){
echo "call ".__FUNCTION__."\n";
}
}
// 我這里是想不出名字, 不建議這樣做, 因為demo0實例化了3次
(new demo0())->fun1();
(new demo0())->fun2();
(new demo0())->fun3();
?>
```
# 接口interface
## 什么是接口?
如果一個內里面所有的方法都是抽象方法, 我們可以把聲明方式換為接口
接口是一種特殊的抽象類, 接口不能包含成員的任何代碼,只定義成員身。接口成員的具體代碼由實現接口的類提供
```php
<?php
interface One{
// 定義常量
const con = "xonst";
// 定義抽象方法, 不用加abstract, 因為都是abstract
function fun1();
function fun2();
}
?>
```
## 接口的繼承
```php
<?php
interface Two extends One{
function fun3();
function fun4();
}
?>
```
## 接口的實現
```php
<?php
interface One{
// 定義常量
const con = "xonst";
// 定義抽象方法, 不用加abstract, 因為都是abstract
function fun1();
function fun2();
}
interface Two extends One{
function fun3();
function fun4();
}
// 使用“implements”這個關鍵字去實現接口中的抽象方法
class demo implements Two
{
function fun1() {
echo "call ".__FUNCTION__."\n";
}
function fun2() {
echo "call ".__FUNCTION__."\n";
}
function fun3() {
echo "call ".__FUNCTION__."\n";
}
function fun4() {
echo "call ".__FUNCTION__."\n";
}
}
(new demo())->fun1();
(new demo())->fun2();
(new demo())->fun3();
(new demo())->fun4();
?>
```
## 一個類實現多個接口
一個人要遵守的法律不止一步吧, 所以see code
```php
<?php
interface One{
// 定義常量
const con = "xonst";
// 定義抽象方法, 不用加abstract, 因為都是abstract
function fun1();
function fun2();
}
interface Two{
function fun3();
function fun4();
}
// 注意這里
class demo implements One, Two
{
function fun1() {
echo "call ".__FUNCTION__."\n";
}
function fun2() {
echo "call ".__FUNCTION__."\n";
}
function fun3() {
echo "call ".__FUNCTION__."\n";
}
function fun4() {
echo "call ".__FUNCTION__."\n";
}
}
(new demo())->fun1();
(new demo())->fun2();
(new demo())->fun3();
(new demo())->fun4();
?>
```
你娶了你老婆你得對她的家人負責吧, 就像下面
```php
// 使用extends繼承一個類,使用implements實現多個接口
class demo extend AbstractClass implements One, Two{
......
// 所有接口中的方法都要實現才可以實例化對象
}
```
# 反射Reflection
作用: 導出或提取出關于類、方法、屬性、參數等的詳細信息, 執行, 甚至是判斷類中某個方法是否存在
這里我不做多說, 看我在實際的項目中的實際應用,
這是一個API的入口處理函數, 如果存在這個方法就執行并返回結果, 不存在就拋出異常,
因為接口函數是不斷增加甚至是變化的, 使用反射作為api的入口可以讓你的具體的api函數變化了入口也不用改
```
try {
// 使用工廠方法實例化具體的接口
$instance = new \Api\Library\ApiFactory($module, $server, $this->params);
// 反射方法
$action = new \ReflectionMethod($instance->instance, $method);
// 判斷方法的類型
if (!$action->isPublic() || $action->isStatic()) throw new \ReflectionException();
// 驗證api參數
$validator = new \Api\Library\ApiValidator();
$result = $validator->check($this->params);
if (false === $result) {
$this->result['code'] = $validator->code ? : $this->result['code'];
$this->result['msg'] = $validator->msg ? : $this->result['msg'];
throw new \Exception();
}
} catch(\Exception $e){
$this->_format();
}
// excute
$this->result['result'] = $instance->$method();
$this->result['code'] = $instance->getCode();
$this->result['msg'] = $instance->getMsg();
$this->_format();
```