PHP面向对象编程

面向对象编程(OOP)是一种编程范式,它使用"对象"来设计应用程序和计算机程序。PHP自PHP 5以来提供了完整的面向对象编程支持。本教程将详细介绍PHP中面向对象编程的核心概念、特性和最佳实践。

面向对象编程基础

面向对象编程基于以下几个核心概念:

  • 类(Class):一种用户定义的数据类型,包含属性(数据)和方法(函数)
  • 对象(Object):类的实例,是具体的实体
  • 属性(Property):类中定义的变量,用于存储对象的状态
  • 方法(Method):类中定义的函数,用于定义对象的行为
  • 继承(Inheritance):一个类可以继承另一个类的属性和方法
  • 多态(Polymorphism):不同类的对象可以响应相同的方法调用,但行为可能不同
  • 封装(Encapsulation):将数据和方法包装在类中,控制对它们的访问
  • 抽象(Abstraction):提供接口而隐藏实现细节

类和对象

定义类

在PHP中,使用class关键字来定义类:

<?php
echo "<h4>定义和使用类</h4>";

// 定义一个简单的Person类
class Person {
    // 定义属性(成员变量)
    public $name;
    public $age;
    public $gender;
    
    // 定义方法(成员函数)
    public function introduce() {
        return "你好,我叫{$this->name},今年{$this->age}岁,是{$this->gender}生。";
    }
    
    public function birthday() {
        $this->age++;
        return "生日快乐!{$this->name}现在{$this->age}岁了。";
    }
    
    // 构造函数(PHP 7.0+支持类型声明)
    public function __construct(string $name, int $age, string $gender) {
        $this->name = $name;
        $this->age = $age;
        $this->gender = $gender;
        echo "创建了一个Person对象:{$name}<br>";
    }
    
    // 析构函数
    public function __destruct() {
        echo "Person对象{$this->name}被销毁了<br>";
    }
}

// 创建对象(实例化类)
echo "<h5>创建对象</h5>";
$person1 = new Person("张三", 25, "男");
$person2 = new Person("李四", 30, "女");

// 访问对象的属性
echo "<h5>访问对象的属性</h5>";
echo "$person1->name 的年龄是 $person1->age 岁<br>";
echo "$person2->name 的年龄是 $person2->age 岁<br>";

// 调用对象的方法
echo "<h5>调用对象的方法</h5>";
echo $person1->introduce() . "<br>";
echo $person1->birthday() . "<br>";
echo $person2->introduce() . "<br>";

// 修改对象的属性
echo "<h5>修改对象的属性</h5>";
$person2->age = 31;
echo "修改后,$person2->name 的年龄是 $person2->age 岁<br>";
?>

属性访问控制

PHP提供了三种访问控制修饰符来控制类成员的可见性:

  • public:公有的,可以在任何地方访问
  • protected:受保护的,只能在类本身和其子类中访问
  • private:私有的,只能在类本身中访问
<?php
echo "<h4>属性访问控制</h4>";

class Student {
    // 公有属性
    public $name;
    
    // 受保护属性
    protected $studentId;
    
    // 私有属性
    private $grades = [];
    
    // 构造函数
    public function __construct(string $name, string $studentId) {
        $this->name = $name;
        $this->studentId = $studentId;
    }
    
    // 公有的获取成绩方法
    public function getGrades() {
        return $this->grades;
    }
    
    // 公有的设置成绩方法
    public function setGrade(string $subject, float $grade) {
        $this->grades[$subject] = $grade;
        return $this;
    }
    
    // 公有的计算平均成绩方法
    public function getAverageGrade() {
        if (empty($this->grades)) {
            return 0;
        }
        return array_sum($this->grades) / count($this->grades);
    }
    
    // 受保护的方法
    protected function getStudentInfo() {
        return "学生ID: {$this->studentId}, 姓名: {$this->name}";
    }
    
    // 公有的显示信息方法
    public function displayInfo() {
        // 在类内部可以访问受保护和私有成员
        $info = $this->getStudentInfo() . ", 平均成绩: " . $this->getAverageGrade();
        echo $info . "<br>";
        return $this;
    }
}

// 创建Student对象
$student = new Student("王五", "2023001");

// 访问公有属性
$student->name = "赵六";

// 设置成绩(通过公有方法)
$student->setGrade("数学", 85.5)
       ->setGrade("语文", 90.0)
       ->setGrade("英语", 78.5);

// 显示学生信息
$student->displayInfo();

// 尝试直接访问受保护或私有属性会导致错误
// $student->studentId = "2023002"; // 错误:无法访问受保护属性
// $student->grades["物理"] = 92; // 错误:无法访问私有属性

