💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
> 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(); ```