Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\ArrayTypes;

use PhpParser\Node;
use PHPStan\Analyser\Scope;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Reflection\ParameterReflection;
use PHPStan\Reflection\ParametersAcceptorSelector;
use PHPStan\Rules\Rule;
use PHPStan\Type\ArrayType;
use PHPStan\Type\MixedType;

final class MissingArrayValueTypeInMethodParameterRule implements Rule
{

/**
* @return string Class implementing \PhpParser\Node
*/
public function getNodeType(): string
{
return \PhpParser\Node\Stmt\ClassMethod::class;
}

/**
* @param \PhpParser\Node\Stmt\ClassMethod $node
* @param \PHPStan\Analyser\Scope $scope
*
* @return string[] errors
*/
public function processNode(Node $node, Scope $scope): array
{
if (!$scope->isInClass()) {
throw new \PHPStan\ShouldNotHappenException();
}

$methodReflection = $scope->getClassReflection()->getNativeMethod($node->name->name);

$messages = [];

foreach (ParametersAcceptorSelector::selectSingle($methodReflection->getVariants())->getParameters() as $parameterReflection) {
$message = $this->checkMethodParameter($methodReflection, $parameterReflection);
if ($message === null) {
continue;
}

/** @var string $message */
$message = $message;

$messages[] = $message;
}

return $messages;
}

private function checkMethodParameter(MethodReflection $methodReflection, ParameterReflection $parameterReflection): ?string
{
$parameterType = $parameterReflection->getType();

if (!$parameterType instanceof ArrayType) {
return null;
}

$valueType = $parameterType->getIterableValueType();

if ($valueType instanceof MixedType && !$valueType->isExplicitMixed()) {
return sprintf(
'Method %s::%s() has parameter $%s of type array with no value typehint specified.',
$methodReflection->getDeclaringClass()->getDisplayName(),
$methodReflection->getName(),
$parameterReflection->getName()
);
}

return null;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php declare(strict_types = 1);

namespace PHPStan\Rules\ArrayTypes;

final class MissingArrayValueTypeInMethodParameterRuleTest extends \PHPStan\Testing\RuleTestCase
{

protected function getRule(): \PHPStan\Rules\Rule
{
return new MissingArrayValueTypeInMethodParameterRule();
}

public function testRule(): void
{
$this->analyse([__DIR__ . '/data/missing-array-types-in-method-parameter-typehint.php'], [
/* not detected by now
[
'Method MissingArrayValueTypeInMethodParameter\FooInterface::getFoo() has parameter $p1 of type array with no value typehint specified.',
11,
],
*/
[
'Method MissingArrayValueTypeInMethodParameter\FooParent::getBar() has parameter $p2 of type array with no value typehint specified.',
18,
],
[
'Method MissingArrayValueTypeInMethodParameter\Foo::getBar() has parameter $p2 of type array with no value typehint specified.',
39,
],
]);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

namespace MissingArrayValueTypeInMethodParameter;

interface FooInterface
{

/**
* @param array $p1
*/
public function getFoo($p1): void;

}

class FooParent
{

public function getBar(array $p2)
{

}

}

class Foo extends FooParent implements FooInterface
{

/**
* @param array<int, array<string, array<int|string, mixed>>> $p1
*/
public function getFoo($p1): void
{

}

/**
* @param $p2
*/
public function getBar(array $p2)
{

}

/**
* @param array<mixed> $p3
*/
public function getBaz($p3, $p4): bool
{
return false;
}

/**
* @param mixed[] $p5
*/
public function getFooBar(array $p5): bool
{
return false;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ function globalFunction2($a, $b, $c): bool
$closure = function($a, $b, $c) {

};

return false;
}

Expand Down