面向对象编程(OOP)是PHP的核心特性之一,它提供了封装、继承和多态等重要概念。本章将详细介绍PHP中的类和对象、属性和方法、继承、接口、抽象类、命名空间等面向对象编程的核心内容。
7.1 类和对象基础
7.1.1 类的定义和实例化
<?php
// 基本类定义
class User {
// 属性(成员变量)
public $name;
public $email;
private $password;
protected $id;
// 构造函数
public function __construct($name, $email, $password) {
$this->name = $name;
$this->email = $email;
$this->password = password_hash($password, PASSWORD_DEFAULT);
$this->id = uniqid();
}
// 方法(成员函数)
public function getName() {
return $this->name;
}
public function setName($name) {
if (strlen($name) >= 2) {
$this->name = $name;
return true;
}
return false;
}
public function getEmail() {
return $this->email;
}
public function verifyPassword($password) {
return password_verify($password, $this->password);
}
// 受保护的方法
protected function getId() {
return $this->id;
}
// 私有方法
private function hashPassword($password) {
return password_hash($password, PASSWORD_DEFAULT);
}
}
// 创建对象实例
$user1 = new User("张三", "zhangsan@example.com", "password123");
$user2 = new User("李四", "lisi@example.com", "mypassword");
echo "用户1姓名:" . $user1->getName() . "\n";
echo "用户1邮箱:" . $user1->getEmail() . "\n";
echo "密码验证:" . ($user1->verifyPassword("password123") ? "正确" : "错误") . "\n";
// 修改属性
$user1->setName("张三丰");
echo "修改后姓名:" . $user1->getName() . "\n";
// 直接访问公共属性
$user1->name = "张无忌";
echo "直接修改后姓名:" . $user1->name . "\n";
?>
7.1.2 访问修饰符
<?php
class AccessModifierDemo {
public $publicProperty = "公共属性";
protected $protectedProperty = "受保护属性";
private $privateProperty = "私有属性";
public function publicMethod() {
echo "公共方法\n";
echo "访问公共属性:" . $this->publicProperty . "\n";
echo "访问受保护属性:" . $this->protectedProperty . "\n";
echo "访问私有属性:" . $this->privateProperty . "\n";
}
protected function protectedMethod() {
echo "受保护方法\n";
}
private function privateMethod() {
echo "私有方法\n";
}
public function callAllMethods() {
$this->publicMethod();
$this->protectedMethod();
$this->privateMethod();
}
}
class ChildClass extends AccessModifierDemo {
public function accessParentProperties() {
echo "子类访问父类属性:\n";
echo "公共属性:" . $this->publicProperty . "\n";
echo "受保护属性:" . $this->protectedProperty . "\n";
// echo $this->privateProperty; // 错误:无法访问私有属性
}
public function callParentMethods() {
echo "子类调用父类方法:\n";
$this->publicMethod();
$this->protectedMethod();
// $this->privateMethod(); // 错误:无法访问私有方法
}
}
$demo = new AccessModifierDemo();
echo "=== 基类测试 ===\n";
$demo->callAllMethods();
echo "\n=== 子类测试 ===\n";
$child = new ChildClass();
$child->accessParentProperties();
$child->callParentMethods();
// 外部访问测试
echo "\n=== 外部访问测试 ===\n";
echo "公共属性:" . $demo->publicProperty . "\n";
// echo $demo->protectedProperty; // 错误:无法访问受保护属性
// echo $demo->privateProperty; // 错误:无法访问私有属性
?>
7.1.3 魔术方法
<?php
class MagicMethodsDemo {
private $data = [];
// 构造函数
public function __construct($initialData = []) {
$this->data = $initialData;
echo "对象被创建\n";
}
// 析构函数
public function __destruct() {
echo "对象被销毁\n";
}
// 获取不存在的属性时调用
public function __get($name) {
echo "尝试获取属性:$name\n";
return isset($this->data[$name]) ? $this->data[$name] : null;
}
// 设置不存在的属性时调用
public function __set($name, $value) {
echo "设置属性:$name = $value\n";
$this->data[$name] = $value;
}
// 检查属性是否存在时调用
public function __isset($name) {
echo "检查属性是否存在:$name\n";
return isset($this->data[$name]);
}
// 删除属性时调用
public function __unset($name) {
echo "删除属性:$name\n";
unset($this->data[$name]);
}
// 调用不存在的方法时调用
public function __call($name, $arguments) {
echo "调用不存在的方法:$name,参数:" . implode(', ', $arguments) . "\n";
return "方法 $name 不存在";
}
// 静态调用不存在的方法时调用
public static function __callStatic($name, $arguments) {
echo "静态调用不存在的方法:$name,参数:" . implode(', ', $arguments) . "\n";
return "静态方法 $name 不存在";
}
// 对象被当作字符串使用时调用
public function __toString() {
return "MagicMethodsDemo对象,数据:" . json_encode($this->data);
}
// 对象被当作函数调用时调用
public function __invoke($param) {
echo "对象被当作函数调用,参数:$param\n";
return "调用结果:$param";
}
// 对象被克隆时调用
public function __clone() {
echo "对象被克隆\n";
// 深拷贝数据
$this->data = array_merge([], $this->data);
}
// 序列化时调用
public function __sleep() {
echo "对象被序列化\n";
return ['data']; // 返回需要序列化的属性名数组
}
// 反序列化时调用
public function __wakeup() {
echo "对象被反序列化\n";
}
}
echo "=== 魔术方法演示 ===\n";
$obj = new MagicMethodsDemo(['name' => '张三', 'age' => 25]);
// __get 和 __set
echo "姓名:" . $obj->name . "\n";
$obj->email = "zhangsan@example.com";
// __isset 和 __unset
var_dump(isset($obj->name));
unset($obj->age);
// __call
$obj->nonExistentMethod('参数1', '参数2');
// __callStatic
MagicMethodsDemo::nonExistentStaticMethod('静态参数');
// __toString
echo "对象字符串表示:" . $obj . "\n";
// __invoke
echo $obj('调用参数') . "\n";
// __clone
$clonedObj = clone $obj;
// 序列化和反序列化
$serialized = serialize($obj);
$unserialized = unserialize($serialized);
echo "\n=== 对象生命周期结束 ===\n";
?>
7.2 继承和多态
7.2.1 类的继承
<?php
// 基类(父类)
class Animal {
protected $name;
protected $species;
protected $age;
public function __construct($name, $species, $age = 0) {
$this->name = $name;
$this->species = $species;
$this->age = $age;
}
public function getName() {
return $this->name;
}
public function getSpecies() {
return $this->species;
}
public function getAge() {
return $this->age;
}
public function makeSound() {
return "动物发出声音";
}
public function move() {
return "{$this->name}在移动";
}
public function getInfo() {
return "姓名:{$this->name},种类:{$this->species},年龄:{$this->age}";
}
}
// 派生类(子类)
class Dog extends Animal {
private $breed;
public function __construct($name, $age = 0, $breed = '未知') {
parent::__construct($name, '犬类', $age);
$this->breed = $breed;
}
public function getBreed() {
return $this->breed;
}
// 重写父类方法
public function makeSound() {
return "{$this->name}汪汪叫";
}
public function move() {
return "{$this->name}跑来跑去";
}
// 新增方法
public function fetch() {
return "{$this->name}去捡球";
}
public function wagTail() {
return "{$this->name}摇尾巴";
}
// 重写getInfo方法
public function getInfo() {
return parent::getInfo() . ",品种:{$this->breed}";
}
}
class Cat extends Animal {
private $isIndoor;
public function __construct($name, $age = 0, $isIndoor = true) {
parent::__construct($name, '猫类', $age);
$this->isIndoor = $isIndoor;
}
public function isIndoorCat() {
return $this->isIndoor;
}
// 重写父类方法
public function makeSound() {
return "{$this->name}喵喵叫";
}
public function move() {
return "{$this->name}优雅地走动";
}
// 新增方法
public function purr() {
return "{$this->name}发出呼噜声";
}
public function climb() {
return "{$this->name}爬树";
}
public function getInfo() {
$location = $this->isIndoor ? '室内' : '室外';
return parent::getInfo() . ",生活环境:{$location}";
}
}
// 使用继承
echo "=== 继承演示 ===\n";
$animal = new Animal('通用动物', '未知种类', 5);
$dog = new Dog('旺财', 3, '金毛');
$cat = new Cat('咪咪', 2, true);
echo "基类动物:\n";
echo $animal->getInfo() . "\n";
echo $animal->makeSound() . "\n";
echo $animal->move() . "\n\n";
echo "狗类:\n";
echo $dog->getInfo() . "\n";
echo $dog->makeSound() . "\n";
echo $dog->move() . "\n";
echo $dog->fetch() . "\n";
echo $dog->wagTail() . "\n\n";
echo "猫类:\n";
echo $cat->getInfo() . "\n";
echo $cat->makeSound() . "\n";
echo $cat->move() . "\n";
echo $cat->purr() . "\n";
echo $cat->climb() . "\n";
?>
7.2.2 多态性
<?php
// 多态演示
function animalConcert($animals) {
echo "=== 动物音乐会 ===\n";
foreach ($animals as $animal) {
echo $animal->getName() . ":" . $animal->makeSound() . "\n";
}
}
function animalSports($animals) {
echo "\n=== 动物运动会 ===\n";
foreach ($animals as $animal) {
echo $animal->move() . "\n";
}
}
// 创建不同类型的动物
$animals = [
new Dog('旺财', 3, '金毛'),
new Cat('咪咪', 2, true),
new Dog('小黑', 1, '拉布拉多'),
new Cat('橘猫', 4, false),
new Animal('鹦鹉', '鸟类', 1)
];
// 多态调用 - 同样的方法调用,不同的行为
animalConcert($animals);
animalSports($animals);
// 类型检查
echo "\n=== 类型检查 ===\n";
foreach ($animals as $animal) {
echo $animal->getName() . " 是 ";
if ($animal instanceof Dog) {
echo "狗类,品种:" . $animal->getBreed() . "\n";
} elseif ($animal instanceof Cat) {
echo "猫类," . ($animal->isIndoorCat() ? "室内猫" : "室外猫") . "\n";
} else {
echo "基础动物类\n";
}
}
// 方法存在性检查
echo "\n=== 方法检查 ===\n";
foreach ($animals as $animal) {
echo $animal->getName() . ":";
if (method_exists($animal, 'fetch')) {
echo $animal->fetch() . "\n";
} elseif (method_exists($animal, 'purr')) {
echo $animal->purr() . "\n";
} else {
echo "没有特殊技能\n";
}
}
?>
7.3 抽象类和接口
7.3.1 抽象类
<?php
// 抽象类
abstract class Shape {
protected $color;
protected $name;
public function __construct($color, $name) {
$this->color = $color;
$this->name = $name;
}
// 具体方法
public function getColor() {
return $this->color;
}
public function getName() {
return $this->name;
}
public function getInfo() {
return "形状:{$this->name},颜色:{$this->color}";
}
// 抽象方法 - 子类必须实现
abstract public function calculateArea();
abstract public function calculatePerimeter();
abstract public function draw();
}
// 具体实现类
class Rectangle extends Shape {
private $width;
private $height;
public function __construct($color, $width, $height) {
parent::__construct($color, '矩形');
$this->width = $width;
$this->height = $height;
}
public function calculateArea() {
return $this->width * $this->height;
}
public function calculatePerimeter() {
return 2 * ($this->width + $this->height);
}
public function draw() {
return "绘制{$this->color}的矩形,宽度:{$this->width},高度:{$this->height}";
}
public function getWidth() {
return $this->width;
}
public function getHeight() {
return $this->height;
}
}
class Circle extends Shape {
private $radius;
public function __construct($color, $radius) {
parent::__construct($color, '圆形');
$this->radius = $radius;
}
public function calculateArea() {
return pi() * $this->radius * $this->radius;
}
public function calculatePerimeter() {
return 2 * pi() * $this->radius;
}
public function draw() {
return "绘制{$this->color}的圆形,半径:{$this->radius}";
}
public function getRadius() {
return $this->radius;
}
}
class Triangle extends Shape {
private $side1, $side2, $side3;
public function __construct($color, $side1, $side2, $side3) {
parent::__construct($color, '三角形');
$this->side1 = $side1;
$this->side2 = $side2;
$this->side3 = $side3;
}
public function calculateArea() {
// 使用海伦公式
$s = $this->calculatePerimeter() / 2;
return sqrt($s * ($s - $this->side1) * ($s - $this->side2) * ($s - $this->side3));
}
public function calculatePerimeter() {
return $this->side1 + $this->side2 + $this->side3;
}
public function draw() {
return "绘制{$this->color}的三角形,边长:{$this->side1}, {$this->side2}, {$this->side3}";
}
}
// 使用抽象类
echo "=== 抽象类演示 ===\n";
$shapes = [
new Rectangle('红色', 10, 5),
new Circle('蓝色', 7),
new Triangle('绿色', 3, 4, 5)
];
foreach ($shapes as $shape) {
echo $shape->getInfo() . "\n";
echo "面积:" . number_format($shape->calculateArea(), 2) . "\n";
echo "周长:" . number_format($shape->calculatePerimeter(), 2) . "\n";
echo $shape->draw() . "\n\n";
}
// 计算总面积
$totalArea = 0;
foreach ($shapes as $shape) {
$totalArea += $shape->calculateArea();
}
echo "所有形状的总面积:" . number_format($totalArea, 2) . "\n";
?>
7.3.2 接口
<?php
// 接口定义
interface Drawable {
public function draw();
public function getDrawingInfo();
}
interface Movable {
public function move($x, $y);
public function getPosition();
}
interface Resizable {
public function resize($factor);
public function getSize();
}
// 实现多个接口的类
class GraphicElement implements Drawable, Movable, Resizable {
private $x, $y;
private $width, $height;
private $color;
private $type;
public function __construct($type, $color, $x = 0, $y = 0, $width = 100, $height = 100) {
$this->type = $type;
$this->color = $color;
$this->x = $x;
$this->y = $y;
$this->width = $width;
$this->height = $height;
}
// 实现Drawable接口
public function draw() {
return "在位置({$this->x}, {$this->y})绘制{$this->color}的{$this->type}";
}
public function getDrawingInfo() {
return [
'type' => $this->type,
'color' => $this->color,
'position' => [$this->x, $this->y],
'size' => [$this->width, $this->height]
];
}
// 实现Movable接口
public function move($x, $y) {
$this->x += $x;
$this->y += $y;
return "移动到位置({$this->x}, {$this->y})";
}
public function getPosition() {
return ['x' => $this->x, 'y' => $this->y];
}
// 实现Resizable接口
public function resize($factor) {
$this->width *= $factor;
$this->height *= $factor;
return "调整大小,新尺寸:{$this->width} x {$this->height}";
}
public function getSize() {
return ['width' => $this->width, 'height' => $this->height];
}
}
// 只实现部分接口的类
class StaticImage implements Drawable {
private $filename;
private $alt;
public function __construct($filename, $alt = '') {
$this->filename = $filename;
$this->alt = $alt;
}
public function draw() {
return "显示图片:{$this->filename}";
}
public function getDrawingInfo() {
return [
'type' => 'image',
'filename' => $this->filename,
'alt' => $this->alt
];
}
}
// 接口继承
interface AdvancedDrawable extends Drawable {
public function drawWithEffects($effects);
public function getLayerInfo();
}
class AdvancedGraphic implements AdvancedDrawable, Movable {
private $x, $y;
private $layer;
private $effects;
public function __construct($x = 0, $y = 0, $layer = 1) {
$this->x = $x;
$this->y = $y;
$this->layer = $layer;
$this->effects = [];
}
public function draw() {
return "在图层{$this->layer}绘制高级图形";
}
public function getDrawingInfo() {
return [
'type' => 'advanced_graphic',
'layer' => $this->layer,
'position' => [$this->x, $this->y],
'effects' => $this->effects
];
}
public function drawWithEffects($effects) {
$this->effects = $effects;
return "应用效果:" . implode(', ', $effects) . " 并绘制";
}
public function getLayerInfo() {
return ['layer' => $this->layer, 'effects_count' => count($this->effects)];
}
public function move($x, $y) {
$this->x += $x;
$this->y += $y;
return "高级图形移动到({$this->x}, {$this->y})";
}
public function getPosition() {
return ['x' => $this->x, 'y' => $this->y];
}
}
// 使用接口
echo "=== 接口演示 ===\n";
$elements = [
new GraphicElement('矩形', '红色', 10, 20, 100, 50),
new StaticImage('logo.png', '公司标志'),
new AdvancedGraphic(50, 100, 2)
];
// 多态调用 - 所有元素都实现了Drawable接口
foreach ($elements as $element) {
echo $element->draw() . "\n";
print_r($element->getDrawingInfo());
echo "\n";
}
// 类型检查和特定接口调用
echo "=== 接口类型检查 ===\n";
foreach ($elements as $element) {
if ($element instanceof Movable) {
echo $element->move(5, 5) . "\n";
print_r($element->getPosition());
}
if ($element instanceof Resizable) {
echo $element->resize(1.5) . "\n";
print_r($element->getSize());
}
if ($element instanceof AdvancedDrawable) {
echo $element->drawWithEffects(['阴影', '发光', '模糊']) . "\n";
print_r($element->getLayerInfo());
}
echo "\n";
}
?>
7.4 特性(Traits)
7.4.1 Trait基础
<?php
// 定义Trait
trait Loggable {
private $logs = [];
public function log($message, $level = 'INFO') {
$timestamp = date('Y-m-d H:i:s');
$this->logs[] = "[$timestamp] [$level] $message";
}
public function getLogs() {
return $this->logs;
}
public function clearLogs() {
$this->logs = [];
}
public function getLastLog() {
return end($this->logs);
}
}
trait Timestampable {
private $createdAt;
private $updatedAt;
public function initTimestamps() {
$this->createdAt = new DateTime();
$this->updatedAt = new DateTime();
}
public function updateTimestamp() {
$this->updatedAt = new DateTime();
}
public function getCreatedAt() {
return $this->createdAt;
}
public function getUpdatedAt() {
return $this->updatedAt;
}
public function getAge() {
if (!$this->createdAt) {
return null;
}
return $this->createdAt->diff(new DateTime())->format('%a 天');
}
}
trait Cacheable {
private $cache = [];
private $cacheEnabled = true;
public function enableCache() {
$this->cacheEnabled = true;
}
public function disableCache() {
$this->cacheEnabled = false;
$this->clearCache();
}
public function cacheGet($key) {
if (!$this->cacheEnabled) {
return null;
}
return isset($this->cache[$key]) ? $this->cache[$key] : null;
}
public function cacheSet($key, $value, $ttl = 3600) {
if (!$this->cacheEnabled) {
return;
}
$this->cache[$key] = [
'value' => $value,
'expires' => time() + $ttl
];
}
public function clearCache() {
$this->cache = [];
}
private function isExpired($key) {
return isset($this->cache[$key]) && $this->cache[$key]['expires'] < time();
}
}
// 使用多个Trait的类
class Article {
use Loggable, Timestampable, Cacheable;
private $title;
private $content;
private $author;
public function __construct($title, $content, $author) {
$this->title = $title;
$this->content = $content;
$this->author = $author;
$this->initTimestamps();
$this->log("文章创建:$title");
}
public function getTitle() {
return $this->title;
}
public function setTitle($title) {
$oldTitle = $this->title;
$this->title = $title;
$this->updateTimestamp();
$this->log("标题修改:$oldTitle -> $title");
}
public function getContent() {
// 尝试从缓存获取
$cached = $this->cacheGet('content');
if ($cached !== null) {
$this->log("从缓存获取内容");
return $cached['value'];
}
// 模拟复杂的内容处理
$processedContent = $this->processContent($this->content);
$this->cacheSet('content', $processedContent);
$this->log("内容处理完成并缓存");
return $processedContent;
}
public function setContent($content) {
$this->content = $content;
$this->updateTimestamp();
$this->clearCache(); // 清除缓存
$this->log("内容已更新");
}
public function getAuthor() {
return $this->author;
}
private function processContent($content) {
// 模拟内容处理(如Markdown转换等)
return strtoupper($content); // 简单示例
}
public function getInfo() {
return [
'title' => $this->title,
'author' => $this->author,
'created' => $this->getCreatedAt()->format('Y-m-d H:i:s'),
'updated' => $this->getUpdatedAt()->format('Y-m-d H:i:s'),
'age' => $this->getAge()
];
}
}
// 使用示例
echo "=== Trait演示 ===\n";
$article = new Article(
"PHP面向对象编程",
"这是一篇关于PHP OOP的文章内容...",
"张三"
);
echo "文章信息:\n";
print_r($article->getInfo());
echo "\n获取内容(第一次):\n";
echo $article->getContent() . "\n";
echo "\n获取内容(第二次,从缓存):\n";
echo $article->getContent() . "\n";
// 修改文章
sleep(1); // 确保时间戳不同
$article->setTitle("PHP高级面向对象编程");
$article->setContent("这是更新后的文章内容...");
echo "\n更新后的文章信息:\n";
print_r($article->getInfo());
echo "\n操作日志:\n";
foreach ($article->getLogs() as $log) {
echo $log . "\n";
}
?>
7.4.2 Trait冲突解决
<?php
// 定义有冲突方法的Trait
trait TraitA {
public function commonMethod() {
return "来自TraitA的方法";
}
public function methodA() {
return "TraitA特有方法";
}
}
trait TraitB {
public function commonMethod() {
return "来自TraitB的方法";
}
public function methodB() {
return "TraitB特有方法";
}
}
trait TraitC {
public function methodC() {
return "TraitC特有方法";
}
private function privateMethod() {
return "TraitC私有方法";
}
}
// 解决冲突的类
class ConflictResolver {
use TraitA, TraitB, TraitC {
// 指定使用TraitA的commonMethod
TraitA::commonMethod insteadof TraitB;
// 给TraitB的commonMethod起别名
TraitB::commonMethod as commonMethodFromB;
// 修改方法可见性
TraitC::privateMethod as public publicPrivateMethod;
}
public function demonstrateResolution() {
echo "默认commonMethod:" . $this->commonMethod() . "\n";
echo "TraitB的commonMethod:" . $this->commonMethodFromB() . "\n";
echo "TraitA特有方法:" . $this->methodA() . "\n";
echo "TraitB特有方法:" . $this->methodB() . "\n";
echo "TraitC特有方法:" . $this->methodC() . "\n";
echo "公开的私有方法:" . $this->publicPrivateMethod() . "\n";
}
}
// Trait组合
trait CombinedTrait {
use TraitA, TraitB {
TraitA::commonMethod insteadof TraitB;
TraitB::commonMethod as alternativeMethod;
}
public function getCombinedInfo() {
return [
'primary' => $this->commonMethod(),
'alternative' => $this->alternativeMethod(),
'methodA' => $this->methodA(),
'methodB' => $this->methodB()
];
}
}
class UsingCombinedTrait {
use CombinedTrait;
public function showAll() {
return $this->getCombinedInfo();
}
}
// 演示冲突解决
echo "=== Trait冲突解决演示 ===\n";
$resolver = new ConflictResolver();
$resolver->demonstrateResolution();
echo "\n=== 组合Trait演示 ===\n";
$combined = new UsingCombinedTrait();
print_r($combined->showAll());
?>
7.5 命名空间
7.5.1 命名空间基础
<?php
// 文件:User.php
namespace App\Models;
class User {
private $name;
private $email;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
}
public function getName() {
return $this->name;
}
public function getEmail() {
return $this->email;
}
}
// 文件:UserController.php
namespace App\Controllers;
use App\Models\User;
use App\Services\EmailService;
use DateTime;
class UserController {
private $emailService;
public function __construct() {
$this->emailService = new EmailService();
}
public function createUser($name, $email) {
// 创建用户
$user = new User($name, $email);
// 发送欢迎邮件
$this->emailService->sendWelcomeEmail($user);
return $user;
}
public function getCurrentTime() {
// 使用全局类
return new DateTime();
}
public function getFullClassName() {
// 获取完全限定类名
return __NAMESPACE__ . '\\UserController';
}
}
// 文件:EmailService.php
namespace App\Services;
use App\Models\User;
class EmailService {
public function sendWelcomeEmail(User $user) {
return "发送欢迎邮件给:" . $user->getName() . " (" . $user->getEmail() . ")";
}
public function sendNotification($email, $message) {
return "发送通知到 $email:$message";
}
}
// 文件:main.php - 使用命名空间
namespace App;
// 导入需要的类
use App\Controllers\UserController;
use App\Models\User;
use App\Services\EmailService;
// 别名
use App\Controllers\UserController as UC;
use DateTime as DT;
echo "=== 命名空间演示 ===\n";
// 使用完全限定名
$user1 = new \App\Models\User("张三", "zhangsan@example.com");
echo "用户1:" . $user1->getName() . "\n";
// 使用导入的类
$controller = new UserController();
$user2 = $controller->createUser("李四", "lisi@example.com");
echo "用户2:" . $user2->getName() . "\n";
// 使用别名
$controller2 = new UC();
echo "控制器类名:" . $controller2->getFullClassName() . "\n";
// 使用别名的DateTime
$now = new DT();
echo "当前时间:" . $now->format('Y-m-d H:i:s') . "\n";
// 直接使用服务
$emailService = new EmailService();
echo $emailService->sendWelcomeEmail($user1) . "\n";
?>
7.5.2 子命名空间和组织结构
<?php
// 文件结构示例
// App/
// Models/
// User.php
// Product.php
// Controllers/
// Web/
// UserController.php
// ProductController.php
// Api/
// V1/
// UserController.php
// ProductController.php
// Services/
// Auth/
// AuthService.php
// TokenService.php
// Payment/
// PayPalService.php
// StripeService.php
// App/Controllers/Web/UserController.php
namespace App\Controllers\Web;
use App\Models\User;
use App\Services\Auth\AuthService;
class UserController {
private $authService;
public function __construct() {
$this->authService = new AuthService();
}
public function login($email, $password) {
return $this->authService->authenticate($email, $password);
}
public function register($userData) {
$user = new User($userData['name'], $userData['email']);
return $this->authService->register($user, $userData['password']);
}
}
// App/Controllers/Api/V1/UserController.php
namespace App\Controllers\Api\V1;
use App\Models\User;
use App\Services\Auth\TokenService;
class UserController {
private $tokenService;
public function __construct() {
$this->tokenService = new TokenService();
}
public function apiLogin($email, $password) {
// API登录逻辑
$token = $this->tokenService->generateToken($email);
return ['token' => $token, 'expires_in' => 3600];
}
public function getProfile($token) {
if ($this->tokenService->validateToken($token)) {
return ['name' => '用户名', 'email' => 'user@example.com'];
}
return ['error' => '无效令牌'];
}
}
// App/Services/Auth/AuthService.php
namespace App\Services\Auth;
use App\Models\User;
class AuthService {
public function authenticate($email, $password) {
// 模拟认证逻辑
if ($email === 'admin@example.com' && $password === 'password') {
return new User('管理员', $email);
}
return false;
}
public function register(User $user, $password) {
// 模拟注册逻辑
return "用户 {$user->getName()} 注册成功";
}
}
// App/Services/Auth/TokenService.php
namespace App\Services\Auth;
class TokenService {
private $tokens = [];
public function generateToken($email) {
$token = bin2hex(random_bytes(32));
$this->tokens[$token] = [
'email' => $email,
'expires' => time() + 3600
];
return $token;
}
public function validateToken($token) {
if (isset($this->tokens[$token])) {
return $this->tokens[$token]['expires'] > time();
}
return false;
}
}
// 使用示例
namespace App;
use App\Controllers\Web\UserController as WebUserController;
use App\Controllers\Api\V1\UserController as ApiUserController;
echo "=== 子命名空间演示 ===\n";
// Web控制器
$webController = new WebUserController();
$webUser = $webController->login('admin@example.com', 'password');
if ($webUser) {
echo "Web登录成功:" . $webUser->getName() . "\n";
}
// API控制器
$apiController = new ApiUserController();
$apiResponse = $apiController->apiLogin('admin@example.com', 'password');
echo "API登录响应:" . json_encode($apiResponse) . "\n";
$profile = $apiController->getProfile($apiResponse['token']);
echo "用户资料:" . json_encode($profile) . "\n";
?>
7.6 自动加载
7.6.1 PSR-4自动加载
<?php
// 文件:autoload.php
class Autoloader {
private $prefixes = [];
/**
* 注册自动加载器
*/
public function register() {
spl_autoload_register([$this, 'loadClass']);
}
/**
* 添加命名空间前缀
*/
public function addNamespace($prefix, $baseDir, $prepend = false) {
// 规范化命名空间前缀
$prefix = trim($prefix, '\\') . '\\';
// 规范化基础目录
$baseDir = rtrim($baseDir, DIRECTORY_SEPARATOR) . '/';
// 初始化命名空间前缀数组
if (isset($this->prefixes[$prefix]) === false) {
$this->prefixes[$prefix] = [];
}
// 保留基础目录
if ($prepend) {
array_unshift($this->prefixes[$prefix], $baseDir);
} else {
array_push($this->prefixes[$prefix], $baseDir);
}
}
/**
* 加载类文件
*/
public function loadClass($class) {
// 当前命名空间前缀
$prefix = $class;
// 从后往前查找命名空间前缀
while (false !== $pos = strrpos($prefix, '\\')) {
// 保留命名空间分隔符
$prefix = substr($class, 0, $pos + 1);
// 剩余的类名
$relativeClass = substr($class, $pos + 1);
// 尝试加载映射的文件
$mappedFile = $this->loadMappedFile($prefix, $relativeClass);
if ($mappedFile) {
return $mappedFile;
}
// 移除尾部的命名空间分隔符
$prefix = rtrim($prefix, '\\');
}
return false;
}
/**
* 加载映射文件
*/
protected function loadMappedFile($prefix, $relativeClass) {
// 检查前缀是否有基础目录
if (isset($this->prefixes[$prefix]) === false) {
return false;
}
// 遍历前缀的基础目录
foreach ($this->prefixes[$prefix] as $baseDir) {
// 将命名空间分隔符替换为目录分隔符
$file = $baseDir . str_replace('\\', '/', $relativeClass) . '.php';
// 如果映射文件存在,则加载它
if ($this->requireFile($file)) {
return $file;
}
}
return false;
}
/**
* 如果文件存在,则从文件系统中加载
*/
protected function requireFile($file) {
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}
// 使用自动加载器
$loader = new Autoloader();
$loader->addNamespace('App', __DIR__ . '/src');
$loader->addNamespace('Tests', __DIR__ . '/tests');
$loader->register();
echo "=== 自动加载演示 ===\n";
echo "自动加载器已注册\n";
// 现在可以直接使用类,无需手动require
// $user = new App\Models\User('张三', 'zhangsan@example.com');
// $controller = new App\Controllers\UserController();
?>
7.6.2 Composer自动加载
// composer.json
{
"name": "myapp/php-tutorial",
"description": "PHP教程示例项目",
"type": "project",
"autoload": {
"psr-4": {
"App\\": "src/",
"Tests\\": "tests/"
},
"files": [
"src/helpers.php"
],
"classmap": [
"legacy/"
]
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"require": {
"php": ">=7.4",
"monolog/monolog": "^2.0"
},
"require-dev": {
"phpunit/phpunit": "^9.0"
}
}
<?php
// 使用Composer自动加载
require_once 'vendor/autoload.php';
use App\Models\User;
use App\Controllers\UserController;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
// 创建日志记录器
$log = new Logger('app');
$log->pushHandler(new StreamHandler('app.log', Logger::WARNING));
// 使用应用类
$user = new User('张三', 'zhangsan@example.com');
$controller = new UserController();
$log->info('用户创建', ['user' => $user->getName()]);
echo "Composer自动加载演示完成\n";
?>
7.7 常用设计模式
7.7.1 单例模式
<?php
class Database {
private static $instance = null;
private $connection;
private $host = 'localhost';
private $username = 'root';
private $password = '';
private $database = 'test';
// 私有构造函数,防止外部实例化
private function __construct() {
try {
$this->connection = new PDO(
"mysql:host={$this->host};dbname={$this->database}",
$this->username,
$this->password,
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
die("数据库连接失败:" . $e->getMessage());
}
}
// 防止克隆
private function __clone() {}
// 防止反序列化
public function __wakeup() {
throw new Exception("不能反序列化单例");
}
// 获取实例
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 获取连接
public function getConnection() {
return $this->connection;
}
// 执行查询
public function query($sql, $params = []) {
try {
$stmt = $this->connection->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
throw new Exception("查询执行失败:" . $e->getMessage());
}
}
}
// 配置管理单例
class Config {
private static $instance = null;
private $config = [];
private function __construct() {
// 加载配置文件
$this->config = [
'app' => [
'name' => 'PHP教程应用',
'version' => '1.0.0',
'debug' => true
],
'database' => [
'host' => 'localhost',
'port' => 3306,
'name' => 'tutorial_db'
],
'cache' => [
'driver' => 'redis',
'ttl' => 3600
]
];
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
public function get($key, $default = null) {
$keys = explode('.', $key);
$value = $this->config;
foreach ($keys as $k) {
if (isset($value[$k])) {
$value = $value[$k];
} else {
return $default;
}
}
return $value;
}
public function set($key, $value) {
$keys = explode('.', $key);
$config = &$this->config;
foreach ($keys as $k) {
if (!isset($config[$k])) {
$config[$k] = [];
}
$config = &$config[$k];
}
$config = $value;
}
}
// 使用单例模式
echo "=== 单例模式演示 ===\n";
// 配置管理
$config = Config::getInstance();
echo "应用名称:" . $config->get('app.name') . "\n";
echo "数据库主机:" . $config->get('database.host') . "\n";
echo "缓存TTL:" . $config->get('cache.ttl') . "\n";
// 验证单例
$config2 = Config::getInstance();
var_dump($config === $config2); // true
// 数据库连接(注释掉,因为可能没有实际数据库)
// $db1 = Database::getInstance();
// $db2 = Database::getInstance();
// var_dump($db1 === $db2); // true
?>
7.7.2 工厂模式
<?php
// 产品接口
interface LoggerInterface {
public function log($message, $level = 'INFO');
public function getType();
}
// 具体产品
class FileLogger implements LoggerInterface {
private $filename;
public function __construct($filename = 'app.log') {
$this->filename = $filename;
}
public function log($message, $level = 'INFO') {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[$timestamp] [$level] $message" . PHP_EOL;
file_put_contents($this->filename, $logEntry, FILE_APPEND | LOCK_EX);
}
public function getType() {
return 'file';
}
}
class DatabaseLogger implements LoggerInterface {
private $table;
public function __construct($table = 'logs') {
$this->table = $table;
}
public function log($message, $level = 'INFO') {
// 模拟数据库日志记录
echo "数据库日志:插入到 {$this->table} 表 - [$level] $message\n";
}
public function getType() {
return 'database';
}
}
class EmailLogger implements LoggerInterface {
private $email;
public function __construct($email = 'admin@example.com') {
$this->email = $email;
}
public function log($message, $level = 'INFO') {
if ($level === 'ERROR' || $level === 'CRITICAL') {
echo "邮件日志:发送到 {$this->email} - [$level] $message\n";
}
}
public function getType() {
return 'email';
}
}
// 简单工厂
class LoggerFactory {
public static function create($type, $config = []) {
switch (strtolower($type)) {
case 'file':
$filename = $config['filename'] ?? 'app.log';
return new FileLogger($filename);
case 'database':
$table = $config['table'] ?? 'logs';
return new DatabaseLogger($table);
case 'email':
$email = $config['email'] ?? 'admin@example.com';
return new EmailLogger($email);
default:
throw new InvalidArgumentException("不支持的日志类型:$type");
}
}
public static function createMultiple($types) {
$loggers = [];
foreach ($types as $type => $config) {
$loggers[] = self::create($type, $config);
}
return $loggers;
}
}
// 抽象工厂模式
abstract class AbstractLoggerFactory {
abstract public function createFileLogger();
abstract public function createDatabaseLogger();
abstract public function createEmailLogger();
}
class ProductionLoggerFactory extends AbstractLoggerFactory {
public function createFileLogger() {
return new FileLogger('/var/log/app/production.log');
}
public function createDatabaseLogger() {
return new DatabaseLogger('production_logs');
}
public function createEmailLogger() {
return new EmailLogger('admin@production.com');
}
}
class DevelopmentLoggerFactory extends AbstractLoggerFactory {
public function createFileLogger() {
return new FileLogger('debug.log');
}
public function createDatabaseLogger() {
return new DatabaseLogger('dev_logs');
}
public function createEmailLogger() {
return new EmailLogger('dev@localhost.com');
}
}
// 使用工厂模式
echo "=== 工厂模式演示 ===\n";
// 简单工厂
echo "--- 简单工厂 ---\n";
$fileLogger = LoggerFactory::create('file', ['filename' => 'test.log']);
$dbLogger = LoggerFactory::create('database', ['table' => 'app_logs']);
$emailLogger = LoggerFactory::create('email', ['email' => 'test@example.com']);
$fileLogger->log('这是一个文件日志消息');
$dbLogger->log('这是一个数据库日志消息', 'WARNING');
$emailLogger->log('这是一个严重错误', 'ERROR');
// 批量创建
echo "\n--- 批量创建 ---\n";
$loggers = LoggerFactory::createMultiple([
'file' => ['filename' => 'batch.log'],
'database' => ['table' => 'batch_logs'],
'email' => ['email' => 'batch@example.com']
]);
foreach ($loggers as $logger) {
$logger->log("批量日志消息 - 类型:" . $logger->getType());
}
// 抽象工厂
echo "\n--- 抽象工厂 ---\n";
$environment = 'development'; // 或 'production'
if ($environment === 'production') {
$factory = new ProductionLoggerFactory();
} else {
$factory = new DevelopmentLoggerFactory();
}
$fileLogger = $factory->createFileLogger();
$dbLogger = $factory->createDatabaseLogger();
$emailLogger = $factory->createEmailLogger();
$fileLogger->log("环境:$environment - 文件日志");
$dbLogger->log("环境:$environment - 数据库日志");
$emailLogger->log("环境:$environment - 严重错误", 'CRITICAL');
?>
7.7.3 观察者模式
<?php
// 观察者接口
interface ObserverInterface {
public function update($subject, $event, $data = null);
}
// 主题接口
interface SubjectInterface {
public function attach(ObserverInterface $observer);
public function detach(ObserverInterface $observer);
public function notify($event, $data = null);
}
// 具体主题 - 用户模型
class User implements SubjectInterface {
private $observers = [];
private $name;
private $email;
private $status;
public function __construct($name, $email) {
$this->name = $name;
$this->email = $email;
$this->status = 'active';
}
public function attach(ObserverInterface $observer) {
$this->observers[] = $observer;
}
public function detach(ObserverInterface $observer) {
$key = array_search($observer, $this->observers, true);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify($event, $data = null) {
foreach ($this->observers as $observer) {
$observer->update($this, $event, $data);
}
}
public function setName($name) {
$oldName = $this->name;
$this->name = $name;
$this->notify('name_changed', ['old' => $oldName, 'new' => $name]);
}
public function setEmail($email) {
$oldEmail = $this->email;
$this->email = $email;
$this->notify('email_changed', ['old' => $oldEmail, 'new' => $email]);
}
public function setStatus($status) {
$oldStatus = $this->status;
$this->status = $status;
$this->notify('status_changed', ['old' => $oldStatus, 'new' => $status]);
}
public function getName() {
return $this->name;
}
public function getEmail() {
return $this->email;
}
public function getStatus() {
return $this->status;
}
}
// 具体观察者 - 邮件通知
class EmailNotificationObserver implements ObserverInterface {
public function update($subject, $event, $data = null) {
switch ($event) {
case 'email_changed':
$this->sendEmailChangeNotification($subject, $data);
break;
case 'status_changed':
if ($data['new'] === 'suspended') {
$this->sendSuspensionNotification($subject);
}
break;
}
}
private function sendEmailChangeNotification($user, $data) {
echo "邮件通知:用户 {$user->getName()} 的邮箱从 {$data['old']} 更改为 {$data['new']}\n";
}
private function sendSuspensionNotification($user) {
echo "邮件通知:用户 {$user->getName()} 账户已被暂停\n";
}
}
// 具体观察者 - 日志记录
class LoggingObserver implements ObserverInterface {
public function update($subject, $event, $data = null) {
$timestamp = date('Y-m-d H:i:s');
$userName = $subject->getName();
switch ($event) {
case 'name_changed':
echo "日志:[$timestamp] 用户名更改 - $userName: {$data['old']} -> {$data['new']}\n";
break;
case 'email_changed':
echo "日志:[$timestamp] 邮箱更改 - $userName: {$data['old']} -> {$data['new']}\n";
break;
case 'status_changed':
echo "日志:[$timestamp] 状态更改 - $userName: {$data['old']} -> {$data['new']}\n";
break;
}
}
}
// 具体观察者 - 缓存更新
class CacheObserver implements ObserverInterface {
private $cache = [];
public function update($subject, $event, $data = null) {
$userId = spl_object_hash($subject);
switch ($event) {
case 'name_changed':
case 'email_changed':
case 'status_changed':
$this->invalidateUserCache($userId);
$this->updateUserCache($subject);
break;
}
}
private function invalidateUserCache($userId) {
unset($this->cache[$userId]);
echo "缓存:清除用户 $userId 的缓存\n";
}
private function updateUserCache($user) {
$userId = spl_object_hash($user);
$this->cache[$userId] = [
'name' => $user->getName(),
'email' => $user->getEmail(),
'status' => $user->getStatus(),
'cached_at' => time()
];
echo "缓存:更新用户 {$user->getName()} 的缓存\n";
}
public function getCache() {
return $this->cache;
}
}
// 使用观察者模式
echo "=== 观察者模式演示 ===\n";
// 创建用户
$user = new User('张三', 'zhangsan@example.com');
// 创建观察者
$emailObserver = new EmailNotificationObserver();
$loggingObserver = new LoggingObserver();
$cacheObserver = new CacheObserver();
// 注册观察者
$user->attach($emailObserver);
$user->attach($loggingObserver);
$user->attach($cacheObserver);
echo "初始用户信息:{$user->getName()}, {$user->getEmail()}, {$user->getStatus()}\n\n";
// 触发事件
echo "--- 更改用户名 ---\n";
$user->setName('张三丰');
echo "\n--- 更改邮箱 ---\n";
$user->setEmail('zhangsanfeng@example.com');
echo "\n--- 暂停用户 ---\n";
$user->setStatus('suspended');
echo "\n--- 移除邮件观察者后再次更改状态 ---\n";
$user->detach($emailObserver);
$user->setStatus('active');
echo "\n--- 缓存内容 ---\n";
print_r($cacheObserver->getCache());
?>
7.8 本章小结
面向对象编程是PHP的重要特性,本章详细介绍了:
核心概念
- 类和对象:封装数据和行为的基本单位
- 属性和方法:类的数据成员和行为定义
- 访问修饰符:控制成员的可见性(public、protected、private)
- 魔术方法:PHP提供的特殊方法,如构造函数、析构函数等
面向对象特性
- 继承:代码复用和扩展的重要机制
- 多态:同一接口的不同实现
- 抽象类:定义通用接口和部分实现
- 接口:定义类必须实现的契约
- Trait:代码复用的水平组合机制
高级特性
- 命名空间:避免命名冲突,组织代码结构
- 自动加载:自动加载类文件,提高开发效率
- 设计模式:解决常见问题的最佳实践
最佳实践
- 遵循SOLID原则
- 使用合适的设计模式
- 合理组织命名空间
- 编写可测试的代码
- 注重代码的可读性和可维护性
7.9 实践练习
练习1:图书管理系统
设计一个图书管理系统,包含以下要求: - 创建Book、Author、Library类 - 实现借书、还书功能 - 使用接口定义可借阅物品 - 使用观察者模式通知图书状态变化
练习2:电商购物车
实现一个购物车系统: - 使用工厂模式创建不同类型的商品 - 实现购物车的添加、删除、计算总价功能 - 使用策略模式实现不同的优惠计算 - 使用单例模式管理购物车会话
练习3:日志系统
设计一个灵活的日志系统: - 支持多种日志输出方式(文件、数据库、邮件) - 使用抽象工厂模式创建不同环境的日志器 - 实现日志级别过滤 - 使用装饰器模式添加日志格式化功能
7.10 扩展阅读
- PHP官方文档 - 类与对象
- PSR-4自动加载标准
- 设计模式:可复用面向对象软件的基础
- 重构:改善既有代码的设计
- Clean Code: A Handbook of Agile Software Craftsmanship
下一章预告:第八章将介绍错误处理与异常,包括错误类型、异常处理机制、自定义异常、调试技巧等内容。