Skip to content

Commit

Permalink
Params are now objects too
Browse files Browse the repository at this point in the history
  • Loading branch information
spaze committed Aug 2, 2021
1 parent 0e73b99 commit 5092bc4
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 45 deletions.
38 changes: 13 additions & 25 deletions src/DisallowedCall.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

namespace Spaze\PHPStan\Rules\Disallowed;

use Spaze\PHPStan\Rules\Disallowed\Params\DisallowedCallParam;

class DisallowedCall
{

Expand All @@ -15,31 +17,27 @@ class DisallowedCall
/** @var string[] */
private $allowIn;

/** @var array<integer, integer|boolean|string> */
/** @var array<integer, DisallowedCallParam> */
private $allowParamsInAllowed;

/** @var array<integer, integer|boolean|string> */
/** @var array<integer, DisallowedCallParam> */
private $allowParamsAnywhere;

/** @var array<integer, integer|boolean|string> */
/** @var array<integer, DisallowedCallParam> */
private $allowExceptParams;

/** @var array<integer, integer|boolean|string> */
private $allowExceptCaseInsensitiveParams;


/**
* DisallowedCall constructor.
*
* @param string $call
* @param string|null $message
* @param string[] $allowIn
* @param array<integer, integer|boolean|string> $allowParamsInAllowed
* @param array<integer, integer|boolean|string> $allowParamsAnywhere
* @param array<integer, integer|boolean|string> $allowExceptParams
* @param array<integer, integer|boolean|string> $allowExceptCaseInsensitiveParams
* @param array<integer, DisallowedCallParam> $allowParamsInAllowed
* @param array<integer, DisallowedCallParam> $allowParamsAnywhere
* @param array<integer, DisallowedCallParam> $allowExceptParams
*/
public function __construct(string $call, ?string $message, array $allowIn, array $allowParamsInAllowed, array $allowParamsAnywhere, array $allowExceptParams, array $allowExceptCaseInsensitiveParams)
public function __construct(string $call, ?string $message, array $allowIn, array $allowParamsInAllowed, array $allowParamsAnywhere, array $allowExceptParams)
{
$call = substr($call, -2) === '()' ? substr($call, 0, -2) : $call;
$this->call = ltrim($call, '\\');
Expand All @@ -48,7 +46,6 @@ public function __construct(string $call, ?string $message, array $allowIn, arra
$this->allowParamsInAllowed = $allowParamsInAllowed;
$this->allowParamsAnywhere = $allowParamsAnywhere;
$this->allowExceptParams = $allowExceptParams;
$this->allowExceptCaseInsensitiveParams = $allowExceptCaseInsensitiveParams;
}


Expand All @@ -74,7 +71,7 @@ public function getAllowIn(): array


/**
* @return array<integer, integer|boolean|string>
* @return array<integer, DisallowedCallParam>
*/
public function getAllowParamsInAllowed(): array
{
Expand All @@ -83,7 +80,7 @@ public function getAllowParamsInAllowed(): array


/**
* @return array<integer, integer|boolean|string>
* @return array<integer, DisallowedCallParam>
*/
public function getAllowParamsAnywhere(): array
{
Expand All @@ -92,28 +89,19 @@ public function getAllowParamsAnywhere(): array


/**
* @return array<integer, integer|boolean|string>
* @return array<integer, DisallowedCallParam>
*/
public function getAllowExceptParams(): array
{
return $this->allowExceptParams;
}


/**
* @return array<integer, integer|boolean|string>
*/
public function getAllowExceptCaseInsensitiveParams(): array
{
return $this->allowExceptCaseInsensitiveParams;
}


public function getKey(): string
{
// The key consists of "initial" config values that would be overwritten with more specific details in a custom config.
// `allowIn` & `allowParams*` aren't included because these are set by the user in their config, not in the bundled files.
return serialize([$this->getCall(), $this->getAllowExceptParams(), $this->getAllowExceptCaseInsensitiveParams()]);
return serialize([$this->getCall(), $this->getAllowExceptParams()]);
}

}
23 changes: 19 additions & 4 deletions src/DisallowedCallFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
namespace Spaze\PHPStan\Rules\Disallowed;

use PHPStan\ShouldNotHappenException;
use Spaze\PHPStan\Rules\Disallowed\Params\DisallowedCallParamWithCaseInsensitiveValue;
use Spaze\PHPStan\Rules\Disallowed\Params\DisallowedCallParamWithValue;

class DisallowedCallFactory
{
Expand All @@ -23,14 +25,27 @@ public function createFromConfig(array $config): array
if (!$call) {
throw new ShouldNotHappenException("Either 'method' or 'function' must be set in configuration items");
}

$allowParamsInAllowed = $allowParamsAnywhere = $allowExceptParams = [];
foreach ($disallowedCall['allowParamsInAllowed'] ?? [] as $param => $value) {
$allowParamsInAllowed[$param] = new DisallowedCallParamWithValue($value);
}
foreach ($disallowedCall['allowParamsAnywhere'] ?? [] as $param => $value) {
$allowParamsAnywhere[$param] = new DisallowedCallParamWithValue($value);
}
foreach ($disallowedCall['allowExceptParams'] ?? [] as $param => $value) {
$allowExceptParams[$param] = new DisallowedCallParamWithValue($value);
}
foreach ($disallowedCall['allowExceptCaseInsensitiveParams'] ?? [] as $param => $value) {
$allowExceptParams[$param] = new DisallowedCallParamWithCaseInsensitiveValue($value);
}
$disallowedCall = new DisallowedCall(
$call,
$disallowedCall['message'] ?? null,
$disallowedCall['allowIn'] ?? [],
$disallowedCall['allowParamsInAllowed'] ?? [],
$disallowedCall['allowParamsAnywhere'] ?? [],
$disallowedCall['allowExceptParams'] ?? [],
$disallowedCall['allowExceptCaseInsensitiveParams'] ?? []
$allowParamsInAllowed,
$allowParamsAnywhere,
$allowExceptParams
);
$calls[$disallowedCall->getKey()] = $disallowedCall;
}
Expand Down
24 changes: 8 additions & 16 deletions src/DisallowedHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@
use PHPStan\Analyser\Scope;
use PHPStan\Broker\ClassNotFoundException;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ConstantScalarType;
use PHPStan\Type\ObjectType;
use PHPStan\Type\Type;
use PHPStan\Type\TypeWithClassName;
use Spaze\PHPStan\Rules\Disallowed\Params\DisallowedCallParam;

class DisallowedHelper
{
Expand Down Expand Up @@ -60,7 +60,7 @@ private function isAllowed(Scope $scope, ?Node $node, DisallowedCall $disallowed
* @param Expr|null $node
* @phpstan-param ForbiddenCalls|null $node
* @noinspection PhpUndefinedClassInspection ForbiddenCalls is a type alias defined in PHPStan config
* @param array<integer, integer|boolean|string> $allowConfig
* @param array<integer, DisallowedCallParam> $allowConfig
* @return boolean
*/
private function hasAllowedParams(Scope $scope, ?Node $node, array $allowConfig): bool
Expand All @@ -71,7 +71,10 @@ private function hasAllowedParams(Scope $scope, ?Node $node, array $allowConfig)

foreach ($allowConfig as $param => $value) {
$type = $this->getArgType($node, $scope, $param);
if (!$type instanceof ConstantScalarType || $value !== $type->getValue()) {
if (!$type instanceof ConstantScalarType) {
return false;
}
if (!$value->matches($type)) {
return false;
}
}
Expand All @@ -95,20 +98,10 @@ private function matchesAllowExceptParam(Scope $scope, ?Node $node, DisallowedCa

foreach ($disallowedCall->getAllowExceptParams() as $param => $value) {
$type = $this->getArgType($node, $scope, $param);
if ($type instanceof ConstantScalarType && $value === $type->getValue()) {
if ($type instanceof ConstantScalarType && $value->matches($type)) {
return true;
}
}
foreach ($disallowedCall->getAllowExceptCaseInsensitiveParams() as $param => $value) {
$type = $this->getArgType($node, $scope, $param);
if ($type instanceof ConstantScalarType) {
$a = is_string($value) ? strtolower($value) : $value;
$b = $type instanceof ConstantStringType ? strtolower($type->getValue()) : $type->getValue();
if ($a === $b) {
return true;
}
}
}
return false;
}

Expand Down Expand Up @@ -169,8 +162,7 @@ public function getDisallowedMessage(?Node $node, Scope $scope, string $name, ?s
private function callMatches(Scope $scope, ?Node $node, DisallowedCall $disallowedCall, string $name): bool
{
if ($name === $disallowedCall->getCall() || fnmatch($disallowedCall->getCall(), $name, FNM_NOESCAPE)) {
$noAllowExceptParams = count($disallowedCall->getAllowExceptParams()) === 0 && count($disallowedCall->getAllowExceptCaseInsensitiveParams()) === 0;
return $noAllowExceptParams || $this->matchesAllowExceptParam($scope, $node, $disallowedCall);
return count($disallowedCall->getAllowExceptParams()) === 0 || $this->matchesAllowExceptParam($scope, $node, $disallowedCall);
}
return false;
}
Expand Down
13 changes: 13 additions & 0 deletions src/Params/DisallowedCallParam.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php
declare(strict_types = 1);

namespace Spaze\PHPStan\Rules\Disallowed\Params;

use PHPStan\Type\ConstantScalarType;

interface DisallowedCallParam
{

public function matches(ConstantScalarType $type): bool;

}
41 changes: 41 additions & 0 deletions src/Params/DisallowedCallParamWithCaseInsensitiveValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php
declare(strict_types = 1);

namespace Spaze\PHPStan\Rules\Disallowed\Params;

use PHPStan\Type\Constant\ConstantStringType;
use PHPStan\Type\ConstantScalarType;

class DisallowedCallParamWithCaseInsensitiveValue implements DisallowedCallParam
{

/** @var integer|boolean|string */
private $value;


/**
* @param integer|boolean|string $value
*/
public function __construct($value)
{
$this->value = $value;
}


public function matches(ConstantScalarType $type): bool
{
$a = is_string($this->value) ? strtolower($this->value) : $this->value;
$b = $type instanceof ConstantStringType ? strtolower($type->getValue()) : $type->getValue();
return $a === $b;
}


/**
* @return integer|boolean|string
*/
public function getValue()
{
return $this->value;
}

}
38 changes: 38 additions & 0 deletions src/Params/DisallowedCallParamWithValue.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php
declare(strict_types = 1);

namespace Spaze\PHPStan\Rules\Disallowed\Params;

use PHPStan\Type\ConstantScalarType;

class DisallowedCallParamWithValue implements DisallowedCallParam
{

/** @var integer|boolean|string */
private $value;


/**
* @param integer|boolean|string $value
*/
public function __construct($value)
{
$this->value = $value;
}


public function matches(ConstantScalarType $type): bool
{
return $this->value === $type->getValue();
}


/**
* @return integer|boolean|string
*/
public function getValue()
{
return $this->value;
}

}

0 comments on commit 5092bc4

Please sign in to comment.