{"id":39173407,"url":"https://github.com/antfroger/progressive","last_synced_at":"2026-01-17T22:25:27.635Z","repository":{"id":56948743,"uuid":"318567858","full_name":"antfroger/progressive","owner":"antfroger","description":"Progressive is a super easy to use PHP feature flag library","archived":false,"fork":false,"pushed_at":"2025-08-26T16:49:48.000Z","size":92,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-26T22:12:33.874Z","etag":null,"topics":["feature-flag","feature-flags","feature-toggle","feature-toggles","php","php-lib","php-library"],"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/antfroger.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-12-04T16:08:36.000Z","updated_at":"2025-08-26T16:48:42.000Z","dependencies_parsed_at":"2025-08-26T18:51:09.445Z","dependency_job_id":"102ab72b-dfc0-440a-94d0-e32d53fbb249","html_url":"https://github.com/antfroger/progressive","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/antfroger/progressive","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antfroger%2Fprogressive","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antfroger%2Fprogressive/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antfroger%2Fprogressive/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antfroger%2Fprogressive/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/antfroger","download_url":"https://codeload.github.com/antfroger/progressive/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/antfroger%2Fprogressive/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28520372,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T22:11:28.393Z","status":"ssl_error","status_checked_at":"2026-01-17T22:11:27.841Z","response_time":85,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["feature-flag","feature-flags","feature-toggle","feature-toggles","php","php-lib","php-library"],"created_at":"2026-01-17T22:25:27.030Z","updated_at":"2026-01-17T22:25:27.626Z","avatar_url":"https://github.com/antfroger.png","language":"PHP","readme":"# Progressive\n\nProgressive is a feature flag library *(also called toggle, switch, ...)*.  \nThanks to Progressive, you can **progressively**, **quickly** and **simply** enable new features.\n(and just as quickly deactivate them in case of an emergency)\n\n[![Build Status](https://github.com/antfroger/progressive/workflows/CI/badge.svg)](https://github.com/antfroger/progressive)\n[![Latest Stable Version](https://poser.pugx.org/antfroger/progressive/v/stable.png)](https://packagist.org/packages/antfroger/progressive \"Latest Stable Version\")\n\n## Installation\n\n```console\n$ composer require antfroger/progressive\n```\n\n## Usage\n\n```php\n// From a file\n$config = \\Symfony\\Component\\Yaml\\Yaml::parseFile('/your-own-config-file.yaml');\n// Content of /your-own-config-file.yaml\n// features:\n//   dark-theme: true\n//   call-center: false\n//   homepage-v2:\n//     enabled: true\n\n// Or from a PHP array\n$config = [\n    'features' =\u003e [\n        'dark-theme'  =\u003e true,\n        'call-center' =\u003e false,\n        'homepage-v2' =\u003e [\n            'enabled' =\u003e true\n        ],\n    ]\n];\n\n$progressive = new Progressive($config);\n$progressive-\u003eisEnabled('dark-theme');    // true\n$progressive-\u003eisEnabled('call-center');   // false\n$progressive-\u003eisEnabled('homepage-v2');   // true\n```\n\n## Rules\n\n### Built-in\n\n#### `enabled: true|false`\n\n`enabled` enables (or disables) the feature for everyone, everywhere, all the time.  \nThe value is meant to be a boolean, `true|false`.\n\n```php\n// Short\n$config = [\n    'features' =\u003e [\n        'dark-theme' =\u003e true\n    ]\n];\n\n// Verbose\n$config = [\n    'features' =\u003e [\n        'dark-theme'  =\u003e [\n            'enabled' =\u003e true\n        ]\n    ]\n];\n```\n\n### Custom\n\nYou will probably need many more rules that will fit your needs and stack.\n\nLet's say you want to redesign your homepage and progressively displaying it to test if everything goes right.  \nYou start by enabling it in dev, then preprod, then prod but only for developers, then admins, then 1% of the users...  \nHow would you be able to achieve that?\n\nWith custom rules!\n\n```php\n$config = [\n    'features' =\u003e [\n        'homepage-v123'  =\u003e [\n            'env' =\u003e ['DEV', 'PREPROD']\n        ]\n    ]\n];\n$progressive = new Progressive($config);\n\n$progressive-\u003eaddCustomRule('env', function (Context $context, array $envs) {\n    return in_array(getenv('ENV'), $envs);\n});\n\n$progressive-\u003eisEnabled('homepage-v123'); // Returns true if ENV is DEV or PREPROD, otherwise returns false\n```\n\n(this lambda can be improved thanks to the `Context object` - more about it [here](#context-object))\n\n## Strategies\n\nRules are great but sometimes one rule is not enough to decide if a feature must be enabled.  \nYou may want to enable a feature in `PROD` only for admins but to everyone in `DEV`, `TEST` and `PREPROD`.  \nOr you may want to enable a feature to the admins **AND** a given percentage of users.\n\nThat's where strategies come into play!\n\n*(Strategies are simply another name for nested rules.  \nAnd as for rules, you can create your own strategies!)*\n\nProgressive comes with two built-in strategies:\n\n### `unanimous: []`\n\n`unanimous` enables the feature if **all** the conditions are met.  \nThe value is meant to be an array of [rules](#rules).\n\n```php\n$config = [\n    'features' =\u003e [\n        'translate-interface'  =\u003e [\n            'unanimous' =\u003e [\n                'env'   =\u003e ['DEV', 'PREPROD'],\n                'roles' =\u003e ['ROLE_ADMIN', 'ROLE_TRANSLATOR']\n            ]\n        ]\n    ]\n];\n```\n\nIn this example, the interface to translate the app will only be displayed in `DEV` and `PREPROD` environments to users having `ROLE_ADMIN` or `ROLE_TRANSLATOR` as roles.  \nThis strategy can be defined as an **AND**.\n\n### `partial: []`\n\n`partial` enables the feature if only **one** of the conditions is met.  \nRules are evaluated one by one. The feature is enabled as soon as one rule is true.  \nThe value is meant to be an array of [rules](#rules).\n\n```php\n$config = [\n    'features' =\u003e [\n        'translate-interface'  =\u003e [\n            'partial' =\u003e [\n                'env'   =\u003e ['DEV', 'PREPROD'],\n                'roles' =\u003e ['ROLE_ADMIN', 'ROLE_TRANSLATOR']\n            ]\n        ]\n    ]\n];\n```\n\nIn this example, the interface to translate the app will be displayed:\n\n* in `DEV` and `PREPROD` environments (for all users)\n* for users having a role `ROLE_ADMIN` or `ROLE_TRANSLATOR`, regardless of the environment.\n\nThis strategy can be defined as an **OR**.\n\n## Context object\n\nProgressive doesn't know anything of your application logic code (and doesn't want to know).  \nBut you may want to use your logic inside custom rules.\n\nIn that case, the context object is your friend!  \nIt's nothing more that an user-defined databag.\n\nYou can use it to improve our previous custom rule:\n\n```php\n$context = new Context([\n    'env' =\u003e getenv('ENV') ?: 'PROD'\n]);\n\n$config = [\n    'features' =\u003e [\n        'homepage-v123'  =\u003e [\n            'env' =\u003e ['DEV', 'PREPROD']\n        ]\n    ]\n];\n\n$progressive = new Progressive($config, $context);\n$progressive-\u003eaddCustomRule('env', function (Context $context, array $envs) {\n    return in_array($context-\u003eget('env'), $envs);\n});\n```\n\nAnother common example is to store the current user in the context:\n\n```php\n$context = new Context(['user' =\u003e $user]);\n\n$config = [\n    'features' =\u003e [\n        'homepage-v123'  =\u003e [\n            'roles' =\u003e ['ROLE_ADMIN']\n        ]\n    ]\n];\n\n$progressive = new Progressive($config, $context);\n$progressive-\u003eaddCustomRule('roles', function (Context $context, array $roles) {\n    $userRoles = $context-\u003eget('user')-\u003egetRoles();\n    foreach ($roles as $role) {\n        if (in_array($role, $userRoles) {\n            return true;\n        }\n    }\n\n    return false;\n});\n```\n\n## Requesting a non-existing flag\n\nWhenever you request an non-existing flag, `isEnabled` will return false.  \nThe flag is considered disabled.\n\n```php\n$config = [\n    'features' =\u003e []\n];\n\n$progressive = new Progressive($config);\n$progressive-\u003eisEnabled('dark-theme'); // false\n```\n\n## Separate feature releases from code deploy\n\nAs Progressive takes an array of config, it makes it possible to decorrelate the release calendar from the code deployment.  \nIn this example, we use the Symfony Yaml component to read the config from a YAML file.\n\n```php\nuse Symfony\\Component\\Yaml\\Yaml;\n\n$config = Yaml::parseFile('/your-own-config-file.yaml');\n// Content of /your-own-config-file.yaml\n// features:\n//   new-feature: false\n$progressive = new Progressive($config);\n$progressive-\u003eisEnabled('new-feature'); // false\n```\n\nImagine that you are able to deploy the config files independently from your code base, you could release a new feature without having to deploy all your code base, by redeploying only `/your-own-config-file.yaml`.\n\n```php\n// Content of /your-own-config-file.yaml\n// features:\n//   new-feature: true\n$progressive-\u003eisEnabled('new-feature'); // true\n```\n\nYou may also want to store the configuration in a database and pass it as an array to Progressive.\n\n## Progressive in your projects\n\n* Progressive is also available as a [Symfony bundle](https://github.com/antfroger/progressive-bundle)\n\n---\n\n*Inspired by [Laurent Callarec](https://github.com/lcallarec)'s javascript feature-flag library [Banderole](https://github.com/lcallarec/banderole)*\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantfroger%2Fprogressive","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fantfroger%2Fprogressive","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fantfroger%2Fprogressive/lists"}