// 但可以通过公有方法获取信息
echo "赵六的所有成绩:<br>";
$grades = $student->getGrades();
foreach ($grades as $subject => $grade) {
    echo "$subject: $grade<br>";
}

// 计算并显示平均成绩
echo "平均成绩: " . $student->getAverageGrade() . "<br>";
?>

继承

继承是面向对象编程的一个重要特性,它允许一个类(子类)继承另一个类(父类)的属性和方法。在PHP中,使用extends关键字来实现继承。

<?php
echo "<h4>类的继承</h4>";

// 父类
class Animal {
    protected $name;
    protected $age;
    
    public function __construct(string $name, int $age) {
        $this->name = $name;
        $this->age = $age;
        echo "创建了一个Animal对象:{$name}<br>";
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getAge() {
        return $this->age;
    }
    
    public function setAge(int $age) {
        $this->age = $age;
        return $this;
    }
    
    // 虚方法,子类可以覆盖
    public function makeSound() {
        return "动物发出声音...";
    }
    
    public function eat() {
        return "{$this->name}在吃东西...";
    }
}

// 子类继承父类
class Dog extends Animal {
    private $breed;
    
    public function __construct(string $name, int $age, string $breed) {
        // 调用父类的构造函数
        parent::__construct($name, $age);
        $this->breed = $breed;
    }
    
    // 覆盖父类的方法
    public function makeSound() {
        return "汪汪汪!";
    }
    
    // 新增子类特有的方法
    public function fetch() {
        return "{$this->name}正在叼飞盘...";
    }
    
    // 获取狗的品种
    public function getBreed() {
        return $this->breed;
    }
}

// 另一个子类
class Cat extends Animal {
    private $color;
    
    public function __construct(string $name, int $age, string $color) {
        parent::__construct($name, $age);
        $this->color = $color;
    }
    
    // 覆盖父类的方法
    public function makeSound() {
        return "喵喵喵!";
    }
    
    // 新增子类特有的方法
    public function climb() {
        return "{$this->name}正在爬树...";
    }
    
    // 获取猫的颜色
    public function getColor() {
        return $this->color;
    }
}

// 创建子类对象
echo "<h5>创建子类对象</h5>";
$dog = new Dog("旺财", 3, "金毛");
$cat = new Cat("咪咪", 2, "橘色");

// 访问继承的属性和方法
echo "<h5>访问继承的属性和方法</h5>";
echo $dog->getName() . "是一只" . $dog->getBreed() . ",今年" . $dog->getAge() . "岁了。<br>";
echo $cat->getName() . "是一只" . $cat->getColor() . "的猫,今年" . $cat->getAge() . "岁了。<br>";

// 调用继承的方法
echo $dog->eat() . "<br>";
echo $cat->eat() . "<br>";

// 调用覆盖的方法(多态)
echo $dog->makeSound() . "<br>";
echo $cat->makeSound() . "<br>";

// 调用子类特有的方法
echo $dog->fetch() . "<br>";
echo $cat->climb() . "<br>";

// 修改继承的属性
echo "<h5>修改继承的属性</h5>";
$dog->setAge(4);
echo $dog->getName() . "现在" . $dog->getAge() . "岁了。<br>";

// 类型提示和多态示例
echo "<h5>类型提示和多态示例</h5>";

function animalSound(Animal $animal) {
    echo "{$animal->getName()} 说: {$animal->makeSound()}<br>";
}

// 传递不同类型的对象给同一个函数
animalSound($dog); // 输出狗的声音
animalSound($cat); // 输出猫的声音

// 创建一个包含不同动物的数组
echo "<h5>创建包含不同动物的数组</h5>";
$animals = [$dog, $cat];

foreach ($animals as $animal) {
    echo "{$animal->getName()}: {$animal->makeSound()}<br>";
}
?>

抽象类和接口

抽象类

抽象类是不能被实例化的类,它可以包含抽象方法(没有实现体的方法)和普通方法。抽象类使用abstract关键字定义。

<?php
echo "<h4>抽象类</h4>";

// 定义抽象类
abstract class Shape {
    // 抽象方法(没有方法体)
    abstract public function calculateArea();
    abstract public function calculatePerimeter();
    
    // 普通方法
    public function displayInfo() {
        echo "形状: " . get_class($this) . "<br>";
        echo "面积: " . $this->calculateArea() . "<br>";
        echo "周长: " . $this->calculatePerimeter() . "<br>";
    }
}

// 继承抽象类并实现抽象方法
class Rectangle extends Shape {
    private $width;
    private $height;
    
    public function __construct(float $width, float $height) {
        $this->width = $width;
        $this->height = $height;
    }
    
