PHP8 类和接口:面向对象编程的核心
PHP8 对面向对象编程(OOP)进行了多项增强,引入了新特性并改进了现有功能,使类和接口的使用更加灵活和强大。本文将详细介绍 PHP8 中类和接口的定义、特性及新功能。
类的基本概念与定义
类是面向对象编程的基本构建块,用于封装数据和操作数据的方法。
1. 基本类定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php class Person { public $name; public $age; public function __construct(string $name, int $age) { $this->name = $name; $this->age = $age; } public function greet() { return "Hello, my name is {$this->name} and I'm {$this->age} years old."; } public function birthday() { $this->age++; } }
$person = new Person("John", 30); echo $person->greet();
$person->birthday(); echo $person->greet(); ?>
|
2. 属性与访问修饰符
PHP 支持三种访问修饰符,用于控制类成员的访问权限:
public:公共成员,可以在任何地方访问
protected:受保护成员,只能在类内部和子类中访问
private:私有成员,只能在类内部访问
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php class BankAccount { public $accountNumber; protected $balance; private $pinCode; public function __construct(string $accountNumber, float $balance, string $pinCode) { $this->accountNumber = $accountNumber; $this->balance = $balance; $this->pinCode = $pinCode; } public function getBalance(string $pin) { if ($this->verifyPin($pin)) { return $this->balance; } return "Invalid PIN"; } protected function addFunds(float $amount) { $this->balance += $amount; } private function verifyPin(string $pin) { return $pin === $this->pinCode; } }
$account = new BankAccount("123456", 1000.00, "1234"); echo $account->accountNumber; echo $account->getBalance("1234");
?>
|
PHP8 类的新特性
1. 构造方法属性提升(PHP8.0+)
PHP8 引入了构造方法属性提升,允许在构造方法参数中直接声明类属性,简化代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php
class UserOld { public $name; public $email; public function __construct(string $name, string $email) { $this->name = $name; $this->email = $email; } }
class UserNew { public function __construct( public string $name, public string $email ) { } }
$user = new UserNew("John Doe", "john@example.com"); echo $user->name; echo $user->email; ?>
|
2. 只读属性(PHP8.1+)
PHP8.1 引入了 readonly 关键字,用于声明只能初始化一次且不能修改的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| <?php class Product { public readonly string $name; public readonly float $price; public function __construct(string $name, float $price) { $this->name = $name; $this->price = $price; } }
$product = new Product("Laptop", 999.99); echo $product->name;
?>
|
3. 联合类型(PHP8.0+)
PHP8 支持联合类型,表示一个参数或返回值可以是多种类型中的一种。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| <?php class Calculator { public function add(int|float $a, int|float $b): int|float { return $a + $b; } }
$calc = new Calculator(); echo $calc->add(2, 3); echo $calc->add(2.5, 3.5); echo $calc->add(2, 3.5); ?>
|
4. 命名参数(PHP8.0+)
PHP8 支持命名参数,允许在调用方法时通过参数名指定值,而不必严格按照参数顺序。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <?php class Message { public function send(string $to, string $subject, string $body): void { echo "Sending to: $to\n"; echo "Subject: $subject\n"; echo "Body: $body\n"; } }
$message = new Message();
$message->send( subject: "Hello", to: "john@example.com", body: "This is a test message" ); ?>
|
继承与多态
1. 类的继承
PHP 支持单继承,使用 extends 关键字实现类的继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <?php class Animal { protected string $name; public function __construct(string $name) { $this->name = $name; } public function getName(): string { return $this->name; } public function makeSound(): string { return "Some generic sound"; } }
class Dog extends Animal { public function makeSound(): string { return "Woof! Woof!"; } public function fetch(): string { return "{$this->name} is fetching the ball"; } }
$dog = new Dog("Buddy"); echo $dog->getName(); echo $dog->makeSound(); echo $dog->fetch(); ?>
|
2. 抽象类与抽象方法
抽象类使用 abstract 关键字声明,不能被实例化,只能被继承。抽象方法没有实现,必须在子类中实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?php abstract class Shape { protected string $color; public function __construct(string $color) { $this->color = $color; } public function getColor(): string { return $this->color; } abstract public function getArea(): float; }
class Circle extends Shape { private float $radius; public function __construct(string $color, float $radius) { parent::__construct($color); $this->radius = $radius; } public function getArea(): float { return pi() * $this->radius * $this->radius; } }
$circle = new Circle("red", 5); echo $circle->getColor(); echo $circle->getArea(); ?>
|
接口
接口定义了类应该实现的方法,但不提供方法的实现。类使用 implements 关键字实现接口。
1. 基本接口定义与实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?php
interface Logger { public function log(string $message): void; public function error(string $message): void; }
class FileLogger implements Logger { private string $filename; public function __construct(string $filename) { $this->filename = $filename; } public function log(string $message): void { $this->writeToFile("LOG: " . $message); } public function error(string $message): void { $this->writeToFile("ERROR: " . $message); } private function writeToFile(string $message): void { $timestamp = date('Y-m-d H:i:s'); file_put_contents( $this->filename, "[$timestamp] $message\n", FILE_APPEND ); } }
$logger = new FileLogger('app.log'); $logger->log('Application started'); $logger->error('Something went wrong'); ?>
|
2. 接口继承
接口可以继承其他接口,使用 extends 关键字。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?php interface Readable { public function read(): string; }
interface Writable { public function write(string $data): void; }
interface ReadWriteable extends Readable, Writable { public function flush(): void; }
class FileHandler implements ReadWriteable { private string $filename; public function __construct(string $filename) { $this->filename = $filename; } public function read(): string { return file_get_contents($this->filename); } public function write(string $data): void { file_put_contents($this->filename, $data); } public function flush(): void { } } ?>
|
3. PHP8 接口新特性
(1)接口中的常量可见性(PHP8.1+)
PHP8.1 允许为接口常量指定可见性修饰符(只能是 public)。
1 2 3 4 5 6 7
| <?php interface Database { public const VERSION = '1.0'; const HOST = 'localhost'; } ?>
|
(2)静态返回类型(PHP8.0+)
PHP8 允许在接口中使用 self 和 parent 作为静态返回类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| <?php interface Builder { public static function create(): self; public function setName(string $name): self; public function build(); }
class ProductBuilder implements Builder { private string $name = ''; public static function create(): self { return new self(); } public function setName(string $name): self { $this->name = $name; return $this; } public function build() { return [ 'name' => $this->name ]; } }
$product = ProductBuilder::create() ->setName("Laptop") ->build();
print_r($product); ?>
|
self 是 PHP 中用于指代当前类本身的关键字,主要在类的内部使用,用于访问类的静态成员(静态属性、静态方法)和类常量,也可用于调用当前类的构造方法
主要用法:
- 访问类常量
- 访问静态属性和静态方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php class MyClass { const VERSION = '1.0'; public static $count = 0; public function __construct() { self::$count++; } public static function getVersion() { return self::VERSION; } public function getCount() { return self::$count; } }
echo MyClass::getVersion();
$obj1 = new MyClass(); $obj2 = new MyClass();
echo MyClass::$count;
echo $obj1->getCount(); ?>
|
-> 操作符: 用法是 $对象->成员 可以访问 实例属性、实例方法(非静态)
:: 操作符: 用法是 类名::成员 静态属性、静态方法、类常量
特质(Traits)
特质是一种代码复用机制,介于类和接口之间,用于解决 PHP 单继承的限制。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| <?php
trait Loggable { public function log(string $message): void { $class = static::class; echo "[$class] $message\n"; } }
trait Timestampable { private $createdAt; public function getCreatedAt() { return $this->createdAt; } public function setCreatedAt($timestamp) { $this->createdAt = $timestamp; } }
class Article { use Loggable, Timestampable; private string $title; public function __construct(string $title) { $this->title = $title; $this->setCreatedAt(time()); $this->log("Article created: $title"); } }
$article = new Article("PHP8 Features"); echo "Created at: " . $article->getCreatedAt(); ?>
|
如果使用的多个特质中有相同的方法,就会报错
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| trait Loggable { public function log(string $message): void { $class = static::class; echo "[$class] $message\n"; } }
trait Timestampable { private $createdAt; public function getCreatedAt() { return $this->createdAt; } public function setCreatedAt($timestamp) { $this->createdAt = $timestamp; }
public function log(string $message): void { $class = static::class; echo "[$this->createdAt] [$class] $message\n"; } }
class Article { use Loggable, Timestampable; private string $title; public function __construct(string $title) { $this->title = $title; $this->setCreatedAt(time()); $this->log("Article created: $title"); } }
$article = new Article("PHP8 Features"); echo "Created at: " . $article->getCreatedAt();
|
在Timestampable中也加入log方法,此时调用log就会报错,因为不知道用哪个log。
可以在使用特质的时候改成
1 2 3
| use Loggable, Timestampable{ Timestampable::log insteadof Loggable; }
|
就会使用Timestampable中的log方法了
类与接口的最佳实践
- 单一职责原则:一个类应该只负责一项功能,使类更加简洁和易于维护。
- 面向接口编程:依赖接口而非具体实现,提高代码的灵活性和可扩展性。
- 合理使用访问修饰符:根据需要选择合适的访问级别,隐藏内部实现细节,只暴露必要的接口。
- 优先使用组合而非继承:通过组合多个类的功能来实现复杂功能,而不是多层继承。
- 使用类型提示:为方法参数和返回值添加类型提示,提高代码的可读性和健壮性。
- 避免深度继承:继承层次过深会导致代码复杂,难以理解和维护。
v1.3.10