PHP 面向对象编程高级教程
- 2025-06-18 浙江
本文字数:56325 字
阅读完需:约 185 分钟
前言
本教程是 PHP 面向对象编程入门教程的进阶版,适合已经掌握基本面向对象概念的开发者。我们将深入探讨 PHP 中更高级的面向对象编程特性,并通过实际案例展示如何在项目中应用这些概念。
目录
高级 OOP 概念
抽象类和抽象方法
接口和多重实现
静态属性和方法
后期静态绑定
魔术方法
特性(Traits)
基本使用
解决多继承问题
特性中的冲突解决
命名空间和自动加载
命名空间基础
PSR-4 标准
Composer 自动加载
设计模式
单例模式
工厂模式
观察者模式
依赖注入
反射 API 和元编程
反射类和方法
动态创建对象
注解的使用
实际项目案例
简单 MVC 框架实现
API 开发中的 OOP 应用
高级 OOP 概念
抽象类和抽象方法
抽象类是一种不能被实例化的类,它的主要目的是为子类提供一个通用的模板。抽象类可以包含抽象方法(没有实现的方法)和普通方法。
<?php
/**
* 抽象支付处理器类
* 定义了所有支付处理器必须实现的方法
*/
abstract class PaymentProcessor {
/**
* 支付处理器名称
* @var string
*/
protected $name;
/**
* 支付处理器描述
* @var string
*/
protected $description;
/**
* 构造函数
*
* @param string $name 处理器名称
* @param string $description 处理器描述
*/
public function __construct(string $name, string $description) {
$this->name = $name;
$this->description = $description;
}
/**
* 获取处理器信息
*
* @return array 处理器信息
*/
public function getInfo(): array {
return [
'name' => $this->name,
'description' => $this->description
];
}
/**
* 处理支付
* 抽象方法,必须由子类实现
*
* @param float $amount 支付金额
* @param array $paymentDetails 支付详情
* @return bool 支付是否成功
*/
abstract public function processPayment(float $amount, array $paymentDetails): bool;
/**
* 退款处理
* 抽象方法,必须由子类实现
*
* @param string $transactionId 交易ID
* @param float $amount 退款金额
* @return bool 退款是否成功
*/
abstract public function processRefund(string $transactionId, float $amount): bool;
/**
* 验证支付详情
* 抽象方法,必须由子类实现
*
* @param array $paymentDetails 支付详情
* @return bool 验证是否通过
*/
abstract protected function validatePaymentDetails(array $paymentDetails): bool;
}
/**
* 信用卡支付处理器
* 继承自抽象支付处理器类
*/
class CreditCardProcessor extends PaymentProcessor {
/**
* 支持的信用卡类型
* @var array
*/
private $supportedCardTypes = ['visa', 'mastercard', 'amex'];
/**
* 构造函数
*/
public function __construct() {
parent::__construct('信用卡支付', '通过信用卡处理支付');
}
/**
* 实现处理支付方法
*
* @param float $amount 支付金额
* @param array $paymentDetails 支付详情
* @return bool 支付是否成功
*/
public function processPayment(float $amount, array $paymentDetails): bool {
// 验证支付详情
if (!$this->validatePaymentDetails($paymentDetails)) {
echo "支付详情验证失败\n";
return false;
}
// 模拟支付处理
echo "使用信用卡处理 {$amount} 元的支付...\n";
// 获取卡信息(实际应用中会有更多安全措施)
$cardNumber = $paymentDetails['card_number'];
$cardType = $this->getCardType($cardNumber);
$lastFour = substr($cardNumber, -4);
echo "使用 {$cardType} 卡 (尾号 {$lastFour}) 支付成功!\n";
return true;
}
/**
* 实现退款处理方法
*
* @param string $transactionId 交易ID
* @param float $amount 退款金额
* @return bool 退款是否成功
*/
public function processRefund(string $transactionId, float $amount): bool {
echo "处理交易 {$transactionId} 的退款,金额:{$amount} 元...\n";
echo "退款成功!\n";
return true;
}
/**
* 实现验证支付详情方法
*
* @param array $paymentDetails 支付详情
* @return bool 验证是否通过
*/
protected function validatePaymentDetails(array $paymentDetails): bool {
// 检查必要字段
$requiredFields = ['card_number', 'expiry_date', 'cvv', 'cardholder_name'];
foreach ($requiredFields as $field) {
if (!isset($paymentDetails[$field]) || empty($paymentDetails[$field])) {
echo "缺少必要字段: {$field}\n";
return false;
}
}
// 验证卡号(简化版)
$cardNumber = $paymentDetails['card_number'];
if (!is_numeric($cardNumber) || strlen($cardNumber) < 13 || strlen($cardNumber) > 19) {
echo "无效的卡号\n";
return false;
}
// 验证卡类型
$cardType = $this->getCardType($cardNumber);
if (!in_array($cardType, $this->supportedCardTypes)) {
echo "不支持的卡类型: {$cardType}\n";
return false;
}
// 验证过期日期(简化版)
$expiryDate = $paymentDetails['expiry_date'];
if (!preg_match('/^(0[1-9]|1[0-2])\/([0-9]{2})$/', $expiryDate)) {
echo "无效的过期日期格式,应为MM/YY\n";
return false;
}
// 验证CVV(简化版)
$cvv = $paymentDetails['cvv'];
if (!is_numeric($cvv) || strlen($cvv) < 3 || strlen($cvv) > 4) {
echo "无效的CVV\n";
return false;
}
return true;
}
/**
* 根据卡号获取卡类型
*
* @param string $cardNumber 卡号
* @return string 卡类型
*/
private function getCardType(string $cardNumber): string {
// 简化的卡类型检测
$firstDigit = substr($cardNumber, 0, 1);
$firstTwoDigits = substr($cardNumber, 0, 2);
if ($firstDigit === '4') {
return 'visa';
} elseif ($firstTwoDigits >= '51' && $firstTwoDigits <= '55') {
return 'mastercard';
} elseif ($firstTwoDigits === '34' || $firstTwoDigits === '37') {
return 'amex';
} else {
return 'unknown';
}
}
}
/**
* PayPal支付处理器
* 继承自抽象支付处理器类
*/
class PayPalProcessor extends PaymentProcessor {
/**
* API凭证
* @var array
*/
private $apiCredentials;
/**
* 构造函数
*
* @param string $clientId PayPal客户端ID
* @param string $secret PayPal密钥
*/
public function __construct(string $clientId, string $secret) {
parent::__construct('PayPal', '通过PayPal处理支付');
$this->apiCredentials = [
'client_id' => $clientId,
'secret' => $secret
];
}
/**
* 实现处理支付方法
*
* @param float $amount 支付金额
* @param array $paymentDetails 支付详情
* @return bool 支付是否成功
*/
public function processPayment(float $amount, array $paymentDetails): bool {
// 验证支付详情
if (!$this->validatePaymentDetails($paymentDetails)) {
echo "支付详情验证失败\n";
return false;
}
// 模拟PayPal API调用
echo "连接到PayPal API...\n";
echo "使用PayPal处理 {$amount} 元的支付...\n";
echo "支付成功!交易ID: " . $this->generateTransactionId() . "\n";
return true;
}
/**
* 实现退款处理方法
*
* @param string $transactionId 交易ID
* @param float $amount 退款金额
* @return bool 退款是否成功
*/
public function processRefund(string $transactionId, float $amount): bool {
echo "连接到PayPal API...\n";
echo "处理交易 {$transactionId} 的退款,金额:{$amount} 元...\n";
echo "退款成功!\n";
return true;
}
/**
* 实现验证支付详情方法
*
* @param array $paymentDetails 支付详情
* @return bool 验证是否通过
*/
protected function validatePaymentDetails(array $paymentDetails): bool {
// 检查必要字段
if (!isset($paymentDetails['email']) || !filter_var($paymentDetails['email'], FILTER_VALIDATE_EMAIL)) {
echo "无效的PayPal邮箱地址\n";
return false;
}
return true;
}
/**
* 生成交易ID
*
* @return string 交易ID
*/
private function generateTransactionId(): string {
return 'PP-' . strtoupper(uniqid());
}
}
// 使用示例
// 注意:抽象类不能直接实例化
// $processor = new PaymentProcessor(); // 这会产生错误
// 创建信用卡处理器
$creditCardProcessor = new CreditCardProcessor();
$paymentDetails = [
'card_number' => '4111111111111111',
'expiry_date' => '12/25',
'cvv' => '123',
'cardholder_name' => '张三'
];
$creditCardProcessor->processPayment(100.50, $paymentDetails);
echo "\n";
// 创建PayPal处理器
$paypalProcessor = new PayPalProcessor('client_id_here', 'secret_here');
$paypalDetails = [
'email' => 'customer@example.com'
];
$paypalProcessor->processPayment(100.50, $paypalDetails);
// 处理退款
$transactionId = 'TX123456';
$paypalProcessor->processRefund($transactionId, 50.25);
接口和多重实现
接口定义了一个类必须实现的方法,但不包含方法的具体实现。一个类可以实现多个接口,从而实现多重继承的效果。
<?php
/**
* 可记录接口
* 定义可记录对象必须实现的方法
*/
interface Loggable {
/**
* 获取日志数据
*
* @return array 日志数据
*/
public function getLogData(): array;
/**
* 记录日志
*
* @param string $message 日志消息
* @return bool 是否成功记录
*/
public function log(string $message): bool;
}
/**
* 可序列化接口
* 定义可序列化对象必须实现的方法
*/
interface Serializable {
/**
* 序列化对象
*
* @return string 序列化后的字符串
*/
public function serialize(): string;
/**
* 反序列化对象
*
* @param string $data 序列化的数据
* @return void
*/
public function unserialize(string $data): void;
}
/**
* 用户类
* 实现了Loggable和Serializable接口
*/
class User implements Loggable, Serializable {
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 电子邮件
* @var string
*/
private $email;
/**
* 创建时间
* @var string
*/
private $createdAt;
/**
* 构造函数
*
* @param int $id 用户ID
* @param string $username 用户名
* @param string $email 电子邮件
*/
public function __construct(int $id, string $username, string $email) {
$this->id = $id;
$this->username = $username;
$this->email = $email;
$this->createdAt = date('Y-m-d H:i:s');
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt
];
}
/**
* 实现Loggable接口的getLogData方法
*
* @return array 日志数据
*/
public function getLogData(): array {
return [
'entity' => 'user',
'id' => $this->id,
'username' => $this->username,
'time' => date('Y-m-d H:i:s')
];
}
/**
* 实现Loggable接口的log方法
*
* @param string $message 日志消息
* @return bool 是否成功记录
*/
public function log(string $message): bool {
$logData = $this->getLogData();
$logEntry = date('Y-m-d H:i:s') . " [{$logData['entity']}:{$logData['id']}] {$message}\n";
// 在实际应用中,这里会将日志写入文件或数据库
echo "记录日志: {$logEntry}";
return true;
}
/**
* 实现Serializable接口的serialize方法
*
* @return string 序列化后的字符串
*/
public function serialize(): string {
return json_encode([
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt
]);
}
/**
* 实现Serializable接口的unserialize方法
*
* @param string $data 序列化的数据
* @return void
*/
public function unserialize(string $data): void {
$userData = json_decode($data, true);
$this->id = $userData['id'] ?? 0;
$this->username = $userData['username'] ?? '';
$this->email = $userData['email'] ?? '';
$this->createdAt = $userData['created_at'] ?? date('Y-m-d H:i:s');
}
}
/**
* 订单类
* 实现了Loggable接口
*/
class Order implements Loggable {
/**
* 订单ID
* @var string
*/
private $id;
/**
* 用户ID
* @var int
*/
private $userId;
/**
* 订单金额
* @var float
*/
private $amount;
/**
* 订单状态
* @var string
*/
private $status;
/**
* 创建时间
* @var string
*/
private $createdAt;
/**
* 构造函数
*
* @param int $userId 用户ID
* @param float $amount 订单金额
*/
public function __construct(int $userId, float $amount) {
$this->id = $this->generateOrderId();
$this->userId = $userId;
$this->amount = $amount;
$this->status = 'pending';
$this->createdAt = date('Y-m-d H:i:s');
}
/**
* 生成订单ID
*
* @return string 订单ID
*/
private function generateOrderId(): string {
return 'ORD-' . strtoupper(uniqid());
}
/**
* 更新订单状态
*
* @param string $status 新状态
* @return bool 是否成功更新
*/
public function updateStatus(string $status): bool {
$validStatuses = ['pending', 'paid', 'shipped', 'completed', 'cancelled'];
if (!in_array($status, $validStatuses)) {
echo "无效的订单状态: {$status}\n";
return false;
}
$oldStatus = $this->status;
$this->status = $status;
$this->log("订单状态从 {$oldStatus} 更新为 {$status}");
return true;
}
/**
* 获取订单信息
*
* @return array 订单信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'user_id' => $this->userId,
'amount' => $this->amount,
'status' => $this->status,
'created_at' => $this->createdAt
];
}
/**
* 实现Loggable接口的getLogData方法
*
* @return array 日志数据
*/
public function getLogData(): array {
return [
'entity' => 'order',
'id' => $this->id,
'user_id' => $this->userId,
'amount' => $this->amount,
'time' => date('Y-m-d H:i:s')
];
}
/**
* 实现Loggable接口的log方法
*
* @param string $message 日志消息
* @return bool 是否成功记录
*/
public function log(string $message): bool {
$logData = $this->getLogData();
$logEntry = date('Y-m-d H:i:s') . " [{$logData['entity']}:{$logData['id']}] {$message}\n";
// 在实际应用中,这里会将日志写入文件或数据库
echo "记录日志: {$logEntry}";
return true;
}
}
/**
* 日志管理器类
* 处理所有可记录对象的日志
*/
class LogManager {
/**
* 记录对象的活动
*
* @param Loggable $entity 可记录的对象
* @param string $action 执行的操作
* @return bool 是否成功记录
*/
public function logActivity(Loggable $entity, string $action): bool {
return $entity->log("执行操作: {$action}");
}
/**
* 批量记录多个对象的活动
*
* @param array $entities 可记录的对象数组
* @param string $action 执行的操作
* @return array 记录结果
*/
public function logBulkActivity(array $entities, string $action): array {
$results = [];
foreach ($entities as $entity) {
if ($entity instanceof Loggable) {
$results[] = $this->logActivity($entity, $action);
} else {
echo "警告: 对象不是Loggable类型,无法记录日志\n";
$results[] = false;
}
}
return $results;
}
}
// 使用示例
// 创建用户
$user = new User(1, 'johndoe', 'john@example.com');
// 创建订单
$order = new Order(1, 299.99);
// 使用日志管理器
$logManager = new LogManager();
$logManager->logActivity($user, '登录');
$logManager->logActivity($order, '创建');
// 更新订单状态
$order->updateStatus('paid');
// 序列化用户对象
$serializedUser = $user->serialize();
echo "序列化的用户数据: {$serializedUser}\n";
// 创建新用户并从序列化数据恢复
$newUser = new User(0, '', '');
$newUser->unserialize($serializedUser);
print_r($newUser->getInfo());
// 批量记录日志
$entities = [$user, $order];
$logManager->logBulkActivity($entities, '数据备份');
静态属性和方法
静态属性和方法属于类本身,而不是类的实例。它们可以在不创建类实例的情况下访问。
<?php
/**
* 配置管理器类
* 使用静态属性和方法管理应用程序配置
*/
class ConfigManager {
/**
* 配置数据
* @var array
*/
private static $config = [];
/**
* 是否已初始化
* @var bool
*/
private static $initialized = false;
/**
* 初始化配置
*
* @param string $configFile 配置文件路径
* @return bool 是否成功初始化
*/
public static function initialize(string $configFile): bool {
if (self::$initialized) {
return true;
}
if (!file_exists($configFile)) {
throw new Exception("配置文件不存在: {$configFile}");
}
try {
self::$config = json_decode(file_get_contents($configFile), true);
self::$initialized = true;
return true;
} catch (Exception $e) {
echo "配置初始化失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 获取配置值
*
* @param string $key 配置键名
* @param mixed $default 默认值
* @return mixed 配置值
*/
public static function get(string $key, $default = null) {
if (!self::$initialized) {
throw new Exception("配置管理器未初始化");
}
return self::$config[$key] ?? $default;
}
/**
* 设置配置值
*
* @param string $key 配置键名
* @param mixed $value 配置值
* @return void
*/
public static function set(string $key, $value): void {
if (!self::$initialized) {
throw new Exception("配置管理器未初始化");
}
self::$config[$key] = $value;
}
/**
* 检查配置是否存在
*
* @param string $key 配置键名
* @return bool 是否存在
*/
public static function has(string $key): bool {
return isset(self::$config[$key]);
}
/**
* 获取所有配置
*
* @return array 配置数组
*/
public static function all(): array {
return self::$config;
}
}
/**
* 计数器类
* 演示静态属性的使用
*/
class Counter {
/**
* 计数器值
* @var int
*/
private static $count = 0;
/**
* 计数器历史记录
* @var array
*/
private static $history = [];
/**
* 增加计数
*
* @param int $value 增加值
* @return int 新的计数值
*/
public static function increment(int $value = 1): int {
self::$count += $value;
self::$history[] = [
'action' => 'increment',
'value' => $value,
'timestamp' => date('Y-m-d H:i:s')
];
return self::$count;
}
/**
* 减少计数
*
* @param int $value 减少值
* @return int 新的计数值
*/
public static function decrement(int $value = 1): int {
self::$count -= $value;
self::$history[] = [
'action' => 'decrement',
'value' => $value,
'timestamp' => date('Y-m-d H:i:s')
];
return self::$count;
}
/**
* 获取当前计数
*
* @return int 当前计数值
*/
public static function getCount(): int {
return self::$count;
}
/**
* 重置计数
*
* @return void
*/
public static function reset(): void {
self::$count = 0;
self::$history[] = [
'action' => 'reset',
'value' => 0,
'timestamp' => date('Y-m-d H:i:s')
];
}
/**
* 获取历史记录
*
* @return array 历史记录数组
*/
public static function getHistory(): array {
return self::$history;
}
}
/**
* 数据库连接类
* 使用静态属性实现单例模式
*/
class Database {
/**
* 数据库实例
* @var Database|null
*/
private static $instance = null;
/**
* 数据库连接
* @var PDO|null
*/
private $connection = null;
/**
* 查询计数
* @var int
*/
private static $queryCount = 0;
/**
* 私有构造函数,防止外部实例化
*/
private function __construct() {
try {
$config = ConfigManager::get('database');
$dsn = "mysql:host={$config['host']};dbname={$config['database']}";
$this->connection = new PDO(
$dsn,
$config['username'],
$config['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
} catch (PDOException $e) {
throw new Exception("数据库连接失败: " . $e->getMessage());
}
}
/**
* 获取数据库实例
*
* @return Database 数据库实例
*/
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 执行SQL查询
*
* @param string $sql SQL语句
* @param array $params 参数数组
* @return PDOStatement|false 查询结果
*/
public function query(string $sql, array $params = []) {
try {
$stmt = $this->connection->prepare($sql);
$stmt->execute($params);
self::$queryCount++;
return $stmt;
} catch (PDOException $e) {
throw new Exception("查询执行失败: " . $e->getMessage());
}
}
/**
* 获取查询计数
*
* @return int 查询计数
*/
public static function getQueryCount(): int {
return self::$queryCount;
}
/**
* 重置查询计数
*
* @return void
*/
public static function resetQueryCount(): void {
self::$queryCount = 0;
}
/**
* 私有克隆方法,防止克隆实例
*/
private function __clone() {}
/**
* 私有反序列化方法,防止反序列化实例
*/
private function __wakeup() {}
}
// 使用示例
try {
// 初始化配置
ConfigManager::initialize('config.json');
// 设置和获取配置
ConfigManager::set('app_name', 'My App');
ConfigManager::set('debug', true);
echo "应用名称: " . ConfigManager::get('app_name') . "\n";
echo "调试模式: " . (ConfigManager::get('debug') ? '开启' : '关闭') . "\n";
// 使用计数器
Counter::increment(5);
echo "当前计数: " . Counter::getCount() . "\n";
Counter::decrement(2);
echo "当前计数: " . Counter::getCount() . "\n";
// 查看计数器历史
print_r(Counter::getHistory());
// 使用数据库单例
$db = Database::getInstance();
$db->query("SELECT * FROM users");
$db->query("SELECT * FROM orders");
echo "执行的查询数量: " . Database::getQueryCount() . "\n";
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
观察者模式
观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,它的所有依赖者都会收到通知并自动更新。
<?php
/**
* 观察者接口
*/
interface Observer {
public function update(Subject $subject, $data = null): void;
}
/**
* 主题接口
*/
interface Subject {
public function attach(Observer $observer): void;
public function detach(Observer $observer): void;
public function notify($data = null): void;
}
/**
* 订单类
* 实现主题接口
*/
class Order implements Subject {
private $observers = [];
private $id;
private $status;
private $items = [];
public function __construct(string $id) {
$this->id = $id;
$this->status = 'pending';
}
public function attach(Observer $observer): void {
$this->observers[] = $observer;
}
public function detach(Observer $observer): void {
$key = array_search($observer, $this->observers, true);
if ($key !== false) {
unset($this->observers[$key]);
}
}
public function notify($data = null): void {
foreach ($this->observers as $observer) {
$observer->update($this, $data);
}
}
public function addItem(string $item, float $price, int $quantity = 1): void {
$this->items[] = [
'item' => $item,
'price' => $price,
'quantity' => $quantity
];
$this->notify([
'action' => 'item_added',
'item' => $item,
'price' => $price,
'quantity' => $quantity
]);
}
public function updateStatus(string $status): void {
$oldStatus = $this->status;
$this->status = $status;
$this->notify([
'action' => 'status_changed',
'old_status' => $oldStatus,
'new_status' => $status
]);
}
public function getId(): string {
return $this->id;
}
public function getStatus(): string {
return $this->status;
}
public function getItems(): array {
return $this->items;
}
public function getTotal(): float {
$total = 0;
foreach ($this->items as $item) {
$total += $item['price'] * $item['quantity'];
}
return $total;
}
}
/**
* 邮件通知观察者
*/
class EmailNotifier implements Observer {
public function update(Subject $subject, $data = null): void {
if (!($subject instanceof Order)) {
return;
}
if ($data['action'] === 'status_changed') {
$this->sendStatusChangeEmail($subject, $data);
}
}
private function sendStatusChangeEmail(Order $order, array $data): void {
echo "发送邮件通知: 订单 {$order->getId()} 状态从 {$data['old_status']} 变更为 {$data['new_status']}\n";
}
}
/**
* 库存管理观察者
*/
class InventoryManager implements Observer {
public function update(Subject $subject, $data = null): void {
if (!($subject instanceof Order)) {
return;
}
if ($data['action'] === 'item_added') {
$this->updateInventory($data);
} elseif ($data['action'] === 'status_changed' && $data['new_status'] === 'cancelled') {
$this->restoreInventory($subject);
}
}
private function updateInventory(array $data): void {
echo "更新库存: 减少 {$data['item']} 库存 {$data['quantity']} 个\n";
}
private function restoreInventory(Order $order): void {
echo "恢复库存: 订单 {$order->getId()} 已取消,恢复所有商品库存\n";
foreach ($order->getItems() as $item) {
echo " - 增加 {$item['item']} 库存 {$item['quantity']} 个\n";
}
}
}
/**
* 日志记录观察者
*/
class Logger implements Observer {
public function update(Subject $subject, $data = null): void {
if (!($subject instanceof Order)) {
return;
}
$orderId = $subject->getId();
$action = $data['action'] ?? 'unknown';
echo "记录日志: 订单 {$orderId} 执行了 {$action} 操作\n";
echo " - 详情: " . json_encode($data) . "\n";
}
}
// 使用示例
$order = new Order('ORD-12345');
// 添加观察者
$emailNotifier = new EmailNotifier();
$inventoryManager = new InventoryManager();
$logger = new Logger();
$order->attach($emailNotifier);
$order->attach($inventoryManager);
$order->attach($logger);
// 添加商品
$order->addItem('笔记本电脑', 5999.00);
$order->addItem('鼠标', 99.00, 2);
// 更新状态
$order->updateStatus('processing');
$order->updateStatus('shipped');
// 移除库存观察者
$order->detach($inventoryManager);
// 取消订单
$order->updateStatus('cancelled');
依赖注入
依赖注入是一种设计模式,它允许一个对象接收它所依赖的其他对象。这种模式有助于解耦代码,使其更易于测试和维护。
<?php
/**
* 用户仓库接口
*/
interface UserRepositoryInterface {
public function findById(int $id): ?array;
public function findByEmail(string $email): ?array;
public function create(array $data): int;
public function update(int $id, array $data): bool;
public function delete(int $id): bool;
}
/**
* MySQL用户仓库实现
*/
class MySqlUserRepository implements UserRepositoryInterface {
private $db;
public function __construct(PDO $db) {
$this->db = $db;
}
public function findById(int $id): ?array {
$stmt = $this->db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$id]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user ?: null;
}
public function findByEmail(string $email): ?array {
$stmt = $this->db->prepare("SELECT * FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch(PDO::FETCH_ASSOC);
return $user ?: null;
}
public function create(array $data): int {
$stmt = $this->db->prepare("INSERT INTO users (name, email, password) VALUES (?, ?, ?)");
$stmt->execute([$data['name'], $data['email'], $data['password']]);
return (int) $this->db->lastInsertId();
}
public function update(int $id, array $data): bool {
$fields = [];
$values = [];
foreach ($data as $key => $value) {
$fields[] = "{$key} = ?";
$values[] = $value;
}
$values[] = $id;
$stmt = $this->db->prepare("UPDATE users SET " . implode(', ', $fields) . " WHERE id = ?");
return $stmt->execute($values);
}
public function delete(int $id): bool {
$stmt = $this->db->prepare("DELETE FROM users WHERE id = ?");
return $stmt->execute([$id]);
}
}
/**
* 用户服务类
*/
class UserService {
private $repository;
private $logger;
public function __construct(UserRepositoryInterface $repository, Logger $logger = null) {
$this->repository = $repository;
$this->logger = $logger;
}
public function registerUser(string $name, string $email, string $password): int {
// 检查邮箱是否已存在
if ($this->repository->findByEmail($email)) {
throw new Exception("邮箱已被注册");
}
// 哈希密码
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
// 创建用户
$userId = $this->repository->create([
'name' => $name,
'email' => $email,
'password' => $hashedPassword
]);
// 记录日志
if ($this->logger) {
$this->logger->log("用户注册成功: {$email}");
}
return $userId;
}
public function authenticateUser(string $email, string $password): bool {
$user = $this->repository->findByEmail($email);
if (!$user) {
return false;
}
$authenticated = password_verify($password, $user['password']);
if ($authenticated && $this->logger) {
$this->logger->log("用户登录成功: {$email}");
}
return $authenticated;
}
public function updateUserProfile(int $id, array $data): bool {
// 确保不能更新敏感字段
unset($data['password']);
$success = $this->repository->update($id, $data);
if ($success && $this->logger) {
$this->logger->log("用户资料更新: ID {$id}");
}
return $success;
}
}
/**
* 简单日志类
*/
class Logger {
private $logFile;
public function __construct(string $logFile = 'application.log') {
$this->logFile = $logFile;
}
public function log(string $message): void {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[{$timestamp}] {$message}" . PHP_EOL;
// 在实际应用中,这里会将日志写入文件
echo "写入日志: {$logEntry}";
}
}
// 使用示例
try {
// 创建数据库连接
$db = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// 创建仓库
$userRepository = new MySqlUserRepository($db);
// 创建日志记录器
$logger = new Logger();
// 创建用户服务(注入依赖)
$userService = new UserService($userRepository, $logger);
// 注册用户
$userId = $userService->registerUser('John Doe', 'john@example.com', 'password123');
echo "用户注册成功,ID: {$userId}\n";
// 验证用户
$authenticated = $userService->authenticateUser('john@example.com', 'password123');
echo "用户认证: " . ($authenticated ? '成功' : '失败') . "\n";
// 更新用户资料
$userService->updateUserProfile($userId, [
'name' => 'John Smith',
'address' => '123 Main St'
]);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
依赖注入的优点:
解耦:减少类之间的依赖关系
可测试性:可以轻松模拟依赖进行单元测试
灵活性:可以轻松替换实现,如切换数据库或日志系统
可维护性:代码更清晰,职责更明确
反射和注解
PHP 的反射 API 允许在运行时检查类、接口、函数、方法和扩展。PHP 8 引入了原生注解(Attributes)支持,可以为代码元素添加元数据。
反射 API
<?php
/**
* 产品类
*/
class Product {
private $id;
private $name;
private $price;
private $description;
public function __construct(int $id, string $name, float $price, string $description = '') {
$this->id = $id;
$this->name = $name;
$this->price = $price;
$this->description = $description;
}
public function getId(): int {
return $this->id;
}
public function getName(): string {
return $this->name;
}
public function getPrice(): float {
return $this->price;
}
public function getDescription(): string {
return $this->description;
}
public function setPrice(float $price): void {
$this->price = $price;
}
public function setDescription(string $description): void {
$this->description = $description;
}
public function getFormattedPrice(): string {
return number_format($this->price, 2) . ' 元';
}
}
/**
* 使用反射API检查类
*/
function inspectClass(string $className): void {
echo "检查类: {$className}\n";
// 获取类的反射
$reflectionClass = new ReflectionClass($className);
// 获取类信息
echo "类名: " . $reflectionClass->getName() . "\n";
echo "命名空间: " . $reflectionClass->getNamespaceName() . "\n";
echo "是否为抽象类: " . ($reflectionClass->isAbstract() ? '是' : '否') . "\n";
echo "是否为接口: " . ($reflectionClass->isInterface() ? '是' : '否') . "\n";
echo "是否为特性: " . ($reflectionClass->isTrait() ? '是' : '否') . "\n";
// 获取属性
echo "\n属性:\n";
$properties = $reflectionClass->getProperties();
foreach ($properties as $property) {
echo " - " . $property->getName();
echo " (" . ($property->isPublic() ? 'public' : ($property->isProtected() ? 'protected' : 'private')) . ")";
echo "\n";
}
// 获取方法
echo "\n方法:\n";
$methods = $reflectionClass->getMethods();
foreach ($methods as $method) {
echo " - " . $method->getName();
echo " (" . ($method->isPublic() ? 'public' : ($method->isProtected() ? 'protected' : 'private')) . ")";
// 获取方法参数
$parameters = $method->getParameters();
if (count($parameters) > 0) {
echo " 参数: ";
$paramStrings = [];
foreach ($parameters as $param) {
$paramStr = '';
if ($param->hasType()) {
$paramStr .= $param->getType() . ' ';
}
$paramStr .= '
这个示例展示了静态属性和方法的三种常见用途:
1. 配置管理:使用静态方法管理全局配置
2. 计数器:使用静态属性跟踪状态
3. 单例模式:确保类只有一个实例
静态成员的主要特点:
- 不需要实例化类就可以访问
- 在所有类实例间共享
- 使用self::关键字访问
- 适合管理全局状态和工具方法
### 后期静态绑定
后期静态绑定是PHP 5.3引入的一个特性,它解决了在继承情况下静态方法调用的问题。使用`static::`关键字代替`self::`可以引用运行时调用类的静态成员,而不是定义方法的类。
```php
<?php
/**
* 基础模型类
* 演示后期静态绑定
*/
class BaseModel {
/**
* 表名
* @var string
*/
protected static $table;
/**
* 获取表名
*
* @return string 表名
*/
public static function getTable(): string {
// 使用self::会引用BaseModel类中的$table
// return self::$table;
// 使用static::会引用运行时调用的类中的$table
return static::$table ?? strtolower(get_called_class());
}
/**
* 查找所有记录
*
* @return array 记录数组
*/
public static function findAll(): array {
$table = static::getTable();
echo "SELECT * FROM {$table}\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
/**
* 根据ID查找记录
*
* @param int $id 记录ID
* @return array|null 记录数组或null
*/
public static function findById(int $id): ?array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE id = {$id}\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
/**
* 创建新记录
*
* @param array $data 记录数据
* @return int 新记录ID
*/
public static function create(array $data): int {
$table = static::getTable();
$columns = implode(', ', array_keys($data));
$values = implode(', ', array_map(function($value) {
return is_string($value) ? "'{$value}'" : $value;
}, $data));
echo "INSERT INTO {$table} ({$columns}) VALUES ({$values})\n";
// 在实际应用中,这里会执行数据库插入
return 1;
}
}
/**
* 用户模型类
* 继承自BaseModel
*/
class UserModel extends BaseModel {
/**
* 表名
* @var string
*/
protected static $table = 'users';
/**
* 查找用户名
*
* @param string $username 用户名
* @return array|null 用户记录或null
*/
public static function findByUsername(string $username): ?array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE username = '{$username}'\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
}
/**
* 产品模型类
* 继承自BaseModel
*/
class ProductModel extends BaseModel {
/**
* 表名
* @var string
*/
protected static $table = 'products';
/**
* 查找活跃产品
*
* @return array 产品记录数组
*/
public static function findActive(): array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE status = 'active'\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
}
// 使用示例
echo "BaseModel表名: " . BaseModel::getTable() . "\n";
echo "UserModel表名: " . UserModel::getTable() . "\n";
echo "ProductModel表名: " . ProductModel::getTable() . "\n";
UserModel::findAll();
UserModel::findById(1);
UserModel::findByUsername('john');
ProductModel::findAll();
ProductModel::findActive();
// 创建记录
UserModel::create([
'username' => 'newuser',
'email' => 'newuser@example.com',
'password' => 'hashed_password'
]);
魔术方法
PHP 提供了一系列魔术方法,允许在特定事件发生时自动调用。这些方法以双下划线开头。
<?php
/**
* 产品类
* 演示魔术方法的使用
*/
class Product {
/**
* 产品属性
* @var array
*/
private $data = [];
/**
* 只读属性
* @var array
*/
private $readOnlyProps = ['id', 'created_at'];
/**
* 构造函数
*
* @param array $data 初始数据
*/
public function __construct(array $data = []) {
$this->data = $data;
if (!isset($this->data['created_at'])) {
$this->data['created_at'] = date('Y-m-d H:i:s');
}
echo "__construct 被调用\n";
}
/**
* 析构函数
* 对象被销毁时调用
*/
public function __destruct() {
echo "__destruct 被调用\n";
}
/**
* 获取不可访问属性的值
*
* @param string $name 属性名
* @return mixed 属性值
*/
public function __get(string $name) {
echo "__get({$name}) 被调用\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
"未定义的属性: {$name} in {$trace[0]['file']} on line {$trace[0]['line']}",
E_USER_NOTICE
);
return null;
}
/**
* 设置不可访问属性的值
*
* @param string $name 属性名
* @param mixed $value 属性值
* @return void
*/
public function __set(string $name, $value) {
echo "__set({$name}, " . json_encode($value) . ") 被调用\n";
if (in_array($name, $this->readOnlyProps)) {
throw new Exception("属性 {$name} 是只读的");
}
$this->data[$name] = $value;
}
/**
* 检查不可访问属性是否存在
*
* @param string $name 属性名
* @return bool 是否存在
*/
public function __isset(string $name): bool {
echo "__isset({$name}) 被调用\n";
return isset($this->data[$name]);
}
/**
* 删除不可访问属性
*
* @param string $name 属性名
* @return void
*/
public function __unset(string $name) {
echo "__unset({$name}) 被调用\n";
if (in_array($name, $this->readOnlyProps)) {
throw new Exception("属性 {$name} 是只读的");
}
unset($this->data[$name]);
}
/**
* 调用不可访问方法时被调用
*
* @param string $name 方法名
* @param array $arguments 参数
* @return mixed 返回值
*/
public function __call(string $name, array $arguments) {
echo "__call({$name}, " . json_encode($arguments) . ") 被调用\n";
// 处理getter方法
if (strpos($name, 'get') === 0) {
$property = lcfirst(substr($name, 3));
if (array_key_exists($property, $this->data)) {
return $this->data[$property];
}
}
// 处理setter方法
if (strpos($name, 'set') === 0) {
$property = lcfirst(substr($name, 3));
if (!in_array($property, $this->readOnlyProps)) {
$this->data[$property] = $arguments[0];
return true;
}
}
throw new Exception("方法 {$name} 不存在");
}
/**
* 调用不可访问的静态方法时被调用
*
* @param string $name 方法名
* @param array $arguments 参数
* @return mixed 返回值
*/
public static function __callStatic(string $name, array $arguments) {
echo "__callStatic({$name}, " . json_encode($arguments) . ") 被调用\n";
// 处理查找方法
if (strpos($name, 'findBy') === 0) {
$property = lcfirst(substr($name, 6));
$value = $arguments[0];
echo "模拟查询: SELECT * FROM products WHERE {$property} = '{$value}'\n";
return new self(['id' => 1, $property => $value]);
}
throw new Exception("静态方法 {$name} 不存在");
}
/**
* 对象被当作字符串使用时被调用
*
* @return string 字符串表示
*/
public function __toString(): string {
echo "__toString() 被调用\n";
return json_encode($this->data, JSON_PRETTY_PRINT);
}
/**
* 对象被当作函数调用时被调用
*
* @return mixed 返回值
*/
public function __invoke() {
echo "__invoke() 被调用\n";
return $this->data;
}
/**
* 序列化对象时被调用
*
* @return array 需要被序列化的属性
*/
public function __sleep(): array {
echo "__sleep() 被调用\n";
return ['data'];
}
/**
* 反序列化对象时被调用
*
* @return void
*/
public function __wakeup() {
echo "__wakeup() 被调用\n";
if (!isset($this->data['updated_at'])) {
$this->data['updated_at'] = date('Y-m-d H:i:s');
}
}
/**
* 调试信息
*
* @return array 调试信息
*/
public function __debugInfo(): array {
echo "__debugInfo() 被调用\n";
return [
'id' => $this->data['id'] ?? null,
'properties' => count($this->data),
'readOnly' => $this->readOnlyProps
];
}
}
// 使用示例
try {
// 创建产品
$product = new Product([
'id' => 1,
'name' => 'Laptop',
'price' => 999.99,
'stock' => 10
]);
// 使用__get
echo "产品名称: {$product->name}\n";
echo "产品价格: {$product->price}\n";
// 使用__set
$product->description = '高性能笔记本电脑';
$product->stock = 5;
// 尝试设置只读属性
try {
$product->id = 2;
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 使用__isset和__unset
if (isset($product->description)) {
echo "描述存在\n";
unset($product->description);
}
if (!isset($product->description)) {
echo "描述不存在\n";
}
// 使用__call
$product->setColor('Silver');
echo "颜色: " . $product->getColor() . "\n";
// 使用__callStatic
$foundProduct = Product::findByName('Keyboard');
// 使用__toString
echo "产品信息: {$product}\n";
// 使用__invoke
$data = $product();
print_r($data);
// 使用__sleep和__wakeup
$serialized = serialize($product);
echo "序列化: {$serialized}\n";
$unserialized = unserialize($serialized);
// 使用__debugInfo
var_dump($product);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
魔术方法提供了强大的功能,可以实现:
属性重载:通过
__get
、__set
、__isset
和__unset
方法重载:通过
__call
和__callStatic
对象序列化控制:通过
__sleep
和__wakeup
对象字符串表示:通过
__toString
对象调试信息:通过
__debugInfo
对象作为函数调用:通过
__invoke
特性(Traits)
特性(Traits)是 PHP 5.4 引入的一种代码复用机制,它允许开发者在不同的类中重用方法集,从而解决 PHP 单继承的限制。
基本使用
<?php
/**
* 日志特性
* 提供日志记录功能
*/
trait Logger {
/**
* 日志文件路径
* @var string
*/
protected $logFile = 'application.log';
/**
* 设置日志文件
*
* @param string $path 日志文件路径
* @return void
*/
public function setLogFile(string $path): void {
$this->logFile = $path;
}
/**
* 记录日志
*
* @param string $message 日志消息
* @param string $level 日志级别
* @return bool 是否成功
*/
public function log(string $message, string $level = 'INFO'): bool {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[{$timestamp}] [{$level}] {$message}" . PHP_EOL;
// 在实际应用中,这里会将日志写入文件
echo "写入日志: {$logEntry}";
return true;
}
/**
* 记录错误日志
*
* @param string $message 错误消息
* @return bool 是否成功
*/
public function logError(string $message): bool {
return $this->log($message, 'ERROR');
}
/**
* 记录警告日志
*
* @param string $message 警告消息
* @return bool 是否成功
*/
public function logWarning(string $message): bool {
return $this->log($message, 'WARNING');
}
}
/**
* 时间戳特性
* 提供时间戳管理功能
*/
trait Timestampable {
/**
* 创建时间
* @var string|null
*/
protected $createdAt = null;
/**
* 更新时间
* @var string|null
*/
protected $updatedAt = null;
/**
* 初始化时间戳
*
* @return void
*/
public function initTimestamps(): void {
$this->createdAt = date('Y-m-d H:i:s');
$this->updatedAt = $this->createdAt;
}
/**
* 更新时间戳
*
* @return void
*/
public function updateTimestamp(): void {
$this->updatedAt = date('Y-m-d H:i:s');
}
/**
* 获取创建时间
*
* @return string|null 创建时间
*/
public function getCreatedAt(): ?string {
return $this->createdAt;
}
/**
* 获取更新时间
*
* @return string|null 更新时间
*/
public function getUpdatedAt(): ?string {
return $this->updatedAt;
}
}
/**
* 用户类
* 使用Logger和Timestampable特性
*/
class User {
// 使用特性
use Logger, Timestampable;
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 电子邮件
* @var string
*/
private $email;
/**
* 构造函数
*
* @param int $id 用户ID
* @param string $username 用户名
* @param string $email 电子邮件
*/
public function __construct(int $id, string $username, string $email) {
$this->id = $id;
$this->username = $username;
$this->email = $email;
// 初始化时间戳
$this->initTimestamps();
// 记录日志
$this->log("用户 {$username} 已创建");
}
/**
* 更新用户信息
*
* @param array $data 更新数据
* @return bool 是否成功
*/
public function update(array $data): bool {
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
// 更新时间戳
$this->updateTimestamp();
// 记录日志
$this->log("用户 {$this->username} 已更新");
return true;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt,
'updated_at' => $this->updatedAt
];
}
}
// 使用示例
$user = new User(1, 'johndoe', 'john@example.com');
print_r($user->getInfo());
// 更新用户
$user->update(['email' => 'newemail@example.com']);
print_r($user->getInfo());
// 记录警告
$user->logWarning("用户尝试访问受限资源");
解决多继承问题
特性可以帮助我们解决 PHP 中的多继承问题,通过组合多个特性来为类添加功能。
<?php
/**
* 序列化特性
* 提供对象序列化功能
*/
trait Serializer {
/**
* 序列化对象
*
* @return string 序列化后的字符串
*/
public function serialize(): string {
$data = get_object_vars($this);
return json_encode($data);
}
/**
* 反序列化对象
*
* @param string $data 序列化的数据
* @return void
*/
public function unserialize(string $data): void {
$properties = json_decode($data, true);
foreach ($properties as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
}
/**
* 验证特性
* 提供数据验证功能
*/
trait Validator {
/**
* 验证错误
* @var array
*/
protected $errors = [];
/**
* 验证电子邮件
*
* @param string $email 电子邮件
* @return bool 是否有效
*/
public function validateEmail(string $email): bool {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->errors['email'] = "无效的电子邮件地址";
return false;
}
return true;
}
/**
* 验证字符串长度
*
* @param string $value 字符串值
* @param int $min 最小长度
* @param int $max 最大长度
* @param string $field 字段名
* @return bool 是否有效
*/
public function validateLength(string $value, int $min, int $max, string $field): bool {
$length = strlen($value);
if ($length < $min || $length > $max) {
$this->errors[$field] = "{$field}长度必须在{$min}到{$max}之间";
return false;
}
return true;
}
/**
* 获取验证错误
*
* @return array 错误数组
*/
public function getErrors(): array {
return $this->errors;
}
/**
* 检查是否有错误
*
* @return bool 是否有错误
*/
public function hasErrors(): bool {
return !empty($this->errors);
}
}
/**
* 用户模型类
* 使用多个特性
*/
class UserModel {
// 使用多个特性
use Logger, Timestampable, Serializer, Validator;
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 电子邮件
* @var string
*/
private $email;
/**
* 构造函数
*
* @param array $data 用户数据
*/
public function __construct(array $data = []) {
if (!empty($data)) {
$this->fill($data);
}
$this->initTimestamps();
$this->setLogFile('users.log');
}
/**
* 填充用户数据
*
* @param array $data 用户数据
* @return bool 是否成功
*/
public function fill(array $data): bool {
// 验证数据
if (isset($data['email'])) {
$this->validateEmail($data['email']);
}
if (isset($data['username'])) {
$this->validateLength($data['username'], 3, 20, 'username');
}
// 如果有验证错误,返回false
if ($this->hasErrors()) {
$this->logError("用户数据验证失败: " . json_encode($this->getErrors()));
return false;
}
// 填充数据
$this->id = $data['id'] ?? null;
$this->username = $data['username'] ?? '';
$this->email = $data['email'] ?? '';
$this->log("用户数据已填充");
return true;
}
/**
* 保存用户
*
* @return bool 是否成功
*/
public function save(): bool {
// 在实际应用中,这里会将用户保存到数据库
$this->updateTimestamp();
$this->log("用户 {$this->username} 已保存");
return true;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function toArray(): array {
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt,
'updated_at' => $this->updatedAt
];
}
}
// 使用示例
$userData = [
'id' => 1,
'username' => 'johndoe',
'email' => 'john@example.com'
];
$user = new UserModel($userData);
print_r($user->toArray());
// 序列化用户
$serialized = $user->serialize();
echo "序列化的用户数据: {$serialized}\n";
// 创建新用户并反序列化
$newUser = new UserModel();
$newUser->unserialize($serialized);
print_r($newUser->toArray());
// 使用验证
$invalidUser = new UserModel([
'username' => 'jo', // 太短
'email' => 'invalid-email' // 无效的邮箱
]);
if ($invalidUser->hasErrors()) {
echo "验证错误:\n";
print_r($invalidUser->getErrors());
}
特性中的冲突解决
当多个特性包含同名方法时,可能会发生冲突。PHP 提供了几种解决冲突的方法。
<?php
/**
* 特性A
*/
trait TraitA {
public function hello() {
return "Hello from TraitA";
}
public function greet() {
return "Greet from TraitA";
}
}
/**
* 特性B
*/
trait TraitB {
public function hello() {
return "Hello from TraitB";
}
public function greet() {
return "Greet from TraitB";
}
}
/**
* 特性C
*/
trait TraitC {
public function hello() {
return "Hello from TraitC";
}
// 使用其他特性
use TraitA, TraitB {
// 解决冲突:使用TraitA的greet方法
TraitA::greet insteadof TraitB;
// 解决冲突:使用TraitB的hello方法
TraitB::hello insteadof TraitA;
// 为TraitA的hello方法创建别名
TraitA::hello as helloA;
}
}
/**
* 示例类
* 演示特性冲突解决
*/
class ConflictExample {
// 使用特性C
use TraitC;
// 使用特性A和特性B,并解决冲突
use TraitA, TraitB {
// 解决冲突:使用TraitB的hello方法
TraitB::hello insteadof TraitA;
// 为TraitA的hello方法创建别名
TraitA::hello as helloFromA;
// 修改方法可见性
TraitB::greet as protected greetFromB;
}
/**
* 测试方法
*
* @return array 测试结果
*/
public function test(): array {
return [
'hello' => $this->hello(),
'helloFromA' => $this->helloFromA(),
'helloA' => $this->helloA()
];
}
/**
* 访问受保护的方法
*
* @return string 结果
*/
public function accessProtected(): string {
return $this->greetFromB();
}
}
// 使用示例
$example = new ConflictExample();
print_r($example->test());
echo "Protected method: " . $example->accessProtected() . "\n";
特性的主要优点:
代码复用:可以在多个类中重用方法,而不需要继承
解决多继承问题:可以组合多个特性,实现类似多继承的功能
灵活性:可以在运行时解决方法冲突
组合优于继承:符合现代编程的"组合优于继承"原则
特性的使用场景:
横切关注点:如日志记录、时间戳管理、序列化等
多个类需要相同功能,但不适合放在基类中
需要在多个不相关的类中共享代码
需要组合多个功能,但单继承限制了类的设计
命名空间和自动加载
命名空间是 PHP 5.3 引入的一种组织代码的方式,它可以避免命名冲突,并使代码结构更加清晰。自动加载则是一种自动包含类文件的机制,避免了手动使用 require 或 include。
命名空间基础
<?php
// 文件: App/Models/User.php
// 声明命名空间
namespace App\Models;
/**
* 用户模型类
*/
class User {
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 构造函数
*
* @param int $id 用户ID
* @param string $username 用户名
*/
public function __construct(int $id, string $username) {
$this->id = $id;
$this->username = $username;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'username' => $this->username
];
}
}
// 文件: App/Services/UserService.php
namespace App\Services;
// 导入User类
use App\Models\User;
use App\Repositories\UserRepository;
use App\Exceptions\UserNotFoundException;
/**
* 用户服务类
*/
class UserService {
/**
* 用户仓库
* @var UserRepository
*/
private $repository;
/**
* 构造函数
*
* @param UserRepository $repository 用户仓库
*/
public function __construct(UserRepository $repository) {
$this->repository = $repository;
}
/**
* 获取用户
*
* @param int $id 用户ID
* @return User 用户对象
* @throws UserNotFoundException 用户不存在时抛出异常
*/
public function getUser(int $id): User {
$user = $this->repository->find($id);
if (!$user) {
throw new UserNotFoundException("用户ID {$id} 不存在");
}
return $user;
}
/**
* 创建用户
*
* @param string $username 用户名
* @return User 新用户对象
*/
public function createUser(string $username): User {
$id = $this->repository->create(['username' => $username]);
return new User($id, $username);
}
}
// 文件: App/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
/**
* 用户仓库类
*/
class UserRepository {
/**
* 查找用户
*
* @param int $id 用户ID
* @return User|null 用户对象或null
*/
public function find(int $id): ?User {
// 模拟数据库查询
if ($id > 0 && $id < 100) {
return new User($id, "user{$id}");
}
return null;
}
/**
* 创建用户
*
* @param array $data 用户数据
* @return int 新用户ID
*/
public function create(array $data): int {
// 模拟数据库插入
$id = rand(1, 1000);
return $id;
}
}
// 文件: App/Exceptions/UserNotFoundException.php
namespace App\Exceptions;
use Exception;
/**
* 用户未找到异常
*/
class UserNotFoundException extends Exception {
// 继承Exception的所有功能
}
// 文件: index.php
// 导入类
use App\Services\UserService;
use App\Repositories\UserRepository;
use App\Exceptions\UserNotFoundException;
// 创建服务
$repository = new UserRepository();
$userService = new UserService($repository);
try {
// 获取用户
$user = $userService->getUser(5);
print_r($user->getInfo());
// 创建用户
$newUser = $userService->createUser('newuser');
print_r($newUser->getInfo());
// 尝试获取不存在的用户
$userService->getUser(101);
} catch (UserNotFoundException $e) {
echo "错误: " . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "未知错误: " . $e->getMessage() . "\n";
}
命名空间的主要优点:
避免命名冲突:可以使用相同的类名,只要它们在不同的命名空间中
更好的组织代码:可以按功能或模块组织代码
更短的类名:可以使用更短的类名,而不必担心全局命名冲突
更好的可读性:代码结构更清晰,更易于理解
PSR-4 标准
PSR-4 是 PHP 标准推荐的自动加载规范,它定义了如何将命名空间映射到文件路径。
<?php
/**
* PSR-4自动加载器实现
*/
class Autoloader {
/**
* 命名空间前缀到目录的映射
* @var array
*/
private $prefixes = [];
/**
* 注册自动加载器
*
* @return void
*/
public function register(): void {
spl_autoload_register([$this, 'loadClass']);
}
/**
* 添加命名空间前缀
*
* @param string $prefix 命名空间前缀
* @param string $baseDir 基础目录
* @param bool $prepend 是否前置
* @return void
*/
public function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void {
// 规范化命名空间前缀
$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);
}
}
/**
* 加载类文件
*
* @param string $class 完全限定类名
* @return bool 是否成功加载
*/
public function loadClass(string $class): bool {
// 当前命名空间前缀
$prefix = $class;
// 遍历命名空间查找文件
while (false !== $pos = strrpos($prefix, '\\')) {
// 保留命名空间前缀和相对类名
$prefix = substr($class, 0, $pos + 1);
$relativeClass = substr($class, $pos + 1);
// 尝试加载映射的文件
$mapped = $this->loadMappedFile($prefix, $relativeClass);
if ($mapped) {
return true;
}
// 移除尾部命名空间分隔符
$prefix = rtrim($prefix, '\\');
}
return false;
}
/**
* 加载映射的文件
*
* @param string $prefix 命名空间前缀
* @param string $relativeClass 相对类名
* @return bool 是否成功加载
*/
protected function loadMappedFile(string $prefix, string $relativeClass): bool {
// 检查命名空间前缀是否存在
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 true;
}
}
return false;
}
/**
* 加载文件
*
* @param string $file 文件路径
* @return bool 文件是否存在
*/
protected function requireFile(string $file): bool {
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}
// 使用示例
$autoloader = new Autoloader();
$autoloader->register();
// 添加命名空间映射
$autoloader->addNamespace('App\\Models', __DIR__ . '/App/Models');
$autoloader->addNamespace('App\\Services', __DIR__ . '/App/Services');
$autoloader->addNamespace('App\\Repositories', __DIR__ . '/App/Repositories');
$autoloader->addNamespace('App\\Exceptions', __DIR__ . '/App/Exceptions');
// 现在可以直接使用类,无需手动require
$user = new App\Models\User(1, 'johndoe');
print_r($user->getInfo());
Composer 自动加载
Composer 是 PHP 的依赖管理工具,它提供了强大的自动加载功能,基于 PSR-4 标准。
// composer.json
{
"name": "myapp/example",
"description": "Example application",
"type": "project",
"require": {
"php": ">=7.4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
使用 Composer 自动加载:
<?php
// 引入Composer自动加载器
require 'vendor/autoload.php';
// 现在可以直接使用命名空间中的类
use App\Models\User;
use App\Services\UserService;
$user = new User(1, 'johndoe');
命名空间和自动加载的最佳实践:
遵循 PSR-4 标准:命名空间应与目录结构一致
每个类一个文件:每个 PHP 文件应只包含一个类
文件名应与类名匹配:例如 User 类应在 User.php 文件中
使用 Composer 管理自动加载:避免手动实现自动加载器
合理组织命名空间:按功能或模块组织代码
设计模式
设计模式是软件开发中常见问题的解决方案,它们是经过验证的、可重用的代码设计。PHP 作为一种面向对象的语言,可以实现多种设计模式。
单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。
<?php
/**
* 数据库连接类
* 使用单例模式
*/
class Database {
/**
* 单例实例
* @var Database|null
*/
private static $instance = null;
/**
* 数据库连接
* @var PDO|null
*/
private $connection = null;
/**
* 配置
* @var array
*/
private $config = [
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4'
];
/**
* 私有构造函数,防止外部实例化
*/
private function __construct() {
try {
$dsn = "mysql:host={$this->config['host']};dbname={$this->config['database']};charset={$this->config['charset']}";
$this->connection = new PDO(
$dsn,
$this->config['username'],
$this->config['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
echo "数据库连接成功\n";
} catch (PDOException $e) {
echo "数据库连接失败: " . $e->getMessage() . "\n";
}
}
/**
* 获取单例实例
*
* @return Database 数据库实例
*/
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 执行查询
*
* @param string $sql SQL语句
* @param array $params 参数数组
* @return PDOStatement|false 查询结果
*/
public function query(string $sql, array $params = []) {
try {
$stmt = $this->connection->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
echo "查询执行失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 私有克隆方法,防止克隆实例
*/
private function __clone() {}
/**
* 私有反序列化方法,防止反序列化实例
*/
private function __wakeup() {}
}
// 使用示例
$db1 = Database::getInstance();
$db2 = Database::getInstance();
// $db1和$db2是同一个实例
var_dump($db1 === $db2); // 输出: bool(true)
// 执行查询
$db1->query("SELECT * FROM users WHERE id = ?", [1]);
工厂模式
工厂模式提供了一种创建对象的接口,允许子类决定实例化的对象类型。
<?php
/**
* 支付接口
* 定义所有支付方式必须实现的方法
*/
interface PaymentGateway {
/**
* 处理支付
*
* @param float $amount 金额
* @param array $options 选项
* @return bool 是否成功
*/
public function processPayment(float $amount, array $options = []): bool;
/**
* 验证支付
*
* @param array $data 验证数据
* @return bool 是否有效
*/
public function validatePayment(array $data): bool;
}
/**
* PayPal支付网关
*/
class PayPalGateway implements PaymentGateway {
private $credentials;
public function __construct(array $credentials) {
$this->credentials = $credentials;
}
public function processPayment(float $amount, array $options = []): bool {
echo "使用PayPal处理 {$amount} 元的支付\n";
return true;
}
public function validatePayment(array $data): bool {
return isset($data['transaction_id']);
}
}
/**
* 支付宝网关
*/
class AlipayGateway implements PaymentGateway {
private $credentials;
public function __construct(array $credentials) {
$this->credentials = $credentials;
}
public function processPayment(float $amount, array $options = []): bool {
echo "使用支付宝处理 {$amount} 元的支付\n";
return true;
}
public function validatePayment(array $data): bool {
return isset($data['trade_no']);
}
}
/**
* 支付网关工厂
*/
class PaymentGatewayFactory {
private $config;
public function __construct(array $config) {
$this->config = $config;
}
public function createGateway(string $type): PaymentGateway {
switch ($type) {
case 'paypal':
return new PayPalGateway($this->config['paypal'] ?? []);
case 'alipay':
return new AlipayGateway($this->config['alipay'] ?? []);
default:
throw new Exception("不支持的支付网关类型: {$type}");
}
}
}
// 使用示例
$config = [
'paypal' => ['client_id' => 'xxx', 'secret' => 'yyy'],
'alipay' => ['app_id' => 'xxx', 'private_key' => 'yyy']
];
$factory = new PaymentGatewayFactory($config);
try {
$paypal = $factory->createGateway('paypal');
$alipay = $factory->createGateway('alipay');
$paypal->processPayment(100.00);
$alipay->processPayment(100.00);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
``` . $param->getName();
if ($param->isDefaultValueAvailable()) {
$defaultValue = $param->getDefaultValue();
if (is_string($defaultValue)) {
$defaultValue = "'{$defaultValue}'";
} elseif (is_null($defaultValue)) {
$defaultValue = 'null';
} elseif (is_bool($defaultValue)) {
$defaultValue = $defaultValue ? 'true' : 'false';
}
$paramStr .= " = {$defaultValue}";
}
$paramStrings[] = $paramStr;
}
echo implode(', ', $paramStrings);
}
// 获取返回类型
if ($method->hasReturnType()) {
echo " 返回: " . $method->getReturnType();
}
echo "\n";
}
}
/**
* 使用反射API创建对象
*/
function createObjectDynamically(string $className, array $constructorArgs = []): object {
$reflectionClass = new ReflectionClass($className);
return $reflectionClass->newInstanceArgs($constructorArgs);
}
/**
* 使用反射API调用方法
*/
function callMethodDynamically(object $object, string $methodName, array $args = []): mixed {
$reflectionMethod = new ReflectionMethod(get_class($object), $methodName);
return $reflectionMethod->invokeArgs($object, $args);
}
/**
* 使用反射API获取和设置属性
*/
function getPropertyValue(object $object, string $propertyName): mixed {
$reflectionProperty = new ReflectionProperty(get_class($object), $propertyName);
$reflectionProperty->setAccessible(true);
return $reflectionProperty->getValue($object);
}
function setPropertyValue(object $object, string $propertyName, $value): void {
$reflectionProperty = new ReflectionProperty(get_class($object), $propertyName);
$reflectionProperty->setAccessible(true);
$reflectionProperty->setValue($object, $value);
}
// 使用示例
inspectClass(Product::class);
// 动态创建对象
$product = createObjectDynamically(Product::class, [1, '笔记本电脑', 5999.99, '高性能笔记本']);
echo "\n创建的产品: " . $product->getName() . ", 价格: " . $product->getPrice() . "\n";
// 动态调用方法
$formattedPrice = callMethodDynamically($product, 'getFormattedPrice');
echo "格式化价格: " . $formattedPrice . "\n";
// 获取私有属性
$id = getPropertyValue($product, 'id');
echo "产品ID: " . $id . "\n";
// 设置私有属性
setPropertyValue($product, 'price', 4999.99);
echo "新价格: " . $product->getPrice() . "\n";
PHP 8 注解(Attributes)
PHP 8 引入了原生注解支持,可以为代码元素添加元数据。
<?php
/**
* 路由注解
*/
#[Attribute]
class Route {
private $path;
private $methods;
public function __construct(string $path, array $methods = ['GET']) {
$this->path = $path;
$this->methods = $methods;
}
public function getPath(): string {
return $this->path;
}
public function getMethods(): array {
return $this->methods;
}
}
/**
* 验证注解
*/
#[Attribute(Attribute::TARGET_PROPERTY)]
class Validate {
private $rules;
public function __construct(array $rules) {
$this->rules = $rules;
}
public function getRules(): array {
return $this->rules;
}
}
/**
* 用户控制器
*/
class UserController {
#[Route('/users', ['GET'])]
public function index() {
return "用户列表";
}
#[Route('/users/{id}', ['GET'])]
public function show(int $id) {
return "显示用户 {$id}";
}
#[Route('/users', ['POST'])]
public function store() {
return "创建用户";
}
#[Route('/users/{id}', ['PUT'])]
public function update(int $id) {
return "更新用户 {$id}";
}
#[Route('/users/{id}', ['DELETE'])]
public function destroy(int $id) {
return "删除用户 {$id}";
}
}
/**
* 用户模型
*/
class User {
#[Validate(['required', 'string', 'max:255'])]
private $name;
#[Validate(['required', 'email', 'unique:users'])]
private $email;
#[Validate(['required', 'string', 'min:8'])]
private $password;
public function __construct(string $name, string $email, string $password) {
$this->name = $name;
$this->email = $email;
$this->password = $password;
}
public function getName(): string {
return $this->name;
}
public function getEmail(): string {
return $this->email;
}
}
/**
* 路由注册器
*/
class Router {
private $routes = [];
public function registerController(string $controllerClass): void {
$reflectionClass = new ReflectionClass($controllerClass);
$methods = $reflectionClass->getMethods();
foreach ($methods as $method) {
$attributes = $method->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$route = $attribute->newInstance();
$this->routes[] = [
'path' => $route->getPath(),
'methods' => $route->getMethods(),
'controller' => $controllerClass,
'action' => $method->getName()
];
}
}
}
public function getRoutes(): array {
return $this->routes;
}
public function findRoute(string $path, string $method): ?array {
foreach ($this->routes as $route) {
// 简单的路由匹配,实际应用中会更复杂
$pattern = preg_replace('/{[^}]+}/', '([^/]+)', $route['path']);
$pattern = str_replace('/', '\/', $pattern);
$pattern = '/^' . $pattern . '$/';
if (preg_match($pattern, $path) && in_array($method, $route['methods'])) {
return $route;
}
}
return null;
}
}
/**
* 验证器
*/
class Validator {
public function validate(object $object): array {
$errors = [];
$reflectionClass = new ReflectionClass($object);
$properties = $reflectionClass->getProperties();
foreach ($properties as $property) {
$attributes = $property->getAttributes(Validate::class);
foreach ($attributes as $attribute) {
$validate = $attribute->newInstance();
$rules = $validate->getRules();
$property->setAccessible(true);
$value = $property->getValue($object);
foreach ($rules as $rule) {
if ($rule === 'required' && empty($value)) {
$errors[] = "{$property->getName()} 是必填的";
} elseif ($rule === 'email' && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
$errors[] = "{$property->getName()} 必须是有效的电子邮件地址";
} elseif (strpos($rule, 'min:') === 0) {
$min = (int) substr($rule, 4);
if (strlen($value) < $min) {
$errors[] = "{$property->getName()} 长度不能小于 {$min}";
}
} elseif (strpos($rule, 'max:') === 0) {
$max = (int) substr($rule, 4);
if (strlen($value) > $max) {
$errors[] = "{$property->getName()} 长度不能大于 {$max}";
}
}
}
}
}
return $errors;
}
}
// 使用示例
// 注册路由
$router = new Router();
$router->registerController(UserController::class);
// 显示所有路由
echo "注册的路由:\n";
foreach ($router->getRoutes() as $route) {
echo " - " . implode(', ', $route['methods']) . " {$route['path']} => {$route['controller']}::{$route['action']}\n";
}
// 查找路由
$route = $router->findRoute('/users/123', 'GET');
if ($route) {
echo "\n找到路由: " . implode(', ', $route['methods']) . " {$route['path']} => {$route['controller']}::{$route['action']}\n";
}
// 验证对象
$user = new User('John Doe', 'invalid-email', 'pass');
$validator = new Validator();
$errors = $validator->validate($user);
if (count($errors) > 0) {
echo "\n验证错误:\n";
foreach ($errors as $error) {
echo " - {$error}\n";
}
}
反射和注解的应用场景:
框架开发:如路由系统、依赖注入容器、ORM 等
代码生成:根据类的结构生成代码
测试工具:自动发现和运行测试
文档生成:从代码注释和注解生成文档
验证系统:基于注解的数据验证
这个示例展示了静态属性和方法的三种常见用途:
配置管理:使用静态方法管理全局配置
计数器:使用静态属性跟踪状态
单例模式:确保类只有一个实例
静态成员的主要特点:
不需要实例化类就可以访问
在所有类实例间共享
使用 self::关键字访问
适合管理全局状态和工具方法
后期静态绑定
后期静态绑定是 PHP 5.3 引入的一个特性,它解决了在继承情况下静态方法调用的问题。使用static::
关键字代替self::
可以引用运行时调用类的静态成员,而不是定义方法的类。
<?php
/**
* 基础模型类
* 演示后期静态绑定
*/
class BaseModel {
/**
* 表名
* @var string
*/
protected static $table;
/**
* 获取表名
*
* @return string 表名
*/
public static function getTable(): string {
// 使用self::会引用BaseModel类中的$table
// return self::$table;
// 使用static::会引用运行时调用的类中的$table
return static::$table ?? strtolower(get_called_class());
}
/**
* 查找所有记录
*
* @return array 记录数组
*/
public static function findAll(): array {
$table = static::getTable();
echo "SELECT * FROM {$table}\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
/**
* 根据ID查找记录
*
* @param int $id 记录ID
* @return array|null 记录数组或null
*/
public static function findById(int $id): ?array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE id = {$id}\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
/**
* 创建新记录
*
* @param array $data 记录数据
* @return int 新记录ID
*/
public static function create(array $data): int {
$table = static::getTable();
$columns = implode(', ', array_keys($data));
$values = implode(', ', array_map(function($value) {
return is_string($value) ? "'{$value}'" : $value;
}, $data));
echo "INSERT INTO {$table} ({$columns}) VALUES ({$values})\n";
// 在实际应用中,这里会执行数据库插入
return 1;
}
}
/**
* 用户模型类
* 继承自BaseModel
*/
class UserModel extends BaseModel {
/**
* 表名
* @var string
*/
protected static $table = 'users';
/**
* 查找用户名
*
* @param string $username 用户名
* @return array|null 用户记录或null
*/
public static function findByUsername(string $username): ?array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE username = '{$username}'\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
}
/**
* 产品模型类
* 继承自BaseModel
*/
class ProductModel extends BaseModel {
/**
* 表名
* @var string
*/
protected static $table = 'products';
/**
* 查找活跃产品
*
* @return array 产品记录数组
*/
public static function findActive(): array {
$table = static::getTable();
echo "SELECT * FROM {$table} WHERE status = 'active'\n";
// 在实际应用中,这里会执行数据库查询
return [];
}
}
// 使用示例
echo "BaseModel表名: " . BaseModel::getTable() . "\n";
echo "UserModel表名: " . UserModel::getTable() . "\n";
echo "ProductModel表名: " . ProductModel::getTable() . "\n";
UserModel::findAll();
UserModel::findById(1);
UserModel::findByUsername('john');
ProductModel::findAll();
ProductModel::findActive();
// 创建记录
UserModel::create([
'username' => 'newuser',
'email' => 'newuser@example.com',
'password' => 'hashed_password'
]);
魔术方法
PHP 提供了一系列魔术方法,允许在特定事件发生时自动调用。这些方法以双下划线开头。
<?php
/**
* 产品类
* 演示魔术方法的使用
*/
class Product {
/**
* 产品属性
* @var array
*/
private $data = [];
/**
* 只读属性
* @var array
*/
private $readOnlyProps = ['id', 'created_at'];
/**
* 构造函数
*
* @param array $data 初始数据
*/
public function __construct(array $data = []) {
$this->data = $data;
if (!isset($this->data['created_at'])) {
$this->data['created_at'] = date('Y-m-d H:i:s');
}
echo "__construct 被调用\n";
}
/**
* 析构函数
* 对象被销毁时调用
*/
public function __destruct() {
echo "__destruct 被调用\n";
}
/**
* 获取不可访问属性的值
*
* @param string $name 属性名
* @return mixed 属性值
*/
public function __get(string $name) {
echo "__get({$name}) 被调用\n";
if (array_key_exists($name, $this->data)) {
return $this->data[$name];
}
$trace = debug_backtrace();
trigger_error(
"未定义的属性: {$name} in {$trace[0]['file']} on line {$trace[0]['line']}",
E_USER_NOTICE
);
return null;
}
/**
* 设置不可访问属性的值
*
* @param string $name 属性名
* @param mixed $value 属性值
* @return void
*/
public function __set(string $name, $value) {
echo "__set({$name}, " . json_encode($value) . ") 被调用\n";
if (in_array($name, $this->readOnlyProps)) {
throw new Exception("属性 {$name} 是只读的");
}
$this->data[$name] = $value;
}
/**
* 检查不可访问属性是否存在
*
* @param string $name 属性名
* @return bool 是否存在
*/
public function __isset(string $name): bool {
echo "__isset({$name}) 被调用\n";
return isset($this->data[$name]);
}
/**
* 删除不可访问属性
*
* @param string $name 属性名
* @return void
*/
public function __unset(string $name) {
echo "__unset({$name}) 被调用\n";
if (in_array($name, $this->readOnlyProps)) {
throw new Exception("属性 {$name} 是只读的");
}
unset($this->data[$name]);
}
/**
* 调用不可访问方法时被调用
*
* @param string $name 方法名
* @param array $arguments 参数
* @return mixed 返回值
*/
public function __call(string $name, array $arguments) {
echo "__call({$name}, " . json_encode($arguments) . ") 被调用\n";
// 处理getter方法
if (strpos($name, 'get') === 0) {
$property = lcfirst(substr($name, 3));
if (array_key_exists($property, $this->data)) {
return $this->data[$property];
}
}
// 处理setter方法
if (strpos($name, 'set') === 0) {
$property = lcfirst(substr($name, 3));
if (!in_array($property, $this->readOnlyProps)) {
$this->data[$property] = $arguments[0];
return true;
}
}
throw new Exception("方法 {$name} 不存在");
}
/**
* 调用不可访问的静态方法时被调用
*
* @param string $name 方法名
* @param array $arguments 参数
* @return mixed 返回值
*/
public static function __callStatic(string $name, array $arguments) {
echo "__callStatic({$name}, " . json_encode($arguments) . ") 被调用\n";
// 处理查找方法
if (strpos($name, 'findBy') === 0) {
$property = lcfirst(substr($name, 6));
$value = $arguments[0];
echo "模拟查询: SELECT * FROM products WHERE {$property} = '{$value}'\n";
return new self(['id' => 1, $property => $value]);
}
throw new Exception("静态方法 {$name} 不存在");
}
/**
* 对象被当作字符串使用时被调用
*
* @return string 字符串表示
*/
public function __toString(): string {
echo "__toString() 被调用\n";
return json_encode($this->data, JSON_PRETTY_PRINT);
}
/**
* 对象被当作函数调用时被调用
*
* @return mixed 返回值
*/
public function __invoke() {
echo "__invoke() 被调用\n";
return $this->data;
}
/**
* 序列化对象时被调用
*
* @return array 需要被序列化的属性
*/
public function __sleep(): array {
echo "__sleep() 被调用\n";
return ['data'];
}
/**
* 反序列化对象时被调用
*
* @return void
*/
public function __wakeup() {
echo "__wakeup() 被调用\n";
if (!isset($this->data['updated_at'])) {
$this->data['updated_at'] = date('Y-m-d H:i:s');
}
}
/**
* 调试信息
*
* @return array 调试信息
*/
public function __debugInfo(): array {
echo "__debugInfo() 被调用\n";
return [
'id' => $this->data['id'] ?? null,
'properties' => count($this->data),
'readOnly' => $this->readOnlyProps
];
}
}
// 使用示例
try {
// 创建产品
$product = new Product([
'id' => 1,
'name' => 'Laptop',
'price' => 999.99,
'stock' => 10
]);
// 使用__get
echo "产品名称: {$product->name}\n";
echo "产品价格: {$product->price}\n";
// 使用__set
$product->description = '高性能笔记本电脑';
$product->stock = 5;
// 尝试设置只读属性
try {
$product->id = 2;
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
// 使用__isset和__unset
if (isset($product->description)) {
echo "描述存在\n";
unset($product->description);
}
if (!isset($product->description)) {
echo "描述不存在\n";
}
// 使用__call
$product->setColor('Silver');
echo "颜色: " . $product->getColor() . "\n";
// 使用__callStatic
$foundProduct = Product::findByName('Keyboard');
// 使用__toString
echo "产品信息: {$product}\n";
// 使用__invoke
$data = $product();
print_r($data);
// 使用__sleep和__wakeup
$serialized = serialize($product);
echo "序列化: {$serialized}\n";
$unserialized = unserialize($serialized);
// 使用__debugInfo
var_dump($product);
} catch (Exception $e) {
echo "错误: " . $e->getMessage() . "\n";
}
魔术方法提供了强大的功能,可以实现:
属性重载:通过
__get
、__set
、__isset
和__unset
方法重载:通过
__call
和__callStatic
对象序列化控制:通过
__sleep
和__wakeup
对象字符串表示:通过
__toString
对象调试信息:通过
__debugInfo
对象作为函数调用:通过
__invoke
特性(Traits)
特性(Traits)是 PHP 5.4 引入的一种代码复用机制,它允许开发者在不同的类中重用方法集,从而解决 PHP 单继承的限制。
基本使用
<?php
/**
* 日志特性
* 提供日志记录功能
*/
trait Logger {
/**
* 日志文件路径
* @var string
*/
protected $logFile = 'application.log';
/**
* 设置日志文件
*
* @param string $path 日志文件路径
* @return void
*/
public function setLogFile(string $path): void {
$this->logFile = $path;
}
/**
* 记录日志
*
* @param string $message 日志消息
* @param string $level 日志级别
* @return bool 是否成功
*/
public function log(string $message, string $level = 'INFO'): bool {
$timestamp = date('Y-m-d H:i:s');
$logEntry = "[{$timestamp}] [{$level}] {$message}" . PHP_EOL;
// 在实际应用中,这里会将日志写入文件
echo "写入日志: {$logEntry}";
return true;
}
/**
* 记录错误日志
*
* @param string $message 错误消息
* @return bool 是否成功
*/
public function logError(string $message): bool {
return $this->log($message, 'ERROR');
}
/**
* 记录警告日志
*
* @param string $message 警告消息
* @return bool 是否成功
*/
public function logWarning(string $message): bool {
return $this->log($message, 'WARNING');
}
}
/**
* 时间戳特性
* 提供时间戳管理功能
*/
trait Timestampable {
/**
* 创建时间
* @var string|null
*/
protected $createdAt = null;
/**
* 更新时间
* @var string|null
*/
protected $updatedAt = null;
/**
* 初始化时间戳
*
* @return void
*/
public function initTimestamps(): void {
$this->createdAt = date('Y-m-d H:i:s');
$this->updatedAt = $this->createdAt;
}
/**
* 更新时间戳
*
* @return void
*/
public function updateTimestamp(): void {
$this->updatedAt = date('Y-m-d H:i:s');
}
/**
* 获取创建时间
*
* @return string|null 创建时间
*/
public function getCreatedAt(): ?string {
return $this->createdAt;
}
/**
* 获取更新时间
*
* @return string|null 更新时间
*/
public function getUpdatedAt(): ?string {
return $this->updatedAt;
}
}
/**
* 用户类
* 使用Logger和Timestampable特性
*/
class User {
// 使用特性
use Logger, Timestampable;
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 电子邮件
* @var string
*/
private $email;
/**
* 构造函数
*
* @param int $id 用户ID
* @param string $username 用户名
* @param string $email 电子邮件
*/
public function __construct(int $id, string $username, string $email) {
$this->id = $id;
$this->username = $username;
$this->email = $email;
// 初始化时间戳
$this->initTimestamps();
// 记录日志
$this->log("用户 {$username} 已创建");
}
/**
* 更新用户信息
*
* @param array $data 更新数据
* @return bool 是否成功
*/
public function update(array $data): bool {
foreach ($data as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
// 更新时间戳
$this->updateTimestamp();
// 记录日志
$this->log("用户 {$this->username} 已更新");
return true;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt,
'updated_at' => $this->updatedAt
];
}
}
// 使用示例
$user = new User(1, 'johndoe', 'john@example.com');
print_r($user->getInfo());
// 更新用户
$user->update(['email' => 'newemail@example.com']);
print_r($user->getInfo());
// 记录警告
$user->logWarning("用户尝试访问受限资源");
解决多继承问题
特性可以帮助我们解决 PHP 中的多继承问题,通过组合多个特性来为类添加功能。
<?php
/**
* 序列化特性
* 提供对象序列化功能
*/
trait Serializer {
/**
* 序列化对象
*
* @return string 序列化后的字符串
*/
public function serialize(): string {
$data = get_object_vars($this);
return json_encode($data);
}
/**
* 反序列化对象
*
* @param string $data 序列化的数据
* @return void
*/
public function unserialize(string $data): void {
$properties = json_decode($data, true);
foreach ($properties as $key => $value) {
if (property_exists($this, $key)) {
$this->$key = $value;
}
}
}
}
/**
* 验证特性
* 提供数据验证功能
*/
trait Validator {
/**
* 验证错误
* @var array
*/
protected $errors = [];
/**
* 验证电子邮件
*
* @param string $email 电子邮件
* @return bool 是否有效
*/
public function validateEmail(string $email): bool {
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$this->errors['email'] = "无效的电子邮件地址";
return false;
}
return true;
}
/**
* 验证字符串长度
*
* @param string $value 字符串值
* @param int $min 最小长度
* @param int $max 最大长度
* @param string $field 字段名
* @return bool 是否有效
*/
public function validateLength(string $value, int $min, int $max, string $field): bool {
$length = strlen($value);
if ($length < $min || $length > $max) {
$this->errors[$field] = "{$field}长度必须在{$min}到{$max}之间";
return false;
}
return true;
}
/**
* 获取验证错误
*
* @return array 错误数组
*/
public function getErrors(): array {
return $this->errors;
}
/**
* 检查是否有错误
*
* @return bool 是否有错误
*/
public function hasErrors(): bool {
return !empty($this->errors);
}
}
/**
* 用户模型类
* 使用多个特性
*/
class UserModel {
// 使用多个特性
use Logger, Timestampable, Serializer, Validator;
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 电子邮件
* @var string
*/
private $email;
/**
* 构造函数
*
* @param array $data 用户数据
*/
public function __construct(array $data = []) {
if (!empty($data)) {
$this->fill($data);
}
$this->initTimestamps();
$this->setLogFile('users.log');
}
/**
* 填充用户数据
*
* @param array $data 用户数据
* @return bool 是否成功
*/
public function fill(array $data): bool {
// 验证数据
if (isset($data['email'])) {
$this->validateEmail($data['email']);
}
if (isset($data['username'])) {
$this->validateLength($data['username'], 3, 20, 'username');
}
// 如果有验证错误,返回false
if ($this->hasErrors()) {
$this->logError("用户数据验证失败: " . json_encode($this->getErrors()));
return false;
}
// 填充数据
$this->id = $data['id'] ?? null;
$this->username = $data['username'] ?? '';
$this->email = $data['email'] ?? '';
$this->log("用户数据已填充");
return true;
}
/**
* 保存用户
*
* @return bool 是否成功
*/
public function save(): bool {
// 在实际应用中,这里会将用户保存到数据库
$this->updateTimestamp();
$this->log("用户 {$this->username} 已保存");
return true;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function toArray(): array {
return [
'id' => $this->id,
'username' => $this->username,
'email' => $this->email,
'created_at' => $this->createdAt,
'updated_at' => $this->updatedAt
];
}
}
// 使用示例
$userData = [
'id' => 1,
'username' => 'johndoe',
'email' => 'john@example.com'
];
$user = new UserModel($userData);
print_r($user->toArray());
// 序列化用户
$serialized = $user->serialize();
echo "序列化的用户数据: {$serialized}\n";
// 创建新用户并反序列化
$newUser = new UserModel();
$newUser->unserialize($serialized);
print_r($newUser->toArray());
// 使用验证
$invalidUser = new UserModel([
'username' => 'jo', // 太短
'email' => 'invalid-email' // 无效的邮箱
]);
if ($invalidUser->hasErrors()) {
echo "验证错误:\n";
print_r($invalidUser->getErrors());
}
特性中的冲突解决
当多个特性包含同名方法时,可能会发生冲突。PHP 提供了几种解决冲突的方法。
<?php
/**
* 特性A
*/
trait TraitA {
public function hello() {
return "Hello from TraitA";
}
public function greet() {
return "Greet from TraitA";
}
}
/**
* 特性B
*/
trait TraitB {
public function hello() {
return "Hello from TraitB";
}
public function greet() {
return "Greet from TraitB";
}
}
/**
* 特性C
*/
trait TraitC {
public function hello() {
return "Hello from TraitC";
}
// 使用其他特性
use TraitA, TraitB {
// 解决冲突:使用TraitA的greet方法
TraitA::greet insteadof TraitB;
// 解决冲突:使用TraitB的hello方法
TraitB::hello insteadof TraitA;
// 为TraitA的hello方法创建别名
TraitA::hello as helloA;
}
}
/**
* 示例类
* 演示特性冲突解决
*/
class ConflictExample {
// 使用特性C
use TraitC;
// 使用特性A和特性B,并解决冲突
use TraitA, TraitB {
// 解决冲突:使用TraitB的hello方法
TraitB::hello insteadof TraitA;
// 为TraitA的hello方法创建别名
TraitA::hello as helloFromA;
// 修改方法可见性
TraitB::greet as protected greetFromB;
}
/**
* 测试方法
*
* @return array 测试结果
*/
public function test(): array {
return [
'hello' => $this->hello(),
'helloFromA' => $this->helloFromA(),
'helloA' => $this->helloA()
];
}
/**
* 访问受保护的方法
*
* @return string 结果
*/
public function accessProtected(): string {
return $this->greetFromB();
}
}
// 使用示例
$example = new ConflictExample();
print_r($example->test());
echo "Protected method: " . $example->accessProtected() . "\n";
特性的主要优点:
代码复用:可以在多个类中重用方法,而不需要继承
解决多继承问题:可以组合多个特性,实现类似多继承的功能
灵活性:可以在运行时解决方法冲突
组合优于继承:符合现代编程的"组合优于继承"原则
特性的使用场景:
横切关注点:如日志记录、时间戳管理、序列化等
多个类需要相同功能,但不适合放在基类中
需要在多个不相关的类中共享代码
需要组合多个功能,但单继承限制了类的设计
命名空间和自动加载
命名空间是 PHP 5.3 引入的一种组织代码的方式,它可以避免命名冲突,并使代码结构更加清晰。自动加载则是一种自动包含类文件的机制,避免了手动使用 require 或 include。
命名空间基础
<?php
// 文件: App/Models/User.php
// 声明命名空间
namespace App\Models;
/**
* 用户模型类
*/
class User {
/**
* 用户ID
* @var int
*/
private $id;
/**
* 用户名
* @var string
*/
private $username;
/**
* 构造函数
*
* @param int $id 用户ID
* @param string $username 用户名
*/
public function __construct(int $id, string $username) {
$this->id = $id;
$this->username = $username;
}
/**
* 获取用户信息
*
* @return array 用户信息
*/
public function getInfo(): array {
return [
'id' => $this->id,
'username' => $this->username
];
}
}
// 文件: App/Services/UserService.php
namespace App\Services;
// 导入User类
use App\Models\User;
use App\Repositories\UserRepository;
use App\Exceptions\UserNotFoundException;
/**
* 用户服务类
*/
class UserService {
/**
* 用户仓库
* @var UserRepository
*/
private $repository;
/**
* 构造函数
*
* @param UserRepository $repository 用户仓库
*/
public function __construct(UserRepository $repository) {
$this->repository = $repository;
}
/**
* 获取用户
*
* @param int $id 用户ID
* @return User 用户对象
* @throws UserNotFoundException 用户不存在时抛出异常
*/
public function getUser(int $id): User {
$user = $this->repository->find($id);
if (!$user) {
throw new UserNotFoundException("用户ID {$id} 不存在");
}
return $user;
}
/**
* 创建用户
*
* @param string $username 用户名
* @return User 新用户对象
*/
public function createUser(string $username): User {
$id = $this->repository->create(['username' => $username]);
return new User($id, $username);
}
}
// 文件: App/Repositories/UserRepository.php
namespace App\Repositories;
use App\Models\User;
/**
* 用户仓库类
*/
class UserRepository {
/**
* 查找用户
*
* @param int $id 用户ID
* @return User|null 用户对象或null
*/
public function find(int $id): ?User {
// 模拟数据库查询
if ($id > 0 && $id < 100) {
return new User($id, "user{$id}");
}
return null;
}
/**
* 创建用户
*
* @param array $data 用户数据
* @return int 新用户ID
*/
public function create(array $data): int {
// 模拟数据库插入
$id = rand(1, 1000);
return $id;
}
}
// 文件: App/Exceptions/UserNotFoundException.php
namespace App\Exceptions;
use Exception;
/**
* 用户未找到异常
*/
class UserNotFoundException extends Exception {
// 继承Exception的所有功能
}
// 文件: index.php
// 导入类
use App\Services\UserService;
use App\Repositories\UserRepository;
use App\Exceptions\UserNotFoundException;
// 创建服务
$repository = new UserRepository();
$userService = new UserService($repository);
try {
// 获取用户
$user = $userService->getUser(5);
print_r($user->getInfo());
// 创建用户
$newUser = $userService->createUser('newuser');
print_r($newUser->getInfo());
// 尝试获取不存在的用户
$userService->getUser(101);
} catch (UserNotFoundException $e) {
echo "错误: " . $e->getMessage() . "\n";
} catch (Exception $e) {
echo "未知错误: " . $e->getMessage() . "\n";
}
命名空间的主要优点:
避免命名冲突:可以使用相同的类名,只要它们在不同的命名空间中
更好的组织代码:可以按功能或模块组织代码
更短的类名:可以使用更短的类名,而不必担心全局命名冲突
更好的可读性:代码结构更清晰,更易于理解
PSR-4 标准
PSR-4 是 PHP 标准推荐的自动加载规范,它定义了如何将命名空间映射到文件路径。
<?php
/**
* PSR-4自动加载器实现
*/
class Autoloader {
/**
* 命名空间前缀到目录的映射
* @var array
*/
private $prefixes = [];
/**
* 注册自动加载器
*
* @return void
*/
public function register(): void {
spl_autoload_register([$this, 'loadClass']);
}
/**
* 添加命名空间前缀
*
* @param string $prefix 命名空间前缀
* @param string $baseDir 基础目录
* @param bool $prepend 是否前置
* @return void
*/
public function addNamespace(string $prefix, string $baseDir, bool $prepend = false): void {
// 规范化命名空间前缀
$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);
}
}
/**
* 加载类文件
*
* @param string $class 完全限定类名
* @return bool 是否成功加载
*/
public function loadClass(string $class): bool {
// 当前命名空间前缀
$prefix = $class;
// 遍历命名空间查找文件
while (false !== $pos = strrpos($prefix, '\\')) {
// 保留命名空间前缀和相对类名
$prefix = substr($class, 0, $pos + 1);
$relativeClass = substr($class, $pos + 1);
// 尝试加载映射的文件
$mapped = $this->loadMappedFile($prefix, $relativeClass);
if ($mapped) {
return true;
}
// 移除尾部命名空间分隔符
$prefix = rtrim($prefix, '\\');
}
return false;
}
/**
* 加载映射的文件
*
* @param string $prefix 命名空间前缀
* @param string $relativeClass 相对类名
* @return bool 是否成功加载
*/
protected function loadMappedFile(string $prefix, string $relativeClass): bool {
// 检查命名空间前缀是否存在
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 true;
}
}
return false;
}
/**
* 加载文件
*
* @param string $file 文件路径
* @return bool 文件是否存在
*/
protected function requireFile(string $file): bool {
if (file_exists($file)) {
require $file;
return true;
}
return false;
}
}
// 使用示例
$autoloader = new Autoloader();
$autoloader->register();
// 添加命名空间映射
$autoloader->addNamespace('App\\Models', __DIR__ . '/App/Models');
$autoloader->addNamespace('App\\Services', __DIR__ . '/App/Services');
$autoloader->addNamespace('App\\Repositories', __DIR__ . '/App/Repositories');
$autoloader->addNamespace('App\\Exceptions', __DIR__ . '/App/Exceptions');
// 现在可以直接使用类,无需手动require
$user = new App\Models\User(1, 'johndoe');
print_r($user->getInfo());
Composer 自动加载
Composer 是 PHP 的依赖管理工具,它提供了强大的自动加载功能,基于 PSR-4 标准。
// composer.json
{
"name": "myapp/example",
"description": "Example application",
"type": "project",
"require": {
"php": ">=7.4"
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
}
}
使用 Composer 自动加载:
<?php
// 引入Composer自动加载器
require 'vendor/autoload.php';
// 现在可以直接使用命名空间中的类
use App\Models\User;
use App\Services\UserService;
$user = new User(1, 'johndoe');
命名空间和自动加载的最佳实践:
遵循 PSR-4 标准:命名空间应与目录结构一致
每个类一个文件:每个 PHP 文件应只包含一个类
文件名应与类名匹配:例如 User 类应在 User.php 文件中
使用 Composer 管理自动加载:避免手动实现自动加载器
合理组织命名空间:按功能或模块组织代码
设计模式
设计模式是软件开发中常见问题的解决方案,它们是经过验证的、可重用的代码设计。PHP 作为一种面向对象的语言,可以实现多种设计模式。
单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点。
<?php
/**
* 数据库连接类
* 使用单例模式
*/
class Database {
/**
* 单例实例
* @var Database|null
*/
private static $instance = null;
/**
* 数据库连接
* @var PDO|null
*/
private $connection = null;
/**
* 配置
* @var array
*/
private $config = [
'host' => 'localhost',
'database' => 'test',
'username' => 'root',
'password' => '',
'charset' => 'utf8mb4'
];
/**
* 私有构造函数,防止外部实例化
*/
private function __construct() {
try {
$dsn = "mysql:host={$this->config['host']};dbname={$this->config['database']};charset={$this->config['charset']}";
$this->connection = new PDO(
$dsn,
$this->config['username'],
$this->config['password'],
[PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
);
echo "数据库连接成功\n";
} catch (PDOException $e) {
echo "数据库连接失败: " . $e->getMessage() . "\n";
}
}
/**
* 获取单例实例
*
* @return Database 数据库实例
*/
public static function getInstance(): Database {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
/**
* 执行查询
*
* @param string $sql SQL语句
* @param array $params 参数数组
* @return PDOStatement|false 查询结果
*/
public function query(string $sql, array $params = []) {
try {
$stmt = $this->connection->prepare($sql);
$stmt->execute($params);
return $stmt;
} catch (PDOException $e) {
echo "查询执行失败: " . $e->getMessage() . "\n";
return false;
}
}
/**
* 私有克隆方法,防止克隆实例
*/
private function __clone() {}
/**
* 私有反序列化方法,防止反序列化实例
*/
private function __wakeup() {}
}
// 使用示例
$db1 = Database::getInstance();
$db2 = Database::getInstance();
// $db1和$db2是同一个实例
var_dump($db1 === $db2); // 输出: bool(true)
// 执行查询
$db1->query("SELECT * FROM users WHERE id = ?", [1]);
版权声明: 本文为 InfoQ 作者【白月书生】的原创文章。
原文链接:【http://xie.infoq.cn/article/7c78829d750ac506f47d430b1】。未经作者许可,禁止转载。

白月书生
还未添加个人签名 2022-08-31 加入
还未添加个人简介
评论