    // 实现抽象方法
    public function calculateArea() {
        return $this->width * $this->height;
    }
    
    // 实现抽象方法
    public function calculatePerimeter() {
        return 2 * ($this->width + $this->height);
    }
}

class Circle extends Shape {
    private $radius;
    
    public function __construct(float $radius) {
        $this->radius = $radius;
    }
    
    // 实现抽象方法
    public function calculateArea() {
        return pi() * pow($this->radius, 2);
    }
    
    // 实现抽象方法
    public function calculatePerimeter() {
        return 2 * pi() * $this->radius;
    }
}

// 不能实例化抽象类
// $shape = new Shape(); // 错误

// 创建具体子类的实例
echo "<h5>创建具体子类的实例</h5>";
$rectangle = new Rectangle(5, 10);
$circle = new Circle(7);

// 调用继承的方法和实现的方法
echo "<h5>矩形信息</h5>";
$rectangle->displayInfo();

echo "<h5>圆形信息</h5>";
$circle->displayInfo();

// 多态示例
echo "<h5>多态示例</h5>";
$shapes = [$rectangle, $circle];

foreach ($shapes as $shape) {
    $shape->displayInfo();
    echo "<br>";
}
?>

接口

接口定义了一组方法规范,但不提供实现。类可以实现一个或多个接口,使用implements关键字。

<?php
echo "<h4>接口</h4>";

// 定义一个接口
interface Logger {
    public function log(string $message);
    public function logError(string $error);
    public function logInfo(string $info);
}

// 定义另一个接口
interface FileOperations {
    public function saveToFile(string $filename, string $content);
    public function readFromFile(string $filename);
}

// 实现单个接口
class ConsoleLogger implements Logger {
    public function log(string $message) {
        echo "[LOG] $message<br>";
    }
    
    public function logError(string $error) {
        echo "[ERROR] $error<br>";
    }
    
    public function logInfo(string $info) {
        echo "[INFO] $info<br>";
    }
}

// 实现多个接口
class FileLogger implements Logger, FileOperations {
    private $logFile;
    
    public function __construct(string $logFile = 'application.log') {
        $this->logFile = $logFile;
    }
    
    public function log(string $message) {
        $logMessage = "[LOG] " . date('Y-m-d H:i:s') . " $message\n";
        $this->saveToFile($this->logFile, $logMessage);
        echo "日志已写入文件: $logMessage<br>";
    }
    
    public function logError(string $error) {
        $logMessage = "[ERROR] " . date('Y-m-d H:i:s') . " $error\n";
        $this->saveToFile($this->logFile, $logMessage);
        echo "错误日志已写入文件: $logMessage<br>";
    }
    
    public function logInfo(string $info) {
        $logMessage = "[INFO] " . date('Y-m-d H:i:s') . " $info\n";
        $this->saveToFile($this->logFile, $logMessage);
        echo "信息日志已写入文件: $logMessage<br>";
    }
    
    public function saveToFile(string $filename, string $content) {
        // 在实际应用中,这里会将内容写入文件
        // file_put_contents($filename, $content, FILE_APPEND);
        echo "模拟写入文件: $filename<br>";
    }
    
    public function readFromFile(string $filename) {
        // 在实际应用中,这里会读取文件内容
        // return file_get_contents($filename);
        echo "模拟读取文件: $filename<br>";
        return "文件内容";
    }
}

// 使用实现接口的类
echo "<h5>使用ConsoleLogger</h5>";
$consoleLogger = new ConsoleLogger();
$consoleLogger->log("这是一条普通日志");
$consoleLogger->logError("这是一条错误日志");
$consoleLogger->logInfo("这是一条信息日志");

echo "<h5>使用FileLogger</h5>";
$fileLogger = new FileLogger('app.log');
$fileLogger->log("这是写入文件的普通日志");
$fileLogger->logError("这是写入文件的错误日志");
$fileLogger->logInfo("这是写入文件的信息日志");

// 读取文件内容
$content = $fileLogger->readFromFile('app.log');
echo "读取的文件内容: $content<br>";

// 类型提示接口
echo "<h5>类型提示接口</h5>";

function processLogger(Logger $logger) {
    $logger->log("处理开始");
    $logger->logInfo("处理中...");
    $logger->logError("处理过程中发现错误");
    $logger->log("处理结束");
}

// 传递不同类型的Logger
processLogger($consoleLogger);
echo "<hr>";
processLogger($fileLogger);
?>

特性(Traits)

特性(Traits)是PHP 5.4引入的一个特性,它允许代码在多个类之间重用,解决了PHP不支持多重继承的问题。

<?php
echo "<h4>特性(Traits)</h4>";

// 定义一个特性
 trait Loggable {
    public function log($message) {
        echo "[LOG] $message<br>";
    }
    
