ThinkChat🤖让你学习和工作更高效,注册即送10W Token,即刻开启你的AI之旅 广告
# :-: 一、命名空间 * 命名空间: 解决全局成员的命名冲突问题, 借鉴了文件目录的基本思想 * 目录: 同一目录下不允许重名文件, 但不同目录下, 允许同名文件存在 * 空间: 同一空间内不允许成员重名, 但不同空间内, 允许同名成员存在 * 命名空间使用 "namespace" 关键字声明 ```php // 如果是相同的名字,php是不运行的 const NAME = '欧阳克'; function sum() {} class Demo {} const NAME = '黄蓉'; function sum() {} class Demo {} ``` >[info] 使用命名空间 ```php namespace red; class Test {} namespace green; class Test {} namespace blue; class Test {} ``` >[info] 命名空间,"大括号" 来声明空间 ```php namespace one { class Test {} } // 空间: two namespace two { class Test {} } // 匿名空间代表: 全局空间, 也是默认的空间,或叫根空间,用 \ 表示 namespace { class Test {} } ``` * 例如: php同时引入两个文件,文件中都有add方法。 * 例如: 合肥有条北京路, 上海也有条北京路, 但不会有人认为这是同一条路,因为用城市做了区隔 * 所以, 城市,就是路名的命名空间,这样的例子生活中到处都是,不难理解 * 目前我们只关心 "类" 的命名空间 ***** # :-: 二、类与对象 * 定义: `class` * 实例化: `new` ```php namespace admin; // 关键词: 类, new, 类实例, 对象, 对象检测 // 类定义 // 类: 是生成对象的代码模板 class People { //... } // 对象: 类的实例化, 是类的一个具体实现案例 // 例如: 动物是类, 而小猪, 则是动物类的一个具体的实现案例 // 注意: "实例" 与 "对象" 是同义词, 大多情况下可互换 $obj1 = new People(); $obj2 = new People(); $obj3 = new People(); // 3个对象都是Demo01类的实例,有不同的#id var_dump($obj1); echo '<br>'; var_dump($obj2); echo '<br>'; var_dump($obj3); echo '<hr>'; // 检测对象是否是同一个类的实例 var_dump($obj1 instanceof Demo1); echo '<br>'; var_dump($obj2 instanceof Demo1); echo '<br>'; var_dump($obj3 instanceof Demo1); echo '<hr>'; // 3个对象虽是同一个类的实例,但彼此并不相等,是完全独立的 var_dump($obj1 == $obj2); echo '<br>'; var_dump($obj2 === $obj3); ``` ***** # :-: 三、对象属性 * 类中的属性,也叫: 成员变量 * 属性的语法与变量类似,但也是有区别的: * 在类中声明属性变量时,需要通过访问限定符,设置它的作用域 * 属性: 初始化必须使用常数,不得使用表达式 * public 是访问限制符,可视为属性作用域(可见性) * public 是指该属性不论是类内,类外,子类都是可访问的 * 访问限定符还有protected,private,目前我们先学习public ```php namespace admin; class Demo{ // 为什么叫实例属性? 因为只能用类的实例, 即对象来访问它 // 设置了初始值, 并约定在类外部可以访问 public $product = '手机'; public $price = 2800; } // 调用类/函数的代码叫: 客户端, 以后我们还会频繁的使用这个名词 // 以下代码其实都是客户端代码 // 通过类的实例对象访问 // 创建类实例对象 $obj = new Demo(); // 输出当前实例属性的值 echo '品名: ' .$obj->product .', 价格: ' . $obj->price . '<br>'; echo '<hr>'; // 动态对象属性: // 属性除了可以声明在类的内外, 在类的外部,也可以动态的添加 $obj->brand = '华为'; echo '品牌: ' . $obj->brand; // get_class_vars()返回类中public属性的属性组成的数组 // 因为使用了命名空间, 完整的类名应该包括了命名空间的, 可以使用::class来获取完整的类名 $properties = get_class_vars(Demo::class); echo '<pre>'.print_r($properties,true); // 注意: 用户自定义动态属性不会出现在属性清单中 ``` * 动态属性: 肯定,必然是对象属性,只能通过对象添加与访问 * 动态属性: 因为是在类外部添加,所以只能是public类型 * 动态属性: 因为不在类中声明,不会得到编辑器的提示, 也不会检查拼写错误 * 所以, 实际开发中, 几乎用不到动态属性,大家知道即可, 尽可能不要去使用它 ***** # :-: 四、对象方法 * 对象方法的定义与访问 * `self`: 当前类 * `$this`: 当前类实例对象的引用 ```php namespace admin; class Demo{ // 实例属性 public $product = '手机'; public $price = 2800; // 实例方法 public function getInfo1(){ // self : 当前类 $obj = new self(); // 输出实例属性 return '品名: ' .$obj->product .', 价格: ' . $obj->price . '<br>'; } // 实例方法 public function getInfo2(){ // 因为该方法必须通过对象调用,所有没必要在类中实例化 // 直接引用该类的实例化对象即可 // 在类中使用伪变量: "$this" 引用当前类的实例 // $this = new self(); 相当于先执行了这条语句,尽管你不需要这样做 return '品名: ' .$this->product .', 价格: ' . $this->price . '<br>'; } } // 类实例化 $obj = new Demo(); echo $obj->getInfo1(); echo $obj->getInfo2(); // 查看类中定义的对象方法: public 才会显示出来 $methods = get_class_methods(Demo::class); echo '<pre>'.print_r($methods,true); ``` ***** # :-: 五、构造方法与析构方法 * 构造方法用来初始化对象成员 * 构造函数是类中的特殊方法,在类实例化时会被自动调用,可用来初始化实例成员 * 析构方法在对象被销毁的时候, 会被自动调用; 不过, 即使没有这个方法, 也会自动清理对象的 ```php namespace admin; class Demo{ // 实例属性 public $product; public $price; // 构造方法 public function __construct($product, $price){ $this->product = $product; $this->price = $price; } // 对象方法 public function getInfo(){ return '品名: ' .$this->product .', 价格: ' . $this->price . '<br>'; } // 析构方法: 在对象被删除/清理时自动调用 public function __destruct(){ echo '<h3 style="color:red">对象已被清理</h3>'; } } // 实例化 $obj = new Demo4('电脑', 5800); echo $obj->getInfo(); unset($obj); // $obj = null; ``` >[info] 实战: 自动连接数据库 ```php namespace admin; class Db{ // 连接对象 public $pdo; // 希望在实例化时, 自动连接数据库, 这个需求很常见 public function __construct($dsn, $user, $password){ // 使用PDO方式管理数据库, 连接成功则返回PDO对象,赋值给对象属性pdo $this->pdo = new \PDO($dsn, $user, $password); } // 析构方法: 在对象被删除/清理时自动调用 public function __destruct(){ echo '<h3 style="color:red">连接已断开</h3>'; } } // 实例化 $db = new Db('mysql:host=localhost;dbname=php', 'root', 'root'); if ($db->pdo) { echo '<h2>连接成功</h2>'; } // 读取数据库测试 $stmt = $db->pdo->prepare('SELECT * FROM `user`'); $stmt->execute(); foreach ($stmt->fetchAll(\PDO::FETCH_ASSOC) as $cate) { print_r($cate); echo '<br>'; } ``` ***** # :-: 六、类的继承 * 继承: `extends` * 扩展: `__construct()`, `method()` * 重写: `function parentMethod(){...}` 重写主要指方法重写, 属性重写意义不大 >[info] 类的继承 ```php namespace admin; class Demo{ // 对象属性 public $product; public $price; // 构造方法 public function __construct($product, $price){ $this->product = $product; $this->price = $price; } // 对象方法 public function getInfo(){ return '品名: ' .$this->product .', 价格: ' . $this->price . '<br>'; } } // 子类Sub1, 代码复用 class Sub1 extends Demo{ // ... } // 实例化子类Sub, 尽管子类中无任何成员,但是它可以直接调用父类Demo中的全部成员 $sub1 = new Sub1('手机', 3500); echo $sub1->getInfo() . '<hr>'; ``` >[info] 扩展父类功能 ```php // 子类Sub2, 增加属性和方法 class Sub2 extends Demo{ public $num; // 数量 // 子类的构造方法 public function __construct($product, $price, $num){ // 调用父类的构造方法,否则还要手工把父类中的属性初始化语句全部写一遍 // parent:: 调用被覆写的父类方法内容 parent::__construct($product, $price); // 只需要添加子类中的成员初始化代码 $this->num = $num; } // 子类中增加一个新方法: 计算总价 public function total(){ return round($this->price * $this->num, 2); } } // 实例化子类 $sub2 = new Sub2('电脑',3980, 13); // 调用子类方法, 计算总价 echo $sub2->product . '的总价: ' . $sub2->total() . '<hr>'; ``` >[info] 方法重写 ```php // 为了促销, 通常会根据总价,给予一定的折扣,刺激消费者购买更多的商品或服务 // 第三个子类, 继承自Sub2, 而Sub2又继承自Demo5,这就形成了多层级的继承关系 class Sub3 extends Sub2{ // 重写父类Sub2total()方法, 增加计算折扣价功能 public function total(){ // 先获取未打折时的商品总金额 $total = parent::total(); // 设置折扣率 switch (true) { case ($total > 20000 && $total < 40000): $discountRate = 0.88; // 88折 break; case ($total > 40000 && $total < 60000): $discountRate = 0.78; // 78折 break; case ($total >= 60000): $discountRate = 0.68; // 68折 break; default: // 小于20000元不打折 $discountRate = 1; } // 结果四舍五入,保留2位小数 // 获取折后价 $discountPrice = round($total*$discountRate, 2); // 如果折扣率小于1, 表示已打折, 给折扣率描红显示 if ($discountRate < 1) { $discountPrice = $discountPrice.' 元, <span style="color: red"> ('.$discountRate.'折)</span>'; } // 返回折扣价 return $discountPrice; } } // 实例化子类, 88折 $sub3 = new Sub3('电脑',3980, 13); // 7.8折 $sub3 = new Sub3('电脑',3980, 23); // 7.8折 $sub3 = new Sub3('电脑',3980, 3); // 不打折 echo '折扣价: ' . $sub3->total(); ``` ***** # :-: 七、访问控制 * `public`: 默认, 类内,类外,子类都可见 * `protected`: 类内, 子类可见, 类外不可见 * `private`: 类内可见, 子类, 类外不可见 ```php namespace admin; class Demo{ // 对象属性 public $name; // 姓名 protected $position; // 职位 private $salary; // 工资 protected $department; // 部门 // 构造方法 public function __construct($name, $position, $salary, $department){ $this->name = $name; $this->position = $position; $this->salary = $salary; $this->department = $department; } // 对于不能通过类实例访问的属性,应该设置对外的访问接口 // 获取器方法 public function getPosition(){ // 职位: 如果不是培训部员工, 无权查看 return $this->department === '培训部' ? $this->position : '无权查看'; } public function getSalary(){ // 工资: 如果不是财务部员工, 无权查看 return $this->department === '财务部' ? $this->salary : '无权查看'; } // 设置器方法 public function setSalary($value){ // 如果是设置salary工资,则必须是财务部有权限 return $this->department === '财务部' ? $this->salary = $value : '无权更新'; } } // 类外部 $obj = new Demo('欧阳克', '讲师', 8888, '培训部'); //$obj = new Demo('欧阳克, '讲师', 8888, '财务部'); echo $obj->name, '<br>'; // public // protected: 外部不能访问 //echo $obj1->position; //private: 外部不能访问 //echo $obj1->salary; // 培训部,有权查看职位,但无权查看和设置工资 // 财务部, 无权查看职位, 但是有权查看和设置工资 echo $obj->getPosition(), '<br>'; echo $obj->getSalary(), '<br>'; echo $obj->setSalary(6666); echo '<hr>'; ``` > 继承,访问控制 ```php // 创建子类Sub, 演示权限的继承与扩展 // 如果在子类中访问父类中的私有属性private class Sub extends Demo6{ public function salary(){ // 在子类中直接访问父类中的private成员是不允许的 // return $this->salary; } } $obj = new Sub('欧阳克', '讲师', 9999, '财务部'); // 在正常的思维中, 父类中的private $salary 是只能在Demo6类中使用, 子类不能访问 echo $obj->salary(); // 报错,子类中无法访问父类中的private成员 // 但是我在父类中创建了一个私有成员的访问接口: getSalary(), 而它的访问限制中public,可以被子类继承 // 所以,通过父类中的私有成员的访问接口方法, 就可以在子类访问父类中的私有成员了,包括私有属性和方法都可以 echo $obj->getSalary(); // 不仅能访问父类的私有属性, 我还能通过父类提供的访问接口设置私有属性的值 $obj->setSalary(5656); echo '<br>'; echo $obj->getSalary(); ```