Invariant PHP is a native PHP extension that brings Design by Contract (DbC) principles to PHP. It automatically enforces class invariants and ensures all typed properties are initialized without relying on reflection.
Released under the PHP License 3.01.
- Class Invariants (
__invariant()) – Ensures an object's state remains valid before and after each method call. - Enforced Initialization – Detects uninitialized typed properties and raises a fatal error before
__invariant()runs. - Native PHP Extension – Written in C, avoiding overhead from reflection or AOP.
- Transparent Execution – Just define
__invariant()and you are good to go.
PHP lacks built-in support for Design by Contract. Existing solutions (like PHPDeal) rely on runtime reflection or AOP hacks, which can be slow or brittle. Invariant PHP hooks directly into the Zend Engine.
<?php
class BankAccount {
// Typed properties must be initialized or you'll get a fatal error!
private int $balance;
public function __construct(int $balance) {
$this->balance = $balance;
}
public function withdraw(int $amount) {
$this->balance -= $amount;
}
public function __invariant(): void {
if ($this->balance < 0) {
throw new \LogicException("Invariant failed: Balance cannot be negative.");
}
}
}
$account = new BankAccount(100);
$account->withdraw(50); // ✅ Works fine
$account->withdraw(100); // ❌ Fatal error or LogicException if below zeroRight now, Invariant PHP doesn’t explicitly detect or prevent invariant loops, so if an __invariant() method calls another method that triggers __invariant() again, it could lead to unintended recursion.
I plan to address this by:
- Tracking active invariant checks per object to detect re-entrant calls.
- Throwing an error or skipping nested invariant checks when recursion is detected.
- Ensuring performance remains unaffected in non-recursive cases.
Currently, you must build from source:
phpize
./configure
make -j$(nproc)
sudo make installThen enable it in php.ini:
extension=invariant_php.so- Hooks the Zend Engine by overriding
zend_execute_ex. - Checks typed properties for initialization before calling
__invariant(). - Throws a fatal error if any typed property is uninitialized.
- Automatically calls
__invariant()before & after each method execution.
- Support
requires()(preconditions). - Support
ensures()(postconditions). - Optimization & caching for better performance.
- PECL package for easy installation.
Want to contribute? PRs and discussions are welcome! 🎯