    public function logError($error) {
        echo "[ERROR] $error<br>";
    }
}

// 定义另一个特性
 trait Timestampable {
    public function getCurrentTimestamp() {
        return date('Y-m-d H:i:s');
    }
    
    public function logWithTimestamp($message) {
        $timestamp = $this->getCurrentTimestamp();
        echo "[$timestamp] $message<br>";
    }
}

// 使用特性
class User {
    use Loggable, Timestampable;
    
    private $name;
    private $email;
    
    public function __construct($name, $email) {
        $this->name = $name;
        $this->email = $email;
        $this->log("创建了用户: $name");
    }
    
    public function getName() {
        return $this->name;
    }
    
    public function getEmail() {
        return $this->email;
    }
    
    public function updateProfile($name, $email) {
        $this->name = $name;
        $this->email = $email;
        $this->logWithTimestamp("更新了用户资料");
    }
}

// 使用包含特性的类
echo "<h5>使用包含特性的类</h5>";
$user = new User("张三", "zhangsan@example.com");

echo "用户名: " . $user->getName() . "<br>";
echo "邮箱: " . $user->getEmail() . "<br>";

// 调用来自特性的方法
$user->log("用户操作");
$user->logError("发生错误");
$user->logWithTimestamp("带时间戳的日志");

// 更新用户资料
$user->updateProfile("李四", "lisi@example.com");
echo "更新后的用户名: " . $user->getName() . "<br>";
echo "更新后的邮箱: " . $user->getEmail() . "<br>";

// 在另一个类中使用相同的特性
echo "<h5>在另一个类中使用相同的特性</h5>";

class Product {
    use Loggable, Timestampable;
    
    private $name;
    private $price;
    
    public function __construct($name, $price) {
        $this->name = $name;
        $this->price = $price;
        $this->logWithTimestamp("创建了产品: $name");
    }
    
    public function getPrice() {
        return $this->price;
    }
    
    public function setPrice($price) {
        $oldPrice = $this->price;
        $this->price = $price;
        $this->log("产品{$this->name}的价格从$oldPrice更新为$price");
    }
}

$product = new Product("笔记本电脑", 5999.00);
echo "产品价格: " . $product->getPrice() . "<br>";
$product->setPrice(5799.00);
echo "更新后的价格: " . $product->getPrice() . "<br>";
?>

魔术方法

PHP提供了一系列魔术方法,这些方法在特定情况下会自动调用,帮助我们实现更灵活的类。

<?php
echo "<h4>魔术方法</h4>";

class MagicClass {
    // 私有属性数组
    private $data = [];
    
    // __construct:构造函数,创建对象时调用
    public function __construct($initialData = []) {
        echo "构造函数被调用<br>";
        $this->data = $initialData;
    }
    
    // __destruct:析构函数,对象销毁时调用
    public function __destruct() {
        echo "析构函数被调用<br>";
    }
    
    // __get:访问不存在或不可访问的属性时调用
    public function __get($name) {
        echo "__get方法被调用,访问属性: $name<br>";
        return isset($this->data[$name]) ? $this->data[$name] : null;
    }
    
    // __set:设置不存在或不可访问的属性时调用
    public function __set($name, $value) {
        echo "__set方法被调用,设置属性: $name = $value<br>";
        $this->data[$name] = $value;
    }
    
    // __isset:对不存在或不可访问的属性使用isset()或empty()时调用
    public function __isset($name) {
        echo "__isset方法被调用,检查属性: $name<br>";
        return isset($this->data[$name]);
    }
    
    // __unset:对不存在或不可访问的属性使用unset()时调用
    public function __unset($name) {
        echo "__unset方法被调用,删除属性: $name<br>";
        if (isset($this->data[$name])) {
            unset($this->data[$name]);
        }
    }
    
    // __call:调用不存在或不可访问的方法时调用
    public function __call($name, $arguments) {
        echo "__call方法被调用,调用方法: $name,参数: " . implode(", ", $arguments) . "<br>";
        // 模拟动态方法调用
        if (strpos($name, 'get') === 0) {
            $property = strtolower(substr($name, 3));
            return $this->__get($property);
        }
        return null;
    }
    
    // __toString:将对象转换为字符串时调用
    public function __toString() {
        echo "__toString方法被调用<br>";
        return "MagicClass(" . json_encode($this->data) . ")";
    }
    
    // __invoke:将对象作为函数调用时调用
    public function __invoke($param1, $param2) {
        echo "__invoke方法被调用,参数: $param1, $param2<br>";
        return "调用结果: " . ($param1 + $param2);
    }
    
