Skip to content

Commit

Permalink
Can re-allow namespace if in a class with given attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
spaze committed Jan 26, 2025
1 parent 92c3b5b commit 4a55258
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 50 deletions.
18 changes: 18 additions & 0 deletions extension.neon
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,15 @@ parametersSchema:
?allowIn: listOf(string()),
?allowExceptIn: listOf(string()),
?disallowIn: listOf(string()),
?allowInClassWithAttributes: listOf(string()),
?allowExceptInClassWithAttributes: listOf(string()),
?disallowInClassWithAttributes: listOf(string()),
?allowInCallWithAttributes: listOf(string()),
?allowExceptInCallWithAttributes: listOf(string()),
?disallowInCallWithAttributes: listOf(string()),
?allowInClassWithMethodAttributes: listOf(string()),
?allowExceptInClassWithMethodAttributes: listOf(string()),
?disallowInClassWithMethodAttributes: listOf(string()),
?errorIdentifier: string(),
?errorTip: string(),
])
Expand All @@ -37,6 +46,15 @@ parametersSchema:
?allowIn: listOf(string()),
?allowExceptIn: listOf(string()),
?disallowIn: listOf(string()),
?allowInClassWithAttributes: listOf(string()),
?allowExceptInClassWithAttributes: listOf(string()),
?disallowInClassWithAttributes: listOf(string()),
?allowInCallWithAttributes: listOf(string()),
?allowExceptInCallWithAttributes: listOf(string()),
?disallowInCallWithAttributes: listOf(string()),
?allowInClassWithMethodAttributes: listOf(string()),
?allowExceptInClassWithMethodAttributes: listOf(string()),
?disallowInClassWithMethodAttributes: listOf(string()),
?errorIdentifier: string(),
?errorTip: string(),
])
Expand Down
14 changes: 8 additions & 6 deletions src/Allowed/Allowed.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\Type;
use PHPStan\Type\UnionType;
use Spaze\PHPStan\Rules\Disallowed\Disallowed;
use Spaze\PHPStan\Rules\Disallowed\DisallowedWithParams;
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
Expand All @@ -36,14 +37,15 @@ public function __construct(
/**
* @param Scope $scope
* @param array<Arg>|null $args
* @param DisallowedWithParams $disallowed
* @param Disallowed|DisallowedWithParams $disallowed
* @return bool
*/
public function isAllowed(Scope $scope, ?array $args, DisallowedWithParams $disallowed): bool
public function isAllowed(Scope $scope, ?array $args, Disallowed $disallowed): bool
{
$hasParams = $disallowed instanceof DisallowedWithParams;
foreach ($disallowed->getAllowInCalls() as $call) {
if ($this->callMatches($scope, $call)) {
return $this->hasAllowedParamsInAllowed($scope, $args, $disallowed);
return !$hasParams || $this->hasAllowedParamsInAllowed($scope, $args, $disallowed);
}
}
foreach ($disallowed->getAllowExceptInCalls() as $call) {
Expand All @@ -53,7 +55,7 @@ public function isAllowed(Scope $scope, ?array $args, DisallowedWithParams $disa
}
foreach ($disallowed->getAllowIn() as $allowedPath) {
if ($this->allowedPath->matches($scope, $allowedPath)) {
return $this->hasAllowedParamsInAllowed($scope, $args, $disallowed);
return !$hasParams || $this->hasAllowedParamsInAllowed($scope, $args, $disallowed);
}
}
if ($disallowed->getAllowExceptIn()) {
Expand All @@ -64,10 +66,10 @@ public function isAllowed(Scope $scope, ?array $args, DisallowedWithParams $disa
}
return true;
}
if ($disallowed->getAllowExceptParams()) {
if ($hasParams && $disallowed->getAllowExceptParams()) {
return $this->hasAllowedParams($scope, $args, $disallowed->getAllowExceptParams(), false);
}
if ($disallowed->getAllowParamsAnywhere()) {
if ($hasParams && $disallowed->getAllowParamsAnywhere()) {
return $this->hasAllowedParams($scope, $args, $disallowed->getAllowParamsAnywhere(), true);
}
if ($disallowed->getAllowInClassWithAttributes() && $scope->isInClass()) {
Expand Down
31 changes: 12 additions & 19 deletions src/DisallowedNamespace.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Spaze\PHPStan\Rules\Disallowed;

use Spaze\PHPStan\Rules\Disallowed\Exceptions\NotImplementedYetException;
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfig;

class DisallowedNamespace implements Disallowed
{
Expand All @@ -15,40 +15,33 @@ class DisallowedNamespace implements Disallowed

private ?string $message;

/** @var list<string> */
private array $allowIn;

/** @var list<string> */
private array $allowExceptIn;

private ?string $errorIdentifier;

private ?string $errorTip;

private AllowedConfig $allowedConfig;


/**
* @param string $namespace
* @param list<string> $excludes
* @param string|null $message
* @param list<string> $allowIn
* @param list<string> $allowExceptIn
* @param AllowedConfig $allowedConfig
* @param string|null $errorIdentifier
* @param string|null $errorTip
*/
public function __construct(
string $namespace,
array $excludes,
?string $message,
array $allowIn,
array $allowExceptIn,
AllowedConfig $allowedConfig,
?string $errorIdentifier,
?string $errorTip
) {
$this->namespace = $namespace;
$this->excludes = $excludes;
$this->message = $message;
$this->allowIn = $allowIn;
$this->allowExceptIn = $allowExceptIn;
$this->allowedConfig = $allowedConfig;
$this->errorIdentifier = $errorIdentifier;
$this->errorTip = $errorTip;
}
Expand Down Expand Up @@ -78,38 +71,38 @@ public function getMessage(): ?string
/** @inheritDoc */
public function getAllowIn(): array
{
return $this->allowIn;
return $this->allowedConfig->getAllowIn();
}


/** @inheritDoc */
public function getAllowExceptIn(): array
{
return $this->allowExceptIn;
return $this->allowedConfig->getAllowExceptIn();
}


public function getAllowInCalls(): array
{
throw new NotImplementedYetException();
return $this->allowedConfig->getAllowInCalls();
}


public function getAllowExceptInCalls(): array
{
throw new NotImplementedYetException();
return $this->allowedConfig->getAllowExceptInCalls();
}


public function getAllowInClassWithAttributes(): array
{
throw new NotImplementedYetException();
return $this->allowedConfig->getAllowInClassWithAttributes();
}


public function getAllowExceptInClassWithAttributes(): array
{
throw new NotImplementedYetException();
return $this->allowedConfig->getAllowExceptInClassWithAttributes();
}


Expand Down
38 changes: 26 additions & 12 deletions src/DisallowedNamespaceFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,33 @@
namespace Spaze\PHPStan\Rules\Disallowed;

use PHPStan\ShouldNotHappenException;
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedConfigFactory;
use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeInConfigException;
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
use Spaze\PHPStan\Rules\Disallowed\Normalizer\Normalizer;

class DisallowedNamespaceFactory
{

private Formatter $formatter;

private Normalizer $normalizer;

private AllowedConfigFactory $allowedConfigFactory;


public function __construct(Normalizer $normalizer)
public function __construct(Formatter $formatter, Normalizer $normalizer, AllowedConfigFactory $allowedConfigFactory)
{
$this->formatter = $formatter;
$this->normalizer = $normalizer;
$this->allowedConfigFactory = $allowedConfigFactory;
}


/**
* @param array<array{namespace?:string|list<string>, class?:string|list<string>, exclude?:string|list<string>, message?:string, allowIn?:list<string>, allowExceptIn?:list<string>, disallowIn?:list<string>, errorIdentifier?:string, errorTip?:string}> $config
* @return list<DisallowedNamespace>
* @throws ShouldNotHappenException
*/
public function createFromConfig(array $config): array
{
Expand All @@ -35,17 +45,21 @@ public function createFromConfig(array $config): array
foreach ((array)($disallowed['exclude'] ?? []) as $exclude) {
$excludes[] = $this->normalizer->normalizeNamespace($exclude);
}
foreach ((array)$namespaces as $namespace) {
$disallowedNamespace = new DisallowedNamespace(
$this->normalizer->normalizeNamespace($namespace),
$excludes,
$disallowed['message'] ?? null,
$disallowed['allowIn'] ?? [],
$disallowed['allowExceptIn'] ?? $disallowed['disallowIn'] ?? [],
$disallowed['errorIdentifier'] ?? null,
$disallowed['errorTip'] ?? null
);
$disallowedNamespaces[$disallowedNamespace->getNamespace()] = $disallowedNamespace;
$namespaces = (array)$namespaces;
try {
foreach ($namespaces as $namespace) {
$disallowedNamespace = new DisallowedNamespace(
$this->normalizer->normalizeNamespace($namespace),
$excludes,
$disallowed['message'] ?? null,
$this->allowedConfigFactory->getConfig($disallowed),
$disallowed['errorIdentifier'] ?? null,
$disallowed['errorTip'] ?? null
);
$disallowedNamespaces[$disallowedNamespace->getNamespace()] = $disallowedNamespace;
}
} catch (UnsupportedParamTypeInConfigException $e) {
throw new ShouldNotHappenException(sprintf('%s: %s', $this->formatter->formatIdentifier($namespaces), $e->getMessage()));
}
}
return array_values($disallowedNamespaces);
Expand Down
17 changes: 8 additions & 9 deletions src/RuleErrors/DisallowedNamespaceRuleErrors.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@
use PHPStan\Analyser\Scope;
use PHPStan\Rules\IdentifierRuleError;
use PHPStan\Rules\RuleErrorBuilder;
use Spaze\PHPStan\Rules\Disallowed\Allowed\AllowedPath;
use Spaze\PHPStan\Rules\Disallowed\Allowed\Allowed;
use Spaze\PHPStan\Rules\Disallowed\DisallowedNamespace;
use Spaze\PHPStan\Rules\Disallowed\Formatter\Formatter;
use Spaze\PHPStan\Rules\Disallowed\Identifier\Identifier;

class DisallowedNamespaceRuleErrors
{

private AllowedPath $allowedPath;
private Allowed $allowed;

private Identifier $identifier;

private Formatter $formatter;


public function __construct(AllowedPath $allowedPath, Identifier $identifier, Formatter $formatter)
public function __construct(Allowed $allowed, Identifier $identifier, Formatter $formatter)
{
$this->allowedPath = $allowedPath;
$this->allowed = $allowed;
$this->identifier = $identifier;
$this->formatter = $formatter;
}
Expand All @@ -40,11 +40,10 @@ public function __construct(AllowedPath $allowedPath, Identifier $identifier, Fo
public function getDisallowedMessage(string $namespace, string $description, Scope $scope, array $disallowedNamespaces, string $identifier): array
{
foreach ($disallowedNamespaces as $disallowedNamespace) {
if ($this->allowedPath->isAllowedPath($scope, $disallowedNamespace)) {
continue;
}

if (!$this->identifier->matches($disallowedNamespace->getNamespace(), $namespace, $disallowedNamespace->getExcludes())) {
if (
!$this->identifier->matches($disallowedNamespace->getNamespace(), $namespace, $disallowedNamespace->getExcludes())
|| $this->allowed->isAllowed($scope, null, $disallowedNamespace)
) {
continue;
}

Expand Down
4 changes: 2 additions & 2 deletions tests/Calls/FunctionCallsAllowInClassWithAttributesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,11 @@ public function testRule(): void
$this->analyse([__DIR__ . '/../src/AttributeClass.php'], [
[
'Calling strlen() is forbidden.',
30,
35,
],
[
'Calling md5() is forbidden.',
41,
48,
],
]);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ public function testRule(): void
$this->analyse([__DIR__ . '/../src/AttributeClass.php'], [
[
'Attribute Attributes\Attribute5 is forbidden.',
27,
32,
],
[
'Attribute Attributes\Attribute4 is forbidden.',
38,
45,
],
]);
}
Expand Down
Loading

0 comments on commit 4a55258

Please sign in to comment.