PHP 中 Class 的使用说明
文章目录
PHP 中 Class 的使用说明
1. 类(Class)的基础概念
在 PHP 中,类是面向对象编程(OOP)的核心概念,它是一种用于创建对象的蓝图或模板。类定义了对象的属性(数据)和方法(行为),而对象则是类的实例化。
类的主要特性包括:
- 封装:将数据和方法组合在一个单元中
- 继承:允许一个类基于另一个类构建
- 多态:允许不同的类实现相同的方法但有不同的行为
- 抽象:允许定义接口和抽象类
2. 类的定义与实例化
2.1 定义类
使用 class 关键字定义一个类:
<?php
// 定义一个简单的 Person 类
class Person {
// 属性(成员变量)
public $name;
public $age;
// 方法(成员函数)
public function sayHello() {
echo "Hello, my name is {$this->name}.";
}
}
?>
2.2 实例化类(创建对象)
使用 new 关键字创建类的实例:
<?php
// 创建 Person 类的实例
$person = new Person();
// 访问对象的属性并赋值
$person->name = "张三";
$person->age = 30;
// 调用对象的方法
$person->sayHello(); // 输出: Hello, my name is 张三.
?>
2.3 构造函数
构造函数是一种特殊的方法,在创建对象时自动调用,用于初始化对象的属性:
<?php
class Person {
public $name;
public $age;
// 构造函数
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function sayHello() {
echo "Hello, my name is {$this->name}.";
}
}
// 创建对象时直接传递参数
$person = new Person("张三", 30);
$person->sayHello(); // 输出: Hello, my name is 张三.
?>
2.4 析构函数
析构函数在对象被销毁时自动调用,通常用于清理资源:
<?php
class FileHandler {
private $file;
public function __construct($filename) {
$this->file = fopen($filename, "r");
}
// 析构函数
public function __destruct() {
if ($this->file) {
fclose($this->file);
echo "文件已关闭";
}
}
}
$handler = new FileHandler("example.txt");
// 当 $handler 不再被引用或脚本结束时,析构函数会自动调用
?>
3. 属性(Properties)
属性是类中用于存储数据的变量,也称为成员变量。
3.1 属性的类型
PHP 7.4+ 支持属性类型声明:
<?php
class Person {
// 基本类型声明
public string $name;
public int $age;
public float $height;
public bool $isStudent;
// 可为空类型(PHP 8.0+)
public ?string $email;
// 类类型声明
public DateTime $registrationDate;
}
?>
3.2 属性的访问控制
PHP 提供了三种访问控制修饰符:
<?php
class Person {
// 公共属性:可以在任何地方访问
public $name;
// 受保护属性:只能在类内部或子类中访问
protected $email;
// 私有属性:只能在定义它的类内部访问
private $phone;
// ...其他代码
}
?>
3.3 属性的默认值
可以为属性设置默认值:
<?php
class Person {
public string $name = "Guest";
public int $age = 0;
public array $hobbies = [];
public ?string $email = null;
}
?>
3.4 静态属性
静态属性属于类本身,而不是类的实例:
<?php
class Counter {
// 静态属性
public static int $count = 0;
public function __construct() {
self::$count++; // 使用 self:: 访问静态属性
}
}
$c1 = new Counter();
$c2 = new Counter();
echo Counter::$count; // 输出: 2
?>
3.5 只读属性(PHP 8.1+)
只读属性只能在初始化时赋值一次:
<?php
class User {
public readonly int $id;
public function __construct(int $id) {
$this->id = $id; // 只能在构造函数中赋值
}
}
$user = new User(123);
// $user->id = 456; // 错误:不能修改只读属性
?>
4. 方法(Methods)
方法是类中定义的函数,用于执行特定操作。
4.1 方法的定义和调用
<?php
class Calculator {
// 方法定义
public function add(int $a, int $b): int {
return $a + $b;
}
}
$calc = new Calculator();
$result = $calc->add(5, 3); // 调用方法
echo $result; // 输出: 8
?>
4.2 方法的访问控制
与属性一样,方法也有三种访问控制修饰符:
<?php
class Person {
private $name;
// 公共方法
public function setName(string $name): void {
$this->name = $name;
}
// 受保护方法
protected function validateName(string $name): bool {
return strlen($name) > 0;
}
// 私有方法
private function formatName(string $name): string {
return ucfirst($name);
}
}
?>
4.3 静态方法
静态方法属于类本身,不依赖于类的实例:
<?php
class Math {
// 静态方法
public static function square(int $num): int {
return $num * $num;
}
}
// 直接通过类名调用静态方法
$result = Math::square(5);
echo $result; // 输出: 25
?>
4.4 抽象方法
抽象方法是在抽象类中声明但不实现的方法,子类必须实现它:
<?php
abstract class Shape {
// 抽象方法
abstract public function calculateArea(): float;
}
class Rectangle extends Shape {
private $width;
private $height;
public function __construct(float $width, float $height) {
$this->width = $width;
$this->height = $height;
}
// 实现抽象方法
public function calculateArea(): float {
return $this->width * $this->height;
}
}
?>
4.5 魔术方法
PHP 提供了一系列魔术方法,这些方法以双下划线 __ 开头,在特定情况下自动调用:
<?php
class Person {
private $data = [];
// 属性访问魔术方法
public function __get($name) {
return $this->data[$name] ?? null;
}
public function __set($name, $value) {
$this->data[$name] = $value;
}
public function __isset($name) {
return isset($this->data[$name]);
}
public function __unset($name) {
unset($this->data[$name]);
}
// 方法调用魔术方法
public function __call($name, $arguments) {
echo "调用了方法 $name ,参数:" . implode(', ', $arguments);
}
// 静态方法调用魔术方法
public static function __callStatic($name, $arguments) {
echo "调用了静态方法 $name ,参数:" . implode(', ', $arguments);
}
// 字符串转换魔术方法
public function __toString() {
return "Person object";
}
}
?>
5. 继承(Inheritance)
继承允许一个类(子类)基于另一个类(父类)构建,从而重用代码:
5.1 基本继承
<?php
// 父类
class Animal {
protected $name;
public function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
public function makeSound() {
echo "Some generic sound";
}
}
// 子类继承父类
class Dog extends Animal {
// 重写父类的方法
public function makeSound() {
echo "Woof! Woof!";
}
// 子类特有的方法
public function fetch() {
echo "{$this->name} is fetching the ball";
}
}
// 创建子类的实例
$dog = new Dog("Rex");
echo $dog->getName(); // 输出: Rex
$dog->makeSound(); // 输出: Woof! Woof!
$dog->fetch(); // 输出: Rex is fetching the ball
?>
5.2 调用父类的方法
使用 parent:: 关键字调用父类的方法:
<?php
class ParentClass {
public function sayHello() {
echo "Hello from Parent";
}
}
class ChildClass extends ParentClass {
public function sayHello() {
// 调用父类的方法
parent::sayHello();
echo "Hello from Child";
}
}
$child = new ChildClass();
$child->sayHello(); // 输出: Hello from ParentHello from Child
?>
5.3 多继承的替代方案(Trait)
PHP 不支持多继承,但可以使用 Trait 实现类似功能:
<?php
// 定义一个 Trait
trait Logger {
public function log($message) {
echo "[LOG] $message\n";
}
}
// 定义另一个 Trait
trait Database {
public function connect() {
echo "Connected to database\n";
}
}
// 使用多个 Trait
class User {
use Logger, Database;
public function register() {
$this->connect();
$this->log("User registered");
}
}
$user = new User();
$user->register(); // 输出: Connected to database [LOG] User registered
?>
6. 多态(Polymorphism)
多态允许不同的类实现相同的接口或继承自同一个父类,但有不同的行为:
<?php
// 定义一个接口
interface Shape {
public function calculateArea();
}
// 实现接口的类
class Circle implements Shape {
private $radius;
public function __construct($radius) {
$this->radius = $radius;
}
public function calculateArea() {
return pi() * pow($this->radius, 2);
}
}
class Rectangle implements Shape {
private $width;
private $height;
public function __construct($width, $height) {
$this->width = $width;
$this->height = $height;
}
public function calculateArea() {
return $this->width * $this->height;
}
}
// 多态的使用
function printArea(Shape $shape) {
echo "Area: " . $shape->calculateArea() . "\n";
}
$circle = new Circle(5);
$rectangle = new Rectangle(4, 6);
printArea($circle); // 输出: Area: 78.539816339745
printArea($rectangle); // 输出: Area: 24
?>
7. 抽象类与接口
7.1 抽象类
抽象类是不能直接实例化的类,可以包含抽象方法和普通方法:
<?php
// 定义抽象类
abstract class Vehicle {
protected $brand;
public function __construct($brand) {
$this->brand = $brand;
}
// 普通方法
public function getBrand() {
return $this->brand;
}
// 抽象方法
abstract public function startEngine();
}
// 继承抽象类并实现抽象方法
class Car extends Vehicle {
public function startEngine() {
echo "{$this->brand} car engine started";
}
}
// 不能直接实例化抽象类
// $vehicle = new Vehicle("Toyota"); // 错误
$car = new Car("Toyota");
$car->startEngine(); // 输出: Toyota car engine started
?>
7.2 接口
接口定义了一个类应该实现的方法,但不提供方法的具体实现:
<?php
// 定义接口
interface Logger {
public function logInfo($message);
public function logError($message);
public function logWarning($message);
}
// 实现接口
class FileLogger implements Logger {
private $logFile;
public function __construct($logFile) {
$this->logFile = $logFile;
}
public function logInfo($message) {
$this->writeLog("INFO", $message);
}
public function logError($message) {
$this->writeLog("ERROR", $message);
}
public function logWarning($message) {
$this->writeLog("WARNING", $message);
}
private function writeLog($level, $message) {
$timestamp = date("Y-m-d H:i:s");
$logEntry = "[$timestamp] [$level] $message\n";
file_put_contents($this->logFile, $logEntry, FILE_APPEND);
}
}
// 使用实现了接口的类
$logger = new FileLogger("app.log");
$logger->logInfo("Application started");
$logger->logError("Database connection failed");
?>
7.3 抽象类与接口的区别
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 方法实现 | 可以包含已实现的方法 | 只能包含方法声明,不能实现 |
| 属性 | 可以包含各种访问级别的属性 | 只能包含公共静态常量 |
| 继承 | 一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
| 构造函数 | 可以有构造函数 | 不能有构造函数 |
| 访问控制 | 可以使用各种访问修饰符 | 方法默认为公共,不能使用其他修饰符 |
8. 面向对象设计原则
8.1 单一职责原则(SRP)
一个类应该只有一个责任:
<?php
// 好的设计:User 类只负责用户数据和行为
class User {
private $id;
private $name;
private $email;
// 只包含与用户相关的方法
public function getName() { return $this->name; }
public function setName($name) { $this->name = $name; }
// ...
}
// 好的设计:UserRepository 类只负责数据库操作
class UserRepository {
public function save(User $user) { /* 保存用户到数据库 */ }
public function findById($id) { /* 从数据库查找用户 */ }
// ...
}
?>
8.2 开闭原则(OCP)
软件实体应该对扩展开放,对修改关闭:
<?php
// 好的设计:使用接口和继承实现开闭原则
interface PaymentMethod {
public function pay($amount);
}
class CreditCardPayment implements PaymentMethod {
public function pay($amount) { /* 信用卡支付逻辑 */ }
}
class PayPalPayment implements PaymentMethod {
public function pay($amount) { /* PayPal支付逻辑 */ }
}
// PaymentProcessor 对扩展开放,对修改关闭
class PaymentProcessor {
public function processPayment(PaymentMethod $method, $amount) {
return $method->pay($amount);
}
}
// 新增支付方式不需要修改 PaymentProcessor
class BitcoinPayment implements PaymentMethod {
public function pay($amount) { /* 比特币支付逻辑 */ }
}
?>
8.3 里氏替换原则(LSP)
子类应该能够替换其父类:
<?php
class Rectangle {
protected $width;
protected $height;
public function setWidth($width) { $this->width = $width; }
public function setHeight($height) { $this->height = $height; }
public function getArea() { return $this->width * $this->height; }
}
// 正方形是一种特殊的矩形,但这个实现违反了里氏替换原则
class Square extends Rectangle {
public function setWidth($width) {
$this->width = $width;
$this->height = $width;
}
public function setHeight($height) {
$this->width = $height;
$this->height = $height;
}
}
// 更好的设计:使用组合而非继承
interface Shape {
public function getArea();
}
class Rectangle implements Shape {
// ...
}
class Square implements Shape {
// ...
}
?>
8.4 接口隔离原则(ISP)
不应该强迫客户端依赖于它们不使用的方法:
<?php
// 不好的设计:一个大而全的接口
interface Worker {
public function work();
public function eat();
public function sleep();
}
// 好的设计:将接口拆分为更小、更具体的接口
interface Workable {
public function work();
}
interface Eatable {
public function eat();
}
interface Sleepable {
public function sleep();
}
// 客户端可以只实现需要的接口
class HumanWorker implements Workable, Eatable, Sleepable {
// 实现所有方法
}
class RobotWorker implements Workable {
// 只需要实现 work() 方法
public function work() { /* 工作逻辑 */ }
}
?>
8.5 依赖倒置原则(DIP)
高层模块不应该依赖于低层模块,两者都应该依赖于抽象:
<?php
// 不好的设计:高层模块直接依赖低层模块
class UserService {
private $mysqlRepository; // 直接依赖具体类
public function __construct() {
$this->mysqlRepository = new MySQLUserRepository();
}
}
// 好的设计:依赖于抽象接口
interface UserRepository {
public function findById($id);
public function save(User $user);
}
class MySQLUserRepository implements UserRepository {
// 实现接口方法
}
class MongoDBUserRepository implements UserRepository {
// 实现接口方法
}
class UserService {
private $repository; // 依赖于抽象接口
// 通过构造函数注入依赖
public function __construct(UserRepository $repository) {
$this->repository = $repository;
}
}
?>
9. 高级类特性
9.1 类的自动加载
使用 spl_autoload_register() 实现类的自动加载:
<?php
// 自动加载函数
function my_autoloader($class) {
// 将命名空间分隔符转换为目录分隔符
$path = str_replace('\\', DIRECTORY_SEPARATOR, $class);
// 加载对应的类文件
require_once __DIR__ . '/classes/' . $path . '.php';
}
// 注册自动加载函数
spl_autoload_register('my_autoloader');
// 现在可以直接使用类,不需要手动引入
$user = new App\Models\User();
?>
9.2 命名空间
命名空间可以避免类名冲突:
<?php
// 定义命名空间
namespace App\Models;
class User {
// 类定义
}
// 在其他文件中使用命名空间
namespace App\Controllers;
// 使用 use 语句引入类
use App\Models\User;
class UserController {
public function index() {
$user = new User();
// ...
}
}
// 也可以使用完全限定名称
$user = new \App\Models\User();
?>
9.3 反射
反射 API 允许在运行时检查类、接口、方法和属性:
<?php
class Person {
public $name;
private $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function sayHello() {
echo "Hello, my name is {$this->name}.";
}
}
// 创建反射类
$reflection = new ReflectionClass('Person');
// 获取类的信息
echo "类名: " . $reflection->getName() . "\n";
echo "公共方法: " . "\n";
foreach ($reflection->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
echo " - " . $method->getName() . "\n";
}
echo "属性: " . "\n";
foreach ($reflection->getProperties() as $property) {
echo " - " . $property->getName() . "\n";
}
// 创建实例
$person = $reflection->newInstanceArgs(["张三", 30]);
$person->sayHello(); // 输出: Hello, my name is 张三.
?>
9.4 对象序列化
序列化将对象转换为字符串,反序列化则将字符串恢复为对象:
<?php
class Person {
public $name;
public $age;
public function __construct($name, $age) {
$this->name = $name;
$this->age = $age;
}
public function sayHello() {
echo "Hello, my name is {$this->name}.";
}
}
// 创建对象
$person = new Person("张三", 30);
// 序列化对象
$serialized = serialize($person);
echo $serialized; // 输出序列化的字符串
// 将序列化的字符串存储到文件
file_put_contents("person.txt", $serialized);
// 从文件读取序列化的字符串
$serialized = file_get_contents("person.txt");
// 反序列化对象
$unserialized = unserialize($serialized);
$unserialized->sayHello(); // 输出: Hello, my name is 张三.
?>
10. 实际应用示例
10.1 基础的 MVC 结构
<?php
// 模型(Model)
class UserModel {
private $db;
public function __construct($db) {
$this->db = $db;
}
public function getUserById($id) {
// 从数据库获取用户
}
public function saveUser($userData) {
// 保存用户到数据库
}
}
// 视图(View)
class UserView {
public function renderUserProfile($user) {
// 渲染用户资料页面
echo "<h1>{$user['name']}</h1>";
echo "<p>Email: {$user['email']}</p>";
}
public function renderUserForm() {
// 渲染用户表单
echo "<form>...</form>";
}
}
// 控制器(Controller)
class UserController {
private $model;
private $view;
public function __construct($model, $view) {
$this->model = $model;
$this->view = $view;
}
public function showProfile($id) {
$user = $this->model->getUserById($id);
$this->view->renderUserProfile($user);
}
public function showEditForm($id) {
$user = $this->model->getUserById($id);
$this->view->renderUserForm($user);
}
public function updateUser($id, $userData) {
$this->model->saveUser($userData);
// 重定向到用户资料页
}
}
// 使用示例
$db = new PDO("mysql:host=localhost;dbname=test", "root", "password");
$userModel = new UserModel($db);
$userView = new UserView();
$userController = new UserController($userModel, $userView);
// 路由决定调用哪个控制器方法
$userController->showProfile(1);
?>
10.2 单例模式
<?php
// 单例模式确保一个类只有一个实例
class Database {
// 保存单例实例的静态属性
private static $instance = null;
// 数据库连接
private $connection;
// 私有构造函数防止外部实例化
private function __construct() {
$this->connection = new PDO("mysql:host=localhost;dbname=test", "root", "password");
}
// 防止克隆对象
private function __clone() {}
// 防止反序列化
private function __wakeup() {}
// 获取单例实例的公共静态方法
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new self();
}
return self::$instance;
}
// 获取数据库连接
public function getConnection() {
return $this->connection;
}
}
// 使用单例
$db1 = Database::getInstance();
$db2 = Database::getInstance();
// $db1 和 $db2 是同一个实例
var_dump($db1 === $db2); // 输出: bool(true)
// 使用连接
$conn = $db1->getConnection();
?>
10.3 工厂模式
<?php
// 产品接口
interface Vehicle {
public function drive();
}
// 具体产品
class Car implements Vehicle {
public function drive() {
echo "Driving a car";
}
}
class Motorcycle implements Vehicle {
public function drive() {
echo "Riding a motorcycle";
}
}
// 工厂类
class VehicleFactory {
// 工厂方法
public static function createVehicle($type) {
switch ($type) {
case 'car':
return new Car();
case 'motorcycle':
return new Motorcycle();
default:
throw new Exception("Unsupported vehicle type");
}
}
}
// 使用工厂创建对象
$car = VehicleFactory::createVehicle('car');
$car->drive(); // 输出: Driving a car
$motorcycle = VehicleFactory::createVehicle('motorcycle');
$motorcycle->drive(); // 输出: Riding a motorcycle
?>
11. 类的最佳实践
11.1 命名规范
- 类名使用大驼峰命名法(PascalCase):
User,DatabaseConnection - 方法名和属性名使用小驼峰命名法(camelCase):
getUserById(),$firstName - 常量名使用全大写加下划线:
MAX_CONNECTIONS,DEFAULT_TIMEOUT - 私有属性和方法可以使用下划线前缀:
$_privateVar,_privateMethod()
11.2 代码组织
- 将每个类放在单独的文件中
- 使用命名空间组织类
- 遵循 PSR 标准(特别是 PSR-1、PSR-4)
- 使用自动加载而非手动引入
11.3 可维护性
- 保持类的职责单一
- 编写清晰的注释和文档
- 使用类型声明提高代码的可靠性
- 避免深度嵌套的继承层次
- 优先使用组合而非继承
- 合理使用设计模式
11.4 安全性
- 不要在类中硬编码敏感信息
- 使用访问控制修饰符保护类的内部状态
- 对输入进行验证和过滤
- 避免魔术方法的滥用
- 注意序列化安全
12. 总结
PHP 的类和面向对象编程提供了强大的工具,使代码更有组织性、可重用性和可维护性。通过合理使用类、继承、接口、抽象类和设计模式,您可以构建复杂且灵活的应用程序。
本指南涵盖了 PHP 类的核心概念和高级特性,包括类的定义与实例化、属性与方法、访问控制、继承、多态、抽象类与接口、设计原则和实际应用示例。通过不断学习和实践,您可以进一步提升您的面向对象编程技能。
更多推荐
所有评论(0)