    // __clone:克隆对象时调用
    public function __clone() {
        echo "__clone方法被调用<br>";
        // 深拷贝处理
        $this->data = array_map(function($value) {
            return is_object($value) ? clone $value : $value;
        }, $this->data);
    }
}

// 测试魔术方法
echo "<h5>测试魔术方法</h5>";

// 测试构造函数
$magic = new MagicClass(['id' => 1, 'name' => '测试']);

// 测试__get和__set
$magic->age = 25;
// 输出: __set方法被调用,设置属性: age = 25

echo "获取age属性: " . $magic->age . "<br>";
// 输出: __get方法被调用,访问属性: age
// 输出: 获取age属性: 25

echo "获取name属性: " . $magic->name . "<br>";
// 输出: __get方法被调用,访问属性: name
// 输出: 获取name属性: 测试

// 测试__isset和__unset
var_dump(isset($magic->age));
// 输出: __isset方法被调用,检查属性: age
// 输出: bool(true)

unset($magic->age);
// 输出: __unset方法被调用,删除属性: age

var_dump(isset($magic->age));
// 输出: __isset方法被调用,检查属性: age
// 输出: bool(false)

// 测试__call
$result = $magic->getName();
echo "调用动态方法结果: $result<br>";
// 输出: __call方法被调用,调用方法: getName,参数: 
// 输出: __get方法被调用,访问属性: name
// 输出: 调用动态方法结果: 测试

// 测试__toString
echo "对象转换为字符串: " . $magic . "<br>";
// 输出: __toString方法被调用
// 输出: 对象转换为字符串: MagicClass({"id":1,"name":"测试"})

// 测试__invoke
$result = $magic(10, 20);
echo "将对象作为函数调用结果: $result<br>";
// 输出: __invoke方法被调用,参数: 10, 20
// 输出: 将对象作为函数调用结果: 调用结果: 30

// 测试__clone
$clonedMagic = clone $magic;
echo "克隆后的对象: " . $clonedMagic . "<br>";
// 输出: __clone方法被调用
// 输出: __toString方法被调用
// 输出: 克隆后的对象: MagicClass({"id":1,"name":"测试"})

// 修改克隆对象,不会影响原对象
$clonedMagic->name = "克隆对象";
echo "原对象: " . $magic . "<br>";
echo "克隆对象: " . $clonedMagic . "<br>";
?>

面向对象编程最佳实践

  1. 单一职责原则:一个类应该只有一个职责,专注于做一件事情并做好它。
  2. 开放封闭原则:类应该对扩展开放,对修改封闭。
  3. 里氏替换原则:子类应该能够替换其基类而不改变程序的行为。
  4. 接口隔离原则:客户端不应该被迫实现它不需要的接口。
  5. 依赖倒置原则:高层模块不应该依赖于低层模块,两者都应该依赖于抽象。
  6. 使用命名空间:使用命名空间组织代码,避免命名冲突。
  7. 使用自动加载:使用自动加载机制(如Composer的PSR-4自动加载),避免手动引入文件。
  8. 使用类型提示:使用类型提示提高代码的可读性和健壮性。
  9. 合理使用访问控制:默认使用私有或受保护访问级别,只在必要时才公开。
  10. 使用魔术方法时保持谨慎:过度使用魔术方法会使代码难以理解和调试。
  11. 优先使用组合而非继承:组合比继承更灵活,可以降低类之间的耦合度。
  12. 编写文档注释:为类、方法和属性添加文档注释,提高代码的可维护性。
  13. 避免全局状态:全局状态会使代码难以测试和维护,尽量使用依赖注入。
  14. 使用设计模式:熟悉并适当使用常见的设计模式解决问题。
  15. 编写单元测试:为类和方法编写单元测试,确保代码质量和正确性。

PHP面向对象编程综合示例

<?php
// 1. 创建一个基本的类结构
echo "<h4>1. 创建一个基本的类结构</h4>";

// 定义一个基础的产品类
class Product {
    // 属性
    private $id;
    private $name;
    private $price;
    private $description;
    private $category;
    
    // 构造函数
    public function __construct(int $id, string $name, float $price, string $description = '', string $category = 'General') {
        $this->id = $id;
        $this->name = $name;
        $this->price = $price;
        $this->description = $description;
        $this->category = $category;
        echo "创建产品: {$this->name}<br>";
    }
    
    // getter和setter方法
    public function getId(): int {
        return $this->id;
    }
    
    public function getName(): string {
        return $this->name;
    }
    
    public function setName(string $name): self {
        $this->name = $name;
        return $this;
    }
    
