PHP 基础知识(面向对象)
这部分内容是理解 Hyperf 框架的基础,建议先掌握这些概念再学习框架
1. 面向对象基础
1.1 类和对象
定义类:
<?php
class User
{
// 属性(成员变量)
public $name;
public $email;
private $password; // 私有属性
// 构造函数(创建对象时自动调用)
public function __construct($name, $email, $password)
{
$this->name = $name;
$this->email = $email;
$this->password = password_hash($password, PASSWORD_DEFAULT);
}
// 方法(成员函数)
public function login($inputPassword)
{
return password_verify($inputPassword, $this->password);
}
// 获取器(访问私有属性)
public function getPassword()
{
return $this->password;
}
// 设置器(修改私有属性)
public function setPassword($newPassword)
{
$this->password = password_hash($newPassword, PASSWORD_DEFAULT);
}
}
// 创建对象
$user = new User('张三', 'zhang@example.com', '123456');
// 访问属性
echo $user->name; // 张三
// 调用方法
if ($user->login('123456')) {
echo '登录成功';
}
1.2 访问修饰符
PHP 有三种访问修饰符:
| 修饰符 | 说明 | 访问范围 |
|---|---|---|
| public | 公有 | 任何地方都可以访问 |
| protected | 受保护 | 类内部和子类可以访问 |
| private | 私有 | 只有类内部可以访问 |
示例:
<?php
class User
{
public $name; // 任何地方都可以访问
protected $email; // 类内部和子类可以访问
private $password; // 只有类内部可以访问
public function __construct($name, $email, $password)
{
$this->name = $name;
$this->email = $email;
$this->password = $password;
}
// public 方法:任何地方都可以调用
public function getEmail()
{
return $this->email;
}
// protected 方法:子类可以调用
protected function validateEmail()
{
return filter_var($this->email, FILTER_VALIDATE_EMAIL);
}
// private 方法:只有类内部可以调用
private function hashPassword($password)
{
return password_hash($password, PASSWORD_DEFAULT);
}
}
$user = new User('张三', 'zhang@example.com', '123456');
echo $user->name; // ✅ 可以访问
echo $user->email; // ❌ 错误:不能访问 protected 属性
echo $user->password; // ❌ 错误:不能访问 private 属性
echo $user->getEmail(); // ✅ 通过 public 方法访问
1.3 静态属性和方法
静态成员属于类本身,而不是对象实例。
<?php
class Counter
{
// 静态属性(所有实例共享)
private static $count = 0;
// 静态方法
public static function increment()
{
self::$count++; // 使用 self:: 访问静态成员
}
public static function getCount()
{
return self::$count;
}
// 实例方法
public function instanceMethod()
{
self::$count++; // 实例方法也可以访问静态成员
}
}
// 调用静态方法(不需要创建对象)
Counter::increment();
Counter::increment();
echo Counter::getCount(); // 2
// 创建对象后也可以调用静态方法
$counter = new Counter();
$counter::increment(); // 3
静态方法的限制:
- ❌ 静态方法不能访问非静态属性(因为没有
$this) - ✅ 静态方法可以访问静态属性
- ✅ 非静态方法可以访问静态属性和非静态属性
2. 继承
2.1 基本继承
extends 关键字用于继承父类。
<?php
// 父类(基类)
class Animal
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
public function eat()
{
echo "{$this->name} 正在吃东西\n";
}
public function sleep()
{
echo "{$this->name} 正在睡觉\n";
}
}
// 子类(派生类)
class Dog extends Animal
{
// 子类特有的方法
public function bark()
{
echo "{$this->name} 正在叫:汪汪汪\n";
}
// 重写父类方法
public function eat()
{
echo "{$this->name} 正在吃狗粮\n";
}
}
// 使用
$dog = new Dog('旺财');
$dog->eat(); // 旺财 正在吃狗粮(调用子类的方法)
$dog->sleep(); // 旺财 正在睡觉(调用父类的方法)
$dog->bark(); // 旺财 正在叫:汪汪汪(子类特有的方法)
2.2 parent 关键字
使用 parent:: 调用父类的方法。
<?php
class Animal
{
protected $name;
public function __construct($name)
{
$this->name = $name;
echo "创建动物:{$name}\n";
}
public function eat()
{
echo "{$this->name} 正在吃东西\n";
}
}
class Dog extends Animal
{
private $breed; // 品种
public function __construct($name, $breed)
{
// 调用父类的构造函数
parent::__construct($name);
$this->breed = $breed;
echo "品种:{$breed}\n";
}
public function eat()
{
// 先调用父类的方法
parent::eat();
// 再添加自己的逻辑
echo "狗狗吃得真香\n";
}
}
$dog = new Dog('旺财', '金毛');
// 输出:
// 创建动物:旺财
// 品种:金毛
$dog->eat();
// 输出:
// 旺财 正在吃东西
// 狗狗吃得真香
2.3 final 关键字
final 关键字防止类被继承或方法被重写。
<?php
// final 类:不能被继承
final class Math
{
public static function add($a, $b)
{
return $a + $b;
}
}
class MyMath extends Math // ❌ 错误:不能继承 final 类
{
}
// final 方法:不能被重写
class Animal
{
// final 方法
final public function breathe()
{
echo "呼吸\n";
}
}
class Dog extends Animal
{
// ❌ 错误:不能重写 final 方法
public function breathe()
{
echo "狗在呼吸\n";
}
}
3. 接口(Interface)
3.1 什么是接口?
接口定义了类必须实现的方法,但不包含具体实现。
<?php
// 定义接口
interface PaymentInterface
{
// 接口中的方法都是 public 的
public function pay(float $amount): bool;
public function refund(float $amount): bool;
}
// 实现接口
class AlipayPayment implements PaymentInterface
{
public function pay(float $amount): bool
{
echo "使用支付宝支付 {$amount} 元\n";
return true;
}
public function refund(float $amount): bool
{
echo "支付宝退款 {$amount} 元\n";
return true;
}
}
class WechatPayment implements PaymentInterface
{
public function pay(float $amount): bool
{
echo "使用微信支付 {$amount} 元\n";
return true;
}
public function refund(float $amount): bool
{
echo "微信退款 {$amount} 元\n";
return true;
}
}
// 使用接口(多态)
function processPayment(PaymentInterface $payment, float $amount)
{
$payment->pay($amount);
}
$alipay = new AlipayPayment();
$wechat = new WechatPayment();
processPayment($alipay, 100); // 使用支付宝支付 100 元
processPayment($wechat, 200); // 使用微信支付 200 元
3.2 接口的特点
- ✅ 接口中的方法都是抽象的(没有实现)
- ✅ 接口中的方法都是 public 的
- ✅ 一个类可以实现多个接口
- ✅ 接口可以继承其他接口
- ❌ 接口不能包含属性(PHP 8.0 之前)
3.3 实现多个接口
<?php
interface Flyable
{
public function fly(): void;
}
interface Swimmable
{
public function swim(): void;
}
// 实现多个接口
class Duck implements Flyable, Swimmable
{
public function fly(): void
{
echo "鸭子在飞\n";
}
public function swim(): void
{
echo "鸭子在游泳\n";
}
}
$duck = new Duck();
$duck->fly();
$duck->swim();
3.4 接口继承
<?php
interface Animal
{
public function eat(): void;
}
interface Mammal extends Animal
{
public function giveBirth(): void;
}
// 实现 Mammal 接口时,必须实现 eat() 和 giveBirth()
class Dog implements Mammal
{
public function eat(): void
{
echo "狗在吃东西\n";
}
public function giveBirth(): void
{
echo "狗在生小狗\n";
}
}
4. 抽象类(Abstract Class)
4.1 什么是抽象类?
抽象类是介于普通类和接口之间的一种类,可以包含抽象方法(没有实现)和具体方法(有实现)。
<?php
// 抽象类
abstract class Shape
{
protected $color;
public function __construct($color)
{
$this->color = $color;
}
// 具体方法(有实现)
public function getColor()
{
return $this->color;
}
// 抽象方法(没有实现,子类必须实现)
abstract public function getArea(): float;
abstract public function getPerimeter(): float;
}
// 继承抽象类
class Circle extends Shape
{
private $radius;
public function __construct($color, $radius)
{
parent::__construct($color);
$this->radius = $radius;
}
// 实现抽象方法
public function getArea(): float
{
return pi() * $this->radius * $this->radius;
}
public function getPerimeter(): float
{
return 2 * pi() * $this->radius;
}
}
class Rectangle extends Shape
{
private $width;
private $height;
public function __construct($color, $width, $height)
{
parent::__construct($color);
$this->width = $width;
$this->height = $height;
}
public function getArea(): float
{
return $this->width * $this->height;
}
public function getPerimeter(): float
{
return 2 * ($this->width + $this->height);
}
}
// 使用
$circle = new Circle('红色', 5);
echo "圆形颜色:" . $circle->getColor() . "\n";
echo "圆形面积:" . $circle->getArea() . "\n";
$rect = new Rectangle('蓝色', 10, 20);
echo "矩形面积:" . $rect->getArea() . "\n";
4.2 抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 方法实现 | 可以有具体方法和抽象方法 | 所有方法都是抽象的(PHP 8 前) |
| 属性 | 可以有属性 | 不能有属性(PHP 8.0 前) |
| 继承/实现 | 一个类只能继承一个抽象类 | 一个类可以实现多个接口 |
| 构造函数 | 可以有构造函数 | 不能有构造函数 |
| 访问修饰符 | 可以是 public、protected | 只能是 public |
| 用途 | 代码复用 | 定义规范 |
使用场景:
- 抽象类:有公共的属性和方法需要复用
- 接口:定义规范,多个不同的类实现相同的接口
4.3 接口 + 抽象类组合使用
<?php
// 接口:定义规范
interface StorageInterface
{
public function save(string $key, $value): bool;
public function get(string $key);
public function delete(string $key): bool;
}
// 抽象类:提供公共实现
abstract class AbstractStorage implements StorageInterface
{
protected $prefix = '';
// 公共方法
protected function buildKey(string $key): string
{
return $this->prefix . $key;
}
// 抽象方法:子类必须实现
abstract public function save(string $key, $value): bool;
abstract public function get(string $key);
abstract public function delete(string $key): bool;
}
// 具体实现:Redis 存储
class RedisStorage extends AbstractStorage
{
private $redis;
public function __construct()
{
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
$this->prefix = 'redis:';
}
public function save(string $key, $value): bool
{
$key = $this->buildKey($key); // 使用父类的方法
return $this->redis->set($key, serialize($value));
}
public function get(string $key)
{
$key = $this->buildKey($key);
$value = $this->redis->get($key);
return $value ? unserialize($value) : null;
}
public function delete(string $key): bool
{
$key = $this->buildKey($key);
return $this->redis->del($key) > 0;
}
}
// 具体实现:文件存储
class FileStorage extends AbstractStorage
{
private $path;
public function __construct($path)
{
$this->path = $path;
$this->prefix = 'file:';
}
public function save(string $key, $value): bool
{
$key = $this->buildKey($key);
$filename = $this->path . '/' . md5($key);
return file_put_contents($filename, serialize($value)) !== false;
}
public function get(string $key)
{
$key = $this->buildKey($key);
$filename = $this->path . '/' . md5($key);
if (file_exists($filename)) {
return unserialize(file_get_contents($filename));
}
return null;
}
public function delete(string $key): bool
{
$key = $this->buildKey($key);
$filename = $this->path . '/' . md5($key);
if (file_exists($filename)) {
return unlink($filename);
}
return false;
}
}
// 使用(多态)
function saveData(StorageInterface $storage, string $key, $value)
{
$storage->save($key, $value);
}
$redisStorage = new RedisStorage();
$fileStorage = new FileStorage('/tmp/cache');
saveData($redisStorage, 'user:1', ['name' => '张三']);
saveData($fileStorage, 'user:2', ['name' => '李四']);
5. Trait(特性)
5.1 什么是 Trait?
Trait 是一种代码复用机制,可以在多个类中复用方法。
问题:PHP 只支持单继承,如果多个类需要相同的方法怎么办?
解决方案:使用 Trait
<?php
// 定义 Trait
trait Timestampable
{
protected $createdAt;
protected $updatedAt;
public function setCreatedAt($time)
{
$this->createdAt = $time;
}
public function setUpdatedAt($time)
{
$this->updatedAt = $time;
}
public function getCreatedAt()
{
return $this->createdAt;
}
}
trait Loggable
{
public function log($message)
{
echo "[" . date('Y-m-d H:i:s') . "] {$message}\n";
}
}
// 使用 Trait
class User
{
use Timestampable, Loggable; // 使用多个 Trait
private $name;
public function __construct($name)
{
$this->name = $name;
$this->setCreatedAt(date('Y-m-d H:i:s'));
$this->log("用户 {$name} 已创建");
}
}
$user = new User('张三');
// 输出:[2025-10-23 12:00:00] 用户 张三 已创建
echo $user->getCreatedAt(); // 2025-10-23 12:00:00
5.2 Trait 冲突解决
当使用多个 Trait,且它们有同名方法时,需要解决冲突。
<?php
trait A
{
public function sayHello()
{
echo "Hello from A\n";
}
}
trait B
{
public function sayHello()
{
echo "Hello from B\n";
}
}
class MyClass
{
use A, B {
A::sayHello insteadof B; // 使用 A 的方法,忽略 B 的
B::sayHello as sayHelloB; // 给 B 的方法起别名
}
}
$obj = new MyClass();
$obj->sayHello(); // Hello from A
$obj->sayHelloB(); // Hello from B
6. 命名空间(Namespace)
6.1 什么是命名空间?
命名空间用于解决类名冲突问题,类似于文件系统的目录。
<?php
// 文件:App/Controller/UserController.php
namespace App\Controller;
class UserController
{
public function index()
{
echo "用户控制器\n";
}
}
// 文件:App/Service/UserService.php
namespace App\Service;
class UserService
{
public function getUsers()
{
return ['张三', '李四'];
}
}
// 文件:index.php
// 方式一:使用完整命名空间
$controller = new \App\Controller\UserController();
// 方式二:使用 use 导入
use App\Controller\UserController;
use App\Service\UserService;
$controller = new UserController();
$service = new UserService();
// 方式三:使用别名
use App\Controller\UserController as UC;
use App\Service\UserService as US;
$controller = new UC();
$service = new US();
6.2 子命名空间
<?php
namespace App\Controller\Admin; // 子命名空间
class UserController
{
// ...
}
// 使用
use App\Controller\Admin\UserController;
6.3 全局类的访问
<?php
namespace App\Service;
class UserService
{
public function sendEmail()
{
// 访问全局类(PHP 内置类)需要加反斜杠
$date = new \DateTime(); // ✅ 正确
$date = new DateTime(); // ❌ 错误:会查找 App\Service\DateTime
}
}
7. 类型声明(Type Hinting)
7.1 参数类型声明
<?php
class UserService
{
// 类型声明:参数必须是指定类型
public function createUser(
string $name, // 字符串
int $age, // 整数
float $balance, // 浮点数
bool $isActive, // 布尔值
array $tags, // 数组
User $friend // 对象
) {
// ...
}
}
// 调用
$service->createUser(
'张三',
25,
100.50,
true,
['tag1', 'tag2'],
new User()
);
// ❌ 错误调用
$service->createUser('张三', '25', ...); // 错误:age 必须是 int
7.2 返回值类型声明
<?php
class UserService
{
// 声明返回值类型
public function getUserById(int $id): ?User // 返回 User 或 null
{
return User::find($id);
}
public function getUserList(): array // 返回数组
{
return User::all()->toArray();
}
public function isAdmin(int $userId): bool // 返回布尔值
{
$user = User::find($userId);
return $user && $user->role === 'admin';
}
}
7.3 联合类型(PHP 8.0+)
<?php
class UserService
{
// 参数可以是 int 或 string
public function getUserById(int|string $id): User|null
{
return User::find($id);
}
// 返回 int 或 float 或 null
public function calculate(): int|float|null
{
return 100;
}
}
7.4 严格类型模式
<?php
declare(strict_types=1); // 开启严格类型模式
function add(int $a, int $b): int
{
return $a + $b;
}
add(1, 2); // ✅ 正确
add(1.5, 2.5); // ❌ 错误:严格模式下不会自动转换类型
add('1', '2'); // ❌ 错误:严格模式下不会自动转换类型
8. 魔术方法
8.1 常用魔术方法
<?php
class User
{
private $data = [];
// 构造函数:创建对象时调用
public function __construct($name, $email)
{
$this->data['name'] = $name;
$this->data['email'] = $email;
}
// 析构函数:对象销毁时调用
public function __destruct()
{
echo "对象被销毁\n";
}
// 获取不存在的属性时调用
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}\n";
echo "参数:" . json_encode($arguments) . "\n";
}
// 调用不存在的静态方法时调用
public static function __callStatic($name, $arguments)
{
echo "调用静态方法:{$name}\n";
}
// 对象转字符串时调用
public function __toString()
{
return "User: {$this->data['name']}";
}
// 对象被当作函数调用时
public function __invoke($arg)
{
echo "对象被调用,参数:{$arg}\n";
}
}
// 使用
$user = new User('张三', 'zhang@example.com');
echo $user->name; // 张三(调用 __get)
$user->age = 25; // 调用 __set
echo $user->age; // 25(调用 __get)
echo isset($user->age); // true(调用 __isset)
unset($user->age); // 调用 __unset
$user->unknownMethod('arg1', 'arg2'); // 调用 __call
User::unknownStatic(); // 调用 __callStatic
echo $user; // User: 张三(调用 __toString)
$user('参数'); // 对象被调用,参数:参数(调用 __invoke)
8.2 __clone(克隆对象)
<?php
class User
{
public $name;
public $friend; // 对象属性
public function __construct($name)
{
$this->name = $name;
}
// 克隆对象时调用
public function __clone()
{
// 深拷贝:同时克隆对象属性
if ($this->friend) {
$this->friend = clone $this->friend;
}
echo "对象被克隆\n";
}
}
$user1 = new User('张三');
$user1->friend = new User('李四');
// 克隆对象
$user2 = clone $user1;
$user2->name = '王五';
$user2->friend->name = '赵六';
echo $user1->name; // 张三(原对象未受影响)
echo $user1->friend->name; // 李四(深拷贝,原对象未受影响)
echo $user2->name; // 王五
echo $user2->friend->name; // 赵六
9. 多态
9.1 什么是多态?
多态:同一个方法调用,不同的对象有不同的实现。
<?php
interface PaymentInterface
{
public function pay(float $amount): bool;
}
class AlipayPayment implements PaymentInterface
{
public function pay(float $amount): bool
{
echo "支付宝支付 {$amount} 元\n";
return true;
}
}
class WechatPayment implements PaymentInterface
{
public function pay(float $amount): bool
{
echo "微信支付 {$amount} 元\n";
return true;
}
}
class CreditCardPayment implements PaymentInterface
{
public function pay(float $amount): bool
{
echo "信用卡支付 {$amount} 元\n";
return true;
}
}
// 多态:同一个方法,不同的实现
class OrderService
{
public function checkout(PaymentInterface $payment, float $amount)
{
// 不关心具体是哪种支付方式,只要实现了 PaymentInterface 即可
if ($payment->pay($amount)) {
echo "支付成功\n";
}
}
}
$orderService = new OrderService();
// 使用不同的支付方式
$orderService->checkout(new AlipayPayment(), 100);
$orderService->checkout(new WechatPayment(), 200);
$orderService->checkout(new CreditCardPayment(), 300);
9.2 多态的优势
- ✅ 扩展性好:新增支付方式无需修改 OrderService
- ✅ 灵活性高:可以动态选择支付方式
- ✅ 易于测试:可以注入 Mock 对象
10. 依赖注入(重要!)
10.1 什么是依赖注入?
依赖注入:将类所依赖的对象从外部传入,而不是在类内部创建。
❌ 不使用依赖注入(高耦合):
<?php
class UserController
{
private $userService;
public function __construct()
{
// 在类内部创建依赖
$this->userService = new UserService();
}
public function getUsers()
{
return $this->userService->getList();
}
}
// 问题:
// 1. UserController 直接依赖 UserService 的具体实现
// 2. 无法替换为其他实现(如 MockUserService)
// 3. 难以进行单元测试
✅ 使用依赖注入(低耦合):
<?php
class UserController
{
private $userService;
// 从外部注入依赖
public function __construct(UserServiceInterface $userService)
{
$this->userService = $userService;
}
public function getUsers()
{
return $this->userService->getList();
}
}
// 使用
$userService = new UserService();
$controller = new UserController($userService);
// 或者用于测试
$mockService = new MockUserService();
$controller = new UserController($mockService); // 可以轻松替换实现
10.2 依赖注入容器
手动注入的问题:如果依赖很多,创建对象会很麻烦。
<?php
// UserService 依赖 Database
$database = new Database();
$userService = new UserService($database);
// UserController 依赖 UserService 和 LogService
$logService = new LogService();
$controller = new UserController($userService, $logService);
// 依赖层级很深时,创建对象非常繁琐
DI 容器:自动创建和注入依赖。
<?php
// Hyperf 的 DI 容器会自动处理依赖关系
$container = \Hyperf\Context\ApplicationContext::getContainer();
// 自动创建 UserController 及其所有依赖
$controller = $container->get(UserController::class);
11. PHP 8 新特性
11.1 属性(Attributes)
PHP 8 引入了属性(Attributes),用于给类、方法、属性添加元数据。
<?php
use Attribute;
// 定义属性
#[Attribute(Attribute::TARGET_METHOD)]
class Route
{
public function __construct(
public string $path,
public string $method = 'GET'
) {
}
}
// 使用属性
class UserController
{
#[Route(path: '/api/users', method: 'GET')]
public function index()
{
return ['users' => []];
}
#[Route(path: '/api/users', method: 'POST')]
public function create()
{
return ['id' => 1];
}
}
// 读取属性(反射)
$reflection = new ReflectionClass(UserController::class);
$methods = $reflection->getMethods();
foreach ($methods as $method) {
$attributes = $method->getAttributes(Route::class);
foreach ($attributes as $attribute) {
$route = $attribute->newInstance();
echo "{$route->method} {$route->path} → {$method->getName()}\n";
}
}
// 输出:
// GET /api/users → index
// POST /api/users → create
11.2 构造器属性提升
<?php
// PHP 7
class User
{
private $name;
private $email;
public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}
}
// PHP 8(更简洁)
class User
{
public function __construct(
private string $name,
private string $email
) {
// 自动创建属性并赋值
}
}
11.3 命名参数
<?php
class User
{
public function __construct(
public string $name,
public string $email,
public int $age = 0,
public bool $isActive = true
) {
}
}
// 传统方式
$user = new User('张三', 'zhang@example.com', 25, true);
// PHP 8:命名参数(可以跳过默认参数)
$user = new User(
name: '张三',
email: 'zhang@example.com'
);
$user = new User(
email: 'zhang@example.com',
name: '张三',
age: 25
);
12. 要点
必须掌握
- 类和对象的概念
- 访问修饰符(public、protected、private)
- 继承和多态
- 接口和抽象类的区别
- 依赖注入的概念
- 命名空间的使用
加分项
- Trait 的使用场景
- 魔术方法的应用
- PHP 8 新特性(属性、联合类型)
- 设计模式(工厂、单例、策略等)
高频题
1. 接口和抽象类有什么区别?
答:
- 接口:定义规范,所有方法都是抽象的,一个类可以实现多个接口
- 抽象类:可以有具体方法和抽象方法,一个类只能继承一个抽象类
- 使用场景:接口定义规范,抽象类提供公共实现
2. 什么是依赖注入?
答:将类所依赖的对象从外部传入,而不是在类内部创建。
优势:
- 降低耦合度
- 便于测试
- 便于替换实现
3. private、protected、public 有什么区别?
答:
- public:任何地方都可以访问
- protected:类内部和子类可以访问
- private:只有类内部可以访问
4. Trait 有什么用?
答:Trait 是一种代码复用机制,可以在多个类中复用方法,解决 PHP 单继承的限制。
下一步:阅读 01-核心概念.md 开始学习 Hyperf