https://github.com/andreamancuso/invariant-php
Native PHP extension for Design by Contract. Enforces class invariants with minimal runtime overhead.
https://github.com/andreamancuso/invariant-php
dbc designbycontract php php-extension
Last synced: 4 months ago
JSON representation
Native PHP extension for Design by Contract. Enforces class invariants with minimal runtime overhead.
- Host: GitHub
- URL: https://github.com/andreamancuso/invariant-php
- Owner: andreamancuso
- License: other
- Created: 2025-03-18T00:30:32.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2025-03-19T21:35:10.000Z (over 1 year ago)
- Last Synced: 2025-12-13T09:51:40.260Z (7 months ago)
- Topics: dbc, designbycontract, php, php-extension
- Language: PHP
- Homepage:
- Size: 51.8 KB
- Stars: 3
- Watchers: 2
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Invariant PHP
**Invariant PHP** is a native PHP extension that brings [Design by Contract (DbC)](https://en.wikipedia.org/wiki/Design_by_contract) principles to PHP. It **automatically enforces class invariants** _and ensures all typed properties are initialized_ without relying on reflection.
## License
Released under the [PHP License 3.01](https://www.php.net/license/3_01.txt).
## Features
- **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.
## Why?
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**.
## Example Usage
```php
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 zero
```
## Limitations
Right 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.
## Installation
Currently, you must **build from source**:
```sh
phpize
./configure
make -j$(nproc)
sudo make install
```
Then enable it in `php.ini`:
```ini
extension=invariant_php.so
```
## **How It Works**
- **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.
## **Roadmap**
- **Support `requires()` (preconditions)**.
- **Support `ensures()` (postconditions)**.
- **Optimization & caching** for better performance.
- **PECL package for easy installation**.
---
**Want to contribute?** PRs and discussions are welcome! 🎯