    public function getPrice(): float {
        return $this->price;
    }
    
    public function setPrice(float $price): self {
        if ($price <= 0) {
            throw new InvalidArgumentException("价格必须大于0");
        }
        $this->price = $price;
        return $this;
    }
    
    public function getDescription(): string {
        return $this->description;
    }
    
    public function setDescription(string $description): self {
        $this->description = $description;
        return $this;
    }
    
    public function getCategory(): string {
        return $this->category;
    }
    
    public function setCategory(string $category): self {
        $this->category = $category;
        return $this;
    }
    
    // 计算打折后的价格
    public function getDiscountedPrice(float $discountPercentage): float {
        if ($discountPercentage < 0 || $discountPercentage > 100) {
            throw new InvalidArgumentException("折扣百分比必须在0到100之间");
        }
        $discountAmount = $this->price * ($discountPercentage / 100);
        return $this->price - $discountAmount;
    }
    
    // 产品信息展示
    public function displayInfo(): void {
        echo "<div style='border:1px solid #ccc; padding:10px; margin:10px 0; border-radius:5px;'>";
        echo "产品ID: {$this->id}<br>";
        echo "产品名称: {$this->name}<br>";
        echo "产品价格: ¥" . number_format($this->price, 2) . "<br>";
        echo "产品描述: {$this->description}<br>";
        echo "产品分类: {$this->category}<br>";
        echo "</div>";
    }
}

// 2. 创建子类(继承)
echo "<h4>2. 创建子类(继承)</h4>";

// 继承Product类
class ElectronicProduct extends Product {
    private $brand;
    private $warrantyPeriod;
    private $powerSource;
    
    public function __construct(int $id, string $name, float $price, string $brand, int $warrantyPeriod = 12) {
        // 调用父类构造函数
        parent::__construct($id, $name, $price, '', 'Electronics');
        $this->brand = $brand;
        $this->warrantyPeriod = $warrantyPeriod;
    }
    
    public function getBrand(): string {
        return $this->brand;
    }
    
    public function getWarrantyPeriod(): int {
        return $this->warrantyPeriod;
    }
    
    public function setPowerSource(string $powerSource): self {
        $this->powerSource = $powerSource;
        return $this;
    }
    
    public function getPowerSource(): ?string {
        return $this->powerSource;
    }
    
    // 覆盖父类方法
    public function displayInfo(): void {
        parent::displayInfo();
        echo "品牌: {$this->brand}<br>";
        echo "保修期限: {$this->warrantyPeriod} 个月<br>";
        if ($this->powerSource) {
            echo "电源: {$this->powerSource}<br>";
        }
        echo "</div>";
    }
}

// 3. 创建接口
echo "<h4>3. 创建接口</h4>";

interface CartItem {
    public function getItemId(): int;
    public function getItemName(): string;
    public function getItemPrice(): float;
    public function getItemQuantity(): int;
    public function setItemQuantity(int $quantity): self;
    public function getItemTotal(): float;
}

// 4. 创建实现接口的类
echo "<h4>4. 创建实现接口的类</h4>";

class ShoppingCartItem implements CartItem {
    private $product;
    private $quantity;
    
    public function __construct(Product $product, int $quantity = 1) {
        $this->product = $product;
        $this->quantity = max(1, $quantity); // 确保数量至少为1
    }
    
    public function getItemId(): int {
        return $this->product->getId();
    }
    
    public function getItemName(): string {
        return $this->product->getName();
    }
    
    public function getItemPrice(): float {
        return $this->product->getPrice();
    }
    
    public function getItemQuantity(): int {
        return $this->quantity;
    }
    
    public function setItemQuantity(int $quantity): self {
        $this->quantity = max(1, $quantity);
        return $this;
    }
    
    public function getItemTotal(): float {
        return $this->getItemPrice() * $this->getItemQuantity();
    }
    
    public function getProduct(): Product {
        return $this->product;
    }
    
    public function display(): void {
        echo "商品: {$this->getItemName()}<br>";
        echo "单价: ¥" . number_format($this->getItemPrice(), 2) . "<br>";
        echo "数量: {$this->getItemQuantity()}<br>";
        echo "小计: ¥" . number_format($this->getItemTotal(), 2) . "<br>";
    }
}

// 5. 创建购物车类
echo "<h4>5. 创建购物车类</h4>";

class ShoppingCart {
    private $items = [];
    
