Browse Source

Merge branch 'master' into 4.next

Mark Story 6 years ago
parent
commit
d9456f3658

+ 31 - 36
phpstan-baseline.neon

@@ -21,6 +21,16 @@ parameters:
 			path: src/Auth/Storage/SessionStorage.php
 
 		-
+			message: "#^Return type \\(\\$this\\(Cake\\\\Auth\\\\Storage\\\\SessionStorage\\)\\) of method Cake\\\\Auth\\\\Storage\\\\SessionStorage\\:\\:setConfig\\(\\) should be compatible with return type \\(\\$this\\(Cake\\\\Core\\\\InstanceConfigTrait\\)\\) of method Cake\\\\Core\\\\InstanceConfigTrait\\:\\:setConfig\\(\\)$#"
+			count: 1
+			path: src/Auth/Storage/SessionStorage.php
+
+		-
+			message: "#^Return type \\(\\$this\\(Cake\\\\Auth\\\\Storage\\\\SessionStorage\\)\\) of method Cake\\\\Auth\\\\Storage\\\\SessionStorage\\:\\:configShallow\\(\\) should be compatible with return type \\(\\$this\\(Cake\\\\Core\\\\InstanceConfigTrait\\)\\) of method Cake\\\\Core\\\\InstanceConfigTrait\\:\\:configShallow\\(\\)$#"
+			count: 1
+			path: src/Auth/Storage/SessionStorage.php
+
+		-
 			message: "#^Access to undefined constant Memcached\\:\\:OPT_CLIENT_MODE\\.$#"
 			count: 1
 			path: src/Cache/Engine/MemcachedEngine.php
@@ -146,16 +156,6 @@ parameters:
 			path: src/Console/ConsoleOptionParser.php
 
 		-
-			message: "#^Call to an undefined method Cake\\\\Auth\\\\Storage\\\\StorageInterface\\:\\:getConfig\\(\\)\\.$#"
-			count: 1
-			path: src/Controller/Component/AuthComponent.php
-
-		-
-			message: "#^Call to an undefined method Cake\\\\Auth\\\\Storage\\\\StorageInterface\\:\\:setConfig\\(\\)\\.$#"
-			count: 1
-			path: src/Controller/Component/AuthComponent.php
-
-		-
 			message: "#^Property Cake\\\\Controller\\\\Controller\\:\\:\\$response \\(Cake\\\\Http\\\\Response\\) does not accept Psr\\\\Http\\\\Message\\\\ResponseInterface\\.$#"
 			count: 1
 			path: src/Controller/Controller.php
@@ -271,6 +271,16 @@ parameters:
 			path: src/Datasource/QueryCacher.php
 
 		-
+			message: "#^Parameter \\#1 \\$rules of method Cake\\\\ORM\\\\Table\\:\\:buildRules\\(\\) expects Cake\\\\ORM\\\\RulesChecker, Cake\\\\Datasource\\\\RulesChecker given\\.$#"
+			count: 1
+			path: src/ORM/Table.php
+
+		-
+			message: "#^Variable \\$entities in PHPDoc tag @var does not match any variable in the foreach loop\\: \\$key, \\$entity$#"
+			count: 1
+			path: src/ORM/Table.php
+
+		-
 			message: "#^Cannot unset offset 'args' on array\\('path' \\=\\> string, 'reference' \\=\\> mixed\\)\\.$#"
 			count: 1
 			path: src/Error/Debugger.php
@@ -391,21 +401,6 @@ parameters:
 			path: src/Mailer/Email.php
 
 		-
