An open API service indexing awesome lists of open source software.

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.

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! 🎯