Skip to content
This repository was archived by the owner on Sep 1, 2024. It is now read-only.

Commit 35e6877

Browse files
committed
Rule for checking non-existent files
1 parent 2a107b5 commit 35e6877

File tree

1 file changed

+90
-0
lines changed

1 file changed

+90
-0
lines changed

src/RequireFileExistsRule.php

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Bellangelo\RequireFileExistsRule;
6+
7+
use PhpParser\Node;
8+
use PhpParser\Node\Expr\Include_;
9+
use PhpParser\Node\Expr\BinaryOp\Concat;
10+
use PhpParser\Node\Expr\ClassConstFetch;
11+
use PhpParser\Node\Scalar\MagicConst\Dir;
12+
use PhpParser\Node\Scalar\String_;
13+
use PHPStan\Analyser\Scope;
14+
use PHPStan\Reflection\ReflectionProvider;
15+
use PHPStan\Rules\Rule;
16+
use PHPStan\Rules\RuleErrorBuilder;
17+
18+
class RequireFileExistsRule implements Rule
19+
{
20+
private ReflectionProvider $reflectionProvider;
21+
22+
public function __construct(ReflectionProvider $reflectionProvider)
23+
{
24+
$this->reflectionProvider = $reflectionProvider;
25+
}
26+
27+
public function getNodeType(): string
28+
{
29+
return Include_::class;
30+
}
31+
32+
public function processNode(Node $node, Scope $scope): array
33+
{
34+
if ($node instanceof Include_) {
35+
$filePath = $this->resolveFilePath($node->expr, $scope);
36+
if ($filePath !== null && !file_exists($filePath)) {
37+
return [
38+
RuleErrorBuilder::message(sprintf('Included or required file "%s" does not exist.', $filePath))->build(),
39+
];
40+
}
41+
}
42+
43+
return [];
44+
}
45+
46+
private function resolveFilePath(Node $node, Scope $scope): ?string
47+
{
48+
if ($node instanceof String_) {
49+
return $node->value;
50+
}
51+
52+
if ($node instanceof Concat) {
53+
$left = $this->resolveFilePath($node->left, $scope);
54+
$right = $this->resolveFilePath($node->right, $scope);
55+
if ($left !== null && $right !== null) {
56+
return $left . $right;
57+
}
58+
}
59+
60+
if ($node instanceof Dir) {
61+
return dirname($scope->getFile());
62+
}
63+
64+
if ($node instanceof ClassConstFetch) {
65+
return $this->resolveClassConstant($node);
66+
}
67+
68+
return null;
69+
}
70+
71+
private function resolveClassConstant(ClassConstFetch $node): ?string
72+
{
73+
if ($node->class instanceof Node\Name && $node->name instanceof Node\Identifier) {
74+
$className = (string) $node->class;
75+
$constantName = $node->name->toString();
76+
77+
if ($this->reflectionProvider->hasClass($className)) {
78+
$classReflection = $this->reflectionProvider->getClass($className);
79+
if ($classReflection->hasConstant($constantName)) {
80+
$constantReflection = $classReflection->getConstant($constantName);
81+
$constantValue = $constantReflection->getValue();
82+
if (is_string($constantValue)) {
83+
return $constantValue;
84+
}
85+
}
86+
}
87+
}
88+
return null;
89+
}
90+
}

0 commit comments

Comments
 (0)