    // 添加商品到购物车
    public function addItem(Product $product, int $quantity = 1): self {
        $productId = $product->getId();
        
        if (isset($this->items[$productId])) {
            // 如果商品已存在,增加数量
            $this->items[$productId]->setItemQuantity(
                $this->items[$productId]->getItemQuantity() + $quantity
            );
            echo "已将{$product->getName()}的数量增加到{$this->items[$productId]->getItemQuantity()}<br>";
        } else {
            // 否则添加新商品
            $this->items[$productId] = new ShoppingCartItem($product, $quantity);
            echo "已将{$product->getName()}添加到购物车<br>";
        }
        
        return $this;
    }
    
    // 从购物车移除商品
    public function removeItem(int $productId): self {
        if (isset($this->items[$productId])) {
            $productName = $this->items[$productId]->getItemName();
            unset($this->items[$productId]);
            echo "已从购物车移除{$productName}<br>";
        }
        return $this;
    }
    
    // 更新购物车中商品的数量
    public function updateItemQuantity(int $productId, int $quantity): self {
        if (isset($this->items[$productId])) {
            if ($quantity <= 0) {
                $this->removeItem($productId);
            } else {
                $this->items[$productId]->setItemQuantity($quantity);
                echo "已更新{$this->items[$productId]->getItemName()}的数量为{$quantity}<br>";
            }
        }
        return $this;
    }
    
    // 获取购物车中的所有商品
    public function getItems(): array {
        return $this->items;
    }
    
    // 获取购物车中的商品总数
    public function getItemCount(): int {
        $count = 0;
        foreach ($this->items as $item) {
            $count += $item->getItemQuantity();
        }
        return $count;
    }
    
    // 获取购物车的总金额
    public function getTotal(): float {
        $total = 0;
        foreach ($this->items as $item) {
            $total += $item->getItemTotal();
        }
        return $total;
    }
    
    // 清空购物车
    public function clear(): self {
        $this->items = [];
        echo "购物车已清空<br>";
        return $this;
    }
    
    // 显示购物车内容
    public function display(): void {
        echo "<div style='border:1px solid #ddd; padding:15px; margin:15px 0; border-radius:5px;'>";
        echo "<h5>购物车内容</h5>";
        
        if (empty($this->items)) {
            echo "购物车是空的<br>";
        } else {
            foreach ($this->items as $item) {
                echo "<div style='border-bottom:1px solid #eee; padding:10px 0;'>";
                $item->display();
                echo "</div>";
            }
            
            echo "<div style='margin-top:15px; font-weight:bold;'>";
            echo "商品总数: {$this->getItemCount()}<br>";
            echo "购物车总额: ¥" . number_format($this->getTotal(), 2) . "<br>";
            echo "</div>";
        }
        
        echo "</div>";
    }
}

// 6. 使用特性
echo "<h4>6. 使用特性</h4>";

trait Discountable {
    // 应用折扣
    public function applyDiscount(float $discountPercentage): self {
        if ($discountPercentage < 0 || $discountPercentage > 100) {
            throw new InvalidArgumentException("折扣百分比必须在0到100之间");
        }
        
        $discountAmount = $this->getTotal() * ($discountPercentage / 100);
        $discountedTotal = $this->getTotal() - $discountAmount;
        
        echo "应用{$discountPercentage}%折扣,减免¥" . number_format($discountAmount, 2) . ",折后总价: ¥" . number_format($discountedTotal, 2) . "<br>";
        
        // 注意:在实际应用中,这里会修改总价
        // 为了演示,我们只输出信息
        
        return $this;
    }
    
    // 获取折扣信息
    public function getDiscountInfo(float $discountPercentage): array {
        $discountAmount = $this->getTotal() * ($discountPercentage / 100);
        $discountedTotal = $this->getTotal() - $discountAmount;
        
        return [
            'percentage' => $discountPercentage,
            'amount' => $discountAmount,
            'total_after_discount' => $discountedTotal
        ];
    }
}

// 扩展购物车类以包含折扣功能
echo "<h4>7. 扩展购物车类以包含折扣功能</h4>";

class DiscountShoppingCart extends ShoppingCart {
    use Discountable;
    
    private $appliedDiscount = 0;
    
    // 重写applyDiscount方法以实际应用折扣
    public function applyDiscount(float $discountPercentage): self {
        if ($discountPercentage < 0 || $discountPercentage > 100) {
            throw new InvalidArgumentException("折扣百分比必须在0到100之间");
        }
        
        $this->appliedDiscount = $discountPercentage;
        $discountInfo = $this->getDiscountInfo($discountPercentage);
        
        echo "已应用{$discountInfo['percentage']}%折扣,减免¥" . number_format($discountInfo['amount'], 2) . ",折后总价: ¥" . number_format($discountInfo['total_after_discount'], 2) . "<br>";
        
        return $this;
    }
    
