{"id":13816411,"url":"https://github.com/upwork/phystrix","last_synced_at":"2026-01-22T00:16:36.121Z","repository":{"id":154469602,"uuid":"14029461","full_name":"upwork/phystrix","owner":"upwork","description":"Phystrix is a latency and fault tolerance library for PHP, inspired by Netflix’s Hystrix","archived":false,"fork":false,"pushed_at":"2021-10-04T20:30:56.000Z","size":66,"stargazers_count":2,"open_issues_count":3,"forks_count":0,"subscribers_count":22,"default_branch":"master","last_synced_at":"2026-01-13T21:49:52.318Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/upwork.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2013-10-31T22:02:35.000Z","updated_at":"2025-12-09T23:03:18.000Z","dependencies_parsed_at":null,"dependency_job_id":"650767f0-d415-4cce-919b-7e25c1cad22b","html_url":"https://github.com/upwork/phystrix","commit_stats":{"total_commits":24,"total_committers":10,"mean_commits":2.4,"dds":0.5833333333333333,"last_synced_commit":"dd4326d5996b655af68407beb78d1f811609a2b7"},"previous_names":["odesk/phystrix"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/upwork/phystrix","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upwork%2Fphystrix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upwork%2Fphystrix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upwork%2Fphystrix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upwork%2Fphystrix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/upwork","download_url":"https://codeload.github.com/upwork/phystrix/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/upwork%2Fphystrix/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28647916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T21:29:11.980Z","status":"ssl_error","status_checked_at":"2026-01-21T21:24:31.872Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5: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":[],"created_at":"2024-08-04T05:00:40.674Z","updated_at":"2026-01-22T00:16:36.098Z","avatar_url":"https://github.com/upwork.png","language":"PHP","funding_links":[],"categories":["PHP"],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/upwork/phystrix.svg)](https://travis-ci.org/upwork/phystrix)\n\n### About Phystrix\n\nIn distributed systems with PHP frontend, application talks to a number of remote services. Be it a set of services of your own, a 3rd party RESTful API or a legacy component that requires networking interaction: in complex, high-load systems occasional failure cannot be avoided.\n\nPhystrix protects the points of access to remote resources by keeping track of various metrics and preventing repetitive failures.\n\nIn case of a service failing way too often, to not make the situation worse, Phystrix will temporarily stop issuing requests to it. When the service comes back to life, Phystrix allows the client application to access it again.\n\n### Understanding Phystrix\n\nNot only Phystrix was heavily inspired by the amazing [Hystrix library](https://github.com/Netflix/Hystrix) for Java by Netflix, it also attempts to follow the best practices set by the library. You will notice that configuration parameters are the same as well as much of how it works internally.\n\nEven though there is not much available at the moment in terms of documentation for Phystrix, you can also use [Hystrix wiki](https://github.com/Netflix/Hystrix/wiki) as an additional source of information, to understand how something works etc.\n\n## Installation\n\nRecommended way to install Phystrix is by using [Composer](https://getcomposer.org):\n\n```javascript\n\"require\": {\n     \"odesk/phystrix\": \"dev-master\"\n}\n```\n\nTo store and share metrics between requests, Phystrix uses [APC](http://php.net/manual/en/book.apc.php), so make sure you have the PHP extension enabled.\n\n### Php 7.2\n\nIn php 7 the API for `apcu` changed. You will need to install [apcu-bc](https://github.com/krakjoe/apcu-bc) in addition to `apcu` to use Phystrix.\nThe backwards compatibility layer extension must be loaded AFTER `apcu`.\n\n## Usage\n\nTo protect a point of access to remote service, we use the [command pattern](http://en.wikipedia.org/wiki/Command_pattern). Here is how a minimal implementation could look like:\n\n```php\nuse Odesk\\Phystrix\\AbstractCommand;\n\n/**\n * All commands must extends Phystrix's AbstractCommand\n */\nclass MyCommand extends AbstractCommand\n{\n    protected $name;\n\n    public function __construct($name)\n    {\n        $this-\u003ename = $name;\n    }\n\n    /**\n     * This function is called internally by Phystrix, only if the request is allowed\n     *\n     * @return mixed\n     */\n    protected function run()\n    {\n        return 'Hello ' . $this-\u003ename;\n    }\n}\n```\n\nTo have the command preconfigured with Phystrix-specific dependencies, you need to obtain it from a special factory that you share with your objects. For instance, in your controller you would do:\n\n```php\n$myCommand = $phystrix-\u003egetCommand('MyCommand', 'Alex'); // 'Alex' is passed to MyCommand's constructor\n$result = $myCommand-\u003eexecute();\n```\n\nNotice, the extra parameters you pass to the factory’s getCommand method are forwarded to the command’s constructor.\n\nThe factory is instantiated as follows:\n\n```php\nuse Zend\\Config\\Config;\nuse Odesk\\Phystrix\\ApcStateStorage;\nuse Odesk\\Phystrix\\CircuitBreakerFactory;\nuse Odesk\\Phystrix\\CommandMetricsFactory;\nuse Odesk\\Phystrix\\CommandFactory;\n\n$config = new Config(require 'phystrix-config.php');\n\n$stateStorage = new ApcStateStorage();\n$circuitBreakerFactory = new CircuitBreakerFactory($stateStorage);\n$commandMetricsFactory = new CommandMetricsFactory($stateStorage);\n\n$phystrix = new CommandFactory(\n    $config, new \\Zend\\Di\\ServiceLocator(), $circuitBreakerFactory, $commandMetricsFactory,\n    new \\Odesk\\Phystrix\\RequestCache(), new \\Odesk\\Phystrix\\RequestLog()\n);\n```\n\nThe way you store the configuration files is up to you. Phystrix relies on [Zend\\Config](https://github.com/zendframework/Component_ZendConfig)  to manage configurations. In this case, __phystrix-config.php__ is a PHP array:\n\n```php\nreturn array(\n    'default' =\u003e array( // Default command configuration\n        'fallback' =\u003e array(\n            // Whether fallback logic of the phystrix command is enabled\n            'enabled' =\u003e true,\n        ),\n        'circuitBreaker' =\u003e array(\n            // Whether circuit breaker is enabled, if not Phystrix will always allow a request\n            'enabled' =\u003e true,\n            // How many failed request it might be before we open the circuit (disallow consecutive requests)\n            'errorThresholdPercentage' =\u003e 50,\n            // If true, the circuit breaker will always be open regardless the metrics\n            'forceOpen' =\u003e false,\n            // If true, the circuit breaker will always be closed, allowing all requests, regardless the metrics\n            'forceClosed' =\u003e false,\n            // How many requests we need minimally before we can start making decisions about service stability\n            'requestVolumeThreshold' =\u003e 10,\n            // For how long to wait before attempting to access a failing service\n            'sleepWindowInMilliseconds' =\u003e 5000,\n        ),\n        'metrics' =\u003e array(\n            // This is for caching metrics so they are not recalculated more often than needed\n            'healthSnapshotIntervalInMilliseconds' =\u003e 1000,\n            // The period of time within which we the stats are collected\n            'rollingStatisticalWindowInMilliseconds' =\u003e 1000,\n            // The more buckets the more precise and actual the stats and slower the calculation.\n            'rollingStatisticalWindowBuckets' =\u003e 10,\n        ),\n        'requestCache' =\u003e array(\n            // Request cache, if enabled and a command has getCacheKey implemented\n            // caches results within current http request\n            'enabled' =\u003e true,\n        ),\n        'requestLog' =\u003e array(\n            // Request log collects all commands executed within current http request\n            'enabled' =\u003e false,\n        ),\n    ),\n    'MyCommand' =\u003e array( // Command specific configuration\n        'fallback' =\u003e array(\n            'enabled' =\u003e false\n        )\n    )\n);\n```\n\nCommand-specific configurations are merged with the default one on instantiation. “MyCommand” in this case is the command key. By default it is the same as command’s class, but you can set it yourself by overriding the __getCommandKey__ protected method:\n\n```php\n    /**\n     * This function defines the command key to use for this command\n     *\n     * @return string\n     */\n    protected function getCommandKey()\n    {\n        return 'CustomCommandKey';\n    }\n```\n\nPhystrix only works with the command keys. If you have two different commands with the same command key - Phystrix will collect metrics, disable and enable requests, as for a single entity. This may be used for grouping commands.\n\nSometimes, you may need to change a parameter when a command is used in a particular context:\n\n```php\nuse Zend\\Config\\Config;\n$myCommand = $phystrix-\u003egetCommand('MyCommand', 'Alex');\n$myCommand-\u003esetConfig(new Config(array('requestCache' =\u003e array('enabled' =\u003e false))));\n$result = $myCommand-\u003eexecute();\n```\n\nNote, the config you set is merged with the previously set value.\n\n## Features\n\n### Fallback\n\nFor a command, you can specify fallback logic, that will be executed in case of a failure, or when the remote service is blocked:\n\n```php\nclass GetAvatarUrlCommand extends AbstractCommand\n{\n    protected $user;\n\n    public function __construct($user)\n    {\n        $this-\u003euser = $user;\n    }\n\n    protected function run()\n    {\n        $remoteAvatarService = $this-\u003eserviceLocator-\u003eget('avatarService');\n        return $remoteAvatarService-\u003egetUrlByUser($this-\u003euser);\n    }\n\n    /**\n     * When __run__ fails for some reason, or when Phystrix doesn't allow the request in the first place,\n     * this function result will be returned instead\n     *\n     * @return string\n     */\n    protected function getFallback()\n    {\n        // we failed getting user's picture, so showing a generic no-photo placeholder instead.\n        return 'http://example/avatars/no-photo.jpg';\n    }\n}\n```\n\nIf you want to use logic requiring networking for your fallback, make sure to “wrap” it into a Phystrix command of its own.\n\n### Request cache\n\nRequest cache, when enabled, caches command execution result __within a single HTTP request__, so you don’t have to worry about loading data over network more than needed.\n\nResults are cached per command key per cache key. To define cache key generation logic, implement __getCacheKey__ protected method:\n\n```php\n    protected function getCacheKey()\n    {\n        return 'cache_' . $this-\u003euser;\n    }\n```\n\n### Timeout\n\n[Hystrix for Java](https://github.com/Netflix/Hystrix) allows you to set specific time a command is allowed to run. What it does is it limits the time for the thread a command is running in. In PHP we cannot do that, however, as we only have the context of one, current, thread.\n\nSuggested approach is to manually configure the timeout in the library used to access the remote service.\n\nLet’s say you have this Phystrix configuration for MyCommand:\n\n```php\n    'MyCommand' =\u003e array(\n        'fallback' =\u003e array(\n            'enabled' =\u003e false\n        ),\n        'timeout' =\u003e 2000, // milliseconds\n    )\n```\n\nwhere “timeout” is a custom parameter which Phystrix does not make any use of. You can specify any arbitrary parameters in Phystrix configuration and they will be available for you in the commands:\n\n```php\n    protected function run()\n    {\n        $remoteAvatarService = $this-\u003eserviceLocator-\u003eget('avatarService');\n        return $remoteAvatarService-\u003egetUrlByUser($this-\u003euser);\n    }\n\n    /**\n     * Custom preparation logic, preceding command execution\n     */\n    protected function prepare()\n    {\n        $remoteAvatarService = $this-\u003eserviceLocator-\u003eget('avatarService');\n        if ($this-\u003econfig-\u003e__isset('timeout')) {\n            // if the timeout is exceeded an exception will be thrown\n            $remoteAvatarService-\u003esetTimeout($this-\u003econfig-\u003eget('timeout'));\n        }\n    }\n```\n\nwhere the client might be a 3rd library you downloaded, or an instance of http client from a framework such as Zend Framework or Symfony or something you wrote yourself.\n\nOf course, having to add this into each command would be suboptimal. Normally, you will have a set of abstract commands, specific to your use cases. E.g. you might have __GenericCurlCommand__ or __GenericGoogleApiCommand__ and __MyCommand__ would extend one of those.\n\n### Custom dependencies\n\nSince you get the commands from a special factory, you need a way to inject custom dependencies into your commands, such as an instance of HTTP client.\n\nOne way would be to extend the __Odesk\\Phystrix\\CommandFactory__, create your own factory and have it inject what you need.\n\nAlternatively, configure the locator instance that __Odesk\\Phystrix\\CommandFactory__ accepts in the constructor.\n\nThe service locator can be anything, implementing the very basic [Zend\\Di\\LocatorInterface](https://github.com/zendframework/zf2/blob/master/library/Zend/Di/LocatorInterface.php). You can inject an IoC container that will lazily instantiate instance as they are needed, or you can use a simpler, preconfigured, instance of __Zend\\Di\\ServiceLocator__:\n\n```php\n$serviceLocator = \\Zend\\Di\\ServiceLocator();\n$googleApiRemoteService = new GoogleApi(...);\n$serviceLocator-\u003eset('googleApi', $googleApiRemoteService);\n\n$phystrix = new CommandFactory(\n    $config, $serviceLocator, $circuitBreakerFactory,\n    $commandMetricsFactory, new \\Odesk\\Phystrix\\RequestCache()\n);\n```\n\nYou can access the service locator from within your commands as follows:\n\n```php\n    protected function run()\n    {\n        $googleApi = $this-\u003eserviceLocator-\u003eget('googleApi');\n        return $googleApi-\u003efetchAllEmail();\n    }\n```\n\n### Request Log\n\nA useful feature for performance monitoring. When enabled, allows you to retrieve the list of commands executed during the current HTTP request:\n\n```php\n/** @var RequestLog $requestLog */\n$commands = $requestLog-\u003egetExecutedCommands();\n```\n\nWhat you get is an array of actual command instances. For each command you can get the execution time in milliseconds:\n\n```php\n$command-\u003egetExecutionTimeInMilliseconds();\n```\n\nand the list of events, such as \"SUCCESS\", \"FAILURE\", \"TIMEOUT\", \"SHORT_CIRCUITED\", \"FALLBACK_SUCCESS\", \"FALLBACK_FAILURE\", \"EXCEPTION_THROWN\", \"RESPONSE_FROM_CACHE\":\n\n```php\n$command-\u003egetExecutionEvents();\n```\n\n### Hystrix Turbine and Dashboard Support\n\nTBD\n\n### Licence\n\nCopyright 2013-2017 Upwork Global Inc. All Rights Reserved.\n\nPhystrix is licensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupwork%2Fphystrix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fupwork%2Fphystrix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fupwork%2Fphystrix/lists"}