-			message: "#^Method Cake\\\\Mailer\\\\Renderer\\:\\:render\\(\\) should return array\\(\\)\\|array\\('html' \\=\\> string, 'text' \\=\\> string\\) but returns array\\<string, string\\>\\.$#"
-			count: 2
-			path: src/Mailer/Renderer.php
-
-		-
-			message: "#^Access to an undefined property Cake\\\\Mailer\\\\Renderer\\:\\:\\$request\\.$#"
-			count: 1
-			path: src/Mailer/Renderer.php
-
-		-
-			message: "#^Access to an undefined property Cake\\\\Mailer\\\\Renderer\\:\\:\\$response\\.$#"
-			count: 1
-			path: src/Mailer/Renderer.php
-
-		-
 			message: "#^Parameter \\#2 \\$callback of function array_filter expects callable\\(mixed, mixed\\)\\: bool, 'strlen' given\\.$#"
 			count: 1
 			path: src/ORM/Association/BelongsToMany.php
@@ -451,13 +446,8 @@ parameters:
 			path: src/ORM/Rule/IsUnique.php
 
 		-
-			message: "#^Variable \\$entities in PHPDoc tag @var does not match any variable in the foreach loop\\: \\$key, \\$entity$#"
-			count: 1
-			path: src/ORM/Table.php
-
-		-
 			message: "#^Parameter \\#1 \\$request of static method Cake\\\\Routing\\\\Router\\:\\:setRequest\\(\\) expects Cake\\\\Http\\\\ServerRequest, Psr\\\\Http\\\\Message\\\\ServerRequestInterface given\\.$#"
-			count: 2
+			count: 1
 			path: src/Routing/Middleware/RoutingMiddleware.php
 
 		-
@@ -561,11 +551,6 @@ parameters:
 			path: src/TestSuite/IntegrationTestCase.php
 
 		-
-			message: "#^Return type \\(array\\<string, array\\|string\\>\\) of method Cake\\\\View\\\\Exception\\\\MissingCellTemplateException\\:\\:getAttributes\\(\\) should be compatible with return type \\(array\\<string, array\\|string\\>\\) of method Cake\\\\View\\\\Exception\\\\MissingTemplateException\\:\\:getAttributes\\(\\)$#"
-			count: 1
-			path: src/View/Exception/MissingCellTemplateException.php
-
-		-
 			message: "#^Unsafe usage of new static\\(\\)\\.$#"
 			count: 1
 			path: src/View/Form/ContextFactory.php
@@ -580,3 +565,13 @@ parameters:
 			count: 1
 			path: src/View/Helper/TimeHelper.php
 
+		-
+			message: "#^Access to an undefined property Cake\\\\Mailer\\\\Renderer\\:\\:\\$request\\.$#"
+			count: 1
+			path: src/Mailer/Renderer.php
+
+		-
+			message: "#^Access to an undefined property Cake\\\\Mailer\\\\Renderer\\:\\:\\$response\\.$#"
+			count: 1
+			path: src/Mailer/Renderer.php
+

+ 98 - 1
psalm-baseline.xml

@@ -1,5 +1,18 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<files psalm-version="3.9.5@0cfe565d0afbcd31eadcc281b9017b5692911661">
+<files psalm-version="3.11.2@d470903722cfcbc1cd04744c5491d3e6d13ec3d9">
+  <file src="src/Collection/CollectionTrait.php">
+    <ArgumentTypeCoercion occurrences="4">
+      <code>$iterator</code>
+      <code>$iterator</code>
+      <code>$this-&gt;unwrap()</code>
+      <code>$this-&gt;newCollection($items)-&gt;unwrap()</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/Collection/Iterator/ZipIterator.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$set</code>
+    </ArgumentTypeCoercion>
+  </file>
   <file src="src/Command/CompletionCommand.php">
     <DeprecatedClass occurrences="1">
       <code>\Cake\Console\Shell</code>
@@ -44,16 +57,80 @@
       <code>\Cake\Console\Shell</code>
     </DeprecatedClass>
   </file>
