Browse Source

Enable psalm's type coercion check.

ADmad 6 years ago
parent
commit
0741a2298f

+ 11 - 6
phpstan-baseline.neon

@@ -276,6 +276,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
@@ -441,13 +451,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
 
 		-

+ 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

@@ -405,6 +405,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

@@ -135,6 +135,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;
@@ -142,6 +143,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

@@ -80,6 +80,7 @@ trait IntegrationTestTrait
      * The customized application class name.
      *
      * @var string|null
+     * @psalm-var class-string<\Cake\Core\HttpApplicationInterface>|null
      */
     protected $_appClass;
 
@@ -227,6 +228,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
     {

+ 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'];