A class has one parent but may implement any number of interfaces, and an interface may extends several others, which builds a large contract from small ones. Interfaces may carry constants, read through the interface or any implementing class. Since PHP 8.1 a class may override an inherited interface constant unless it is final; since 8.3 constants can be typed (Typed Class Constants).
<?php
interface HasSku { final public const string PREFIX = 'BK-'; public function sku(): string; }
interface Taxable { public const int TAX = 6; public function price(): int; }
interface Sellable extends HasSku, Taxable {} // combines two contracts
interface Shippable { public function price(): int; public function grams(): int; }
abstract class Book implements Sellable {
public function __construct(protected string $code, protected int $cents) {}
public function sku(): string { return self::PREFIX . $this->code; }
public function price(): int { return intdiv($this->cents * (100 + static::TAX), 100); }
}
final class Paperback extends Book implements Shippable { // price() serves both
public function grams(): int { return 410; }
}
final class Ebook extends Book { public const int TAX = 8; } // override: PHP 8.1+
foreach ([new Paperback('101', 2500), new Ebook('102', 1500)] as $b) {
echo $b->sku(), ' ', $b::class, ' tax ', $b::TAX, '% ', $b->price(), "\n";
}Output
BK-101 Paperback tax 6% 2650 BK-102 Ebook tax 8% 1620
Both Taxable and Shippable declare price(), and one method satisfies both; redefining PREFIX would stop the script with Ebook::PREFIX cannot override final constant HasSku::PREFIX. Older tutorials forbid both shared method names (allowed since PHP 5.3.9) and overridden interface constants (allowed since 8.1).