+  <file src="src/Controller/Controller.php">
+    <PropertyTypeCoercion occurrences="1">
+      <code>$result</code>
+    </PropertyTypeCoercion>
+  </file>
+  <file src="src/Controller/ControllerFactory.php">
+    <ArgumentTypeCoercion occurrences="3">
+      <code>$request</code>
+      <code>$request</code>
+      <code>$request</code>
+    </ArgumentTypeCoercion>
+  </file>
   <file src="src/Datasource/ModelAwareTrait.php">
     <DeprecatedClass occurrences="1">
       <code>$this</code>
     </DeprecatedClass>
   </file>
+  <file src="src/Datasource/RulesAwareTrait.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>new $class(['repository' =&gt; $this])</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/Http/BaseApplication.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$request</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/Http/Cookie/Cookie.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$options['expires']</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/Http/ServerRequest.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$this-&gt;data</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/I18n/Date.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$time</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/I18n/FrozenDate.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$time</code>
+    </ArgumentTypeCoercion>
+  </file>
   <file src="src/ORM/Locator/LocatorAwareTrait.php">
     <DeprecatedClass occurrences="1">
       <code>$this</code>
     </DeprecatedClass>
   </file>
+  <file src="src/ORM/Marshaller.php">
+    <ArgumentTypeCoercion occurrences="2">
+      <code>$assoc</code>
+      <code>$assoc</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/ORM/Query.php">
+    <ArgumentTypeCoercion occurrences="6">
+      <code>$this-&gt;_repository</code>
+      <code>$this-&gt;getRepository()</code>
+      <code>$this-&gt;getRepository()</code>
+      <code>$this-&gt;getRepository()</code>
+      <code>$this-&gt;getRepository()</code>
+      <code>$this-&gt;getRepository()</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/Routing/Middleware/RoutingMiddleware.php">
+    <ArgumentTypeCoercion occurrences="2">
+      <code>$request</code>
+      <code>$request</code>
+    </ArgumentTypeCoercion>
+  </file>
   <file src="src/Shell/Task/CommandTask.php">
     <DeprecatedClass occurrences="1">
       <code>Shell</code>
@@ -85,6 +162,21 @@
       <code>FixtureInjector</code>
     </DeprecatedInterface>
   </file>
+  <file src="src/TestSuite/Fixture/TestFixture.php">
+    <ArgumentTypeCoercion occurrences="5">
+      <code>$db</code>
+      <code>$db</code>
+      <code>$db</code>
+      <code>$db</code>
+      <code>$db</code>
+    </ArgumentTypeCoercion>
+  </file>
+  <file src="src/TestSuite/IntegrationTestTrait.php">
+    <ArgumentTypeCoercion occurrences="2">
+      <code>$this-&gt;_response</code>
+      <code>$this-&gt;_response</code>
+    </ArgumentTypeCoercion>
+  </file>
   <file src="src/TestSuite/LegacyShellDispatcher.php">
     <DeprecatedClass occurrences="5">
       <code>ShellDispatcher</code>
@@ -108,4 +200,9 @@
       <code>defaultCurrency</code>
     </DeprecatedMethod>
   </file>
+  <file src="src/View/View.php">
+    <ArgumentTypeCoercion occurrences="1">
+      <code>$options</code>
+    </ArgumentTypeCoercion>
+  </file>
 </files>

+ 0 - 1
psalm.xml

@@ -20,7 +20,6 @@
 
     <issueHandlers>
         <RedundantConditionGivenDocblockType errorLevel="suppress"/>
-        <TypeCoercion errorLevel="suppress"/>
         <DocblockTypeContradiction errorLevel="suppress"/>
         <MissingClosureParamType errorLevel="suppress"/>
         <MissingClosureReturnType errorLevel="suppress"/>

+ 1 - 0
src/Database/Connection.php

