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