13 KiB
AOT Compiler Class Inheritance Rules
📋 Overview
The AOT (Ahead-Of-Time) compiler performs strict checks on class inheritance relationships during the compilation stage. Unlike traditional PHP runtime checking, the AOT compiler requires that the existence of all parent classes and interfaces can be determined at compile time.
⚠️ Core Rules
Rule 1: Can Only Inherit Existing Classes
// ✅ Correct: inheriting a PHP built-in class
class MyException extends Exception {}
// ✅ Correct: inheriting a class defined in the same project
class BaseClass {}
class ChildClass extends BaseClass {}
// ❌ Wrong: inheriting a non-existent class
class MyClass extends NonExistentClass {}
// Compile error: Class `MyClass` inherits from a non-existent class `NonExistentClass`
Rule 2: Cannot Inherit Classes Loaded at Runtime by autoload
// ❌ Wrong: a class that depends on autoload
class MyClass extends ExternalLibrary\BaseClass {}
// If BaseClass needs to be loaded by autoload at runtime, compilation will fail
// ✅ Correct: use an interface instead
interface BaseInterface {}
class MyClass implements BaseInterface {}
Rule 3: Cannot Inherit Internal Classes
// ❌ Wrong: inheriting a PHP internal class
class MyArray extends ArrayObject {}
// Compile error: Class `MyArray` Cannot inherit built-in class `ArrayObject`
// ✅ Correct: use the composition pattern
class MyContainer {
private ArrayObject $storage;
public function __construct() {
$this->storage = new ArrayObject();
}
}
🔍 Compile-Time Checking Mechanism
Checking Process
-
Lexical analysis stage
- Parses the
extendsandimplementskeywords - Extracts the names of the parent class and interfaces
- Parses the
-
Symbol table construction
- Collects all classes defined in the current file
- Queries the PHP built-in class list
- Verifies the legality of the inheritance relationship
-
Error reporting
- Reports an error immediately when illegal inheritance is found
- Provides detailed error location and reason
Checking Code Location
The relevant implementation is in src/Php/Translator.php:
if ($extends) {
$parentClass = $this->getParentClass($class->extends);
if ($this->hasNativeClass($parentClass)) {
// The parent class is a defined native class
$parent = $this->getClassDef($parentClass);
if ($parent->flags & Modifiers::FINAL) {
$this->fatalError($class, "Class `{$this->class}` cannot extend final class `{$parentClass}`");
}
$this->classDef->extends = $parentClass;
} else {
// Check whether it is a PHP internal class
if (Reflection::isInternalClass($parentClass)) {
$this->fatalError($class, "Class `{$this->class}` Cannot inherit built-in class `$parentClass`");
} else {
$this->fatalError($class, "Class `{$this->class}` inherits from a non-existent class `$parentClass`");
}
}
}
🎯 Legal Inheritance Examples
Example 1: Inheriting a Class Defined in the Project
<?php
// Define the base class
abstract class Shape {
protected float $area = 0.0;
abstract public function calculateArea(): float;
public function getArea(): float {
return $this->area;
}
}
// Inherit the base class (✅ allowed)
class Circle extends Shape {
private float $radius;
public function __construct(float $radius) {
$this->radius = $radius;
}
public function calculateArea(): float {
$this->area = M_PI * $this->radius ** 2;
return $this->area;
}
}
function main() {
$circle = new Circle(5);
echo "Circle area: " . $circle->getArea() . "\n";
}
Example 2: Implementing Interfaces
<?php
// Define the interfaces
interface Renderable {
public function render(): string;
}
interface Serializable {
public function serialize(): string;
}
// Implement multiple interfaces (✅ allowed)
class Product implements Renderable, Serializable {
private string $name;
private float $price;
public function __construct(string $name, float $price) {
$this->name = $name;
$this->price = $price;
}
public function render(): string {
return "<div>{$this->name} - \${$this->price}</div>";
}
public function serialize(): string {
return json_encode(['name' => $this->name, 'price' => $this->price]);
}
}
function main() {
$product = new Product('Widget', 19.99);
echo $product->render() . "\n";
echo $product->serialize() . "\n";
}
Example 3: Multi-Level Inheritance
<?php
// Base controller
abstract class Controller {
protected array $data = [];
public function setData(string $key, mixed $value): void {
$this->data[$key] = $value;
}
abstract public function execute(): void;
}
// Abstract user controller
abstract class UserController extends Controller {
protected ?string $userId = null;
public function setUserId(string $id): void {
$this->userId = $id;
}
}
// Concrete user list controller (✅ allowed)
class UserListController extends UserController {
private array $users = [];
public function execute(): void {
echo "User List for ID: {$this->userId}\n";
foreach ($this->users as $user) {
echo " - {$user}\n";
}
}
}
function main() {
$controller = new UserListController();
$controller->setUserId('123');
$controller->execute();
}
❌ Illegal Inheritance Examples
Example 1: Inheriting a Non-Existent Class
<?php
// ❌ Wrong: NonExistentBase is not defined
class MyClass extends NonExistentBase {
// ...
}
function main() {
$obj = new MyClass();
}
?>
Compile error:
Fatal error: Class `MyClass` inherits from a non-existent class `NonExistentBase`
in /path/to/file.php on line X
Solution:
<?php
// ✅ Define the base class first
class NonExistentBase {
// ...
}
class MyClass extends NonExistentBase {
// ...
}
Example 2: An External Class That Depends on autoload
<?php
// ❌ Wrong: ExternalLib\BaseClass needs to be loaded by autoload
use ExternalLib\BaseClass;
class MyService extends BaseClass {
// ...
}
function main() {
$service = new MyService();
}
?>
Compile error:
Fatal error: Class `MyService` inherits from a non-existent class `ExternalLib\BaseClass`
in /path/to/file.php on line X
Solution:
Option A - Use an interface:
<?php
interface BaseServiceInterface {
public function process(): mixed;
}
class MyService implements BaseServiceInterface {
public function process(): mixed {
// Implementation logic
}
}
Option B - Use composition:
<?php
use ExternalLib\BaseClass;
class MyService {
private BaseClass $base;
public function __construct() {
$this->base = new BaseClass();
}
public function process(): mixed {
return $this->base->doSomething();
}
}
Example 3: Inheriting a PHP Internal Class
<?php
// ❌ Wrong: cannot inherit PDO
class MyDatabase extends PDO {
// ...
}
function main() {
$db = new MyDatabase('mysql:host=localhost;dbname=test');
}
?>
Compile error:
Fatal error: Class `MyDatabase` Cannot inherit built-in class `PDO`
in /path/to/file.php on line X
Solution:
<?php
// ✅ Use the composition pattern
class MyDatabase {
private PDO $connection;
public function __construct(string $dsn) {
$this->connection = new PDO($dsn);
}
public function query(string $sql): array {
return $this->connection->query($sql)->fetchAll();
}
}
💡 Best Practices
1. Prefer Interfaces over Inheritance
// ❌ Not recommended: deep inheritance chain
class A {}
class B extends A {}
class C extends B {}
class D extends C {}
// ✅ Recommended: use interfaces
interface ServiceInterface {}
class UserService implements ServiceInterface {}
class ProductService implements ServiceInterface {}
2. Use Composition Instead of Inheritance
// ❌ Not recommended: inheriting an internal class
class MyCollection extends ArrayCollection {
// ...
}
// ✅ Recommended: the composition pattern
class Collection {
private ArrayCollection $items;
public function __construct() {
$this->items = new ArrayCollection();
}
public function add(mixed $item): void {
$this->items->add($item);
}
}
3. Define Inheritable Base Classes Within the Project
<?php
// base.stub.php - define an inheritable base class
abstract class BaseController {
protected array $middlewares = [];
abstract public function handle(): void;
protected function middleware(string $name): void {
echo "Running middleware: {$name}\n";
}
}
// api.php - inherit the base class within the project
class UserController extends BaseController {
public function handle(): void {
$this->middleware('auth');
echo "Handling user request\n";
}
}
function main() {
$controller = new UserController();
$controller->handle();
}
4. Avoid Circular Dependencies
// ❌ Wrong: circular inheritance causes compilation to fail
// file1.php
class A extends B {}
// file2.php
class B extends A {}
// ✅ Correct: one-way inheritance
// base.php
class Base {}
// child.php
class Child extends Base {}
🔧 Troubleshooting
Problem 1: Compilation Reports "non-existent class"
Symptom:
Fatal error: Class `MyClass` inherits from a non-existent class `ParentClass`
Diagnostic steps:
- Check whether
ParentClassis defined in the compilation unit - Confirm there are no spelling errors
- Verify that the class namespace is correct
Solution:
// Ensure the parent class is defined before the subclass or in the same file
class ParentClass {}
class MyClass extends ParentClass {}
Problem 2: Cannot Use a Third-Party Library Class as a Parent Class
Symptom: Need to use the functionality of an external library, but cannot inherit it directly
Solution:
Method A - Adapter pattern:
class ExternalAdapter {
private ExternalClass $external;
public function __construct() {
$this->external = new ExternalClass();
}
public function doWork(): mixed {
return $this->external->execute();
}
}
Method B - Delegation pattern:
class Wrapper {
public function __call(string $name, array $args): mixed {
$external = new ExternalClass();
return call_user_func_array([$external, $name], $args);
}
}
Problem 3: Need to Extend PHP Built-in Functionality
Symptom: Want to enhance or modify the behavior of PHP built-in classes
Solution:
Use the decorator pattern:
class EnhancedStorage {
private ArrayObject $storage;
public function __construct() {
$this->storage = new ArrayObject();
}
public function set(string $key, mixed $value): void {
echo "Setting {$key}\n";
$this->storage[$key] = $value;
}
public function get(string $key): mixed {
echo "Getting {$key}\n";
return $this->storage[$key] ?? null;
}
}
📊 Comparison: AOT vs Traditional PHP
| Feature | AOT Compiler | Traditional PHP |
|---|---|---|
| Check timing | Compile time | Runtime |
| Error discovery | Reports an error immediately at compile time | Reports an error only when the code is executed |
| autoload support | ❌ Not supported as a parent class | ✅ Fully supported |
| Inheriting internal classes | ❌ Forbidden | ✅ Allowed (partially) |
| Performance impact | No runtime overhead | Has runtime checking overhead |
📚 Related Resources
- Type System: NATIVE_TYPES.md
- Compatibility Limitations: INCOMPATIBLE_PHP_FEATURES.md
- Compilation Modes: COMPILATION_MODES.md
- Quick Start: QUICKSTART.md
❓ FAQ
Q: Why doesn't the AOT compiler allow inheriting autoload classes?
A: The AOT compiler needs to know the complete structure of all classes at the compilation stage. autoload is a mechanism triggered at runtime, so the compiler cannot obtain class definition information at compile time and therefore cannot generate correct C++ code.
Q: How do I determine whether a class is an internal class?
A: The AOT compiler uses the Reflection::isInternalClass() function to determine this. The function scans all declared classes and uses the ReflectionClass::isInternal() method to detect.
Q: Can interfaces be implemented?
A: ✅ Yes! The AOT compiler fully supports implementing interfaces. Interfaces only define method signatures and do not involve concrete implementation, so they can be verified at compile time.
Q: Can traits be used?
A: The use of traits is likewise restricted. Traits must be visible at compile time and cannot depend on autoload for dynamic loading.
Last updated: March 20, 2024 Applicable version: PHP AOT Compiler v1.x