> 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();
```