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 教程。