From 4cfa7fc7dbc3b35b472fad1e33431d5a585dbf9d Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Tue, 6 Mar 2018 17:29:14 -0600 Subject: [PATCH 01/35] Updates to latest dependencies Updates to zend-expressive-router 2.4.1, and pins each of the router implementations to their 2.2 releases. Additionally, pins zend-stratigility to `^2.2.0rc3`, and updates `Zend\Expressive\Application` to use the `path()` utility function within `pipe()` to ensure it pipes a `PathMiddlewareDecorator`, avoiding trigger of a deprecation notice. --- composer.json | 10 +- composer.lock | 411 ++++++++++++++++++++++---------------------- src/Application.php | 8 +- 3 files changed, 219 insertions(+), 210 deletions(-) diff --git a/composer.json b/composer.json index 66cfeb8c..bd596e98 100644 --- a/composer.json +++ b/composer.json @@ -27,9 +27,9 @@ "psr/container": "^1.0", "psr/http-message": "^1.0.1", "zendframework/zend-diactoros": "^1.3.10", - "zendframework/zend-expressive-router": "^2.1", + "zendframework/zend-expressive-router": "^2.4.1", "zendframework/zend-expressive-template": "^1.0.4", - "zendframework/zend-stratigility": "^2.0.1" + "zendframework/zend-stratigility": "^2.2.0rc3" }, "require-dev": { "filp/whoops": "^2.1.6 || ^1.1.10", @@ -37,9 +37,9 @@ "mockery/mockery": "^1.0", "phpunit/phpunit": "^5.7.23 || ^6.4.3", "zendframework/zend-coding-standard": "~1.0.0", - "zendframework/zend-expressive-aurarouter": "^2.0", - "zendframework/zend-expressive-fastroute": "^2.0", - "zendframework/zend-expressive-zendrouter": "^2.0.1", + "zendframework/zend-expressive-aurarouter": "^2.2", + "zendframework/zend-expressive-fastroute": "^2.2", + "zendframework/zend-expressive-zendrouter": "^2.2", "zendframework/zend-servicemanager": "^3.3 || ^2.7.8" }, "conflict": { diff --git a/composer.lock b/composer.lock index 172e986c..1489985c 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "166d8374b75d79839d3a33cfab11678a", + "content-hash": "254736717fd6f9f35dadc9298ed768e0", "packages": [ { "name": "fig/http-message-util", @@ -298,20 +298,21 @@ "psr-15", "webimpress" ], + "abandoned": "psr/http-server-middleware", "time": "2017-10-17T17:31:10+00:00" }, { "name": "zendframework/zend-diactoros", - "version": "1.6.1", + "version": "1.7.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-diactoros.git", - "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877" + "reference": "bf26aff803a11c5cc8eb7c4878a702c403ec67f1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/c8664b92a6d5bc229e48b0923486c097e45a7877", - "reference": "c8664b92a6d5bc229e48b0923486c097e45a7877", + "url": "https://api.github.com/repos/zendframework/zend-diactoros/zipball/bf26aff803a11c5cc8eb7c4878a702c403ec67f1", + "reference": "bf26aff803a11c5cc8eb7c4878a702c403ec67f1", "shasum": "" }, "require": { @@ -330,8 +331,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev", - "dev-develop": "1.7-dev" + "dev-master": "1.7.x-dev", + "dev-develop": "1.8.x-dev" } }, "autoload": { @@ -350,7 +351,7 @@ "psr", "psr-7" ], - "time": "2017-10-12T15:24:51+00:00" + "time": "2018-02-26T15:44:50+00:00" }, { "name": "zendframework/zend-escaper", @@ -398,27 +399,29 @@ }, { "name": "zendframework/zend-expressive-router", - "version": "2.2.0", + "version": "2.4.1", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-router.git", - "reference": "f6eac53d39cdbf7b6db11b3e6bb3565896633de4" + "reference": "e1a00596aa20a29968bdc6ecdf0256c8bfd6e0b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-router/zipball/f6eac53d39cdbf7b6db11b3e6bb3565896633de4", - "reference": "f6eac53d39cdbf7b6db11b3e6bb3565896633de4", + "url": "https://api.github.com/repos/zendframework/zend-expressive-router/zipball/e1a00596aa20a29968bdc6ecdf0256c8bfd6e0b5", + "reference": "e1a00596aa20a29968bdc6ecdf0256c8bfd6e0b5", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.2", "php": "^5.6 || ^7.0", + "psr/container": "^1.0", "psr/http-message": "^1.0.1", "webimpress/http-middleware-compatibility": "^0.1.1" }, "require-dev": { + "http-interop/http-middleware": "0.4.1", "malukenho/docheader": "^0.1.5", - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "phpunit/phpunit": "^5.7.23 || ^6.4.3", "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { @@ -429,8 +432,11 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.2-dev", - "dev-develop": "2.3-dev" + "dev-master": "2.4.x-dev", + "dev-develop": "3.0.x-dev" + }, + "zf": { + "config-provider": "Zend\\Expressive\\Router\\ConfigProvider" } }, "autoload": { @@ -438,7 +444,33 @@ "Zend\\Expressive\\Router\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", + "autoload-dev": { + "psr-4": { + "ZendTest\\Expressive\\Router\\": "test/" + } + }, + "scripts": { + "check": [ + "@license-check", + "@cs-check", + "@test" + ], + "cs-check": [ + "phpcs" + ], + "cs-fix": [ + "phpcbf" + ], + "test": [ + "phpunit --colors=always" + ], + "test-coverage": [ + "phpunit --colors=always --coverage-clover clover.xml" + ], + "license-check": [ + "docheader check src/ test/" + ] + }, "license": [ "BSD-3-Clause" ], @@ -448,9 +480,19 @@ "http", "middleware", "psr", - "psr-7" + "psr-7", + "zend-expressive", + "zendframework", + "zf" ], - "time": "2017-10-09T18:44:11+00:00" + "support": { + "issues": "https://github.com/zendframework/zend-expressive-router/issues", + "source": "https://github.com/zendframework/zend-expressive-router", + "rss": "https://github.com/zendframework/zend-expressive-router/releases.atom", + "slack": "https://zendframework-slack.herokuapp.com", + "forum": "https://discourse.zendframework.com/c/questions/expressive" + }, + "time": "2018-03-08T19:27:02+00:00" }, { "name": "zendframework/zend-expressive-template", @@ -503,27 +545,27 @@ }, { "name": "zendframework/zend-stratigility", - "version": "2.1.2", + "version": "2.2.0rc3", "source": { "type": "git", "url": "https://github.com/zendframework/zend-stratigility.git", - "reference": "7dfec8dee92dad0d01e68365015f2848c250fe9f" + "reference": "824b7fba07c81dacf54eddafc15935bf9db1a45f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/7dfec8dee92dad0d01e68365015f2848c250fe9f", - "reference": "7dfec8dee92dad0d01e68365015f2848c250fe9f", + "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/824b7fba07c81dacf54eddafc15935bf9db1a45f", + "reference": "824b7fba07c81dacf54eddafc15935bf9db1a45f", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/http-message": "^1.0", - "webimpress/http-middleware-compatibility": "^0.1.3", + "webimpress/http-middleware-compatibility": "^0.1.4", "zendframework/zend-escaper": "^2.3" }, "require-dev": { "malukenho/docheader": "^0.1.5", - "phpunit/phpunit": "^6.0.8 || ^5.7.15", + "phpunit/phpunit": "^5.7.22 || ^6.4.1", "zendframework/zend-coding-standard": "~1.0.0", "zendframework/zend-diactoros": "^1.0" }, @@ -533,11 +575,17 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.0-dev", - "dev-develop": "2.2.0-dev" + "dev-master": "2.1.x-dev", + "dev-develop": "2.2.x-dev", + "dev-release-3.0.0": "3.0.x-dev" } }, "autoload": { + "files": [ + "src/functions/double-pass-middleware.php", + "src/functions/middleware.php", + "src/functions/path.php" + ], "psr-4": { "Zend\\Stratigility\\": "src/" } @@ -549,11 +597,13 @@ "description": "Middleware for PHP", "homepage": "https://github.com/zendframework/zend-stratigility", "keywords": [ + "ZendFramework", "http", "middleware", - "psr-7" + "psr-7", + "zf" ], - "time": "2017-10-12T13:14:14+00:00" + "time": "2018-03-08T20:47:18+00:00" } ], "packages-dev": [ @@ -802,22 +852,22 @@ }, { "name": "malukenho/docheader", - "version": "0.1.6", + "version": "0.1.7", "source": { "type": "git", "url": "https://github.com/malukenho/docheader.git", - "reference": "b3857387fe5e6b0928b67875ea09ebb5745d5b8b" + "reference": "3eb59f0621125c0dc40775f1bcc3206c37993703" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/malukenho/docheader/zipball/b3857387fe5e6b0928b67875ea09ebb5745d5b8b", - "reference": "b3857387fe5e6b0928b67875ea09ebb5745d5b8b", + "url": "https://api.github.com/repos/malukenho/docheader/zipball/3eb59f0621125c0dc40775f1bcc3206c37993703", + "reference": "3eb59f0621125c0dc40775f1bcc3206c37993703", "shasum": "" }, "require": { "php": "~5.5|^7.0", - "symfony/console": "~2.0|^3.0", - "symfony/finder": "~2.0|^3.0" + "symfony/console": "~2.0 || ^3.0 || ^4.0", + "symfony/finder": "~2.0 || ^3.0 || ^4.0" }, "require-dev": { "phpunit/phpunit": "^4.7", @@ -849,7 +899,7 @@ "code standard", "license" ], - "time": "2017-05-03T05:22:55+00:00" + "time": "2017-12-18T09:16:11+00:00" }, { "name": "mockery/mockery", @@ -963,21 +1013,24 @@ }, { "name": "nikic/fast-route", - "version": "v1.2.0", + "version": "v1.3.0", "source": { "type": "git", "url": "https://github.com/nikic/FastRoute.git", - "reference": "b5f95749071c82a8e0f58586987627054400cdf6" + "reference": "181d480e08d9476e61381e04a71b34dc0432e812" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/FastRoute/zipball/b5f95749071c82a8e0f58586987627054400cdf6", - "reference": "b5f95749071c82a8e0f58586987627054400cdf6", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/181d480e08d9476e61381e04a71b34dc0432e812", + "reference": "181d480e08d9476e61381e04a71b34dc0432e812", "shasum": "" }, "require": { "php": ">=5.4.0" }, + "require-dev": { + "phpunit/phpunit": "^4.8.35|~5.7" + }, "type": "library", "autoload": { "psr-4": { @@ -1002,7 +1055,7 @@ "router", "routing" ], - "time": "2017-01-19T11:35:12+00:00" + "time": "2018-02-13T20:26:39+00:00" }, { "name": "phar-io/manifest", @@ -1162,16 +1215,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "4.2.0", + "version": "4.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "66465776cfc249844bde6d117abff1d22e06c2da" + "reference": "94fd0001232e47129dd3504189fa1c7225010d08" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/66465776cfc249844bde6d117abff1d22e06c2da", - "reference": "66465776cfc249844bde6d117abff1d22e06c2da", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94fd0001232e47129dd3504189fa1c7225010d08", + "reference": "94fd0001232e47129dd3504189fa1c7225010d08", "shasum": "" }, "require": { @@ -1209,7 +1262,7 @@ } ], "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-27T17:38:31+00:00" + "time": "2017-11-30T07:14:17+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1260,16 +1313,16 @@ }, { "name": "phpspec/prophecy", - "version": "1.7.3", + "version": "1.7.5", "source": { "type": "git", "url": "https://github.com/phpspec/prophecy.git", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf" + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpspec/prophecy/zipball/e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", - "reference": "e4ed002c67da8eceb0eb8ddb8b3847bb53c5c2bf", + "url": "https://api.github.com/repos/phpspec/prophecy/zipball/dfd6be44111a7c41c2e884a336cc4f461b3b2401", + "reference": "dfd6be44111a7c41c2e884a336cc4f461b3b2401", "shasum": "" }, "require": { @@ -1281,7 +1334,7 @@ }, "require-dev": { "phpspec/phpspec": "^2.5|^3.2", - "phpunit/phpunit": "^4.8.35 || ^5.7" + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.5" }, "type": "library", "extra": { @@ -1319,7 +1372,7 @@ "spy", "stub" ], - "time": "2017-11-24T13:59:53+00:00" + "time": "2018-02-19T10:16:54+00:00" }, { "name": "phpunit/php-code-coverage", @@ -1572,16 +1625,16 @@ }, { "name": "phpunit/phpunit", - "version": "6.5.4", + "version": "6.5.7", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c" + "reference": "6bd77b57707c236833d2b57b968e403df060c9d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/1b2f933d5775f9237369deaa2d2bfbf9d652be4c", - "reference": "1b2f933d5775f9237369deaa2d2bfbf9d652be4c", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/6bd77b57707c236833d2b57b968e403df060c9d9", + "reference": "6bd77b57707c236833d2b57b968e403df060c9d9", "shasum": "" }, "require": { @@ -1652,20 +1705,20 @@ "testing", "xunit" ], - "time": "2017-12-10T08:06:19+00:00" + "time": "2018-02-26T07:01:09+00:00" }, { "name": "phpunit/phpunit-mock-objects", - "version": "5.0.5", + "version": "5.0.6", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit-mock-objects.git", - "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933" + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", - "reference": "283b9f4f670e3a6fd6c4ff95c51a952eb5c75933", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit-mock-objects/zipball/33fd41a76e746b8fa96d00b49a23dadfa8334cdf", + "reference": "33fd41a76e746b8fa96d00b49a23dadfa8334cdf", "shasum": "" }, "require": { @@ -1711,7 +1764,7 @@ "mock", "xunit" ], - "time": "2017-12-10T08:01:53+00:00" + "time": "2018-01-06T05:45:45+00:00" }, { "name": "psr/log", @@ -1807,21 +1860,21 @@ }, { "name": "sebastian/comparator", - "version": "2.1.0", + "version": "2.1.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158" + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/1174d9018191e93cb9d719edec01257fc05f8158", - "reference": "1174d9018191e93cb9d719edec01257fc05f8158", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/34369daee48eafb2651bea869b4b15d75ccc35f9", + "reference": "34369daee48eafb2651bea869b4b15d75ccc35f9", "shasum": "" }, "require": { "php": "^7.0", - "sebastian/diff": "^2.0", + "sebastian/diff": "^2.0 || ^3.0", "sebastian/exporter": "^3.1" }, "require-dev": { @@ -1867,7 +1920,7 @@ "compare", "equality" ], - "time": "2017-11-03T07:16:52+00:00" + "time": "2018-02-01T13:46:46+00:00" }, { "name": "sebastian/diff", @@ -2399,21 +2452,20 @@ }, { "name": "symfony/console", - "version": "v3.4.1", + "version": "v4.0.6", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "2cdef78de8f54f68ff16a857e710e7302b47d4c7" + "reference": "555c8dbe0ae9e561740451eabdbed2cc554b6a51" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2cdef78de8f54f68ff16a857e710e7302b47d4c7", - "reference": "2cdef78de8f54f68ff16a857e710e7302b47d4c7", + "url": "https://api.github.com/repos/symfony/console/zipball/555c8dbe0ae9e561740451eabdbed2cc554b6a51", + "reference": "555c8dbe0ae9e561740451eabdbed2cc554b6a51", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8", - "symfony/debug": "~2.8|~3.0|~4.0", + "php": "^7.1.3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { @@ -2422,11 +2474,11 @@ }, "require-dev": { "psr/log": "~1.0", - "symfony/config": "~3.3|~4.0", + "symfony/config": "~3.4|~4.0", "symfony/dependency-injection": "~3.4|~4.0", - "symfony/event-dispatcher": "~2.8|~3.0|~4.0", + "symfony/event-dispatcher": "~3.4|~4.0", "symfony/lock": "~3.4|~4.0", - "symfony/process": "~3.3|~4.0" + "symfony/process": "~3.4|~4.0" }, "suggest": { "psr/log": "For using the console logger", @@ -2437,7 +2489,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2464,85 +2516,29 @@ ], "description": "Symfony Console Component", "homepage": "https://symfony.com", - "time": "2017-12-02T18:20:11+00:00" - }, - { - "name": "symfony/debug", - "version": "v4.0.1", - "source": { - "type": "git", - "url": "https://github.com/symfony/debug.git", - "reference": "26a15dab86c3820473716be4f846eac774ad4ad9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/debug/zipball/26a15dab86c3820473716be4f846eac774ad4ad9", - "reference": "26a15dab86c3820473716be4f846eac774ad4ad9", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "psr/log": "~1.0" - }, - "conflict": { - "symfony/http-kernel": "<3.4" - }, - "require-dev": { - "symfony/http-kernel": "~3.4|~4.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "4.0-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Component\\Debug\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony Debug Component", - "homepage": "https://symfony.com", - "time": "2017-11-21T09:27:49+00:00" + "time": "2018-02-26T15:55:47+00:00" }, { "name": "symfony/finder", - "version": "v3.4.1", + "version": "v4.0.6", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "dac8d7db537bac7ad8143eb11360a8c2231f251a" + "reference": "44a796d2ecc2a16a5fc8f2956a34ee617934d55f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/dac8d7db537bac7ad8143eb11360a8c2231f251a", - "reference": "dac8d7db537bac7ad8143eb11360a8c2231f251a", + "url": "https://api.github.com/repos/symfony/finder/zipball/44a796d2ecc2a16a5fc8f2956a34ee617934d55f", + "reference": "44a796d2ecc2a16a5fc8f2956a34ee617934d55f", "shasum": "" }, "require": { - "php": "^5.5.9|>=7.0.8" + "php": "^7.1.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.4-dev" + "dev-master": "4.0-dev" } }, "autoload": { @@ -2569,20 +2565,20 @@ ], "description": "Symfony Finder Component", "homepage": "https://symfony.com", - "time": "2017-11-05T16:10:10+00:00" + "time": "2018-03-05T18:28:26+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.6.0", + "version": "v1.7.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296" + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", - "reference": "2ec8b39c38cb16674bbf3fea2b6ce5bf117e1296", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/78be803ce01e55d3491c1397cf1c64beb9c1b63b", + "reference": "78be803ce01e55d3491c1397cf1c64beb9c1b63b", "shasum": "" }, "require": { @@ -2594,7 +2590,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "1.6-dev" + "dev-master": "1.7-dev" } }, "autoload": { @@ -2628,7 +2624,7 @@ "portable", "shim" ], - "time": "2017-10-11T12:05:26+00:00" + "time": "2018-01-30T19:27:44+00:00" }, { "name": "theseer/tokenizer", @@ -2672,16 +2668,16 @@ }, { "name": "webmozart/assert", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/webmozart/assert.git", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f" + "reference": "0df1908962e7a3071564e857d86874dad1ef204a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/2db61e59ff05fe5126d152bd0655c9ea113e550f", - "reference": "2db61e59ff05fe5126d152bd0655c9ea113e550f", + "url": "https://api.github.com/repos/webmozart/assert/zipball/0df1908962e7a3071564e857d86874dad1ef204a", + "reference": "0df1908962e7a3071564e857d86874dad1ef204a", "shasum": "" }, "require": { @@ -2718,7 +2714,7 @@ "check", "validate" ], - "time": "2016-11-23T20:04:58+00:00" + "time": "2018-01-29T19:49:41+00:00" }, { "name": "zendframework/zend-coding-standard", @@ -2751,16 +2747,16 @@ }, { "name": "zendframework/zend-expressive-aurarouter", - "version": "2.1.1", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-aurarouter.git", - "reference": "0d8d60b7b419c47291fc567e566bc1acf1aca9ab" + "reference": "cb0d8b343f11d32f3171c558c7e9e42c16ff5d3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-aurarouter/zipball/0d8d60b7b419c47291fc567e566bc1acf1aca9ab", - "reference": "0d8d60b7b419c47291fc567e566bc1acf1aca9ab", + "url": "https://api.github.com/repos/zendframework/zend-expressive-aurarouter/zipball/cb0d8b343f11d32f3171c558c7e9e42c16ff5d3b", + "reference": "cb0d8b343f11d32f3171c558c7e9e42c16ff5d3b", "shasum": "" }, "require": { @@ -2768,7 +2764,7 @@ "fig/http-message-util": "^1.1.2", "php": "^5.6 || ^7.0", "psr/http-message": "^1.0.1", - "zendframework/zend-expressive-router": "^2.1" + "zendframework/zend-expressive-router": "^2.4" }, "require-dev": { "malukenho/docheader": "^0.1.5", @@ -2778,8 +2774,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev", - "dev-develop": "2.2.x-dev" + "dev-master": "2.2.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -2803,20 +2799,20 @@ "zend-expressive", "zf" ], - "time": "2017-12-06T21:48:12+00:00" + "time": "2018-03-08T17:22:09+00:00" }, { "name": "zendframework/zend-expressive-fastroute", - "version": "2.1.2", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-fastroute.git", - "reference": "8ba998e5f5de883d2753031338029cb4e545e963" + "reference": "e35c040c7b76fd03156e537053b3c05c700028dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-fastroute/zipball/8ba998e5f5de883d2753031338029cb4e545e963", - "reference": "8ba998e5f5de883d2753031338029cb4e545e963", + "url": "https://api.github.com/repos/zendframework/zend-expressive-fastroute/zipball/e35c040c7b76fd03156e537053b3c05c700028dc", + "reference": "e35c040c7b76fd03156e537053b3c05c700028dc", "shasum": "" }, "require": { @@ -2825,7 +2821,7 @@ "php": "^5.6 || ^7.0", "psr/container": "^1.0", "psr/http-message": "^1.0.1", - "zendframework/zend-expressive-router": "^2.0.1", + "zendframework/zend-expressive-router": "^2.4", "zendframework/zend-stdlib": "^3.1 || 2.*" }, "conflict": { @@ -2839,8 +2835,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1-dev", - "dev-develop": "2.2-dev" + "dev-master": "2.2.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -2864,27 +2860,27 @@ "zend-expressive", "zf" ], - "time": "2017-12-06T21:35:57+00:00" + "time": "2018-03-08T16:09:36+00:00" }, { "name": "zendframework/zend-expressive-zendrouter", - "version": "2.1.0", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-expressive-zendrouter.git", - "reference": "73aa3d81b29add8424a5db865e069fd6c67502e5" + "reference": "c7edcd47eb22842f7120432d068ef832077352d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-expressive-zendrouter/zipball/73aa3d81b29add8424a5db865e069fd6c67502e5", - "reference": "73aa3d81b29add8424a5db865e069fd6c67502e5", + "url": "https://api.github.com/repos/zendframework/zend-expressive-zendrouter/zipball/c7edcd47eb22842f7120432d068ef832077352d9", + "reference": "c7edcd47eb22842f7120432d068ef832077352d9", "shasum": "" }, "require": { "fig/http-message-util": "^1.1.2", "php": "^5.6 || ^7.0", "psr/http-message": "^1.0.1", - "zendframework/zend-expressive-router": "^2.1", + "zendframework/zend-expressive-router": "^2.4", "zendframework/zend-psr7bridge": "^0.2.2 || ^1.0.0", "zendframework/zend-router": "^3.0.2" }, @@ -2897,8 +2893,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev", - "dev-develop": "2.2.x-dev" + "dev-master": "2.2.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -2921,7 +2917,7 @@ "zend-expressive", "zf" ], - "time": "2017-12-06T22:46:38+00:00" + "time": "2018-03-08T17:32:50+00:00" }, { "name": "zendframework/zend-http", @@ -3022,26 +3018,26 @@ }, { "name": "zendframework/zend-psr7bridge", - "version": "1.0.0", + "version": "1.0.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-psr7bridge.git", - "reference": "935721336ded76fd5ba90ba7637c7d85b4d0cf68" + "reference": "b79236866a1ebc819014049cbf3570931ca166c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/935721336ded76fd5ba90ba7637c7d85b4d0cf68", - "reference": "935721336ded76fd5ba90ba7637c7d85b4d0cf68", + "url": "https://api.github.com/repos/zendframework/zend-psr7bridge/zipball/b79236866a1ebc819014049cbf3570931ca166c7", + "reference": "b79236866a1ebc819014049cbf3570931ca166c7", "shasum": "" }, "require": { "php": "^5.6 || ^7.0", "psr/http-message": "^1.0", - "zendframework/zend-diactoros": "^1.1", - "zendframework/zend-http": "^2.6" + "zendframework/zend-diactoros": "^1.7", + "zendframework/zend-http": "^2.7" }, "require-dev": { - "phpunit/phpunit": "^5.7.15 || ^6.0.8", + "phpunit/phpunit": "^5.7.15 || ^6.5.6", "zendframework/zend-coding-standard": "~1.0.0" }, "type": "library", @@ -3060,16 +3056,16 @@ "license": [ "BSD-3-Clause" ], - "description": "PSR-7 <-> Zend\\Http bridge", - "homepage": "https://github.com/zendframework/zend-psr7bridge", + "description": "PSR-7 <-> zend-http message conversions", "keywords": [ "ZendFramework", "http", "psr", "psr-7", - "zend" + "zend", + "zf" ], - "time": "2017-08-02T15:52:02+00:00" + "time": "2018-02-14T04:27:50+00:00" }, { "name": "zendframework/zend-router", @@ -3134,16 +3130,16 @@ }, { "name": "zendframework/zend-servicemanager", - "version": "3.3.1", + "version": "3.3.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-servicemanager.git", - "reference": "0fa3d3cf588dde0850fff1efa60d44a7aa3c3ab7" + "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/0fa3d3cf588dde0850fff1efa60d44a7aa3c3ab7", - "reference": "0fa3d3cf588dde0850fff1efa60d44a7aa3c3ab7", + "url": "https://api.github.com/repos/zendframework/zend-servicemanager/zipball/9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", + "reference": "9f35a104b8d4d3b32da5f4a3b6efc0dd62e5af42", "shasum": "" }, "require": { @@ -3157,10 +3153,10 @@ "psr/container-implementation": "^1.0" }, "require-dev": { - "mikey179/vfsstream": "^1.6", + "mikey179/vfsstream": "^1.6.5", "ocramius/proxy-manager": "^1.0 || ^2.0", - "phpbench/phpbench": "^0.10.0", - "phpunit/phpunit": "^5.7 || ^6.0.6", + "phpbench/phpbench": "^0.13.0", + "phpunit/phpunit": "^5.7.25 || ^6.4.4", "zendframework/zend-coding-standard": "~1.0.0" }, "suggest": { @@ -3187,13 +3183,18 @@ "license": [ "BSD-3-Clause" ], - "homepage": "https://github.com/zendframework/zend-servicemanager", + "description": "Factory-Driven Dependency Injection Container", "keywords": [ + "PSR-11", + "ZendFramework", + "dependency-injection", + "di", + "dic", "service-manager", "servicemanager", "zf" ], - "time": "2017-11-27T18:11:25+00:00" + "time": "2018-01-29T16:48:37+00:00" }, { "name": "zendframework/zend-stdlib", @@ -3289,16 +3290,16 @@ }, { "name": "zendframework/zend-validator", - "version": "2.10.1", + "version": "2.10.2", "source": { "type": "git", "url": "https://github.com/zendframework/zend-validator.git", - "reference": "010084ddbd33299bf51ea6f0e07f8f4e8bd832a8" + "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/010084ddbd33299bf51ea6f0e07f8f4e8bd832a8", - "reference": "010084ddbd33299bf51ea6f0e07f8f4e8bd832a8", + "url": "https://api.github.com/repos/zendframework/zend-validator/zipball/38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", + "reference": "38109ed7d8e46cfa71bccbe7e6ca80cdd035f8c9", "shasum": "" }, "require": { @@ -3333,8 +3334,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.10-dev", - "dev-develop": "2.11-dev" + "dev-master": "2.10.x-dev", + "dev-develop": "2.11.x-dev" }, "zf": { "component": "Zend\\Validator", @@ -3356,12 +3357,14 @@ "validator", "zf2" ], - "time": "2017-08-22T14:19:23+00:00" + "time": "2018-02-01T17:05:33+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "zendframework/zend-stratigility": 5 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Application.php b/src/Application.php index 7411235e..e05acf76 100644 --- a/src/Application.php +++ b/src/Application.php @@ -21,6 +21,8 @@ use Zend\Diactoros\ServerRequestFactory; use Zend\Stratigility\MiddlewarePipe; +use function Zend\Stratigility\path; + /** * Middleware application providing routing based on paths and HTTP methods. */ @@ -230,7 +232,11 @@ public function pipe($path, $middleware = null) return $this; } - parent::pipe($path, $middleware); + if ($path !== '/') { + $middleware = path($path, $middleware); + } + + parent::pipe($middleware); if ($middleware instanceof Middleware\RouteMiddleware) { $this->routeMiddlewareIsRegistered = true; From 495cbbbf41a0bf104cfe1079e2debd7e58f6f314 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 15:00:34 -0600 Subject: [PATCH 02/35] Code and test updates to work with stratigility 2.2 and zend-expressive-router 2.4 Primary changes are around: - Routes raise deprecation notices when non-middleware instances are provided. All tests now provide instances. - We now use the `path()` utility function to decorate path-segregated middleware prior to piping it; this changes a number of test expectations, even if the end operation is identical. The `ApplicationConfigInjectionTrait` was updated to use `prepareMiddleware()` on middleware specifications prior to routing. --- src/Application.php | 2 +- test/Application/ConfigInjectionTest.php | 2 ++ test/ApplicationTest.php | 13 ++++++++++-- test/Container/ApplicationFactoryTest.php | 23 ++++++++++++++++------ test/Middleware/DispatchMiddlewareTest.php | 14 ++++++++----- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/src/Application.php b/src/Application.php index e05acf76..45880fd2 100644 --- a/src/Application.php +++ b/src/Application.php @@ -232,7 +232,7 @@ public function pipe($path, $middleware = null) return $this; } - if ($path !== '/') { + if (! in_array($path, ['', '/'], true)) { $middleware = path($path, $middleware); } diff --git a/test/Application/ConfigInjectionTest.php b/test/Application/ConfigInjectionTest.php index f02486e9..ffabb81f 100644 --- a/test/Application/ConfigInjectionTest.php +++ b/test/Application/ConfigInjectionTest.php @@ -59,6 +59,8 @@ public static function assertRoute($spec, array $routes) return false; } + // We're just testing that middleware is present; since it may + // be decorated, this might fail otherwise. if (! $route->getMiddleware()) { return false; } diff --git a/test/ApplicationTest.php b/test/ApplicationTest.php index 58529507..a9c75568 100644 --- a/test/ApplicationTest.php +++ b/test/ApplicationTest.php @@ -8,6 +8,7 @@ namespace ZendTest\Expressive; use DomainException; +use Fig\Http\Message\RequestMethodInterface as RequestMethod; use Fig\Http\Message\StatusCodeInterface as StatusCode; use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; @@ -19,6 +20,7 @@ use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; use Psr\Http\Message\ServerRequestInterface; +use Psr\Http\Message\UriInterface; use ReflectionMethod; use ReflectionProperty; use RuntimeException; @@ -41,6 +43,7 @@ use Zend\Expressive\Router\RouterInterface; use Zend\Expressive\Template\TemplateRendererInterface; use Zend\Stratigility\MiddlewarePipe; +use Zend\Stratigility\Middleware\PathMiddlewareDecorator; use Zend\Stratigility\Route as StratigilityRoute; /** @@ -453,7 +456,12 @@ public function testAllowsPipingMiddlewareAsServiceNameWithPath() $route = $pipeline->dequeue(); $this->assertInstanceOf(StratigilityRoute::class, $route); $handler = $route->handler; - $this->assertInstanceOf(Middleware\LazyLoadingMiddleware::class, $handler); + $this->assertInstanceOf(PathMiddlewareDecorator::class, $handler); + + $r = new ReflectionProperty($handler, 'middleware'); + $r->setAccessible(true); + $handler = $r->getValue($handler); + $this->assertAttributeEquals('foo', 'middlewareName', $handler); } @@ -478,7 +486,8 @@ public function testPipingNotInvokableMiddlewareRaisesExceptionWhenInvokingRoute $this->assertInstanceOf(StratigilityRoute::class, $route); $handler = $route->handler; - $request = $this->prophesize(ServerRequest::class)->reveal(); + $request = new ServerRequest([], [], '/foo', RequestMethod::METHOD_GET); + $delegate = $this->prophesize(DelegateInterface::class)->reveal(); $this->expectException(InvalidMiddlewareException::class); diff --git a/test/Container/ApplicationFactoryTest.php b/test/Container/ApplicationFactoryTest.php index eaeef9f4..ffdfaee6 100644 --- a/test/Container/ApplicationFactoryTest.php +++ b/test/Container/ApplicationFactoryTest.php @@ -29,6 +29,7 @@ use Zend\Expressive\Router\Route; use Zend\Expressive\Router\RouterInterface; use Zend\Stratigility\MiddlewarePipe; +use Zend\Stratigility\Middleware\PathMiddlewareDecorator; use ZendTest\Expressive\ContainerTrait; use ZendTest\Expressive\TestAsset\InteropMiddleware; use ZendTest\Expressive\TestAsset\InvokableMiddleware; @@ -81,6 +82,8 @@ public static function assertRoute($spec, array $routes) return false; } + // We're just testing that middleware is present; since it may + // be decorated, this might fail otherwise. if (! $route->getMiddleware()) { return false; } @@ -331,11 +334,12 @@ public function testExceptionIsRaisedInCaseOfInvalidRouteMethodsConfiguration($c */ public function testExceptionIsRaisedInCaseOfInvalidRouteOptionsConfiguration($configType) { + $middleware = $this->prophesize(MiddlewareInterface::class)->reveal(); $config = [ 'routes' => [ [ 'path' => '/', - 'middleware' => $this->prophesize(MiddlewareInterface::class)->reveal(), + 'middleware' => $middleware, 'options' => 'invalid', ], ], @@ -366,6 +370,7 @@ public function testWillCreatePipelineBasedOnMiddlewareConfiguration($configType $hello = clone $api; $pipelineLast = clone $api; + $this->injectServiceInContainer($this->container, 'DynamicPath', $dynamicPath); $this->injectServiceInContainer($this->container, 'Goodbye', $goodbye); $this->injectServiceInContainer($this->container, 'Hello', $hello); @@ -411,14 +416,20 @@ public function testWillCreatePipelineBasedOnMiddlewareConfiguration($configType $this->assertCount(5, $pipeline, 'Did not get expected pipeline count!'); $test = $pipeline->dequeue(); - $this->assertEquals('/api', $test->path); - $this->assertSame($api, $test->handler); + $this->assertEquals('/', $test->path); + $this->assertInstanceOf(PathMiddlewareDecorator::class, $test->handler); + $handler = $test->handler; + $this->assertAttributeSame('/api', 'prefix', $handler); + $this->assertAttributeSame($api, 'middleware', $handler); // Lazy middleware is not marshaled until invocation $test = $pipeline->dequeue(); - $this->assertEquals('/dynamic-path', $test->path); - $this->assertNotSame($dynamicPath, $test->handler); - $this->assertInstanceOf(LazyLoadingMiddleware::class, $test->handler); + $this->assertEquals('/', $test->path); + $this->assertInstanceOf(PathMiddlewareDecorator::class, $test->handler); + $handler = $test->handler; + $this->assertAttributeSame('/dynamic-path', 'prefix', $handler); + $this->assertAttributeNotSame($dynamicPath, 'middleware', $handler); + $this->assertAttributeInstanceOf(LazyLoadingMiddleware::class, 'middleware', $handler); $test = $pipeline->dequeue(); $this->assertEquals('/', $test->path); diff --git a/test/Middleware/DispatchMiddlewareTest.php b/test/Middleware/DispatchMiddlewareTest.php index 6936d539..8c3f06b2 100644 --- a/test/Middleware/DispatchMiddlewareTest.php +++ b/test/Middleware/DispatchMiddlewareTest.php @@ -92,8 +92,10 @@ public function testCanDispatchCallableDoublePassMiddleware() $expected = $this->prophesize(ResponseInterface::class)->reveal(); $routedMiddleware = $this->prophesize(ServerMiddlewareInterface::class); $routedMiddleware - ->process(Argument::that([$this->request, 'reveal']), Argument::that([$this->delegate, 'reveal'])) - ->willReturn($expected); + ->process( + Argument::that([$this->request, 'reveal']), + Argument::that([$this->delegate, 'reveal']) + )->willReturn($expected); $routeResult = RouteResult::fromRoute(new Route('/', $routedMiddleware->reveal())); @@ -114,11 +116,13 @@ public function testCanDispatchLazyMiddleware() $expected = $this->prophesize(ResponseInterface::class)->reveal(); $routedMiddleware = $this->prophesize(ServerMiddlewareInterface::class); $routedMiddleware - ->process(Argument::that([$this->request, 'reveal']), Argument::that([$this->delegate, 'reveal'])) - ->willReturn($expected); + ->process( + Argument::that([$this->request, 'reveal']), + Argument::that([$this->delegate, 'reveal']) + )->willReturn($expected); $this->container->has('RoutedMiddleware')->willReturn(true); - $this->container->get('RoutedMiddleware')->willReturn($routedMiddleware->reveal()); + $this->container->get('RoutedMiddleware')->will([$routedMiddleware, 'reveal']); // Since 2.0, we never have service names in routes, only lazy-loading middleware $lazyMiddleware = new LazyLoadingMiddleware( From ae02b44212c52db3674d0f4ce8dc40d5eb225033 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 15:35:49 -0600 Subject: [PATCH 03/35] Add symfony/console to legacy deps in travis config --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 472ee9dc..8966aab0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ matrix: - php: 7 env: - DEPS=locked - - LEGACY_DEPS="phpunit/phpunit symfony/debug" + - LEGACY_DEPS="phpunit/phpunit symfony/console symfony/debug" - php: 7 env: - DEPS=latest From c0581803def3a514e03bed762d9f7b3ccf54d836 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 16:06:41 -0600 Subject: [PATCH 04/35] Refactors RouteMiddleware - Extends zend-expressive-router RouteMiddleware. - Marked as deprecated. --- src/Middleware/RouteMiddleware.php | 59 ++---------------------------- 1 file changed, 4 insertions(+), 55 deletions(-) diff --git a/src/Middleware/RouteMiddleware.php b/src/Middleware/RouteMiddleware.php index 59e5b362..9f854013 100644 --- a/src/Middleware/RouteMiddleware.php +++ b/src/Middleware/RouteMiddleware.php @@ -7,13 +7,7 @@ namespace Zend\Expressive\Middleware; -use Fig\Http\Message\StatusCodeInterface as StatusCode; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use Zend\Expressive\Router\RouteResult; -use Zend\Expressive\Router\RouterInterface; +use Zend\Expressive\Router\Middleware\RouteMiddleware as BaseRouteMiddleware; /** * Default routing middleware. @@ -28,55 +22,10 @@ * RouteResult class name), as well as any matched parameters, before * delegating to the next middleware. * + * @deprecated since 2.2.0. This class is now a part of zend-expressive-router, + * and will be removed for the 3.0.0 release. * @internal */ -class RouteMiddleware implements ServerMiddlewareInterface +class RouteMiddleware extends BaseRouteMiddleware { - /** - * Response prototype for 405 responses. - * - * @var ResponseInterface - */ - private $responsePrototype; - - /** - * @var RouterInterface - */ - private $router; - - /** - * @param RouterInterface $router - * @param ResponseInterface $responsePrototype - */ - public function __construct(RouterInterface $router, ResponseInterface $responsePrototype) - { - $this->router = $router; - $this->responsePrototype = $responsePrototype; - } - - /** - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $result = $this->router->match($request); - - if ($result->isFailure()) { - if ($result->isMethodFailure()) { - return $this->responsePrototype->withStatus(StatusCode::STATUS_METHOD_NOT_ALLOWED) - ->withHeader('Allow', implode(',', $result->getAllowedMethods())); - } - return $delegate->process($request); - } - - // Inject the actual route result, as well as individual matched parameters. - $request = $request->withAttribute(RouteResult::class, $result); - foreach ($result->getMatchedParams() as $param => $value) { - $request = $request->withAttribute($param, $value); - } - - return $delegate->process($request); - } } From 222ebd90edae6a12c03ac01e23580d03118c102d Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 16:09:18 -0600 Subject: [PATCH 05/35] Refactors DispatchMiddleware - Extends zend-expressive-router class. - Marked as deprecated. --- src/Middleware/DispatchMiddleware.php | 72 ++------------------------- 1 file changed, 5 insertions(+), 67 deletions(-) diff --git a/src/Middleware/DispatchMiddleware.php b/src/Middleware/DispatchMiddleware.php index 7e1c80c1..7ca87eb6 100644 --- a/src/Middleware/DispatchMiddleware.php +++ b/src/Middleware/DispatchMiddleware.php @@ -1,20 +1,13 @@ router = $router; - $this->responsePrototype = $responsePrototype; - $this->container = $container; - } - - /** - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - $routeResult = $request->getAttribute(RouteResult::class, false); - if (! $routeResult) { - return $delegate->process($request); - } - - $middleware = $routeResult->getMatchedMiddleware(); - - if (! $middleware instanceof ServerMiddlewareInterface) { - $middleware = $this->prepareMiddleware( - $middleware, - $this->router, - $this->responsePrototype, - $this->container - ); - } - - return $middleware->process($request, $delegate); - } } From f7306df61991ffbbf0b87614446e320b11f5d98a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 16:09:55 -0600 Subject: [PATCH 06/35] Remove unnecessary tests The route and dispatch middleware are now extensions with zero overrides, meaning no tests are necessary as they are tested in the zend-expressive-router package. Previous tests continued to pass, indicating no additional behaviors were expected. `MarshalMiddlewareTraitTest` did test for values injected during instantiation for the `DispatchMiddleware`. Since these are no longer used, those tests could be removed. --- .../MarshalMiddlewareTraitTest.php | 6 - test/Middleware/DispatchMiddlewareTest.php | 142 ------------------ test/Middleware/RouteMiddlewareTest.php | 111 -------------- 3 files changed, 259 deletions(-) delete mode 100644 test/Middleware/DispatchMiddlewareTest.php delete mode 100644 test/Middleware/RouteMiddlewareTest.php diff --git a/test/Application/MarshalMiddlewareTraitTest.php b/test/Application/MarshalMiddlewareTraitTest.php index 9c8366ba..12806b96 100644 --- a/test/Application/MarshalMiddlewareTraitTest.php +++ b/test/Application/MarshalMiddlewareTraitTest.php @@ -92,18 +92,12 @@ public function testPreparingDispatchMiddlewareReturnsDispatchMiddleware() { $middleware = $this->prepareMiddleware(Application::DISPATCH_MIDDLEWARE); $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $middleware); - $this->assertAttributeSame($this->container->reveal(), 'container', $middleware); - $this->assertAttributeSame($this->router->reveal(), 'router', $middleware); - $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } public function testPreparingDispatchMiddlewareWithoutContainerReturnsDispatchMiddleware() { $middleware = $this->prepareMiddlewareWithoutContainer(Application::DISPATCH_MIDDLEWARE); $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $middleware); - $this->assertAttributeEmpty('container', $middleware); - $this->assertAttributeSame($this->router->reveal(), 'router', $middleware); - $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } public function testPreparingInteropMiddlewareReturnsMiddlewareVerbatim() diff --git a/test/Middleware/DispatchMiddlewareTest.php b/test/Middleware/DispatchMiddlewareTest.php deleted file mode 100644 index 8c3f06b2..00000000 --- a/test/Middleware/DispatchMiddlewareTest.php +++ /dev/null @@ -1,142 +0,0 @@ -container = $this->prophesize(ContainerInterface::class); - $this->responsePrototype = $this->prophesize(ResponseInterface::class); - $this->router = $this->prophesize(RouterInterface::class); - $this->request = $this->prophesize(ServerRequestInterface::class); - $this->delegate = $this->prophesize(DelegateInterface::class); - $this->middleware = new DispatchMiddleware( - $this->router->reveal(), - $this->responsePrototype->reveal(), - $this->container->reveal() - ); - } - - public function testInvokesDelegateIfRequestDoesNotContainRouteResult() - { - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $this->request->getAttribute(RouteResult::class, false)->willReturn(false); - $this->delegate->process($this->request->reveal())->willReturn($expected); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - - $this->assertSame($expected, $response); - } - - public function testInvokesMatchedMiddlewareWhenRouteResult() - { - $this->delegate->process()->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $routedMiddleware = $this->prophesize(ServerMiddlewareInterface::class); - $routedMiddleware - ->process($this->request->reveal(), $this->delegate->reveal()) - ->willReturn($expected); - - $routeResult = RouteResult::fromRoute(new Route('/', $routedMiddleware->reveal())); - - $this->request->getAttribute(RouteResult::class, false)->willReturn($routeResult); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - - $this->assertSame($expected, $response); - } - - /** - * @group 453 - */ - public function testCanDispatchCallableDoublePassMiddleware() - { - $this->delegate->process()->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $routedMiddleware = $this->prophesize(ServerMiddlewareInterface::class); - $routedMiddleware - ->process( - Argument::that([$this->request, 'reveal']), - Argument::that([$this->delegate, 'reveal']) - )->willReturn($expected); - - $routeResult = RouteResult::fromRoute(new Route('/', $routedMiddleware->reveal())); - - $this->request->getAttribute(RouteResult::class, false)->willReturn($routeResult); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - - $this->assertSame($expected, $response); - } - - /** - * @group 453 - */ - public function testCanDispatchLazyMiddleware() - { - $this->delegate->process()->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $routedMiddleware = $this->prophesize(ServerMiddlewareInterface::class); - $routedMiddleware - ->process( - Argument::that([$this->request, 'reveal']), - Argument::that([$this->delegate, 'reveal']) - )->willReturn($expected); - - $this->container->has('RoutedMiddleware')->willReturn(true); - $this->container->get('RoutedMiddleware')->will([$routedMiddleware, 'reveal']); - - // Since 2.0, we never have service names in routes, only lazy-loading middleware - $lazyMiddleware = new LazyLoadingMiddleware( - $this->container->reveal(), - $this->responsePrototype->reveal(), - 'RoutedMiddleware' - ); - - $routeResult = RouteResult::fromRoute(new Route('/', $lazyMiddleware)); - - $this->request->getAttribute(RouteResult::class, false)->willReturn($routeResult); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - - $this->assertSame($expected, $response); - } -} diff --git a/test/Middleware/RouteMiddlewareTest.php b/test/Middleware/RouteMiddlewareTest.php deleted file mode 100644 index 672f167b..00000000 --- a/test/Middleware/RouteMiddlewareTest.php +++ /dev/null @@ -1,111 +0,0 @@ -router = $this->prophesize(RouterInterface::class); - $this->response = $this->prophesize(ResponseInterface::class); - $this->middleware = new RouteMiddleware( - $this->router->reveal(), - $this->response->reveal() - ); - - $this->request = $this->prophesize(ServerRequestInterface::class); - $this->delegate = $this->prophesize(DelegateInterface::class); - } - - public function testRoutingFailureDueToHttpMethodCallsNextWithNotAllowedResponseAndError() - { - $result = RouteResult::fromRouteFailure(['GET', 'POST']); - - $this->router->match($this->request->reveal())->willReturn($result); - $this->delegate->process()->shouldNotBeCalled(); - $this->request->withAttribute()->shouldNotBeCalled(); - $this->response->withStatus(StatusCode::STATUS_METHOD_NOT_ALLOWED)->will([$this->response, 'reveal']); - $this->response->withHeader('Allow', 'GET,POST')->will([$this->response, 'reveal']); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - $this->assertSame($response, $this->response->reveal()); - } - - public function testGeneralRoutingFailureInvokesDelegateWithSameRequest() - { - $result = RouteResult::fromRouteFailure(); - - $this->router->match($this->request->reveal())->willReturn($result); - $this->response->withStatus()->shouldNotBeCalled(); - $this->response->withHeader()->shouldNotBeCalled(); - $this->request->withAttribute()->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $this->delegate->process($this->request->reveal())->willReturn($expected); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - $this->assertSame($expected, $response); - } - - public function testRoutingSuccessDelegatesToNextAfterFirstInjectingRouteResultAndAttributesInRequest() - { - $middleware = $this->prophesize(ServerMiddlewareInterface::class)->reveal(); - $parameters = ['foo' => 'bar', 'baz' => 'bat']; - $result = RouteResult::fromRoute( - new Route('/foo', $middleware), - $parameters - ); - - $this->router->match($this->request->reveal())->willReturn($result); - - $this->request - ->withAttribute(RouteResult::class, $result) - ->will([$this->request, 'reveal']); - foreach ($parameters as $key => $value) { - $this->request->withAttribute($key, $value)->will([$this->request, 'reveal']); - } - - $this->response->withStatus()->shouldNotBeCalled(); - $this->response->withHeader()->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class)->reveal(); - $this->delegate - ->process($this->request->reveal()) - ->willReturn($expected); - - $response = $this->middleware->process($this->request->reveal(), $this->delegate->reveal()); - $this->assertSame($expected, $response); - } -} From 2793ac23dc69634f5983f434d0b5ceeaa5aba2f3 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 16:28:20 -0600 Subject: [PATCH 07/35] Use zend-expressive-router RouteMiddleware and DispatchMiddleware Internally, we're now using the zend-expressive-router route and dispatch middleware instead of the ones in this package. This primarily affects test expectations, which were looking for the previous variants. The previous variants will continue to work at this time, as they extend the zend-expressive-router versions. --- src/Application.php | 8 ++-- src/MarshalMiddlewareTrait.php | 45 ++++++++++++++++--- test/Application/ConfigInjectionTest.php | 31 ++++++++++--- .../MarshalMiddlewareTraitTest.php | 32 ++++++++++--- test/ApplicationTest.php | 26 +++++++++-- test/Container/ApplicationFactoryTest.php | 21 ++++++++- test/Router/IntegrationTest.php | 16 +++++++ 7 files changed, 148 insertions(+), 31 deletions(-) diff --git a/src/Application.php b/src/Application.php index 45880fd2..cd59bc4c 100644 --- a/src/Application.php +++ b/src/Application.php @@ -224,11 +224,11 @@ public function pipe($path, $middleware = null) ); } - if ($middleware instanceof Middleware\RouteMiddleware && $this->routeMiddlewareIsRegistered) { + if ($middleware instanceof Router\Middleware\RouteMiddleware && $this->routeMiddlewareIsRegistered) { return $this; } - if ($middleware instanceof Middleware\DispatchMiddleware && $this->dispatchMiddlewareIsRegistered) { + if ($middleware instanceof Router\Middleware\DispatchMiddleware && $this->dispatchMiddlewareIsRegistered) { return $this; } @@ -238,11 +238,11 @@ public function pipe($path, $middleware = null) parent::pipe($middleware); - if ($middleware instanceof Middleware\RouteMiddleware) { + if ($middleware instanceof Router\Middleware\RouteMiddleware) { $this->routeMiddlewareIsRegistered = true; } - if ($middleware instanceof Middleware\DispatchMiddleware) { + if ($middleware instanceof Router\Middleware\DispatchMiddleware) { $this->dispatchMiddlewareIsRegistered = true; } diff --git a/src/MarshalMiddlewareTrait.php b/src/MarshalMiddlewareTrait.php index 76b8bc89..0268b02b 100644 --- a/src/MarshalMiddlewareTrait.php +++ b/src/MarshalMiddlewareTrait.php @@ -7,9 +7,11 @@ namespace Zend\Expressive; -use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface; use Psr\Container\ContainerInterface; use Psr\Http\Message\ResponseInterface; +use Webimpress\HttpMiddlewareCompatibility\MiddlewareInterface; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Stratigility\Middleware\CallableInteropMiddlewareWrapper; use Zend\Stratigility\Middleware\CallableMiddlewareWrapper; use Zend\Stratigility\MiddlewarePipe; @@ -48,14 +50,20 @@ private function prepareMiddleware( ContainerInterface $container = null ) { if ($middleware === Application::ROUTING_MIDDLEWARE) { - return new Middleware\RouteMiddleware($router, $responsePrototype); + $this->triggerLegacyMiddlewareDeprecation($middleware); + return $container && $container->has(RouteMiddleware::class) + ? $container->get(RouteMiddleware::class) + : new RouteMiddleware($router, $responsePrototype); } if ($middleware === Application::DISPATCH_MIDDLEWARE) { - return new Middleware\DispatchMiddleware($router, $responsePrototype, $container); + $this->triggerLegacyMiddlewareDeprecation($middleware); + return $container && $container->has(DispatchMiddleware::class) + ? $container->get(DispatchMiddleware::class) + : new DispatchMiddleware(); } - if ($middleware instanceof ServerMiddlewareInterface) { + if ($middleware instanceof MiddlewareInterface) { return $middleware; } @@ -80,9 +88,8 @@ private function prepareMiddleware( } throw new Exception\InvalidMiddlewareException(sprintf( - 'Unable to resolve middleware "%s" to a callable or %s', - is_object($middleware) ? get_class($middleware) . '[Object]' : gettype($middleware) . '[Scalar]', - ServerMiddlewareInterface::class + 'Unable to resolve middleware "%s" to a callable or MiddlewareInterface implementation', + is_object($middleware) ? get_class($middleware) . '[Object]' : gettype($middleware) . '[Scalar]' )); } @@ -162,4 +169,28 @@ private function marshalInvokableMiddleware($middleware, ResponseInterface $resp return new CallableMiddlewareWrapper($instance, $responsePrototype); } + + private function triggerLegacyMiddlewareDeprecation($middlewareType) + { + switch ($middlewareType) { + case (Application::ROUTING_MIDDLEWARE): + $constant = sprintf('%s::ROUTING_MIDDLEWARE', Application::class); + $type = 'routing'; + $useInstead = RouteMiddleware::class; + break; + case (Application::DISPATCH_MIDDLEWARE): + $constant = sprintf('%s::DISPATCH_MIDDLEWARE', Application::class); + $type = 'dispatch'; + $useInstead = DispatchMiddleware::class; + break; + } + + trigger_error(sprintf( + 'Usage of the %s constant for specifying %s middleware is deprecated;' + . ' pipe() the middleware directly, or reference it by its service name "%s"', + $constant, + $type, + $useInstead + ), E_USER_DEPRECATED); + } } diff --git a/test/Application/ConfigInjectionTest.php b/test/Application/ConfigInjectionTest.php index ffabb81f..de866908 100644 --- a/test/Application/ConfigInjectionTest.php +++ b/test/Application/ConfigInjectionTest.php @@ -16,7 +16,8 @@ use SplQueue; use Zend\Expressive\Application; use Zend\Expressive\Exception\InvalidArgumentException; -use Zend\Expressive\Middleware; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\Route; use Zend\Expressive\Router\RouterInterface; use Zend\Stratigility\MiddlewarePipe; @@ -40,6 +41,22 @@ public function setUp() { $this->container = $this->mockContainerInterface(); $this->router = $this->prophesize(RouterInterface::class); + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); + } + + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'pipe() the middleware directly')) { + return true; + } + return false; + }, E_USER_DEPRECATED); } public function createApplication() @@ -209,11 +226,11 @@ public function testProvidingRoutesAndNoPipelineImplicitlyRegistersRoutingAndDis $test = $pipeline->dequeue(); $this->assertEquals('/', $test->path); - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $test->handler); + $this->assertInstanceOf(RouteMiddleware::class, $test->handler); $test = $pipeline->dequeue(); $this->assertEquals('/', $test->path); - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $test->handler); + $this->assertInstanceOf(DispatchMiddleware::class, $test->handler); } public function testPipelineContainingRoutingMiddlewareConstantPipesRoutingMiddleware() @@ -316,10 +333,10 @@ public function testRoutingAndDispatchMiddlewareUseDefaultPriority() $test = $r->getValue($app); $this->assertSame($pipeline[5]['middleware'], $test->dequeue()->handler); - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $test->dequeue()->handler); + $this->assertInstanceOf(RouteMiddleware::class, $test->dequeue()->handler); $this->assertSame($pipeline[2]['middleware'], $test->dequeue()->handler); $this->assertSame($pipeline[3]['middleware'], $test->dequeue()->handler); - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $test->dequeue()->handler); + $this->assertInstanceOf(DispatchMiddleware::class, $test->dequeue()->handler); $this->assertSame($pipeline[0]['middleware'], $test->dequeue()->handler); } @@ -375,11 +392,11 @@ public function testRoutingAndDispatchMiddlewareCanBeComposedWithinArrayStandard foreach ($expected as $type) { switch ($type) { case Application::ROUTING_MIDDLEWARE: - $middleware = Middleware\RouteMiddleware::class; + $middleware = RouteMiddleware::class; $message = 'Did not find routing middleware in pipeline'; break; case Application::DISPATCH_MIDDLEWARE: - $middleware = Middleware\DispatchMiddleware::class; + $middleware = DispatchMiddleware::class; $message = 'Did not find dispatch middleware in pipeline'; break; default: diff --git a/test/Application/MarshalMiddlewareTraitTest.php b/test/Application/MarshalMiddlewareTraitTest.php index 12806b96..33ab4882 100644 --- a/test/Application/MarshalMiddlewareTraitTest.php +++ b/test/Application/MarshalMiddlewareTraitTest.php @@ -19,7 +19,9 @@ use stdClass; use Zend\Expressive\Application; use Zend\Expressive\Exception\InvalidMiddlewareException; -use Zend\Expressive\Middleware; +use Zend\Expressive\Middleware\LazyLoadingMiddleware; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\RouterInterface; use Zend\Stratigility\Middleware\CallableInteropMiddlewareWrapper; use Zend\Stratigility\Middleware\CallableMiddlewareWrapper; @@ -45,6 +47,22 @@ public function setUp() $this->router = $this->prophesize(RouterInterface::class); $this->responsePrototype = $this->prophesize(ResponseInterface::class); $this->application = new Application($this->router->reveal()); + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); + } + + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'pipe() the middleware directly')) { + return true; + } + return false; + }, E_USER_DEPRECATED); } public function prepareMiddleware($middleware) @@ -75,7 +93,7 @@ public function prepareMiddlewareWithoutContainer($middleware) public function testPreparingRoutingMiddlewareReturnsRoutingMiddleware() { $middleware = $this->prepareMiddleware(Application::ROUTING_MIDDLEWARE); - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $middleware); + $this->assertInstanceOf(RouteMiddleware::class, $middleware); $this->assertAttributeSame($this->router->reveal(), 'router', $middleware); $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } @@ -83,7 +101,7 @@ public function testPreparingRoutingMiddlewareReturnsRoutingMiddleware() public function testPreparingRoutingMiddlewareWithoutContainerReturnsRoutingMiddleware() { $middleware = $this->prepareMiddlewareWithoutContainer(Application::ROUTING_MIDDLEWARE); - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $middleware); + $this->assertInstanceOf(RouteMiddleware::class, $middleware); $this->assertAttributeSame($this->router->reveal(), 'router', $middleware); $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } @@ -91,13 +109,13 @@ public function testPreparingRoutingMiddlewareWithoutContainerReturnsRoutingMidd public function testPreparingDispatchMiddlewareReturnsDispatchMiddleware() { $middleware = $this->prepareMiddleware(Application::DISPATCH_MIDDLEWARE); - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $middleware); + $this->assertInstanceOf(DispatchMiddleware::class, $middleware); } public function testPreparingDispatchMiddlewareWithoutContainerReturnsDispatchMiddleware() { $middleware = $this->prepareMiddlewareWithoutContainer(Application::DISPATCH_MIDDLEWARE); - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $middleware); + $this->assertInstanceOf(DispatchMiddleware::class, $middleware); } public function testPreparingInteropMiddlewareReturnsMiddlewareVerbatim() @@ -239,7 +257,7 @@ public function testPreparingServiceBasedMiddlewareReturnsLazyLoadingMiddleware( $this->container->has($middlewareName)->willReturn(true); $middleware = $this->prepareMiddleware($middlewareName); - $this->assertInstanceOf(Middleware\LazyLoadingMiddleware::class, $middleware); + $this->assertInstanceOf(LazyLoadingMiddleware::class, $middleware); $this->assertAttributeSame($this->container->reveal(), 'container', $middleware); $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); @@ -282,7 +300,7 @@ public function testPreparingInvokableInteropMiddlewareThatIsRegisteredInContain $this->container->has(TestAsset\CallableMiddleware::class)->willReturn(true); $middleware = $this->prepareMiddleware($base); - $this->assertInstanceOf(Middleware\LazyLoadingMiddleware::class, $middleware); + $this->assertInstanceOf(LazyLoadingMiddleware::class, $middleware); $this->assertAttributeEquals($base, 'middlewareName', $middleware); } diff --git a/test/ApplicationTest.php b/test/ApplicationTest.php index a9c75568..6ce96bbb 100644 --- a/test/ApplicationTest.php +++ b/test/ApplicationTest.php @@ -38,6 +38,8 @@ use Zend\Expressive\Exception\InvalidMiddlewareException; use Zend\Expressive\Middleware; use Zend\Expressive\Router\Exception as RouterException; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\Route; use Zend\Expressive\Router\RouteResult; use Zend\Expressive\Router\RouterInterface; @@ -64,6 +66,22 @@ public function setUp() { $this->noopMiddleware = new TestAsset\InteropMiddleware(); $this->router = $this->prophesize(RouterInterface::class); + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); + } + + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'pipe() the middleware directly')) { + return true; + } + return false; + }, E_USER_DEPRECATED); } public function getApp() @@ -262,7 +280,7 @@ public function testCannotPipeRouteMiddlewareMoreThanOnce() $this->assertInstanceOf(StratigilityRoute::class, $route); $test = $route->handler; - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $test); + $this->assertInstanceOf(RouteMiddleware::class, $test); } public function testCannotPipeDispatchMiddlewareMoreThanOnce() @@ -282,7 +300,7 @@ public function testCannotPipeDispatchMiddlewareMoreThanOnce() $this->assertInstanceOf(StratigilityRoute::class, $route); $test = $route->handler; - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $test); + $this->assertInstanceOf(DispatchMiddleware::class, $test); } public function testCanInjectDefaultDelegateViaConstructor() @@ -391,7 +409,7 @@ public function testCanTriggerPipingOfRouteMiddleware() $route = $pipeline->dequeue(); $this->assertInstanceOf(StratigilityRoute::class, $route); - $this->assertInstanceOf(Middleware\RouteMiddleware::class, $route->handler); + $this->assertInstanceOf(RouteMiddleware::class, $route->handler); $this->assertEquals('/', $route->path); } @@ -408,7 +426,7 @@ public function testCanTriggerPipingOfDispatchMiddleware() $route = $pipeline->dequeue(); $this->assertInstanceOf(StratigilityRoute::class, $route); - $this->assertInstanceOf(Middleware\DispatchMiddleware::class, $route->handler); + $this->assertInstanceOf(DispatchMiddleware::class, $route->handler); $this->assertEquals('/', $route->path); } diff --git a/test/Container/ApplicationFactoryTest.php b/test/Container/ApplicationFactoryTest.php index ffdfaee6..521b6c95 100644 --- a/test/Container/ApplicationFactoryTest.php +++ b/test/Container/ApplicationFactoryTest.php @@ -22,9 +22,9 @@ use Zend\Expressive\Delegate\NotFoundDelegate; use Zend\Expressive\Emitter\EmitterStack; use Zend\Expressive\Exception as ExpressiveException; -use Zend\Expressive\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; use Zend\Expressive\Middleware\LazyLoadingMiddleware; -use Zend\Expressive\Middleware\RouteMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\FastRouteRouter; use Zend\Expressive\Router\Route; use Zend\Expressive\Router\RouterInterface; @@ -68,6 +68,23 @@ public function setUp() $this->injectServiceInContainer($this->container, RouterInterface::class, $this->router->reveal()); $this->injectServiceInContainer($this->container, EmitterInterface::class, $this->emitter->reveal()); $this->injectServiceInContainer($this->container, 'Zend\Expressive\Delegate\DefaultDelegate', $this->delegate); + + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); + } + + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'pipe() the middleware directly')) { + return true; + } + return false; + }, E_USER_DEPRECATED); } public static function assertRoute($spec, array $routes) diff --git a/test/Router/IntegrationTest.php b/test/Router/IntegrationTest.php index fc435bd2..f5462a89 100644 --- a/test/Router/IntegrationTest.php +++ b/test/Router/IntegrationTest.php @@ -43,6 +43,22 @@ public function setUp() $this->response = new Response(); $this->router = $this->prophesize(RouterInterface::class); $this->container = $this->mockContainerInterface(); + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); + } + + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'pipe() the middleware directly')) { + return true; + } + return false; + }, E_USER_DEPRECATED); } public function getApplication() From b897735a263c125051c6f404b9cb10da2286283e Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 16:29:55 -0600 Subject: [PATCH 08/35] Adds symfony/finder to PHP 7 legacy deps for locked target --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 8966aab0..b53f07b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -31,7 +31,7 @@ matrix: - php: 7 env: - DEPS=locked - - LEGACY_DEPS="phpunit/phpunit symfony/console symfony/debug" + - LEGACY_DEPS="phpunit/phpunit symfony/console symfony/debug symfony/finder" - php: 7 env: - DEPS=latest From b05fd945fc1e575c90d20c7e5d1e720e66210918 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:02:37 -0600 Subject: [PATCH 09/35] Use utility functions from zend-stratigility to decorate callable middleware Uses each of the `middleware()` and `doublePassMiddleware()` functions from zend-stratigility in order to decorate the different styles of callable middleware. When double pass middleware is encountered, it triggers a deprecation notice. --- src/MarshalMiddlewareTrait.php | 51 +++++++++++++++---- test/Application/ConfigInjectionTest.php | 3 ++ .../MarshalMiddlewareTraitTest.php | 19 ++++--- test/Container/ApplicationFactoryTest.php | 3 ++ test/Router/IntegrationTest.php | 3 ++ 5 files changed, 60 insertions(+), 19 deletions(-) diff --git a/src/MarshalMiddlewareTrait.php b/src/MarshalMiddlewareTrait.php index 0268b02b..d6a4e592 100644 --- a/src/MarshalMiddlewareTrait.php +++ b/src/MarshalMiddlewareTrait.php @@ -12,10 +12,11 @@ use Webimpress\HttpMiddlewareCompatibility\MiddlewareInterface; use Zend\Expressive\Router\Middleware\DispatchMiddleware; use Zend\Expressive\Router\Middleware\RouteMiddleware; -use Zend\Stratigility\Middleware\CallableInteropMiddlewareWrapper; -use Zend\Stratigility\Middleware\CallableMiddlewareWrapper; use Zend\Stratigility\MiddlewarePipe; +use function Zend\Stratigility\middleware; +use function Zend\Stratigility\doublePassMiddleware; + /** * Trait defining methods for verifying and/or generating middleware to pipe to * an application. @@ -40,7 +41,7 @@ trait MarshalMiddlewareTrait * @param Router\RouterInterface $router * @param ResponseInterface $responsePrototype * @param null|ContainerInterface $container - * @return ServerMiddlewareInterface + * @return MiddlewareInterface * @throws Exception\InvalidMiddlewareException */ private function prepareMiddleware( @@ -68,11 +69,12 @@ private function prepareMiddleware( } if ($this->isCallableInteropMiddleware($middleware)) { - return new CallableInteropMiddlewareWrapper($middleware); + return middleware($middleware); } if ($this->isCallable($middleware)) { - return new CallableMiddlewareWrapper($middleware, $responsePrototype); + $this->triggerDoublePassMiddlewareDeprecation($middleware); + return doublePassMiddleware($middleware, $responsePrototype); } if (is_array($middleware)) { @@ -151,25 +153,29 @@ private function marshalInvokableMiddleware($middleware, ResponseInterface $resp $instance = new $middleware(); - if ($instance instanceof ServerMiddlewareInterface) { + if ($instance instanceof MiddlewareInterface) { return $instance; } if ($this->isCallableInteropMiddleware($instance)) { - return new CallableInteropMiddlewareWrapper($instance); + return middleware($instance); } if (! is_callable($instance)) { throw new Exception\InvalidMiddlewareException(sprintf( - 'Middleware of class "%s" is invalid; neither invokable nor %s', - $middleware, - ServerMiddlewareInterface::class + 'Middleware of class "%s" is invalid; neither invokable nor a MiddlewareInterface instance', + $middleware )); } - return new CallableMiddlewareWrapper($instance, $responsePrototype); + $this->triggerDoublePassMiddlewareDeprecation($instance); + return doublePassMiddleware($instance, $responsePrototype); } + /** + * @param string $middlewareType + * @return void + */ private function triggerLegacyMiddlewareDeprecation($middlewareType) { switch ($middlewareType) { @@ -193,4 +199,27 @@ private function triggerLegacyMiddlewareDeprecation($middlewareType) $useInstead ), E_USER_DEPRECATED); } + + /** + * @param callable $middleware + * @return void + */ + private function triggerDoublePassMiddlewareDeprecation(callable $middleware) + { + if (is_object($middleware)) { + $type = get_class($middleware); + } elseif (is_string($middleware)) { + $type = 'callable:' . $middleware; + } else { + $type = 'callable'; + } + + trigger_error(sprintf( + 'Detected double-pass middleware (%s).' + . ' Usage of callable double-pass middleware is deprecated. Before piping or routing' + . ' such middleware, pass it to Zend\Stratigility\doublePassMiddleware(), along with' + . ' a PSR-7 response instance.', + $type + ), E_USER_DEPRECATED); + } } diff --git a/test/Application/ConfigInjectionTest.php b/test/Application/ConfigInjectionTest.php index de866908..4cf0d344 100644 --- a/test/Application/ConfigInjectionTest.php +++ b/test/Application/ConfigInjectionTest.php @@ -55,6 +55,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'pipe() the middleware directly')) { return true; } + if (strstr($errstr, 'doublePassMiddleware()')) { + return true; + } return false; }, E_USER_DEPRECATED); } diff --git a/test/Application/MarshalMiddlewareTraitTest.php b/test/Application/MarshalMiddlewareTraitTest.php index 33ab4882..f34bbb37 100644 --- a/test/Application/MarshalMiddlewareTraitTest.php +++ b/test/Application/MarshalMiddlewareTraitTest.php @@ -23,8 +23,8 @@ use Zend\Expressive\Router\Middleware\DispatchMiddleware; use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\RouterInterface; -use Zend\Stratigility\Middleware\CallableInteropMiddlewareWrapper; -use Zend\Stratigility\Middleware\CallableMiddlewareWrapper; +use Zend\Stratigility\Middleware\CallableMiddlewareDecorator; +use Zend\Stratigility\Middleware\DoublePassMiddlewareDecorator; use Zend\Stratigility\MiddlewarePipe; class MarshalMiddlewareTraitTest extends TestCase @@ -61,6 +61,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'pipe() the middleware directly')) { return true; } + if (strstr($errstr, 'doublePassMiddleware()')) { + return true; + } return false; }, E_USER_DEPRECATED); } @@ -137,7 +140,7 @@ public function testPreparingDuckTypedInteropMiddlewareReturnsDecoratedInteropMi $base = function ($request, DelegateInterface $delegate) { }; $middleware = $this->prepareMiddleware($base); - $this->assertInstanceOf(CallableInteropMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(CallableMiddlewareDecorator::class, $middleware); $this->assertAttributeSame($base, 'middleware', $middleware); } @@ -146,7 +149,7 @@ public function testPreparingDuckTypedInteropMiddlewareWithoutContainerReturnsDe $base = function ($request, DelegateInterface $delegate) { }; $middleware = $this->prepareMiddlewareWithoutContainer($base); - $this->assertInstanceOf(CallableInteropMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(CallableMiddlewareDecorator::class, $middleware); $this->assertAttributeSame($base, 'middleware', $middleware); } @@ -155,7 +158,7 @@ public function testPreparingCallableMiddlewareReturnsDecoratedMiddleware() $base = function ($request, $response, callable $next) { }; $middleware = $this->prepareMiddleware($base); - $this->assertInstanceOf(CallableMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(DoublePassMiddlewareDecorator::class, $middleware); $this->assertAttributeSame($base, 'middleware', $middleware); $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } @@ -165,7 +168,7 @@ public function testPreparingCallableMiddlewareWithoutContainerReturnsDecoratedM $base = function ($request, $response, callable $next) { }; $middleware = $this->prepareMiddlewareWithoutContainer($base); - $this->assertInstanceOf(CallableMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(DoublePassMiddlewareDecorator::class, $middleware); $this->assertAttributeSame($base, 'middleware', $middleware); $this->assertAttributeSame($this->responsePrototype->reveal(), 'responsePrototype', $middleware); } @@ -271,7 +274,7 @@ public function testPreparingInvokableInteropMiddlewareReturnsDecoratedInteropMi $middleware = $this->prepareMiddleware($base); - $this->assertInstanceOf(CallableInteropMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(CallableMiddlewareDecorator::class, $middleware); $this->assertAttributeInstanceOf(TestAsset\CallableInteropMiddleware::class, 'middleware', $middleware); } @@ -280,7 +283,7 @@ public function testPreparingInvokableCallableMiddlewareReturnsDecoratedMiddlewa $base = TestAsset\CallableMiddleware::class; $this->container->has(TestAsset\CallableMiddleware::class)->willReturn(false); $middleware = $this->prepareMiddleware($base); - $this->assertInstanceOf(CallableMiddlewareWrapper::class, $middleware); + $this->assertInstanceOf(DoublePassMiddlewareDecorator::class, $middleware); $this->assertAttributeInstanceOf(TestAsset\CallableMiddleware::class, 'middleware', $middleware); } diff --git a/test/Container/ApplicationFactoryTest.php b/test/Container/ApplicationFactoryTest.php index 521b6c95..b712478a 100644 --- a/test/Container/ApplicationFactoryTest.php +++ b/test/Container/ApplicationFactoryTest.php @@ -83,6 +83,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'pipe() the middleware directly')) { return true; } + if (strstr($errstr, 'doublePassMiddleware()')) { + return true; + } return false; }, E_USER_DEPRECATED); } diff --git a/test/Router/IntegrationTest.php b/test/Router/IntegrationTest.php index f5462a89..3d134a60 100644 --- a/test/Router/IntegrationTest.php +++ b/test/Router/IntegrationTest.php @@ -57,6 +57,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'pipe() the middleware directly')) { return true; } + if (strstr($errstr, 'doublePassMiddleware()')) { + return true; + } return false; }, E_USER_DEPRECATED); } From 0fc4155e97be935efe6dd413a2398bbf6411ab43 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:04:53 -0600 Subject: [PATCH 10/35] Mark MarshalMiddlewareTrait as deprecated --- src/MarshalMiddlewareTrait.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/MarshalMiddlewareTrait.php b/src/MarshalMiddlewareTrait.php index d6a4e592..3e21b0af 100644 --- a/src/MarshalMiddlewareTrait.php +++ b/src/MarshalMiddlewareTrait.php @@ -20,6 +20,11 @@ /** * Trait defining methods for verifying and/or generating middleware to pipe to * an application. + * + * @deprecated since 2.2.0. This feature will be removed in version 3.0.0, and + * replaced with a combination of a PSR-11 container and a composable factory + * class. + * @internal */ trait MarshalMiddlewareTrait { From c2313655e6e8f485a60e514b0eb2cc265a58d388 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:11:16 -0600 Subject: [PATCH 11/35] Marks deprecations in Application class - `pipeRoutingMiddleware()`; no deprecations are triggered, as the ones in `MarshalMiddlewareTrait` will be triggered. - `pipeDispatchMiddleware()`; no deprecations are triggered, as the ones in `MarshalMiddlewareTrait` will be triggered. - `getContainer()` - `getDefaultDelegate()` - `getEmitter()` --- src/Application.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/Application.php b/src/Application.php index cd59bc4c..ef9ec9b2 100644 --- a/src/Application.php +++ b/src/Application.php @@ -252,6 +252,8 @@ public function pipe($path, $middleware = null) /** * Register the routing middleware in the middleware pipeline. * + * @deprecated since 2.2.0; to be removed in 3.0.0. Use pipe() with routing + * middleware or a service name resolving to routing middleware instead. * @return void */ public function pipeRoutingMiddleware() @@ -265,6 +267,8 @@ public function pipeRoutingMiddleware() /** * Register the dispatch middleware in the middleware pipeline. * + * @deprecated since 2.2.0; to be removed in 3.0.0. Use pipe() with dispatch + * middleware or a service name resolving to dispatch middleware instead. * @return void */ public function pipeDispatchMiddleware() @@ -382,6 +386,9 @@ public function run(ServerRequestInterface $request = null, ResponseInterface $r * * If no IoC container is registered, we raise an exception. * + * @deprecated since 2.2.0; to be removed in 3.0.0. This feature is + * replaced by Zend\Expressive\MiddlewareFactory in that release, which + * can be retrieved as a service from the application container. * @return ContainerInterface * @throws Exception\ContainerNotRegisteredException */ @@ -403,6 +410,8 @@ public function getContainer() * - If no container is composed, creates an instance of Delegate\NotFoundDelegate * using the current response prototype only (i.e., no templating). * + * @deprecated since 2.2.0; to be removed in 3.0.0. This feature has no + * equivalent in that version. * @return DelegateInterface */ public function getDefaultDelegate() @@ -432,6 +441,9 @@ public function getDefaultDelegate() * If none was registered during instantiation, this will lazy-load an * EmitterStack composing an SapiEmitter instance. * + * @deprecated since 2.2.0; to be removed in 3.0.0. This feature has no + * equivalent in that version; the responsibility has been moved to a + * new collaborator. * @return EmitterInterface */ public function getEmitter() From 0befa813f3e4f56ab02ed71e6ad2f52bf0777b9e Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:17:25 -0600 Subject: [PATCH 12/35] Marks AppFactory as deprecated --- src/AppFactory.php | 13 +++++++++++++ test/AppFactoryTest.php | 21 ++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/AppFactory.php b/src/AppFactory.php index e7606089..cfe185dc 100644 --- a/src/AppFactory.php +++ b/src/AppFactory.php @@ -10,6 +10,16 @@ use Psr\Container\ContainerInterface; use Zend\Diactoros\Response\SapiEmitter; use Zend\ServiceManager\ServiceManager; +use Zend\Stratigility\MiddlewarePipe; + +trigger_error(sprintf( + 'Usage of %s is deprecated as of version 2.2.0. In version 3, you will need' + . ' to either instantiate an %s instance directly, or use a %s instance' + . ' with other collaborators.', + AppFactory::class, + Application::class, + MiddlewarePipe::class +), E_USER_DEPRECATED); /** * Create and return an Application instance. @@ -20,6 +30,9 @@ * The Application instance returned is guaranteed to have a router, a * container, and an emitter stack; by default, the FastRoute router and the * ZF2 ServiceManager are used. + * + * @deprecated since 2.2.0; will be removed in 3.0.0. Directly instantiate a + * Zend\Expressive\Application instance instead. */ final class AppFactory { diff --git a/test/AppFactoryTest.php b/test/AppFactoryTest.php index d1f06fb0..117ed02b 100644 --- a/test/AppFactoryTest.php +++ b/test/AppFactoryTest.php @@ -27,11 +27,30 @@ class AppFactoryTest extends TestCase { static public $existingClasses; - protected function tearDown() + public function setUp() { + $this->noopMiddleware = new TestAsset\InteropMiddleware(); + $this->router = $this->prophesize(RouterInterface::class); + $this->disregardDeprecationNotices(); + } + + public function tearDown() + { + restore_error_handler(); self::$existingClasses = null; } + public function disregardDeprecationNotices() + { + set_error_handler(function ($errno, $errstr) { + if (strstr($errstr, 'AppFactory is deprecated')) { + return true; + } + return false; + }, E_USER_DEPRECATED); + } + + public function getRouterFromApplication(Application $app) { $r = new ReflectionProperty($app, 'router'); From 1124d24f32bdf91aebf07554e3010dc78e0fc4d3 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:21:21 -0600 Subject: [PATCH 13/35] Marks NotFoundDelegateFactory as deprecated --- src/Container/NotFoundDelegateFactory.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Container/NotFoundDelegateFactory.php b/src/Container/NotFoundDelegateFactory.php index fb34d834..df0cc58f 100644 --- a/src/Container/NotFoundDelegateFactory.php +++ b/src/Container/NotFoundDelegateFactory.php @@ -12,6 +12,11 @@ use Zend\Expressive\Delegate\NotFoundDelegate; use Zend\Expressive\Template\TemplateRendererInterface; +/** + * @deprecated since 2.2.0; to be removed in 3.0.0. Use NotFoundHandlerFactory + * in version 3, as it will return its replacement, the + * Zend\Expressive\Handler\NotFoundHandler. + */ class NotFoundDelegateFactory { /** From cca13e805b05d31e418af83923d2732201538c00 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:23:54 -0600 Subject: [PATCH 14/35] Marks EmitterStack as deprecated --- src/Delegate/NotFoundDelegate.php | 4 ++++ src/Emitter/EmitterStack.php | 3 +++ 2 files changed, 7 insertions(+) diff --git a/src/Delegate/NotFoundDelegate.php b/src/Delegate/NotFoundDelegate.php index ee3431f7..79c8ec81 100644 --- a/src/Delegate/NotFoundDelegate.php +++ b/src/Delegate/NotFoundDelegate.php @@ -13,6 +13,10 @@ use Psr\Http\Message\ServerRequestInterface; use Zend\Expressive\Template\TemplateRendererInterface; +/** + * @deprecated since 2.2.0; to be removed in 3.0.0. Use + * Zend\Expressive\Handler\NotFoundHandler instead, available since 2.2.0. + */ class NotFoundDelegate implements DelegateInterface { const TEMPLATE_DEFAULT = 'error::404'; diff --git a/src/Emitter/EmitterStack.php b/src/Emitter/EmitterStack.php index 2387dfcc..412b7a72 100644 --- a/src/Emitter/EmitterStack.php +++ b/src/Emitter/EmitterStack.php @@ -20,6 +20,9 @@ * * When iterating the stack, the first emitter to return a value that is not * identical to boolean false will short-circuit iteration. + * + * @deprecated since 2.2.0; to be removed in 3.0.0. Functionality is moved to + * the zend-httphandlerrunner package. */ class EmitterStack extends SplStack implements EmitterInterface { From cd90af737fc0dfc8c7e4f58e1b93200cdb930b77 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:24:24 -0600 Subject: [PATCH 15/35] Marks IsCallableInteropMiddlewareTrait as deprecated --- src/IsCallableInteropMiddlewareTrait.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/IsCallableInteropMiddlewareTrait.php b/src/IsCallableInteropMiddlewareTrait.php index 88876a54..a99a3e30 100644 --- a/src/IsCallableInteropMiddlewareTrait.php +++ b/src/IsCallableInteropMiddlewareTrait.php @@ -11,6 +11,10 @@ use ReflectionFunction; use ReflectionMethod; +/** + * @deprecated since 2.2.0; to be removed in 3.0.0. + * @internal + */ trait IsCallableInteropMiddlewareTrait { /** From 845b5937b308b1f19fc222ab2223a622711916ee Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Thu, 8 Mar 2018 17:28:52 -0600 Subject: [PATCH 16/35] Fixes CS issues flagged by phpcs --- phpcs.xml | 4 ++++ src/MarshalMiddlewareTrait.php | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/phpcs.xml b/phpcs.xml index 25521bf1..2bebf4c4 100644 --- a/phpcs.xml +++ b/phpcs.xml @@ -5,4 +5,8 @@ src test + + + src/AppFactory.php + diff --git a/src/MarshalMiddlewareTrait.php b/src/MarshalMiddlewareTrait.php index 3e21b0af..8b3fd1b1 100644 --- a/src/MarshalMiddlewareTrait.php +++ b/src/MarshalMiddlewareTrait.php @@ -218,7 +218,7 @@ private function triggerDoublePassMiddlewareDeprecation(callable $middleware) } else { $type = 'callable'; } - + trigger_error(sprintf( 'Detected double-pass middleware (%s).' . ' Usage of callable double-pass middleware is deprecated. Before piping or routing' From 5349dbd9bca20bd1aa12a4697b566f39a19d3e03 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:07:53 -0600 Subject: [PATCH 17/35] Deprecates ImplicitHeadMiddleware Router integration tests need to swallow the deprecation notice. --- src/Middleware/ImplicitHeadMiddleware.php | 105 +++-------- .../Middleware/ImplicitHeadMiddlewareTest.php | 175 ++---------------- test/Router/IntegrationTest.php | 3 + 3 files changed, 35 insertions(+), 248 deletions(-) diff --git a/src/Middleware/ImplicitHeadMiddleware.php b/src/Middleware/ImplicitHeadMiddleware.php index 9d9afaa3..cbe61fc7 100644 --- a/src/Middleware/ImplicitHeadMiddleware.php +++ b/src/Middleware/ImplicitHeadMiddleware.php @@ -1,55 +1,30 @@ response = $response; - } - - /** - * Handle an implicit HEAD request. - * - * If the route allows GET requests, dispatches as a GET request and - * resets the response body to be empty; otherwise, creates a new empty - * response. - * - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - if ($request->getMethod() !== RequestMethod::METHOD_HEAD) { - return $delegate->process($request); - } - - if (false === ($result = $request->getAttribute(RouteResult::class, false))) { - return $delegate->process($request); - } - - $route = $result->getMatchedRoute(); - if (! $route || ! $route->implicitHead()) { - return $delegate->process($request); - } - - if (! $route->allowsMethod(RequestMethod::METHOD_GET)) { - return $this->getResponse(); - } - - $response = $delegate->process( - $request - ->withMethod(RequestMethod::METHOD_GET) - ->withAttribute(self::FORWARDED_HTTP_METHOD_ATTRIBUTE, RequestMethod::METHOD_HEAD) - ); - - return $response->withBody(new Stream('php://temp/', 'wb+')); - } - - /** - * Return the response prototype to use for an implicit HEAD request. - * - * @return ResponseInterface - */ - private function getResponse() - { - return $this->response ?: new Response(); + trigger_error(sprintf( + '%s is deprecated starting with zend-expressive 2.2.0; please use the %s class' + . ' provided in zend-expressive-router 2.4.0 and later. That class has required' + . ' dependencies, so please also add Zend\Expressive\Router\ConfigProvider to' + . ' your config/config.php file as well.', + __CLASS__, + BaseImplicitHeadMiddleware::class + ), E_USER_DEPRECATED); + + parent::__construct($response ?: new Response(), function () { + return new Stream('php://temp/', 'wb+'); + }); } } diff --git a/test/Middleware/ImplicitHeadMiddlewareTest.php b/test/Middleware/ImplicitHeadMiddlewareTest.php index c340aa73..198f514e 100644 --- a/test/Middleware/ImplicitHeadMiddlewareTest.php +++ b/test/Middleware/ImplicitHeadMiddlewareTest.php @@ -1,184 +1,31 @@ prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_GET); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal()) - ->willReturn($response); - - $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertSame($response, $result); - } - - public function testReturnsResultOfNextWhenNoRouteResultPresentInRequest() - { - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_HEAD); - $request->getAttribute(RouteResult::class, false)->willReturn(false); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal()) - ->willReturn($response); - - $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertSame($response, $result); - } - - public function testReturnsResultOfNextWhenRouteResultDoesNotComposeRoute() - { - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->willReturn(null); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_HEAD); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal()) - ->willReturn($response); - - $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertSame($response, $result); - } - - public function testReturnsResultOfNextWhenRouteSupportsHeadExplicitly() - { - $route = $this->prophesize(Route::class); - $route->implicitHead()->willReturn(false); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_HEAD); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal()) - ->willReturn($response); - - $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertSame($response, $result); - } - - public function testReturnsNewResponseWhenRouteImplicitlySupportsHeadAndDoesNotSupportGet() - { - $route = $this->prophesize(Route::class); - $route->implicitHead()->willReturn(true); - $route->allowsMethod(RequestMethod::METHOD_GET)->willReturn(false); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_HEAD); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->shouldNotBeCalled($response); - - $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertInstanceOf(Response::class, $result); - $this->assertEquals(StatusCode::STATUS_OK, $result->getStatusCode()); - $this->assertEquals('', (string) $result->getBody()); - } - - public function testReturnsComposedResponseWhenPresentWhenRouteImplicitlySupportsHeadAndDoesNotSupportGet() - { - $route = $this->prophesize(Route::class); - $route->implicitHead()->willReturn(true); - $route->allowsMethod(RequestMethod::METHOD_GET)->willReturn(false); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_HEAD); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->shouldNotBeCalled($response); - - $expected = new Response(); - $middleware = new ImplicitHeadMiddleware($expected); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - - $this->assertSame($expected, $result); - } - - public function testInvokesNextWhenRouteImplicitlySupportsHeadAndSupportsGet() - { - $route = $this->prophesize(Route::class); - $route->implicitHead()->willReturn(true); - $route->allowsMethod(RequestMethod::METHOD_GET)->willReturn(true); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = (new ServerRequest([], [], null, RequestMethod::METHOD_HEAD)) - ->withAttribute(RouteResult::class, $result->reveal()); - - $response = new Response\JsonResponse(['some_data' => true], 400); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process(Argument::that(function (ServerRequestInterface $request) { - $attr = $request->getAttribute(ImplicitHeadMiddleware::FORWARDED_HTTP_METHOD_ATTRIBUTE); - $this->assertSame('HEAD', $attr); + $test = (object) ['message' => false]; + set_error_handler(function ($errno, $errstr) use ($test) { + $test->message = $errstr; return true; - }))->willReturn($response); + }, E_USER_DEPRECATED); $middleware = new ImplicitHeadMiddleware(); - $result = $middleware->process($request, $delegate->reveal()); + restore_error_handler(); - $this->assertSame(400, $result->getStatusCode()); - $this->assertSame('', (string) $result->getBody()); - $this->assertSame('application/json', $result->getHeaderLine('content-type')); + $this->assertInstanceOf(BaseImplicitHeadMiddleware::class, $middleware); + $this->assertInternalType('string', $test->message); + $this->assertContains('deprecated starting with zend-expressive 2.2', $test->message); } } diff --git a/test/Router/IntegrationTest.php b/test/Router/IntegrationTest.php index 3d134a60..15d94352 100644 --- a/test/Router/IntegrationTest.php +++ b/test/Router/IntegrationTest.php @@ -60,6 +60,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'doublePassMiddleware()')) { return true; } + if (strstr($errstr, 'ImplicitHeadMiddleware is deprecated')) { + return true; + } return false; }, E_USER_DEPRECATED); } From 4dd48cafefa686daf82a1f55c9c61c3f5929af90 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:15:26 -0600 Subject: [PATCH 18/35] Deprecates ImplicitHeadMiddleware Router integration tests need to swallow the deprecation notice. --- src/Middleware/ImplicitOptionsMiddleware.php | 85 +++-------- .../ImplicitOptionsMiddlewareTest.php | 140 ++---------------- test/Router/IntegrationTest.php | 3 + 3 files changed, 32 insertions(+), 196 deletions(-) diff --git a/src/Middleware/ImplicitOptionsMiddleware.php b/src/Middleware/ImplicitOptionsMiddleware.php index b1a9dbdc..234457f9 100644 --- a/src/Middleware/ImplicitOptionsMiddleware.php +++ b/src/Middleware/ImplicitOptionsMiddleware.php @@ -7,45 +7,23 @@ namespace Zend\Expressive\Middleware; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; -use Fig\Http\Message\StatusCodeInterface as StatusCode; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Interop\Http\ServerMiddleware\MiddlewareInterface as ServerMiddlewareInterface; use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; use Zend\Diactoros\Response; -use Zend\Expressive\Router\RouteResult; +use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware as BaseImplicitOptionsMiddleware; /** * Handle implicit OPTIONS requests. * - * Place this middleware after the routing middleware so that it can handle - * implicit OPTIONS requests -- requests where OPTIONS is used, but the route - * does not explicitly handle that request method. + * This is an extension to the canonical version provided in + * zend-expressive-router v2.4 and up, and is deprecated in favor of that + * version starting in zend-expressive 2.2. * - * When invoked, it will create a response with status code 200 and an Allow - * header that defines all accepted request methods. - * - * You may optionally pass a response prototype to the constructor; when - * present, that prototype will be used to create a new response with the - * Allow header. - * - * The middleware is only invoked in these specific conditions: - * - * - an OPTIONS request - * - with a `RouteResult` present - * - where the `RouteResult` contains a `Route` instance - * - and the `Route` instance defines implicit OPTIONS. - * - * In all other circumstances, it will return the result of the delegate. + * @deprecated since 2.2.0; to be removed in 3.0.0. Please use the version + * provided in zend-expressive-router 2.4+, and use the factory from + * that component to create an instance. */ -class ImplicitOptionsMiddleware implements ServerMiddlewareInterface +class ImplicitOptionsMiddleware extends BaseImplicitOptionsMiddleware { - /** - * @var null|ResponseInterface - */ - private $response; - /** * @param null|ResponseInterface $response Response prototype to use for * implicit OPTIONS requests; if not provided a zend-diactoros Response @@ -53,42 +31,15 @@ class ImplicitOptionsMiddleware implements ServerMiddlewareInterface */ public function __construct(ResponseInterface $response = null) { - $this->response = $response; - } - - /** - * Handle an implicit OPTIONS request. - * - * @param ServerRequestInterface $request - * @param DelegateInterface $delegate - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request, DelegateInterface $delegate) - { - if ($request->getMethod() !== RequestMethod::METHOD_OPTIONS) { - return $delegate->process($request); - } - - if (false === ($result = $request->getAttribute(RouteResult::class, false))) { - return $delegate->process($request); - } - - $route = $result->getMatchedRoute(); - if (! $route || ! $route->implicitOptions()) { - return $delegate->process($request); - } - - $methods = implode(',', $route->getAllowedMethods()); - return $this->getResponse()->withHeader('Allow', $methods); - } - - /** - * Return the response prototype to use for an implicit OPTIONS request. - * - * @return ResponseInterface - */ - private function getResponse() - { - return $this->response ?: new Response('php://temp', StatusCode::STATUS_OK); + trigger_error(sprintf( + '%s is deprecated starting with zend-expressive 2.2.0; please use the %s class' + . ' provided in zend-expressive-router 2.4.0 and later. That class has required' + . ' dependencies, so please also add Zend\Expressive\Router\ConfigProvider to' + . ' your config/config.php file as well.', + __CLASS__, + BaseImplicitOptionsMiddleware::class + ), E_USER_DEPRECATED); + + parent::__construct($response ?: new Response()); } } diff --git a/test/Middleware/ImplicitOptionsMiddlewareTest.php b/test/Middleware/ImplicitOptionsMiddlewareTest.php index 81259683..f138ac19 100644 --- a/test/Middleware/ImplicitOptionsMiddlewareTest.php +++ b/test/Middleware/ImplicitOptionsMiddlewareTest.php @@ -7,143 +7,25 @@ namespace ZendTest\Expressive\Middleware; -use Fig\Http\Message\RequestMethodInterface as RequestMethod; -use Fig\Http\Message\StatusCodeInterface as StatusCode; -use Interop\Http\ServerMiddleware\DelegateInterface; use PHPUnit\Framework\TestCase; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use Zend\Diactoros\Response; use Zend\Expressive\Middleware\ImplicitOptionsMiddleware; -use Zend\Expressive\Router\Route; -use Zend\Expressive\Router\RouteResult; +use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware as BaseImplicitOptionsMiddleware; class ImplicitOptionsMiddlewareTest extends TestCase { - public function testNonOptionsRequestInvokesNext() + public function testConstructorTriggersDeprecationNotice() { - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_GET); - $request->getAttribute(RouteResult::class, false)->shouldNotBeCalled(); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->willReturn($response); - - $middleware = new ImplicitOptionsMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertSame($response, $result); - } - - public function testMissingRouteResultInvokesNext() - { - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_OPTIONS); - $request->getAttribute(RouteResult::class, false)->willReturn(false); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->willReturn($response); - - $middleware = new ImplicitOptionsMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertSame($response, $result); - } - - public function testMissingRouteInRouteResultInvokesNext() - { - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->willReturn(null); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_OPTIONS); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->willReturn($response); - - $middleware = new ImplicitOptionsMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertSame($response, $result); - } - - public function testOptionsRequestWhenRouteDefinesOptionsInvokesNext() - { - $route = $this->prophesize(Route::class); - $route->implicitOptions()->willReturn(false); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_OPTIONS); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->willReturn($response); - - $middleware = new ImplicitOptionsMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertSame($response, $result); - } - - public function testWhenNoResponseProvidedToConstructorImplicitOptionsRequestCreatesResponse() - { - $allowedMethods = [RequestMethod::METHOD_GET, RequestMethod::METHOD_POST]; - - $route = $this->prophesize(Route::class); - $route->implicitOptions()->willReturn(true); - $route->getAllowedMethods()->willReturn($allowedMethods); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_OPTIONS); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $response = $this->prophesize(ResponseInterface::class)->reveal(); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->shouldNotBeCalled(); + $test = (object) ['message' => false]; + set_error_handler(function ($errno, $errstr) use ($test) { + $test->message = $errstr; + return true; + }, E_USER_DEPRECATED); $middleware = new ImplicitOptionsMiddleware(); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertInstanceOf(Response::class, $result); - $this->assertNotSame($response, $result); - $this->assertEquals(StatusCode::STATUS_OK, $result->getStatusCode()); - $this->assertEquals(implode(',', $allowedMethods), $result->getHeaderLine('Allow')); - } - - public function testInjectsAllowHeaderInResponseProvidedToConstructorDuringOptionsRequest() - { - $allowedMethods = [RequestMethod::METHOD_GET, RequestMethod::METHOD_POST]; - - $route = $this->prophesize(Route::class); - $route->implicitOptions()->willReturn(true); - $route->getAllowedMethods()->willReturn($allowedMethods); - - $result = $this->prophesize(RouteResult::class); - $result->getMatchedRoute()->will([$route, 'reveal']); - - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_OPTIONS); - $request->getAttribute(RouteResult::class, false)->will([$result, 'reveal']); - - $delegate = $this->prophesize(DelegateInterface::class); - $delegate->process($request->reveal())->shouldNotBeCalled(); - - $expected = $this->prophesize(ResponseInterface::class); - $expected->withHeader('Allow', implode(',', $allowedMethods))->will([$expected, 'reveal']); + restore_error_handler(); - $middleware = new ImplicitOptionsMiddleware($expected->reveal()); - $result = $middleware->process($request->reveal(), $delegate->reveal()); - $this->assertSame($expected->reveal(), $result); + $this->assertInstanceOf(BaseImplicitOptionsMiddleware::class, $middleware); + $this->assertInternalType('string', $test->message); + $this->assertContains('deprecated starting with zend-expressive 2.2', $test->message); } } diff --git a/test/Router/IntegrationTest.php b/test/Router/IntegrationTest.php index 15d94352..950cc57f 100644 --- a/test/Router/IntegrationTest.php +++ b/test/Router/IntegrationTest.php @@ -63,6 +63,9 @@ public function disregardDeprecationNotices() if (strstr($errstr, 'ImplicitHeadMiddleware is deprecated')) { return true; } + if (strstr($errstr, 'ImplicitOptionsMiddleware is deprecated')) { + return true; + } return false; }, E_USER_DEPRECATED); } From 3de4d42c0d117b65f944b62b72a98d6a05ff09f4 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:22:35 -0600 Subject: [PATCH 19/35] Backports ResponseFactoryFactory from release-3.0.0 Backports the ResponseFactoryFactory from release-3.0.0, and updates it to work with PHP versions prior to 7.0. Additionally, since the v2 releases of zend-expressive _require_ zend-diactoros, removes the check for it from the factory. --- src/Container/ResponseFactoryFactory.php | 29 +++++++++++++++++++ test/Container/ResponseFactoryFactoryTest.php | 29 +++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/Container/ResponseFactoryFactory.php create mode 100644 test/Container/ResponseFactoryFactoryTest.php diff --git a/src/Container/ResponseFactoryFactory.php b/src/Container/ResponseFactoryFactory.php new file mode 100644 index 00000000..5861c39a --- /dev/null +++ b/src/Container/ResponseFactoryFactory.php @@ -0,0 +1,29 @@ +prophesize(ContainerInterface::class)->reveal(); + $factory = new ResponseFactoryFactory(); + + $result = $factory($container); + + $this->assertInternalType('callable', $result); + + $response = $result(); + $this->assertInstanceOf(Response::class, $response); + } +} From e7c8ef4d0f66aa0409b6266bb399c5f904227b1a Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:26:05 -0600 Subject: [PATCH 20/35] Backports StreamFactoryFactory from release-3.0.0 Backports the StreamFactoryFactory from release-3.0.0, and updates it to work with PHP versions prior to 7.0. Because zend-expressive v2 releases _require_ zend-diactoros, removes the conditional check for it within the factory. --- src/Container/StreamFactoryFactory.php | 29 +++++++++++++++++++++ test/Container/StreamFactoryFactoryTest.php | 29 +++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/Container/StreamFactoryFactory.php create mode 100644 test/Container/StreamFactoryFactoryTest.php diff --git a/src/Container/StreamFactoryFactory.php b/src/Container/StreamFactoryFactory.php new file mode 100644 index 00000000..8d9ea95a --- /dev/null +++ b/src/Container/StreamFactoryFactory.php @@ -0,0 +1,29 @@ +prophesize(ContainerInterface::class)->reveal(); + $factory = new StreamFactoryFactory(); + + $result = $factory($container); + + $this->assertInternalType('callable', $result); + + $stream = $result(); + $this->assertInstanceOf(Stream::class, $stream); + } +} From 1e784b263273fa89aa29341ba411f88e43f648b4 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:37:21 -0600 Subject: [PATCH 21/35] Creates Zend\Expressive\Handler\NotFoundHandler Based on Zend\Expressive\Delegate\NotFoundDelegate. --- src/Handler/NotFoundHandler.php | 109 +++++++++++++++++++++++++++ test/Handler/NotFoundHandlerTest.php | 96 +++++++++++++++++++++++ 2 files changed, 205 insertions(+) create mode 100644 src/Handler/NotFoundHandler.php create mode 100644 test/Handler/NotFoundHandlerTest.php diff --git a/src/Handler/NotFoundHandler.php b/src/Handler/NotFoundHandler.php new file mode 100644 index 00000000..ded92084 --- /dev/null +++ b/src/Handler/NotFoundHandler.php @@ -0,0 +1,109 @@ +responsePrototype = $responsePrototype; + $this->renderer = $renderer; + $this->template = $template; + $this->layout = $layout; + } + + /** + * Creates and returns a 404 response. + * + * @param ServerRequestInterface $request + * @return ResponseInterface + */ + public function process(ServerRequestInterface $request) + { + if (! $this->renderer) { + return $this->generatePlainTextResponse($request); + } + + return $this->generateTemplatedResponse($request); + } + + /** + * Generates a plain text response indicating the request method and URI. + * + * @param ServerRequestInterface $request + * @return ResponseInterface + */ + private function generatePlainTextResponse(ServerRequestInterface $request) + { + $response = $this->responsePrototype->withStatus(StatusCodeInterface::STATUS_NOT_FOUND); + $response->getBody() + ->write(sprintf( + 'Cannot %s %s', + $request->getMethod(), + (string) $request->getUri() + )); + return $response; + } + + /** + * Generates a response using a template. + * + * Template will receive the current request via the "request" variable. + * + * @param ServerRequestInterface $request + * @return ResponseInterface + */ + private function generateTemplatedResponse(ServerRequestInterface $request) + { + $response = $this->responsePrototype->withStatus(StatusCodeInterface::STATUS_NOT_FOUND); + $response->getBody()->write( + $this->renderer->render($this->template, ['request' => $request, 'layout' => $this->layout]) + ); + + return $response; + } +} diff --git a/test/Handler/NotFoundHandlerTest.php b/test/Handler/NotFoundHandlerTest.php new file mode 100644 index 00000000..cd868057 --- /dev/null +++ b/test/Handler/NotFoundHandlerTest.php @@ -0,0 +1,96 @@ +response = $this->prophesize(ResponseInterface::class); + } + + public function testConstructorDoesNotRequireARenderer() + { + $handler = new NotFoundHandler($this->response->reveal()); + $this->assertInstanceOf(NotFoundHandler::class, $handler); + $this->assertAttributeSame($this->response->reveal(), 'responsePrototype', $handler); + } + + public function testConstructorCanAcceptRendererAndTemplate() + { + $renderer = $this->prophesize(TemplateRendererInterface::class)->reveal(); + $template = 'foo::bar'; + $layout = 'layout::error'; + + $handler = new NotFoundHandler($this->response->reveal(), $renderer, $template, $layout); + + $this->assertInstanceOf(NotFoundHandler::class, $handler); + $this->assertAttributeSame($renderer, 'renderer', $handler); + $this->assertAttributeEquals($template, 'template', $handler); + $this->assertAttributeEquals($layout, 'layout', $handler); + } + + public function testRendersDefault404ResponseWhenNoRendererPresent() + { + $request = $this->prophesize(ServerRequestInterface::class); + $request->getMethod()->willReturn(RequestMethod::METHOD_POST); + $request->getUri()->willReturn('https://example.com/foo/bar'); + + $stream = $this->prophesize(StreamInterface::class); + $stream->write('Cannot POST https://example.com/foo/bar')->shouldBeCalled(); + $this->response->withStatus(StatusCode::STATUS_NOT_FOUND)->will([$this->response, 'reveal']); + $this->response->getBody()->will([$stream, 'reveal']); + + $handler = new NotFoundHandler($this->response->reveal()); + + $response = $handler->process($request->reveal()); + + $this->assertSame($this->response->reveal(), $response); + } + + public function testUsesRendererToGenerateResponseContentsWhenPresent() + { + $request = $this->prophesize(ServerRequestInterface::class)->reveal(); + + $renderer = $this->prophesize(TemplateRendererInterface::class); + $renderer + ->render( + NotFoundHandler::TEMPLATE_DEFAULT, + [ + 'request' => $request, + 'layout' => NotFoundHandler::LAYOUT_DEFAULT, + ] + ) + ->willReturn('CONTENT'); + + $stream = $this->prophesize(StreamInterface::class); + $stream->write('CONTENT')->shouldBeCalled(); + + $this->response->withStatus(StatusCode::STATUS_NOT_FOUND)->will([$this->response, 'reveal']); + $this->response->getBody()->will([$stream, 'reveal']); + + $handler = new NotFoundHandler($this->response->reveal(), $renderer->reveal()); + + $response = $handler->process($request); + + $this->assertSame($this->response->reveal(), $response); + } +} From 70e7c0961dfb18f97ca46d1c44342c5a2add3181 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:38:06 -0600 Subject: [PATCH 22/35] Updates NotFoundDelegate to extend NotFoundHandler --- src/Delegate/NotFoundDelegate.php | 102 +------------------------ test/Delegate/NotFoundDelegateTest.php | 95 ----------------------- 2 files changed, 2 insertions(+), 195 deletions(-) delete mode 100644 test/Delegate/NotFoundDelegateTest.php diff --git a/src/Delegate/NotFoundDelegate.php b/src/Delegate/NotFoundDelegate.php index 79c8ec81..1b1cbf33 100644 --- a/src/Delegate/NotFoundDelegate.php +++ b/src/Delegate/NotFoundDelegate.php @@ -7,110 +7,12 @@ namespace Zend\Expressive\Delegate; -use Fig\Http\Message\StatusCodeInterface; -use Interop\Http\ServerMiddleware\DelegateInterface; -use Psr\Http\Message\ResponseInterface; -use Psr\Http\Message\ServerRequestInterface; -use Zend\Expressive\Template\TemplateRendererInterface; +use Zend\Expressive\Handler\NotFoundHandler; /** * @deprecated since 2.2.0; to be removed in 3.0.0. Use * Zend\Expressive\Handler\NotFoundHandler instead, available since 2.2.0. */ -class NotFoundDelegate implements DelegateInterface +class NotFoundDelegate extends NotFoundHandler { - const TEMPLATE_DEFAULT = 'error::404'; - const LAYOUT_DEFAULT = 'layout::default'; - - /** - * @var TemplateRendererInterface - */ - private $renderer; - - /** - * This duplicates the property in StratigilityNotFoundHandler, but is done - * to ensure that we have access to the value in the methods we override. - * - * @var ResponseInterface - */ - protected $responsePrototype; - - /** - * @var string - */ - private $template; - - /** - * @var string - */ - private $layout; - - /** - * @param ResponseInterface $responsePrototype - * @param TemplateRendererInterface $renderer - * @param string $template - * @param string $layout - */ - public function __construct( - ResponseInterface $responsePrototype, - TemplateRendererInterface $renderer = null, - $template = self::TEMPLATE_DEFAULT, - $layout = self::LAYOUT_DEFAULT - ) { - $this->responsePrototype = $responsePrototype; - $this->renderer = $renderer; - $this->template = $template; - $this->layout = $layout; - } - - /** - * Creates and returns a 404 response. - * - * @param ServerRequestInterface $request - * @return ResponseInterface - */ - public function process(ServerRequestInterface $request) - { - if (! $this->renderer) { - return $this->generatePlainTextResponse($request); - } - - return $this->generateTemplatedResponse($request); - } - - /** - * Generates a plain text response indicating the request method and URI. - * - * @param ServerRequestInterface $request - * @return ResponseInterface - */ - private function generatePlainTextResponse(ServerRequestInterface $request) - { - $response = $this->responsePrototype->withStatus(StatusCodeInterface::STATUS_NOT_FOUND); - $response->getBody() - ->write(sprintf( - 'Cannot %s %s', - $request->getMethod(), - (string) $request->getUri() - )); - return $response; - } - - /** - * Generates a response using a template. - * - * Template will receive the current request via the "request" variable. - * - * @param ServerRequestInterface $request - * @return ResponseInterface - */ - private function generateTemplatedResponse(ServerRequestInterface $request) - { - $response = $this->responsePrototype->withStatus(StatusCodeInterface::STATUS_NOT_FOUND); - $response->getBody()->write( - $this->renderer->render($this->template, ['request' => $request, 'layout' => $this->layout]) - ); - - return $response; - } } diff --git a/test/Delegate/NotFoundDelegateTest.php b/test/Delegate/NotFoundDelegateTest.php deleted file mode 100644 index d8fe8b66..00000000 --- a/test/Delegate/NotFoundDelegateTest.php +++ /dev/null @@ -1,95 +0,0 @@ -response = $this->prophesize(ResponseInterface::class); - } - - public function testConstructorDoesNotRequireARenderer() - { - $delegate = new NotFoundDelegate($this->response->reveal()); - $this->assertInstanceOf(NotFoundDelegate::class, $delegate); - $this->assertAttributeSame($this->response->reveal(), 'responsePrototype', $delegate); - } - - public function testConstructorCanAcceptRendererAndTemplate() - { - $renderer = $this->prophesize(TemplateRendererInterface::class)->reveal(); - $template = 'foo::bar'; - $layout = 'layout::error'; - - $delegate = new NotFoundDelegate($this->response->reveal(), $renderer, $template, $layout); - - $this->assertInstanceOf(NotFoundDelegate::class, $delegate); - $this->assertAttributeSame($renderer, 'renderer', $delegate); - $this->assertAttributeEquals($template, 'template', $delegate); - $this->assertAttributeEquals($layout, 'layout', $delegate); - } - - public function testRendersDefault404ResponseWhenNoRendererPresent() - { - $request = $this->prophesize(ServerRequestInterface::class); - $request->getMethod()->willReturn(RequestMethod::METHOD_POST); - $request->getUri()->willReturn('https://example.com/foo/bar'); - - $stream = $this->prophesize(StreamInterface::class); - $stream->write('Cannot POST https://example.com/foo/bar')->shouldBeCalled(); - $this->response->withStatus(StatusCode::STATUS_NOT_FOUND)->will([$this->response, 'reveal']); - $this->response->getBody()->will([$stream, 'reveal']); - - $delegate = new NotFoundDelegate($this->response->reveal()); - - $response = $delegate->process($request->reveal()); - - $this->assertSame($this->response->reveal(), $response); - } - - public function testUsesRendererToGenerateResponseContentsWhenPresent() - { - $request = $this->prophesize(ServerRequestInterface::class)->reveal(); - - $renderer = $this->prophesize(TemplateRendererInterface::class); - $renderer - ->render( - NotFoundDelegate::TEMPLATE_DEFAULT, - [ - 'request' => $request, - 'layout' => NotFoundDelegate::LAYOUT_DEFAULT, - ] - ) - ->willReturn('CONTENT'); - - $stream = $this->prophesize(StreamInterface::class); - $stream->write('CONTENT')->shouldBeCalled(); - - $this->response->withStatus(StatusCode::STATUS_NOT_FOUND)->will([$this->response, 'reveal']); - $this->response->getBody()->will([$stream, 'reveal']); - - $delegate = new NotFoundDelegate($this->response->reveal(), $renderer->reveal()); - - $response = $delegate->process($request); - - $this->assertSame($this->response->reveal(), $response); - } -} From 5fb9bb96d4ebdb39214916beb10a19714e74a17f Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 10:46:31 -0600 Subject: [PATCH 23/35] Marks Zend\Expressive\Middleware\NotFoundHandler as deprecated --- src/Middleware/NotFoundHandler.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Middleware/NotFoundHandler.php b/src/Middleware/NotFoundHandler.php index e5f687df..7b7f36f9 100644 --- a/src/Middleware/NotFoundHandler.php +++ b/src/Middleware/NotFoundHandler.php @@ -13,6 +13,11 @@ use Psr\Http\Message\ServerRequestInterface; use Zend\Expressive\Delegate\NotFoundDelegate; +/** + * @deprecated since 2.2.0; to be removed in 3.0.0. Version 3.0.0 will reuse + * re-use the Zend\Expressive\Handler\NotFoundHandler directly within a + * middleware pipeline instead. + */ class NotFoundHandler implements MiddlewareInterface { /** From 8d25d7d6ceb11ac7204de38b0a31c0ca0fc48929 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Fri, 9 Mar 2018 11:12:06 -0600 Subject: [PATCH 24/35] Adds ConfigProvider Adds config provider with most services as defined in the skeleton, and which this package can provide directly. It also defines services for router middleware in some cases, to ensure they map to known-good factories. --- src/ConfigProvider.php | 67 +++++++++++++++++++++++++++++++ test/ConfigProviderTest.php | 79 +++++++++++++++++++++++++++++++++++++ 2 files changed, 146 insertions(+) create mode 100644 src/ConfigProvider.php create mode 100644 test/ConfigProviderTest.php diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php new file mode 100644 index 00000000..34ffbd07 --- /dev/null +++ b/src/ConfigProvider.php @@ -0,0 +1,67 @@ + $this->getDependencies(), + ]; + } + + /** + * @return array + */ + public function getDependencies() + { + // @codingStandardsIgnoreStart + return [ + 'aliases' => [ + Delegate\NotFoundDelegate::class => Handler\NotFoundHandler::class, + Middleware\DispatchMiddleware::class => Router\Middleware\DispatchMiddleware::class, + Middleware\ImplicitHeadMiddleware::class => Router\Middleware\ImplicitHeadMiddleware::class, + Middleware\ImplicitOptionsMiddleware::class => Router\Middleware\ImplicitOptionsMiddleware::class, + Middleware\RouteMiddleware::class => Router\Middleware\RouteMiddleware::class, + 'Zend\Expressive\Delegate\DefaultDelegate' => Handler\NotFoundHandler::class, + ], + 'invokables' => [ + Router\Middleware\DispatchMiddleware::class => Router\Middleware\DispatchMiddleware::class, + ], + 'factories' => [ + Application::class => Container\ApplicationFactory::class, + ErrorHandler::class => Container\ErrorHandlerFactory::class, + Handler\NotFoundHandler::class => Container\NotFoundDelegateFactory::class, + // Change the following in development to the WhoopsErrorResponseGeneratorFactory: + Middleware\ErrorResponseGenerator::class => Container\ErrorResponseGeneratorFactory::class, + Middleware\NotFoundHandler::class => Container\NotFoundHandlerFactory::class, + ResponseInterface::class => Container\ResponseFactoryFactory::class, + StreamInterface::class => Container\StreamFactoryFactory::class, + + // These are duplicates, in case the zend-expressive-router package ConfigProvider is not wired: + Router\Middleware\ImplicitHeadMiddleware::class => Router\Middleware\ImplicitHeadMiddlewareFactory::class, + Router\Middleware\ImplicitOptionsMiddleware::class => Router\Middleware\ImplicitOptionsMiddlewareFactory::class, + Router\Middleware\RouteMiddleware::class => Router\Middleware\RouteMiddlewareFactory::class, + ], + ]; + // @codingStandardsIgnoreEnd + } +} diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php new file mode 100644 index 00000000..ab254b05 --- /dev/null +++ b/test/ConfigProviderTest.php @@ -0,0 +1,79 @@ +provider = new ConfigProvider(); + } + + public function testProviderDefinesExpectedAliases() + { + $config = $this->provider->getDependencies(); + $aliases = $config['aliases']; + $this->assertArrayHasKey(Middleware\DispatchMiddleware::class, $aliases); + $this->assertArrayHasKey(Middleware\ImplicitHeadMiddleware::class, $aliases); + $this->assertArrayHasKey(Middleware\ImplicitOptionsMiddleware::class, $aliases); + $this->assertArrayHasKey(Middleware\RouteMiddleware::class, $aliases); + $this->assertArrayHasKey(NotFoundDelegate::class, $aliases); + $this->assertArrayHasKey('Zend\Expressive\Delegate\DefaultDelegate', $aliases); + } + + public function testProviderDefinesExpectedInvokableServices() + { + $config = $this->provider->getDependencies(); + $invokables = $config['invokables']; + $this->assertArrayHasKey(RouterMiddleware\DispatchMiddleware::class, $invokables); + } + + public function testProviderDefinesExpectedFactoryServices() + { + $config = $this->provider->getDependencies(); + $factories = $config['factories']; + + $this->assertArrayHasKey(Application::class, $factories); + $this->assertArrayHasKey(ErrorHandler::class, $factories); + $this->assertArrayHasKey(Middleware\ErrorResponseGenerator::class, $factories); + $this->assertArrayHasKey(Middleware\NotFoundHandler::class, $factories); + $this->assertArrayHasKey(NotFoundHandler::class, $factories); + $this->assertArrayHasKey(ResponseInterface::class, $factories); + $this->assertArrayHasKey(StreamInterface::class, $factories); + $this->assertArrayHasKey(RouterMiddleware\ImplicitHeadMiddleware::class, $factories); + $this->assertArrayHasKey(RouterMiddleware\ImplicitOptionsMiddleware::class, $factories); + $this->assertArrayHasKey(RouterMiddleware\RouteMiddleware::class, $factories); + } + + public function testInvocationReturnsArrayWithDependencies() + { + $config = ($this->provider)(); + $this->assertInternalType('array', $config); + $this->assertArrayHasKey('dependencies', $config); + $this->assertArrayHasKey('aliases', $config['dependencies']); + $this->assertArrayHasKey('invokables', $config['dependencies']); + $this->assertArrayHasKey('factories', $config['dependencies']); + } +} From 484d821456df44701172794fc9a17673abf290be Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 10 Mar 2018 10:05:23 -0600 Subject: [PATCH 25/35] Backports ApplicationConfigInjectionDelegator from release-3.0.0 This patch backports the ApplicationConfigInjectionDelegator and modifies it to work with v2. The ApplicationConfigInjectionTrait was updated to delegate to the new delegator. The ApplicationFactory now also uses the delegator methods instead of the application methods. Test changes were primarily due to usage of callable double-pass middleware; updating them to use interop middleware simplifies setup and prevents unnecessary deprecation notices. --- src/Application.php | 13 +- src/ApplicationConfigInjectionTrait.php | 235 +-------- .../ApplicationConfigInjectionDelegator.php | 373 +++++++++++++ src/Container/ApplicationFactory.php | 4 +- test/Application/ConfigInjectionTest.php | 14 +- ...pplicationConfigInjectionDelegatorTest.php | 498 ++++++++++++++++++ test/Container/ApplicationFactoryTest.php | 16 +- test/TestAsset/CallableInteropMiddleware.php | 6 + 8 files changed, 920 insertions(+), 239 deletions(-) create mode 100644 src/Container/ApplicationConfigInjectionDelegator.php create mode 100644 test/Container/ApplicationConfigInjectionDelegatorTest.php diff --git a/src/Application.php b/src/Application.php index ef9ec9b2..6661b6fc 100644 --- a/src/Application.php +++ b/src/Application.php @@ -314,14 +314,14 @@ public function route($path, $middleware = null, array $methods = null, $name = $this->checkForDuplicateRoute($path, $methods); if (! isset($route)) { - $methods = null === $methods ? Router\Route::HTTP_METHOD_ANY : $methods; + $methods = null === $methods ? Router\Route::HTTP_METHOD_ANY : $methods; $middleware = $this->prepareMiddleware( $middleware, $this->router, $this->responsePrototype, $this->container ); - $route = new Router\Route($path, $middleware, $methods, $name); + $route = new Router\Route($path, $middleware, $methods, $name); } $this->routes[] = $route; @@ -487,9 +487,12 @@ private function checkForDuplicateRoute($path, $methods = null) }); if (! empty($matches)) { - throw new Exception\DuplicateRouteException( - 'Duplicate route detected; same name or path, and one or more HTTP methods intersect' - ); + throw new Exception\DuplicateRouteException(sprintf( + 'Duplicate route detected; same name or path ("%s"),' + . ' and one or more HTTP methods intersect (%s)', + $path, + is_array($methods) ? implode(', ', $methods) : '*' + )); } } diff --git a/src/ApplicationConfigInjectionTrait.php b/src/ApplicationConfigInjectionTrait.php index 8dc667ca..7be1b540 100644 --- a/src/ApplicationConfigInjectionTrait.php +++ b/src/ApplicationConfigInjectionTrait.php @@ -8,59 +8,14 @@ namespace Zend\Expressive; use SplPriorityQueue; +use Zend\Expressive\Container\ApplicationConfigInjectionDelegator; trait ApplicationConfigInjectionTrait { /** * Inject a middleware pipeline from the middleware_pipeline configuration. * - * Inspects the configuration provided to determine if a middleware pipeline - * exists to inject in the application. - * - * If no pipeline is defined, but routes *are*, then the method will inject - * the routing and dispatch middleware. - * - * Use the following configuration format: - * - * - * return [ - * 'middleware_pipeline' => [ - * // An array of middleware to register with the pipeline. - * // entries to register prior to routing/dispatching... - * Application::ROUTING_MIDDLEWARE, - * Application::DISPATCH_MIDDLEWARE, - * // entries to register after routing/dispatching... - * ], - * ]; - * - * - * Each item in the middleware_pipeline array (with the exception of the routing - * and dispatch middleware entries) must be of the following specification: - * - * - * [ - * // required: - * 'middleware' => 'Name of middleware service, or a callable', - * // optional: - * 'path' => '/path/to/match', - * 'priority' => 1, // integer - * ] - * - * - * Note that the `path` element can only be a literal. - * - * `priority` is used to shape the order in which middleware is piped to the - * application. Values are integers, with high values having higher priority - * (piped earlier), and low/negative values having lower priority (piped last). - * Default priority if none is specified is 1. Middleware with the same - * priority are piped in the order in which they appear. - * - * Middleware piped may be either callables or service names. - * - * Additionally, you can specify an array of callables or service names as - * the `middleware` value of a specification. Internally, this will create - * a `Zend\Stratigility\MiddlewarePipe` instance, with the middleware - * specified piped in the order provided. + * Proxies to ApplicationConfigInjectionDelegator::injectPipelineFromConfig * * @param null|array $config If null, attempts to pull the 'config' service * from the composed container. @@ -68,68 +23,22 @@ trait ApplicationConfigInjectionTrait */ public function injectPipelineFromConfig(array $config = null) { - if (null === $config) { - $config = $this->container->has('config') ? $this->container->get('config') : []; - } - - if (empty($config['middleware_pipeline'])) { - if (! isset($config['routes']) || ! is_array($config['routes'])) { - return; - } - - $this->pipeRoutingMiddleware(); - $this->pipeDispatchMiddleware(); + if (! is_array($config) + && (! $this->container || ! $this->container->has('config')) + ) { return; } - // Create a priority queue from the specifications - $queue = array_reduce( - array_map($this->createCollectionMapper(), $config['middleware_pipeline']), - $this->createPriorityQueueReducer(), - new SplPriorityQueue() + ApplicationConfigInjectionDelegator::injectPipelineFromConfig( + $this, + is_array($config) ? $config : $this->container->get('config') ); - - foreach ($queue as $spec) { - $path = isset($spec['path']) ? $spec['path'] : '/'; - $this->pipe($path, $spec['middleware']); - } } /** * Inject routes from configuration. * - * Introspects the provided configuration for routes to inject in the - * application instance. - * - * The following configuration structure can be used to define routes: - * - * - * return [ - * 'routes' => [ - * [ - * 'path' => '/path/to/match', - * 'middleware' => 'Middleware Service Name or Callable', - * 'allowed_methods' => ['GET', 'POST', 'PATCH'], - * 'options' => [ - * 'stuff' => 'to', - * 'pass' => 'to', - * 'the' => 'underlying router', - * ], - * ], - * // etc. - * ], - * ]; - * - * - * Each route MUST have a path and middleware key at the minimum. - * - * The "allowed_methods" key may be omitted, can be either an array or the - * value of the Zend\Expressive\Router\Route::HTTP_METHOD_ANY constant; any - * valid HTTP method token is allowed, which means you can specify custom HTTP - * methods as well. - * - * The "options" key may also be omitted, and its interpretation will be - * dependent on the underlying router used. + * Proxies to ApplicationConfigInjectionDelegator::injectRoutesFromConfig * * @param null|array $config If null, attempts to pull the 'config' service * from the composed container. @@ -138,127 +47,15 @@ public function injectPipelineFromConfig(array $config = null) */ public function injectRoutesFromConfig(array $config = null) { - if (null === $config) { - $config = $this->container->has('config') ? $this->container->get('config') : []; - } - - if (! isset($config['routes']) || ! is_array($config['routes'])) { + if (! is_array($config) + && (! $this->container || ! $this->container->has('config')) + ) { return; } - foreach ($config['routes'] as $spec) { - if (! isset($spec['path']) || ! isset($spec['middleware'])) { - continue; - } - - $methods = null; - if (isset($spec['allowed_methods'])) { - $methods = $spec['allowed_methods']; - if (! is_array($methods)) { - throw new Exception\InvalidArgumentException(sprintf( - 'Allowed HTTP methods for a route must be in form of an array; received "%s"', - gettype($methods) - )); - } - } - - $name = isset($spec['name']) ? $spec['name'] : null; - $middleware = $this->prepareMiddleware( - $spec['middleware'], - $this->router, - $this->responsePrototype, - $this->container - ); - - $route = $this->route($spec['path'], $middleware, $methods, $name); - - if (isset($spec['options'])) { - $options = $spec['options']; - if (! is_array($options)) { - throw new Exception\InvalidArgumentException(sprintf( - 'Route options must be an array; received "%s"', - gettype($options) - )); - } - - $route->setOptions($options); - } - } - } - - /** - * Create the collection mapping function. - * - * Returns a callable with the following signature: - * - * - * function (array|string $item) : array - * - * - * When it encounters one of the self::*_MIDDLEWARE constants, it passes - * the value to the `createPipelineMapper()` callback to create a spec - * that uses the return value as pipeline middleware. - * - * If the 'middleware' value is an array, it uses the `createPipelineMapper()` - * callback as an array mapper in order to ensure the self::*_MIDDLEWARE - * are injected correctly. - * - * If the 'middleware' value is missing, or not viable as middleware, it - * raises an exception, to ensure the pipeline is built correctly. - * - * @return callable - * @throws Exception\InvalidArgumentException - */ - private function createCollectionMapper() - { - $appMiddlewares = [ - Application::ROUTING_MIDDLEWARE, - Application::DISPATCH_MIDDLEWARE, - ]; - - return function ($item) use ($appMiddlewares) { - if (in_array($item, $appMiddlewares, true)) { - return ['middleware' => $item]; - } - - if (! is_array($item) || ! array_key_exists('middleware', $item)) { - throw new Exception\InvalidArgumentException(sprintf( - 'Invalid pipeline specification received; must be an array containing a middleware ' - . 'key, or one of the Application::*_MIDDLEWARE constants; received %s', - is_object($item) ? get_class($item) : gettype($item) - )); - } - - return $item; - }; - } - - /** - * Create reducer function that will reduce an array to a priority queue. - * - * Creates and returns a function with the signature: - * - * - * function (SplQueue $queue, array $item) : SplQueue - * - * - * The function is useful to reduce an array of pipeline middleware to a - * priority queue. - * - * @return callable - */ - private function createPriorityQueueReducer() - { - // $serial is used to ensure that items of the same priority are enqueued - // in the order in which they are inserted. - $serial = PHP_INT_MAX; - return function ($queue, $item) use (&$serial) { - $priority = isset($item['priority']) && is_int($item['priority']) - ? $item['priority'] - : 1; - $queue->insert($item, [$priority, $serial]); - $serial -= 1; - return $queue; - }; + ApplicationConfigInjectionDelegator::injectRoutesFromConfig( + $this, + is_array($config) ? $config : $this->container->get('config') + ); } } diff --git a/src/Container/ApplicationConfigInjectionDelegator.php b/src/Container/ApplicationConfigInjectionDelegator.php new file mode 100644 index 00000000..e154bf2a --- /dev/null +++ b/src/Container/ApplicationConfigInjectionDelegator.php @@ -0,0 +1,373 @@ +has('config')) { + return $application; + } + + $config = $container->get('config'); + if (! isset($config['routes']) && ! isset($config['middleware_pipeline'])) { + return $application; + } + + self::injectPipelineFromConfig($application, $config); + self::injectRoutesFromConfig($application, $config); + + return $application; + } + + /** + * Inject a middleware pipeline from the middleware_pipeline configuration. + * + * Inspects the configuration provided to determine if a middleware pipeline + * exists to inject in the application. + * + * If no pipeline is defined, but routes *are*, then the method will inject + * the routing and dispatch middleware. + * + * Use the following configuration format: + * + * + * return [ + * 'middleware_pipeline' => [ + * // An array of middleware to register with the pipeline. + * // entries to register prior to routing/dispatching... + * // - entry for \Zend\Expressive\Router\Middleware\RouteMiddleware::class + * // - entry for \Zend\Expressive\Router\Middleware\DispatchMiddleware::class + * // entries to register after routing/dispatching... + * ], + * ]; + * + * + * Each item in the middleware_pipeline array must be of the following + * specification: + * + * + * [ + * // required: + * 'middleware' => 'Name of middleware service, or a callable', + * // optional: + * 'path' => '/path/to/match', + * 'priority' => 1, // integer + * ] + * + * + * Note that the `path` element can only be a literal. + * + * `priority` is used to shape the order in which middleware is piped to the + * application. Values are integers, with high values having higher priority + * (piped earlier), and low/negative values having lower priority (piped last). + * Default priority if none is specified is 1. Middleware with the same + * priority are piped in the order in which they appear. + * + * Middleware piped may be either callables or service names. + * + * Additionally, you can specify an array of callables or service names as + * the `middleware` value of a specification. Internally, this will create + * a `Zend\Stratigility\MiddlewarePipe` instance, with the middleware + * specified piped in the order provided. + * + * @return void + */ + public static function injectPipelineFromConfig(Application $application, array $config) + { + if (empty($config['middleware_pipeline'])) { + if (! isset($config['routes']) || ! is_array($config['routes'])) { + return; + } + + self::pipeRoutingMiddleware($application); + self::pipeDispatchMiddleware($application); + return; + } + + // Create a priority queue from the specifications + $queue = array_reduce( + array_map(self::createCollectionMapper(), $config['middleware_pipeline']), + self::createPriorityQueueReducer(), + new SplPriorityQueue() + ); + + foreach ($queue as $spec) { + $path = $spec['path'] ?? '/'; + $application->pipe($path, $spec['middleware']); + } + } + + /** + * Inject routes from configuration. + * + * Introspects the provided configuration for routes to inject in the + * application instance. + * + * The following configuration structure can be used to define routes: + * + * + * return [ + * 'routes' => [ + * [ + * 'path' => '/path/to/match', + * 'middleware' => 'Middleware Service Name or Callable', + * 'allowed_methods' => ['GET', 'POST', 'PATCH'], + * 'options' => [ + * 'stuff' => 'to', + * 'pass' => 'to', + * 'the' => 'underlying router', + * ], + * ], + * // etc. + * ], + * ]; + * + * + * Each route MUST have a path and middleware key at the minimum. + * + * The "allowed_methods" key may be omitted, can be either an array or the + * value of the Zend\Expressive\Router\Route::HTTP_METHOD_ANY constant; any + * valid HTTP method token is allowed, which means you can specify custom HTTP + * methods as well. + * + * The "options" key may also be omitted, and its interpretation will be + * dependent on the underlying router used. + * + * @return void + * @throws InvalidArgumentException + */ + public static function injectRoutesFromConfig(Application $application, array $config) + { + if (! isset($config['routes']) || ! is_array($config['routes'])) { + return; + } + + foreach ($config['routes'] as $spec) { + if (! isset($spec['path']) || ! isset($spec['middleware'])) { + continue; + } + + $methods = null; + if (isset($spec['allowed_methods'])) { + $methods = $spec['allowed_methods']; + if (! is_array($methods)) { + throw new InvalidArgumentException(sprintf( + 'Allowed HTTP methods for a route must be in form of an array; received "%s"', + gettype($methods) + )); + } + } + + $name = $spec['name'] ?? null; + $route = $application->route( + $spec['path'], + $spec['middleware'], + $methods, + $name + ); + + if (isset($spec['options'])) { + $options = $spec['options']; + if (! is_array($options)) { + throw new InvalidArgumentException(sprintf( + 'Route options must be an array; received "%s"', + gettype($options) + )); + } + + $route->setOptions($options); + } + } + } + + /** + * Create the collection mapping function. + * + * Returns a callable with the following signature: + * + * + * function (array|string $item) : array + * + * + * If the 'middleware' value is missing, or not viable as middleware, it + * raises an exception, to ensure the pipeline is built correctly. + * + * @return callable + * @throws InvalidArgumentException + */ + private static function createCollectionMapper() + { + $appMiddleware = [ + Application::ROUTING_MIDDLEWARE, + Application::DISPATCH_MIDDLEWARE, + ]; + + return function ($item) use ($appMiddleware) { + if (in_array($item, $appMiddleware, true)) { + return ['middleware' => $item]; + } + + if (! is_array($item) || ! array_key_exists('middleware', $item)) { + throw new InvalidArgumentException(sprintf( + 'Invalid pipeline specification received; must be an array' + . ' containing a middleware key; received %s', + is_object($item) ? get_class($item) : gettype($item) + )); + } + + return $item; + }; + } + + /** + * Create reducer function that will reduce an array to a priority queue. + * + * Creates and returns a function with the signature: + * + * + * function (SplQueue $queue, array $item) : SplQueue + * + * + * The function is useful to reduce an array of pipeline middleware to a + * priority queue. + * + * @return callable + */ + private static function createPriorityQueueReducer() + { + // $serial is used to ensure that items of the same priority are enqueued + // in the order in which they are inserted. + $serial = PHP_INT_MAX; + return function ($queue, $item) use (&$serial) { + $priority = isset($item['priority']) && is_int($item['priority']) + ? $item['priority'] + : 1; + $queue->insert($item, [$priority, $serial]); + $serial -= 1; + return $queue; + }; + } + + /** + * Pipe routing middleware into the application. + * + * Attempts to pull both the router and response interface from the application + * container, falling back to reflection of the application instance + * when unable to find either one. + * + * @return void + */ + private static function pipeRoutingMiddleware(Application $application) + { + $container = $application->getContainer(); + if ($container->has(RouteMiddleware::class)) { + $application->pipe(RouteMiddleware::class); + return; + } + + $application->pipe(new RouteMiddleware( + self::getRouter($application, $container), + self::getResponsePrototype($application, $container) + )); + } + + /** + * Pipe dispatch middleware into the application. + * + * If the DispatchMiddleware is present in the application's container, it + * pipes that service. Otherwise, instantiates it directly and pipes it. + * + * @return void + * @throws MissingDependencyException if the RouterInterface service is not + * found when the RouteMiddleware is not present. + */ + private static function pipeDispatchMiddleware(Application $application) + { + $container = $application->getContainer(); + $application->pipe( + $container->has(DispatchMiddleware::class) + ? $container->get(DispatchMiddleware::class) + : new DispatchMiddleware() + ); + } + + /** + * Get the router. + * + * If not available in the container, uses reflection to pull it from the + * application. + * + * @return RouterInterface + */ + private static function getRouter(Application $application, ContainerInterface $container) + { + if (! $container->has(RouterInterface::class)) { + $r = new ReflectionProperty($application, 'router'); + $r->setAccessible(true); + return $r->getValue($application); + } + + return $container->get(RouterInterface::class); + } + + /** + * Get the response prototype. + * + * If not available in the container, uses reflection to pull it from the + * application. + * + * If in the container, fetches it. If the value is callable, uses it as + * a factory to generate and return the response. + * + * @return ResponseInterface + */ + private static function getResponsePrototype(Application $application, ContainerInterface $container) + { + if (! $container->has(ResponseInterface::class)) { + $r = new ReflectionProperty($application, 'responsePrototype'); + $r->setAccessible(true); + return $r->getValue($application); + } + + $response = $container->get(ResponseInterface::class); + return is_callable($response) ? $response() : $response; + } +} diff --git a/src/Container/ApplicationFactory.php b/src/Container/ApplicationFactory.php index b1f93e01..cf8b7aa3 100644 --- a/src/Container/ApplicationFactory.php +++ b/src/Container/ApplicationFactory.php @@ -91,7 +91,7 @@ public function __invoke(ContainerInterface $container) */ private function injectRoutesAndPipeline(Application $app, array $config) { - $app->injectRoutesFromConfig($config); - $app->injectPipelineFromConfig($config); + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); } } diff --git a/test/Application/ConfigInjectionTest.php b/test/Application/ConfigInjectionTest.php index 4cf0d344..09df6940 100644 --- a/test/Application/ConfigInjectionTest.php +++ b/test/Application/ConfigInjectionTest.php @@ -7,6 +7,7 @@ namespace ZendTest\Expressive\Application; +use Interop\Http\ServerMiddleware\DelegateInterface; use Interop\Http\ServerMiddleware\MiddlewareInterface; use PHPUnit\Framework\Assert; use PHPUnit\Framework\TestCase; @@ -22,6 +23,7 @@ use Zend\Expressive\Router\RouterInterface; use Zend\Stratigility\MiddlewarePipe; use ZendTest\Expressive\ContainerTrait; +use ZendTest\Expressive\TestAsset\CallableInteropMiddleware; use ZendTest\Expressive\TestAsset\InvokableMiddleware; /** @@ -119,20 +121,20 @@ public static function assertPipelineContainsInstanceOf($class, $pipeline, $mess Assert::assertThat($found, Assert::isTrue(), $message); } - public function callableMiddlewares() + public function injectableMiddleware() { return [ - [InvokableMiddleware::class], + [CallableInteropMiddleware::class], [ - function () { + function ($request, DelegateInterface $delegate) { }, ], - [[InvokableMiddleware::class, 'staticallyCallableMiddleware']], + [[CallableInteropMiddleware::class, 'staticallyCallableMiddleware']], ]; } /** - * @dataProvider callableMiddlewares + * @dataProvider injectableMiddleware * * @param callable|array|string $middleware */ @@ -188,7 +190,7 @@ public function testNoRoutesAreAddedIfSpecDoesNotProvidePathOrMiddleware() public function configWithRoutesButNoPipeline() { - $middleware = function ($request, $response, $next) { + $middleware = function ($request, DelegateInterface $delegate) { }; $routes = [ diff --git a/test/Container/ApplicationConfigInjectionDelegatorTest.php b/test/Container/ApplicationConfigInjectionDelegatorTest.php new file mode 100644 index 00000000..205e1329 --- /dev/null +++ b/test/Container/ApplicationConfigInjectionDelegatorTest.php @@ -0,0 +1,498 @@ +container = $this->mockContainerInterface(); + $this->router = $this->prophesize(RouterInterface::class); + $this->routeMiddleware = new PathBasedRoutingMiddleware( + $this->router->reveal(), + new Response() + ); + $this->dispatchMiddleware = $this->prophesize(DispatchMiddleware::class)->reveal(); + } + + public function createApplication() + { + return new Application( + $this->router->reveal(), + $this->container->reveal() + ); + } + + public function getQueueFromApplicationPipeline(Application $app) + { + $r = new ReflectionProperty($app, 'pipeline'); + $r->setAccessible(true); + return $r->getValue($app); + } + + public static function assertRoute($spec, array $routes) + { + Assert::assertThat( + array_reduce($routes, function ($found, $route) use ($spec) { + if ($found) { + return $found; + } + + if ($route->getPath() !== $spec['path']) { + return false; + } + + if (! $route->getMiddleware() instanceof MiddlewareInterface) { + return false; + } + + if (isset($spec['allowed_methods']) + && $route->getAllowedMethods() !== $spec['allowed_methods'] + ) { + return false; + } + + if (! isset($spec['allowed_methods']) + && $route->getAllowedMethods() !== Route::HTTP_METHOD_ANY + ) { + return false; + } + + return true; + }, false), + Assert::isTrue(), + 'Route created does not match any specifications' + ); + } + + public static function assertPipelineContainsInstanceOf($class, $pipeline, $message = null) + { + $message = $message ?: 'Did not find expected middleware class type in pipeline'; + $found = false; + + foreach ($pipeline as $middleware) { + if ($middleware instanceof $class) { + $found = true; + break; + } + } + + Assert::assertThat($found, Assert::isTrue(), $message); + } + + public static function assertRouteMiddleware(MiddlewareInterface $middleware) + { + if ($middleware instanceof PathBasedRoutingMiddleware) { + Assert::assertInstanceOf(PathBasedRoutingMiddleware::class, $middleware); + return; + } + + if (! $middleware instanceof Middleware\LazyLoadingMiddleware) { + Assert::fail('Middleware is not an instance of PathBasedRoutingMiddleware'); + } + + Assert::assertAttributeSame( + PathBasedRoutingMiddleware::class, + 'middlewareName', + $middleware, + 'Middleware is not an instance of PathBasedRoutingMiddleware' + ); + } + + public static function assertDispatchMiddleware(MiddlewareInterface $middleware) + { + if ($middleware instanceof DispatchMiddleware) { + Assert::assertInstanceOf(DispatchMiddleware::class, $middleware); + return; + } + + if (! $middleware instanceof Middleware\LazyLoadingMiddleware) { + Assert::fail('Middleware is not an instance of DispatchMiddleware'); + } + + Assert::assertAttributeSame( + DispatchMiddleware::class, + 'middlewareName', + $middleware, + 'Middleware is not an instance of DispatchMiddleware' + ); + } + + public function injectableMiddleware() + { + return [ + [CallableInteropMiddleware::class], + [ + function ($request, DelegateInterface $delegate) { + }, + ], + [[CallableInteropMiddleware::class, 'staticallyCallableMiddleware']], + ]; + } + + public function testInvocationAsDelegatorFactoryRaisesExceptionIfCallbackIsNotAnApplication() + { + $container = $this->prophesize(ContainerInterface::class)->reveal(); + $callback = function () { + return $this; + }; + $factory = new ApplicationConfigInjectionDelegator(); + $this->expectException(InvalidServiceException::class); + $this->expectExceptionMessage('cannot operate'); + $factory($container, Application::class, $callback); + } + + /** + * @dataProvider injectableMiddleware + * + * @param callable|array|string $middleware + */ + public function testInjectRoutesFromConfigSetsUpRoutesFromConfig($middleware) + { + $this->container->has('HelloWorld')->willReturn(true); + $this->container->has('Ping')->willReturn(true); + + $config = [ + 'routes' => [ + [ + 'path' => '/', + 'middleware' => $middleware, + 'allowed_methods' => ['GET'], + ], + [ + 'path' => '/ping', + 'middleware' => 'Ping', + 'allowed_methods' => ['GET'], + ], + ], + ]; + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + + $routes = $app->getRoutes(); + + foreach ($config['routes'] as $route) { + $this->assertRoute($route, $routes); + } + } + + public function testNoRoutesAreAddedIfSpecDoesNotProvidePathOrMiddleware() + { + $config = [ + 'routes' => [ + [ + 'allowed_methods' => ['GET'], + ], + [ + 'allowed_methods' => ['POST'], + ], + ], + ]; + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + + $routes = $app->getRoutes(); + $this->assertCount(0, $routes); + } + + public function configWithRoutesButNoPipeline() + { + $middleware = function ($request, DelegateInterface $delegate) { + }; + + $routes = [ + [ + 'path' => '/', + 'middleware' => clone $middleware, + 'allowed_methods' => ['GET'], + ], + ]; + + return [ + 'no-pipeline-defined' => [['routes' => $routes]], + 'empty-pipeline' => [['middleware_pipeline' => [], 'routes' => $routes]], + 'null-pipeline' => [['middleware_pipeline' => null, 'routes' => $routes]], + ]; + } + + /** + * @dataProvider configWithRoutesButNoPipeline + * + * @param array $config + */ + public function testProvidingRoutesAndNoPipelineImplicitlyRegistersRoutingAndDispatchMiddleware(array $config) + { + $this->injectServiceInContainer( + $this->container, + PathBasedRoutingMiddleware::class, + $this->routeMiddleware + ); + $this->injectServiceInContainer( + $this->container, + DispatchMiddleware::class, + $this->dispatchMiddleware + ); + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + + $queue = $this->getQueueFromApplicationPipeline($app); + + $this->assertCount(2, $queue, 'Did not get expected pipeline count!'); + + $test = $queue->dequeue(); + $this->assertRouteMiddleware($test->handler); + + $test = $queue->dequeue(); + $this->assertDispatchMiddleware($test->handler); + } + + public function testInjectPipelineFromConfigHonorsPriorityOrderWhenAttachingMiddleware() + { + $middleware = new InteropMiddleware(); + + $pipeline1 = [['middleware' => clone $middleware, 'priority' => 1]]; + $pipeline2 = [['middleware' => clone $middleware, 'priority' => 100]]; + $pipeline3 = [['middleware' => clone $middleware, 'priority' => -100]]; + + $pipeline = array_merge($pipeline3, $pipeline1, $pipeline2); + $config = ['middleware_pipeline' => $pipeline]; + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + + $pipeline = $this->getQueueFromApplicationPipeline($app); + + $this->assertSame($pipeline2[0]['middleware'], $pipeline->dequeue()->handler); + $this->assertSame($pipeline1[0]['middleware'], $pipeline->dequeue()->handler); + $this->assertSame($pipeline3[0]['middleware'], $pipeline->dequeue()->handler); + } + + public function testMiddlewareWithoutPriorityIsGivenDefaultPriorityAndRegisteredInOrderReceived() + { + $middleware = new InteropMiddleware(); + + $pipeline1 = [['middleware' => clone $middleware]]; + $pipeline2 = [['middleware' => clone $middleware]]; + $pipeline3 = [['middleware' => clone $middleware]]; + + $pipeline = array_merge($pipeline3, $pipeline1, $pipeline2); + $config = ['middleware_pipeline' => $pipeline]; + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + + $pipeline = $this->getQueueFromApplicationPipeline($app); + + $this->assertSame($pipeline3[0]['middleware'], $pipeline->dequeue()->handler); + $this->assertSame($pipeline1[0]['middleware'], $pipeline->dequeue()->handler); + $this->assertSame($pipeline2[0]['middleware'], $pipeline->dequeue()->handler); + } + + public function testInjectPipelineFromConfigWithEmptyConfigDoesNothing() + { + $app = $this->createApplication(); + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, []); + $pipeline = $this->getQueueFromApplicationPipeline($app); + $this->assertEquals(0, $pipeline->count()); + } + + public function testInjectRoutesFromConfigWithEmptyConfigDoesNothing() + { + $app = $this->createApplication(); + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, []); + $this->assertEquals([], $app->getRoutes()); + $pipeline = $this->getQueueFromApplicationPipeline($app); + $this->assertEquals(0, $pipeline->count()); + } + + public function testInjectRoutesFromConfigRaisesExceptionIfAllowedMethodsIsInvalid() + { + $config = [ + 'routes' => [ + [ + 'path' => '/', + 'middleware' => new InteropMiddleware(), + 'allowed_methods' => 'not-valid', + ], + ], + ]; + $this->container->has('config')->willReturn(false); + $app = $this->createApplication(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Allowed HTTP methods'); + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + } + + public function testInjectRoutesFromConfigRaisesExceptionIfOptionsIsNotAnArray() + { + $config = [ + 'routes' => [ + [ + 'path' => '/', + 'middleware' => new InteropMiddleware(), + 'allowed_methods' => ['GET'], + 'options' => 'invalid', + ], + ], + ]; + $this->container->has('config')->willReturn(false); + $app = $this->createApplication(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Route options must be an array'); + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + } + + public function testInjectRoutesFromConfigCanProvideRouteOptions() + { + $config = [ + 'routes' => [ + [ + 'path' => '/', + 'middleware' => new InteropMiddleware(), + 'allowed_methods' => ['GET'], + 'options' => [ + 'foo' => 'bar', + ], + ], + ], + ]; + $this->container->has('config')->willReturn(false); + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); + + $routes = $app->getRoutes(); + + $route = array_shift($routes); + $this->assertEquals($config['routes'][0]['options'], $route->getOptions()); + } + + public function testInjectRoutesFromConfigWillSkipSpecsThatOmitPath() + { + $config = [ + 'routes' => [ + [ + 'middleware' => new InteropMiddleware(), + 'allowed_methods' => ['GET'], + 'options' => [ + 'foo' => 'bar', + ], + ], + ], + ]; + $this->container->has('config')->willReturn(false); + $this->injectServiceInContainer( + $this->container, + PathBasedRoutingMiddleware::class, + $this->routeMiddleware + ); + $this->injectServiceInContainer( + $this->container, + DispatchMiddleware::class, + $this->dispatchMiddleware + ); + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + $this->assertEquals([], $app->getRoutes()); + } + + public function testInjectRoutesFromConfigWillSkipSpecsThatOmitMiddleware() + { + $config = [ + 'routes' => [ + [ + 'path' => '/', + 'allowed_methods' => ['GET'], + 'options' => [ + 'foo' => 'bar', + ], + ], + ], + ]; + $this->container->has('config')->willReturn(false); + $this->injectServiceInContainer( + $this->container, + PathBasedRoutingMiddleware::class, + $this->routeMiddleware + ); + $this->injectServiceInContainer( + $this->container, + DispatchMiddleware::class, + $this->dispatchMiddleware + ); + + $app = $this->createApplication(); + + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + $this->assertEquals([], $app->getRoutes()); + } + + public function testInjectPipelineFromConfigRaisesExceptionForSpecsOmittingMiddlewareKey() + { + $config = [ + 'middleware_pipeline' => [ + [ + 'this' => 'will not work', + ], + ], + ]; + $app = $this->createApplication(); + + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid pipeline specification received'); + ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); + } +} diff --git a/test/Container/ApplicationFactoryTest.php b/test/Container/ApplicationFactoryTest.php index b712478a..495fb326 100644 --- a/test/Container/ApplicationFactoryTest.php +++ b/test/Container/ApplicationFactoryTest.php @@ -31,8 +31,8 @@ use Zend\Stratigility\MiddlewarePipe; use Zend\Stratigility\Middleware\PathMiddlewareDecorator; use ZendTest\Expressive\ContainerTrait; +use ZendTest\Expressive\TestAsset\CallableInteropMiddleware; use ZendTest\Expressive\TestAsset\InteropMiddleware; -use ZendTest\Expressive\TestAsset\InvokableMiddleware; /** * @covers Zend\Expressive\Container\ApplicationFactory @@ -145,13 +145,15 @@ public function testFactoryWillPullAllReplaceableDependenciesFromContainerWhenPr $this->assertSame($this->delegate, $app->getDefaultDelegate()); } - public function callableMiddlewares() + public function injectableMiddleware() { $middlewareTypes = [ - 'service-name' => InvokableMiddleware::class, - 'closure' => function () { - }, - 'callable' => [InvokableMiddleware::class, 'staticallyCallableMiddleware'], + [CallableInteropMiddleware::class], + [ + function ($request, DelegateInterface $delegate) { + }, + ], + [[CallableInteropMiddleware::class, 'staticallyCallableMiddleware']], ]; $configTypes = [ @@ -168,7 +170,7 @@ public function callableMiddlewares() } /** - * @dataProvider callableMiddlewares + * @dataProvider injectableMiddleware * * @param callable|array|string $middleware * @param string $configType diff --git a/test/TestAsset/CallableInteropMiddleware.php b/test/TestAsset/CallableInteropMiddleware.php index 0fd5c824..74e99801 100644 --- a/test/TestAsset/CallableInteropMiddleware.php +++ b/test/TestAsset/CallableInteropMiddleware.php @@ -17,4 +17,10 @@ public function __invoke(ServerRequestInterface $request, DelegateInterface $del $response = $delegate->process($request); return $response->withHeader('X-Callable-Interop-Middleware', __CLASS__); } + + public static function staticallyCallableMiddleware(ServerRequestInterface $request, DelegateInterface $delegate) + { + $response = $delegate->process($request); + return $response->withHeader('X-Invoked', __CLASS__); + } } From d0f80b9825b6a74f40648b49bd6e3e21dc7bd7f5 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 10 Mar 2018 10:08:59 -0600 Subject: [PATCH 26/35] Use `__invoke()` instead of uniform dereference syntax Latter is only available in PHP 7. --- test/ConfigProviderTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/ConfigProviderTest.php b/test/ConfigProviderTest.php index ab254b05..55eb2403 100644 --- a/test/ConfigProviderTest.php +++ b/test/ConfigProviderTest.php @@ -69,7 +69,7 @@ public function testProviderDefinesExpectedFactoryServices() public function testInvocationReturnsArrayWithDependencies() { - $config = ($this->provider)(); + $config = $this->provider->__invoke(); $this->assertInternalType('array', $config); $this->assertArrayHasKey('dependencies', $config); $this->assertArrayHasKey('aliases', $config['dependencies']); From 8117180a25624c365393d2c590d03f3a83193e48 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 10 Mar 2018 10:19:51 -0600 Subject: [PATCH 27/35] Disable conversion of errors to exceptions when running coverage reports Otherwise, `AppFactory` causes tests to halt before they even run. --- .travis.yml | 2 ++ phpunit.xml.dist-coverage | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 phpunit.xml.dist-coverage diff --git a/.travis.yml b/.travis.yml index b53f07b7..3e8ccc45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -65,7 +65,9 @@ install: - if [[ $DEPS == 'latest' ]]; then travis_retry composer update $COMPOSER_ARGS ; fi - if [[ $DEPS == 'lowest' ]]; then travis_retry composer update --prefer-lowest --prefer-stable $COMPOSER_ARGS ; fi - if [[ $TEST_COVERAGE == 'true' ]]; then travis_retry composer require --dev $COMPOSER_ARGS $COVERAGE_DEPS ; fi + - if [[ $TEST_COVERAGE == 'true' ]]; then cp phpunit.xml.dist-coverage phpunit.xml.dist ; fi - stty cols 120 && composer show + - cat phpunit.xml.dist script: - if [[ $TEST_COVERAGE == 'true' ]]; then composer test-coverage ; else composer test ; fi diff --git a/phpunit.xml.dist-coverage b/phpunit.xml.dist-coverage new file mode 100644 index 00000000..e321765d --- /dev/null +++ b/phpunit.xml.dist-coverage @@ -0,0 +1,18 @@ + + + + + ./test + + + + + + src + + + From 5ccb806cc1d71518e2f4dab1233a9a1e812f1275 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 10 Mar 2018 10:22:11 -0600 Subject: [PATCH 28/35] Converts null coalesce to isset+ternary Null coalesce is only available in PHP 7+ --- src/Container/ApplicationConfigInjectionDelegator.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Container/ApplicationConfigInjectionDelegator.php b/src/Container/ApplicationConfigInjectionDelegator.php index e154bf2a..8e923ed9 100644 --- a/src/Container/ApplicationConfigInjectionDelegator.php +++ b/src/Container/ApplicationConfigInjectionDelegator.php @@ -130,7 +130,7 @@ public static function injectPipelineFromConfig(Application $application, array ); foreach ($queue as $spec) { - $path = $spec['path'] ?? '/'; + $path = isset($spec['path']) ? $spec['path'] : '/'; $application->pipe($path, $spec['middleware']); } } @@ -196,7 +196,7 @@ public static function injectRoutesFromConfig(Application $application, array $c } } - $name = $spec['name'] ?? null; + $name = isset($spec['name']) ? $spec['name'] : null; $route = $application->route( $spec['path'], $spec['middleware'], From 8e3c7f0166af17e826322141d1bc0f22922d3356 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Sat, 10 Mar 2018 10:29:20 -0600 Subject: [PATCH 29/35] Updates license docblocks for new files to conform with .docheader --- src/ConfigProvider.php | 2 +- src/Container/ApplicationConfigInjectionDelegator.php | 2 +- src/Container/ResponseFactoryFactory.php | 2 +- src/Container/StreamFactoryFactory.php | 2 +- test/ConfigProviderTest.php | 2 +- test/Container/ApplicationConfigInjectionDelegatorTest.php | 2 +- test/Container/ResponseFactoryFactoryTest.php | 2 +- test/Container/StreamFactoryFactoryTest.php | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ConfigProvider.php b/src/ConfigProvider.php index 34ffbd07..015b9daa 100644 --- a/src/ConfigProvider.php +++ b/src/ConfigProvider.php @@ -1,7 +1,7 @@ Date: Sat, 10 Mar 2018 10:59:01 -0600 Subject: [PATCH 30/35] Exposes config-provider via package --- composer.json | 3 +++ composer.lock | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index bd596e98..b90118fd 100644 --- a/composer.json +++ b/composer.json @@ -74,6 +74,9 @@ "dev-master": "2.1.x-dev", "dev-develop": "2.2.x-dev", "dev-release-3.0.0": "3.0.x-dev" + }, + "zf": { + "config-provider": "Zend\\Expressive\\ConfigProvider" } }, "bin": [ diff --git a/composer.lock b/composer.lock index 1489985c..ec47db64 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "254736717fd6f9f35dadc9298ed768e0", + "content-hash": "0bc64fba488ce035a2ab2e7404b65431", "packages": [ { "name": "fig/http-message-util", From ec34ec9053e9ff394da1ad7facda1351dd9de921 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Mar 2018 08:48:50 -0500 Subject: [PATCH 31/35] Only implicitly register route, dispatch middleware from application factory Implicitly registering the route and dispatch middleware does not make sense _unless the zend-expressive.programmatic_pipeline configuration is enabled_. In all other cases, it is more likely going to result in a potential of accidently adding the routing and dispatch middleware prior to the rest of the pipeline. --- .../ApplicationConfigInjectionDelegator.php | 96 ------------------- src/Container/ApplicationFactory.php | 89 ++++++++++++++++- test/Application/ConfigInjectionTest.php | 50 ---------- ...pplicationConfigInjectionDelegatorTest.php | 52 ---------- 4 files changed, 84 insertions(+), 203 deletions(-) diff --git a/src/Container/ApplicationConfigInjectionDelegator.php b/src/Container/ApplicationConfigInjectionDelegator.php index 022b1b07..e90b1685 100644 --- a/src/Container/ApplicationConfigInjectionDelegator.php +++ b/src/Container/ApplicationConfigInjectionDelegator.php @@ -8,15 +8,10 @@ namespace Zend\Expressive\Container; use Psr\Container\ContainerInterface; -use Psr\Http\Message\ResponseInterface; -use ReflectionProperty; use SplPriorityQueue; use Zend\Expressive\Application; use Zend\Expressive\Exception\InvalidArgumentException; use Zend\Expressive\Exception\MissingDependencyException; -use Zend\Expressive\Router\Middleware\DispatchMiddleware; -use Zend\Expressive\Router\Middleware\RouteMiddleware; -use Zend\Expressive\Router\RouterInterface; class ApplicationConfigInjectionDelegator { @@ -113,12 +108,6 @@ public function __invoke(ContainerInterface $container, $serviceName, callable $ public static function injectPipelineFromConfig(Application $application, array $config) { if (empty($config['middleware_pipeline'])) { - if (! isset($config['routes']) || ! is_array($config['routes'])) { - return; - } - - self::pipeRoutingMiddleware($application); - self::pipeDispatchMiddleware($application); return; } @@ -285,89 +274,4 @@ private static function createPriorityQueueReducer() return $queue; }; } - - /** - * Pipe routing middleware into the application. - * - * Attempts to pull both the router and response interface from the application - * container, falling back to reflection of the application instance - * when unable to find either one. - * - * @return void - */ - private static function pipeRoutingMiddleware(Application $application) - { - $container = $application->getContainer(); - if ($container->has(RouteMiddleware::class)) { - $application->pipe(RouteMiddleware::class); - return; - } - - $application->pipe(new RouteMiddleware( - self::getRouter($application, $container), - self::getResponsePrototype($application, $container) - )); - } - - /** - * Pipe dispatch middleware into the application. - * - * If the DispatchMiddleware is present in the application's container, it - * pipes that service. Otherwise, instantiates it directly and pipes it. - * - * @return void - * @throws MissingDependencyException if the RouterInterface service is not - * found when the RouteMiddleware is not present. - */ - private static function pipeDispatchMiddleware(Application $application) - { - $container = $application->getContainer(); - $application->pipe( - $container->has(DispatchMiddleware::class) - ? $container->get(DispatchMiddleware::class) - : new DispatchMiddleware() - ); - } - - /** - * Get the router. - * - * If not available in the container, uses reflection to pull it from the - * application. - * - * @return RouterInterface - */ - private static function getRouter(Application $application, ContainerInterface $container) - { - if (! $container->has(RouterInterface::class)) { - $r = new ReflectionProperty($application, 'router'); - $r->setAccessible(true); - return $r->getValue($application); - } - - return $container->get(RouterInterface::class); - } - - /** - * Get the response prototype. - * - * If not available in the container, uses reflection to pull it from the - * application. - * - * If in the container, fetches it. If the value is callable, uses it as - * a factory to generate and return the response. - * - * @return ResponseInterface - */ - private static function getResponsePrototype(Application $application, ContainerInterface $container) - { - if (! $container->has(ResponseInterface::class)) { - $r = new ReflectionProperty($application, 'responsePrototype'); - $r->setAccessible(true); - return $r->getValue($application); - } - - $response = $container->get(ResponseInterface::class); - return is_callable($response) ? $response() : $response; - } } diff --git a/src/Container/ApplicationFactory.php b/src/Container/ApplicationFactory.php index cf8b7aa3..1849b7b4 100644 --- a/src/Container/ApplicationFactory.php +++ b/src/Container/ApplicationFactory.php @@ -8,10 +8,15 @@ namespace Zend\Expressive\Container; use ArrayObject; +use Interop\Http\ServerMiddleware\MiddlewareInterface; use Psr\Container\ContainerInterface; +use Psr\Http\Message\ResponseInterface; +use ReflectionProperty; use Zend\Diactoros\Response\EmitterInterface; use Zend\Expressive\Application; use Zend\Expressive\Router\FastRouteRouter; +use Zend\Expressive\Router\Middleware\DispatchMiddleware; +use Zend\Expressive\Router\Middleware\RouteMiddleware; use Zend\Expressive\Router\RouterInterface; /** @@ -76,7 +81,7 @@ public function __invoke(ContainerInterface $container) $app = new Application($router, $container, $delegate, $emitter); if (empty($config['zend-expressive']['programmatic_pipeline'])) { - $this->injectRoutesAndPipeline($app, $config); + $this->injectRoutesAndPipeline($container, $router, $app, $config); } return $app; @@ -85,13 +90,87 @@ public function __invoke(ContainerInterface $container) /** * Injects routes and the middleware pipeline into the application. * - * @param Application $app - * @param array $config * @return void */ - private function injectRoutesAndPipeline(Application $app, array $config) - { + private function injectRoutesAndPipeline( + ContainerInterface $container, + RouterInterface $router, + Application $app, + array $config + ) { + if (empty($config['middleware_pipeline']) + && (! isset($config['routes']) || ! is_array($config['routes'])) + ) { + return; + } + + if (empty($config['middleware_pipeline']) && isset($config['routes']) && is_array($config['routes'])) { + $app->pipe($this->getRoutingMiddleware($container, $router, $app)); + $app->pipe($this->getDispatchMiddleware($container, $app)); + } + ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); } + + /** + * Discovers or creates the route middleware. + * + * If the RouteMiddleware is present in the container, it returns the + * service. + * + * Otherwise, it creates RouteMiddleware using the router being composed in + * the application, along with a response prototype. + * + * @return MiddlewareInterface + */ + private function getRoutingMiddleware(ContainerInterface $container, RouterInterface $router, Application $app) + { + if ($container->has(RouteMiddleware::class)) { + return $container->get(RouteMiddleware::class); + } + + return new RouteMiddleware( + $router, + $this->getResponsePrototype($container, $app) + ); + } + + /** + * Discover or create the dispatch middleware. + * + * If the DispatchMiddleware is present in the application's container, it + * returns the service. Otherwise, instantiates and returns it directly. + * + * @return MiddlewareInterface + */ + private function getDispatchMiddleware(ContainerInterface $container, Application $app) + { + return $container->has(DispatchMiddleware::class) + ? $container->get(DispatchMiddleware::class) + : new DispatchMiddleware(); + } + + /** + * Get the response prototype. + * + * If not available in the container, uses reflection to pull it from the + * application. + * + * If in the container, fetches it. If the value is callable, uses it as + * a factory to generate and return the response. + * + * @return ResponseInterface + */ + private function getResponsePrototype(ContainerInterface $container, Application $app) + { + if (! $container->has(ResponseInterface::class)) { + $r = new ReflectionProperty($app, 'responsePrototype'); + $r->setAccessible(true); + return $r->getValue($app); + } + + $response = $container->get(ResponseInterface::class); + return is_callable($response) ? $response() : $response; + } } diff --git a/test/Application/ConfigInjectionTest.php b/test/Application/ConfigInjectionTest.php index 09df6940..702eb49d 100644 --- a/test/Application/ConfigInjectionTest.php +++ b/test/Application/ConfigInjectionTest.php @@ -188,56 +188,6 @@ public function testNoRoutesAreAddedIfSpecDoesNotProvidePathOrMiddleware() $this->assertCount(0, $routes); } - public function configWithRoutesButNoPipeline() - { - $middleware = function ($request, DelegateInterface $delegate) { - }; - - $routes = [ - [ - 'path' => '/', - 'middleware' => clone $middleware, - 'allowed_methods' => ['GET'], - ], - ]; - - return [ - 'no-pipeline-defined' => [['routes' => $routes]], - 'empty-pipeline' => [['middleware_pipeline' => [], 'routes' => $routes]], - 'null-pipeline' => [['middleware_pipeline' => null, 'routes' => $routes]], - ]; - } - - /** - * @dataProvider configWithRoutesButNoPipeline - * - * @param array $config - */ - public function testProvidingRoutesAndNoPipelineImplicitlyRegistersRoutingAndDispatchMiddleware(array $config) - { - $this->injectServiceInContainer($this->container, RouterInterface::class, $this->router->reveal()); - $app = $this->createApplication(); - - $app->injectPipelineFromConfig($config); - - $this->assertAttributeSame(true, 'routeMiddlewareIsRegistered', $app); - $this->assertAttributeSame(true, 'dispatchMiddlewareIsRegistered', $app); - - $r = new ReflectionProperty($app, 'pipeline'); - $r->setAccessible(true); - $pipeline = $r->getValue($app); - - $this->assertCount(2, $pipeline, 'Did not get expected pipeline count!'); - - $test = $pipeline->dequeue(); - $this->assertEquals('/', $test->path); - $this->assertInstanceOf(RouteMiddleware::class, $test->handler); - - $test = $pipeline->dequeue(); - $this->assertEquals('/', $test->path); - $this->assertInstanceOf(DispatchMiddleware::class, $test->handler); - } - public function testPipelineContainingRoutingMiddlewareConstantPipesRoutingMiddleware() { $config = [ diff --git a/test/Container/ApplicationConfigInjectionDelegatorTest.php b/test/Container/ApplicationConfigInjectionDelegatorTest.php index 5a716336..939d2745 100644 --- a/test/Container/ApplicationConfigInjectionDelegatorTest.php +++ b/test/Container/ApplicationConfigInjectionDelegatorTest.php @@ -241,58 +241,6 @@ public function testNoRoutesAreAddedIfSpecDoesNotProvidePathOrMiddleware() $this->assertCount(0, $routes); } - public function configWithRoutesButNoPipeline() - { - $middleware = function ($request, DelegateInterface $delegate) { - }; - - $routes = [ - [ - 'path' => '/', - 'middleware' => clone $middleware, - 'allowed_methods' => ['GET'], - ], - ]; - - return [ - 'no-pipeline-defined' => [['routes' => $routes]], - 'empty-pipeline' => [['middleware_pipeline' => [], 'routes' => $routes]], - 'null-pipeline' => [['middleware_pipeline' => null, 'routes' => $routes]], - ]; - } - - /** - * @dataProvider configWithRoutesButNoPipeline - * - * @param array $config - */ - public function testProvidingRoutesAndNoPipelineImplicitlyRegistersRoutingAndDispatchMiddleware(array $config) - { - $this->injectServiceInContainer( - $this->container, - PathBasedRoutingMiddleware::class, - $this->routeMiddleware - ); - $this->injectServiceInContainer( - $this->container, - DispatchMiddleware::class, - $this->dispatchMiddleware - ); - $app = $this->createApplication(); - - ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); - - $queue = $this->getQueueFromApplicationPipeline($app); - - $this->assertCount(2, $queue, 'Did not get expected pipeline count!'); - - $test = $queue->dequeue(); - $this->assertRouteMiddleware($test->handler); - - $test = $queue->dequeue(); - $this->assertDispatchMiddleware($test->handler); - } - public function testInjectPipelineFromConfigHonorsPriorityOrderWhenAttachingMiddleware() { $middleware = new InteropMiddleware(); From fce8181d2d2232212cf78588ca5dafccf92523df Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Mar 2018 14:58:51 -0500 Subject: [PATCH 32/35] Adds document covering migration to v2.2. --- docs/book/v2/reference/migration-to-v2-2.md | 267 ++++++++++++++++++++ mkdocs.yml | 4 +- 2 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 docs/book/v2/reference/migration-to-v2-2.md diff --git a/docs/book/v2/reference/migration-to-v2-2.md b/docs/book/v2/reference/migration-to-v2-2.md new file mode 100644 index 00000000..0f166c93 --- /dev/null +++ b/docs/book/v2/reference/migration-to-v2-2.md @@ -0,0 +1,267 @@ +# Migration to Expressive 2.2 + +Version 2.2 exists to message deprecated functionality, and to provide backports +of functionality from version 3.0 as it makes sense. In most cases, your code +should continue to work as it did before, but may now emit deprecation notices. +This document details some specific deprecations, and how you can change your +code to remove the messages, and, simultaneously, help prepare your code for +version 3. + +## Config providers + +The zend-expressive and zend-expressive-router packages now expose _config +providers_. These are dedicated classes that return package-specific +configuration, including dependency information. We suggest you add these to +your application's configuration. Add the following two lines in your +`config/config.php` file, inside the array passed to the `ConfigAggregator` +constructor: + +```php +\Zend\Expressive\ConfigProvider::class, +\Zend\Expressive\Router\ConfigProvider::class, +``` + +> The command `./vendor/bin/expressive migrate:expressive-v2.2` will do this for +> you. + +## Routing and dispatch middleware + +In previous releases of Expressive, you would route your routing and dispatch +middleware using the following dedicated methods: + +```php +$app->pipeRoutingMiddleware(); +$app->pipeDispatchMiddleware(); +``` + +These methods are now _deprecated_, and will be removed in version 3.0. + +Instead, you should use `pipe()` with the following services: + +```php +$app->pipe(\Zend\Expressive\Router\Middleware\RouteMiddleware::class); +$app->pipe(\Zend\Expressive\Router\Middleware\DispatchMiddleware::class); +``` + +> The command `./vendor/bin/expressive migrate:expressive-v2.2` will do this for +> you. + +This also means you can easily replace these middleware with your own at this +time! + +## Routing and dispatch constants + +If you are using configuration-driven routes, you are likely using the constants +`Zend\Expressive\Application::ROUTING_MIDDLEWARE` and `DISPATCH_MIDDLEWARE` to +indicate the routing and dispatch middleware, as follows: + +```php +'middleware_pipeline' => [ + Application::ROUTING_MIDDLEWARE, + Application::DISPATCH_MIDDLEWARE, +], +``` + +In the above section, we detailed deprecation of the methods +`pipeRoutingMiddleware()` and `pipeDispatchMiddleware()`; the constants above +are the configuration equivalent of calling these methods, and are similarly +deprecated. + +Change these entries to use the same syntax as other pipeline middleware, and +have the `middleware` key indicate the appropriate middleware class as follows: + +```php +'middleware_pipeline' => [ + [ + 'middleware' => \Zend\Expressive\Router\Middleware\RouteMiddleware::class, + ], + [ + 'middleware' => \Zend\Expressive\Router\Middleware\DispatchMiddleware::class, + ], +], +``` + +## Implicit HEAD and OPTIONS middleware + +These middleware have moved to the zend-expressive-router package. While they +still exist within the zend-expressive package, we have added deprecation +notices indicating their removal in v3. As such, update either of the following +statements, if they exist in your application: + +```php +$app->pipe(\Zend\Expressive\Middleware\ImplicitHeadMiddleware::class); +$app->pipe(\Zend\Expressive\Middleware\ImplicitOptionsMiddleware::class); +``` + +to: + +```php +$app->pipe(\Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware::class); +$app->pipe(\Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware::class); +``` + +> The command `./vendor/bin/expressive migrate:expressive-v2.2` will do this for +> you. + +## Response prototypes + +A number of services expect a _response prototype_ which will be used in order +to generate and return a response. Previously, we did not expose a service for +this, and instead hard-coded factories to create a zend-diactoros `Response` +instance when creating a service. + +In version 3, we plan to instead compose a _response factory_ in such services. +This is done to ensure a unique response prototype instance is generated for +each use; this is particularly important if you wish to use such services with +async web servers such as Swoole, ReactPHP, AMP, etc. + +To prepare for that, Expressive 2.2 does the following: + +- Creates `Zend\Expressive\Container\ResponseFactoryFactory`, and maps it to the + service name `Psr\Http\Response\ResponseInterface`. It returns a _callable_ + that will generate a zend-diactoros `Response` instance each time it is + called. + +- Creates `Zend\Expressive\Container\StreamFactoryFactory`, and maps it to the + service name `Psr\Http\Response\StreamInterface`. It returns a _callable_ + that will generate a zend-diactoros `Stream` instance (backed by a read/write + `php://temp` stream) each time it is called. + +The various factories that hard-coded generation of a response previously now +pull the `ResponseInterface` service and, if it is callable, call it to produce +a response, but otherwise use the return value. + +This change should not affect most applications, _unless they were defining a +`ResponseInterface` service previously_. In such cases, ensure your factory +mapping has precedence by placing it in a `config/autoload/` configuration file. + +## Double-Pass middleware + +_Double-pass middleware_ refers to middleware that has the following signature: + +```php +function ( + ServerReqeustInterface $request, + ResponseInterface $response, + callable $next +) : ResponseInterface +``` + +where `$next` will receive _both_ a request _and_ a response instance (this +latter is the origin of the "double-pass" phrasing). + +Such middleware was used in v1 releases of Expressive, and we have continued to +support it through v2. However, starting in v3, we will no longer allow you to +directly pipe or route such middleware. + +If you need to continue using such middleware, you will need to decorate it +using `Zend\Stratigility\Middleware\DoublePassMiddlewareDecorator()`. This +decorator class accepts the middleware and a response prototype as constructor +arguments, and decorates it to be used as http-interop middleware. (In version +3, it will decorate it as PSR-15 middleware.) + +The zend-stratigility package provides a convenience function, +`Zend\Stratigility\doublePassMiddleware()`, to simplify this for you: + +```php +use Zend\Diactoros\Response; +use function Zend\Stratigility\doublePassMiddleware; + +// Piping: +$app->pipe(doublePassMiddleware($someMiddleware, new Response())); + +// Routing: +$app->get('/foo', doublePassMiddleware($someMiddleware, new Response())); +``` + +## Other deprecations + +The following classes, traits, and instance methods were deprecated, and will be +removed in version 3: + +- `Zend\Expressive\AppFactory`: if you are using this, you will need to switch + to direct usage of `Zend\Expressive\Application` or a + `Zend\Stratigility\MiddlewarePipe` instance. + +- `Zend\Expressive\Application`: deprecates the following methods: + - `pipeRoutingMiddleware()`: [see the section above](#routing-and-dispatch-middleware) + - `pipeDispatchMiddleware()`: [see the section above](#routing-and-dispatch-middleware) + - `getContainer()`: this method is removed in version 3; container access will only be via the bootstrap. + - `getDefaultDelegate()`: the concept of a default delegate is removed in version 3. + - `getEmitter()`: emitters move to a different collaborator in version 3. + - `injectPipelineFromConfig()` andd `injectRoutesFromConfig()` are methods + defined by the `ApplicationConfigInjectionTrait`, which will be removed in + version 3. See the section on the [ApplicationConfigInjectionDelegator](#applicationconfiginjectiondelegator) + for an alternate, forwards-compatible, approach. + +- `Zend\Expressive\ApplicationConfigInjectionTrait`: if you are using it, it is + marked internal, and deprecated; it will be removed in version 3. + +- `Zend\Expressive\Container\NotFoundDelegateFactory`: the `NotFoundDelegate` + will be renamed to `Zend\Expressive\Handler\NotFoundHandler` in version 3, + making this factory obsolete. + +- `Zend\Expressive\Delegate\NotFoundDelegate`: this class becomes + `Zend\Expressive\Handler\NotFoundHandler` in v3, and the new class is added in + version 2.2 as well. + +- `Zend\Expressive\Emitter\EmitterStack`: the emitter concept is extracted from + zend-diactoros to a new component, zend-httphandlerrunner. This latter + component is used in version 3, and defines the `EmitterStack` class. Unless + you are extending it or interacting with it directly, this change should not + affect you; the `Zend\Diactoros\Response\EmitterInterface` service will be + directed to the new class in that version. + +- `Zend\Expressive\IsCallableInteropMiddlewareTrait`: if you are using it, it is + marked internal, and deprecated; it will be removed in version 3. + +- `Zend\Expressive\MarshalMiddlewareTrait`: if you are using it, it is marked + internal, and deprecated; it will be removed in version 3. + +- `Zend\Expressive\Middleware\DispatchMiddleware`: [see the section above](#routing-and-dispatch-middleware). + +- `Zend\Expressive\Middleware\ImplicitHeadMiddleware`: [see the section above](#implicit-head-and-options-middleware). + +- `Zend\Expressive\Middleware\ImplicitOptionsMiddleware`: [see the section above](#implicit-head-and-options-middleware). + +- `Zend\Expressive\Middleware\NotFoundHandler`: this will be removed in version 3, where you can instead pipe `Zend\Expressive\Handler\NotFoundHandler` directly instead. + +- `Zend\Expressive\Middleware\RouteMiddleware`: [see the section above](#routing-and-dispatch-middleware). + +## ApplicationConfigInjectionDelegator + +In addition to the above deprecations, we also provide a new class, +`Zend\Expressive\Container\ApplicationConfigInjectionDelegator`. This class +services two purposes: + +- It can act as a [delegator factory](../features/container/delegator-factories.md) + for the `Zend\Expressive\Application` service; when enabled, it will + look for `middleware_pipeline` and `routes` configuration, and use them to + inject the `Application` instance before returning it. +- It defines static methods for injecting pipelines and routes to an + `Application` instance. + +To enable the delegator as a delegator factory, add the following configuration +to a `config/autoload/` configuration file, or a configuration provider class: + +```php +'dependencies' => [ + 'delegators' => [ + \Zend\Expressive\Application::class => [ + \Zend\Expressive\Container\ApplicationConfigInjectionDelegator::class, + ], + ], +], +``` + +To manually inject an `Application` instance, you can do the following: + +```php +use Zend\Expressive\Container\ApplicationConfigInjectionDelegator; + +// assuming $config is the application configuration: +ApplicationConfigInjectionDelegator::injectPipelineFromConfig($app, $config); +ApplicationConfigInjectionDelegator::injectRoutesFromConfig($app, $config); +``` + +These changes will be forwards-compatible with version 3. diff --git a/mkdocs.yml b/mkdocs.yml index 94a762d5..6dca4a2e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -60,7 +60,9 @@ pages: - "CLI Tooling": v2/reference/cli-tooling.md - Examples: v2/reference/usage-examples.md - 'Expressive Projects': v2/reference/expressive-projects.md - - Migration: v2/reference/migration.md + - Migration: + - "To version 2": v2/reference/migration.md + - "To version 2.2": v2/reference/migration-to-v2-2.md - v1: - v1/index.md - 'Getting Started': From 8f6f3ba2e56d7b843fa7e4beae4b0bb9dabcce0f Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Mar 2018 15:26:33 -0500 Subject: [PATCH 33/35] Documents deprecations and tooling --- .../autowiring-routes-and-pipelines.md | 32 +++++++ docs/book/v2/features/application.md | 90 +++++++++---------- docs/book/v2/features/emitters.md | 10 +++ .../middleware/implicit-methods-middleware.md | 83 +++++++++-------- docs/book/v2/getting-started/standalone.md | 7 ++ 5 files changed, 140 insertions(+), 82 deletions(-) diff --git a/docs/book/v2/cookbook/autowiring-routes-and-pipelines.md b/docs/book/v2/cookbook/autowiring-routes-and-pipelines.md index 7837368a..19b9bb5a 100644 --- a/docs/book/v2/cookbook/autowiring-routes-and-pipelines.md +++ b/docs/book/v2/cookbook/autowiring-routes-and-pipelines.md @@ -208,3 +208,35 @@ delegator factory by wiring it into their own configuration. occur in exactly _one_ delegator factory. - Distributable packages should create a delegator factory for _routes only_, but _should not_ register the delegator factory by default. + +## ApplicationConfigInjectionDelegator + +- Since version 2.2 + +`Zend\Expressive\Container\ApplicationConfigInjectionDelegator` allows you to +define configuration that is then used to call `pipe()` or the various routing +methods of `Zend\Expressive\Application`. This is particularly useful for +injecting _routes_. + +The format of routes configuration is as follows: + +```php +return [ + 'routes' => [ + [ + 'path' => '/path/to/match', + 'middleware' => 'Middleware Service Name', + 'allowed_methods' => ['GET', 'POST', 'PATCH'], + 'options' => [ + 'stuff' => 'to', + 'pass' => 'to', + 'the' => 'underlying router', + ], + ], + // etc. + ], +]; +``` + +All your various modules could provide route configuration, and you could then +use a single delegator to add all of them at once. diff --git a/docs/book/v2/features/application.md b/docs/book/v2/features/application.md index ed688e9d..777ae539 100644 --- a/docs/book/v2/features/application.md +++ b/docs/book/v2/features/application.md @@ -34,50 +34,32 @@ As noted at the start of this document, we provide several ways to create an If you wish to manually instantiate the `Application` instance, it has the following constructor: -- Expressive 2.X: - - ```php - /** - * @param Zend\Expressive\Router\RouterInterface $router - * @param null|Psr\Container\ContainerInterface $container IoC container from which to pull services, if any. - * @param null|Interop\Http\ServerMiddleware\DelegateInterface $defaultDelegate - * Delegate to invoke when the internal middleware pipeline is exhausted - * without returning a response. - * @param null|Zend\Diactoros\Response\EmitterInterface $emitter Emitter to use when `run()` is - * invoked. - */ - public function __construct( - Zend\Expressive\Router\RouterInterface $router, - Psr\Container\ContainerInterface $container = null, - Interop\Http\ServerMiddleware\DelegateInterface $defaultDelegate = null, - Zend\Diactoros\Response\EmitterInterface $emitter = null - ); - ``` - -- Expressive 1.X: - - ```php - /** - * @param Zend\Expressive\Router\RouterInterface $router - * @param null|Psr\Container\ContainerInterface $container IoC container from which to pull services, if any. - * @param null|callable $finalHandler Final handler to use when $out is not - * provided on invocation. - * @param null|Zend\Diactoros\Response\EmitterInterface $emitter Emitter to use when `run()` is - * invoked. - */ - public function __construct( - Zend\Expressive\Router\RouterInterface $router, - Psr\Container\ContainerInterface $container = null, - callable $finalHandler = null, - Zend\Diactoros\Response\EmitterInterface $emitter = null - ); - ``` +```php +/** + * @param Zend\Expressive\Router\RouterInterface $router + * @param null|Psr\Container\ContainerInterface $container IoC container from which to pull services, if any. + * @param null|Interop\Http\ServerMiddleware\DelegateInterface $defaultDelegate + * Delegate to invoke when the internal middleware pipeline is exhausted + * without returning a response. + * @param null|Zend\Diactoros\Response\EmitterInterface $emitter Emitter to use when `run()` is + * invoked. + */ +public function __construct( + Zend\Expressive\Router\RouterInterface $router, + Psr\Container\ContainerInterface $container = null, + Interop\Http\ServerMiddleware\DelegateInterface $defaultDelegate = null, + Zend\Diactoros\Response\EmitterInterface $emitter = null +); +``` If no container is provided at instantiation, then all routed and piped middleware **must** be provided as callables. ### AppFactory +- Deprecated since version 2.2; instantiate `Application` directly and/or use a + `Zend\Stratigility\MiddlewarePipe` instance instead. + `Zend\Expressive\AppFactory` provides a convenience layer for creating an `Application` instance; it makes the assumption that you will use defaults in most situations, and likely only change which container and/or router you wish @@ -210,10 +192,10 @@ Read the section on [piping vs routing](router/piping.md) for more information. ### Registering routing and dispatch middleware -Routing is accomplished via a dedicated middleware method, -`Application::routeMiddleware()`; similarly, dispatching of routed middleware -has a corresponding instance middleware method, `Application::dispatchMiddleware()`. -Each can be piped/registered with other middleware platforms if desired. +Routing is accomplished via dedicated middleware, `Zend\Expressive\Middleware\RouteMiddleware`; +similarly, dispatching of routed middleware has a corresponding middleware, +`Zend\Expressive\Middleware\DispatchMiddleware`. Each can be piped/registered +with other middleware platforms if desired. These methods **MUST** be piped to the application so that the application will route and dispatch routed middleware. This is done using the following methods: @@ -226,6 +208,18 @@ $app->pipeDispatchMiddleware(); See the section on [piping](router/piping.md) to see how you can register non-routed middleware and create layered middleware applications. +> #### Changed in version 2.2 +> +> Starting in version 2.2, the methods `pipeRoutingMiddleware` and +> `pipeDispatchMiddleware` are deprecated in favor of piping the middleware +> manually. +> +> Additionally, the middleware has been ported to the zend-expressive-router +> package, under the namespace `Zend\Expressive\Router\Middleware`. We suggest +> piping them by service name. Please see our [migration documentation](../reference/migration-to-v2-2.md#routing-and-dispatch-middleware) +> for more details, and for information on how to automatically update your +> application. + ## Retrieving dependencies As noted in the intro, the `Application` class has several dependencies. Some of @@ -237,7 +231,7 @@ methods for retrieving them. They include: - `getEmitter()`: returns the composed [emitter](https://docs.zendframework.com/zend-diactoros/emitting-responses/), typically a `Zend\Expressive\Emitter\EmitterStack` instance. -- `getDefaultDelegate()`: (Since 2.0) retrieves the default delegate to use when the internal middleware pipeline is exhausted without returning a response. If none is provided at instantiation, this method will do one of the following: +- `getDefaultDelegate()`: retrieves the default delegate to use when the internal middleware pipeline is exhausted without returning a response. If none is provided at instantiation, this method will do one of the following: - If no container is composed, instanatiates a `Zend\Expressive\Delegate\NotFoundDelegate` using the composed response prototype only. @@ -247,10 +241,12 @@ methods for retrieving them. They include: the composed container, and uses the value created. - If a container is composed and contains the `Zend\Expressive\Delegate\DefaultDelegate` service, it returns that. -- `getFinalHandler(ResponseInterface $response = null)`: (**REMOVED in version 2.0**) - retrieves the final handler instance. This is middleware with the signature - `function ($request, $response, $error = null)`, and it is invoked when the - middleware pipeline queue is depleted and no response has been returned. + +> #### Deprecated +> +> Each of the above methods are deprecated starting in version 2.2, and will be +> removed in version 3.0. + ## Executing the application: run() diff --git a/docs/book/v2/features/emitters.md b/docs/book/v2/features/emitters.md index 6141a1c2..8fdc5d91 100644 --- a/docs/book/v2/features/emitters.md +++ b/docs/book/v2/features/emitters.md @@ -39,3 +39,13 @@ $stack->push($emitterInstance); As a stack, execution is in LIFO (last in, first out) order; the first emitter on the stack will be evaluated last. + +> ## Deprecated with version 2.2 +> +> Starting in version 2.2, the `EmitterStack` is deprecated, and moved, along with the +> zend-diactoros `EmitterInterface` and implementations, to a new package, +> [zend-httphandlerrunner](https://docs.zendframework.com/zend-httphandlerrunner). +> +> The interface and the `EmitterStack` are roughly identical to what is present in +> version 2; if you are defining a `Zend\Diactoros\Emitter\EmitterInterface` +> service of your own, you will need to update it in that version. diff --git a/docs/book/v2/features/middleware/implicit-methods-middleware.md b/docs/book/v2/features/middleware/implicit-methods-middleware.md index ce0b9174..e05071d3 100644 --- a/docs/book/v2/features/middleware/implicit-methods-middleware.md +++ b/docs/book/v2/features/middleware/implicit-methods-middleware.md @@ -7,6 +7,19 @@ _must_ support `HEAD` requests for any given URI, and that they _should_ support layer, and middleware that can detect _implicit_ support for these methods (i.e., the route was not registered _explicitly_ with the method). +> ## Versions prior to 2.2 +> +> If you are using Expressive versions earlier than 2.2, you may define a +> `Zend\Expressive\Middleware\ImplicitHeadMiddleware` or +> `Zend\Expressive\Middleware\ImplicitOptionsMiddleware` service under the +> `invokables` service configuration. +> +> However, starting in version 2.2, these classes are deprecated in favor of their +> equivalents that are now offered in the zend-expressive-router v2.4+ releases, +> under the namespace `Zend\Expressive\Router\Middleware`. +> +> The documentation here has been updated to reflect usage under Expressive 2.2+. + ## ImplicitHeadMiddleware `Zend\Expressive\Middleware\ImplicitHeadMiddleware` provides support for @@ -14,27 +27,27 @@ handling `HEAD` requests to routed middleware when the route does not expliclity allow for the method. It should be registered _between_ the routing and dispatch middleware. -By default, it can be instantiated with no extra arguments. However, you _may_ -provide a response instance to use by default to the constructor if you need to -craft special headers, status code, etc. +To use it, it must first be registered with your container. The easiest way to +do that is to register the zend-expressive-router `ConfigProvider` in your +`config/config.php`: -Register the dependency via `dependencies` configuration: +```php +$aggregator = new ConfigAggregator([ + \Zend\Expressive\Router\ConfigProvider::class, +``` + +Alternately, add the following dependency configuration in one of your +`config/autoload/` configuration files or a `ConfigProvider` class: ```php -use Zend\Expressive\Middleware\ImplicitHeadMiddleware; - -return [ - 'dependencies' => [ - 'invokables' => [ - ImplicitHeadMiddleware::class => ImplicitHeadMiddleware::class, - ], - - // or, if you have defined a factory to inject a response: - 'factories' => [ - ImplicitHeadMiddleware::class => \Your\ImplicitHeadMiddlewareFactory::class, - ], +use Zend\Expressive\Router\Middleware\ImplicitHeadMiddleware; +use Zend\Expressive\Router\Middleware\ImplicitHeadMiddlewareFactory; + +'dependencies' => [ + 'factories' => [ + ImplicitHeadMiddleware::class => ImplicitHeadMiddlewareFactory::class, ], -]; +], ``` Within your application pipeline, add the middleware between the routing and @@ -80,32 +93,32 @@ the headers returned if desired. ## ImplicitOptionsMiddleware -`Zend\Expressive\Middleware\ImplicitOptionsMiddleware` provides support for +`Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware` provides support for handling `OPTIONS` requests to routed middleware when the route does not expliclity allow for the method. Like the `ImplicitHeadMiddleware`, it should be registered _between_ the routing and dispatch middleware. -By default, it can be instantiated with no extra arguments. However, you _may_ -provide a response prototype instance to use by default to the constructor if -you need to craft special headers, status code, etc. +To use it, it must first be registered with your container. The easiest way to +do that is to register the zend-expressive-router `ConfigProvider` in your +`config/config.php`: -Register the dependency via `dependencies` configuration: +```php +$aggregator = new ConfigAggregator([ + \Zend\Expressive\Router\ConfigProvider::class, +``` + +Alternately, add the following dependency configuration in one of your +`config/autoload/` configuration files or a `ConfigProvider` class: ```php -use Zend\Expressive\Middleware\ImplicitOptionsMiddleware; - -return [ - 'dependencies' => [ - 'invokables' => [ - ImplicitOptionsMiddleware::class => ImplicitOptionsMiddleware::class, - ], - - // or, if you have defined a factory to inject a response: - 'factories' => [ - ImplicitOptionsMiddleware::class => \Your\ImplicitOptionsMiddlewareFactory::class, - ], +use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddleware; +use Zend\Expressive\Router\Middleware\ImplicitOptionsMiddlewareFactory; + +'dependencies' => [ + 'factories' => [ + ImplicitOptionsMiddleware::class => ImplicitOptionsMiddlewareFactory::class, ], -]; +], ``` Within your application pipeline, add the middleware between the routing and diff --git a/docs/book/v2/getting-started/standalone.md b/docs/book/v2/getting-started/standalone.md index cfaa510c..9dca77b9 100644 --- a/docs/book/v2/getting-started/standalone.md +++ b/docs/book/v2/getting-started/standalone.md @@ -4,6 +4,13 @@ Expressive allows you to get started at your own pace. You can start with the simplest example, detailed below, or move on to a more structured, configuration-driven approach as detailed in the [use case examples](../reference/usage-examples.md). +> ## Deprecated with version 2.2 +> +> The `Zend\Expressive\AppFactory` detailed in this chapter is deprecated as of +> version 2.2, and will be removed in version 3.0. We recommend instead +> constructing `Zend\Expressive\Application` manually, or using a +> `Zend\Stratigility\MiddlewarePipe` instance instead. + ## 1. Create a new project directory First, let's create a new project directory and enter it: From 2ff59a341b2717019da691bce659dc770c36828c Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Mar 2018 15:36:03 -0500 Subject: [PATCH 34/35] Adds CHANGELOG for 2.2.0 --- CHANGELOG.md | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index daf832f3..30891f5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,114 @@ All notable changes to this project will be documented in this file, in reverse chronological order by release. +## 2.2.0 - 2018-03-12 + +### Added + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) adds the + class `Zend\Expressive\ConfigProvider`, and exposes it to the + zend-component-installer Composer plugin. We recommend updating your + `config/config.php` to reference it, as well as the + `Zend\Expressive\Router\ConfigProvider` shipped with zend-expressive-router + versions 2.4 and up. + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) adds the + class `Zend\Expressive\Container\ApplicationConfigInjectionDelegator`. The + class can act as a delegator factory, and, when enabled, will inject routes + and pipeline middleware defined in configuration. + + Additionally, the class exposes two static methods: + + - `injectPipelineFromConfig(Application $app, array $config)` + - `injectRoutesFromConfig(Application $app, array $config)` + + These may be called to modify an `Application` instance based on an array of + configuration. See thd documentation for more details. + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) adds the + class `Zend\Expressive\Handler\NotFoundHandler`; the class takes over the + functionality previously provided in `Zend\Expressive\Delegate\NotFoundDelegate`. + +### Changed + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) updates the + minimum supported zend-stratigility version to 2.2.0. + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) updates the + minimum supported zend-expressive-router version to 2.4.0. + +### Deprecated + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) deprecates + the following classes and traits: + + - `Zend\Expressive\AppFactory`: if you are using this, you will need to switch + to direct usage of `Zend\Expressive\Application` or a + `Zend\Stratigility\MiddlewarePipe` instance. + + - `Zend\Expressive\ApplicationConfigInjectionTrait`: if you are using it, it is + marked internal, and deprecated; it will be removed in version 3. + + - `Zend\Expressive\Container\NotFoundDelegateFactory`: the `NotFoundDelegate` + will be renamed to `Zend\Expressive\Handler\NotFoundHandler` in version 3, + making this factory obsolete. + + - `Zend\Expressive\Delegate\NotFoundDelegate`: this class becomes + `Zend\Expressive\Handler\NotFoundHandler` in v3, and the new class is added in + version 2.2 as well. + + - `Zend\Expressive\Emitter\EmitterStack`: the emitter concept is extracted from + zend-diactoros to a new component, zend-httphandlerrunner. This latter + component is used in version 3, and defines the `EmitterStack` class. Unless + you are extending it or interacting with it directly, this change should not + affect you; the `Zend\Diactoros\Response\EmitterInterface` service will be + directed to the new class in that version. + + - `Zend\Expressive\IsCallableInteropMiddlewareTrait`: if you are using it, it is + marked internal, and deprecated; it will be removed in version 3. + + - `Zend\Expressive\MarshalMiddlewareTrait`: if you are using it, it is marked + internal, and deprecated; it will be removed in version 3. + + - `Zend\Expressive\Middleware\DispatchMiddleware`: this functionality has been + moved to zend-expressive-router, under the `Zend\Expressive\Router\Middleware` + namespace. + + - `Zend\Expressive\Middleware\ImplicitHeadMiddleware`: this functionality has been + moved to zend-expressive-router, under the `Zend\Expressive\Router\Middleware` + namespace. + + - `Zend\Expressive\Middleware\ImplicitOptionsMiddleware`: this functionality has been + moved to zend-expressive-router, under the `Zend\Expressive\Router\Middleware` + namespace. + + - `Zend\Expressive\Middleware\NotFoundHandler`: this will be removed in + version 3, where you can instead pipe `Zend\Expressive\Handler\NotFoundHandler` + directly instead. + + - `Zend\Expressive\Middleware\RouteMiddleware`: this functionality has been + moved to zend-expressive-router, under the `Zend\Expressive\Router\Middleware` + namespace. + +- [#581](https://github.com/zendframework/zend-expressive/pull/581) deprecates + the following methods from `Zend\Expressive\Application`: + - `pipeRoutingMiddleware()` + - `pipeDispatchMiddleware()` + - `getContainer()`: this method is removed in version 3; container access will only be via the bootstrap. + - `getDefaultDelegate()`: the concept of a default delegate is removed in version 3. + - `getEmitter()`: emitters move to a different collaborator in version 3. + - `injectPipelineFromConfig()` andd `injectRoutesFromConfig()` are methods + defined by the `ApplicationConfigInjectionTrait`, which will be removed in + version 3. Use the `ApplicationConfigInjectionDelegator` instead. + +### Removed + +- Nothing. + +### Fixed + +- Nothing. + ## 2.1.1 - 2018-03-09 ### Added From 8e467db775d4a4b2195b320ef9257e3dec06bed6 Mon Sep 17 00:00:00 2001 From: Matthew Weier O'Phinney Date: Mon, 12 Mar 2018 16:15:06 -0500 Subject: [PATCH 35/35] Use stable 2.2.0 release of zend-stratigility --- composer.json | 2 +- composer.lock | 21 +++++++++------------ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/composer.json b/composer.json index b90118fd..d5c2e5c5 100644 --- a/composer.json +++ b/composer.json @@ -29,7 +29,7 @@ "zendframework/zend-diactoros": "^1.3.10", "zendframework/zend-expressive-router": "^2.4.1", "zendframework/zend-expressive-template": "^1.0.4", - "zendframework/zend-stratigility": "^2.2.0rc3" + "zendframework/zend-stratigility": "^2.2.0" }, "require-dev": { "filp/whoops": "^2.1.6 || ^1.1.10", diff --git a/composer.lock b/composer.lock index ec47db64..8cd6e07f 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "0bc64fba488ce035a2ab2e7404b65431", + "content-hash": "43ef3ad68c22086ad9fa309ae57ea601", "packages": [ { "name": "fig/http-message-util", @@ -545,16 +545,16 @@ }, { "name": "zendframework/zend-stratigility", - "version": "2.2.0rc3", + "version": "2.2.0", "source": { "type": "git", "url": "https://github.com/zendframework/zend-stratigility.git", - "reference": "824b7fba07c81dacf54eddafc15935bf9db1a45f" + "reference": "e8c413fcba926ede63099936a5f86acf9b8156c5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/824b7fba07c81dacf54eddafc15935bf9db1a45f", - "reference": "824b7fba07c81dacf54eddafc15935bf9db1a45f", + "url": "https://api.github.com/repos/zendframework/zend-stratigility/zipball/e8c413fcba926ede63099936a5f86acf9b8156c5", + "reference": "e8c413fcba926ede63099936a5f86acf9b8156c5", "shasum": "" }, "require": { @@ -575,9 +575,8 @@ "type": "library", "extra": { "branch-alias": { - "dev-master": "2.1.x-dev", - "dev-develop": "2.2.x-dev", - "dev-release-3.0.0": "3.0.x-dev" + "dev-master": "2.2.x-dev", + "dev-develop": "3.0.x-dev" } }, "autoload": { @@ -603,7 +602,7 @@ "psr-7", "zf" ], - "time": "2018-03-08T20:47:18+00:00" + "time": "2018-03-12T21:04:19+00:00" } ], "packages-dev": [ @@ -3362,9 +3361,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "zendframework/zend-stratigility": 5 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": {