    // 重写getTotal方法以考虑折扣
    public function getTotal(): float {
        $originalTotal = parent::getTotal();
        
        if ($this->appliedDiscount > 0) {
            $discountAmount = $originalTotal * ($this->appliedDiscount / 100);
            return $originalTotal - $discountAmount;
        }
        
        return $originalTotal;
    }
    
    // 清除折扣
    public function clearDiscount(): self {
        $this->appliedDiscount = 0;
        echo "已清除所有折扣<br>";
        return $this;
    }
}

// 8. 创建订单类
echo "<h4>8. 创建订单类</h4>";

class Order {
    private $orderId;
    private $cart;
    private $customerName;
    private $customerEmail;
    private $orderDate;
    private $status = 'pending';
    
    public function __construct(DiscountShoppingCart $cart, string $customerName, string $customerEmail) {
        $this->orderId = uniqid('order_');
        $this->cart = $cart;
        $this->customerName = $customerName;
        $this->customerEmail = $customerEmail;
        $this->orderDate = date('Y-m-d H:i:s');
        
        echo "创建订单: {$this->orderId}<br>";
    }
    
    public function getOrderId(): string {
        return $this->orderId;
    }
    
    public function getCart(): DiscountShoppingCart {
        return $this->cart;
    }
    
    public function getCustomerName(): string {
        return $this->customerName;
    }
    
    public function getCustomerEmail(): string {
        return $this->customerEmail;
    }
    
    public function getOrderDate(): string {
        return $this->orderDate;
    }
    
    public function getStatus(): string {
        return $this->status;
    }
    
    public function setStatus(string $status): self {
        $this->status = $status;
        return $this;
    }
    
    public function getTotal(): float {
        return $this->cart->getTotal();
    }
    
    // 处理订单
    public function process(): bool {
        echo "处理订单 {$this->orderId}...<br>";
        // 在实际应用中,这里会进行支付处理、库存检查等操作
        $this->setStatus('processed');
        echo "订单 {$this->orderId} 已处理<br>";
        return true;
    }
    
    // 显示订单详情
    public function display(): void {
        echo "<div style='border:1px solid #ccc; padding:15px; margin:15px 0; border-radius:5px;'>";
        echo "<h5>订单详情</h5>";
        echo "订单ID: {$this->orderId}<br>";
        echo "客户名称: {$this->customerName}<br>";
        echo "客户邮箱: {$this->customerEmail}<br>";
        echo "订单日期: {$this->orderDate}<br>";
        echo "订单状态: {$this->status}<br>";
        
        // 显示购物车内容
        $this->cart->display();
        
        echo "订单总额: ¥" . number_format($this->getTotal(), 2) . "<br>";
        echo "</div>";
    }
}

// 9. 实际使用示例
echo "<h4>9. 实际使用示例</h4>";

// 创建一些产品
echo "<h5>创建产品</h5>";
$laptop = new ElectronicProduct(1, "笔记本电脑", 5999.99, "品牌A", 24);
$laptop->setDescription("高性能笔记本电脑,适合游戏和办公")
       ->setPowerSource("电池/电源适配器");

$phone = new ElectronicProduct(2, "智能手机", 3999.99, "品牌B", 12);
$phone->setDescription("最新款智能手机,配备高清摄像头和大容量电池")
      ->setPowerSource("电池");

$book = new Product(3, "PHP编程指南", 89.99, "全面介绍PHP编程技术", "Books");

// 显示产品信息
echo "<h5>产品信息</h5>";
$laptop->displayInfo();
$phone->displayInfo();
$book->displayInfo();

// 创建购物车并添加产品
echo "<h5>创建购物车并添加产品</h5>";
$cart = new DiscountShoppingCart();
$cart->addItem($laptop, 1)
     ->addItem($phone, 2)
     ->addItem($book, 3);

// 显示购物车
echo "<h5>显示购物车</h5>";
$cart->display();

// 更新购物车中的商品数量
echo "<h5>更新购物车中的商品数量</h5>";
$cart->updateItemQuantity(2, 1); // 将手机数量从2更新为1

// 应用折扣
echo "<h5>应用折扣</h5>";
$cart->applyDiscount(10); // 应用10%的折扣

// 再次显示购物车
echo "<h5>再次显示购物车(含折扣)</h5>";
$cart->display();

// 创建订单
echo "<h5>创建订单</h5>";
$order = new Order($cart, "张三", "zhangsan@example.com");

// 显示订单
$order->display();

// 处理订单
echo "<h5>处理订单</h5>";
$order->process();

// 再次显示订单(已处理状态)
echo "<h5>再次显示订单(已处理状态)</h5>";
$order->display();
?>