From a4130ba1099e0ccc4e17e8b9bca3f22be4be04f4 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 13:14:41 -0800 Subject: [PATCH 01/18] A version of FeatureRequester compatible with PHP v5.3 --- composer.json | 5 +- composer.lock | 638 ++++++++------------ src/LaunchDarkly/GuzzleFeatureRequester.php | 27 +- 3 files changed, 257 insertions(+), 413 deletions(-) diff --git a/composer.json b/composer.json index 8090c70d0..5ec1aca15 100644 --- a/composer.json +++ b/composer.json @@ -14,9 +14,8 @@ } ], "require": { - "php": ">=5.4", - "guzzlehttp/guzzle": "5.*", - "guzzlehttp/cache-subscriber": "0.1.*" + "php": ">=5.3", + "guzzlehttp/guzzle": "3.8.*" }, "require-dev": { "phpunit/phpunit": "4.3.*", diff --git a/composer.lock b/composer.lock index eff0ac1bc..5bb16d5fa 100644 --- a/composer.lock +++ b/composer.lock @@ -4,163 +4,71 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "064d02ddae1114a6ecb771b0500ceccb", - "content-hash": "6d07711fd3a9d3fb5009b02b6a8cafee", + "hash": "d5329067b06bb2f70c0f6669cde98596", + "content-hash": "69555c83d13efbe2e5ac8177b9fa5d45", "packages": [ - { - "name": "doctrine/cache", - "version": "v1.4.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/cache.git", - "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/cache/zipball/8c434000f420ade76a07c64cbe08ca47e5c101ca", - "reference": "8c434000f420ade76a07c64cbe08ca47e5c101ca", - "shasum": "" - }, - "require": { - "php": ">=5.3.2" - }, - "conflict": { - "doctrine/common": ">2.2,<2.4" - }, - "require-dev": { - "phpunit/phpunit": ">=3.7", - "predis/predis": "~1.0", - "satooshi/php-coveralls": "~0.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.5.x-dev" - } - }, - "autoload": { - "psr-0": { - "Doctrine\\Common\\Cache\\": "lib/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Caching library offering an object-oriented API for many cache backends", - "homepage": "http://www.doctrine-project.org", - "keywords": [ - "cache", - "caching" - ], - "time": "2015-08-31 12:36:41" - }, - { - "name": "guzzlehttp/cache-subscriber", - "version": "0.1.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/cache-subscriber.git", - "reference": "ecb903f6e11b5ca9f2cdbc460e2e68deea9e8858" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/cache-subscriber/zipball/ecb903f6e11b5ca9f2cdbc460e2e68deea9e8858", - "reference": "ecb903f6e11b5ca9f2cdbc460e2e68deea9e8858", - "shasum": "" - }, - "require": { - "doctrine/cache": "~1.3", - "guzzlehttp/guzzle": "~5.0", - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "0.1-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Subscriber\\Cache\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Guzzle HTTP cache subscriber", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "cache" - ], - "time": "2014-10-29 21:06:25" - }, { "name": "guzzlehttp/guzzle", - "version": "5.3.0", + "version": "v3.8.1", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "f3c8c22471cb55475105c14769644a49c3262b93" + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/f3c8c22471cb55475105c14769644a49c3262b93", - "reference": "f3c8c22471cb55475105c14769644a49c3262b93", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba", + "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba", "shasum": "" }, "require": { - "guzzlehttp/ringphp": "^1.1", - "php": ">=5.4.0" + "ext-curl": "*", + "php": ">=5.3.3", + "symfony/event-dispatcher": ">=2.1" + }, + "replace": { + "guzzle/batch": "self.version", + "guzzle/cache": "self.version", + "guzzle/common": "self.version", + "guzzle/http": "self.version", + "guzzle/inflection": "self.version", + "guzzle/iterator": "self.version", + "guzzle/log": "self.version", + "guzzle/parser": "self.version", + "guzzle/plugin": "self.version", + "guzzle/plugin-async": "self.version", + "guzzle/plugin-backoff": "self.version", + "guzzle/plugin-cache": "self.version", + "guzzle/plugin-cookie": "self.version", + "guzzle/plugin-curlauth": "self.version", + "guzzle/plugin-error-response": "self.version", + "guzzle/plugin-history": "self.version", + "guzzle/plugin-log": "self.version", + "guzzle/plugin-md5": "self.version", + "guzzle/plugin-mock": "self.version", + "guzzle/plugin-oauth": "self.version", + "guzzle/service": "self.version", + "guzzle/stream": "self.version" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "^4.0", - "psr/log": "^1.0" + "doctrine/cache": "*", + "monolog/monolog": "1.*", + "phpunit/phpunit": "3.7.*", + "psr/log": "1.0.*", + "symfony/class-loader": "*", + "zendframework/zend-cache": "<2.3", + "zendframework/zend-log": "<2.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "5.0-dev" + "dev-master": "3.8-dev" } }, "autoload": { - "psr-4": { - "GuzzleHttp\\": "src/" + "psr-0": { + "Guzzle": "src/", + "Guzzle\\Tests": "tests/" } }, "notification-url": "https://packagist.org/downloads/", @@ -172,6 +80,10 @@ "name": "Michael Dowling", "email": "mtdowling@gmail.com", "homepage": "https://github.com/mtdowling" + }, + { + "name": "Guzzle Community", + "homepage": "https://github.com/guzzle/guzzle/contributors" } ], "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", @@ -185,138 +97,48 @@ "rest", "web service" ], - "time": "2015-05-20 03:47:55" + "time": "2014-01-28 22:29:15" }, { - "name": "guzzlehttp/ringphp", - "version": "1.1.0", + "name": "symfony/event-dispatcher", + "version": "v2.8.0", "source": { "type": "git", - "url": "https://github.com/guzzle/RingPHP.git", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/RingPHP/zipball/dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", - "reference": "dbbb91d7f6c191e5e405e900e3102ac7f261bc0b", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc", + "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc", "shasum": "" }, "require": { - "guzzlehttp/streams": "~3.0", - "php": ">=5.4.0", - "react/promise": "~2.0" + "php": ">=5.3.9" }, "require-dev": { - "ext-curl": "*", - "phpunit/phpunit": "~4.0" + "psr/log": "~1.0", + "symfony/config": "~2.0,>=2.0.5|~3.0.0", + "symfony/dependency-injection": "~2.6|~3.0.0", + "symfony/expression-language": "~2.6|~3.0.0", + "symfony/stopwatch": "~2.3|~3.0.0" }, "suggest": { - "ext-curl": "Guzzle will use specific adapters if cURL is present" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.1-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Ring\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple API and specification that abstracts away the details of HTTP into a single PHP function.", - "time": "2015-05-20 03:37:09" - }, - { - "name": "guzzlehttp/streams", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/guzzle/streams.git", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/streams/zipball/47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "reference": "47aaa48e27dae43d39fc1cea0ccf0d84ac1a2ba5", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" - }, - "require-dev": { - "phpunit/phpunit": "~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.0-dev" - } - }, - "autoload": { - "psr-4": { - "GuzzleHttp\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Michael Dowling", - "email": "mtdowling@gmail.com", - "homepage": "https://github.com/mtdowling" - } - ], - "description": "Provides a simple abstraction over streams of data", - "homepage": "http://guzzlephp.org/", - "keywords": [ - "Guzzle", - "stream" - ], - "time": "2014-10-12 19:18:40" - }, - { - "name": "react/promise", - "version": "v2.2.1", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/3b6fca09c7d56321057fa8867c8dbe1abf648627", - "reference": "3b6fca09c7d56321057fa8867c8dbe1abf648627", - "shasum": "" - }, - "require": { - "php": ">=5.4.0" + "symfony/dependency-injection": "", + "symfony/http-kernel": "" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { - "React\\Promise\\": "src/" + "Symfony\\Component\\EventDispatcher\\": "" }, - "files": [ - "src/functions_include.php" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -325,12 +147,17 @@ ], "authors": [ { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "time": "2015-07-03 13:48:55" + "description": "Symfony EventDispatcher Component", + "homepage": "https://symfony.com", + "time": "2015-10-30 20:15:42" } ], "packages-dev": [ @@ -2118,28 +1945,28 @@ }, { "name": "sebastian/diff", - "version": "1.3.0", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3" + "reference": "2b0112e42c338afa9ad9dfeb94d66f6d84c2f828" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/863df9687835c62aa423a22412d26fa2ebde3fd3", - "reference": "863df9687835c62aa423a22412d26fa2ebde3fd3", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/2b0112e42c338afa9ad9dfeb94d66f6d84c2f828", + "reference": "2b0112e42c338afa9ad9dfeb94d66f6d84c2f828", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~4.2" + "phpunit/phpunit": "~5" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.3-dev" + "dev-master": "1.4-dev" } }, "autoload": { @@ -2162,24 +1989,24 @@ } ], "description": "Diff implementation", - "homepage": "http://www.github.com/sebastianbergmann/diff", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ "diff" ], - "time": "2015-02-22 15:13:53" + "time": "2015-12-06 07:21:36" }, { "name": "sebastian/environment", - "version": "1.3.2", + "version": "1.3.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44" + "reference": "6e7133793a8e5a5714a551a8324337374be209df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6324c907ce7a52478eeeaede764f48733ef5ae44", - "reference": "6324c907ce7a52478eeeaede764f48733ef5ae44", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", + "reference": "6e7133793a8e5a5714a551a8324337374be209df", "shasum": "" }, "require": { @@ -2216,7 +2043,7 @@ "environment", "hhvm" ], - "time": "2015-08-03 06:14:51" + "time": "2015-12-02 08:37:27" }, { "name": "sebastian/exporter", @@ -2286,16 +2113,16 @@ }, { "name": "sebastian/recursion-context", - "version": "1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba" + "reference": "913401df809e99e4f47b27cdd781f4a258d58791" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/994d4a811bafe801fb06dccbee797863ba2792ba", - "reference": "994d4a811bafe801fb06dccbee797863ba2792ba", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/913401df809e99e4f47b27cdd781f4a258d58791", + "reference": "913401df809e99e4f47b27cdd781f4a258d58791", "shasum": "" }, "require": { @@ -2335,7 +2162,7 @@ ], "description": "Provides functionality to recursively process PHP variables", "homepage": "http://www.github.com/sebastianbergmann/recursion-context", - "time": "2015-06-21 08:04:50" + "time": "2015-11-11 19:50:13" }, { "name": "sebastian/version", @@ -2374,20 +2201,20 @@ }, { "name": "seld/jsonlint", - "version": "1.3.1", + "version": "1.4.0", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4" + "reference": "66834d3e3566bb5798db7294619388786ae99394" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/863ae85c6d3ef60ca49cb12bd051c4a0648c40c4", - "reference": "863ae85c6d3ef60ca49cb12bd051c4a0648c40c4", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", + "reference": "66834d3e3566bb5798db7294619388786ae99394", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": "^5.3 || ^7.0" }, "bin": [ "bin/jsonlint" @@ -2416,36 +2243,39 @@ "parser", "validator" ], - "time": "2015-01-04 21:18:15" + "time": "2015-11-21 02:21:41" }, { "name": "symfony/config", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "831f88908b51b9ce945f5e6f402931d1ac544423" + "reference": "f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/831f88908b51b9ce945f5e6f402931d1ac544423", - "reference": "831f88908b51b9ce945f5e6f402931d1ac544423", + "url": "https://api.github.com/repos/symfony/config/zipball/f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae", + "reference": "f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae", "shasum": "" }, "require": { "php": ">=5.3.9", - "symfony/filesystem": "~2.3" + "symfony/filesystem": "~2.3|~3.0.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Config\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2463,29 +2293,30 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2015-11-23 20:38:01" }, { "name": "symfony/console", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "5efd632294c8320ea52492db22292ff853a43766" + "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/5efd632294c8320ea52492db22292ff853a43766", - "reference": "5efd632294c8320ea52492db22292ff853a43766", + "url": "https://api.github.com/repos/symfony/console/zipball/d232bfc100dfd32b18ccbcab4bcc8f28697b7e41", + "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" }, "require-dev": { "psr/log": "~1.0", - "symfony/event-dispatcher": "~2.1", - "symfony/process": "~2.1" + "symfony/event-dispatcher": "~2.1|~3.0.0", + "symfony/process": "~2.1|~3.0.0" }, "suggest": { "psr/log": "For using the console logger", @@ -2495,13 +2326,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Console\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2519,46 +2353,38 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-10-20 14:38:46" + "time": "2015-11-30 12:35:10" }, { - "name": "symfony/event-dispatcher", - "version": "v2.7.6", + "name": "symfony/filesystem", + "version": "v2.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8" + "url": "https://github.com/symfony/filesystem.git", + "reference": "3e661a0d521ac67496515fa6e6704bd61bcfff60" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/87a5db5ea887763fa3a31a5471b512ff1596d9b8", - "reference": "87a5db5ea887763fa3a31a5471b512ff1596d9b8", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/3e661a0d521ac67496515fa6e6704bd61bcfff60", + "reference": "3e661a0d521ac67496515fa6e6704bd61bcfff60", "shasum": "" }, "require": { "php": ">=5.3.9" }, - "require-dev": { - "psr/log": "~1.0", - "symfony/config": "~2.0,>=2.0.5", - "symfony/dependency-injection": "~2.6", - "symfony/expression-language": "~2.6", - "symfony/stopwatch": "~2.3" - }, - "suggest": { - "symfony/dependency-injection": "", - "symfony/http-kernel": "" - }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - } + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2574,22 +2400,22 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony EventDispatcher Component", + "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2015-11-23 10:19:46" }, { - "name": "symfony/filesystem", - "version": "v2.7.6", + "name": "symfony/finder", + "version": "v2.8.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "56fd6df73be859323ff97418d97edc1d756df6df" + "url": "https://github.com/symfony/finder.git", + "reference": "ead9b07af4ba77b6507bee697396a5c79e633f08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/56fd6df73be859323ff97418d97edc1d756df6df", - "reference": "56fd6df73be859323ff97418d97edc1d756df6df", + "url": "https://api.github.com/repos/symfony/finder/zipball/ead9b07af4ba77b6507bee697396a5c79e633f08", + "reference": "ead9b07af4ba77b6507bee697396a5c79e633f08", "shasum": "" }, "require": { @@ -2598,13 +2424,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - } + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2620,37 +2449,40 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Filesystem Component", + "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2015-10-18 20:23:18" + "time": "2015-10-30 20:15:42" }, { - "name": "symfony/finder", - "version": "v2.7.6", + "name": "symfony/polyfill-mbstring", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "0b6a8940385311a24e060ec1fe35680e17c74497" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d", - "reference": "2ffb4e9598db3c48eb6d0ae73b04bbf09280c59d", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497", + "reference": "0b6a8940385311a24e060ec1fe35680e17c74497", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "1.0-dev" } }, "autoload": { "psr-4": { - "Symfony\\Component\\Finder\\": "" - } + "Symfony\\Polyfill\\Mbstring\\": "" + }, + "files": [ + "bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2658,30 +2490,37 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony Finder Component", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "time": "2015-11-04 20:28:58" }, { "name": "symfony/process", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7" + "reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/4a959dd4e19c2c5d7512689413921e0a74386ec7", - "reference": "4a959dd4e19c2c5d7512689413921e0a74386ec7", + "url": "https://api.github.com/repos/symfony/process/zipball/1b988a88e3551102f3c2d9e1d47a18c3a78d6312", + "reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312", "shasum": "" }, "require": { @@ -2690,13 +2529,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Process\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2714,20 +2556,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-10-23 14:47:27" + "time": "2015-11-30 12:35:10" }, { "name": "symfony/stopwatch", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "f8ab957c17e4b85a73c4df03bdf94ee597f2bd55" + "reference": "5f1e2ebd1044da542d2b9510527836e8be92b1cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/f8ab957c17e4b85a73c4df03bdf94ee597f2bd55", - "reference": "f8ab957c17e4b85a73c4df03bdf94ee597f2bd55", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5f1e2ebd1044da542d2b9510527836e8be92b1cb", + "reference": "5f1e2ebd1044da542d2b9510527836e8be92b1cb", "shasum": "" }, "require": { @@ -2736,13 +2578,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Stopwatch\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2760,33 +2605,34 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2015-10-12 12:42:24" + "time": "2015-10-30 20:15:42" }, { "name": "symfony/translation", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8" + "reference": "6772657767649fc3b31df12705194fb4af11ef98" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/6ccd9289ec1c71d01a49d83480de3b5293ce30c8", - "reference": "6ccd9289ec1c71d01a49d83480de3b5293ce30c8", + "url": "https://api.github.com/repos/symfony/translation/zipball/6772657767649fc3b31df12705194fb4af11ef98", + "reference": "6772657767649fc3b31df12705194fb4af11ef98", "shasum": "" }, "require": { - "php": ">=5.3.9" + "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/config": "<2.7" }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~2.7", - "symfony/intl": "~2.4", - "symfony/yaml": "~2.2" + "symfony/config": "~2.8", + "symfony/intl": "~2.4|~3.0.0", + "symfony/yaml": "~2.2|~3.0.0" }, "suggest": { "psr/log": "To use logging capability in translator", @@ -2796,13 +2642,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Translation\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2820,37 +2669,36 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2015-10-27 15:38:06" + "time": "2015-11-18 13:45:00" }, { "name": "symfony/validator", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "df9021e689aa3d08367881e7f8917219fabe5e64" + "reference": "8c42b96f5b23f0642c1a518addafcef8077154a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/df9021e689aa3d08367881e7f8917219fabe5e64", - "reference": "df9021e689aa3d08367881e7f8917219fabe5e64", + "url": "https://api.github.com/repos/symfony/validator/zipball/8c42b96f5b23f0642c1a518addafcef8077154a2", + "reference": "8c42b96f5b23f0642c1a518addafcef8077154a2", "shasum": "" }, "require": { "php": ">=5.3.9", - "symfony/translation": "~2.4" + "symfony/translation": "~2.4|~3.0.0" }, "require-dev": { "doctrine/annotations": "~1.0", "doctrine/cache": "~1.0", - "doctrine/common": "~2.3", "egulias/email-validator": "~1.2,>=1.2.1", - "symfony/config": "~2.2", - "symfony/expression-language": "~2.4", - "symfony/http-foundation": "~2.1", - "symfony/intl": "~2.4", - "symfony/property-access": "~2.3", - "symfony/yaml": "~2.0,>=2.0.5" + "symfony/config": "~2.2|~3.0.0", + "symfony/expression-language": "~2.4|~3.0.0", + "symfony/http-foundation": "~2.1|~3.0.0", + "symfony/intl": "~2.4|~3.0.0", + "symfony/property-access": "~2.3|~3.0.0", + "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, "suggest": { "doctrine/annotations": "For using the annotation mapping. You will also need doctrine/cache.", @@ -2866,13 +2714,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Validator\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2890,20 +2741,20 @@ ], "description": "Symfony Validator Component", "homepage": "https://symfony.com", - "time": "2015-10-18 20:23:18" + "time": "2015-11-20 14:39:26" }, { "name": "symfony/yaml", - "version": "v2.7.6", + "version": "v2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d" + "reference": "f79824187de95064a2f5038904c4d7f0227fedb5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/eca9019c88fbe250164affd107bc8057771f3f4d", - "reference": "eca9019c88fbe250164affd107bc8057771f3f4d", + "url": "https://api.github.com/repos/symfony/yaml/zipball/f79824187de95064a2f5038904c4d7f0227fedb5", + "reference": "f79824187de95064a2f5038904c4d7f0227fedb5", "shasum": "" }, "require": { @@ -2912,13 +2763,16 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-master": "2.8-dev" } }, "autoload": { "psr-4": { "Symfony\\Component\\Yaml\\": "" - } + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2936,20 +2790,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-10-11 09:39:48" + "time": "2015-11-30 12:35:10" }, { "name": "twig/twig", - "version": "v1.22.3", + "version": "v1.23.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "ebfc36b7e77b0c1175afe30459cf943010245540" + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/ebfc36b7e77b0c1175afe30459cf943010245540", - "reference": "ebfc36b7e77b0c1175afe30459cf943010245540", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", "shasum": "" }, "require": { @@ -2962,7 +2816,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.22-dev" + "dev-master": "1.23-dev" } }, "autoload": { @@ -2997,7 +2851,7 @@ "keywords": [ "templating" ], - "time": "2015-10-13 07:07:02" + "time": "2015-11-05 12:49:06" }, { "name": "zendframework/zend-cache", @@ -3667,7 +3521,7 @@ "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=5.4" + "php": ">=5.3" }, "platform-dev": [] } diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index c6f41bce5..dec0e7637 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -1,34 +1,25 @@ _client = new Client(array( - 'base_url' => $baseUri, - 'defaults' => array( + $this->_client = new Client($baseUri, + array( + 'plugins' => array(new Guzzle\Plugin\Cache\CachePlugin()), + 'debug' => false, + 'request.options' => array( 'headers' => array( 'Authorization' => "api_key {$apiKey}", - 'Content-Type' => 'application/json', - 'User-Agent' => 'PHPClient/' . LDClient::VERSION + 'Content-Type' => 'application/json' ), - 'debug' => false, 'timeout' => $options['timeout'], 'connect_timeout' => $options['connect_timeout'] ) )); - - if (!isset($options['cache_storage'])) { - $csOptions = array('validate' => false); - } - else { - $csOptions = array('storage' => $options['cache_storage'], 'validate' => false); - } - - CacheSubscriber::attach($this->_client, $csOptions); + $this->_client->setUserAgent('PHPClient/' . LDClient::VERSION); } From d1532c8584981d8235d0394f7bb7990219f5edfc Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 13:21:26 -0800 Subject: [PATCH 02/18] Add CURLOPT_TCP_NODELAY option --- src/LaunchDarkly/GuzzleFeatureRequester.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index dec0e7637..181c10f9b 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -10,6 +10,7 @@ function __construct($baseUri, $apiKey, $options) { array( 'plugins' => array(new Guzzle\Plugin\Cache\CachePlugin()), 'debug' => false, + 'curl.options' => array('CURLOPT_TCP_NODELAY' => 1), 'request.options' => array( 'headers' => array( 'Authorization' => "api_key {$apiKey}", From 84f56619c3e65a078a445b50c58be290e0210be1 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 13:33:18 -0800 Subject: [PATCH 03/18] Try specifying guzzle/guzzle --- composer.json | 2 +- composer.lock | 39 +++++++++++++++++++++------------------ 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/composer.json b/composer.json index 5ec1aca15..e59104e03 100644 --- a/composer.json +++ b/composer.json @@ -15,7 +15,7 @@ ], "require": { "php": ">=5.3", - "guzzlehttp/guzzle": "3.8.*" + "guzzle/guzzle": "3.9.*" }, "require-dev": { "phpunit/phpunit": "4.3.*", diff --git a/composer.lock b/composer.lock index 5bb16d5fa..a1cda42cf 100644 --- a/composer.lock +++ b/composer.lock @@ -4,27 +4,27 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d5329067b06bb2f70c0f6669cde98596", - "content-hash": "69555c83d13efbe2e5ac8177b9fa5d45", + "hash": "d2553358c98b625f026f02c26cbe5948", + "content-hash": "fc7d8f995bbf32cef906a24fef145673", "packages": [ { - "name": "guzzlehttp/guzzle", - "version": "v3.8.1", + "name": "guzzle/guzzle", + "version": "v3.9.3", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle.git", - "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba" + "url": "https://github.com/guzzle/guzzle3.git", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/4de0618a01b34aa1c8c33a3f13f396dcd3882eba", - "reference": "4de0618a01b34aa1c8c33a3f13f396dcd3882eba", + "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", + "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", "shasum": "" }, "require": { "ext-curl": "*", "php": ">=5.3.3", - "symfony/event-dispatcher": ">=2.1" + "symfony/event-dispatcher": "~2.1" }, "replace": { "guzzle/batch": "self.version", @@ -51,18 +51,21 @@ "guzzle/stream": "self.version" }, "require-dev": { - "doctrine/cache": "*", - "monolog/monolog": "1.*", + "doctrine/cache": "~1.3", + "monolog/monolog": "~1.0", "phpunit/phpunit": "3.7.*", - "psr/log": "1.0.*", - "symfony/class-loader": "*", - "zendframework/zend-cache": "<2.3", - "zendframework/zend-log": "<2.3" + "psr/log": "~1.0", + "symfony/class-loader": "~2.1", + "zendframework/zend-cache": "2.*,<2.3", + "zendframework/zend-log": "2.*,<2.3" + }, + "suggest": { + "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated." }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.8-dev" + "dev-master": "3.9-dev" } }, "autoload": { @@ -86,7 +89,7 @@ "homepage": "https://github.com/guzzle/guzzle/contributors" } ], - "description": "Guzzle is a PHP HTTP client library and framework for building RESTful web service clients", + "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", "homepage": "http://guzzlephp.org/", "keywords": [ "client", @@ -97,7 +100,7 @@ "rest", "web service" ], - "time": "2014-01-28 22:29:15" + "time": "2015-03-18 18:23:50" }, { "name": "symfony/event-dispatcher", From a14453a8a0c31185e4418be33e2dab9c3b0e9efe Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 13:55:07 -0800 Subject: [PATCH 04/18] Clean up cache storage --- src/LaunchDarkly/GuzzleFeatureRequester.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index 181c10f9b..cb21e50b8 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -1,14 +1,15 @@ _client = new Client($baseUri, array( - 'plugins' => array(new Guzzle\Plugin\Cache\CachePlugin()), 'debug' => false, 'curl.options' => array('CURLOPT_TCP_NODELAY' => 1), 'request.options' => array( @@ -21,6 +22,12 @@ function __construct($baseUri, $apiKey, $options) { ) )); $this->_client->setUserAgent('PHPClient/' . LDClient::VERSION); + + if (isset($options['cache_storage'])) { + $cachePlugin = new CachePlugin(array('storage' => $options['cache_storage'], 'validate' => false)); + $this->_client->addSubscriber($cachePlugin); + } + } From 60f2eefc75e91c713ab0e9e5f84dfa906d15e88d Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 14:26:12 -0800 Subject: [PATCH 05/18] Handle an API difference between guzzle 3 and guzzle 5 --- src/LaunchDarkly/GuzzleFeatureRequester.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index cb21e50b8..78e6c8954 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -39,7 +39,8 @@ function __construct($baseUri, $apiKey, $options) { */ public function get($key) { try { - $response = $this->_client->get("/api/eval/features/$key"); + $request = $this->_client->get("/api/eval/features/$key"); + $response = $request->send(); return $response->json(); } catch (BadResponseException $e) { $code = $e->getResponse()->getStatusCode(); From bba17197622d18819bc710b44f99ea8d2d85865b Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 7 Dec 2015 20:04:56 -0800 Subject: [PATCH 06/18] Point to a forked copy of guzzle 3, to fix bug with isFresh() check --- composer.json | 8 +++++++- composer.lock | 30 +++++++++++++++--------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index e59104e03..2a6153c62 100644 --- a/composer.json +++ b/composer.json @@ -5,6 +5,12 @@ "launchdarkly", "launchdarkly php" ], + "repositories": [ + { + "type": "git", + "url": "git://github.com/launchdarkly/guzzle3.git" + } + ], "homepage": "https://github.com/launchdarkly/php-client", "license": "Apache-2.0", "authors": [ @@ -15,7 +21,7 @@ ], "require": { "php": ">=5.3", - "guzzle/guzzle": "3.9.*" + "guzzle/guzzle": "dev-master#cc55d8c884f44c68bedf0d6077ce9022de0b2305" }, "require-dev": { "phpunit/phpunit": "4.3.*", diff --git a/composer.lock b/composer.lock index a1cda42cf..30489c0e7 100644 --- a/composer.lock +++ b/composer.lock @@ -4,22 +4,16 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "d2553358c98b625f026f02c26cbe5948", - "content-hash": "fc7d8f995bbf32cef906a24fef145673", + "hash": "55d557209f91568532d25042be26de06", + "content-hash": "64be38fda51e122b0081f7a9bd5ee429", "packages": [ { "name": "guzzle/guzzle", - "version": "v3.9.3", + "version": "dev-master", "source": { "type": "git", - "url": "https://github.com/guzzle/guzzle3.git", - "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9", - "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9", - "shasum": "" + "url": "git://github.com/launchdarkly/guzzle3.git", + "reference": "cc55d8c884f44c68bedf0d6077ce9022de0b2305" }, "require": { "ext-curl": "*", @@ -74,7 +68,11 @@ "Guzzle\\Tests": "tests/" } }, - "notification-url": "https://packagist.org/downloads/", + "scripts": { + "test": [ + "phpunit" + ] + }, "license": [ "MIT" ], @@ -92,15 +90,15 @@ "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle", "homepage": "http://guzzlephp.org/", "keywords": [ + "HTTP client", "client", "curl", "framework", "http", - "http client", "rest", "web service" ], - "time": "2015-03-18 18:23:50" + "time": "2015-12-08 02:04:01" }, { "name": "symfony/event-dispatcher", @@ -3520,7 +3518,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "guzzle/guzzle": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 2ceff9bf2d793da2d0d328134e3a7aa47e6b95e2 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 8 Dec 2015 14:41:47 -0800 Subject: [PATCH 07/18] Bump guzzle version --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 2a6153c62..c985a4390 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ ], "require": { "php": ">=5.3", - "guzzle/guzzle": "dev-master#cc55d8c884f44c68bedf0d6077ce9022de0b2305" + "guzzle/guzzle": "dev-master#7c722287e5d12bf162da98ad0f12087ed904803f" }, "require-dev": { "phpunit/phpunit": "4.3.*", From adf365568409857ae22169bbef04348e3131e8c3 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 8 Dec 2015 14:44:58 -0800 Subject: [PATCH 08/18] Updated composer lock file --- composer.lock | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/composer.lock b/composer.lock index 30489c0e7..92a0ae21a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "55d557209f91568532d25042be26de06", - "content-hash": "64be38fda51e122b0081f7a9bd5ee429", + "hash": "cd334a84603889124e9d169092be41da", + "content-hash": "4f8b242953aacc692be716c0637eb7d0", "packages": [ { "name": "guzzle/guzzle", @@ -13,7 +13,7 @@ "source": { "type": "git", "url": "git://github.com/launchdarkly/guzzle3.git", - "reference": "cc55d8c884f44c68bedf0d6077ce9022de0b2305" + "reference": "7c722287e5d12bf162da98ad0f12087ed904803f" }, "require": { "ext-curl": "*", @@ -98,7 +98,7 @@ "rest", "web service" ], - "time": "2015-12-08 02:04:01" + "time": "2015-12-08 22:41:05" }, { "name": "symfony/event-dispatcher", @@ -1946,23 +1946,23 @@ }, { "name": "sebastian/diff", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "2b0112e42c338afa9ad9dfeb94d66f6d84c2f828" + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/2b0112e42c338afa9ad9dfeb94d66f6d84c2f828", - "reference": "2b0112e42c338afa9ad9dfeb94d66f6d84c2f828", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/13edfd8706462032c2f52b4b862974dd46b71c9e", + "reference": "13edfd8706462032c2f52b4b862974dd46b71c9e", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~5" + "phpunit/phpunit": "~4.8" }, "type": "library", "extra": { @@ -1994,7 +1994,7 @@ "keywords": [ "diff" ], - "time": "2015-12-06 07:21:36" + "time": "2015-12-08 07:14:41" }, { "name": "sebastian/environment", From 430ec0d157d8f3844852dc39fcc2a75cf3d197a8 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 8 Dec 2015 15:38:58 -0800 Subject: [PATCH 09/18] Bump guzzle3 again --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index c985a4390..86edeba99 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,7 @@ ], "require": { "php": ">=5.3", - "guzzle/guzzle": "dev-master#7c722287e5d12bf162da98ad0f12087ed904803f" + "guzzle/guzzle": "dev-master#ecb935d2d0ecd8cddae4dcfb90515cac5e2cb023" }, "require-dev": { "phpunit/phpunit": "4.3.*", From 7f07fd03a2b87e150ad4c304a73c9dc00ed433f8 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 26 Sep 2016 13:23:26 -0700 Subject: [PATCH 10/18] WIP to update the php 5.3 branch with v2 --- CHANGELOG.md | 18 + CONTRIBUTING.md | 4 + LICENSE => LICENSE.txt | 2 +- README.md | 46 +- VERSION | 2 +- circle.yml | 2 +- composer.json | 9 +- composer.lock | 828 ++++++++++-------- integration-tests/LDDFeatureRequesterTest.php | 12 +- src/LaunchDarkly/Clause.php | 99 +++ src/LaunchDarkly/EvaluationException.php | 15 + src/LaunchDarkly/EventProcessor.php | 21 +- src/LaunchDarkly/FeatureFlag.php | 367 ++++++++ src/LaunchDarkly/FeatureRep.php | 90 -- src/LaunchDarkly/FeatureRequester.php | 9 +- src/LaunchDarkly/GuzzleFeatureRequester.php | 71 +- src/LaunchDarkly/LDClient.php | 258 +++--- src/LaunchDarkly/LDDFeatureRequester.php | 41 +- src/LaunchDarkly/LDUser.php | 69 +- src/LaunchDarkly/Operators.php | 118 +++ src/LaunchDarkly/Rule.php | 42 + src/LaunchDarkly/TargetRule.php | 81 -- src/LaunchDarkly/Util.php | 50 ++ src/LaunchDarkly/Variation.php | 55 -- src/LaunchDarkly/VariationOrRollout.php | 89 ++ tests/FeatureFlagTest.php | 144 +++ tests/FeatureRepTest.php | 89 -- tests/LDClientTest.php | 34 +- tests/LDUserTest.php | 15 + tests/OperatorsTest.php | 52 ++ 30 files changed, 1880 insertions(+), 852 deletions(-) create mode 100644 CHANGELOG.md create mode 100644 CONTRIBUTING.md rename LICENSE => LICENSE.txt (94%) create mode 100644 src/LaunchDarkly/Clause.php create mode 100644 src/LaunchDarkly/EvaluationException.php create mode 100644 src/LaunchDarkly/FeatureFlag.php delete mode 100644 src/LaunchDarkly/FeatureRep.php create mode 100644 src/LaunchDarkly/Operators.php create mode 100644 src/LaunchDarkly/Rule.php delete mode 100644 src/LaunchDarkly/TargetRule.php create mode 100644 src/LaunchDarkly/Util.php delete mode 100644 src/LaunchDarkly/Variation.php create mode 100644 src/LaunchDarkly/VariationOrRollout.php create mode 100644 tests/FeatureFlagTest.php delete mode 100644 tests/FeatureRepTest.php create mode 100644 tests/OperatorsTest.php diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..2e548727c --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,18 @@ +# Change log + +All notable changes to the LaunchDarkly Java SDK will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org). + +## [2.0.0] - 2016-08-08 +### Added +- Support for multivariate feature flags. In addition to booleans, feature flags can now return numbers, strings, dictionaries, or arrays via the `variation` method. +- New `allFlags` method returns all flag values for a specified user. +- New `secureModeHash` function computes a hash suitable for the new LaunchDarkly JavaScript client's secure mode feature. + +### Changed +- The `FeatureRep` data model has been replaced with `FeatureFlag`. `FeatureFlag` is not generic. + +### Deprecated +- The `toggle` call has been deprecated in favor of `variation`. + +### Removed +- The `getFlag` function has been removed. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..3ec616bd2 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,4 @@ +Contributing to the LaunchDarkly SDK for PHP +================================================ + +We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/v1.0/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work. \ No newline at end of file diff --git a/LICENSE b/LICENSE.txt similarity index 94% rename from LICENSE rename to LICENSE.txt index b5c790da8..e82de54ed 100644 --- a/LICENSE +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright 2015 Catamorphic, Co. +Copyright 2016 Catamorphic, Co. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 677a39bc5..ed88128c0 100644 --- a/README.md +++ b/README.md @@ -11,14 +11,16 @@ Quick setup 0. Install the PHP SDK with [Composer](https://getcomposer.org/) php composer.phar require launchdarkly/launchdarkly-php + php composer.phar require "guzzlehttp/guzzle:6.2.1" + php composer.phar require "kevinrob/guzzle-cache-middleware": "1.4.1" 1. After installing, require Composer's autoloader: require 'vendor/autoload.php'; -2. Create a new LDClient with your API key: +2. Create a new LDClient with your SDK key: - $client = new LaunchDarkly\LDClient("your_api_key"); + $client = new LaunchDarkly\LDClient("your_sdk_key"); Your first feature flag ----------------------- @@ -28,21 +30,55 @@ Your first feature flag 2. In your application code, use the feature's key to check whether the flag is on for each user: $user = new LaunchDarkly\LDUser("user@test.com"); - if ($client->toggle("your.flag.key", $user)) { + if ($client->variation("your.flag.key", $user)) { # application code to show the feature } else { # the code to run if the feature is off } +Fetching flags +-------------- + +There are two approaches to fetching the flag rules from LaunchDarkly: + +* Making HTTP requests (using Guzzle) +* Setting up the [ld-daemon](https://github.com/launchdarkly/ld-daemon) to store the flags in Redis + +Using Guzzle +============ + +To use Guzzle it must be required as a dependency: + + php composer.phar require "guzzlehttp/guzzle:6.2.1" + php composer.phar require "kevinrob/guzzle-cache-middleware": "1.4.1" + +It will then be used as the default way of fetching flags. + +Using Redis +=========== + +1. Require Predis as a dependency: + + php composer.phar require "predis/predis:1.0.*" + +2. Create the LDClient with the Redis feature requester as an option: + + $client = new LaunchDarkly\LDClient("your_sdk_key", ['feature_requester_class' => 'LaunchDarkly\LDDFeatureRequester']); + Learn more ----------- -Check out our [documentation](http://docs.launchdarkly.com) for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the [complete reference guide for this SDK](http://docs.launchdarkly.com/v1.0/docs/php-sdk-reference). +Check out our [documentation](http://docs.launchdarkly.com) for in-depth instructions on configuring and using LaunchDarkly. You can also head straight to the [complete reference guide for this SDK](http://docs.launchdarkly.com/docs/php-sdk-reference). + +Testing +------- + +We run integration tests for all our SDKs using a centralized test harness. This approach gives us the ability to test for consistency across SDKs, as well as test networking behavior in a long-running application. These tests cover each method in the SDK, and verify that event sending, flag evaluation, stream reconnection, and other aspects of the SDK all behave correctly. Contributing ------------ -We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/v1.0/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work. +We encourage pull-requests and other contributions from the community. We've also published an [SDK contributor's guide](http://docs.launchdarkly.com/docs/sdk-contributors-guide) that provides a detailed explanation of how our SDKs work. About LaunchDarkly ----------- diff --git a/VERSION b/VERSION index a0a15177f..359a5b952 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.6.3 \ No newline at end of file +2.0.0 \ No newline at end of file diff --git a/circle.yml b/circle.yml index 77aae221c..7347245d3 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: php: - version: 5.4.37 + version: 5.5.21 test: override: diff --git a/composer.json b/composer.json index 86edeba99..892db53c7 100644 --- a/composer.json +++ b/composer.json @@ -21,14 +21,17 @@ ], "require": { "php": ">=5.3", - "guzzle/guzzle": "dev-master#ecb935d2d0ecd8cddae4dcfb90515cac5e2cb023" + "guzzle/guzzle": "dev-master#ecb935d2d0ecd8cddae4dcfb90515cac5e2cb023", + "psr/log": "1.0.0" }, "require-dev": { - "phpunit/phpunit": "4.3.*", + "phpunit/phpunit": "4.8.26", "phpdocumentor/phpdocumentor": "2.*", - "predis/predis": "1.0.*" + "predis/predis": "1.0.*", + "zendframework/zend-serializer": "2.4.*" }, "suggested": { + "monolog/monolog": "1.21.0", "predis/predis": "1.0.*" }, "autoload": { diff --git a/composer.lock b/composer.lock index 92a0ae21a..61426104a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,8 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "cd334a84603889124e9d169092be41da", - "content-hash": "4f8b242953aacc692be716c0637eb7d0", + "hash": "68cb258a6ff921174b988769600d2cb2", + "content-hash": "03dd4eab149acdc05b15bc1bf2ea235b", "packages": [ { "name": "guzzle/guzzle", @@ -13,7 +13,7 @@ "source": { "type": "git", "url": "git://github.com/launchdarkly/guzzle3.git", - "reference": "7c722287e5d12bf162da98ad0f12087ed904803f" + "reference": "ecb935d2d0ecd8cddae4dcfb90515cac5e2cb023" }, "require": { "ext-curl": "*", @@ -98,20 +98,58 @@ "rest", "web service" ], - "time": "2015-12-08 22:41:05" + "time": "2015-12-08 23:21:31" + }, + { + "name": "psr/log", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", + "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-0": { + "Psr\\Log\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "time": "2012-12-21 11:40:51" }, { "name": "symfony/event-dispatcher", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc" + "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a5eb815363c0388e83247e7e9853e5dbc14999cc", - "reference": "a5eb815363c0388e83247e7e9853e5dbc14999cc", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/889983a79a043dfda68f38c38b6dba092dd49cd8", + "reference": "889983a79a043dfda68f38c38b6dba092dd49cd8", "shasum": "" }, "require": { @@ -158,7 +196,7 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2016-07-28 16:56:28" } ], "packages-dev": [ @@ -703,36 +741,41 @@ }, { "name": "jms/serializer", - "version": "0.16.0", + "version": "1.1.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/serializer.git", - "reference": "c8a171357ca92b6706e395c757f334902d430ea9" + "reference": "fe13a1f993ea3456e195b7820692f2eb2b6bbb48" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/c8a171357ca92b6706e395c757f334902d430ea9", - "reference": "c8a171357ca92b6706e395c757f334902d430ea9", + "url": "https://api.github.com/repos/schmittjoh/serializer/zipball/fe13a1f993ea3456e195b7820692f2eb2b6bbb48", + "reference": "fe13a1f993ea3456e195b7820692f2eb2b6bbb48", "shasum": "" }, "require": { "doctrine/annotations": "1.*", + "doctrine/instantiator": "~1.0.3", "jms/metadata": "~1.1", "jms/parser-lib": "1.*", - "php": ">=5.3.2", + "php": ">=5.4.0", "phpcollection/phpcollection": "~0.1" }, + "conflict": { + "twig/twig": "<1.12" + }, "require-dev": { "doctrine/orm": "~2.1", "doctrine/phpcr-odm": "~1.0.1", "jackalope/jackalope-doctrine-dbal": "1.0.*", + "phpunit/phpunit": "~4.0", "propel/propel1": "~1.7", "symfony/filesystem": "2.*", "symfony/form": "~2.1", "symfony/translation": "~2.0", "symfony/validator": "~2.0", "symfony/yaml": "2.*", - "twig/twig": ">=1.8,<2.0-dev" + "twig/twig": "~1.12|~2.0" }, "suggest": { "symfony/yaml": "Required if you'd like to serialize data to YAML format." @@ -740,7 +783,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.15-dev" + "dev-master": "1.1-dev" } }, "autoload": { @@ -754,10 +797,8 @@ ], "authors": [ { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" } ], "description": "Library for (de-)serializing data of any complexity; supports XML, JSON, and YAML.", @@ -769,24 +810,24 @@ "serialization", "xml" ], - "time": "2014-03-18 08:39:00" + "time": "2015-10-27 09:24:41" }, { "name": "justinrainbow/json-schema", - "version": "1.5.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/justinrainbow/json-schema.git", - "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe" + "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/a4bee9f4b344b66e0a0d96c7afae1e92edf385fe", - "reference": "a4bee9f4b344b66e0a0d96c7afae1e92edf385fe", + "url": "https://api.github.com/repos/justinrainbow/json-schema/zipball/cc84765fb7317f6b07bd8ac78364747f95b86341", + "reference": "cc84765fb7317f6b07bd8ac78364747f95b86341", "shasum": "" }, "require": { - "php": ">=5.3.2" + "php": ">=5.3.29" }, "require-dev": { "json-schema/json-schema-test-suite": "1.1.0", @@ -799,7 +840,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4.x-dev" + "dev-master": "1.6.x-dev" } }, "autoload": { @@ -835,7 +876,7 @@ "json", "schema" ], - "time": "2015-09-08 22:28:04" + "time": "2016-01-25 15:43:01" }, { "name": "kherge/version", @@ -882,16 +923,16 @@ }, { "name": "monolog/monolog", - "version": "1.17.2", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/Seldaek/monolog.git", - "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24" + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/monolog/zipball/bee7f0dc9c3e0b69a6039697533dca1e845c8c24", - "reference": "bee7f0dc9c3e0b69a6039697533dca1e845c8c24", + "url": "https://api.github.com/repos/Seldaek/monolog/zipball/f42fbdfd53e306bda545845e4dbfd3e72edb4952", + "reference": "f42fbdfd53e306bda545845e4dbfd3e72edb4952", "shasum": "" }, "require": { @@ -906,13 +947,13 @@ "doctrine/couchdb": "~1.0@dev", "graylog2/gelf-php": "~1.0", "jakub-onderka/php-parallel-lint": "0.9", + "php-amqplib/php-amqplib": "~2.4", "php-console/php-console": "^3.1.3", "phpunit/phpunit": "~4.5", "phpunit/phpunit-mock-objects": "2.3.0", - "raven/raven": "^0.13", "ruflin/elastica": ">=0.90 <3.0", - "swiftmailer/swiftmailer": "~5.3", - "videlalvaro/php-amqplib": "~2.4" + "sentry/sentry": "^0.13", + "swiftmailer/swiftmailer": "~5.3" }, "suggest": { "aws/aws-sdk-php": "Allow sending log messages to AWS services like DynamoDB", @@ -920,16 +961,17 @@ "ext-amqp": "Allow sending log messages to an AMQP server (1.0+ required)", "ext-mongo": "Allow sending log messages to a MongoDB server", "graylog2/gelf-php": "Allow sending log messages to a GrayLog2 server", + "mongodb/mongodb": "Allow sending log messages to a MongoDB server via PHP Driver", + "php-amqplib/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib", "php-console/php-console": "Allow sending log messages to Google Chrome", - "raven/raven": "Allow sending log messages to a Sentry server", "rollbar/rollbar": "Allow sending log messages to Rollbar", "ruflin/elastica": "Allow sending log messages to an Elastic Search server", - "videlalvaro/php-amqplib": "Allow sending log messages to an AMQP server using php-amqplib" + "sentry/sentry": "Allow sending log messages to a Sentry server" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.16.x-dev" + "dev-master": "2.0.x-dev" } }, "autoload": { @@ -955,36 +997,36 @@ "logging", "psr-3" ], - "time": "2015-10-14 12:51:02" + "time": "2016-07-29 03:23:52" }, { "name": "nikic/php-parser", - "version": "v0.9.5", + "version": "v1.4.1", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "ef70767475434bdb3615b43c327e2cae17ef12eb" + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ef70767475434bdb3615b43c327e2cae17ef12eb", - "reference": "ef70767475434bdb3615b43c327e2cae17ef12eb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", + "reference": "f78af2c9c86107aa1a34cd1dbb5bbe9eeb0d9f51", "shasum": "" }, "require": { "ext-tokenizer": "*", - "php": ">=5.2" + "php": ">=5.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "0.9-dev" + "dev-master": "1.4-dev" } }, "autoload": { - "psr-0": { - "PHPParser": "lib/" - } + "files": [ + "lib/bootstrap.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1000,20 +1042,20 @@ "parser", "php" ], - "time": "2014-07-23 18:24:17" + "time": "2015-09-19 14:15:08" }, { "name": "phpcollection/phpcollection", - "version": "0.4.0", + "version": "0.5.0", "source": { "type": "git", "url": "https://github.com/schmittjoh/php-collection.git", - "reference": "b8bf55a0a929ca43b01232b36719f176f86c7e83" + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/b8bf55a0a929ca43b01232b36719f176f86c7e83", - "reference": "b8bf55a0a929ca43b01232b36719f176f86c7e83", + "url": "https://api.github.com/repos/schmittjoh/php-collection/zipball/f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", + "reference": "f2bcff45c0da7c27991bbc1f90f47c4b7fb434a6", "shasum": "" }, "require": { @@ -1022,7 +1064,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "0.3-dev" + "dev-master": "0.4-dev" } }, "autoload": { @@ -1036,10 +1078,8 @@ ], "authors": [ { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com", - "homepage": "https://github.com/schmittjoh", - "role": "Developer of wrapped JMSSerializerBundle" + "name": "Johannes M. Schmitt", + "email": "schmittjoh@gmail.com" } ], "description": "General-Purpose Collection Library for PHP", @@ -1050,7 +1090,7 @@ "sequence", "set" ], - "time": "2014-03-11 13:46:42" + "time": "2015-05-17 12:39:23" }, { "name": "phpdocumentor/fileset", @@ -1097,23 +1137,23 @@ }, { "name": "phpdocumentor/graphviz", - "version": "1.0.3", + "version": "1.0.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/GraphViz.git", - "reference": "aa243118c8a055fc853c02802e8503c5435862f7" + "reference": "a906a90a9f230535f25ea31caf81b2323956283f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/aa243118c8a055fc853c02802e8503c5435862f7", - "reference": "aa243118c8a055fc853c02802e8503c5435862f7", + "url": "https://api.github.com/repos/phpDocumentor/GraphViz/zipball/a906a90a9f230535f25ea31caf81b2323956283f", + "reference": "a906a90a9f230535f25ea31caf81b2323956283f", "shasum": "" }, "require": { "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "~3.7" + "phpunit/phpunit": "~4.0" }, "type": "library", "autoload": { @@ -1134,32 +1174,32 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2014-07-19 06:52:59" + "time": "2016-02-02 13:00:08" }, { "name": "phpdocumentor/phpdocumentor", - "version": "v2.8.5", + "version": "v2.9.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/phpDocumentor2.git", - "reference": "adfb4affa80e8cc0134616f2d2d264dd25c243eb" + "reference": "be607da0eef9b9249c43c5b4820d25d631c73667" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/adfb4affa80e8cc0134616f2d2d264dd25c243eb", - "reference": "adfb4affa80e8cc0134616f2d2d264dd25c243eb", + "url": "https://api.github.com/repos/phpDocumentor/phpDocumentor2/zipball/be607da0eef9b9249c43c5b4820d25d631c73667", + "reference": "be607da0eef9b9249c43c5b4820d25d631c73667", "shasum": "" }, "require": { "cilex/cilex": "~1.0", "erusev/parsedown": "~1.0", "herrera-io/phar-update": "1.0.3", - "jms/serializer": "~0.12", + "jms/serializer": ">=0.12", "monolog/monolog": "~1.6", "php": ">=5.3.3", "phpdocumentor/fileset": "~1.0", "phpdocumentor/graphviz": "~1.0", - "phpdocumentor/reflection": "~1.0", + "phpdocumentor/reflection": "^3.0", "phpdocumentor/reflection-docblock": "~2.0", "symfony/config": "~2.3", "symfony/console": "~2.3", @@ -1223,24 +1263,24 @@ "documentation", "phpdoc" ], - "time": "2015-07-28 06:36:40" + "time": "2016-05-22 09:50:56" }, { "name": "phpdocumentor/reflection", - "version": "1.0.7", + "version": "3.0.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "fc40c3f604ac2287eb5c314174d5109b2c699372" + "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/fc40c3f604ac2287eb5c314174d5109b2c699372", - "reference": "fc40c3f604ac2287eb5c314174d5109b2c699372", + "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", + "reference": "793bfd92d9a0fc96ae9608fb3e947c3f59fb3a0d", "shasum": "" }, "require": { - "nikic/php-parser": "~0.9.4", + "nikic/php-parser": "^1.0", "php": ">=5.3.3", "phpdocumentor/reflection-docblock": "~2.0", "psr/log": "~1.0" @@ -1277,7 +1317,7 @@ "reflection", "static analysis" ], - "time": "2014-11-14 11:43:04" + "time": "2016-05-21 08:42:32" }, { "name": "phpdocumentor/reflection-docblock", @@ -1378,6 +1418,68 @@ ], "time": "2015-07-25 16:39:46" }, + { + "name": "phpspec/prophecy", + "version": "v1.6.1", + "source": { + "type": "git", + "url": "https://github.com/phpspec/prophecy.git", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/58a8137754bc24b25740d4281399a4a3596058e0", + "reference": "58a8137754bc24b25740d4281399a4a3596058e0", + "shasum": "" + }, + "require": { + "doctrine/instantiator": "^1.0.2", + "php": "^5.3|^7.0", + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2", + "sebastian/comparator": "^1.1", + "sebastian/recursion-context": "^1.0" + }, + "require-dev": { + "phpspec/phpspec": "^2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.6.x-dev" + } + }, + "autoload": { + "psr-0": { + "Prophecy\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Konstantin Kudryashov", + "email": "ever.zet@gmail.com", + "homepage": "http://everzet.com" + }, + { + "name": "Marcello Duarte", + "email": "marcello.duarte@gmail.com" + } + ], + "description": "Highly opinionated mocking framework for PHP 5.3+", + "homepage": "https://github.com/phpspec/prophecy", + "keywords": [ + "Double", + "Dummy", + "fake", + "mock", + "spy", + "stub" + ], + "time": "2016-06-07 08:13:47" + }, { "name": "phpunit/php-code-coverage", "version": "2.2.4", @@ -1442,31 +1544,33 @@ }, { "name": "phpunit/php-file-iterator", - "version": "1.3.4", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb" + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/acd690379117b042d1c8af1fafd61bde001bf6bb", - "reference": "acd690379117b042d1c8af1fafd61bde001bf6bb", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/6150bf2c35d3fc379e50c7602b75caceaa39dbf0", + "reference": "6150bf2c35d3fc379e50c7602b75caceaa39dbf0", "shasum": "" }, "require": { "php": ">=5.3.3" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, "autoload": { "classmap": [ - "File/" + "src/" ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "" - ], "license": [ "BSD-3-Clause" ], @@ -1483,7 +1587,7 @@ "filesystem", "iterator" ], - "time": "2013-10-10 15:34:57" + "time": "2015-06-21 13:08:43" }, { "name": "phpunit/php-text-template", @@ -1528,21 +1632,24 @@ }, { "name": "phpunit/php-timer", - "version": "1.0.7", + "version": "1.0.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b" + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3e82f4e9fc92665fafd9157568e4dcb01d014e5b", - "reference": "3e82f4e9fc92665fafd9157568e4dcb01d014e5b", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/38e9124049cf1a164f1e4537caf19c99bf1eb260", + "reference": "38e9124049cf1a164f1e4537caf19c99bf1eb260", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "require-dev": { + "phpunit/phpunit": "~4|~5" + }, "type": "library", "autoload": { "classmap": [ @@ -1565,7 +1672,7 @@ "keywords": [ "timer" ], - "time": "2015-06-21 08:01:12" + "time": "2016-05-12 18:03:57" }, { "name": "phpunit/php-token-stream", @@ -1618,16 +1725,16 @@ }, { "name": "phpunit/phpunit", - "version": "4.3.5", + "version": "4.8.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1" + "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/2dab9d593997db4abcf58d0daf798eb4e9cecfe1", - "reference": "2dab9d593997db4abcf58d0daf798eb4e9cecfe1", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/fc1d8cd5b5de11625979125c5639347896ac2c74", + "reference": "fc1d8cd5b5de11625979125c5639347896ac2c74", "shasum": "" }, "require": { @@ -1637,17 +1744,19 @@ "ext-reflection": "*", "ext-spl": "*", "php": ">=5.3.3", - "phpunit/php-code-coverage": "~2.0", - "phpunit/php-file-iterator": "~1.3.2", + "phpspec/prophecy": "^1.3.1", + "phpunit/php-code-coverage": "~2.1", + "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", - "phpunit/php-timer": "~1.0.2", + "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "~2.3", - "sebastian/comparator": "~1.0", - "sebastian/diff": "~1.1", - "sebastian/environment": "~1.0", - "sebastian/exporter": "~1.0", + "sebastian/comparator": "~1.1", + "sebastian/diff": "~1.2", + "sebastian/environment": "~1.3", + "sebastian/exporter": "~1.2", + "sebastian/global-state": "~1.0", "sebastian/version": "~1.0", - "symfony/yaml": "~2.0" + "symfony/yaml": "~2.1|~3.0" }, "suggest": { "phpunit/php-invoker": "~1.1" @@ -1658,7 +1767,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "4.3.x-dev" + "dev-master": "4.8.x-dev" } }, "autoload": { @@ -1667,10 +1776,6 @@ ] }, "notification-url": "https://packagist.org/downloads/", - "include-path": [ - "", - "../../symfony/yaml/" - ], "license": [ "BSD-3-Clause" ], @@ -1682,13 +1787,13 @@ } ], "description": "The PHP Unit Testing framework.", - "homepage": "http://www.phpunit.de/", + "homepage": "https://phpunit.de/", "keywords": [ "phpunit", "testing", "xunit" ], - "time": "2014-11-11 10:11:09" + "time": "2016-05-17 03:09:28" }, { "name": "phpunit/phpunit-mock-objects", @@ -1794,23 +1899,23 @@ }, { "name": "predis/predis", - "version": "v1.0.3", + "version": "v1.0.4", "source": { "type": "git", "url": "https://github.com/nrk/predis.git", - "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04" + "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nrk/predis/zipball/84060b9034d756b4d79641667d7f9efe1aeb8e04", - "reference": "84060b9034d756b4d79641667d7f9efe1aeb8e04", + "url": "https://api.github.com/repos/nrk/predis/zipball/9ead747663bb1b1ae017dfa0d152aca87792b42f", + "reference": "9ead747663bb1b1ae017dfa0d152aca87792b42f", "shasum": "" }, "require": { "php": ">=5.3.2" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.8" }, "suggest": { "ext-curl": "Allows access to Webdis when paired with phpiredis", @@ -1833,52 +1938,14 @@ "homepage": "http://clorophilla.net" } ], - "description": "Flexible and feature-complete PHP client library for Redis", + "description": "Flexible and feature-complete Redis client for PHP and HHVM", "homepage": "http://github.com/nrk/predis", "keywords": [ "nosql", "predis", "redis" ], - "time": "2015-07-30 18:34:15" - }, - { - "name": "psr/log", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe0936ee26643249e916849d48e3a51d5f5e278b", - "reference": "fe0936ee26643249e916849d48e3a51d5f5e278b", - "shasum": "" - }, - "type": "library", - "autoload": { - "psr-0": { - "Psr\\Log\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "time": "2012-12-21 11:40:51" + "time": "2016-05-30 15:25:52" }, { "name": "sebastian/comparator", @@ -1998,23 +2065,23 @@ }, { "name": "sebastian/environment", - "version": "1.3.3", + "version": "1.3.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "6e7133793a8e5a5714a551a8324337374be209df" + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/6e7133793a8e5a5714a551a8324337374be209df", - "reference": "6e7133793a8e5a5714a551a8324337374be209df", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/be2c607e43ce4c89ecd60e75c6a85c126e754aea", + "reference": "be2c607e43ce4c89ecd60e75c6a85c126e754aea", "shasum": "" }, "require": { - "php": ">=5.3.3" + "php": "^5.3.3 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "~4.4" + "phpunit/phpunit": "^4.8 || ^5.0" }, "type": "library", "extra": { @@ -2044,20 +2111,20 @@ "environment", "hhvm" ], - "time": "2015-12-02 08:37:27" + "time": "2016-08-18 05:49:44" }, { "name": "sebastian/exporter", - "version": "1.2.1", + "version": "1.2.2", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e" + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/7ae5513327cb536431847bcc0c10edba2701064e", - "reference": "7ae5513327cb536431847bcc0c10edba2701064e", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/42c4c2eec485ee3e159ec9884f95b431287edde4", + "reference": "42c4c2eec485ee3e159ec9884f95b431287edde4", "shasum": "" }, "require": { @@ -2065,12 +2132,13 @@ "sebastian/recursion-context": "~1.0" }, "require-dev": { + "ext-mbstring": "*", "phpunit/phpunit": "~4.4" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.2.x-dev" + "dev-master": "1.3.x-dev" } }, "autoload": { @@ -2110,7 +2178,58 @@ "export", "exporter" ], - "time": "2015-06-21 07:55:53" + "time": "2016-06-17 09:04:28" + }, + { + "name": "sebastian/global-state", + "version": "1.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "require-dev": { + "phpunit/phpunit": "~4.2" + }, + "suggest": { + "ext-uopz": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + } + ], + "description": "Snapshotting of global state", + "homepage": "http://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], + "time": "2015-10-12 03:26:01" }, { "name": "sebastian/recursion-context", @@ -2202,16 +2321,16 @@ }, { "name": "seld/jsonlint", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "66834d3e3566bb5798db7294619388786ae99394" + "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/66834d3e3566bb5798db7294619388786ae99394", - "reference": "66834d3e3566bb5798db7294619388786ae99394", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/e827b5254d3e58c736ea2c5616710983d80b0b70", + "reference": "e827b5254d3e58c736ea2c5616710983d80b0b70", "shasum": "" }, "require": { @@ -2244,26 +2363,29 @@ "parser", "validator" ], - "time": "2015-11-21 02:21:41" + "time": "2016-09-14 15:17:56" }, { "name": "symfony/config", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae" + "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae", - "reference": "f21c97aec1b5302d2dc0d17047ea8f4e4ff93aae", + "url": "https://api.github.com/repos/symfony/config/zipball/005bf10c156335ede2e89fb9a9ee10a0b742bc84", + "reference": "005bf10c156335ede2e89fb9a9ee10a0b742bc84", "shasum": "" }, "require": { "php": ">=5.3.9", "symfony/filesystem": "~2.3|~3.0.0" }, + "suggest": { + "symfony/yaml": "To use the yaml reference dumper" + }, "type": "library", "extra": { "branch-alias": { @@ -2294,20 +2416,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2015-11-23 20:38:01" + "time": "2016-08-16 14:56:08" }, { "name": "symfony/console", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41" + "reference": "3d3e4fa5f0614c8e45220e5de80332322e33bd90" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/d232bfc100dfd32b18ccbcab4bcc8f28697b7e41", - "reference": "d232bfc100dfd32b18ccbcab4bcc8f28697b7e41", + "url": "https://api.github.com/repos/symfony/console/zipball/3d3e4fa5f0614c8e45220e5de80332322e33bd90", + "reference": "3d3e4fa5f0614c8e45220e5de80332322e33bd90", "shasum": "" }, "require": { @@ -2354,20 +2476,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2015-11-30 12:35:10" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/filesystem", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "3e661a0d521ac67496515fa6e6704bd61bcfff60" + "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/3e661a0d521ac67496515fa6e6704bd61bcfff60", - "reference": "3e661a0d521ac67496515fa6e6704bd61bcfff60", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/44b499521defddf2eae17a18c811bbdae4f98bdf", + "reference": "44b499521defddf2eae17a18c811bbdae4f98bdf", "shasum": "" }, "require": { @@ -2403,20 +2525,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2015-11-23 10:19:46" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/finder", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "ead9b07af4ba77b6507bee697396a5c79e633f08" + "reference": "bec5533e6ed650547d6ec8de4b541dc9929066f7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/ead9b07af4ba77b6507bee697396a5c79e633f08", - "reference": "ead9b07af4ba77b6507bee697396a5c79e633f08", + "url": "https://api.github.com/repos/symfony/finder/zipball/bec5533e6ed650547d6ec8de4b541dc9929066f7", + "reference": "bec5533e6ed650547d6ec8de4b541dc9929066f7", "shasum": "" }, "require": { @@ -2452,29 +2574,32 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2016-08-26 11:57:43" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.0.0", + "version": "v1.2.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "0b6a8940385311a24e060ec1fe35680e17c74497" + "reference": "dff51f72b0706335131b00a7f49606168c582594" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/0b6a8940385311a24e060ec1fe35680e17c74497", - "reference": "0b6a8940385311a24e060ec1fe35680e17c74497", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/dff51f72b0706335131b00a7f49606168c582594", + "reference": "dff51f72b0706335131b00a7f49606168c582594", "shasum": "" }, "require": { "php": ">=5.3.3" }, + "suggest": { + "ext-mbstring": "For best performance" + }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0-dev" + "dev-master": "1.2-dev" } }, "autoload": { @@ -2508,20 +2633,20 @@ "portable", "shim" ], - "time": "2015-11-04 20:28:58" + "time": "2016-05-18 14:26:46" }, { "name": "symfony/process", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312" + "reference": "05a03ed27073638658cab9405d99a67dd1014987" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1b988a88e3551102f3c2d9e1d47a18c3a78d6312", - "reference": "1b988a88e3551102f3c2d9e1d47a18c3a78d6312", + "url": "https://api.github.com/repos/symfony/process/zipball/05a03ed27073638658cab9405d99a67dd1014987", + "reference": "05a03ed27073638658cab9405d99a67dd1014987", "shasum": "" }, "require": { @@ -2557,20 +2682,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2015-11-30 12:35:10" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/stopwatch", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5f1e2ebd1044da542d2b9510527836e8be92b1cb" + "reference": "35bae476693150728b0eb51647faac82faf9aaca" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5f1e2ebd1044da542d2b9510527836e8be92b1cb", - "reference": "5f1e2ebd1044da542d2b9510527836e8be92b1cb", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/35bae476693150728b0eb51647faac82faf9aaca", + "reference": "35bae476693150728b0eb51647faac82faf9aaca", "shasum": "" }, "require": { @@ -2606,20 +2731,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2015-10-30 20:15:42" + "time": "2016-06-29 05:29:29" }, { "name": "symfony/translation", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "6772657767649fc3b31df12705194fb4af11ef98" + "reference": "bf0ff95faa9b6c0708efc1986255e3608d0ed3c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/6772657767649fc3b31df12705194fb4af11ef98", - "reference": "6772657767649fc3b31df12705194fb4af11ef98", + "url": "https://api.github.com/repos/symfony/translation/zipball/bf0ff95faa9b6c0708efc1986255e3608d0ed3c7", + "reference": "bf0ff95faa9b6c0708efc1986255e3608d0ed3c7", "shasum": "" }, "require": { @@ -2670,24 +2795,25 @@ ], "description": "Symfony Translation Component", "homepage": "https://symfony.com", - "time": "2015-11-18 13:45:00" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/validator", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/validator.git", - "reference": "8c42b96f5b23f0642c1a518addafcef8077154a2" + "reference": "53b1b1d3f7550e4b1e365dbad39a0b6c60bfcf85" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/validator/zipball/8c42b96f5b23f0642c1a518addafcef8077154a2", - "reference": "8c42b96f5b23f0642c1a518addafcef8077154a2", + "url": "https://api.github.com/repos/symfony/validator/zipball/53b1b1d3f7550e4b1e365dbad39a0b6c60bfcf85", + "reference": "53b1b1d3f7550e4b1e365dbad39a0b6c60bfcf85", "shasum": "" }, "require": { "php": ">=5.3.9", + "symfony/polyfill-mbstring": "~1.0", "symfony/translation": "~2.4|~3.0.0" }, "require-dev": { @@ -2696,8 +2822,8 @@ "egulias/email-validator": "~1.2,>=1.2.1", "symfony/config": "~2.2|~3.0.0", "symfony/expression-language": "~2.4|~3.0.0", - "symfony/http-foundation": "~2.1|~3.0.0", - "symfony/intl": "~2.4|~3.0.0", + "symfony/http-foundation": "~2.3|~3.0.0", + "symfony/intl": "~2.7.4|~2.8|~3.0.0", "symfony/property-access": "~2.3|~3.0.0", "symfony/yaml": "~2.0,>=2.0.5|~3.0.0" }, @@ -2742,20 +2868,20 @@ ], "description": "Symfony Validator Component", "homepage": "https://symfony.com", - "time": "2015-11-20 14:39:26" + "time": "2016-09-06 10:55:00" }, { "name": "symfony/yaml", - "version": "v2.8.0", + "version": "v2.8.11", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "f79824187de95064a2f5038904c4d7f0227fedb5" + "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/f79824187de95064a2f5038904c4d7f0227fedb5", - "reference": "f79824187de95064a2f5038904c4d7f0227fedb5", + "url": "https://api.github.com/repos/symfony/yaml/zipball/e7540734bad981fe59f8ef14b6fc194ae9df8d9c", + "reference": "e7540734bad981fe59f8ef14b6fc194ae9df8d9c", "shasum": "" }, "require": { @@ -2791,20 +2917,20 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2015-11-30 12:35:10" + "time": "2016-09-02 01:57:56" }, { "name": "twig/twig", - "version": "v1.23.1", + "version": "v1.25.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6" + "reference": "f16a634ab08d87e520da5671ec52153d627f10f6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6", - "reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/f16a634ab08d87e520da5671ec52153d627f10f6", + "reference": "f16a634ab08d87e520da5671ec52153d627f10f6", "shasum": "" }, "require": { @@ -2817,7 +2943,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.23-dev" + "dev-master": "1.25-dev" } }, "autoload": { @@ -2852,33 +2978,34 @@ "keywords": [ "templating" ], - "time": "2015-11-05 12:49:06" + "time": "2016-09-21 23:05:12" }, { "name": "zendframework/zend-cache", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-cache.git", - "reference": "5999e5a03f7dcf82abbbe67eea74da641f959684" + "reference": "5c4e6231082f74ab3e4fd58927c867ef4c24d71f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/5999e5a03f7dcf82abbbe67eea74da641f959684", - "reference": "5999e5a03f7dcf82abbbe67eea74da641f959684", + "url": "https://api.github.com/repos/zendframework/zend-cache/zipball/5c4e6231082f74ab3e4fd58927c867ef4c24d71f", + "reference": "5c4e6231082f74ab3e4fd58927c867ef4c24d71f", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-eventmanager": "~2.4.0", + "zendframework/zend-serializer": "~2.4.0", + "zendframework/zend-servicemanager": "~2.4.0", + "zendframework/zend-stdlib": "~2.4.0" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-session": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-session": "~2.4.0" }, "suggest": { "ext-apc": "APC >= 3.1.6 to use the APC storage adapter", @@ -2893,8 +3020,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -2912,34 +3039,34 @@ "cache", "zf2" ], - "time": "2015-06-03 15:31:59" + "time": "2015-09-15 16:23:56" }, { "name": "zendframework/zend-config", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-config.git", - "reference": "ec49b1df1bdd9772df09dc2f612fbfc279bf4c27" + "reference": "6b879e54096b8e0d2290f7414c38f9a5947cb8ac" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-config/zipball/ec49b1df1bdd9772df09dc2f612fbfc279bf4c27", - "reference": "ec49b1df1bdd9772df09dc2f612fbfc279bf4c27", + "url": "https://api.github.com/repos/zendframework/zend-config/zipball/6b879e54096b8e0d2290f7414c38f9a5947cb8ac", + "reference": "6b879e54096b8e0d2290f7414c38f9a5947cb8ac", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-i18n": "~2.5", - "zendframework/zend-json": "~2.5", - "zendframework/zend-mvc": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-filter": "self.version", + "zendframework/zend-i18n": "self.version", + "zendframework/zend-json": "self.version", + "zendframework/zend-servicemanager": "self.version" }, "suggest": { "zendframework/zend-filter": "Zend\\Filter component", @@ -2950,8 +3077,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -2969,35 +3096,36 @@ "config", "zf2" ], - "time": "2015-06-03 15:32:00" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-eventmanager", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-eventmanager.git", - "reference": "d94a16039144936f107f906896349900fd634443" + "reference": "c2c46a7a2809b74ceb66fd79f66d43f97e1747b4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/d94a16039144936f107f906896349900fd634443", - "reference": "d94a16039144936f107f906896349900fd634443", + "url": "https://api.github.com/repos/zendframework/zend-eventmanager/zipball/c2c46a7a2809b74ceb66fd79f66d43f97e1747b4", + "reference": "c2c46a7a2809b74ceb66fd79f66d43f97e1747b4", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", - "phpunit/phpunit": "~4.0" + "phpunit/phpunit": "~4.0", + "satooshi/php-coveralls": "dev-master" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3009,40 +3137,38 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-eventmanager", + "homepage": "https://github.com/zendframework/zend-event-manager", "keywords": [ "eventmanager", "zf2" ], - "time": "2015-06-03 15:32:01" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-filter", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-filter.git", - "reference": "93e6990a198e6cdd811064083acac4693f4b29ae" + "reference": "a3711101850078b2aa69586c71897acaada2e9cb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/93e6990a198e6cdd811064083acac4693f4b29ae", - "reference": "93e6990a198e6cdd811064083acac4693f4b29ae", + "url": "https://api.github.com/repos/zendframework/zend-filter/zipball/a3711101850078b2aa69586c71897acaada2e9cb", + "reference": "a3711101850078b2aa69586c71897acaada2e9cb", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-crypt": "~2.5", - "zendframework/zend-i18n": "~2.5", - "zendframework/zend-loader": "~2.5", - "zendframework/zend-servicemanager": "~2.5", - "zendframework/zend-uri": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-crypt": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-uri": "self.version" }, "suggest": { "zendframework/zend-crypt": "Zend\\Crypt component", @@ -3053,8 +3179,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3072,36 +3198,37 @@ "filter", "zf2" ], - "time": "2015-06-03 15:32:01" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-i18n", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-i18n.git", - "reference": "509271eb7947e4aabebfc376104179cffea42696" + "reference": "f26d6ae4be3f1ac98fbb3708aafae908c38f46c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/509271eb7947e4aabebfc376104179cffea42696", - "reference": "509271eb7947e4aabebfc376104179cffea42696", + "url": "https://api.github.com/repos/zendframework/zend-i18n/zipball/f26d6ae4be3f1ac98fbb3708aafae908c38f46c8", + "reference": "f26d6ae4be3f1ac98fbb3708aafae908c38f46c8", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-cache": "~2.5", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-servicemanager": "~2.5", - "zendframework/zend-validator": "~2.5", - "zendframework/zend-view": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-cache": "self.version", + "zendframework/zend-config": "self.version", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-servicemanager": "self.version", + "zendframework/zend-validator": "self.version", + "zendframework/zend-view": "self.version" }, "suggest": { "ext-intl": "Required for most features of Zend\\I18n; included in default builds of PHP", @@ -3117,8 +3244,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3135,32 +3262,32 @@ "i18n", "zf2" ], - "time": "2015-06-03 15:32:01" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-json", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-json.git", - "reference": "c74eaf17d2dd37dc1e964be8dfde05706a821ebc" + "reference": "1db4b878846520e619fbcdc7ce826c8563f8e839" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-json/zipball/c74eaf17d2dd37dc1e964be8dfde05706a821ebc", - "reference": "c74eaf17d2dd37dc1e964be8dfde05706a821ebc", + "url": "https://api.github.com/repos/zendframework/zend-json/zipball/1db4b878846520e619fbcdc7ce826c8563f8e839", + "reference": "1db4b878846520e619fbcdc7ce826c8563f8e839", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-http": "~2.5", - "zendframework/zend-server": "~2.5", - "zendframework/zendxml": "~1.0" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-http": "self.version", + "zendframework/zend-server": "self.version" }, "suggest": { "zendframework/zend-http": "Zend\\Http component", @@ -3170,8 +3297,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3189,20 +3316,20 @@ "json", "zf2" ], - "time": "2015-06-03 15:32:01" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-math", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-math.git", - "reference": "9f02a1ac4d3374d3332c80f9215deec9c71558fc" + "reference": "1e7e803366fc7618a8668ce2403c932196174faa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-math/zipball/9f02a1ac4d3374d3332c80f9215deec9c71558fc", - "reference": "9f02a1ac4d3374d3332c80f9215deec9c71558fc", + "url": "https://api.github.com/repos/zendframework/zend-math/zipball/1e7e803366fc7618a8668ce2403c932196174faa", + "reference": "1e7e803366fc7618a8668ce2403c932196174faa", "shasum": "" }, "require": { @@ -3210,9 +3337,8 @@ }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", - "ircmaxell/random-lib": "~1.1", "phpunit/phpunit": "~4.0", - "zendframework/zend-servicemanager": "~2.5" + "satooshi/php-coveralls": "dev-master" }, "suggest": { "ext-bcmath": "If using the bcmath functionality", @@ -3223,8 +3349,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3241,32 +3367,33 @@ "math", "zf2" ], - "time": "2015-06-03 15:32:02" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-serializer", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-serializer.git", - "reference": "b7208eb17dc4a4fb3a660b85e6c4af035eeed40c" + "reference": "31a0da5c09f54fe76bc4e145e348b0d3d277aaf0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/b7208eb17dc4a4fb3a660b85e6c4af035eeed40c", - "reference": "b7208eb17dc4a4fb3a660b85e6c4af035eeed40c", + "url": "https://api.github.com/repos/zendframework/zend-serializer/zipball/31a0da5c09f54fe76bc4e145e348b0d3d277aaf0", + "reference": "31a0da5c09f54fe76bc4e145e348b0d3d277aaf0", "shasum": "" }, "require": { "php": ">=5.3.23", - "zendframework/zend-json": "~2.5", - "zendframework/zend-math": "~2.5", - "zendframework/zend-stdlib": "~2.5" + "zendframework/zend-json": "self.version", + "zendframework/zend-math": "self.version", + "zendframework/zend-stdlib": "self.version" }, "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-servicemanager": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-servicemanager": "self.version" }, "suggest": { "zendframework/zend-servicemanager": "To support plugin manager support" @@ -3274,8 +3401,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3293,20 +3420,20 @@ "serializer", "zf2" ], - "time": "2015-06-03 15:32:02" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-servicemanager", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "3b22c403e351d92526c642cba0bd810bc22e1c56" + "reference": "855294e12771b4295c26446b6ed2df2f1785f234" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/3b22c403e351d92526c642cba0bd810bc22e1c56", - "reference": "3b22c403e351d92526c642cba0bd810bc22e1c56", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/855294e12771b4295c26446b6ed2df2f1785f234", + "reference": "855294e12771b4295c26446b6ed2df2f1785f234", "shasum": "" }, "require": { @@ -3315,8 +3442,8 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-di": "~2.5", - "zendframework/zend-mvc": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-di": "self.version" }, "suggest": { "ocramius/proxy-manager": "ProxyManager 0.5.* to handle lazy initialization of services", @@ -3325,8 +3452,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3338,25 +3465,25 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "homepage": "https://github.com/zendframework/zend-service-manager", "keywords": [ "servicemanager", "zf2" ], - "time": "2015-06-03 15:32:02" + "time": "2015-05-07 14:55:31" }, { "name": "zendframework/zend-stdlib", - "version": "2.5.1", + "version": "2.4.10", "source": { "type": "git", "url": "https://github.com/zendframework/zend-stdlib.git", - "reference": "cc8e90a60dd5d44b9730b77d07b97550091da1ae" + "reference": "d8ecb629a72da9f91bd95c5af006384823560b42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/cc8e90a60dd5d44b9730b77d07b97550091da1ae", - "reference": "cc8e90a60dd5d44b9730b77d07b97550091da1ae", + "url": "https://api.github.com/repos/zendframework/zend-stdlib/zipball/d8ecb629a72da9f91bd95c5af006384823560b42", + "reference": "d8ecb629a72da9f91bd95c5af006384823560b42", "shasum": "" }, "require": { @@ -3365,12 +3492,11 @@ "require-dev": { "fabpot/php-cs-fixer": "1.7.*", "phpunit/phpunit": "~4.0", - "zendframework/zend-config": "~2.5", - "zendframework/zend-eventmanager": "~2.5", - "zendframework/zend-filter": "~2.5", - "zendframework/zend-inputfilter": "~2.5", - "zendframework/zend-serializer": "~2.5", - "zendframework/zend-servicemanager": "~2.5" + "satooshi/php-coveralls": "dev-master", + "zendframework/zend-eventmanager": "self.version", + "zendframework/zend-filter": "self.version", + "zendframework/zend-serializer": "self.version", + "zendframework/zend-servicemanager": "self.version" }, "suggest": { "zendframework/zend-eventmanager": "To support aggregate hydrator usage", @@ -3381,8 +3507,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.5-dev", - "dev-develop": "2.6-dev" + "dev-master": "2.4-dev", + "dev-develop": "2.5-dev" } }, "autoload": { @@ -3399,7 +3525,7 @@ "stdlib", "zf2" ], - "time": "2015-06-03 15:32:03" + "time": "2015-07-21 13:55:46" }, { "name": "zetacomponents/base", diff --git a/integration-tests/LDDFeatureRequesterTest.php b/integration-tests/LDDFeatureRequesterTest.php index 5afeb7a88..602b87717 100644 --- a/integration-tests/LDDFeatureRequesterTest.php +++ b/integration-tests/LDDFeatureRequesterTest.php @@ -18,9 +18,9 @@ public function testGet() { $user = $builder->build(); $redis->del("launchdarkly:features"); - $this->assertEquals("jim", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("jim", $client->variation('foo', $user, 'jim')); $redis->hset("launchdarkly:features", 'foo', $this->gen_feature("foo", "bar")); - $this->assertEquals("bar", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("bar", $client->variation('foo', $user, 'jim')); } public function testGetApc() { @@ -34,16 +34,16 @@ public function testGetApc() { $user = $builder->build(); $redis->del("launchdarkly:features"); - $this->assertEquals("jim", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("jim", $client->variation('foo', $user, 'jim')); $redis->hset("launchdarkly:features", 'foo', $this->gen_feature("foo", "bar")); - $this->assertEquals("bar", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("bar", $client->variation('foo', $user, 'jim')); # cached value so not updated $redis->hset("launchdarkly:features", 'foo', $this->gen_feature("foo", "baz")); - $this->assertEquals("bar", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("bar", $client->variation('foo', $user, 'jim')); apc_delete("launchdarkly:features.foo"); - $this->assertEquals("baz", $client->toggle('foo', $user, 'jim')); + $this->assertEquals("baz", $client->variation('foo', $user, 'jim')); } private function gen_feature($key, $val) { diff --git a/src/LaunchDarkly/Clause.php b/src/LaunchDarkly/Clause.php new file mode 100644 index 000000000..adf01615e --- /dev/null +++ b/src/LaunchDarkly/Clause.php @@ -0,0 +1,99 @@ +_attribute = $attribute; + $this->_op = $op; + $this->_values = $values; + $this->_negate = $negate; + } + + public static function getDecoder() { + return function ($v) { + return new Clause($v['attribute'], $v['op'], $v['values'], $v['negate']); + }; + } + + /** + * @param $user LDUser + * @return bool + */ + public function matchesUser($user) { + $userValue = $user->getValueForEvaluation($this->_attribute); + if ($userValue === null) { + return false; + } + if (is_array($userValue)) { + foreach ($userValue as $element) { + if ($this->matchAny($element)) { + return $this->_maybeNegate(true); + } + } + return $this->_maybeNegate(false); + } else { + return $this->_maybeNegate($this->matchAny($userValue)); + } + } + + + /** + * @return string + */ + public function getAttribute() { + return $this->_attribute; + } + + /** + * @return string + */ + public function getOp() { + return $this->_op; + } + + /** + * @return array + */ + public function getValues() { + return $this->_values; + } + + /** + * @return boolean + */ + public function isNegate() { + return $this->_negate; + } + + /** + * @param $userValue + * @return bool + */ + private function matchAny($userValue) { + foreach ($this->_values as $v) { + $result = Operators::apply($this->_op, $userValue, $v); + if ($result === true) { + return true; + } + } + return false; + } + + private function _maybeNegate($b) { + if ($this->_negate) { + return !$b; + } else { + return $b; + } + } +} \ No newline at end of file diff --git a/src/LaunchDarkly/EvaluationException.php b/src/LaunchDarkly/EvaluationException.php new file mode 100644 index 000000000..294688752 --- /dev/null +++ b/src/LaunchDarkly/EvaluationException.php @@ -0,0 +1,15 @@ +_apiKey = $apiKey; - if (!isset($options['base_uri'])) { - $this->_host = 'app.launchdarkly.com'; + $this->_sdkKey = $apiKey; + if (!isset($options['events_uri'])) { + $this->_host = 'events.launchdarkly.com'; $this->_port = 443; $this->_ssl = true; + $this->_path = ''; } else { - $url = parse_url($options['base_uri']); + $url = parse_url(rtrim($options['events_uri'],'/')); $this->_host = $url['host']; $this->_ssl = $url['scheme'] === 'https'; if (isset($url['port'])) { @@ -31,6 +32,12 @@ public function __construct($apiKey, $options = array()) { else { $this->_port = $this->_ssl ? 443 : 80; } + if (isset($url['path'])) { + $this->_path = $url['path']; + } + else { + $this->_path = ''; + } } $this->_capacity = $options['capacity']; @@ -73,11 +80,11 @@ private function createArgs($payload) { $scheme = $this->_ssl ? "https://" : "http://"; $args = " -X POST"; $args.= " -H 'Content-Type: application/json'"; - $args.= " -H " . escapeshellarg("Authorization: api_key " . $this->_apiKey); + $args.= " -H " . escapeshellarg("Authorization: " . $this->_sdkKey); $args.= " -H 'User-Agent: PHPClient/" . LDClient::VERSION . "'"; $args.= " -H 'Accept: application/json'"; $args.= " -d " . escapeshellarg($payload); - $args.= " " . escapeshellarg($scheme . $this->_host . ":" . $this->_port . "/api/events/bulk"); + $args.= " " . escapeshellarg($scheme . $this->_host . ":" . $this->_port . $this->_path . "/bulk"); return $args; } diff --git a/src/LaunchDarkly/FeatureFlag.php b/src/LaunchDarkly/FeatureFlag.php new file mode 100644 index 000000000..b20ced40b --- /dev/null +++ b/src/LaunchDarkly/FeatureFlag.php @@ -0,0 +1,367 @@ +_key = $key; + $this->_version = $version; + $this->_on = $on; + $this->_prerequisites = $prerequisites; + $this->_salt = $salt; + $this->_targets = $targets; + $this->_rules = $rules; + $this->_fallthrough = $fallthrough; + $this->_offVariation = $offVariation; + $this->_variations = $variations; + $this->_deleted = $deleted; + } + + public static function getDecoder() { + return function ($v) { + return new FeatureFlag( + $v['key'], + $v['version'], + $v['on'], + array_map(Prerequisite::getDecoder(), $v['prerequisites']), + $v['salt'], + array_map(Target::getDecoder(), $v['targets']), + array_map(Rule::getDecoder(), $v['rules']), + call_user_func(VariationOrRollout::getDecoder(), $v['fallthrough']), + $v['offVariation'], + $v['variations'], + $v['deleted']); + }; + } + + public static function decode($v) { + return call_user_func(FeatureFlag::getDecoder(), $v); + } + + public function isOn() { + return $this->_on; + } + + /** + * @param $user LDUser + * @param $featureRequester FeatureRequester + * @return EvalResult|null + */ + public function evaluate($user, $featureRequester) { + $prereqEvents = array(); + if (is_null($user) || is_null($user->getKey())) { + return new EvalResult(null, $prereqEvents); + } + if ($this->isOn()) { + $result = $this->_evaluate($user, $featureRequester, $prereqEvents); + if ($result != null) { + return new EvalResult($result, $prereqEvents); + } + } + $offVariation = $this->getOffVariationValue(); + return new EvalResult($offVariation, $prereqEvents); + } + + /** + * @param $user LDUser + * @param $featureRequester FeatureRequester + * @param $events + * @return mixed|null + */ + private function _evaluate($user, $featureRequester, &$events) { + $prereqOk = true; + if ($this->_prerequisites != null) { + foreach ($this->_prerequisites as $prereq) { + try { + $prereqEvalResult = null; + $prereqFeatureFlag = $featureRequester->get($prereq->getKey()); + if ($prereqFeatureFlag == null) { + return null; + } else if ($prereqFeatureFlag->isOn()) { + $prereqEvalResult = $prereqFeatureFlag->_evaluate($user, $featureRequester, $events); + $variation = $prereqFeatureFlag->getVariation($prereq->getVariation()); + if ($prereqEvalResult === null || $variation === null || $prereqEvalResult !== $variation) { + $prereqOk = false; + } + } else { + $prereqOk = false; + } + array_push($events, Util::newFeatureRequestEvent($prereqFeatureFlag->getKey(), $user, $prereqEvalResult, null, $prereqFeatureFlag->getVersion(), $this->_key)); + } catch (EvaluationException $e) { + $prereqOk = false; + } + } + } + if ($prereqOk) { + return $this->getVariation($this->evaluateIndex($user)); + } + return null; + } + + /** + * @param $user LDUser + * @return int|null + */ + private function evaluateIndex($user) { + // Check to see if targets match + if ($this->_targets != null) { + foreach ($this->_targets as $target) { + foreach ($target->getValues() as $value) { + if ($value === $user->getKey()) { + return $target->getVariation(); + } + } + } + } + // Now walk through the rules and see if any match + if ($this->_rules != null) { + foreach ($this->_rules as $rule) { + if ($rule->matchesUser($user)) { + return $rule->variationIndexForUser($user, $this->_key, $this->_salt); + } + } + } + // Walk through the fallthrough and see if it matches + return $this->_fallthrough->variationIndexForUser($user, $this->_key, $this->_salt); + } + + private function getVariation($index) { + // If the supplied index is null, then rules didn't match, and we want to return + // the off variation + if (!isset($index)) { + return null; + } + // If the index doesn't refer to a valid variation, that's an unexpected exception and we will + // return the default variation + if ($index >= count($this->_variations)) { + throw new EvaluationException("Invalid Index"); + } else { + return $this->_variations[$index]; + } + } + + public function getOffVariationValue() { + if ($this->_offVariation === null) { + return null; + } + if ($this->_offVariation >= count($this->_variations)) { + throw new EvaluationException("Invalid offVariation index"); + } + return $this->_variations[$this->_offVariation]; + } + + /** + * @return int + */ + public function getVersion() { + return $this->_version; + } + + /** + * @return string + */ + public function getKey() { + return $this->_key; + } + + /** + * @return boolean + */ + public function isDeleted() { + return $this->_deleted; + } +} + +class EvalResult { + private $_value = null; + /** @var array */ + private $_prerequisiteEvents = []; + + /** + * EvalResult constructor. + * @param null $value + * @param array $prerequisiteEvents + */ + public function __construct($value, array $prerequisiteEvents) { + $this->_value = $value; + $this->_prerequisiteEvents = $prerequisiteEvents; + } + + /** + * @return null + */ + public function getValue() { + return $this->_value; + } + + /** + * @return array + */ + public function getPrerequisiteEvents() { + return $this->_prerequisiteEvents; + } +} + +class WeightedVariation { + /** @var int */ + private $_variation = null; + /** @var int */ + private $_weight = null; + + private function __construct($variation, $weight) { + $this->_variation = $variation; + $this->_weight = $weight; + } + + public static function getDecoder() { + return function ($v) { + return new WeightedVariation($v['variation'], $v['weight']); + }; + } + + /** + * @return int + */ + public function getVariation() { + return $this->_variation; + } + + /** + * @return int + */ + public function getWeight() { + return $this->_weight; + } +} + +class Target { + /** @var string[] */ + private $_values = array(); + /** @var int */ + private $_variation = null; + + protected function __construct(array $values, $variation) { + $this->_values = $values; + $this->_variation = $variation; + } + + public static function getDecoder() { + return function ($v) { + return new Target($v['values'], $v['variation']); + }; + } + + /** + * @return \string[] + */ + public function getValues() { + return $this->_values; + } + + /** + * @return int + */ + public function getVariation() { + return $this->_variation; + } +} + +class Prerequisite { + /** @var string */ + private $_key = null; + /** @var int */ + private $_variation = null; + + protected function __construct($key, $variation) { + $this->_key = $key; + $this->_variation = $variation; + } + + public static function getDecoder() { + return function ($v) { + return new Prerequisite($v['key'], $v['variation']); + }; + } + + /** + * @return string + */ + public function getKey() { + return $this->_key; + } + + /** + * @return int + */ + public function getVariation() { + return $this->_variation; + } +} + +class Rollout { + /** @var WeightedVariation[] */ + private $_variations = array(); + /** @var string */ + private $_bucketBy = null; + + protected function __construct(array $variations, $bucketBy) { + $this->_variations = $variations; + $this->_bucketBy = $bucketBy; + } + + public static function getDecoder() { + return function ($v) { + return new Rollout( + array_map(WeightedVariation::getDecoder(), $v['variations']), + isset($v['bucketBy']) ? $v['bucketBy'] : null); + }; + } + + /** + * @return WeightedVariation[] + */ + public function getVariations() { + return $this->_variations; + } + + /** + * @return string + */ + public function getBucketBy() { + return $this->_bucketBy; + } +} diff --git a/src/LaunchDarkly/FeatureRep.php b/src/LaunchDarkly/FeatureRep.php deleted file mode 100644 index 36d297d1a..000000000 --- a/src/LaunchDarkly/FeatureRep.php +++ /dev/null @@ -1,90 +0,0 @@ -_name = $name; - $this->_key = $key; - $this->_salt = $salt; - $this->_on = $on; - $this->_variations = $variations; - } - - /** - * @param $user LDUser - * @return mixed - */ - public function evaluate($user) { - if (!$this->_on || !$user) { - return null; - } - - $param = $this->_get_param($user); - if (is_null($param)) { - return null; - } - else { - foreach ($this->_variations as $variation) { - if ($variation->matchUser($user)) { - return $variation->getValue(); - } - } - - foreach ($this->_variations as $variation) { - if ($variation->matchTarget($user)) { - return $variation->getValue(); - } - } - - $sum = 0.0; - foreach ($this->_variations as $variation) { - $sum += $variation->getWeight() / 100.0; - - if ($param < $sum) { - return $variation->getValue(); - } - } - } - - return null; - } - - /** - * @param $user LDUser - * @return float|null - */ - private function _get_param($user) { - $id_hash = null; - $hash = null; - - if ($user->getKey()) { - $id_hash = $user->getKey(); - } - else { - return null; - } - - if ($user->getSecondary()) { - $id_hash .= "." . $user->getSecondary(); - } - - $hash = substr(sha1($this->_key . "." . $this->_salt . "." . $id_hash), 0, 15); - $longVal = base_convert($hash, 16, 10); - $result = $longVal / self::$LONG_SCALE; - - return $result; - } -} diff --git a/src/LaunchDarkly/FeatureRequester.php b/src/LaunchDarkly/FeatureRequester.php index f330dba76..2ce2757b1 100644 --- a/src/LaunchDarkly/FeatureRequester.php +++ b/src/LaunchDarkly/FeatureRequester.php @@ -7,7 +7,14 @@ interface FeatureRequester { * Gets feature data from a likely cached store * * @param $key string feature key - * @return array|null The decoded JSON feature data, or null if missing + * @return FeatureFlag|null The decoded FeatureFlag, or null if missing */ public function get($key); + + /** + * Gets all features. + * + * @return array()|null The decoded FeatureFlags, or null if missing + */ + public function getAll(); } \ No newline at end of file diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index 78e6c8954..345f352e1 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -1,33 +1,42 @@ _client = new Client($baseUri, - array( - 'debug' => false, - 'curl.options' => array('CURLOPT_TCP_NODELAY' => 1), - 'request.options' => array( + const SDK_FLAGS = "/sdk/flags"; + /** @var Client */ + private $_client; + /** @var LoggerInterface */ + private $_logger; + + function __construct($baseUri, $sdkKey, $options) { + $this->_client = new Client(array( + 'base_url' => $baseUri, + 'defaults' => array( 'headers' => array( - 'Authorization' => "api_key {$apiKey}", - 'Content-Type' => 'application/json' + 'Authorization' => "{$sdkKey}", + 'Content-Type' => 'application/json', + 'User-Agent' => 'PHPClient/' . LDClient::VERSION ), + 'debug' => false, 'timeout' => $options['timeout'], 'connect_timeout' => $options['connect_timeout'] ) )); - $this->_client->setUserAgent('PHPClient/' . LDClient::VERSION); - if (isset($options['cache_storage'])) { - $cachePlugin = new CachePlugin(array('storage' => $options['cache_storage'], 'validate' => false)); - $this->_client->addSubscriber($cachePlugin); + if (!isset($options['cache_storage'])) { + $csOptions = array('validate' => false); + } + else { + $csOptions = array('storage' => $options['cache_storage'], 'validate' => false); } + CacheSubscriber::attach($this->_client, $csOptions); + $this->_logger = $options['logger']; } @@ -37,15 +46,35 @@ function __construct($baseUri, $apiKey, $options) { * @param $key string feature key * @return array|null The decoded JSON feature data, or null if missing */ - public function get($key) { + public function get($key) + { try { - $request = $this->_client->get("/api/eval/features/$key"); - $response = $request->send(); - return $response->json(); + $uri = self::SDK_FLAGS . "/" . $key; + $response = $this->_client->get($uri, $this->_defaults); + $body = $response->getBody(); + return FeatureFlag::decode(json_decode($body, true)); } catch (BadResponseException $e) { $code = $e->getResponse()->getStatusCode(); - error_log("GuzzleFeatureRetriever::get received an unexpected HTTP status code $code"); + $this->_logger->error("GuzzleFeatureRetriever::get received an unexpected HTTP status code $code"); return null; } } + + /** + * Gets all features from a likely cached store + * + * @return array()|null The decoded FeatureFlags, or null if missing + */ + public function getAll() { + try { + $uri = $this->_baseUri . self::SDK_FLAGS; + $response = $this->_client->get($uri, $this->_defaults); + $body = $response->getBody(); + return array_map(FeatureFlag::getDecoder(), json_decode($body, true)); + } catch (BadResponseException $e) { + $code = $e->getResponse()->getStatusCode(); + $this->_logger->error("GuzzleFeatureRetriever::getAll received an unexpected HTTP status code $code"); + return null; + } + } } \ No newline at end of file diff --git a/src/LaunchDarkly/LDClient.php b/src/LaunchDarkly/LDClient.php index 7de5252ef..5d1dd8305 100644 --- a/src/LaunchDarkly/LDClient.php +++ b/src/LaunchDarkly/LDClient.php @@ -1,22 +1,31 @@ _apiKey = $apiKey; + public function __construct($sdkKey, $options = array()) { + $this->_sdkKey = $sdkKey; if (!isset($options['base_uri'])) { $this->_baseUri = self::DEFAULT_BASE_URI; - } - else { + } else { $this->_baseUri = rtrim($options['base_uri'], '/'); } - if (isset($options['events'])) { - $this->_events = $options['events']; + if (isset($options['send_events'])) { + $this->_send_events = $options['send_events']; + } + if (isset($options['offline']) && $options['offline'] === true) { + $this->_offline = true; + $this->_send_events = false; } + if (isset($options['defaults'])) { $this->_defaults = $options['defaults']; } @@ -57,77 +74,92 @@ public function __construct($apiKey, $options = array()) { $options['capacity'] = 1000; } - $this->_eventProcessor = new EventProcessor($apiKey, $options); + if (!isset($options['logger'])) { + $logger = new Logger("LaunchDarkly", [new ErrorLogHandler()]); + $options['logger'] = $logger; + } + $this->_logger = $options['logger']; + + $this->_eventProcessor = new EventProcessor($sdkKey, $options); if (isset($options['feature_requester_class'])) { $featureRequesterClass = $options['feature_requester_class']; } else { $featureRequesterClass = '\\LaunchDarkly\\GuzzleFeatureRequester'; } - $this->_featureRequester = new $featureRequesterClass($this->_baseUri, $apiKey, $options); - } - public function getFlag($key, $user, $default = false) { - return $this->toggle($key, $user, $default); + if (!is_a($featureRequesterClass, FeatureRequester::class, true)) { + throw new \InvalidArgumentException; + } + $this->_featureRequester = new $featureRequesterClass($this->_baseUri, $sdkKey, $options); } - /** - * Calculates the value of a feature flag for a given user. - * - * @param string $key The unique key for the feature flag - * @param LDUser $user The end user requesting the flag - * @param boolean $default The default value of the flag - * - * @return boolean Whether or not the flag should be enabled, or `default` if the flag is disabled in the LaunchDarkly control panel - */ - public function toggle($key, $user, $default = false) { + /** + * Calculates the value of a feature flag for a given user. + * + * @param string $key The unique key for the feature flag + * @param LDUser $user The end user requesting the flag + * @param boolean $default The default value of the flag + * + * @return mixed The result of the Feature Flag evaluation, or $default if any errors occurred. + */ + public function variation($key, $user, $default = false) { + $default = $this->_get_default($key, $default); + if ($this->_offline) { return $default; } try { - $default = $this->_get_default($key, $default); - $flag = $this->_toggle($key, $user); + if (is_null($user) || is_null($user->getKey())) { + $this->_sendFlagRequestEvent($key, $user, $default, $default); + $this->_logger->warn("Variation called with null user or null user key! Returning default value"); + return $default; + } + if ($user->isKeyBlank()) { + $this->_logger->warn("User key is blank. Flag evaluation will proceed, but the user will not be stored in LaunchDarkly."); + } + $flag = $this->_featureRequester->get($key); if (is_null($flag)) { - $this->_sendFlagRequestEvent($key, $user, $default); + $this->_sendFlagRequestEvent($key, $user, $default, $default); return $default; } - else { - $this->_sendFlagRequestEvent($key, $user, $flag); - return $flag; - } - } catch (\Exception $e) { - error_log("LaunchDarkly caught $e"); - try { - $this->_sendFlagRequestEvent($key, $user, $default); + $evalResult = $flag->evaluate($user, $this->_featureRequester); + if (!$this->isOffline() && $this->_send_events) { + foreach ($evalResult->getPrerequisiteEvents() as $e) { + $this->_eventProcessor->enqueue($e); + } } - catch (\Exception $e) { - error_log("LaunchDarkly caught $e"); + if ($evalResult->getValue() != null) { + $this->_sendFlagRequestEvent($key, $user, $evalResult->getValue(), $default, $flag->getVersion()); + return $evalResult->getValue(); } - return $default; + } catch (\Exception $e) { + $this->_logger->error("Caught $e"); } + try { + $this->_sendFlagRequestEvent($key, $user, $default, $default); + } catch (\Exception $e) { + $this->_logger->error("Caught $e"); + } + return $default; } - /** - * Puts the LaunchDarkly client in offline mode. - * In offline mode, all calls to `toggle` will return the default value, and `track` will be a no-op. - * - */ - public function setOffline() { - $this->_offline = true; - } - /** - * Puts the LaunchDarkly client in online mode. - * + /** @deprecated Use variation() instead. + * @param $key + * @param $user + * @param bool $default + * @return mixed */ - public function setOnline() { - $this->_offline = false; + public function toggle($key, $user, $default = false) { + $this->_logger->warning("Deprecated function: toggle() called. Use variation() instead."); + return $this->variation($key, $user, $default); } /** - * Returns whether the LaunchDarlkly client is in offline mode. + * Returns whether the LaunchDarkly client is in offline mode. * */ public function isOffline() { @@ -145,11 +177,14 @@ public function track($eventName, $user, $data) { if ($this->isOffline()) { return; } + if (is_null($user) || $user->isKeyBlank()) { + $this->_logger->warn("Track called with null user or null/empty user key!"); + } $event = array(); $event['user'] = $user->toJSON(); $event['kind'] = "custom"; - $event['creationDate'] = round(microtime(1) * 1000); + $event['creationDate'] = Util::currentTimeUnixMillis(); $event['key'] = $eventName; if (isset($data)) { $event['data'] = $data; @@ -164,46 +199,74 @@ public function identify($user) { if ($this->isOffline()) { return; } + if (is_null($user) || $user->isKeyBlank()) { + $this->_logger->warn("Track called with null user or null/empty user key!"); + } $event = array(); $event['user'] = $user->toJSON(); $event['kind'] = "identify"; - $event['creationDate'] = round(microtime(1) * 1000); + $event['creationDate'] = Util::currentTimeUnixMillis(); $event['key'] = $user->getKey(); - $this->_eventProcessor->enqueue($event); + $this->_eventProcessor->enqueue($event); + } + + /** Returns an array mapping Feature Flag keys to their evaluated results for a given user. + * + * If the result of a flag's evaluation would have returned the default variation, it will have a null entry. + * If the client is offline, has not been initialized, or a null user or user with null/empty user key, null will be returned. + * This method will not send analytics events back to LaunchDarkly. + *

+ * The most common use case for this method is to bootstrap a set of client-side feature flags from a back-end service. + * + * @param $user LDUser the end user requesting the feature flags + * @return array()|null Mapping of feature flag keys to their evaluated results for $user + */ + public function allFlags($user) { + if (is_null($user) || is_null($user->getKey())) { + $this->_logger->warn("allFlags called with null user or null/empty user key! Returning null"); + return null; + } + $flags = $this->_featureRequester->getAll(); + if ($flags === null) { + return null; + } + + /** + * @param $flag FeatureFlag + * @return mixed|null + */ + $eval = function($flag) use($user) { + return $flag->evaluate($user, $this->_featureRequester)->getValue(); + }; + + return array_map($eval, $flags); + } + + /** Generates an HMAC sha256 hash for use in Secure mode: https://github.com/launchdarkly/js-client#secure-mode + * @param $user LDUser + * @return string + */ + public function secureModeHash($user) { + if (is_null($user) || strlen($user->getKey()) === 0) { + return ""; + } + return hash_hmac("sha256", $user->getKey(), $this->_sdkKey, false); } /** * @param $key string * @param $user LDUser * @param $value mixed + * @param $default + * @param $version int | null + * @param string | null $prereqOf */ - protected function _sendFlagRequestEvent($key, $user, $value) { - if ($this->isOffline() || !$this->_events) { + protected function _sendFlagRequestEvent($key, $user, $value, $default, $version = null, $prereqOf = null) { + if ($this->isOffline() || !$this->_send_events) { return; } - - $event = array(); - $event['user'] = $user->toJSON(); - $event['value'] = $value; - $event['kind'] = "feature"; - $event['creationDate'] = round(microtime(1) * 1000); - $event['key'] = $key; - $this->_eventProcessor->enqueue($event); - } - - protected function _toggle($key, $user) { - try { - $data = $this->_featureRequester->get($key); - if ($data == null) { - return null; - } - return self::_decode($data, $user); - } catch (Exception $e) { - $msg = $e->getMessage(); - error_log("LDClient::_toggle received error $msg, using default"); - return null; - } + $this->_eventProcessor->enqueue(Util::newFeatureRequestEvent($key, $user, $value, $default, $version, $prereqOf)); } protected function _get_default($key, $default) { @@ -213,27 +276,4 @@ protected function _get_default($key, $default) { return $default; } } - - protected static function _decode($json, $user) { - $makeVariation = function ($v) { - $makeTarget = function ($t) { - return new TargetRule($t['attribute'], $t['op'], $t['values']); - }; - - $ts = empty($v['targets']) ? array() : $v['targets']; - $targets = array_map($makeTarget, $ts); - if (isset($v['userTarget'])) { - return new Variation($v['value'], $v['weight'], $targets, $makeTarget($v['userTarget'])); - } - else { - return new Variation($v['value'], $v['weight'], $targets, null); - } - }; - - $vs = empty($json['variations']) ? array() : $json['variations']; - $variations = array_map($makeVariation, $vs); - $feature = new FeatureRep($json['name'], $json['key'], $json['salt'], $json['on'], $variations); - - return $feature->evaluate($user); - } } diff --git a/src/LaunchDarkly/LDDFeatureRequester.php b/src/LaunchDarkly/LDDFeatureRequester.php index 08cdf665b..f163ee90f 100644 --- a/src/LaunchDarkly/LDDFeatureRequester.php +++ b/src/LaunchDarkly/LDDFeatureRequester.php @@ -2,11 +2,15 @@ namespace LaunchDarkly; +use Psr\Log\LoggerInterface; + class LDDFeatureRequester implements FeatureRequester { protected $_baseUri; protected $_apiKey; protected $_options; protected $_features_key; + /** @var LoggerInterface */ + private $_logger; function __construct($baseUri, $apiKey, $options) { $this->_baseUri = $baseUri; @@ -25,6 +29,8 @@ function __construct($baseUri, $apiKey, $options) { $prefix = $options['redis_prefix']; } $this->_features_key = "$prefix:features"; + $this->_logger = $options['logger']; + } protected function get_connection() { @@ -40,7 +46,7 @@ protected function get_connection() { * Gets feature data from a likely cached store * * @param $key string feature key - * @return array|null The decoded JSON feature data, or null if missing + * @return FeatureFlag|null The decoded JSON feature data, or null if missing */ public function get($key) { $raw = $this->get_from_cache($key); @@ -52,8 +58,15 @@ public function get($key) { } } if ($raw) { - return json_decode($raw, True); + $flag = FeatureFlag::decode(json_decode($raw, True)); + if ($flag->isDeleted()) { + $this->_logger->warning("LDDFeatureRequester: Attempted to get deleted feature with key: " . $key); + return null; + } + return $flag; + } else { + $this->_logger->warning("LDDFeatureRequester: Attempted to get missing feature with key: " . $key); return null; } } @@ -73,4 +86,28 @@ protected function get_from_cache($key) { * @param $val array The feature data */ protected function store_in_cache($key, $val) {} + + /** + * Gets all features + * + * @return array()|null The decoded FeatureFlags, or null if missing + */ + public function getAll() { + $redis = $this->get_connection(); + $raw = $redis->hgetall($this->_features_key); + if ($raw) { + $allFlags = array_map(FeatureFlag::getDecoder(), json_decode($raw, true)); + /** + * @param $flag FeatureFlag + * @return bool + */ + $isNotDeleted = function ($flag) { + return !$flag->isDeleted(); + }; + return array_filter($allFlags, $isNotDeleted); + } else { + $this->_logger->warning("LDDFeatureRequester: Attempted to get all features, instead got nothing."); + return null; + } + } } \ No newline at end of file diff --git a/src/LaunchDarkly/LDUser.php b/src/LaunchDarkly/LDUser.php index fb6ea96cb..76aaac5fe 100644 --- a/src/LaunchDarkly/LDUser.php +++ b/src/LaunchDarkly/LDUser.php @@ -20,20 +20,22 @@ class LDUser { protected $_custom = array(); /** - * @param string $key Unique key for the user. For authenticated users, this may be a username or e-mail address. For anonymous users, this could be an IP address or session ID. - * @param string|null $secondary An optional secondary identifier - * @param string|null $ip The user's IP address (optional) - * @param string|null $country The user's country, as an ISO 3166-1 alpha-2 code (e.g. 'US') (optional) - * @param string|null $email The user's e-mail address (optional) - * @param string|null $name The user's full name (optional) - * @param string|null $avatar A URL pointing to the user's avatar image (optional) - * @param string|null $firstName The user's first name (optional) - * @param string|null $lastName The user's last name (optional) + * @param string $key Unique key for the user. For authenticated users, this may be a username or e-mail address. For anonymous users, this could be an IP address or session ID. + * @param string|null $secondary An optional secondary identifier + * @param string|null $ip The user's IP address (optional) + * @param string|null $country The user's country, as an ISO 3166-1 alpha-2 code (e.g. 'US') (optional) + * @param string|null $email The user's e-mail address (optional) + * @param string|null $name The user's full name (optional) + * @param string|null $avatar A URL pointing to the user's avatar image (optional) + * @param string|null $firstName The user's first name (optional) + * @param string|null $lastName The user's last name (optional) * @param boolean|null $anonymous Whether this is an anonymous user - * @param array|null $custom Other custom attributes that can be used to create custom rules + * @param array|null $custom Other custom attributes that can be used to create custom rules */ - public function __construct($key, $secondary = null, $ip = null, $country = null, $email = null, $name = null, $avatar = null, $firstName = null, $lastName= null, $anonymous = null, $custom = array()) { - $this->_key = strval($key); + public function __construct($key, $secondary = null, $ip = null, $country = null, $email = null, $name = null, $avatar = null, $firstName = null, $lastName = null, $anonymous = null, $custom = array()) { + if ($key !== null) { + $this->_key = strval($key); + } $this->_secondary = $secondary; $this->_ip = $ip; $this->_country = $country; @@ -46,6 +48,40 @@ public function __construct($key, $secondary = null, $ip = null, $country = null $this->_custom = $custom; } + public function getValueForEvaluation($attr) { + switch ($attr) { + case "key": + return $this->getKey(); + case "secondary": //not available for evaluation. + return null; + case "ip": + return $this->getIP(); + case "country": + return $this->getCountry(); + case "email": + return $this->getEmail(); + case "name": + return $this->getName(); + case "avatar": + return $this->getAvatar(); + case "firstName": + return $this->getFirstName(); + case "lastName": + return $this->getLastName(); + case "anonymous": + return $this->getAnonymous(); + default: + $custom = $this->getCustom(); + if (is_null($custom)) { + return null; + } + if (!array_key_exists($attr, $custom)) { + return null; + } + return $custom[$attr]; + } + } + public function getCountry() { return $this->_country; } @@ -58,6 +94,9 @@ public function getIP() { return $this->_ip; } + /** + * @return null|string + */ public function getKey() { return $this->_key; } @@ -90,6 +129,10 @@ public function getAnonymous() { return $this->_anonymous; } + public function isKeyBlank() { + return isset($this->_key) && empty($this->_key); + } + public function toJSON() { $json = array("key" => $this->_key); @@ -122,7 +165,7 @@ public function toJSON() { } if (isset($this->_anonymous)) { $json['anonymous'] = $this->_anonymous; - } + } return $json; } } diff --git a/src/LaunchDarkly/Operators.php b/src/LaunchDarkly/Operators.php new file mode 100644 index 000000000..a491fc932 --- /dev/null +++ b/src/LaunchDarkly/Operators.php @@ -0,0 +1,118 @@ += 0 && strpos($u, $c, $temp) !== false); + } + break; + case "startsWith": + if (is_string($u) && is_string($c)) { + return strpos($u, $c) === 0; + } + break; + case "matches": + if (is_string($u) && is_string($c)) { + //PHP can do subpatterns, but everything needs to be wrapped in an outer (): + return preg_match("($c)", $u) === 1; + } + break; + case "contains": + if (is_string($u) && is_string($c)) { + return strpos($u, $c) !== FALSE; + } + break; + case "lessThan": + if (is_numeric($u) && is_numeric($c)) { + return $u < $c; + } + break; + case "lessThanOrEqual": + if (is_numeric($u) && is_numeric($c)) { + return $u <= $c; + } + break; + case "greaterThan": + if (is_numeric($u) && is_numeric($c)) { + return $u > $c; + } + break; + case "greaterThanOrEqual": + if (is_numeric($u) && is_numeric($c)) { + return $u >= $c; + } + break; + case "before": + $uTime = self::parseTime($u); + if ($uTime != null) { + $cTime = self::parseTime($c); + if ($cTime != null) { + return $uTime < $cTime; + } + } + break; + case "after": + $uTime = self::parseTime($u); + if ($uTime != null) { + $cTime = self::parseTime($c); + if ($cTime != null) { + return $uTime > $cTime; + } + } + break; + } + } catch (Exception $ignored) { + } + return false; + } + + /** + * @param $in + * @return null|int|float + */ + public static function parseTime($in) { + if (is_numeric($in)) { + return $in; + } + + if ($in instanceof DateTime) { + return Util::dateTimeToUnixMillis($in); + } + + if (is_string($in)) { + try { + $dateTime = new DateTime($in); + return Util::dateTimeToUnixMillis($dateTime); + } catch (Exception $e) { + return null; + } + } + return null; + } +} \ No newline at end of file diff --git a/src/LaunchDarkly/Rule.php b/src/LaunchDarkly/Rule.php new file mode 100644 index 000000000..c2ac20d43 --- /dev/null +++ b/src/LaunchDarkly/Rule.php @@ -0,0 +1,42 @@ +_clauses = $clauses; + } + + public static function getDecoder() { + return function ($v) { + return new Rule( + isset($v['variation']) ? $v['variation'] : null, + isset($v['rollout']) ? call_user_func(Rollout::getDecoder(), $v['rollout']) : null, + array_map(Clause::getDecoder(), $v['clauses'])); + }; + } + + /** + * @param $user LDUser + * @return bool + */ + public function matchesUser($user) { + foreach ($this->_clauses as $clause) { + if (!$clause->matchesUser($user)) { + return false; + } + } + return true; + } + + /** + * @return Clause[] + */ + public function getClauses() { + return $this->_clauses; + } +} \ No newline at end of file diff --git a/src/LaunchDarkly/TargetRule.php b/src/LaunchDarkly/TargetRule.php deleted file mode 100644 index 9388df083..000000000 --- a/src/LaunchDarkly/TargetRule.php +++ /dev/null @@ -1,81 +0,0 @@ -_attribute = $attribute; - $this->_operator = $operator; - $this->_values = $values; - } - - public function isKey() { - return $this->_attribute == "key"; - } - - - /** - * @param $user LDUser - * @return bool - */ - public function matchTarget($user) { - $u_value = null; - - switch ($this->_attribute) { - case "key": - $u_value = $user->getKey(); - break; - case "ip": - $u_value = $user->getIP(); - break; - case "country": - $u_value = $user->getCountry(); - break; - case "email": - $u_value = $user->getEmail(); - break; - case "name": - $u_value = $user->getName(); - break; - case "avatar": - $u_value = $user->getAvatar(); - break; - case "firstName": - $u_value = $user->getFirstName(); - break; - case "lastName": - $u_value = $user->getLastName(); - break; - case "anonymous": - $u_value = $user->getAnonymous(); - break; - default: - $custom = $user->getCustom(); - if (is_null($custom)) { - return false; - } - if (!array_key_exists($this->_attribute, $custom)) { - return false; - } - $u_value = $custom[$this->_attribute]; - - if (is_array($u_value)) { - foreach ($u_value as $elt) { - if (in_array($elt, $this->_values)) { - return true; - } - } - return false; - } - break; - } - - return isset($u_value) && in_array($u_value, $this->_values); - } -} \ No newline at end of file diff --git a/src/LaunchDarkly/Util.php b/src/LaunchDarkly/Util.php new file mode 100644 index 000000000..e1d84897a --- /dev/null +++ b/src/LaunchDarkly/Util.php @@ -0,0 +1,50 @@ +getTimeStamp(); + $timestampMicros = $dateTime->format('u'); + return $timeStampSeconds * 1000 + (int)($timestampMicros / 1000); + } + + /** + * @return int + */ + public static function currentTimeUnixMillis() { + return Util::dateTimeToUnixMillis(new DateTime('now', new DateTimeZone("UTC"))); + } + + + /** + * @param $key string + * @param $user LDUser + * @param $value + * @param $default + * @param null $version int | null + * @param null $prereqOf string | null + * @return array + */ + public static function newFeatureRequestEvent($key, $user, $value, $default, $version = null, $prereqOf = null) { + $event = array(); + $event['user'] = $user->toJSON(); + $event['value'] = $value; + $event['kind'] = "feature"; + $event['creationDate'] = Util::currentTimeUnixMillis(); + $event['key'] = $key; + $event['default'] = $default; + $event['version'] = $version; + $event['prereqOf'] = $prereqOf; + return $event; + } + +} \ No newline at end of file diff --git a/src/LaunchDarkly/Variation.php b/src/LaunchDarkly/Variation.php deleted file mode 100644 index 40cdd97a8..000000000 --- a/src/LaunchDarkly/Variation.php +++ /dev/null @@ -1,55 +0,0 @@ -_value = $value; - $this->_weight = $weight; - $this->_targets = $targets; - $this->_userTarget = $userTarget; - } - - /** - * @param $user LDUser - * @return bool - */ - public function matchUser($user) { - if ($this->_userTarget != null) { - return $this->_userTarget->matchTarget($user); - } - return false; - } - - public function matchTarget($user) { - foreach($this->_targets as $target) { - if ($this->_userTarget != null && $target->isKey()) { - continue; - } - if ($target->matchTarget($user)) { - return true; - } - } - return false; - } - - public function getValue() { - return $this->_value; - } - - public function getWeight() { - return $this->_weight; - } -} \ No newline at end of file diff --git a/src/LaunchDarkly/VariationOrRollout.php b/src/LaunchDarkly/VariationOrRollout.php new file mode 100644 index 000000000..e1ff63b60 --- /dev/null +++ b/src/LaunchDarkly/VariationOrRollout.php @@ -0,0 +1,89 @@ +_variation = $variation; + $this->_rollout = $rollout; + } + + public static function getDecoder() { + return function ($v) { + return new VariationOrRollout( + isset($v['variation']) ? $v['variation'] : null, + isset($v['rollout']) ? call_user_func(Rollout::getDecoder(), $v['rollout']) : null); + }; + } + + /** + * @return int | null + */ + public function getVariation() { + return $this->_variation; + } + + /** + * @return Rollout | null + */ + public function getRollout() { + return $this->_rollout; + } + + /** + * @param $user LDUser + * @param $_key string + * @param $_salt string + * @return int|null + */ + public function variationIndexForUser($user, $_key, $_salt) { + if ($this->_variation !== null) { + return $this->_variation; + } else if ($this->_rollout !== null) { + $bucketBy = $this->_rollout->getBucketBy() === null ? "key" : $this->_rollout->getBucketBy(); + $bucket = $this->bucketUser($user, $_key, $bucketBy, $_salt); + $sum = 0.0; + foreach ($this->_rollout->getVariations() as $wv) { + $sum += $wv->getWeight() / 100000.0; + if ($bucket < $sum) { + return $wv->getVariation(); + } + } + } + return null; + } + + /** + * @param $user LDUser + * @param $_key string + * @param $attr string + * @param $_salt string + * @return float + */ + private function bucketUser($user, $_key, $attr, $_salt) { + $userValue = $user->getValueForEvaluation($attr); + $idHash = null; + if ($userValue != null) { + if (is_string($userValue)) { + $idHash = $userValue; + if ($user->getSecondary() !== null) { + $idHash = $idHash . "." . $user->getSecondary(); + } + $hash = substr(sha1($_key . "." . $_salt . "." . $idHash), 0, 15); + $longVal = base_convert($hash, 16, 10); + $result = $longVal / self::$LONG_SCALE; + + return $result; + } + } + return 0.0; + } +} \ No newline at end of file diff --git a/tests/FeatureFlagTest.php b/tests/FeatureFlagTest.php new file mode 100644 index 000000000..a81cd11e4 --- /dev/null +++ b/tests/FeatureFlagTest.php @@ -0,0 +1,144 @@ +_simpleFlag = new FeatureRep("Sample flag", "sample.flag", "feefifofum", true, array($trueVariation, $falseVariation)); - $this->_disabledFlag = new FeatureRep("Sample flag", "sample.flag", "feefifofum", false, array($trueVariation, $falseVariation)); - - $userTargetVariation = new Variation(false, 20, array(), $targetUserOn); - - $this->_userTargetFlag = new FeatureRep("Sample flag", "sample.flag", "feefifofum", true, array($trueVariation, $userTargetVariation)); - } - - protected function tearDown() { - parent::tearDown(); - $this->_simpleFlag = null; - } - - public function testFlagForTargetedUserOff() { - $user = new LDUser("targetOff@test.com"); - $b = $this->_simpleFlag->evaluate($user); - $this->assertEquals(false, $b); - } - - public function testFlagForTargetedUserOn() { - $user = new LDUser("targetOn@test.com"); - $b = $this->_simpleFlag->evaluate($user); - $this->assertEquals(true, $b); - } - - public function testFlagForTargetGroupOn() { - $builder = new LDUserBuilder("targetOther@test.com"); - $user = $builder->custom(array("groups" => array("google", "microsoft")))->build(); - $b = $this->_simpleFlag->evaluate($user); - $this->assertEquals(true, $b); - } - - public function testFlagForTargetGroupOff() { - $builder = new LDUserBuilder("targetOther@test.com"); - $user = $builder->custom(array("groups" => array("oracle")))->build(); - $b = $this->_simpleFlag->evaluate($user); - $this->assertEquals(false, $b); - } - - public function testDisabledFlagAlwaysOff() { - $user = new LDUser("targetOn@test.com"); - $b = $this->_disabledFlag->evaluate($user); - $this->assertEquals(null, $b); - } - - public function testUserRuleFlagForTargetUserOff() { - $builder = new LDUserBuilder("targetOff@test.com"); - $user = $builder->build(); - $b = $this->_userTargetFlag->evaluate($user); - $this->assertEquals(false, $b); - } - - public function testFlagForTargetEmailOff() { - $builder = new LDUserBuilder("targetOff@test.com"); - $user = $builder->email("targetEmailOn@test.com")->build(); - $b = $this->_simpleFlag->evaluate($user); - $this->assertEquals(true,$b); - } -} - diff --git a/tests/LDClientTest.php b/tests/LDClientTest.php index 56b7da442..8487e0ca5 100644 --- a/tests/LDClientTest.php +++ b/tests/LDClientTest.php @@ -1,15 +1,17 @@ build(); - $this->assertEquals('argdef', $client->toggle('foo', $user, 'argdef')); + $this->assertEquals('argdef', $client->variation('foo', $user, 'argdef')); } public function testToggleFromArray() { @@ -34,7 +36,7 @@ public function testToggleFromArray() { $builder = new LDUserBuilder(3); $user = $builder->build(); - $this->assertEquals('fromarray', $client->toggle('foo', $user, 'argdef')); + $this->assertEquals('fromarray', $client->variation('foo', $user, 'argdef')); } public function testToggleEvent() { @@ -46,25 +48,21 @@ public function testToggleEvent() { $builder = new LDUserBuilder(3); $user = $builder->build(); - $client->toggle('foo', $user, 'argdef'); + $client->variation('foo', $user, 'argdef'); $proc = getPrivateField($client, '_eventProcessor'); $queue = getPrivateField($proc, '_queue'); $this->assertEquals(1, sizeof($queue)); } - public function testToggleEventsOff() { - MockFeatureRequester::$val = null; - $client = new LDClient("someKey", array( - 'feature_requester_class' => '\\LaunchDarkly\Tests\\MockFeatureRequester', - 'events' => false - )); + public function testOnlyValidFeatureRequester() { + $this->setExpectedException(InvalidArgumentException::class); + new LDClient("BOGUS_SDK_KEY", ['feature_requester_class' => 'stdClass']); + } - $builder = new LDUserBuilder(3); - $user = $builder->build(); - $client->toggle('foo', $user, 'argdef'); - $proc = getPrivateField($client, '_eventProcessor'); - $queue = getPrivateField($proc, '_queue'); - $this->assertEquals(0, sizeof($queue)); + public function testSecureModeHash() { + $client = new LDClient("secret", ['offline' => true]); + $user = new LDUser("Message"); + $this->assertEquals("aa747c502a898200f9e4fa21bac68136f886a0e27aec70ba06daf2e2a5cb5597", $client->secureModeHash($user)); } } @@ -86,4 +84,8 @@ function __construct($baseurl, $key, $options) { public function get($key) { return self::$val; } + + public function getAll() { + return null; + } } diff --git a/tests/LDUserTest.php b/tests/LDUserTest.php index 106b2a441..0dfbb0d01 100644 --- a/tests/LDUserTest.php +++ b/tests/LDUserTest.php @@ -100,5 +100,20 @@ public function testLDUserAnonymous() { $this->assertEquals(true, $json['anonymous']); } + + public function testLDUserBlankKey() { + $builder = new LDUserBuilder(""); + $user = $builder->build(); + $this->assertTrue($user->isKeyBlank()); + $this->assertFalse(is_null($user->getKey())); + + $builder = new LDUserBuilder("key"); + $user = $builder->build(); + $this->assertFalse($user->isKeyBlank()); + + $builder = new LDUserBuilder(null); + $user = $builder->build(); + $this->assertFalse($user->isKeyBlank()); + } } diff --git a/tests/OperatorsTest.php b/tests/OperatorsTest.php new file mode 100644 index 000000000..bb25ed423 --- /dev/null +++ b/tests/OperatorsTest.php @@ -0,0 +1,52 @@ +assertTrue(Operators::apply("in", "A string to match", "A string to match")); + $this->assertFalse(Operators::apply("in", "A string to match", true)); + $this->assertTrue(Operators::apply("in", 34, 34)); + $this->assertTrue(Operators::apply("in", 34, 34.0)); + $this->assertFalse(Operators::apply("in", 34, true)); + $this->assertTrue(Operators::apply("in", false, false)); + $this->assertTrue(Operators::apply("in", true, true)); + $this->assertFalse(Operators::apply("in", true, false)); + $this->assertFalse(Operators::apply("in", false, true)); + } + + public function testStartsWith() { + $this->assertTrue(Operators::apply("startsWith", "start", "start")); + $this->assertTrue(Operators::apply("startsWith", "start plus more", "start")); + $this->assertFalse(Operators::apply("startsWith", "does not contain", "start")); + $this->assertFalse(Operators::apply("startsWith", "does not start with", "start")); + } + + public function testEndsWith() { + $this->assertTrue(Operators::apply("endsWith", "end", "end")); + $this->assertTrue(Operators::apply("endsWith", "something somethingend", "end")); + $this->assertFalse(Operators::apply("endsWith", "does not contain", "end")); + $this->assertFalse(Operators::apply("endsWith", "does not end with", "end")); + } + + public function testMatches() { + $this->assertTrue(Operators::apply("matches", "anything", ".*")); + $this->assertTrue(Operators::apply("matches", "darn", "(\\W|^)(baloney|darn|drat|fooey|gosh\\sdarnit|heck)(\\W|$)")); + } + + public function testParseTime() { + $this->assertEquals(0, Operators::parseTime(0)); + $this->assertEquals(100, Operators::parseTime(100)); + $this->assertEquals(100, Operators::parseTime(100)); + $this->assertEquals(1000, Operators::parseTime("1970-01-01T00:00:01Z")); + $this->assertEquals(1001, Operators::parseTime("1970-01-01T00:00:01.001Z")); + + + $this->assertEquals(null, Operators::parseTime("NOT A REAL TIMESTAMP")); + $this->assertEquals(null, Operators::parseTime([])); + } +} \ No newline at end of file From 257cbd65f580e272c81e93bfb8555afb8e3b0ddb Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 26 Sep 2016 15:17:40 -0700 Subject: [PATCH 11/18] Get rid of ::class, which requires php 5.5+ --- src/LaunchDarkly/LDClient.php | 3 --- tests/LDClientTest.php | 5 ----- 2 files changed, 8 deletions(-) diff --git a/src/LaunchDarkly/LDClient.php b/src/LaunchDarkly/LDClient.php index 5d1dd8305..88f53d398 100644 --- a/src/LaunchDarkly/LDClient.php +++ b/src/LaunchDarkly/LDClient.php @@ -88,9 +88,6 @@ public function __construct($sdkKey, $options = array()) { $featureRequesterClass = '\\LaunchDarkly\\GuzzleFeatureRequester'; } - if (!is_a($featureRequesterClass, FeatureRequester::class, true)) { - throw new \InvalidArgumentException; - } $this->_featureRequester = new $featureRequesterClass($this->_baseUri, $sdkKey, $options); } diff --git a/tests/LDClientTest.php b/tests/LDClientTest.php index 8487e0ca5..726410c0c 100644 --- a/tests/LDClientTest.php +++ b/tests/LDClientTest.php @@ -54,11 +54,6 @@ public function testToggleEvent() { $this->assertEquals(1, sizeof($queue)); } - public function testOnlyValidFeatureRequester() { - $this->setExpectedException(InvalidArgumentException::class); - new LDClient("BOGUS_SDK_KEY", ['feature_requester_class' => 'stdClass']); - } - public function testSecureModeHash() { $client = new LDClient("secret", ['offline' => true]); $user = new LDUser("Message"); From 74147addf8e490aa21bd18c53bf496cc7abe34ce Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 26 Sep 2016 15:32:37 -0700 Subject: [PATCH 12/18] Fix import paths for Guzzle 3 --- src/LaunchDarkly/GuzzleFeatureRequester.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index 345f352e1..2c3bad91c 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -1,9 +1,9 @@ Date: Mon, 26 Sep 2016 15:38:54 -0700 Subject: [PATCH 13/18] Fix GuzzleFeatureRequester ctor --- src/LaunchDarkly/GuzzleFeatureRequester.php | 33 +++++++++------------ src/LaunchDarkly/LDClient.php | 2 +- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index 2c3bad91c..f5c6da145 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -3,7 +3,7 @@ use Guzzle\Http\Client; use Guzzle\Http\Exception\BadResponseException; -use Guzzle\Http\Subscriber\Cache\CacheSubscriber; +use Guzzle\Plugin\Cache\CachePlugin; use Psr\Log\LoggerInterface; class GuzzleFeatureRequester implements FeatureRequester { @@ -14,29 +14,24 @@ class GuzzleFeatureRequester implements FeatureRequester { private $_logger; function __construct($baseUri, $sdkKey, $options) { - $this->_client = new Client(array( - 'base_url' => $baseUri, - 'defaults' => array( + $this->_client = new Client($baseUri, + array( + 'debug' => false, + 'curl.options' => array('CURLOPT_TCP_NODELAY' => 1), + 'request.options' => array( 'headers' => array( 'Authorization' => "{$sdkKey}", - 'Content-Type' => 'application/json', - 'User-Agent' => 'PHPClient/' . LDClient::VERSION + 'Content-Type' => 'application/json' ), - 'debug' => false, 'timeout' => $options['timeout'], 'connect_timeout' => $options['connect_timeout'] ) )); - - if (!isset($options['cache_storage'])) { - $csOptions = array('validate' => false); - } - else { - $csOptions = array('storage' => $options['cache_storage'], 'validate' => false); + $this->_client->setUserAgent('PHPClient53/' . LDClient::VERSION); + if (isset($options['cache_storage'])) { + $cachePlugin = new CachePlugin(array('storage' => $options['cache_storage'], 'validate' => false)); + $this->_client->addSubscriber($cachePlugin); } - - CacheSubscriber::attach($this->_client, $csOptions); - $this->_logger = $options['logger']; } @@ -50,7 +45,7 @@ public function get($key) { try { $uri = self::SDK_FLAGS . "/" . $key; - $response = $this->_client->get($uri, $this->_defaults); + $response = $this->_client->get($uri); $body = $response->getBody(); return FeatureFlag::decode(json_decode($body, true)); } catch (BadResponseException $e) { @@ -67,8 +62,8 @@ public function get($key) */ public function getAll() { try { - $uri = $this->_baseUri . self::SDK_FLAGS; - $response = $this->_client->get($uri, $this->_defaults); + $uri = self::SDK_FLAGS; + $response = $this->_client->get($uri); $body = $response->getBody(); return array_map(FeatureFlag::getDecoder(), json_decode($body, true)); } catch (BadResponseException $e) { diff --git a/src/LaunchDarkly/LDClient.php b/src/LaunchDarkly/LDClient.php index 88f53d398..a720df50f 100644 --- a/src/LaunchDarkly/LDClient.php +++ b/src/LaunchDarkly/LDClient.php @@ -39,7 +39,7 @@ class LDClient { * - events_uri: Base URI for sending events to LaunchDarkly. Defaults to 'https://events.launchdarkly.com' * - timeout: Float describing the maximum length of a request in seconds. Defaults to 3 * - connect_timeout: Float describing the number of seconds to wait while trying to connect to a server. Defaults to 3 - * - cache: An optional Kevinrob\GuzzleCache\Strategy\CacheStorageInterface. Defaults to an in-memory cache. + * - cache_storage: An optional GuzzleHttp\Subscriber\Cache\CacheStorageInterface. Defaults to an in-memory cache. * - send_events: An optional bool that can disable the sending of events to LaunchDarkly. Defaults to false. * - logger: An optional Psr\Log\LoggerInterface. Defaults to a Monolog\Logger sending all messages to the php error_log. * - offline: An optional boolean which will disable all network calls and always return the default value. Defaults to false. From de08161c7d898d00e18699c20df547d79115e026 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Mon, 26 Sep 2016 15:47:50 -0700 Subject: [PATCH 14/18] Fix calls to Guzzle 3 --- src/LaunchDarkly/GuzzleFeatureRequester.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/LaunchDarkly/GuzzleFeatureRequester.php b/src/LaunchDarkly/GuzzleFeatureRequester.php index f5c6da145..df5739e5a 100644 --- a/src/LaunchDarkly/GuzzleFeatureRequester.php +++ b/src/LaunchDarkly/GuzzleFeatureRequester.php @@ -45,7 +45,7 @@ public function get($key) { try { $uri = self::SDK_FLAGS . "/" . $key; - $response = $this->_client->get($uri); + $response = $this->_client->get($uri)->send(); $body = $response->getBody(); return FeatureFlag::decode(json_decode($body, true)); } catch (BadResponseException $e) { @@ -63,7 +63,7 @@ public function get($key) public function getAll() { try { $uri = self::SDK_FLAGS; - $response = $this->_client->get($uri); + $response = $this->_client->get($uri)->send(); $body = $response->getBody(); return array_map(FeatureFlag::getDecoder(), json_decode($body, true)); } catch (BadResponseException $e) { From baeebcc44f6f870f730af24175b6ed67d41fcb8c Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 27 Sep 2016 09:44:58 -0700 Subject: [PATCH 15/18] Change CircleCI PHP version, update tests --- circle.yml | 2 +- tests/FeatureFlagTest.php | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/circle.yml b/circle.yml index 7347245d3..22fdc539d 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: php: - version: 5.5.21 + version: 5.3.29 test: override: diff --git a/tests/FeatureFlagTest.php b/tests/FeatureFlagTest.php index a81cd11e4..c61fb3e01 100644 --- a/tests/FeatureFlagTest.php +++ b/tests/FeatureFlagTest.php @@ -137,8 +137,8 @@ class FeatureFlagTest extends \PHPUnit_Framework_TestCase { }"; public function testDecode() { - FeatureFlag::decode(\GuzzleHttp\json_decode(FeatureFlagTest::$json1, true)); - FeatureFlag::decode(\GuzzleHttp\json_decode(FeatureFlagTest::$json2, true)); + FeatureFlag::decode(json_decode(FeatureFlagTest::$json1, true)); + FeatureFlag::decode(json_decode(FeatureFlagTest::$json2, true)); } } From 7544e913ea9061a418e9a494b96e30f8194aac2d Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 27 Sep 2016 09:48:22 -0700 Subject: [PATCH 16/18] Switch installed PHP version --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 22fdc539d..747a45d25 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: php: - version: 5.3.29 + version: 5.3.25 test: override: From 0c43f9966cec0ba4187dfdc3d2d8d2e8a09213b7 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 27 Sep 2016 10:01:40 -0700 Subject: [PATCH 17/18] Bump to PHP 5.4.x compatible PHP version --- circle.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/circle.yml b/circle.yml index 747a45d25..77aae221c 100644 --- a/circle.yml +++ b/circle.yml @@ -1,6 +1,6 @@ machine: php: - version: 5.3.25 + version: 5.4.37 test: override: From c86fa3d368d94b1388430a396e78862e72f4deb2 Mon Sep 17 00:00:00 2001 From: John Kodumal Date: Tue, 27 Sep 2016 10:08:28 -0700 Subject: [PATCH 18/18] Change guzzle3 dep to https --- composer.json | 2 +- composer.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 892db53c7..3c109221f 100644 --- a/composer.json +++ b/composer.json @@ -8,7 +8,7 @@ "repositories": [ { "type": "git", - "url": "git://github.com/launchdarkly/guzzle3.git" + "url": "https://github.com/launchdarkly/guzzle3.git" } ], "homepage": "https://github.com/launchdarkly/php-client", diff --git a/composer.lock b/composer.lock index 61426104a..b04fe4754 100644 --- a/composer.lock +++ b/composer.lock @@ -4,15 +4,15 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "hash": "68cb258a6ff921174b988769600d2cb2", - "content-hash": "03dd4eab149acdc05b15bc1bf2ea235b", + "hash": "74e1b7bb93bb1571d1ab0153920fa0a9", + "content-hash": "8a18d98bba3bb72ac1d72ac4264d976b", "packages": [ { "name": "guzzle/guzzle", "version": "dev-master", "source": { "type": "git", - "url": "git://github.com/launchdarkly/guzzle3.git", + "url": "https://github.com/launchdarkly/guzzle3.git", "reference": "ecb935d2d0ecd8cddae4dcfb90515cac5e2cb023" }, "require": {