Skip to content

Commit

Permalink
Cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
LastDragon-ru authored Jan 14, 2024
2 parents f5934ef + 0b096ac commit bbc6b3c
Show file tree
Hide file tree
Showing 177 changed files with 621 additions and 260 deletions.
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"opis/json-schema": "^2.3.0",
"phpdocumentor/reflection-docblock": "^5.2",
"phpdocumentor/type-resolver": "^1.7",
"phpstan/phpdoc-parser": "^1.23",
"phpstan/phpdoc-parser": "^1.25",
"psr/http-message": "^1.0.0|^2.0.0",
"symfony/filesystem": "^6.3.0",
"symfony/finder": "^6.3.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace Larastan\Larastan\ReturnTypes;

use Illuminate\Contracts\Container\Container;
use LastDragon_ru\LaraASP\Dev\PhpStan\Container\Extension;
use Override;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
Expand All @@ -15,7 +16,7 @@
*
* @internal
*
* @see ContainerExtension
* @see Extension
*/
class ContainerArrayAccessDynamicMethodReturnTypeExtension implements DynamicMethodReturnTypeExtension {
public function __construct(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Larastan\Larastan\ReturnTypes;

use Illuminate\Contracts\Container\Container;
use LastDragon_ru\LaraASP\Dev\PhpStan\Extensions\Container\ContainerExtension;
use LastDragon_ru\LaraASP\Dev\PhpStan\Container\Extension;
use Override;
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
Expand All @@ -14,9 +14,8 @@
/**
* Original class uses {@see Container::make()} it is slow and unwanted
*
* @see Extension
* @internal
*
* @see ContainerExtension
*/
class ContainerMakeDynamicReturnTypeExtension implements DynamicMethodReturnTypeExtension {
#[Override]
Expand Down
70 changes: 70 additions & 0 deletions dev/PhpStan/ClassMustBeFinal/Rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\Dev\PhpStan\ClassMustBeFinal;

use Override;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\Rule as RuleContract;
use PHPStan\Rules\RuleErrorBuilder;

use function sprintf;

/**
* Makes the `final` keyword required for all subclasses of the specified class.
*
* @internal
* @implements RuleContract<InClassNode>
*/
class Rule implements RuleContract {
public function __construct(
/**
* @var array<array-key, class-string>
*/
protected readonly array $classes,
) {
// empty
}

#[Override]
public function getNodeType(): string {
return InClassNode::class;
}

/**
* @inheritDoc
*/
#[Override]
public function processNode(Node $node, Scope $scope): array {
// Skip?
$origin = $node->getOriginalNode();

if (!($origin instanceof Class_) || $origin->isFinal() || $origin->isAbstract() || $origin->isAnonymous()) {
return [];
}

// Must be final?
$reflection = $node->getClassReflection();
$mustBeFinal = false;

foreach ($this->classes as $class) {
if ($reflection->is($class) || $reflection->implementsInterface($class)) {
$mustBeFinal = true;
break;
}
}

if ($mustBeFinal) {
return [
RuleErrorBuilder::message(
sprintf('Class `%s` must be `final`.', $reflection->getName()),
)->build(),
];
}

// Nope
return [];
}
}
60 changes: 60 additions & 0 deletions dev/PhpStan/ClassMustBeFinal/RuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\Dev\PhpStan\ClassMustBeFinal;

use Override;
use PHPStan\Rules\Rule as RuleContract;
use PHPStan\Testing\RuleTestCase;
use PHPUnit\Framework\Attributes\CoversClass;

use function sprintf;

/**
* @internal
* @extends RuleTestCase<Rule>
*/
#[CoversClass(Rule::class)]
final class RuleTest extends RuleTestCase {
#[Override]
protected function getRule(): RuleContract {
return new Rule([
RuleTest_MustBeFinalMarker::class,
]);
}

public function testRule(): void {
$this->analyse([__FILE__], [
[
sprintf('Class `%s` must be `final`.', RuleTest_MustBeFinal::class),
50,
],
]);
}
}

// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
interface RuleTest_MustBeFinalMarker {
// empty
}

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_MustBeFinal implements RuleTest_MustBeFinalMarker {
// empty
}

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
final class RuleTest_AlreadyFinal implements RuleTest_MustBeFinalMarker {
// empty
}
15 changes: 15 additions & 0 deletions dev/PhpStan/ClassMustBeFinal/extension.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
parameters:
classMustBeFinal: []

parametersSchema:
classMustBeFinal: structure({
classes: listOf(string())
})

services:
-
class: LastDragon_ru\LaraASP\Dev\PhpStan\ClassMustBeFinal\Rule
arguments:
classes: %classMustBeFinal.classes%
tags:
- phpstan.rules.rule
113 changes: 113 additions & 0 deletions dev/PhpStan/ClassMustBeInternal/Rule.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\Dev\PhpStan\ClassMustBeInternal;

use Override;
use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Reflection\ClassReflection;
use PHPStan\Rules\Rule as RuleContract;
use PHPStan\Rules\RuleErrorBuilder;

use function in_array;
use function pathinfo;
use function sprintf;
use function str_ends_with;
use function str_starts_with;

use const PATHINFO_FILENAME;

/**
* Makes the `@internal` tag required for all subclasses of the specified class.
*
* @internal
* @implements RuleContract<InClassNode>
*/
class Rule implements RuleContract {
public function __construct(
/**
* @var array<array-key, class-string>
*/
protected readonly array $classes,
/**
* @var array<array-key, class-string>
*/
protected readonly array $ignored = [],
) {
// empty
}

#[Override]
public function getNodeType(): string {
return InClassNode::class;
}

/**
* @inheritDoc
*/
#[Override]
public function processNode(Node $node, Scope $scope): array {
// Skip?
$origin = $node->getOriginalNode();

if (!($origin instanceof Class_) || $origin->isAnonymous()) {
return [];
}

// Ignored?
if (in_array((string) $origin->namespacedName, $this->ignored, true)) {
return [];
}

// Internal?
$reflection = $node->getClassReflection();
$isInternal = (bool) $reflection->getResolvedPhpDoc()?->isInternal();

if ($isInternal) {
return [];
}

// Must be internal?
if ($this->mustBe($reflection)) {
return [
RuleErrorBuilder::message(
sprintf('Class `%s` must be marked by `@internal`.', $reflection->getName()),
)->build(),
];
}

// Return
return [];
}

private function mustBe(ClassReflection $reflection): bool {
return $this->mustBeIsTestInternal($reflection)
|| $this->mustBeIsInstanceOf($reflection);
}

private function mustBeIsInstanceOf(ClassReflection $reflection): bool {
$mustBe = false;

foreach ($this->classes as $class) {
// Instance?
if ($reflection->is($class) || $reflection->implementsInterface($class)) {
$mustBe = true;
break;
}
}

return $mustBe;
}

private function mustBeIsTestInternal(ClassReflection $reflection): bool {
$classname = $reflection->getNativeReflection()->getShortName();
$filename = pathinfo((string) $reflection->getFileName(), PATHINFO_FILENAME);
$mustBe = $filename
&& str_ends_with($filename, 'Test')
&& str_starts_with($classname, "{$filename}_");

return $mustBe;
}
}
90 changes: 90 additions & 0 deletions dev/PhpStan/ClassMustBeInternal/RuleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php declare(strict_types = 1);

namespace LastDragon_ru\LaraASP\Dev\PhpStan\ClassMustBeInternal;

use Override;
use PHPStan\Rules\Rule as RuleContract;
use PHPStan\Testing\RuleTestCase;
use PHPUnit\Framework\Attributes\CoversClass;

use function sprintf;

/**
* @internal
* @extends RuleTestCase<Rule>
*/
#[CoversClass(Rule::class)]
final class RuleTest extends RuleTestCase {
#[Override]
protected function getRule(): RuleContract {
return new Rule(
[
RuleTest_MustBeInternalMarker::class,
],
[
RuleTest_MustBeIgnored::class,
],
);
}

public function testRule(): void {
$this->analyse([__FILE__], [
[
sprintf('Class `%s` must be marked by `@internal`.', RuleTest_MustBeInternal::class),
58,
],
[
sprintf('Class `%s` must be marked by `@internal`.', RuleTest_TestMustBeInternal::class),
80,
],
]);
}
}

// @phpcs:disable PSR1.Classes.ClassDeclaration.MultipleClasses
// @phpcs:disable Squiz.Classes.ValidClassName.NotCamelCaps

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
interface RuleTest_MustBeInternalMarker {
// empty
}

/**
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_MustBeInternal implements RuleTest_MustBeInternalMarker {
// empty
}

/**
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_MustBeIgnored implements RuleTest_MustBeInternalMarker {
// empty
}

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_AlreadyInternal implements RuleTest_MustBeInternalMarker {
// empty
}

/**
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_TestMustBeInternal {
// empty
}

/**
* @internal
* @noinspection PhpMultipleClassesDeclarationsInOneFile
*/
class RuleTest_TestAlreadyInternal {
// empty
}
Loading

0 comments on commit bbc6b3c

Please sign in to comment.