@@ -191,6 +191,7 @@ class Connection implements ConnectionInterface
     public function setDriver($driver, $config = [])
     {
         if (is_string($driver)) {
+            /** @psalm-var class-string<\Cake\Database\DriverInterface>|null $className */
             $className = App::className($driver, 'Database/Driver');
             if ($className === null) {
                 throw new MissingDriverException(['driver' => $driver]);

+ 2 - 0
src/Database/Type/DateTimeType.php

@@ -406,6 +406,8 @@ class DateTimeType extends BaseType
      * @param string $class The classname to use.
      * @param string $fallback The classname to use when the preferred class does not exist.
      * @return void
+     * @psalm-param class-string<\DateTime>|class-string<\DateTimeImmutable> $class
+     * @psalm-param class-string<\DateTime>|class-string<\DateTimeImmutable> $fallback
      */
     protected function _setClassName(string $class, string $fallback): void
     {

+ 1 - 0
src/Database/TypeFactory.php

@@ -128,6 +128,7 @@ class TypeFactory
      *
      * @param string[] $map List of types to be mapped.
      * @return void
+     * @psalm-param array<string, class-string<\Cake\Database\TypeInterface>> $map
      */
     public static function setMap(array $map): void
     {

+ 1 - 0
src/Datasource/RulesAwareTrait.php

@@ -95,6 +95,7 @@ trait RulesAwareTrait
         if ($this->_rulesChecker !== null) {
             return $this->_rulesChecker;
         }
+        /** @psalm-var class-string<\Cake\Datasource\RulesChecker> $class */
         $class = defined('static::RULES_CLASS') ? static::RULES_CLASS : RulesChecker::class;
         $this->_rulesChecker = $this->buildRules(new $class(['repository' => $this]));
         $this->dispatchEvent('Model.buildRules', ['rules' => $this->_rulesChecker]);

+ 1 - 0
src/Form/Form.php

@@ -68,6 +68,7 @@ class Form implements EventListenerInterface, EventDispatcherInterface, Validato
      * Schema class.
      *
      * @var string
+     * @psalm-var class-string<\Cake\Form\Schema>
      */
     protected $_schemaClass = Schema::class;
 

+ 1 - 0
src/Mailer/Mailer.php

@@ -149,6 +149,7 @@ class Mailer implements EventListenerInterface
      * Message class name.
      *
      * @var string
+     * @psalm-var class-string<\Cake\Mailer\Message>
      */
     protected $messageClass = Message::class;
 

+ 1 - 1
src/ORM/Behavior/TranslateBehavior.php

@@ -133,7 +133,7 @@ class TranslateBehavior extends Behavior implements PropertyMarshalInterface
      * @param string $class Class name.
      * @return void
      * @since 4.0.0
-     * @psalm-var class-string<\Cake\ORM\Behavior\Translate\TranslateStrategyInterface>
+     * @psalm-param class-string<\Cake\ORM\Behavior\Translate\TranslateStrategyInterface> $class
      */
     public static function setDefaultStrategyClass(string $class)
     {

+ 2 - 0
src/Routing/Middleware/RoutingMiddleware.php

@@ -136,6 +136,7 @@ class RoutingMiddleware implements MiddlewareInterface
             if (empty($params['controller'])) {
                 $parsedBody = $request->getParsedBody();
                 if (is_array($parsedBody) && isset($parsedBody['_method'])) {
+                    /** @var \Cake\Http\ServerRequest $request */
                     $request = $request->withMethod($parsedBody['_method']);
                 }
                 $params = Router::parseRequest($request) + $params;
@@ -143,6 +144,7 @@ class RoutingMiddleware implements MiddlewareInterface
                     $middleware = $params['_middleware'];
                     unset($params['_middleware']);
                 }
+                /** @var \Cake\Http\ServerRequest $request */
                 $request = $request->withAttribute('params', $params);
                 Router::setRequest($request);
             }

+ 2 - 0
src/Routing/Router.php

@@ -146,6 +146,7 @@ class Router
      * parameters to the route collection.
      *
      * @var callable[]
+     * @psalm-var array<int, (\Closure|callable-string)>
      */
     protected static $_urlFilters = [];
 
@@ -331,6 +332,7 @@ class Router
      *
      * @param callable $function The function to add
      * @return void
+     * @psalm-param \Closure|callable-string $function
      */
     public static function addUrlFilter(callable $function): void
     {

+ 8 - 1
src/TestSuite/ConsoleIntegrationTestTrait.php

@@ -48,6 +48,7 @@ trait ConsoleIntegrationTestTrait
      * The customized application class name.
      *
      * @var string|null
+     * @psalm-var class-string<\Cake\Core\ConsoleApplicationInterface>|null
      */
     protected $_appClass;
 
@@ -162,6 +163,7 @@ trait ConsoleIntegrationTestTrait
      * @param string $class The application class name.
      * @param array|null $constructorArgs The constructor arguments for your application class.
      * @return void
+     * @psalm-param class-string<\Cake\Core\ConsoleApplicationInterface> $class
      */
     public function configApplication(string $class, ?array $constructorArgs): void
     {
@@ -305,7 +307,12 @@ trait ConsoleIntegrationTestTrait
     protected function makeRunner()
     {
         if ($this->_useCommandRunner) {
-            $appClass = $this->_appClass ?: Configure::read('App.namespace') . '\Application';
+            if ($this->_appClass) {
+                $appClass = $this->_appClass;
+            } else {
+                /** @psalm-var class-string<\Cake\Core\ConsoleApplicationInterface> */
+                $appClass = Configure::read('App.namespace') . '\Application';
+            }
             $appArgs = $this->_appArgs ?: [CONFIG];
 
             return new CommandRunner(new $appClass(...$appArgs));

+ 2 - 0
src/TestSuite/Fixture/FixtureManager.php

@@ -206,8 +206,10 @@ class FixtureManager
                     $additionalPath,
                     $name . 'Fixture',
                 ];
+                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
                 $className = implode('\\', array_filter($nameSegments));
             } else {
+                /** @psalm-var class-string<\Cake\Datasource\FixtureInterface> */
                 $className = $fixture;
                 /** @psalm-suppress PossiblyFalseArgument */
                 $name = preg_replace('/Fixture\z/', '', substr(strrchr($fixture, '\\'), 1));

+ 2 - 0
src/TestSuite/IntegrationTestTrait.php

@@ -81,6 +81,7 @@ trait IntegrationTestTrait
      * The customized application class name.
      *
      * @var string|null
+     * @psalm-var class-string<\Cake\Core\HttpApplicationInterface>|null
      */
     protected $_appClass;
 
@@ -228,6 +229,7 @@ trait IntegrationTestTrait
      * @param string $class The application class name.
      * @param array|null $constructorArgs The constructor arguments for your application class.
      * @return void
+     * @psalm-param class-string<\Cake\Core\HttpApplicationInterface> $class
      */
     public function configApplication(string $class, ?array $constructorArgs): void
     {

+ 13 - 25
src/TestSuite/MiddlewareDispatcher.php

@@ -26,8 +26,6 @@ use Cake\Routing\RoutingApplicationInterface;
 use Laminas\Diactoros\Stream;
 use LogicException;
 use Psr\Http\Message\ResponseInterface;
-use ReflectionClass;
-use ReflectionException;
 
 /**
  * Dispatches a request capturing the response for integration
@@ -48,7 +46,7 @@ class MiddlewareDispatcher
      * The application class name
      *
      * @var string
-     * @psalm-var class-string
+     * @psalm-var class-string<\Cake\Core\HttpApplicationInterface>
      */
     protected $_class;
 
@@ -62,7 +60,7 @@ class MiddlewareDispatcher
     /**
      * The application that is being dispatched.
      *
-     * @var \Cake\Core\HttpApplicationInterface|\Cake\Core\ConsoleApplicationInterface
+     * @var \Cake\Core\HttpApplicationInterface
      */
     protected $app;
 
@@ -74,7 +72,7 @@ class MiddlewareDispatcher
      * @param array|null $constructorArgs The constructor arguments for your application class.
      *   Defaults to `['./config']`
      * @throws \LogicException If it cannot load class for use in integration testing.
-     * @psalm-param \Cake\Core\HttpApplicationInterface::class|\Cake\Core\ConsoleApplicationInterface::class|null $class
+     * @psalm-param class-string<\Cake\Core\HttpApplicationInterface>|null $class
      */
     public function __construct(
         TestCase $test,
@@ -82,17 +80,18 @@ class MiddlewareDispatcher
         ?array $constructorArgs = null
     ) {
         $this->_test = $test;
-        $this->_class = $class ?: Configure::read('App.namespace') . '\Application';
+        if ($class === null) {
+            /** @psalm-var class-string<\Cake\Core\HttpApplicationInterface> */
+            $class = Configure::read('App.namespace') . '\Application';
+        }
+        $this->_class = $class;
         $this->_constructorArgs = $constructorArgs ?: [CONFIG];
 
-        try {
-            $reflect = new ReflectionClass($this->_class);
-            /** @var \Cake\Core\HttpApplicationInterface $app */
-            $app = $reflect->newInstanceArgs($this->_constructorArgs);
-            $this->app = $app;
-        } catch (ReflectionException $e) {
-            throw new LogicException("Cannot load `{$this->_class}` for use in integration testing.", 0, $e);
+        if (!class_exists($this->_class)) {
+            throw new LogicException("Cannot load `{$this->_class}` for use in integration testing.", 0);
         }
+
+        $this->app = new $this->_class(...$this->_constructorArgs);
     }
 
     /**
@@ -186,17 +185,6 @@ class MiddlewareDispatcher
      */
     public function execute(array $requestSpec): ResponseInterface
     {
-        try {
-            $reflect = new ReflectionClass($this->_class);
-            /** @var \Cake\Core\HttpApplicationInterface $app */
-            $app = $reflect->newInstanceArgs($this->_constructorArgs);
-        } catch (ReflectionException $e) {
-            throw new LogicException(sprintf(
-                'Cannot load "%s" for use in integration testing.',
-                $this->_class
-            ));
-        }
-
         // Spy on the controller using the initialize hook instead
         // of the dispatcher hooks as those will be going away one day.
         EventManager::instance()->on(
@@ -204,7 +192,7 @@ class MiddlewareDispatcher
             [$this->_test, 'controllerSpy']
         );
 
-        $server = new Server($app);
+        $server = new Server($this->app);
 
         return $server->run($this->_createRequest($requestSpec));
     }

+ 1 - 1
src/Utility/Xml.php

@@ -247,7 +247,7 @@ class Xml
      *
      * `<root><tag id="1" value="defect">description</tag></root>`
      *
-     * @param array|\Cake\Collection\Collection $input Array with data or a collection instance.
+     * @param array|object $input Array with data or a collection instance.
      * @param array $options The options to use.
      * @return \SimpleXMLElement|\DOMDocument SimpleXMLElement or DOMDocument
      * @throws \Cake\Utility\Exception\XmlException

+ 2 - 6
src/View/View.php

@@ -1582,7 +1582,7 @@ class View implements EventDispatcherInterface
      * @param array $data Data
      * @param array $options Element options
      * @return array Element Cache configuration.
-     * @psalm-param array{cache:(string|array{key:string, config:string}), callbacks:mixed, plugin:mixed} $options
+     * @psalm-param array{cache:(string|array{key:string, config:string}|null), callbacks:mixed, plugin:mixed} $options
      * @psalm-return array{key:string, config:string}
      */
     protected function _elementCache(string $name, array $data, array $options): array
@@ -1614,11 +1614,7 @@ class View implements EventDispatcherInterface
             'key' => implode('_', $keys),
         ];
         if (is_array($cache)) {
-            $defaults = [
-                'config' => $this->elementCache,
-                'key' => $config['key'],
-            ];
-            $config = $cache + $defaults;
+            $config = $cache + $config;
         }
         $config['key'] = 'element_' . $config['key'];