Browse Source

Merge pull request #16398 from cakephp/5.x-psalm

Update psalm's config.
ADmad 4 years ago
parent
commit
cda699b334

+ 1 - 4
psalm.xml

@@ -1,5 +1,6 @@
 <?xml version="1.0"?>
 <psalm
+    errorLevel="2"
     allowStringToStandInForClass="true"
     usePhpDocMethodsWithoutMagicCall="true"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
@@ -12,15 +13,11 @@
         <directory name="src"/>
         <ignoreFiles>
             <directory name="vendor"/>
-            <file name="src/Utility/Filesystem.php"/>
         </ignoreFiles>
     </projectFiles>
 
     <issueHandlers>
         <RedundantPropertyInitializationCheck errorLevel="suppress"/>
-        <RedundantConditionGivenDocblockType errorLevel="suppress"/>
-        <RedundantCastGivenDocblockType errorLevel="suppress"/>
-        <DocblockTypeContradiction errorLevel="suppress"/>
         <MissingClosureParamType errorLevel="suppress"/>
         <MissingClosureReturnType errorLevel="suppress"/>
         <UndefinedClass>

+ 1 - 0
src/Cache/Engine/RedisEngine.php

@@ -168,6 +168,7 @@ class RedisEngine extends CacheEngine
     public function get(string $key, mixed $default = null): mixed
     {
         $value = $this->_Redis->get($this->_key($key));
+        /** @psalm-suppress DocblockTypeContradiction */
         if ($value === false) {
             return $default;
         }

+ 1 - 0
src/Console/ConsoleOutput.php

@@ -350,6 +350,7 @@ class ConsoleOutput
      */
     public function __destruct()
     {
+        /** @psalm-suppress RedundantCondition */
         if (is_resource($this->_output)) {
             fclose($this->_output);
         }

+ 1 - 3
src/Controller/Controller.php

@@ -87,8 +87,6 @@ use UnexpectedValueException;
  *
  * @property \Cake\Controller\Component\FlashComponent $Flash
  * @property \Cake\Controller\Component\FormProtectionComponent $FormProtection
- * @property \Cake\Controller\Component\PaginatorComponent $Paginator
- * @property \Cake\Controller\Component\RequestHandlerComponent $RequestHandler
  * @link https://book.cakephp.org/4/en/controllers.html
  */
 class Controller implements EventListenerInterface, EventDispatcherInterface
@@ -837,7 +835,7 @@ class Controller implements EventListenerInterface, EventDispatcherInterface
 
         $settings += $this->paginate;
 
-        /** @var class-string<\Cake\Datasource\PaginatorInterface> $paginator */
+        /** @var \Cake\Datasource\PaginatorInterface|class-string<\Cake\Datasource\PaginatorInterface> $paginator */
         $paginator = $settings['paginator'] ?? Paginator::class;
         unset($settings['paginator']);
         if (is_string($paginator)) {

+ 1 - 1
src/Core/StaticConfigTrait.php

@@ -32,7 +32,7 @@ trait StaticConfigTrait
     /**
      * Configuration sets.
      *
-     * @var array<string, mixed>
+     * @var array<string|int, mixed>
      */
     protected static array $_config = [];
 

+ 1 - 2
src/Database/Expression/CaseExpressionTrait.php

@@ -28,8 +28,6 @@ use Stringable;
 /**
  * Trait that holds shared functionality for case related expressions.
  *
- * @property \Cake\Database\TypeMap $_typeMap The type map to use when using an array of conditions for the `WHEN`
- *  value.
  * @internal
  */
 trait CaseExpressionTrait
@@ -44,6 +42,7 @@ trait CaseExpressionTrait
     {
         $type = null;
 
+        /** @psalm-suppress RedundantCondition */
         if (is_string($value)) {
             $type = 'string';
         } elseif (is_int($value)) {

+ 2 - 0
src/Database/Expression/CaseStatementExpression.php

@@ -124,6 +124,7 @@ class CaseStatementExpression implements ExpressionInterface, TypedResultInterfa
      */
     public function __construct(mixed $value = null, ?string $type = null)
     {
+        /** @psalm-suppress RedundantConditionGivenDocblockType, DocblockTypeContradiction */
         if (func_num_args() > 0) {
             if (
                 $value !== null &&
@@ -407,6 +408,7 @@ class CaseStatementExpression implements ExpressionInterface, TypedResultInterfa
             throw new LogicException('Cannot call `else()` between `when()` and `then()`.');
         }
 
+        /** @psalm-suppress RedundantConditionGivenDocblockType, DocblockTypeContradiction */
         if (
             $result !== null &&
             !is_scalar($result) &&

+ 2 - 0
src/Database/Expression/WhenThenExpression.php

@@ -122,6 +122,7 @@ class WhenThenExpression implements ExpressionInterface
      */
     public function when(mixed $when, array|string|null $type = null)
     {
+        /** @psalm-suppress RedundantConditionGivenDocblockType, DocblockTypeContradiction */
         if (
             !(is_array($when) && !empty($when)) &&
             !is_scalar($when) &&
@@ -193,6 +194,7 @@ class WhenThenExpression implements ExpressionInterface
      */
     public function then(mixed $result, ?string $type = null)
     {
+        /** @psalm-suppress DocblockTypeContradiction */
         if (
             $result !== null &&
             !is_scalar($result) &&

+ 1 - 1
src/Database/Type/DateTimeType.php

@@ -334,7 +334,7 @@ class DateTimeType extends BaseType implements BatchCastingInterface
                     $dateTime = $this->_parseValue($value);
                 }
 
-                /** @var \Datetime|\DateTimeImmutable $dateTime */
+                /** @var \Datetime|\DateTimeImmutable|null $dateTime */
                 if ($dateTime !== null) {
                     $dateTime = $dateTime->setTimezone($this->defaultTimezone);
                 }

+ 1 - 0
src/Datasource/EntityTrait.php

@@ -235,6 +235,7 @@ trait EntityTrait
         $options += ['setter' => true, 'guard' => $guard];
 
         foreach ($field as $name => $value) {
+            /** @psalm-suppress RedundantCastGivenDocblockType */
             $name = (string)$name;
             if ($options['guard'] === true && !$this->isAccessible($name)) {
                 continue;

+ 1 - 0
src/Error/ExceptionRenderer.php

@@ -187,6 +187,7 @@ class ExceptionRenderer implements ExceptionRendererInterface
         if ($errorOccured && isset($controller->RequestHandler)) {
             try {
                 $event = new Event('Controller.startup', $controller);
+                /** @psalm-suppress PossiblyUndefinedMethod */
                 $controller->RequestHandler->startup($event);
             } catch (Throwable) {
             }

+ 1 - 0
src/Http/ServerRequest.php

@@ -940,6 +940,7 @@ class ServerRequest implements ServerRequestInterface
     {
         $new = clone $this;
 
+        /** @psalm-suppress DocblockTypeContradiction */
         if (
             !is_string($method) ||
             !preg_match('/^[!#$%&\'*+.^_`\|~0-9a-z-]+$/i', $method)

+ 1 - 0
src/ORM/Association/BelongsToMany.php

@@ -1225,6 +1225,7 @@ class BelongsToMany extends Association
                 $property = $this->getProperty();
 
                 if (count($inserts)) {
+                    /** @psalm-suppress RedundantConditionGivenDocblockType */
                     $inserted = array_combine(
                         array_keys($inserts),
                         (array)$sourceEntity->get($property)

+ 1 - 0
src/ORM/Table.php

@@ -2043,6 +2043,7 @@ class Table implements RepositoryInterface, EventListenerInterface, EventDispatc
         $id = (array)$this->_newId($primary) + $keys;
 
         // Generate primary keys preferring values in $data.
+        /** @psalm-suppress RedundantConditionGivenDocblockType */
         $primary = array_combine($primary, $id) ?: [];
         $primary = array_intersect_key($data, $primary) + $primary;
 

+ 5 - 2
src/Utility/Filesystem.php

@@ -58,6 +58,7 @@ class Filesystem
         $flags = $flags ?? FilesystemIterator::KEY_AS_PATHNAME
             | FilesystemIterator::CURRENT_AS_FILEINFO
             | FilesystemIterator::SKIP_DOTS;
+        /** @psalm-suppress ArgumentTypeCoercion */
         $directory = new FilesystemIterator($path, $flags);
 
         if ($filter === null) {
@@ -82,8 +83,10 @@ class Filesystem
         $flags = $flags ?? FilesystemIterator::KEY_AS_PATHNAME
             | FilesystemIterator::CURRENT_AS_FILEINFO
             | FilesystemIterator::SKIP_DOTS;
+        /** @psalm-suppress ArgumentTypeCoercion */
         $directory = new RecursiveDirectoryIterator($path, $flags);
 
+        /** @psalm-suppress InvalidArgument */
         $dirFilter = new RecursiveCallbackFilterIterator(
             $directory,
             function (SplFileInfo $current) {
@@ -111,10 +114,10 @@ class Filesystem
      * Wrap iterator in additional filtering iterator.
      *
      * @param \Iterator $iterator Iterator
-     * @param \Closure|string|null $filter Regex string or callback.
+     * @param \Closure|string $filter Regex string or callback.
      * @return \Iterator
      */
-    protected function filterIterator(Iterator $iterator, Closure|string|null $filter): Iterator
+    protected function filterIterator(Iterator $iterator, Closure|string $filter): Iterator
     {
         if (is_string($filter)) {
             return new RegexIterator($iterator, $filter);

+ 1 - 3
src/Validation/Validation.php

@@ -718,12 +718,10 @@ class Validation
                     $check = sprintf('%.1f', $check);
                 }
                 $regex = "/^{$sign}{$dnum}{$exp}$/";
-            } elseif (is_numeric($places)) {
+            } else {
                 $places = '[0-9]{' . $places . '}';
                 $dnum = "(?:[0-9]*[\.]{$places}|{$lnum}[\.]{$places})";
                 $regex = "/^{$sign}{$dnum}{$exp}$/";
-            } else {
-                return false;
             }
         }
 

+ 16 - 15
src/Validation/ValidationRule.php

@@ -38,9 +38,9 @@ class ValidationRule
     /**
      * The 'on' key
      *
-     * @var callable|string
+     * @var callable|string|null
      */
-    protected $_on;
+    protected $_on = null;
 
     /**
      * The 'last' key
@@ -74,9 +74,9 @@ class ValidationRule
     /**
      * Constructor
      *
-     * @param array $validator [optional] The validator properties
+     * @param array $validator The validator properties
      */
-    public function __construct(array $validator = [])
+    public function __construct(array $validator)
     {
         $this->_addValidatorProps($validator);
     }
@@ -118,13 +118,13 @@ class ValidationRule
             return true;
         }
 
-        if (!is_string($this->_rule) && is_callable($this->_rule)) {
-            $callable = $this->_rule;
-            $isCallable = true;
-        } else {
+        if (is_string($this->_rule)) {
             $provider = $providers[$this->_provider];
             $callable = [$provider, $this->_rule];
             $isCallable = is_callable($callable);
+        } else {
+            $callable = $this->_rule;
+            $isCallable = true;
         }
 
         if (!$isCallable) {
@@ -166,18 +166,19 @@ class ValidationRule
      */
     protected function _skip(array $context): bool
     {
-        if (!is_string($this->_on) && is_callable($this->_on)) {
-            $function = $this->_on;
+        if (is_string($this->_on)) {
+            $newRecord = $context['newRecord'];
 
-            return !$function($context);
-        }
-
-        $newRecord = $context['newRecord'];
-        if (!empty($this->_on)) {
             return ($this->_on === Validator::WHEN_CREATE && !$newRecord)
                 || ($this->_on === Validator::WHEN_UPDATE && $newRecord);
         }
 
+        if ($this->_on !== null) {
+            $function = $this->_on;
+
+            return !$function($context);
+        }
+
         return false;
     }
 

+ 2 - 1
src/View/StringTemplate.php

@@ -44,6 +44,7 @@ class StringTemplate
         'allowfullscreen' => true,
         'async' => true,
         'autofocus' => true,
+        'autoload' => true,
         'autoplay' => true,
         'checked' => true,
         'compact' => true,
@@ -296,7 +297,7 @@ class StringTemplate
 
         foreach ($options as $key => $value) {
             if (!isset($exclude[$key]) && $value !== false && $value !== null) {
-                $attributes[] = $this->_formatAttribute((string)$key, $value, $escape);
+                $attributes[] = $this->_formatAttribute($key, $value, $escape);
             }
         }
         $out = trim(implode(' ', $attributes));

+ 1 - 0
src/View/Widget/RadioWidget.php

@@ -138,6 +138,7 @@ class RadioWidget extends BasicWidget
         }
         $isNumeric = is_numeric($radio['value']);
 
+        /** @psalm-suppress DocblockTypeContradiction */
         return !is_array($disabled) || in_array((string)$radio['value'], $disabled, !$isNumeric);
     }
 

+ 1 - 1
tests/TestCase/Validation/ValidatorTest.php

@@ -96,7 +96,7 @@ class ValidatorTest extends TestCase
         $result = $validator->field('email')->rule('notBlank')->get('rule');
         $this->assertSame('notBlank', $result);
 
-        $rule = new ValidationRule();
+        $rule = new ValidationRule([]);
         $validator->add('field', 'myrule', $rule);
         $result = $validator->field('field')->rule('myrule');
         $this->assertSame($rule, $result);

+ 1 - 1
tests/TestCase/View/Helper/HtmlHelperTest.php

@@ -1957,7 +1957,7 @@ class HtmlHelperTest extends TestCase
         $expected = ['video' => ['src' => 'files/video.webm'], 'Your browser does not support the HTML5 Video element.', '/video'];
         $this->assertHtml($expected, $result);
 
-        $result = $this->Html->media('video.webm', ['autoload', 'muted' => 'muted']);
+        $result = $this->Html->media('video.webm', ['autoload' => true, 'muted' => 'muted']);
         $expected = [
             'video' => [
                 'src' => 'files/video.webm',