PHP 接口
上次修改时间:2025 年 5 月 18 日
PHP 中的接口定义了类必须遵循的契约。它们指定了类必须实现哪些方法,而没有定义这些方法应该如何工作。接口实现多态性,允许不同的类可以互换使用,如果它们实现了相同的接口。
基本接口
一个简单的接口定义了实现类必须提供的方法签名。接口本身不包含方法实现。
<?php
declare(strict_types=1);
interface Logger {
public function log(string $message): void;
}
class FileLogger implements Logger {
public function log(string $message): void {
echo "Logging to file: $message\n";
}
}
class DatabaseLogger implements Logger {
public function log(string $message): void {
echo "Logging to database: $message\n";
}
}
$fileLogger = new FileLogger();
$fileLogger->log("Error occurred!");
$dbLogger = new DatabaseLogger();
$dbLogger->log("Error occurred!");
此示例显示了一个基本的 Logger 接口,该接口具有 FileLogger 和 DatabaseLogger 都实现的一个方法。虽然它们执行不同的操作,但这两个类都遵守相同的接口契约。
λ php basic_interface.php Logging to file: Error occurred! Logging to database: Error occurred!
多接口
一个类可以实现多个接口,从而允许它履行多个契约。这提供了比继承更大的灵活性,因为 PHP 不支持类的多重继承。
<?php
declare(strict_types=1);
interface Loggable {
public function log(string $message): void;
}
interface Cacheable {
public function cache(string $key, string $value): void;
public function getFromCache(string $key): ?string;
}
class Application implements Loggable, Cacheable {
private array $cache = [];
public function log(string $message): void {
echo "Application log: $message\n";
}
public function cache(string $key, string $value): void {
$this->cache[$key] = $value;
}
public function getFromCache(string $key): ?string {
return $this->cache[$key] ?? null;
}
}
$app = new Application();
$app->log("Starting up");
$app->cache("config", "debug=true");
echo $app->getFromCache("config") . "\n";
Application 类实现了 Loggable 和 Cacheable 接口,为所有必需的方法提供了实现。这允许该类用于期望任何一个接口的上下文中。
λ php multiple_interfaces.php Application log: Starting up debug=true
接口继承
接口可以扩展其他接口,继承它们的方法签名。这允许创建契约层次结构。
<?php
declare(strict_types=1);
interface Readable {
public function read(): string;
}
interface Writable {
public function write(string $data): void;
}
interface ReadWritable extends Readable, Writable {
public function clear(): void;
}
class FileStorage implements ReadWritable {
public function read(): string {
return "Reading data from file";
}
public function write(string $data): void {
echo "Writing data to file: $data\n";
}
public function clear(): void {
echo "Clearing file storage\n";
}
}
$storage = new FileStorage();
echo $storage->read() . "\n";
$storage->write("Test data");
$storage->clear();
ReadWritable 接口扩展了 Readable 和 Writable 接口,并添加了一个额外的方法。FileStorage 类必须实现所有三个接口中的所有方法。
λ php interface_inheritance.php Reading data from file Writing data to file: Test data Clearing file storage
使用接口实现多态性
接口实现多态性——如果不同的类实现了相同的接口,就可以互换使用它们。这对于创建灵活的系统非常强大。
<?php
declare(strict_types=1);
interface PaymentGateway {
public function processPayment(float $amount): bool;
}
class StripeGateway implements PaymentGateway {
public function processPayment(float $amount): bool {
echo "Processing Stripe payment for \$$amount\n";
return true;
}
}
class PayPalGateway implements PaymentGateway {
public function processPayment(float $amount): bool {
echo "Processing PayPal payment for \$$amount\n";
return true;
}
}
class OrderProcessor {
public function __construct(private PaymentGateway $gateway) {}
public function processOrder(float $amount): bool {
return $this->gateway->processPayment($amount);
}
}
// Can use either payment gateway interchangeably
$stripeProcessor = new OrderProcessor(new StripeGateway());
$stripeProcessor->processOrder(100.50);
$paypalProcessor = new OrderProcessor(new PayPalGateway());
$paypalProcessor->processOrder(75.25);
OrderProcessor 不需要知道它正在使用哪个特定的支付网关——它只关心它是否实现了 PaymentGateway 接口。这使得代码更灵活,更易于维护。
λ php polymorphism.php Processing Stripe payment for $100.5 Processing PayPal payment for $75.25
接口与抽象类
虽然相似,但接口和抽象类的用途不同。接口定义了没有实现的契约,而抽象类可以提供部分实现。
<?php
declare(strict_types=1);
// Interface - pure contract
interface Logger {
public function log(string $message): void;
}
// Abstract class - can provide some implementation
abstract class AbstractLogger {
protected string $logPrefix = '';
abstract public function log(string $message): void;
public function setPrefix(string $prefix): void {
$this->logPrefix = $prefix;
}
}
class FileLogger extends AbstractLogger implements Logger {
public function log(string $message): void {
echo "[{$this->logPrefix}] Log to file: $message\n";
}
}
$logger = new FileLogger();
$logger->setPrefix("FILE");
$logger->log("Test message");
此示例显示了一个类,该类既扩展了一个抽象类,又实现了一个接口。抽象类提供了一些共享功能 (setPrefix),而接口保证 log 方法存在。
λ php comparison.php [FILE] Log to file: Test message
接口与 Traits
虽然接口定义了类必须遵循的契约,但 traits 是 PHP 中代码重用的机制。Traits 允许你在多个类中包含方法,帮助避免代码重复。与接口不同,Traits 可以提供实际的方法实现,但它们不能定义契约或独立实例化。
<?php
declare(strict_types=1);
interface Logger {
public function log(string $message): void;
}
trait FileLogTrait {
public function logToFile(string $message): void {
echo "File log: $message\n";
}
}
class MyService implements Logger {
use FileLogTrait;
public function log(string $message): void {
// Use trait method for actual logging
$this->logToFile($message);
}
}
$service = new MyService();
$service->log("Hello from trait!");
在此示例中,Logger 接口定义了一个契约,而 FileLogTrait trait 提供了一个可重用的方法。MyService 类实现了该接口并使用 trait 来履行该契约。
- 当你想定义多个类必须遵循的契约时,请使用接口。
- 当你想在多个类之间共享方法实现而无需继承时,请使用 traits。
Traits 和接口可以结合使用,以在 PHP 应用程序中实现最大的灵活性和代码重用。
使用接口进行类型提示
接口对于类型提示参数、返回类型和属性特别有用。这确保代码适用于接口的任何实现。
<?php
declare(strict_types=1);
interface Notifier {
public function sendNotification(string $message): void;
}
class EmailNotifier implements Notifier {
public function sendNotification(string $message): void {
echo "Sending email: $message\n";
}
}
class SMSNotifier implements Notifier {
public function sendNotification(string $message): void {
echo "Sending SMS: $message\n";
}
}
class NotificationService {
public function __construct(private Notifier $notifier) {}
public function notify(string $message): void {
$this->notifier->sendNotification($message);
}
}
$emailService = new NotificationService(new EmailNotifier());
$emailService->notify("Hello via email");
$smsService = new NotificationService(new SMSNotifier());
$smsService->notify("Hello via SMS");
NotificationService 不关心它收到哪个特定的通知程序——它只需要它实现 Notifier 接口。这使得该服务更加灵活且可测试。
λ php type_hinting.php Sending email: Hello via email Sending SMS: Hello via SMS
最佳实践
遵循使用接口时的最佳实践可以产生更简洁、更易于维护的代码。以下是一些关键指南。
<?php
declare(strict_types=1);
// 1. Keep interfaces small and focused (Single Responsibility Principle)
interface UserAuthenticator {
public function authenticate(string $username, string $password): bool;
}
// 2. Name interfaces clearly (often with adjective or -able suffix)
interface Cacheable {
public function cache(string $key, mixed $value): void;
public function getCached(string $key): mixed;
}
// 3. Use interfaces for dependency injection
class OrderService {
public function __construct(private Logger $logger) {}
public function processOrder(Order $order): void {
$this->logger->log("Processing order #{$order->getId()}");
// Process order...
}
}
// 4. Document interface methods with PHPDoc
interface DataTransformer {
/**
* Transforms data from one format to another
* @param mixed $input The input data to transform
* @return mixed The transformed data
* @throws InvalidArgumentException If input is invalid
*/
public function transform($input);
}
// 5. Prefer interface type hints over concrete classes
function sendAlert(Notifier $notifier, string $message): void {
$notifier->sendNotification($message);
}
// 6. Consider using interfaces for testing/mocking
interface DatabaseConnection {
public function query(string $sql): array;
}
// In tests:
class MockDatabaseConnection implements DatabaseConnection {
public function query(string $sql): array {
return ['test' => 'data']; // Return test data without real DB
}
}
这些实践有助于创建易于理解、实现和维护的接口。小型、命名良好的接口以及良好的文档使代码更灵活且更具可测试性。
- PHP 接口:增强代码灵活性
- 定义类必须遵循的契约。
- 实现多态性和整洁的架构。
- 接口的基本优势
- 在组件之间定义清晰的 API。
- 启用依赖注入以实现更好的模块化。
- 通过模拟机制支持可测试的代码。
- 通过多态性创建灵活的系统。
- 需要记住的要点
- 接口定义了方法签名,但没有实现。
- 类可以实现多个接口以实现多功能性。
- 接口可以扩展其他接口以增强功能。
- 使用接口进行类型提示可确保最大的灵活性。
- 更喜欢小型、专注的接口,而不是大型、复杂的接口。
在本教程中,我们探讨了 PHP 中接口的概念。我们讨论了如何定义和实现接口,它们对代码组织的好处,以及它们如何实现多态性。我们还将接口与抽象类进行了比较,重点介绍了它们之间的区别以及何时使用它们。最后,我们介绍了使用接口的最佳实践,以确保代码的整洁和可维护性。
作者
列出 所有 PHP 教程。