{"id":15957071,"url":"https://github.com/serafimarts/contracts","last_synced_at":"2025-10-31T05:38:50.477Z","repository":{"id":62542300,"uuid":"313032580","full_name":"SerafimArts/Contracts","owner":"SerafimArts","description":"[WIP] Design by Contract library for PHP","archived":false,"fork":false,"pushed_at":"2022-04-02T22:10:39.000Z","size":111,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-05-07T06:05:59.653Z","etag":null,"topics":["contracts","dbc","design-by-contracts","ensure","invariant","php","verify"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SerafimArts.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-15T13:03:32.000Z","updated_at":"2024-04-03T17:32:41.000Z","dependencies_parsed_at":"2022-11-02T15:46:45.239Z","dependency_job_id":null,"html_url":"https://github.com/SerafimArts/Contracts","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SerafimArts%2FContracts","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SerafimArts%2FContracts/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SerafimArts%2FContracts/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SerafimArts%2FContracts/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SerafimArts","download_url":"https://codeload.github.com/SerafimArts/Contracts/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252823918,"owners_count":21809713,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["contracts","dbc","design-by-contracts","ensure","invariant","php","verify"],"created_at":"2024-10-07T13:40:29.037Z","updated_at":"2025-10-31T05:38:50.428Z","avatar_url":"https://github.com/SerafimArts.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Contracts\n\nContracts for PHP, is a contract programming framework and test tool \nfor PHP, which uses attributes to provide run-time checking. \n(In particular, this is not a static analysis tool.)\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/SerafimArts/Contracts/actions\"\u003e\u003cimg src=\"https://github.com/SerafimArts/Contracts/workflows/build/badge.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/serafim/dbc\"\u003e\u003cimg src=\"https://img.shields.io/badge/PHP-^8.1-ff0140.svg\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/serafim/dbc\"\u003e\u003cimg src=\"https://poser.pugx.org/serafim/dbc/version\" alt=\"Latest Stable Version\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/serafim/dbc\"\u003e\u003cimg src=\"https://poser.pugx.org/serafim/dbc/v/unstable\" alt=\"Latest Unstable Version\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://packagist.org/packages/serafim/dbc\"\u003e\u003cimg src=\"https://poser.pugx.org/serafim/dbc/downloads\" alt=\"Total Downloads\"\u003e\u003c/a\u003e\n    \u003ca href=\"https://raw.githubusercontent.com/SerafimArts/Contracts/master/LICENSE.md\"\u003e\u003cimg src=\"https://poser.pugx.org/ffi-headers/vulkan-headers/license\" alt=\"License MIT\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n## Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Configuration](#configuration)\n- [Usage](#usage)\n    - [Invariants](#invariants)\n    - [Method Contracts](#method-contracts)\n- [Features](#features)\n\n## Requirements\n\n- PHP 8.1+\n\n## Installation\n\nLibrary is available as composer repository and can be installed using the \nfollowing command in a root of your project.\n\n```bash\n$ composer require serafim/dbc\n```\n\n## Configuration\n\nBy default, the behavior of contracts depends on whether assertions \n(`assert.active` in php.ini configuration) are enabled on your system.\n\nHowever, you can force them to enable or disable contracts:\n\n```php\nuse Serafim\\Contracts\\Runtime;\n\n// Enable runtime contract assertions\nRuntime::enable();\n\n// Disable runtime contract assertions\nRuntime::disable();\n\n// Enable runtime contract assertions if PHP\n// assertions are enabled or disable otherwise.\nRuntime::auto();\n```\n\nBy default, the framework does not listen to any namespaces. To add \na namespace for your application, use the `Runtime::listen()` method.\n\n```php\nuse Serafim\\Contracts\\Runtime;\n\nRuntime::listen('App\\\\Entity', 'App\\\\Http\\\\Controllers');\n```\n\nIn addition, you can specify the directory where the cache \nfiles should be stored.\n\n\u003e Please note that these cache files are included by the PHP, therefore they \n\u003e are cached by opcache extension and do not degrade performance.\n\n```php\nuse Serafim\\Contracts\\Runtime;\n\nRuntime::cache(__DIR__ . '/storage');\n```\n\n## Usage\n\nContracts are written as PHP code within quoted strings, embedded in \nattributes. E.g., `#[Verify('$x \u003c 100')]` states that `$x` must be less \nthan `100`. Any PHP expression, except anonymous classes, may be used, \nprovided the string is properly escaped.\n\nAn annotation binds a contract to a code element: either a method or a class. \nLibrary defines three main annotation types, which live in the \n`Serafim\\Contracts\\Attribute` namespace:\n\n- `#[Verify]` for method preconditions;\n- `#[Ensure]` for method postconditions;\n- `#[Invariant]` for class invariants;\n\nContract annotations work only (yet) with classes.\n\n### Invariants\n\nA class may have associated invariants. Instead of specifying a contract \nbetween a caller and a callee, those invariants describe the state of a \nvalid object of the qualified type. Calling methods on an object may \ncause it to change; invariants guarantee that after any such change, \nthe object remains in a consistent state.\n\nOf course, internal operations are allowed to muck around and temporarily \ninvalidate invariants to do their job, but they agree to eventually put \neverything back into their proper places. Intuitively, any operation made \nagainst this is considered internal and does not need to obey the invariants. \nOnly method invocations on other variables do.\n\nAny defined invariant in a class has access to all of its fields, \nincluding any protected and private.\n\n```php\nuse Serafim\\Contracts\\Attribute\\Invariant;\n\n#[Invariant('$this-\u003ebalance \u003e= 0')]\nclass Account\n{\n    private int $balance = 0;\n    \n    public function deposit(int $amount): void\n    {\n        $this-\u003ebalance += $amount + 1;\n    }\n}\n```\n\nWhen determining such an invariant, the account's balance will always be \ngreater or equal than zero.\n\n```php\n$account = new Account();\n\n$account-\u003edeposit(42);      // OK\n$account-\u003edeposit(-666);    // Serafim\\Contracts\\Exception\\InvariantException: $this-\u003ebalance \u003e= 0\n```\n\n### Method contracts\n\nA method may have preconditions and postconditions attached to it. Together, \nthey specify the contract between caller and callee: if the precondition is \nsatisfied on entry of the method, then the caller may assume the postcondition \non exit. The precondition is what the callee demands of the caller, and in \nreturn the caller expects the postcondition to hold after the call.\n\nAs an example, consider the following specification of the square root \nfunction, which states that for any non-negative double x given, sqrt will \nreturn a non-negative result.\n\n```php\nuse Serafim\\Contracts\\Attribute\\Verify;\nuse Serafim\\Contracts\\Attribute\\Ensure;\n\nclass Math\n{\n    #[Verify('x \u003e= 0')]\n    #[Ensure('$result \u003e= 0')]\n    public static function sqrt(float $x): float \n    {\n        // ...code\n    }\n}\n```\n\nAs shown in this example, a precondition may access parameter values; in \nfact, preconditions and postconditions are evaluated in the context of the \nmethod they are bound to. More precisely, each annotation behaves as if it \nwere a method, with the same arguments and in the same scope as the qualified\nmethod. In terms of scoping, the previous code is equivalent to the following:\n\n```php\nuse Serafim\\Contracts\\Attribute\\Verify;\nuse Serafim\\Contracts\\Attribute\\Ensure;\n\nclass Math\n{\n    public static function sqrt(float $x): float \n    {\n        if ($x \u003c 0) {\n            throw new \\Serafim\\Contracts\\Exception\\PreconditionException('$x \u003e= 0');\n        }\n        \n        // ...code\n        \n        if ($result \u003c 0) {\n            throw new \\Serafim\\Contracts\\Exception\\PostconditionException('$result \u003e= 0');\n        }\n        \n        return $result;\n    }\n}\n```\n\nIn addition, postconditions may contain a few extensions:\n\n- As we have seen, they may refer to the returned value, using the `$result`\n  keyword.\n- Within a postcondition,`$old` is a keyword that contains the state of the \n  object before its changes.\n\nAt run time, when contracts are enabled, preconditions and postconditions\ntranslate to checks on entry and exit, respectively, of the method. A failure\nresults in a `PreconditionException` or `PostconditionException` being thrown,\ndepending on the origin: failure to meet a precondition means that the method\nwas called incorrectly, whereas an unsatisfied postcondition points to a bug\nin the implementation of the method itself.\n  \n#### Keywords\n\n| Keyword    | May appear in | Description           |\n|------------|---------------|-----------------------|\n| `$old`     | `#[Ensure]`   | Value on method entry |\n| `$result`  | `#[Ensure]`   | Value to be returned  |\n\n### Features\n\n- [x] Class Contracts\n  - [ ] Abstract methods\n  - [ ] Inheritance (import from parent classes, traits and interfaces)\n- [x] Trait Contracts\n  - [ ] Abstract methods\n- [ ] Interface Contracts\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserafimarts%2Fcontracts","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fserafimarts%2Fcontracts","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fserafimarts%2Fcontracts/lists"}