diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..9e4c2eee0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,8 @@ +version: 2 +updates: +- package-ecosystem: composer + directory: "/" + schedule: + interval: monthly + open-pull-requests-limit: 10 + versioning-strategy: increase diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4fe461afc..63bf99355 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,7 @@ jobs: strategy: fail-fast: false matrix: - php: [7.2, 7.3, 7.4, 8.0] + php: [7.3, 7.4, 8.0] experimental: [false] include: - php: 8.0 @@ -37,7 +37,7 @@ jobs: - name: Static analysis if: matrix.analysis - run: vendor/bin/phpstan analyse Slim + run: vendor/bin/phpstan - name: Tests run: vendor/bin/phpunit --coverage-clover clover.xml @@ -47,5 +47,5 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - composer require php-coveralls/php-coveralls -n + composer require php-coveralls/php-coveralls -n -W vendor/bin/php-coveralls --coverage_clover=clover.xml -v diff --git a/CHANGELOG.md b/CHANGELOG.md index 1f5e823dc..46dc21bf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,22 @@ # Changelog +# 4.8.0 - 2021-05-19 +- [3034: Fix phpunit dependency version](https://github.com/slimphp/Slim/pull/3034) thanks to @l0gicgate +- [3037: Replace Travis by GitHub Actions](https://github.com/slimphp/Slim/pull/3037) thanks to @t0mmy742 +- [3043: Cover App creation from AppFactory with empty Container](https://github.com/slimphp/Slim/pull/3043) thanks to @t0mmy742 +- [3045: Update phpstan/phpstan requirement from ^0.12.58 to ^0.12.64](https://github.com/slimphp/Slim/pull/3045) thanks to @dependabot-preview[bot] +- [3047: documentation: min php 7.2 required](https://github.com/slimphp/Slim/pull/3047) thanks to @Rotzbua +- [3054: Update phpstan/phpstan requirement from ^0.12.64 to ^0.12.70](https://github.com/slimphp/Slim/pull/3054) thanks to @dependabot-preview[bot] +- [3056: Fix docblock in ErrorMiddleware](https://github.com/slimphp/Slim/pull/3056) thanks to @piotr-cz +- [3060: Update phpstan/phpstan requirement from ^0.12.70 to ^0.12.80](https://github.com/slimphp/Slim/pull/3060) thanks to @dependabot-preview[bot] +- [3061: Update nyholm/psr7 requirement from ^1.3 to ^1.4](https://github.com/slimphp/Slim/pull/3061) thanks to @dependabot-preview[bot] +- [3063: Allow ^1.0 || ^2.0 in psr/container](https://github.com/slimphp/Slim/pull/3063) thanks to @Ayesh +- [3069: Classname/Method Callable Arrays](https://github.com/slimphp/Slim/pull/3069) thanks to @ddrv +- [3078: Update squizlabs/php_codesniffer requirement from ^3.5 to ^3.6](https://github.com/slimphp/Slim/pull/3078) thanks to @dependabot[bot] +- [3079: Update phpspec/prophecy requirement from ^1.12 to ^1.13](https://github.com/slimphp/Slim/pull/3079) thanks to @dependabot[bot] +- [3080: Update guzzlehttp/psr7 requirement from ^1.7 to ^1.8](https://github.com/slimphp/Slim/pull/3080) thanks to @dependabot[bot] +- [3082: Update phpstan/phpstan requirement from ^0.12.80 to ^0.12.85](https://github.com/slimphp/Slim/pull/3082) thanks to @dependabot[bot] + # 4.7.0 - 2020-11-30 ### Fixed diff --git a/README.md b/README.md index ad1177db6..59ff1d857 100644 --- a/README.md +++ b/README.md @@ -12,17 +12,17 @@ Slim is a PHP micro-framework that helps you quickly write simple yet powerful w It's recommended that you use [Composer](https://getcomposer.org/) to install Slim. ```bash -$ composer require slim/slim:^4.0 +$ composer require slim/slim ``` -This will install Slim and all required dependencies. Slim requires PHP 7.2 or newer. +This will install Slim and all required dependencies. Slim requires PHP 7.3 or newer. ## Choose a PSR-7 Implementation & ServerRequest Creator Before you can get up and running with Slim you will need to choose a PSR-7 implementation that best fits your application. A few notable ones: - [Slim-Psr7](https://github.com/slimphp/Slim-Psr7) - This is the Slim Framework PSR-7 implementation - [Nyholm/psr7](https://github.com/Nyholm/psr7) & [Nyholm/psr7-server](https://github.com/Nyholm/psr7-server) - This is the fastest, strictest and most lightweight implementation available -- [Guzzle/psr7](https://github.com/guzzle/psr7) & [http-interop/http-factory-guzzle](https://github.com/http-interop/http-factory-guzzle) - This is the implementation used by the Guzzle Client, featuring extra functionality for stream and file handling +- [Guzzle/psr7](https://github.com/guzzle/psr7) - This is the implementation used by the Guzzle Client, featuring extra functionality for stream and file handling - [laminas-diactoros](https://github.com/laminas/laminas-diactoros) - This is the Laminas (Zend) PSR-7 implementation @@ -54,7 +54,7 @@ $app = AppFactory::create(); In order for auto-detection to work and enable you to use `AppFactory::create()` and `App::run()` without having to manually create a `ServerRequest` you need to install one of the following implementations: - [Slim-Psr7](https://github.com/slimphp/Slim-Psr7) - Install using `composer require slim/psr7` - [Nyholm/psr7](https://github.com/Nyholm/psr7) & [Nyholm/psr7-server](https://github.com/Nyholm/psr7-server) - Install using `composer require nyholm/psr7 nyholm/psr7-server` -- [Guzzle/psr7](https://github.com/guzzle/psr7) & [http-interop/http-factory-guzzle](https://github.com/http-interop/http-factory-guzzle) - Install using `composer require guzzlehttp/psr7 http-interop/http-factory-guzzle` +- [Guzzle/psr7](https://github.com/guzzle/psr7) - Install using `composer require guzzlehttp/psr7` - [laminas-diactoros](https://github.com/laminas/laminas-diactoros) - Install using `composer require laminas/laminas-diactoros` Then create file _public/index.php_. diff --git a/Slim/App.php b/Slim/App.php index b97a6969b..8799188ba 100644 --- a/Slim/App.php +++ b/Slim/App.php @@ -38,7 +38,7 @@ class App extends RouteCollectorProxy implements RequestHandlerInterface * * @var string */ - public const VERSION = '4.7.0'; + public const VERSION = '4.8.1'; /** * @var RouteResolverInterface diff --git a/Slim/CallableResolver.php b/Slim/CallableResolver.php index f2c5fe4cd..c1c351276 100644 --- a/Slim/CallableResolver.php +++ b/Slim/CallableResolver.php @@ -51,6 +51,7 @@ public function __construct(?ContainerInterface $container = null) */ public function resolve($toResolve): callable { + $toResolve = $this->prepareToResolve($toResolve); if (is_callable($toResolve)) { return $this->bindToContainer($toResolve); } @@ -90,6 +91,7 @@ public function resolveMiddleware($toResolve): callable */ private function resolveByPredicate($toResolve, callable $predicate, string $defaultMethod): callable { + $toResolve = $this->prepareToResolve($toResolve); if (is_callable($toResolve)) { return $this->bindToContainer($toResolve); } @@ -144,6 +146,9 @@ private function resolveSlimNotation(string $toResolve): array $instance = $this->container->get($class); } else { if (!class_exists($class)) { + if ($method) { + $class .= '::' . $method . '()'; + } throw new RuntimeException(sprintf('Callable %s does not exist', $class)); } $instance = new $class($this->container); @@ -187,4 +192,22 @@ private function bindToContainer(callable $callable): callable } return $callable; } + + /** + * @param string|callable $toResolve + * @return string|callable + */ + private function prepareToResolve($toResolve) + { + if (!is_array($toResolve)) { + return $toResolve; + } + $candidate = $toResolve; + $class = array_shift($candidate); + $method = array_shift($candidate); + if (is_string($class) && is_string($method)) { + return $class . ':' . $method; + } + return $toResolve; + } } diff --git a/Slim/Factory/Psr17/GuzzlePsr17Factory.php b/Slim/Factory/Psr17/GuzzlePsr17Factory.php index 8ac770291..702d537d1 100644 --- a/Slim/Factory/Psr17/GuzzlePsr17Factory.php +++ b/Slim/Factory/Psr17/GuzzlePsr17Factory.php @@ -12,8 +12,8 @@ class GuzzlePsr17Factory extends Psr17Factory { - protected static $responseFactoryClass = 'Http\Factory\Guzzle\ResponseFactory'; - protected static $streamFactoryClass = 'Http\Factory\Guzzle\StreamFactory'; + protected static $responseFactoryClass = 'GuzzleHttp\Psr7\HttpFactory'; + protected static $streamFactoryClass = 'GuzzleHttp\Psr7\HttpFactory'; protected static $serverRequestCreatorClass = 'GuzzleHttp\Psr7\ServerRequest'; protected static $serverRequestCreatorMethod = 'fromGlobals'; } diff --git a/Slim/Factory/Psr17/Psr17FactoryProvider.php b/Slim/Factory/Psr17/Psr17FactoryProvider.php index 113ee432e..a17670e4b 100644 --- a/Slim/Factory/Psr17/Psr17FactoryProvider.php +++ b/Slim/Factory/Psr17/Psr17FactoryProvider.php @@ -23,7 +23,6 @@ class Psr17FactoryProvider implements Psr17FactoryProviderInterface SlimPsr17Factory::class, NyholmPsr17Factory::class, LaminasDiactorosPsr17Factory::class, - ZendDiactorosPsr17Factory::class, GuzzlePsr17Factory::class, ]; diff --git a/Slim/Factory/Psr17/ZendDiactorosPsr17Factory.php b/Slim/Factory/Psr17/ZendDiactorosPsr17Factory.php deleted file mode 100644 index 779e761e7..000000000 --- a/Slim/Factory/Psr17/ZendDiactorosPsr17Factory.php +++ /dev/null @@ -1,19 +0,0 @@ - $context + * @param mixed $level + * @param string|Stringable $message + * @param array $context * * @return void * * @throws InvalidArgumentException */ - public function log($level, $message, array $context = []) + public function log($level, $message, array $context = []): void { - error_log($message); + error_log((string) $message); } } diff --git a/composer.json b/composer.json index e0ba2de67..2c8fa0b29 100644 --- a/composer.json +++ b/composer.json @@ -43,31 +43,30 @@ "wiki": "https://github.com/slimphp/Slim/wiki" }, "require": { - "php": "^7.2 || ^8.0", + "php": "^7.3 || ^8.0", "ext-json": "*", "nikic/fast-route": "^1.3", - "psr/container": "^1.0", + "psr/container": "^1.0 || ^2.0", "psr/http-factory": "^1.0", "psr/http-message": "^1.0", "psr/http-server-handler": "^1.0", "psr/http-server-middleware": "^1.0", - "psr/log": "^1.1" + "psr/log": "^1.1 || ^2.0 || ^3.0" }, "require-dev": { "ext-simplexml": "*", "adriansuter/php-autoload-override": "^1.2", - "guzzlehttp/psr7": "^1.7", - "http-interop/http-factory-guzzle": "^1.0", - "laminas/laminas-diactoros": "^2.4", - "nyholm/psr7": "^1.3", - "nyholm/psr7-server": "^1.0.1", - "phpspec/prophecy": "^1.12", - "phpstan/phpstan": "^0.12.70", - "phpunit/phpunit": "^8.5.13 || ^9.3.8", + "guzzlehttp/psr7": "^2.0", + "laminas/laminas-diactoros": "^2.8", + "nyholm/psr7": "^1.4", + "nyholm/psr7-server": "^1.0", + "phpspec/prophecy": "^1.14", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/phpstan": "^0.12.99", + "phpunit/phpunit": "^9.5", "slim/http": "^1.2", - "slim/psr7": "^1.3", - "squizlabs/php_codesniffer": "^3.5", - "weirdan/prophecy-shim": "^1.0 || ^2.0.2" + "slim/psr7": "^1.5", + "squizlabs/php_codesniffer": "^3.6" }, "autoload": { "psr-4": { @@ -87,7 +86,7 @@ ], "phpunit": "phpunit", "phpcs": "phpcs", - "phpstan": "phpstan analyse Slim --memory-limit=-1" + "phpstan": "phpstan --memory-limit=-1" }, "suggest": { "ext-simplexml": "Needed to support XML format in BodyParsingMiddleware", diff --git a/phpstan.neon.dist b/phpstan.neon.dist index 036e5a516..d81898e60 100644 --- a/phpstan.neon.dist +++ b/phpstan.neon.dist @@ -1,3 +1,4 @@ parameters: - level: max - inferPrivatePropertyTypeFromConstructor: true + level: max + paths: + - Slim diff --git a/phpunit.xml.dist b/phpunit.xml.dist index ca2714d44..2bbfaf422 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,36 +1,24 @@ - ./tests/ + tests - - - ./Slim/ - - - - - + + + + Slim + + + + + diff --git a/tests/Assets/PhpFunctionOverrides.php b/tests/Assets/PhpFunctionOverrides.php deleted file mode 100644 index c13b8cb69..000000000 --- a/tests/Assets/PhpFunctionOverrides.php +++ /dev/null @@ -1,58 +0,0 @@ - $string, - 'replace' => $replace, - 'status_code' => $statusCode, - ] - ); -} - -/** - * Allows the mocking of invalid HTTP states. - * - * @return int - */ -function connection_status() -{ - if (isset($GLOBALS['connection_status_return'])) { - return $GLOBALS['connection_status_return']; - } - - return \connection_status(); -} diff --git a/tests/Assets/PhpRoutingFunctionOverrides.php b/tests/Assets/PhpRoutingFunctionOverrides.php deleted file mode 100644 index 40f441933..000000000 --- a/tests/Assets/PhpRoutingFunctionOverrides.php +++ /dev/null @@ -1,41 +0,0 @@ -assertEquals(3, CallableTest::$CalledCount); } + public function testSlimCallableAsArray() + { + $resolver = new CallableResolver(); // No container injected + $callable = $resolver->resolve([CallableTest::class, 'toCall']); + $callableRoute = $resolver->resolveRoute([CallableTest::class, 'toCall']); + $callableMiddleware = $resolver->resolveMiddleware([CallableTest::class, 'toCall']); + + $callable(); + $this->assertEquals(1, CallableTest::$CalledCount); + + $callableRoute(); + $this->assertEquals(2, CallableTest::$CalledCount); + + $callableMiddleware(); + $this->assertEquals(3, CallableTest::$CalledCount); + } + public function testSlimCallableContainer() { /** @var ContainerInterface $container */ @@ -150,6 +167,23 @@ public function testSlimCallableContainer() $this->assertEquals($container, CallableTest::$CalledContainer); } + public function testSlimCallableAsArrayContainer() + { + /** @var ContainerInterface $container */ + $container = $this->containerProphecy->reveal(); + $resolver = new CallableResolver($container); + $resolver->resolve([CallableTest::class, 'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + + CallableTest::$CalledContainer = null; + $resolver->resolveRoute([CallableTest::class, 'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + + CallableTest::$CalledContainer = null; + $resolver->resolveMiddleware([CallableTest::class ,'toCall']); + $this->assertEquals($container, CallableTest::$CalledContainer); + } + public function testContainer() { $this->containerProphecy->has('callable_service')->willReturn(true); @@ -441,7 +475,7 @@ public function testMiddlewareFunctionNotFoundThrowException() public function testClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -452,7 +486,7 @@ public function testClassNotFoundThrowException() public function testRouteClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -463,7 +497,7 @@ public function testRouteClassNotFoundThrowException() public function testMiddlewareClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('Callable Unknown does not exist'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -474,7 +508,7 @@ public function testMiddlewareClassNotFoundThrowException() public function testCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -485,7 +519,7 @@ public function testCallableClassNotFoundThrowException() public function testRouteCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); @@ -496,7 +530,7 @@ public function testRouteCallableClassNotFoundThrowException() public function testMiddlewareCallableClassNotFoundThrowException() { $this->expectException(RuntimeException::class); - $this->expectExceptionMessage('is not resolvable'); + $this->expectExceptionMessage('Callable Unknown::notFound() does not exist'); /** @var ContainerInterface $container */ $container = $this->containerProphecy->reveal(); diff --git a/tests/Factory/AppFactoryTest.php b/tests/Factory/AppFactoryTest.php index ecfae162e..4ea36ba90 100644 --- a/tests/Factory/AppFactoryTest.php +++ b/tests/Factory/AppFactoryTest.php @@ -10,7 +10,7 @@ namespace Slim\Tests\Factory; -use Http\Factory\Guzzle\ResponseFactory as GuzzleResponseFactory; +use GuzzleHttp\Psr7\HttpFactory; use Laminas\Diactoros\ResponseFactory as LaminasDiactorosResponseFactory; use Nyholm\Psr7\Factory\Psr17Factory; use Psr\Container\ContainerInterface; @@ -25,7 +25,6 @@ use Slim\Factory\Psr17\NyholmPsr17Factory; use Slim\Factory\Psr17\Psr17FactoryProvider; use Slim\Factory\Psr17\SlimPsr17Factory; -use Slim\Factory\Psr17\ZendDiactorosPsr17Factory; use Slim\Http\Factory\DecoratedResponseFactory; use Slim\Http\Response as DecoratedResponse; use Slim\Interfaces\CallableResolverInterface; @@ -37,7 +36,6 @@ use Slim\Routing\RouteCollector; use Slim\Tests\Mocks\MockPsr17FactoryWithoutStreamFactory; use Slim\Tests\TestCase; -use Zend\Diactoros\ResponseFactory as ZendDiactorosResponseFactory; class AppFactoryTest extends TestCase { @@ -46,9 +44,8 @@ public function provideImplementations() return [ [SlimPsr17Factory::class, SlimResponseFactory::class], [NyholmPsr17Factory::class, Psr17Factory::class], - [GuzzlePsr17Factory::class, GuzzleResponseFactory::class], + [GuzzlePsr17Factory::class, HttpFactory::class], [LaminasDiactorosPsr17Factory::class, LaminasDiactorosResponseFactory::class], - [ZendDiactorosPsr17Factory::class, ZendDiactorosResponseFactory::class], ]; } diff --git a/tests/Factory/ServerRequestCreatorFactoryTest.php b/tests/Factory/ServerRequestCreatorFactoryTest.php index dc84307e6..5f0de9e29 100644 --- a/tests/Factory/ServerRequestCreatorFactoryTest.php +++ b/tests/Factory/ServerRequestCreatorFactoryTest.php @@ -21,13 +21,11 @@ use Slim\Factory\Psr17\Psr17FactoryProvider; use Slim\Factory\Psr17\SlimHttpServerRequestCreator; use Slim\Factory\Psr17\SlimPsr17Factory; -use Slim\Factory\Psr17\ZendDiactorosPsr17Factory; use Slim\Factory\ServerRequestCreatorFactory; use Slim\Http\ServerRequest; use Slim\Interfaces\ServerRequestCreatorInterface; use Slim\Psr7\Request as SlimServerRequest; use Slim\Tests\TestCase; -use Zend\Diactoros\ServerRequest as ZendDiactorosServerRequest; class ServerRequestCreatorFactoryTest extends TestCase { @@ -38,7 +36,6 @@ public function provideImplementations() [NyholmPsr17Factory::class, NyholmServerRequest::class], [GuzzlePsr17Factory::class, GuzzleServerRequest::class], [LaminasDiactorosPsr17Factory::class, LaminasDiactorosServerRequest::class], - [ZendDiactorosPsr17Factory::class, ZendDiactorosServerRequest::class], ]; } diff --git a/tests/MigratePhpUnitDeprecations.php b/tests/MigratePhpUnitDeprecations.php deleted file mode 100644 index 100878ff7..000000000 --- a/tests/MigratePhpUnitDeprecations.php +++ /dev/null @@ -1,27 +0,0 @@ - [ 'connection_status' => function (): int {