Skip to content
Merged
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,34 @@ Add the following section to your `composer.json` file:
},
```

### PHPStan

PHPStan extensions are registered via [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), which is installed
alongside this package. Allow the plugin when prompted by Composer, or add it to your `composer.json` file:

```json
"config": {
"allow-plugins": {
"phpstan/extension-installer": true
}
}
```

The Worksome rules, along with the PHPStan extensions this package depends on, are then loaded automatically.

Laravel projects should also install Larastan and the Larastan strict rules:

```
composer require --dev larastan/larastan canvural/larastan-strict-rules
```

The Laravel rules (and Larastan within the Rector config) are only loaded when Larastan is installed, and the strict
rules are only loaded when both packages are installed. Packages listed in the extension installer's `ignore` option
are treated as not installed.

> **Note:** Do not include the configuration files of any PHPStan extension that is registered with the extension installer (e.g. `vendor/larastan/larastan/extension.neon`
> or `vendor/worksome/coding-style/larastan.neon`) in your `phpstan.neon` file. PHPStan will fail if a file is included more than once.

## Usage

To use it, simply run one of the scripts added to composer.
Expand Down
27 changes: 23 additions & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
"require": {
"php": "^8.4",
"composer-plugin-api": "^2.0",
"canvural/larastan-strict-rules": "^3.0",
"larastan/larastan": "^3.10",
"laravel/pao": "^1.1",
"phpstan/extension-installer": "^1.4",
"phpstan/phpstan": "^2.2",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-mockery": "^2.0",
Expand All @@ -17,13 +16,21 @@
"symplify/easy-coding-standard": "^13.2.1"
},
"require-dev": {
"canvural/larastan-strict-rules": "^3.0",
"composer/composer": "^2.10",
"friendsofphp/php-cs-fixer": "^3.95.5",
"larastan/larastan": "^3.10",
"orchestra/testbench": "^11.1",
"pestphp/pest": "^5.0",
"squizlabs/php_codesniffer": "^4.0.1"
},
"suggest": {
"canvural/larastan-strict-rules": "Required for the Larastan strict rules in Laravel projects.",
"larastan/larastan": "Required for the Laravel PHPStan rules in Laravel projects."
},
"conflict": {
"canvural/larastan-strict-rules": "<3.0",
"larastan/larastan": "<3.10",
"squizlabs/php_codesniffer": ">= 5"
},
"autoload": {
Expand Down Expand Up @@ -52,12 +59,24 @@
}
],
"extra": {
"class": "Worksome\\CodingStyle\\Plugin"
"class": "Worksome\\CodingStyle\\Plugin",
"phpstan": {
"includes": [
"extension.php"
]
},
"phpstan/extension-installer": {
"ignore": [
"canvural/larastan-strict-rules",
"larastan/larastan"
]
}
},
"config": {
"allow-plugins": {
"dealerdirect/phpcodesniffer-composer-installer": true,
"pestphp/pest-plugin": true
"pestphp/pest-plugin": true,
"phpstan/extension-installer": true
},
"sort-packages": true
},
Expand Down
16 changes: 16 additions & 0 deletions extension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

declare(strict_types=1);

use PHPStan\ExtensionInstaller\GeneratedConfig;

$larastan = isset(GeneratedConfig::EXTENSIONS['larastan/larastan']);
$larastanStrictRules = $larastan && isset(GeneratedConfig::EXTENSIONS['canvural/larastan-strict-rules']);

return [
'includes' => [
__DIR__ . '/phpstan.neon',
...($larastan ? [__DIR__ . '/larastan.neon'] : []),
...($larastanStrictRules ? [__DIR__ . '/larastan-strict-rules.neon'] : []),
],
];
46 changes: 46 additions & 0 deletions larastan-strict-rules.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
includes:
- ../../canvural/larastan-strict-rules/rules.neon

parameters:
larastanStrictRules:
allRules: false
noFacade: true
noValidationInController: true
scopeShouldReturnQueryBuilder: true
noPropertyAccessor: true
noGlobalLaravelFunction: true
allowedGlobalFunctions:
- class_basename
- class_uses_recursive
- e
- env
- object_get
- preg_replace_array
- retry
- str
- tap
- throw_if
- throw_unless
- trait_uses_recursive
- with
- collect
- data_fill
- data_get
- data_set
- value
- config
- fake
- method_field
- now
- old
- redirect
- response
- route
- trans
- trans_choice
- __
- view
- url
- secure_url
- asset
- secure_asset
47 changes: 0 additions & 47 deletions larastan.neon
Original file line number Diff line number Diff line change
@@ -1,51 +1,4 @@
includes:
- ./phpstan.neon
- ../../larastan/larastan/extension.neon
- ../../canvural/larastan-strict-rules/rules.neon

parameters:
larastanStrictRules:
allRules: false
noFacade: true
noValidationInController: true
scopeShouldReturnQueryBuilder: true
noPropertyAccessor: true
noGlobalLaravelFunction: true
allowedGlobalFunctions:
- class_basename
- class_uses_recursive
- e
- env
- object_get
- preg_replace_array
- retry
- str
- tap
- throw_if
- throw_unless
- trait_uses_recursive
- with
- collect
- data_fill
- data_get
- data_set
- value
- config
- fake
- method_field
- now
- old
- redirect
- response
- route
- trans
- trans_choice
- __
- view
- url
- secure_url
- asset
- secure_asset
worksomeLaravel:
allRules: true
disallowEnvironmentChecks: %worksomeLaravel.allRules%
Expand Down
5 changes: 0 additions & 5 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
includes:
- ../../phpstan/phpstan-deprecation-rules/rules.neon
- ../../phpstan/phpstan-mockery/extension.neon
- ../../spaze/phpstan-disallowed-calls/extension.neon

parameters:
disallowedNamespaces:
- namespace: 'JetBrains\PhpStorm\*'
Expand Down
2 changes: 1 addition & 1 deletion rector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use Worksome\CodingStyle\WorksomeRectorConfig;

return WorksomeRectorConfig::configure(larastan: false)
return WorksomeRectorConfig::configure()
->withPaths([
__DIR__ . '/src',
__DIR__ . '/tests',
Expand Down
16 changes: 7 additions & 9 deletions src/PHPStan/DisallowPHPUnitRule.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,36 +5,34 @@
namespace Worksome\CodingStyle\PHPStan;

use PhpParser\Node;
use PhpParser\Node\Stmt\Class_;
use PHPStan\Analyser\Scope;
use PHPStan\Node\InClassNode;
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
use PHPUnit\Framework\TestCase;

/**
* @implements Rule<Class_>
* @implements Rule<InClassNode>
*/
final class DisallowPHPUnitRule implements Rule
{
public function getNodeType(): string
{
return Class_::class;
return InClassNode::class;
}

/**
* @param Class_ $node
* @param InClassNode $node
*/
public function processNode(Node $node, Scope $scope): array
{
if ($node->isAbstract()) {
return [];
}
$classReflection = $node->getClassReflection();

if ($node->namespacedName === null) {
if ($classReflection->isAbstract() || $classReflection->isAnonymous()) {
return [];
}

if (! is_subclass_of($node->namespacedName->toString(), TestCase::class)) {
if (! $classReflection->isSubclassOf(TestCase::class)) {
return [];
}

Expand Down
11 changes: 10 additions & 1 deletion src/Rector/Generic/DisallowedAttributesRector.php
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,27 @@ public function configure(array $configuration): void
/** @param Node\AttributeGroup $node */
public function refactor(Node $node)
{
$hasChanged = false;

foreach ($node->attrs as $key => $attribute) {
if (! $this->isNames($attribute, $this->disallowedAttributes)) {
continue;
}

unset($node->attrs[$key]);
$hasChanged = true;
}

if (! $hasChanged) {
return null;
}

if ($node->attrs === []) {
return NodeTraverser::REMOVE_NODE;
}

return null;
$node->attrs = array_values($node->attrs);

return $node;
}
}
39 changes: 31 additions & 8 deletions src/WorksomeRectorConfig.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Worksome\CodingStyle;

use PHPStan\ExtensionInstaller\GeneratedConfig;
use Rector\Config\RectorConfig;
use Rector\Configuration\RectorConfigBuilder;
use Rector\DeadCode\Rector\ClassMethod\RemoveUselessParamTagRector;
Expand All @@ -19,18 +20,18 @@

class WorksomeRectorConfig
{
public static function configure(bool $larastan = true): RectorConfigBuilder
public static function configure(): RectorConfigBuilder
{
$phpstanConfig = getcwd() . '/phpstan.neon';
$larastan = class_exists(
GeneratedConfig::class
)
? GeneratedConfig::EXTENSIONS['larastan/larastan'] ?? null
: null;

return RectorConfig::configure()
->withPHPStanConfigs(match (true) {
file_exists($phpstanConfig) => [getcwd() . '/phpstan.neon'],
$larastan => [getcwd() . '/vendor/larastan/larastan/extension.neon'],
default => [],
})
->withPHPStanConfigs(self::phpstanExtensionConfigs())
->withBootstrapFiles(
$larastan ? [getcwd() . '/vendor/larastan/larastan/bootstrap.php'] : []
$larastan !== null ? [$larastan['install_path'] . '/bootstrap.php'] : []
)
->withSets([
WorksomeSetList::GENERIC_CODE_QUALITY,
Expand All @@ -50,4 +51,26 @@ public static function configure(bool $larastan = true): RectorConfigBuilder
StrStartsWithRector::class,
]);
}

/**
* Rector does not load extensions registered by phpstan/extension-installer, so they are passed explicitly.
*
* @return list<string>
*/
private static function phpstanExtensionConfigs(): array
{
if (! class_exists(GeneratedConfig::class)) {
return [];
}

$configs = [];

foreach (GeneratedConfig::EXTENSIONS as $extension) {
foreach ($extension['extra']['includes'] ?? [] as $include) {
$configs[] = $extension['install_path'] . '/' . $include;
}
}

return $configs;
}
}
3 changes: 0 additions & 3 deletions stubs/phpstan.neon.stub
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
includes:
- ./vendor/worksome/coding-style/larastan.neon

parameters:
paths:
- app
Expand Down
Loading