diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..c89dfcc --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,40 @@ +name: Lint + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4'] + + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: Cache Composer dependencies + uses: actions/cache@v2 + with: + path: /tmp/composer-cache + key: ${{ runner.os }}-php${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} + + - name: Install dependencies + uses: php-actions/composer@v5 + with: + command: install + args: --prefer-dist --no-progress --no-suggest --verbose + php_version: ${{ matrix.php-versions }} + version: 1 + + - name: Run lint + run: make lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..38823a0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,40 @@ +name: Test + +on: + push: + branches: [ '*' ] + pull_request: + branches: [ '*' ] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + php-versions: ['7.0', '7.1', '7.2', '7.3', '7.4'] + + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: Cache Composer dependencies + uses: actions/cache@v2 + with: + path: /tmp/composer-cache + key: ${{ runner.os }}-php${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} + + - name: Install dependencies + uses: php-actions/composer@v5 + with: + command: install + args: --prefer-dist --no-progress --no-suggest --verbose + php_version: ${{ matrix.php-versions }} + version: 1 + + - name: Run tests + run: make test diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 9c0cb34..0000000 --- a/.travis.yml +++ /dev/null @@ -1,15 +0,0 @@ -language: php - -php: - - 7.0 - - 7.1 - -before_install: - - composer self-update - -install: - - travis_retry composer install --no-interaction --prefer-source - -script: - - make lint - - make test diff --git a/Makefile b/Makefile index 2bd666d..5d2917d 100644 --- a/Makefile +++ b/Makefile @@ -1,17 +1,14 @@ -THIS := $(realpath $(lastword $(MAKEFILE_LIST))) -HERE := $(shell dirname $(THIS)) - .PHONY: all fix lint test all: lint test fix: - php $(HERE)/vendor/bin/php-cs-fixer fix --config=$(HERE)/.php_cs + php vendor/bin/php-cs-fixer fix --config=.php_cs lint: - php $(HERE)/vendor/bin/php-cs-fixer fix --config=$(HERE)/.php_cs --dry-run - php $(HERE)/vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode - php $(HERE)/vendor/bin/phpmd tests/ text cleancode,codesize,controversial,design,naming,unusedcode + php vendor/bin/php-cs-fixer fix --config=.php_cs --dry-run + php vendor/bin/phpmd src/ text cleancode,codesize,controversial,design,naming,unusedcode + php vendor/bin/phpmd tests/ text cleancode,codesize,controversial,design,naming,unusedcode test: - php $(HERE)/vendor/bin/phpunit --configuration $(HERE)/phpunit.xml + php vendor/bin/phpunit --configuration phpunit.xml diff --git a/composer.json b/composer.json index 76cbda0..6b3c057 100644 --- a/composer.json +++ b/composer.json @@ -3,13 +3,13 @@ "description": "PHP binding for v4 of the Cloudflare Client API.", "type": "library", "require": { - "guzzlehttp/guzzle": "^6.2.2", - "php": ">=7.0.0", + "guzzlehttp/guzzle": "^7.0.1", + "php": ">=7.2.5", "psr/http-message": "~1.0", "ext-json": "*" }, "require-dev": { - "phpunit/phpunit": "5.7.5", + "phpunit/phpunit": "^5.7", "phpmd/phpmd" : "@stable", "friendsofphp/php-cs-fixer": "^2.6" }, diff --git a/composer.lock b/composer.lock index 0b8441f..633000d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,48 +4,50 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9a243e606c74dcbd67ae934182df9d2c", + "content-hash": "dfd011c55474ffaab84b7a0a6d164049", "packages": [ { "name": "guzzlehttp/guzzle", - "version": "6.3.3", + "version": "6.5.2", "source": { "type": "git", "url": "https://github.com/guzzle/guzzle.git", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba" + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/guzzle/zipball/407b0cb880ace85c9b63c5f9551db498cb2d50ba", - "reference": "407b0cb880ace85c9b63c5f9551db498cb2d50ba", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/43ece0e75098b7ecd8d13918293029e555a50f82", + "reference": "43ece0e75098b7ecd8d13918293029e555a50f82", "shasum": "" }, "require": { + "ext-json": "*", "guzzlehttp/promises": "^1.0", - "guzzlehttp/psr7": "^1.4", + "guzzlehttp/psr7": "^1.6.1", "php": ">=5.5" }, "require-dev": { "ext-curl": "*", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.4 || ^7.0", - "psr/log": "^1.0" + "psr/log": "^1.1" }, "suggest": { + "ext-intl": "Required for Internationalized Domain Name (IDN) support", "psr/log": "Required for using the Log middleware" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "6.3-dev" + "dev-master": "6.5-dev" } }, "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { "GuzzleHttp\\": "src/" - } + }, + "files": [ + "src/functions_include.php" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -69,7 +71,7 @@ "rest", "web service" ], - "time": "2018-04-22T15:46:56+00:00" + "time": "2019-12-23T11:57:10+00:00" }, { "name": "guzzlehttp/promises", @@ -124,32 +126,37 @@ }, { "name": "guzzlehttp/psr7", - "version": "1.4.2", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/guzzle/psr7.git", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c" + "reference": "239400de7a173fe9901b9ac7c06497751f00727a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/guzzle/psr7/zipball/f5b8a8512e2b58b0071a7280e39f14f72e05d87c", - "reference": "f5b8a8512e2b58b0071a7280e39f14f72e05d87c", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/239400de7a173fe9901b9ac7c06497751f00727a", + "reference": "239400de7a173fe9901b9ac7c06497751f00727a", "shasum": "" }, "require": { "php": ">=5.4.0", - "psr/http-message": "~1.0" + "psr/http-message": "~1.0", + "ralouphie/getallheaders": "^2.0.5 || ^3.0.0" }, "provide": { "psr/http-message-implementation": "1.0" }, "require-dev": { - "phpunit/phpunit": "~4.0" + "ext-zlib": "*", + "phpunit/phpunit": "~4.8.36 || ^5.7.27 || ^6.5.8" + }, + "suggest": { + "zendframework/zend-httphandlerrunner": "Emit PSR-7 responses" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.4-dev" + "dev-master": "1.6-dev" } }, "autoload": { @@ -179,13 +186,14 @@ "keywords": [ "http", "message", + "psr-7", "request", "response", "stream", "uri", "url" ], - "time": "2017-03-20T17:10:46+00:00" + "time": "2019-07-01T23:21:34+00:00" }, { "name": "psr/http-message", @@ -236,29 +244,68 @@ "response" ], "time": "2016-08-06T14:39:51+00:00" + }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "time": "2019-03-08T08:55:37+00:00" } ], "packages-dev": [ { "name": "composer/semver", - "version": "1.4.2", + "version": "1.5.1", "source": { "type": "git", "url": "https://github.com/composer/semver.git", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573" + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/c7cb9a2095a074d131b65a8a0cd294479d785573", - "reference": "c7cb9a2095a074d131b65a8a0cd294479d785573", + "url": "https://api.github.com/repos/composer/semver/zipball/c6bea70230ef4dd483e6bbcab6005f682ed3a8de", + "reference": "c6bea70230ef4dd483e6bbcab6005f682ed3a8de", "shasum": "" }, "require": { "php": "^5.3.2 || ^7.0" }, "require-dev": { - "phpunit/phpunit": "^4.5 || ^5.0.5", - "phpunit/phpunit-mock-objects": "2.3.0 || ^3.0" + "phpunit/phpunit": "^4.5 || ^5.0.5" }, "type": "library", "extra": { @@ -299,28 +346,28 @@ "validation", "versioning" ], - "time": "2016-08-30T16:08:34+00:00" + "time": "2020-01-13T12:06:48+00:00" }, { "name": "composer/xdebug-handler", - "version": "1.3.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/composer/xdebug-handler.git", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c" + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/b8e9745fb9b06ea6664d8872c4505fb16df4611c", - "reference": "b8e9745fb9b06ea6664d8872c4505fb16df4611c", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/1ab9842d69e64fb3a01be6b656501032d1b78cb7", + "reference": "1ab9842d69e64fb3a01be6b656501032d1b78cb7", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0", + "php": "^5.3.2 || ^7.0 || ^8.0", "psr/log": "^1.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" + "phpunit/phpunit": "^4.8.35 || ^5.7 || 6.5 - 8" }, "type": "library", "autoload": { @@ -338,12 +385,12 @@ "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Restarts a process without xdebug.", + "description": "Restarts a process without Xdebug.", "keywords": [ "Xdebug", "performance" ], - "time": "2018-08-31T19:07:57+00:00" + "time": "2020-03-01T12:26:26+00:00" }, { "name": "doctrine/annotations", @@ -469,21 +516,24 @@ }, { "name": "doctrine/lexer", - "version": "v1.0.1", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/doctrine/lexer.git", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c" + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/83893c552fd2045dd78aef794c31e694c37c0b8c", - "reference": "83893c552fd2045dd78aef794c31e694c37c0b8c", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/1febd6c3ef84253d7c815bed85fc622ad207a9f8", + "reference": "1febd6c3ef84253d7c815bed85fc622ad207a9f8", "shasum": "" }, "require": { "php": ">=5.3.2" }, + "require-dev": { + "phpunit/phpunit": "^4.5" + }, "type": "library", "extra": { "branch-alias": { @@ -491,8 +541,8 @@ } }, "autoload": { - "psr-0": { - "Doctrine\\Common\\Lexer\\": "lib/" + "psr-4": { + "Doctrine\\Common\\Lexer\\": "lib/Doctrine/Common/Lexer" } }, "notification-url": "https://packagist.org/downloads/", @@ -513,26 +563,29 @@ "email": "schmittjoh@gmail.com" } ], - "description": "Base library for a lexer that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "http://www.doctrine-project.org", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ + "annotations", + "docblock", "lexer", - "parser" + "parser", + "php" ], - "time": "2014-09-09T13:34:57+00:00" + "time": "2019-06-08T11:03:04+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v2.13.1", + "version": "v2.16.1", "source": { "type": "git", "url": "https://github.com/FriendsOfPHP/PHP-CS-Fixer.git", - "reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161" + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/54814c62d5beef3ba55297b9b3186ed8b8a1b161", - "reference": "54814c62d5beef3ba55297b9b3186ed8b8a1b161", + "url": "https://api.github.com/repos/FriendsOfPHP/PHP-CS-Fixer/zipball/c8afb599858876e95e8ebfcd97812d383fa23f02", + "reference": "c8afb599858876e95e8ebfcd97812d383fa23f02", "shasum": "" }, "require": { @@ -541,33 +594,31 @@ "doctrine/annotations": "^1.2", "ext-json": "*", "ext-tokenizer": "*", - "php": "^5.6 || >=7.0 <7.3", + "php": "^5.6 || ^7.0", "php-cs-fixer/diff": "^1.3", - "symfony/console": "^3.4.17 || ^4.1.6", - "symfony/event-dispatcher": "^3.0 || ^4.0", - "symfony/filesystem": "^3.0 || ^4.0", - "symfony/finder": "^3.0 || ^4.0", - "symfony/options-resolver": "^3.0 || ^4.0", + "symfony/console": "^3.4.17 || ^4.1.6 || ^5.0", + "symfony/event-dispatcher": "^3.0 || ^4.0 || ^5.0", + "symfony/filesystem": "^3.0 || ^4.0 || ^5.0", + "symfony/finder": "^3.0 || ^4.0 || ^5.0", + "symfony/options-resolver": "^3.0 || ^4.0 || ^5.0", "symfony/polyfill-php70": "^1.0", "symfony/polyfill-php72": "^1.4", - "symfony/process": "^3.0 || ^4.0", - "symfony/stopwatch": "^3.0 || ^4.0" - }, - "conflict": { - "hhvm": "*" + "symfony/process": "^3.0 || ^4.0 || ^5.0", + "symfony/stopwatch": "^3.0 || ^4.0 || ^5.0" }, "require-dev": { "johnkary/phpunit-speedtrap": "^1.1 || ^2.0 || ^3.0", "justinrainbow/json-schema": "^5.0", - "keradus/cli-executor": "^1.1", + "keradus/cli-executor": "^1.2", "mikey179/vfsstream": "^1.6", "php-coveralls/php-coveralls": "^2.1", "php-cs-fixer/accessible-object": "^1.0", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.0.1", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.0.1", - "phpunit/phpunit": "^5.7.27 || ^6.5.8 || ^7.1", - "phpunitgoodpractices/traits": "^1.5.1", - "symfony/phpunit-bridge": "^4.0" + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.1", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.1", + "phpunit/phpunit": "^5.7.27 || ^6.5.14 || ^7.1", + "phpunitgoodpractices/traits": "^1.8", + "symfony/phpunit-bridge": "^4.3 || ^5.0", + "symfony/yaml": "^3.0 || ^4.0 || ^5.0" }, "suggest": { "ext-mbstring": "For handling non-UTF8 characters in cache signature.", @@ -600,17 +651,17 @@ "MIT" ], "authors": [ - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - }, { "name": "Fabien Potencier", "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" } ], "description": "A tool to automatically fix PHP code style", - "time": "2018-10-21T00:32:10+00:00" + "time": "2019-11-25T22:10:32+00:00" }, { "name": "myclabs/deep-copy", @@ -704,32 +755,39 @@ }, { "name": "pdepend/pdepend", - "version": "2.5.2", + "version": "2.7.1", "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239" + "reference": "daba1cf0a6edaf172fa02a17807ae29f4c1c7471" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/9daf26d0368d4a12bed1cacae1a9f3a6f0adf239", - "reference": "9daf26d0368d4a12bed1cacae1a9f3a6f0adf239", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/daba1cf0a6edaf172fa02a17807ae29f4c1c7471", + "reference": "daba1cf0a6edaf172fa02a17807ae29f4c1c7471", "shasum": "" }, "require": { "php": ">=5.3.7", - "symfony/config": "^2.3.0|^3|^4", - "symfony/dependency-injection": "^2.3.0|^3|^4", - "symfony/filesystem": "^2.3.0|^3|^4" + "symfony/config": "^2.3.0|^3|^4|^5", + "symfony/dependency-injection": "^2.3.0|^3|^4|^5", + "symfony/filesystem": "^2.3.0|^3|^4|^5" }, "require-dev": { - "phpunit/phpunit": "^4.8|^5.7", + "easy-doc/easy-doc": "0.0.0 || ^1.2.3", + "gregwar/rst": "^1.0", + "phpunit/phpunit": "^4.8.35|^5.7", "squizlabs/php_codesniffer": "^2.0.0" }, "bin": [ "src/bin/pdepend" ], "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, "autoload": { "psr-4": { "PDepend\\": "src/main/php/PDepend" @@ -740,7 +798,7 @@ "BSD-3-Clause" ], "description": "Official version of pdepend to be handled with Composer", - "time": "2017-12-13T13:21:38+00:00" + "time": "2020-02-08T12:06:13+00:00" }, { "name": "php-cs-fixer/diff", @@ -849,27 +907,28 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.3.0", + "version": "4.3.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08" + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", - "reference": "94fd0001232e47129dd3504189fa1c7225010d08", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/da3fd972d6bafd628114f7e7e036f45944b62e9c", + "reference": "da3fd972d6bafd628114f7e7e036f45944b62e9c", "shasum": "" }, "require": { "php": "^7.0", - "phpdocumentor/reflection-common": "^1.0.0", - "phpdocumentor/type-resolver": "^0.4.0", + "phpdocumentor/reflection-common": "^1.0.0 || ^2.0.0", + "phpdocumentor/type-resolver": "~0.4 || ^1.0.0", "webmozart/assert": "^1.0" }, "require-dev": { - "doctrine/instantiator": "~1.0.5", + "doctrine/instantiator": "^1.0.5", "mockery/mockery": "^1.0", + "phpdocumentor/type-resolver": "0.4.*", "phpunit/phpunit": "^6.4" }, "type": "library", @@ -896,29 +955,29 @@ } ], "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "time": "2017-11-30T07:14:17+00:00" + "time": "2019-12-28T18:55:12+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "0.4.0", + "version": "0.5.1", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7" + "reference": "cf842904952e64e703800d094cdf34e715a8a3ae" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/9c977708995954784726e25d0cd1dddf4e65b0f7", - "reference": "9c977708995954784726e25d0cd1dddf4e65b0f7", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/cf842904952e64e703800d094cdf34e715a8a3ae", + "reference": "cf842904952e64e703800d094cdf34e715a8a3ae", "shasum": "" }, "require": { - "php": "^5.5 || ^7.0", + "php": "^7.0", "phpdocumentor/reflection-common": "^1.0" }, "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^5.2||^4.8.24" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^6.4" }, "type": "library", "extra": { @@ -928,9 +987,7 @@ }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] + "phpDocumentor\\Reflection\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -943,7 +1000,7 @@ "email": "me@mikevanriel.com" } ], - "time": "2017-07-14T14:27:02+00:00" + "time": "2017-12-30T13:23:38+00:00" }, { "name": "phpmd/phpmd", @@ -1013,38 +1070,38 @@ }, { "name": "phpspec/prophecy", - "version": "1.8.0", + "version": "v1.10.3", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06" + "reference": "451c3cd1418cf640de218914901e51b064abb093" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/4ba436b55987b4bf311cb7c6ba82aa528aac0a06", - "reference": "4ba436b55987b4bf311cb7c6ba82aa528aac0a06", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/451c3cd1418cf640de218914901e51b064abb093", + "reference": "451c3cd1418cf640de218914901e51b064abb093", "shasum": "" }, "require": { "doctrine/instantiator": "^1.0.2", "php": "^5.3|^7.0", - "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0", - "sebastian/comparator": "^1.1|^2.0|^3.0", - "sebastian/recursion-context": "^1.0|^2.0|^3.0" + "phpdocumentor/reflection-docblock": "^2.0|^3.0.2|^4.0|^5.0", + "sebastian/comparator": "^1.2.3|^2.0|^3.0|^4.0", + "sebastian/recursion-context": "^1.0|^2.0|^3.0|^4.0" }, "require-dev": { - "phpspec/phpspec": "^2.5|^3.2", + "phpspec/phpspec": "^2.5 || ^3.2", "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5 || ^7.1" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "1.8.x-dev" + "dev-master": "1.10.x-dev" } }, "autoload": { - "psr-0": { - "Prophecy\\": "src/" + "psr-4": { + "Prophecy\\": "src/Prophecy" } }, "notification-url": "https://packagist.org/downloads/", @@ -1072,7 +1129,7 @@ "spy", "stub" ], - "time": "2018-08-05T17:53:17+00:00" + "time": "2020-03-05T15:02:03+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1325,16 +1382,16 @@ }, { "name": "phpunit/phpunit", - "version": "5.7.5", + "version": "5.7.27", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe" + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/50fd2be8f3e23e91da825f36f08e5f9633076ffe", - "reference": "50fd2be8f3e23e91da825f36f08e5f9633076ffe", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", + "reference": "b7803aeca3ccb99ad0a506fa80b64cd6a56bbc0c", "shasum": "" }, "require": { @@ -1346,20 +1403,20 @@ "myclabs/deep-copy": "~1.3", "php": "^5.6 || ^7.0", "phpspec/prophecy": "^1.6.2", - "phpunit/php-code-coverage": "^4.0.3", + "phpunit/php-code-coverage": "^4.0.4", "phpunit/php-file-iterator": "~1.4", "phpunit/php-text-template": "~1.2", "phpunit/php-timer": "^1.0.6", "phpunit/phpunit-mock-objects": "^3.2", - "sebastian/comparator": "~1.2.2", - "sebastian/diff": "~1.2", + "sebastian/comparator": "^1.2.4", + "sebastian/diff": "^1.4.3", "sebastian/environment": "^1.3.4 || ^2.0", "sebastian/exporter": "~2.0", - "sebastian/global-state": "^1.0 || ^2.0", + "sebastian/global-state": "^1.1", "sebastian/object-enumerator": "~2.0", "sebastian/resource-operations": "~1.0", - "sebastian/version": "~1.0|~2.0", - "symfony/yaml": "~2.1|~3.0" + "sebastian/version": "^1.0.6|^2.0.1", + "symfony/yaml": "~2.1|~3.0|~4.0" }, "conflict": { "phpdocumentor/reflection-docblock": "3.0.2" @@ -1403,7 +1460,7 @@ "testing", "xunit" ], - "time": "2016-12-28T07:18:51+00:00" + "time": "2018-02-01T05:50:59+00:00" }, { "name": "phpunit/phpunit-mock-objects", @@ -1462,6 +1519,7 @@ "mock", "xunit" ], + "abandoned": true, "time": "2017-06-30T09:13:00+00:00" }, { @@ -1515,16 +1573,16 @@ }, { "name": "psr/log", - "version": "1.0.2", + "version": "1.1.2", "source": { "type": "git", "url": "https://github.com/php-fig/log.git", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d" + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", - "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d", + "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801", + "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801", "shasum": "" }, "require": { @@ -1533,7 +1591,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.0.x-dev" + "dev-master": "1.1.x-dev" } }, "autoload": { @@ -1558,7 +1616,7 @@ "psr", "psr-3" ], - "time": "2016-10-10T12:19:37+00:00" + "time": "2019-11-01T11:05:21+00:00" }, { "name": "sebastian/code-unit-reverse-lookup", @@ -1840,23 +1898,23 @@ }, { "name": "sebastian/global-state", - "version": "2.0.0", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4" + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", - "reference": "e8ba02eed7bbbb9e59e43dedd3dddeff4a56b0c4", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/bc37d50fea7d017d3d340f230811c9f1d7280af4", + "reference": "bc37d50fea7d017d3d340f230811c9f1d7280af4", "shasum": "" }, "require": { - "php": "^7.0" + "php": ">=5.3.3" }, "require-dev": { - "phpunit/phpunit": "^6.0" + "phpunit/phpunit": "~4.2" }, "suggest": { "ext-uopz": "*" @@ -1864,7 +1922,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0-dev" + "dev-master": "1.0-dev" } }, "autoload": { @@ -1887,7 +1945,7 @@ "keywords": [ "global state" ], - "time": "2017-04-27T15:39:26+00:00" + "time": "2015-10-12T03:26:01+00:00" }, { "name": "sebastian/object-enumerator", @@ -2075,16 +2133,16 @@ }, { "name": "symfony/config", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "e5389132dc6320682de3643091121c048ff796b3" + "reference": "03328d6e172ec7384341c622d4c28d350040d021" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/e5389132dc6320682de3643091121c048ff796b3", - "reference": "e5389132dc6320682de3643091121c048ff796b3", + "url": "https://api.github.com/repos/symfony/config/zipball/03328d6e172ec7384341c622d4c28d350040d021", + "reference": "03328d6e172ec7384341c622d4c28d350040d021", "shasum": "" }, "require": { @@ -2135,20 +2193,20 @@ ], "description": "Symfony Config Component", "homepage": "https://symfony.com", - "time": "2018-09-08T13:15:14+00:00" + "time": "2020-02-03T08:11:57+00:00" }, { "name": "symfony/console", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b" + "reference": "6827023c5872bea44b29d145de693b21981cf4cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b", - "reference": "3b2b415d4c48fbefca7dc742aa0a0171bfae4e0b", + "url": "https://api.github.com/repos/symfony/console/zipball/6827023c5872bea44b29d145de693b21981cf4cd", + "reference": "6827023c5872bea44b29d145de693b21981cf4cd", "shasum": "" }, "require": { @@ -2160,6 +2218,9 @@ "symfony/dependency-injection": "<3.4", "symfony/process": "<3.3" }, + "provide": { + "psr/log-implementation": "1.0" + }, "require-dev": { "psr/log": "~1.0", "symfony/config": "~3.3|~4.0", @@ -2169,7 +2230,7 @@ "symfony/process": "~3.3|~4.0" }, "suggest": { - "psr/log-implementation": "For using the console logger", + "psr/log": "For using the console logger", "symfony/event-dispatcher": "", "symfony/lock": "", "symfony/process": "" @@ -2204,20 +2265,20 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2018-10-02T16:33:53+00:00" + "time": "2020-02-15T13:27:16+00:00" }, { "name": "symfony/debug", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/debug.git", - "reference": "0a612e9dfbd2ccce03eb174365f31ecdca930ff6" + "reference": "a99278d50af8a9164219da38d61fb161a7f6e0a6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/0a612e9dfbd2ccce03eb174365f31ecdca930ff6", - "reference": "0a612e9dfbd2ccce03eb174365f31ecdca930ff6", + "url": "https://api.github.com/repos/symfony/debug/zipball/a99278d50af8a9164219da38d61fb161a7f6e0a6", + "reference": "a99278d50af8a9164219da38d61fb161a7f6e0a6", "shasum": "" }, "require": { @@ -2260,20 +2321,20 @@ ], "description": "Symfony Debug Component", "homepage": "https://symfony.com", - "time": "2018-10-02T16:33:53+00:00" + "time": "2020-02-03T15:10:40+00:00" }, { "name": "symfony/dependency-injection", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "aea20fef4e92396928b5db175788b90234c0270d" + "reference": "b06b36883abc61eb8fb576e89102a9ba6c9ee7ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/aea20fef4e92396928b5db175788b90234c0270d", - "reference": "aea20fef4e92396928b5db175788b90234c0270d", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/b06b36883abc61eb8fb576e89102a9ba6c9ee7ee", + "reference": "b06b36883abc61eb8fb576e89102a9ba6c9ee7ee", "shasum": "" }, "require": { @@ -2331,20 +2392,20 @@ ], "description": "Symfony DependencyInjection Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2020-02-19T17:19:43+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb" + "reference": "2f67a869aef3eecf42e7f8be4a8b86c92308686c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", - "reference": "b2e1f19280c09a42dc64c0b72b80fe44dd6e88fb", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/2f67a869aef3eecf42e7f8be4a8b86c92308686c", + "reference": "2f67a869aef3eecf42e7f8be4a8b86c92308686c", "shasum": "" }, "require": { @@ -2394,20 +2455,20 @@ ], "description": "Symfony EventDispatcher Component", "homepage": "https://symfony.com", - "time": "2018-07-26T09:06:28+00:00" + "time": "2020-02-04T08:04:52+00:00" }, { "name": "symfony/filesystem", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "d69930fc337d767607267d57c20a7403d0a822a4" + "reference": "0a0d3b4bda11aa3a0464531c40e681e184e75628" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/d69930fc337d767607267d57c20a7403d0a822a4", - "reference": "d69930fc337d767607267d57c20a7403d0a822a4", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/0a0d3b4bda11aa3a0464531c40e681e184e75628", + "reference": "0a0d3b4bda11aa3a0464531c40e681e184e75628", "shasum": "" }, "require": { @@ -2444,20 +2505,20 @@ ], "description": "Symfony Filesystem Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2020-01-17T08:50:08+00:00" }, { "name": "symfony/finder", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d" + "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/54ba444dddc5bd5708a34bd095ea67c6eb54644d", - "reference": "54ba444dddc5bd5708a34bd095ea67c6eb54644d", + "url": "https://api.github.com/repos/symfony/finder/zipball/5ec813ccafa8164ef21757e8c725d3a57da59200", + "reference": "5ec813ccafa8164ef21757e8c725d3a57da59200", "shasum": "" }, "require": { @@ -2493,20 +2554,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2018-10-03T08:46:40+00:00" + "time": "2020-02-14T07:34:21+00:00" }, { "name": "symfony/options-resolver", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d" + "reference": "730ef56164ed6c9356c159e9f5ff2b84d753b9ed" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", - "reference": "1cf7d8e704a9cc4164c92e430f2dfa3e6983661d", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/730ef56164ed6c9356c159e9f5ff2b84d753b9ed", + "reference": "730ef56164ed6c9356c159e9f5ff2b84d753b9ed", "shasum": "" }, "require": { @@ -2547,20 +2608,20 @@ "configuration", "options" ], - "time": "2018-09-17T17:29:18+00:00" + "time": "2020-01-01T11:03:25+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.9.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19" + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19", - "reference": "e3d826245268269cd66f8326bd8bc066687b4a19", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", + "reference": "fbdeaec0df06cf3d51c93de80c7eb76e271f5a38", "shasum": "" }, "require": { @@ -2572,7 +2633,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2588,13 +2649,13 @@ "MIT" ], "authors": [ - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - }, { "name": "Gert de Pagter", "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], "description": "Symfony polyfill for ctype functions", @@ -2605,20 +2666,20 @@ "polyfill", "portable" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.9.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8" + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/d0cd638f4634c16d8df4508e847f14e9e43168b8", - "reference": "d0cd638f4634c16d8df4508e847f14e9e43168b8", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/34094cfa9abe1f0f14f48f490772db7a775559f2", + "reference": "34094cfa9abe1f0f14f48f490772db7a775559f2", "shasum": "" }, "require": { @@ -2630,7 +2691,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2664,20 +2725,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-php70", - "version": "v1.9.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php70.git", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934" + "reference": "419c4940024c30ccc033650373a1fe13890d3255" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/1e24b0c4a56d55aaf368763a06c6d1c7d3194934", - "reference": "1e24b0c4a56d55aaf368763a06c6d1c7d3194934", + "url": "https://api.github.com/repos/symfony/polyfill-php70/zipball/419c4940024c30ccc033650373a1fe13890d3255", + "reference": "419c4940024c30ccc033650373a1fe13890d3255", "shasum": "" }, "require": { @@ -2687,7 +2748,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2723,20 +2784,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/polyfill-php72", - "version": "v1.9.0", + "version": "v1.14.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php72.git", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae" + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/95c50420b0baed23852452a7f0c7b527303ed5ae", - "reference": "95c50420b0baed23852452a7f0c7b527303ed5ae", + "url": "https://api.github.com/repos/symfony/polyfill-php72/zipball/46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", + "reference": "46ecacf4751dd0dc81e4f6bf01dbf9da1dc1dadf", "shasum": "" }, "require": { @@ -2745,7 +2806,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.9-dev" + "dev-master": "1.14-dev" } }, "autoload": { @@ -2778,20 +2839,20 @@ "portable", "shim" ], - "time": "2018-08-06T14:22:27+00:00" + "time": "2020-01-13T11:15:53+00:00" }, { "name": "symfony/process", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "1dc2977afa7d70f90f3fefbcd84152813558910e" + "reference": "b03b02dcea26ba4c65c16a73bab4f00c186b13da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/1dc2977afa7d70f90f3fefbcd84152813558910e", - "reference": "1dc2977afa7d70f90f3fefbcd84152813558910e", + "url": "https://api.github.com/repos/symfony/process/zipball/b03b02dcea26ba4c65c16a73bab4f00c186b13da", + "reference": "b03b02dcea26ba4c65c16a73bab4f00c186b13da", "shasum": "" }, "require": { @@ -2827,20 +2888,20 @@ ], "description": "Symfony Process Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2020-02-04T08:04:52+00:00" }, { "name": "symfony/stopwatch", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4" + "reference": "e2d954156d4817c9a5c79f519a71516693a4a9c8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/05e52a39de52ba690aebaed462b2bc8a9649f0a4", - "reference": "05e52a39de52ba690aebaed462b2bc8a9649f0a4", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/e2d954156d4817c9a5c79f519a71516693a4a9c8", + "reference": "e2d954156d4817c9a5c79f519a71516693a4a9c8", "shasum": "" }, "require": { @@ -2876,20 +2937,20 @@ ], "description": "Symfony Stopwatch Component", "homepage": "https://symfony.com", - "time": "2018-10-02T12:28:39+00:00" + "time": "2020-01-01T11:03:25+00:00" }, { "name": "symfony/yaml", - "version": "v3.4.17", + "version": "v3.4.38", "source": { "type": "git", "url": "https://github.com/symfony/yaml.git", - "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f" + "reference": "bc63e15160866e8730a1f738541b194c401f72bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/yaml/zipball/640b6c27fed4066d64b64d5903a86043f4a4de7f", - "reference": "640b6c27fed4066d64b64d5903a86043f4a4de7f", + "url": "https://api.github.com/repos/symfony/yaml/zipball/bc63e15160866e8730a1f738541b194c401f72bf", + "reference": "bc63e15160866e8730a1f738541b194c401f72bf", "shasum": "" }, "require": { @@ -2935,35 +2996,33 @@ ], "description": "Symfony Yaml Component", "homepage": "https://symfony.com", - "time": "2018-10-02T16:33:53+00:00" + "time": "2020-01-16T19:04:26+00:00" }, { "name": "webmozart/assert", - "version": "1.3.0", + "version": "1.7.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a" + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", - "reference": "0df1908962e7a3071564e857d86874dad1ef204a", + "url": "https://api.github.com/repos/webmozart/assert/zipball/aed98a490f9a8f78468232db345ab9cf606cf598", + "reference": "aed98a490f9a8f78468232db345ab9cf606cf598", "shasum": "" }, "require": { - "php": "^5.3.3 || ^7.0" + "php": "^5.3.3 || ^7.0", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "vimeo/psalm": "<3.6.0" }, "require-dev": { - "phpunit/phpunit": "^4.6", - "sebastian/version": "^1.0.1" + "phpunit/phpunit": "^4.8.36 || ^7.5.13" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.3-dev" - } - }, "autoload": { "psr-4": { "Webmozart\\Assert\\": "src/" @@ -2985,18 +3044,17 @@ "check", "validate" ], - "time": "2018-01-29T19:49:41+00:00" + "time": "2020-02-14T12:15:55+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "phpmd/phpmd": 0 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { - "php": ">=7.0.0" + "php": ">=7.0.0", + "ext-json": "*" }, "platform-dev": [] } diff --git a/phpcbf.phar b/phpcbf.phar deleted file mode 100644 index 6771440..0000000 Binary files a/phpcbf.phar and /dev/null differ diff --git a/phpcs.phar b/phpcs.phar deleted file mode 100644 index b8ed399..0000000 Binary files a/phpcs.phar and /dev/null differ diff --git a/src/Adapter/Guzzle.php b/src/Adapter/Guzzle.php index a7d8ad1..71e7dd7 100644 --- a/src/Adapter/Guzzle.php +++ b/src/Adapter/Guzzle.php @@ -1,14 +1,10 @@ request('delete', $uri, $data, $headers); } + /** + * @SuppressWarnings(PHPMD.StaticAccess) + */ public function request(string $method, string $uri, array $data = [], array $headers = []) { if (!in_array($method, ['get', 'post', 'put', 'patch', 'delete'])) { throw new \InvalidArgumentException('Request method must be get, post, put, patch, or delete'); } - $response = $this->client->$method($uri, [ - 'headers' => $headers, - ($method === 'get' ? 'query' : 'json') => $data, - ]); - - $this->checkError($response); + try { + $response = $this->client->$method($uri, [ + 'headers' => $headers, + ($method === 'get' ? 'query' : 'json') => $data, + ]); + } catch (RequestException $err) { + throw ResponseException::fromRequestException($err); + } return $response; } - - private function checkError(ResponseInterface $response) - { - $json = json_decode($response->getBody()); - - if (json_last_error() !== JSON_ERROR_NONE) { - throw new JSONException(); - } - - if (isset($json->errors) && count($json->errors) >= 1) { - throw new ResponseException($json->errors[0]->message, $json->errors[0]->code); - } - - if (isset($json->success) && !$json->success) { - throw new ResponseException('Request was unsuccessful.'); - } - } } diff --git a/src/Adapter/ResponseException.php b/src/Adapter/ResponseException.php index 22c01d4..d1c0ce2 100644 --- a/src/Adapter/ResponseException.php +++ b/src/Adapter/ResponseException.php @@ -8,6 +8,37 @@ namespace Cloudflare\API\Adapter; +use GuzzleHttp\Exception\RequestException; + class ResponseException extends \Exception { + /** + * Generates a ResponseException from a Guzzle RequestException. + * + * @param RequestException $err The client request exception (typicall 4xx or 5xx response). + * @return ResponseException + */ + public static function fromRequestException(RequestException $err): self + { + if (!$err->hasResponse()) { + return new ResponseException($err->getMessage(), 0, $err); + } + + $response = $err->getResponse(); + $contentType = $response->getHeaderLine('Content-Type'); + + // Attempt to derive detailed error from standard JSON response. + if (strpos($contentType, 'application/json') !== false) { + $json = json_decode($response->getBody()); + if (json_last_error() !== JSON_ERROR_NONE) { + return new ResponseException($err->getMessage(), 0, new JSONException(json_last_error_msg(), 0, $err)); + } + + if (isset($json->errors) && count($json->errors) >= 1) { + return new ResponseException($json->errors[0]->message, $json->errors[0]->code, $err); + } + } + + return new ResponseException($err->getMessage(), 0, $err); + } } diff --git a/src/Configurations/Certificate.php b/src/Configurations/Certificate.php new file mode 100644 index 0000000..8b7d082 --- /dev/null +++ b/src/Configurations/Certificate.php @@ -0,0 +1,58 @@ +config; + } + + /** + * Array of hostnames or wildcard names (e.g., *.example.com) bound to the certificate + * Example: $hostnames = ["example.com", "foo.example.com"] + * @param array $hostnames + */ + public function setHostnames(array $hostnames) + { + $this->config['hostnames'] = $hostnames; + } + + /** + * The number of days for which the certificate should be valid + * Default value: 5475 + * Valid values: 7, 30, 90, 365, 730, 1095, 5475 + * @param int $validity + */ + public function setRequestedValidity(int $validity) + { + $this->config['requested_validity'] = $validity; + } + + /** + * Signature type desired on certificate ("origin-rsa" (rsa), "origin-ecc" (ecdsa), or "keyless-certificate" (for Keyless SSL servers) + * Valid values: origin-rsa, origin-ecc, keyless-certificate + * @param string $type + */ + public function setRequestType(string $type) + { + $this->config['request_type'] = $type; + } + + /** + * The Certificate Signing Request (CSR). Must be newline-encoded. + * + * @param string $csr + */ + public function setCsr(string $csr) + { + $this->config['csr'] = $csr; + } +} diff --git a/src/Configurations/DNSAnalytics.php b/src/Configurations/DNSAnalytics.php new file mode 100644 index 0000000..b084534 --- /dev/null +++ b/src/Configurations/DNSAnalytics.php @@ -0,0 +1,69 @@ +configs; + } + + public function setDimensions(array $dimensions) + { + if (count($dimensions) !== 0) { + $this->configs['dimensions'] = implode(',', $dimensions); + } + } + + public function setMetrics(array $metrics) + { + if (count($metrics) !== 0) { + $this->configs['metrics'] = implode(',', $metrics); + } + } + + public function setSince(string $since) + { + if ($since) { + $this->configs['since'] = $since; + } + } + + public function setUntil(string $until) + { + if ($until) { + $this->configs['until'] = $until; + } + } + + public function setSorting(array $sorting) + { + if (count($sorting) !== 0) { + $this->configs['sort'] = implode(',', $sorting); + } + } + + public function setFilters(string $filters) + { + if ($filters) { + $this->configs['filters'] = $filters; + } + } + + public function setLimit(int $limit) + { + if ($limit) { + $this->configs['limit'] = $limit; + } + } + + public function setTimeDelta(string $timeDelta) + { + if ($timeDelta) { + $this->configs['time_delta'] = $timeDelta; + } + } +} diff --git a/src/Configurations/PageRulesActions.php b/src/Configurations/PageRulesActions.php index 70b4b3a..27790bd 100755 --- a/src/Configurations/PageRulesActions.php +++ b/src/Configurations/PageRulesActions.php @@ -117,7 +117,7 @@ class PageRulesActions implements Configurations public function setEdgeCacheTTL(int $value) { - if ($value > 2419200) { + if ($value > 2678400) { throw new ConfigurationsException('Edge Cache TTL too high.'); } diff --git a/src/Endpoints/Certificates.php b/src/Endpoints/Certificates.php new file mode 100644 index 0000000..a1888b5 --- /dev/null +++ b/src/Endpoints/Certificates.php @@ -0,0 +1,86 @@ +adapter = $adapter; + } + + /** + * List all existing Origin CA certificates for a given zone + * + * @param string $zoneID + * @return array + */ + public function listCertificates(string $zoneID): \stdClass + { + $certificates = $this->adapter->get('certificates', ['zone_id' => $zoneID]); + $this->body = json_decode($certificates->getBody()); + + return (object)['result' => $this->body->result]; + } + + /** + * Get an existing Origin CA certificate by its serial number + * + * @param string $certificateID + * @param string $zoneID + * @return mixed + */ + public function getCertificate(string $certificateID, string $zoneID) + { + $certificates = $this->adapter->get('certificates/'.$certificateID, ['zone_id' => $zoneID]); + $this->body = json_decode($certificates->getBody()); + + return (object)['result' => $this->body->result]; + } + + /** + * Revoke an existing Origin CA certificate by its serial number + * + * @param string $certificateID + * @param string $zoneID + * @return bool + */ + public function revokeCertificate(string $certificateID, string $zoneID): bool + { + $certificates = $this->adapter->delete('certificates/'.$certificateID, ['zone_id' => $zoneID]); + $this->body = json_decode($certificates->getBody()); + + if (isset($this->body->result->id)) { + return true; + } + + return false; + } + + /** + * Create an Origin CA certificate + * + * @param CertificateConfig $config + * @return bool + */ + public function createCertificate(CertificateConfig $config): bool + { + $certificate = $this->adapter->post('certificates', $config->getArray()); + + $this->body = json_decode($certificate->getBody()); + + if (isset($this->body->result->id)) { + return true; + } + + return false; + } +} diff --git a/src/Endpoints/CustomHostnames.php b/src/Endpoints/CustomHostnames.php index 4af5c9e..7a0d2de 100644 --- a/src/Endpoints/CustomHostnames.php +++ b/src/Endpoints/CustomHostnames.php @@ -14,7 +14,7 @@ use Cloudflare\API\Traits\BodyAccessorTrait; class CustomHostnames implements API { use BodyAccessorTrait; - + private $adapter; public function __construct(Adapter $adapter) @@ -30,18 +30,50 @@ class CustomHostnames implements API * @param string $hostname * @param string $sslMethod * @param string $sslType + * @param array $sslSettings + * @param string $customOriginServer + * @param bool $wildcard + * @param string $bundleMethod + * @param array $customSsl * @return \stdClass */ - public function addHostname(string $zoneID, string $hostname, string $sslMethod = 'http', string $sslType = 'dv'): \stdClass - { + public function addHostname( + string $zoneID, + string $hostname, + string $sslMethod = 'http', + string $sslType = 'dv', + array $sslSettings = [], + string $customOriginServer = '', + bool $wildcard = false, + string $bundleMethod = '', + array $customSsl = [] + ): \stdClass { $options = [ 'hostname' => $hostname, 'ssl' => [ 'method' => $sslMethod, - 'type' => $sslType - ] + 'type' => $sslType, + 'settings' => $sslSettings, + 'wildcard' => $wildcard, + ], ]; + if (!empty($customOriginServer)) { + $options['custom_origin_server'] = $customOriginServer; + } + + if (!empty($bundleMethod)) { + $options['ssl']['bundle_method'] = $bundleMethod; + } + + if (!empty($customSsl['key'])) { + $options['ssl']['custom_key'] = $customSsl['key']; + } + + if (!empty($customSsl['certificate'])) { + $options['ssl']['custom_certificate'] = $customSsl['certificate']; + } + $zone = $this->adapter->post('zones/'.$zoneID.'/custom_hostnames', $options); $this->body = json_decode($zone->getBody()); return $this->body->result; @@ -111,16 +143,33 @@ class CustomHostnames implements API /** * @SuppressWarnings(PHPMD.BooleanArgumentFlag) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) * - * @param string $zoneID - * @param string $hostnameID - * @param string $sslMethod - * @param string $sslType + * @param string $zoneID + * @param string $hostnameID + * @param string $sslMethod + * @param string $sslType + * @param array $sslSettings + * @param string $customOriginServer + * @param bool|null $wildcard + * @param string $bundleMethod + * @param array $customSsl * @return \stdClass */ - public function updateHostname(string $zoneID, string $hostnameID, string $sslMethod = '', string $sslType = ''): \stdClass - { + public function updateHostname( + string $zoneID, + string $hostnameID, + string $sslMethod = '', + string $sslType = '', + array $sslSettings = [], + string $customOriginServer = '', + bool $wildcard = null, + string $bundleMethod = '', + array $customSsl = [] + ): \stdClass { $query = []; + $options = []; if (!empty($sslMethod)) { $query['method'] = $sslMethod; @@ -130,9 +179,35 @@ class CustomHostnames implements API $query['type'] = $sslType; } - $options = [ - 'ssl' => $query - ]; + if (!empty($sslSettings)) { + $query['settings'] = $sslSettings; + } + + if (!is_null($wildcard)) { + $query['wildcard'] = $wildcard; + } + + if (!empty($bundleMethod)) { + $query['bundle_method'] = $bundleMethod; + } + + if (!empty($customSsl['key'])) { + $query['custom_key'] = $customSsl['key']; + } + + if (!empty($customSsl['certificate'])) { + $query['custom_certificate'] = $customSsl['certificate']; + } + + if (!empty($query)) { + $options = [ + 'ssl' => $query + ]; + } + + if (!empty($customOriginServer)) { + $options['custom_origin_server'] = $customOriginServer; + } $zone = $this->adapter->patch('zones/'.$zoneID.'/custom_hostnames/'.$hostnameID, $options); $this->body = json_decode($zone->getBody()); @@ -150,4 +225,16 @@ class CustomHostnames implements API $this->body = json_decode($zone->getBody()); return $this->body; } + + /** + * @param string $zoneID + * @return \stdClass + */ + public function getFallbackOrigin(string $zoneID): \stdClass + { + $zone = $this->adapter->get('zones/'.$zoneID.'/custom_hostnames/fallback_origin'); + $this->body = json_decode($zone->getBody()); + + return $this->body->result; + } } diff --git a/src/Endpoints/DNS.php b/src/Endpoints/DNS.php index 4a23fde..48145a7 100644 --- a/src/Endpoints/DNS.php +++ b/src/Endpoints/DNS.php @@ -56,7 +56,7 @@ class DNS implements API $options['ttl'] = $ttl; } - if (!empty($priority)) { + if (is_numeric($priority)) { $options['priority'] = (int)$priority; } diff --git a/src/Endpoints/DNSAnalytics.php b/src/Endpoints/DNSAnalytics.php new file mode 100644 index 0000000..bad07ee --- /dev/null +++ b/src/Endpoints/DNSAnalytics.php @@ -0,0 +1,144 @@ +adapter = $adapter; + } + + /** + * Retrieves a list of summarised aggregate metrics over a given time period. + * + * @param string $zoneID ID of zone to get report for + * @param string $dimensions Comma separated names of dimensions + * @param string $metrics Comma separated names of dimension to get metrics for + * @param string $sort Comma separated names of dimension to sort by prefixed by order - (descending) or + (ascending) + * @param string $filters Segmentation filter in 'attribute operator value' format + * @param string $since Start date and time of requesting data period in the ISO8601 format + * @param string $until End date and time of requesting data period in the ISO8601 format + * @param string $limit Limit number of returned metrics + * @return array + */ + public function getReportTable( + string $zoneID, + array $dimensions = [], + array $metrics = [], + array $sort = [], + string $filters = '', + string $since = '', + string $until = '', + int $limit = 100 + ): \stdClass { + if (count($dimensions) === 0) { + throw new EndpointException( + 'At least one dimension is required for getting a report.' + ); + } + + if (count($metrics) === 0) { + throw new EndpointException( + 'At least one metric is required for getting a report.' + ); + } + + if (!$since) { + throw new EndpointException( + 'Start date is required for getting a report.' + ); + } + + if (!$until) { + throw new EndpointException( + 'End date is required for getting a report.' + ); + } + + $options = [ + 'dimensions' => implode(',', $dimensions), + 'metrics' => implode(',', $metrics), + 'since' => $since, + 'until' => $until + ]; + + if (count($sort) !== 0) { + $options['sort'] = implode(',', $sort); + } + + if ($filters) { + $options['filters'] = $filters; + } + + if ($limit) { + $options['limit'] = $limit; + } + + $endpoint = 'zones/' . $zoneID . '/dns_analytics/report'; + + $report = $this->adapter->get($endpoint, $options); + + $this->body = json_decode($report->getBody()); + + return $this->body->result; + } + + /** + * Retrieves a list of aggregate metrics grouped by time interval. + * + * @param string $zoneID ID of zone to get report for + * @param string $dimensions Comma separated names of dimensions + * @param string $metrics Comma separated names of dimension to get metrics for + * @param string $sort Comma separated names of dimension to sort by prefixed by order - (descending) or + (ascending) + * @param string $filters Segmentation filter in 'attribute operator value' format + * @param string $since Start date and time of requesting data period in the ISO8601 format + * @param string $until End date and time of requesting data period in the ISO8601 format + * @param string $limit Limit number of returned metrics + * @param string $timeDelta Unit of time to group data by + * @return array + */ + public function getReportByTime( + string $zoneID, + array $dimensions = [], + array $metrics = [], + array $sort = [], + string $filters = '', + string $since = '', + string $until = '', + int $limit = 100, + string $timeDelta = '' + ): \stdClass { + $options = new Configs(); + $options->setDimensions($dimensions); + $options->setMetrics($metrics); + $options->setSince($since); + $options->setUntil($until); + $options->setSorting($sort); + $options->setFilters($filters); + $options->setLimit($limit); + $options->setTimeDelta($timeDelta); + + $endpoint = 'zones/' . $zoneID . '/dns_analytics/report/bytime'; + + $report = $this->adapter->get($endpoint, $options->getArray()); + + $this->body = json_decode($report->getBody()); + + return $this->body->result; + } +} diff --git a/src/Endpoints/ZoneSettings.php b/src/Endpoints/ZoneSettings.php index b7baf48..5c237eb 100644 --- a/src/Endpoints/ZoneSettings.php +++ b/src/Endpoints/ZoneSettings.php @@ -106,6 +106,37 @@ class ZoneSettings implements API return false; } + public function getBrowserCacheTtlSetting($zoneID) + { + $return = $this->adapter->get( + 'zones/' . $zoneID . '/settings/browser_cache_ttl' + ); + $body = json_decode($return->getBody()); + + if ($body->success) { + return $body->result->value; + } + + return false; + } + + public function updateBrowserCacheTtlSetting($zoneID, $value) + { + $return = $this->adapter->patch( + 'zones/' . $zoneID . '/settings/browser_cache_ttl', + [ + 'value' => $value + ] + ); + $body = json_decode($return->getBody()); + + if ($body->success) { + return true; + } + + return false; + } + public function updateMinifySetting($zoneID, $html, $css, $javascript) { $return = $this->adapter->patch( diff --git a/src/Endpoints/Zones.php b/src/Endpoints/Zones.php index 19390b1..2335b1c 100644 --- a/src/Endpoints/Zones.php +++ b/src/Endpoints/Zones.php @@ -217,7 +217,7 @@ class Zones implements API */ public function cachePurgeEverything(string $zoneID): bool { - $user = $this->adapter->delete('zones/' . $zoneID . '/purge_cache', ['purge_everything' => true]); + $user = $this->adapter->post('zones/' . $zoneID . '/purge_cache', ['purge_everything' => true]); $this->body = json_decode($user->getBody()); @@ -247,7 +247,7 @@ class Zones implements API $options['hosts'] = $hosts; } - $user = $this->adapter->delete('zones/' . $zoneID . '/purge_cache', $options); + $user = $this->adapter->post('zones/' . $zoneID . '/purge_cache', $options); $this->body = json_decode($user->getBody()); diff --git a/tests/Adapter/GuzzleTest.php b/tests/Adapter/GuzzleTest.php index 505ed72..25208df 100644 --- a/tests/Adapter/GuzzleTest.php +++ b/tests/Adapter/GuzzleTest.php @@ -1,12 +1,6 @@ assertEquals('Testing a DELETE request.', $body->json->{'X-Delete-Test'}); } - public function testErrors() - { - $class = new ReflectionClass(\Cloudflare\API\Adapter\Guzzle::class); - $method = $class->getMethod('checkError'); - $method->setAccessible(true); - - $body = - '{ - "result": null, - "success": false, - "errors": [{"code":1003,"message":"Invalid or missing zone id."}], - "messages": [] - }' - ; - $response = new Response(200, [], $body); - - $this->expectException(\Cloudflare\API\Adapter\ResponseException::class); - $method->invokeArgs($this->client, [$response]); - - $body = - '{ - "result": null, - "success": false, - "errors": [], - "messages": [] - }' - ; - $response = new Response(200, [], $body); - - $this->expectException(\Cloudflare\API\Adapter\ResponseException::class); - $method->invokeArgs($this->client, [$response]); - - $body = 'this isnt json.'; - $response = new Response(200, [], $body); - - $this->expectException(\Cloudflare\API\Adapter\JSONException::class); - $method->invokeArgs($this->client, [$response]); - } - public function testNotFound() { - $this->expectException(\GuzzleHttp\Exception\RequestException::class); + $this->expectException(ResponseException::class); $this->client->get('https://httpbin.org/status/404'); } + + public function testServerError() + { + $this->expectException(ResponseException::class); + $this->client->get('https://httpbin.org/status/500'); + } } diff --git a/tests/Adapter/ResponseExceptionTest.php b/tests/Adapter/ResponseExceptionTest.php new file mode 100644 index 0000000..8283e0f --- /dev/null +++ b/tests/Adapter/ResponseExceptionTest.php @@ -0,0 +1,81 @@ +assertInstanceOf(ResponseException::class, $respErr); + $this->assertEquals($reqErr->getMessage(), $respErr->getMessage()); + $this->assertEquals(0, $respErr->getCode()); + $this->assertEquals($reqErr, $respErr->getPrevious()); + } + + public function testFromRequestExceptionEmptyContentType() + { + $resp = new Response(404); + $reqErr = new RequestException('foo', new Request('GET', '/test'), $resp); + $respErr = ResponseException::fromRequestException($reqErr); + + $this->assertInstanceOf(ResponseException::class, $respErr); + $this->assertEquals($reqErr->getMessage(), $respErr->getMessage()); + $this->assertEquals(0, $respErr->getCode()); + $this->assertEquals($reqErr, $respErr->getPrevious()); + } + + + public function testFromRequestExceptionUnknownContentType() + { + $resp = new Response(404, ['Content-Type' => ['application/octet-stream']]); + $reqErr = new RequestException('foo', new Request('GET', '/test'), $resp); + $respErr = ResponseException::fromRequestException($reqErr); + + $this->assertInstanceOf(ResponseException::class, $respErr); + $this->assertEquals($reqErr->getMessage(), $respErr->getMessage()); + $this->assertEquals(0, $respErr->getCode()); + $this->assertEquals($reqErr, $respErr->getPrevious()); + } + + public function testFromRequestExceptionJSONDecodeError() + { + $resp = new Response(404, ['Content-Type' => ['application/json; charset=utf-8']], '[what]'); + $reqErr = new RequestException('foo', new Request('GET', '/test'), $resp); + $respErr = ResponseException::fromRequestException($reqErr); + + $this->assertInstanceOf(ResponseException::class, $respErr); + $this->assertEquals($reqErr->getMessage(), $respErr->getMessage()); + $this->assertEquals(0, $respErr->getCode()); + $this->assertInstanceOf(JSONException::class, $respErr->getPrevious()); + $this->assertEquals($reqErr, $respErr->getPrevious()->getPrevious()); + } + + public function testFromRequestExceptionJSONWithErrors() + { + $body = '{ + "result": null, + "success": false, + "errors": [{"code":1003, "message":"This is an error"}], + "messages": [] + }'; + + $resp = new Response(404, ['Content-Type' => ['application/json; charset=utf-8']], $body); + $reqErr = new RequestException('foo', new Request('GET', '/test'), $resp); + $respErr = ResponseException::fromRequestException($reqErr); + + $this->assertInstanceOf(ResponseException::class, $respErr); + $this->assertEquals('This is an error', $respErr->getMessage()); + $this->assertEquals(1003, $respErr->getCode()); + $this->assertEquals($reqErr, $respErr->getPrevious()); + } +} diff --git a/tests/Configurations/CertificateTest.php b/tests/Configurations/CertificateTest.php new file mode 100644 index 0000000..059f38e --- /dev/null +++ b/tests/Configurations/CertificateTest.php @@ -0,0 +1,22 @@ +setHostnames(['foo.com', '*.bar.com']); + $certificate->setRequestType(Certificate::ORIGIN_ECC); + $certificate->setRequestedValidity(365); + $certificate->setCsr('some-csr-encoded-text'); + + $array = $certificate->getArray(); + $this->assertEquals(['foo.com', '*.bar.com'], $array['hostnames']); + $this->assertEquals('origin-ecc', $array['request_type']); + $this->assertEquals(365, $array['requested_validity']); + $this->assertEquals('some-csr-encoded-text', $array['csr']); + } +} diff --git a/tests/Endpoints/CertificatesTest.php b/tests/Endpoints/CertificatesTest.php new file mode 100644 index 0000000..aa6c80d --- /dev/null +++ b/tests/Endpoints/CertificatesTest.php @@ -0,0 +1,120 @@ +getPsr7JsonResponseForFixture('Endpoints/listCertificates.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once()) + ->method('get') + ->with( + $this->equalTo('certificates'), + $this->equalTo( + [ + 'zone_id' => '023e105f4ecef8ad9ca31a8372d0c353', + ] + ) + ); + + $certEndpoint = new Certificates($mock); + $result = $certEndpoint->listCertificates('023e105f4ecef8ad9ca31a8372d0c353'); + + $this->assertObjectHasAttribute('result', $result); + + $cert = $result->result[0]; + $this->assertEquals('328578533902268680212849205732770752308931942346', $cert->id); + $this->assertEquals('origin-rsa', $cert->request_type); + $this->assertEquals(5475, $cert->requested_validity); + $this->assertEquals(['example.com', '*.example.com'], $cert->hostnames); + $this->assertEquals('some-cert-data', $cert->certificate); + $this->assertEquals('some-csr-data', $cert->csr); + } + + public function testGetCertificate() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/getCertificate.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once()) + ->method('get') + ->with( + $this->equalTo('certificates/6666699999996666699999999966666666'), + $this->equalTo(['zone_id' => '023e105f4ecef8ad9ca31a8372d0c353']), + $this->equalTo([]) + ); + + $certEndpoint = new Certificates($mock); + $response = $certEndpoint->getCertificate( + '6666699999996666699999999966666666', + '023e105f4ecef8ad9ca31a8372d0c353' + ); + + $this->assertObjectHasAttribute('result', $response); + $cert = $response->result; + $this->assertEquals('6666699999996666699999999966666666', $cert->id); + $this->assertEquals('origin-ecc', $cert->request_type); + $this->assertEquals(5475, $cert->requested_validity); + $this->assertEquals(['foo.example.com', 'bar.example.com'], $cert->hostnames); + $this->assertEquals('some-cert-data-foobar', $cert->certificate); + $this->assertEquals('some-csr-data-foobar', $cert->csr); + } + + public function testRevokeCertificate() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/getCertificate.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('delete')->willReturn($response); + + $mock->expects($this->once()) + ->method('delete') + ->with( + $this->equalTo('certificates/11112222233333444455555'), + $this->equalTo(['zone_id' => '023e105f4ecef8ad9ca31a8372d0c353']), + $this->equalTo([]) + ); + + $certEndpoint = new Certificates($mock); + $result = $certEndpoint->revokeCertificate( + '11112222233333444455555', + '023e105f4ecef8ad9ca31a8372d0c353' + ); + + $this->assertTrue($result); + } + + public function testCreateCertificate() + { + $certificate = new \Cloudflare\API\Configurations\Certificate(); + $certificate->setHostnames(['foo.example.com', 'bar.exapmle.com']); + $certificate->setRequestType(\Cloudflare\API\Configurations\Certificate::ORIGIN_ECC); + $certificate->setRequestedValidity(365); + $certificate->setCsr('some-csr-data-barbar'); + + $response = $this->getPsr7JsonResponseForFixture('Endpoints/getCertificate.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('post')->willReturn($response); + + $mock->expects($this->once()) + ->method('post') + ->with( + $this->equalTo('certificates'), + $this->equalTo($certificate->getArray()), + $this->equalTo([]) + ); + + $certEndpoint = new Certificates($mock); + $result = $certEndpoint->createCertificate($certificate); + + $this->assertTrue($result); + } +} diff --git a/tests/Endpoints/CustomHostnamesTest.php b/tests/Endpoints/CustomHostnamesTest.php index faa247f..f8cf990 100644 --- a/tests/Endpoints/CustomHostnamesTest.php +++ b/tests/Endpoints/CustomHostnamesTest.php @@ -14,6 +14,8 @@ class CustomHostnamesTest extends TestCase { $response = $this->getPsr7JsonResponseForFixture('Endpoints/createCustomHostname.json'); + $customSsl = $this->getCustomSsl(); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); $mock->method('post')->willReturn($response); @@ -23,15 +25,41 @@ class CustomHostnamesTest extends TestCase $this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/custom_hostnames'), $this->equalTo([ 'hostname' => 'app.example.com', + 'custom_origin_server' => 'origin.example.com', 'ssl' => [ 'method' => 'http', - 'type' => 'dv' - ] + 'type' => 'dv', + 'settings' => [ + 'http2' => 'on', + 'http3' => 'on', + 'min_tls_version' => '1.2', + ], + 'bundle_method' => 'optimal', + 'custom_key' => $customSsl['key'], + 'custom_certificate' => $customSsl['certificate'], + 'wildcard' => true, + ], ]) ); $hostname = new CustomHostnames($mock); - $hostname->addHostname('023e105f4ecef8ad9ca31a8372d0c353', 'app.example.com', 'http', 'dv'); + $sslSettings = [ + 'http2' => 'on', + 'http3' => 'on', + 'min_tls_version' => '1.2' + ]; + + $hostname->addHostname( + '023e105f4ecef8ad9ca31a8372d0c353', + 'app.example.com', + 'http', + 'dv', + $sslSettings, + 'origin.example.com', + true, + 'optimal', + $customSsl + ); $this->assertEquals('0d89c70d-ad9f-4843-b99f-6cc0252067e9', $hostname->getBody()->result->id); } @@ -93,6 +121,8 @@ class CustomHostnamesTest extends TestCase { $response = $this->getPsr7JsonResponseForFixture('Endpoints/updateHostname.json'); + $customSsl = $this->getCustomSsl(); + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); $mock->method('patch')->willReturn($response); @@ -101,15 +131,45 @@ class CustomHostnamesTest extends TestCase ->with( $this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/custom_hostnames/0d89c70d-ad9f-4843-b99f-6cc0252067e9'), $this->equalTo([ + 'custom_origin_server' => 'origin.example.com', 'ssl' => [ 'method' => 'http', - 'type' => 'dv' + 'type' => 'dv', + 'settings' => [ + 'http2' => 'on', + 'http3' => 'on', + 'min_tls_version' => '1.2' + ], + 'bundle_method' => 'optimal', + 'custom_key' => $customSsl['key'], + 'custom_certificate' => $customSsl['certificate'], + 'wildcard' => true, + ] ]) ); $zones = new \Cloudflare\API\Endpoints\CustomHostnames($mock); - $result = $zones->updateHostname('023e105f4ecef8ad9ca31a8372d0c353', '0d89c70d-ad9f-4843-b99f-6cc0252067e9', 'http', 'dv'); + $sslSettings = [ + 'http2' => 'on', + 'http3' => 'on', + 'min_tls_version' => '1.2' + ]; + + $result = $zones->updateHostname( + '023e105f4ecef8ad9ca31a8372d0c353', + '0d89c70d-ad9f-4843-b99f-6cc0252067e9', + 'http', + 'dv', + $sslSettings, + 'origin.example.com', + true, + 'optimal', + [ + 'key' => $customSsl['key'], + 'certificate' => $customSsl['certificate'], + ] + ); $this->assertObjectHasAttribute('id', $result); $this->assertObjectHasAttribute('hostname', $result); @@ -135,4 +195,88 @@ class CustomHostnamesTest extends TestCase $this->assertEquals('0d89c70d-ad9f-4843-b99f-6cc0252067e9', $result->id); $this->assertEquals('0d89c70d-ad9f-4843-b99f-6cc0252067e9', $zones->getBody()->id); } + + public function testGetHostnameFallbackOrigin() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/getCustomHostnameFallbackOrigin.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once()) + ->method('get') + ->with( + $this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/custom_hostnames/fallback_origin') + ); + + $zones = new \Cloudflare\API\Endpoints\CustomHostnames($mock); + $result = $zones->getFallbackOrigin('023e105f4ecef8ad9ca31a8372d0c353'); + + $this->assertObjectHasAttribute('origin', $result); + $this->assertObjectHasAttribute('status', $result); + } + + private function getCustomSsl(): array + { + $customKey = << $customKey, + 'certificate' => $customCertificate, + ]; + } } diff --git a/tests/Endpoints/DNSAnalyticsTest.php b/tests/Endpoints/DNSAnalyticsTest.php new file mode 100644 index 0000000..6d0a64e --- /dev/null +++ b/tests/Endpoints/DNSAnalyticsTest.php @@ -0,0 +1,91 @@ +getPsr7JsonResponseForFixture( + 'Endpoints/getDNSAnalyticsReportTable.json' + ); + + $mock = $this->getMockBuilder( + \Cloudflare\API\Adapter\Adapter::class + )->getMock(); + $mock->method('get')->willReturn($response); + + $mock + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo( + 'zones/023e105f4ecef8ad9ca31a8372d0c353/dns_analytics/report' + ) + ); + + $analytics = new \Cloudflare\API\Endpoints\DNSAnalytics($mock); + $since = '2020-02-01T00:00:00Z'; + $until = '2020-02-28T23:59:59Z'; + $filters = 'responseCode==NOERROR AND queryType==A'; + + $result = $analytics->getReportTable( + '023e105f4ecef8ad9ca31a8372d0c353', + ['queryName', 'queryType', 'responseCode'], + ['queryCount'], + ['-queryCount'], + $filters, + $since, + $until + ); + + $this->assertEquals(1, $result->rows); + $this->assertEquals($since, $result->query->since); + $this->assertEquals($until, $result->query->until); + } + + public function testGetDNSAnalyticsReportByTime() + { + $response = $this->getPsr7JsonResponseForFixture( + 'Endpoints/getDNSAnalyticsReportByTime.json' + ); + + $mock = $this->getMockBuilder( + \Cloudflare\API\Adapter\Adapter::class + )->getMock(); + $mock->method('get')->willReturn($response); + + $mock + ->expects($this->once()) + ->method('get') + ->with( + $this->equalTo( + 'zones/023e105f4ecef8ad9ca31a8372d0c353/dns_analytics/report/bytime' + ) + ); + + $analytics = new \Cloudflare\API\Endpoints\DNSAnalytics($mock); + $since = '2020-02-01T00:00:00Z'; + $until = '2020-02-28T23:59:59Z'; + $filters = 'responseCode==NOERROR AND queryType==A'; + + $result = $analytics->getReportByTime( + '023e105f4ecef8ad9ca31a8372d0c353', + ['queryName', 'queryType', 'responseCode'], + ['queryCount'], + ['-queryCount'], + $filters, + $since, + $until, + 2 + ); + + $this->assertEquals(2, $result->rows); + $this->assertEquals($since, $result->query->since); + $this->assertEquals($until, $result->query->until); + } +} diff --git a/tests/Endpoints/DNSTest.php b/tests/Endpoints/DNSTest.php index 27f6030..2eec553 100644 --- a/tests/Endpoints/DNSTest.php +++ b/tests/Endpoints/DNSTest.php @@ -32,6 +32,57 @@ class DNSTest extends TestCase $dns->addRecord('023e105f4ecef8ad9ca31a8372d0c353', 'A', 'example.com', '127.0.0.1', '120', false); } + public function testAddMXRecordPriority10() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/addRecord.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('post')->willReturn($response); + + $mock->expects($this->once()) + ->method('post') + ->with( + $this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/dns_records'), + $this->equalTo([ + 'type' => 'MX', + 'name' => 'example.com', + 'content' => '127.0.0.1', + 'ttl' => 120, + 'proxied' => false, + 'priority' => 10, + ]) + ); + + $dns = new \Cloudflare\API\Endpoints\DNS($mock); + $dns->addRecord('023e105f4ecef8ad9ca31a8372d0c353', 'MX', 'example.com', '127.0.0.1', '120', false, 10); + } + + public function testAddMXRecordPriority0() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/addRecord.json'); + + $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); + $mock->method('post')->willReturn($response); + + $mock->expects($this->once()) + ->method('post') + ->with( + $this->equalTo('zones/023e105f4ecef8ad9ca31a8372d0c353/dns_records'), + $this->equalTo([ + 'type' => 'MX', + 'name' => 'example.com', + 'content' => '127.0.0.1', + 'ttl' => 120, + 'proxied' => false, + 'priority' => 0, + ]) + ); + + $dns = new \Cloudflare\API\Endpoints\DNS($mock); + $dns->addRecord('023e105f4ecef8ad9ca31a8372d0c353', 'MX', 'example.com', '127.0.0.1', '120', false, 0); + } + + public function testListRecords() { $response = $this->getPsr7JsonResponseForFixture('Endpoints/listRecords.json'); diff --git a/tests/Endpoints/ZoneSettingsTest.php b/tests/Endpoints/ZoneSettingsTest.php index 05bc2a7..e4d2d8d 100644 --- a/tests/Endpoints/ZoneSettingsTest.php +++ b/tests/Endpoints/ZoneSettingsTest.php @@ -36,4 +36,34 @@ class ZoneSettingsTest extends TestCase $this->assertSame('on', $result); } + + public function testGetBrowserCacheTtlSetting() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/getBrowserCacheTtlSetting.json'); + + $mock = $this->getMockBuilder(Adapter::class)->getMock(); + $mock->method('get')->willReturn($response); + + $mock->expects($this->once())->method('get'); + + $zones = new ZoneSettings($mock); + $result = $zones->getBrowserCacheTtlSetting('023e105f4ecef8ad9ca31a8372d0c353'); + + $this->assertSame(14400, $result); + } + + public function testUpdateBrowserCacheTtlSetting() + { + $response = $this->getPsr7JsonResponseForFixture('Endpoints/updateBrowserCacheTtlSetting.json'); + + $mock = $this->getMockBuilder(Adapter::class)->getMock(); + $mock->method('patch')->willReturn($response); + + $mock->expects($this->once())->method('patch'); + + $zones = new ZoneSettings($mock); + $result = $zones->updateBrowserCacheTtlSetting('023e105f4ecef8ad9ca31a8372d0c353', 16070400); + + $this->assertTrue($result); + } } diff --git a/tests/Endpoints/ZonesTest.php b/tests/Endpoints/ZonesTest.php index f6b75fc..02addeb 100644 --- a/tests/Endpoints/ZonesTest.php +++ b/tests/Endpoints/ZonesTest.php @@ -194,10 +194,10 @@ class ZonesTest extends TestCase $response = $this->getPsr7JsonResponseForFixture('Endpoints/cachePurgeEverything.json'); $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); - $mock->method('delete')->willReturn($response); + $mock->method('post')->willReturn($response); $mock->expects($this->once()) - ->method('delete') + ->method('post') ->with( $this->equalTo('zones/c2547eb745079dac9320b638f5e225cf483cc5cfdda41/purge_cache'), $this->equalTo(['purge_everything' => true]) @@ -215,10 +215,10 @@ class ZonesTest extends TestCase $response = $this->getPsr7JsonResponseForFixture('Endpoints/cachePurgeHost.json'); $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); - $mock->method('delete')->willReturn($response); + $mock->method('post')->willReturn($response); $mock->expects($this->once()) - ->method('delete') + ->method('post') ->with( $this->equalTo('zones/c2547eb745079dac9320b638f5e225cf483cc5cfdda41/purge_cache'), $this->equalTo( @@ -242,10 +242,10 @@ class ZonesTest extends TestCase $response = $this->getPsr7JsonResponseForFixture('Endpoints/cachePurge.json'); $mock = $this->getMockBuilder(\Cloudflare\API\Adapter\Adapter::class)->getMock(); - $mock->method('delete')->willReturn($response); + $mock->method('post')->willReturn($response); $mock->expects($this->once()) - ->method('delete') + ->method('post') ->with( $this->equalTo('zones/c2547eb745079dac9320b638f5e225cf483cc5cfdda41/purge_cache'), $this->equalTo(['files' => [ diff --git a/tests/Fixtures/Endpoints/createCertificate.json b/tests/Fixtures/Endpoints/createCertificate.json new file mode 100644 index 0000000..38ef97e --- /dev/null +++ b/tests/Fixtures/Endpoints/createCertificate.json @@ -0,0 +1,17 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "12341234123412341234", + "certificate": "some-cert-data-foofoo", + "hostnames": [ + "foo.example.com", + "bar.example.com" + ], + "expires_on": "2014-01-01T05:20:00.12345Z", + "request_type": "origin-ecc", + "requested_validity": 365, + "csr": "some-csr-data-barbar" + } +} \ No newline at end of file diff --git a/tests/Fixtures/Endpoints/getBrowserCacheTtlSetting.json b/tests/Fixtures/Endpoints/getBrowserCacheTtlSetting.json new file mode 100644 index 0000000..be08512 --- /dev/null +++ b/tests/Fixtures/Endpoints/getBrowserCacheTtlSetting.json @@ -0,0 +1,11 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "browser_cache_ttl", + "value": 14400, + "editable": true, + "modified_on": "2014-01-01T05:20:00.12345Z" + } +} \ No newline at end of file diff --git a/tests/Fixtures/Endpoints/getCertificate.json b/tests/Fixtures/Endpoints/getCertificate.json new file mode 100644 index 0000000..1c76668 --- /dev/null +++ b/tests/Fixtures/Endpoints/getCertificate.json @@ -0,0 +1,17 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "6666699999996666699999999966666666", + "certificate": "some-cert-data-foobar", + "hostnames": [ + "foo.example.com", + "bar.example.com" + ], + "expires_on": "2014-01-01T05:20:00.12345Z", + "request_type": "origin-ecc", + "requested_validity": 5475, + "csr": "some-csr-data-foobar" + } +} \ No newline at end of file diff --git a/tests/Fixtures/Endpoints/getCustomHostnameFallbackOrigin.json b/tests/Fixtures/Endpoints/getCustomHostnameFallbackOrigin.json new file mode 100644 index 0000000..66a9fac --- /dev/null +++ b/tests/Fixtures/Endpoints/getCustomHostnameFallbackOrigin.json @@ -0,0 +1,18 @@ +{ + "success": true, + "errors": [ + {} + ], + "messages": [ + {} + ], + "result": { + "origin": "fallback.example.com", + "status": "pending_deployment", + "errors": [ + "DNS records are not setup correctly. Origin should be a proxied A/AAAA/CNAME dns record" + ], + "created_at": "2019-10-28T18:11:23.37411Z", + "updated_at": "2020-03-16T18:11:23.531995Z" + } +} diff --git a/tests/Fixtures/Endpoints/getDNSAnalyticsReportByTime.json b/tests/Fixtures/Endpoints/getDNSAnalyticsReportByTime.json new file mode 100644 index 0000000..f1bc656 --- /dev/null +++ b/tests/Fixtures/Endpoints/getDNSAnalyticsReportByTime.json @@ -0,0 +1,31 @@ +{ + "result": { + "rows": 2, + "data": [ + { + "metrics": [[911, 993]] + } + ], + "data_lag": 0, + "min": {}, + "max": {}, + "totals": { + "queryCount": 455312 + }, + "time_intervals": [ + ["2020-02-10T11:19:00Z", "2020-02-10T11:19:59Z"], + ["2020-02-10T11:20:00Z", "2020-02-10T11:20:59Z"] + ], + "query": { + "dimensions": [], + "metrics": ["queryCount"], + "since": "2020-02-01T00:00:00Z", + "until": "2020-02-28T23:59:59Z", + "time_delta": "minute", + "limit": 2 + } + }, + "success": true, + "errors": [], + "messages": [] +} diff --git a/tests/Fixtures/Endpoints/getDNSAnalyticsReportTable.json b/tests/Fixtures/Endpoints/getDNSAnalyticsReportTable.json new file mode 100644 index 0000000..c6a737d --- /dev/null +++ b/tests/Fixtures/Endpoints/getDNSAnalyticsReportTable.json @@ -0,0 +1,34 @@ +{ + "result": { + "rows": 1, + "data": [ + { + "dimensions": ["thrdld.sld.tld", "TXT", "NOERROR"], + "metrics": [2] + } + ], + "data_lag": 0, + "min": {}, + "max": {}, + "totals": { + "queryCount": 2 + }, + "query": { + "dimensions": ["queryName", "queryType", "responseCode"], + "metrics": ["queryCount"], + "filters": "responseCode==NOERROR AND queryType==TXT", + "sort": [ + { + "Id": "queryCount", + "Desc": true + } + ], + "since": "2020-02-01T00:00:00Z", + "until": "2020-02-28T23:59:59Z", + "limit": 10000 + } + }, + "success": true, + "errors": [], + "messages": [] +} diff --git a/tests/Fixtures/Endpoints/listCertificates.json b/tests/Fixtures/Endpoints/listCertificates.json new file mode 100644 index 0000000..5cd117b --- /dev/null +++ b/tests/Fixtures/Endpoints/listCertificates.json @@ -0,0 +1,19 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": [ + { + "id": "328578533902268680212849205732770752308931942346", + "certificate": "some-cert-data", + "hostnames": [ + "example.com", + "*.example.com" + ], + "expires_on": "2014-01-01T05:20:00.12345Z", + "request_type": "origin-rsa", + "requested_validity": 5475, + "csr": "some-csr-data" + } + ] +} \ No newline at end of file diff --git a/tests/Fixtures/Endpoints/revokeCertificate.json b/tests/Fixtures/Endpoints/revokeCertificate.json new file mode 100644 index 0000000..a845836 --- /dev/null +++ b/tests/Fixtures/Endpoints/revokeCertificate.json @@ -0,0 +1,8 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "11112222233333444455555" + } +} \ No newline at end of file diff --git a/tests/Fixtures/Endpoints/updateBrowserCacheTtlSetting.json b/tests/Fixtures/Endpoints/updateBrowserCacheTtlSetting.json new file mode 100644 index 0000000..be08512 --- /dev/null +++ b/tests/Fixtures/Endpoints/updateBrowserCacheTtlSetting.json @@ -0,0 +1,11 @@ +{ + "success": true, + "errors": [], + "messages": [], + "result": { + "id": "browser_cache_ttl", + "value": 14400, + "editable": true, + "modified_on": "2014-01-01T05:20:00.12345Z" + } +} \ No newline at end of file