Skip to content

andreamancuso/invariant-php

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

12 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invariant PHP

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.

License

Released under the PHP License 3.01.

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

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 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:

phpize
./configure
make -j$(nproc)
sudo make install

Then enable it in php.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! 🎯

About

Native PHP extension for Design by Contract. Enforces class invariants with minimal runtime overhead.

Topics

Resources

License

Stars

Watchers

Forks

Contributors