Object-Oriented Architecture
Classes in RHP represent structured types with explicit property typing and visibility modifiers:
class UserProfile {
public int $id;
public string $name;
private string $email;
public function __construct(int $id, string $name, string $email) {
$this->id = $id;
$this->name = $name;
$this->email = $email;
}
public function getEmail(): string {
return $this->email;
}
}
$user = new UserProfile(1, "Alice", "alice@example.com");
print("User: " . $user->name);
Interfaces and Traits
interface Loggable {
public function logMessage(string $message): void;
}
trait Timestampable {
public string $createdAt;
public function touch(): void {
$this->createdAt = Sys::timestamp();
}
}
class AuditLog implements Loggable {
use Timestampable;
public function logMessage(string $message): void {
Log::info("[Audit] " . $message);
}
}
Persistent Parent Process: singleton class
One of RHP’s signature innovations is the singleton class. Singletons are instantiated once in the persistent parent process and remain shared across all worker threads via Arc<EngineState>:
singleton class DatabaseManager {
private static int $connectionCount = 0;
public static function connect(): void {
self::$connectionCount++;
Log::info("Connected to shared pool");
}
public static function getPoolSize(): int {
return self::$connectionCount;
}
}
Invariants for Singleton Invocation:
- Inside Non-Singleton Classes: You can only invoke static methods on a singleton (e.g.
DatabaseManager::connect()). Callingnew DatabaseManager()or attemptingclass Child extends DatabaseManagerthrows a fatalNonStaticSingletonMethodCallForbiddencompile error. - Inside Other Singleton Classes: Developers may call both static and instance methods directly.
Facade Tiering & Extensibility: Runtime Macros vs. Inheritance
Why Traditional Class Extension (extends Arr) is Forbidden
Native facades (Arr, Str, Future, Memory, HTTP, etc.) are static facades wrapping optimized Rust heap allocations (Box<Vec<PhpValue>>, UTF-8 byte buffers, bump arenas) rather than standard user-land classes. Traditional inheritance (class CustomArr extends Arr) is strictly forbidden:
- Tight Coupling to Rust Internals: Allowing class inheritance would expose internal memory boundaries and struct representations, complicating the Cranelift JIT compiler and VM optimization pipeline.
- Stateless Design Violations: Core facades are intentionally designed as stateless utility wrappers and fluent pipeline builders. Subclassing them introduces state management confusion and severe thread-safety hazards in a multi-threaded Tokio runtime.
Attempting to extend a static facade fails with a fatal compile error:
Fatal Error: Cannot extend static facade class 'Arr'. Use 'Arr::macro()' to register custom methods dynamically.
Dynamic Runtime Macro Registration (::macro)
Instead of extending the class via inheritance, developers register custom methods dynamically:
// Define a custom macro on the Arr facade
Arr::macro('toCsv', function ($delimiter = ',') {
// Custom logic accessing the underlying collection
return implode($delimiter, $this->all());
});
// Usage anywhere in your application
$csv = Arr::list([1, 2, 3])->toCsv();
Why Macros Work Perfectly for RHP
- Zero Performance Cost on Core Types: Macros reside in a global thread-safe runtime registry rather than modifying core class definitions or Rust struct layouts.
- Maintains Familiarity: Matches the exact developer ergonomics of Laravel (
Macroable), making migration painless for teams upgrading performance-critical applications. - Thread-Safe Registration: Macros can be registered once during application bootstrap (
server.rhp) in the parent process, making them safely accessible across all concurrent worker threads with zero mutex contention on request paths.