Issues (135)

Security Analysis    no request data  

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

SourceStubber/ReflectionSourceStubber.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Roave\BetterReflection\SourceLocator\SourceStubber;
6
7
use PhpParser\Builder;
8
use PhpParser\Builder\Class_;
9
use PhpParser\Builder\Declaration;
10
use PhpParser\Builder\Function_;
11
use PhpParser\Builder\FunctionLike;
12
use PhpParser\Builder\Interface_;
13
use PhpParser\Builder\Method;
14
use PhpParser\Builder\Param;
15
use PhpParser\Builder\Property;
16
use PhpParser\Builder\Trait_;
17
use PhpParser\BuilderFactory;
18
use PhpParser\BuilderHelpers;
19
use PhpParser\Comment\Doc;
20
use PhpParser\Node;
21
use PhpParser\Node\Const_;
22
use PhpParser\Node\Name;
23
use PhpParser\Node\Name\FullyQualified;
24
use PhpParser\Node\NullableType;
25
use PhpParser\Node\Stmt\Class_ as ClassNode;
26
use PhpParser\Node\Stmt\ClassConst;
27
use PhpParser\Node\Stmt\TraitUse;
28
use PhpParser\Node\Stmt\TraitUseAdaptation;
29
use PhpParser\NodeAbstract;
30
use PhpParser\PrettyPrinter\Standard;
31
use ReflectionClass as CoreReflectionClass;
32
use ReflectionClassConstant;
33
use ReflectionFunction as CoreReflectionFunction;
34
use ReflectionFunctionAbstract as CoreReflectionFunctionAbstract;
35
use ReflectionMethod as CoreReflectionMethod;
36
use ReflectionNamedType as CoreReflectionNamedType;
37
use ReflectionParameter;
38
use ReflectionProperty as CoreReflectionProperty;
39
use Reflector as CoreReflector;
40
use function array_diff;
41
use function array_key_exists;
42
use function assert;
43
use function class_exists;
44
use function explode;
45
use function function_exists;
46
use function get_defined_constants;
47
use function in_array;
48
use function interface_exists;
49
use function trait_exists;
50
51
/**
52
 * It generates a stub source from internal reflection for given class or function name.
53
 *
54
 * @internal
55
 */
56
final class ReflectionSourceStubber implements SourceStubber
57
{
58
    private const BUILDER_OPTIONS = ['shortArraySyntax' => true];
59
60
    /** @var BuilderFactory */
61
    private $builderFactory;
62
63
    /** @var Standard */
64
    private $prettyPrinter;
65
66 213
    public function __construct()
67
    {
68 213
        $this->builderFactory = new BuilderFactory();
69 213
        $this->prettyPrinter  = new Standard(self::BUILDER_OPTIONS);
70 213
    }
71
72 204
    public function generateClassStub(string $className) : ?StubData
73
    {
74 204
        if (! (class_exists($className, false) || interface_exists($className, false) || trait_exists($className, false))) {
75
            return null;
76
        }
77
78 204
        $classReflection = new CoreReflectionClass($className);
79 204
        $classNode       = $this->createClass($classReflection);
80
81 204
        if ($classNode instanceof Class_) {
82 182
            $this->addClassModifiers($classNode, $classReflection);
83
        }
84
85 204
        if ($classNode instanceof Class_ || $classNode instanceof Interface_) {
86 202
            $this->addExtendsAndImplements($classNode, $classReflection);
87
        }
88
89 204
        if ($classNode instanceof Class_ || $classNode instanceof Trait_) {
90 184
            $this->addProperties($classNode, $classReflection);
91 184
            $this->addTraitUse($classNode, $classReflection);
92
        }
93
94 204
        $this->addDocComment($classNode, $classReflection);
95 204
        $this->addConstants($classNode, $classReflection);
96 204
        $this->addMethods($classNode, $classReflection);
97
98 204
        $extensionName = $classReflection->getExtensionName() ?: null;
99
100 204
        if (! $classReflection->inNamespace()) {
101 200
            return $this->createStubData($this->generateStub($classNode->getNode()), $extensionName);
102
        }
103
104 4
        return $this->createStubData($this->generateStubInNamespace($classNode->getNode(), $classReflection->getNamespaceName()), $extensionName);
105
    }
106
107 4
    public function generateFunctionStub(string $functionName) : ?StubData
108
    {
109 4
        if (! function_exists($functionName)) {
110
            return null;
111
        }
112
113 4
        $functionReflection = new CoreReflectionFunction($functionName);
114 4
        $functionNode       = $this->builderFactory->function($functionReflection->getShortName());
115
116 4
        $this->addDocComment($functionNode, $functionReflection);
117 4
        $this->addParameters($functionNode, $functionReflection);
118
119 4
        $extensionName = $functionReflection->getExtensionName() ?: null;
120
121 4
        if (! $functionReflection->inNamespace()) {
122 4
            return $this->createStubData($this->generateStub($functionNode->getNode()), $extensionName);
123
        }
124
125
        return $this->createStubData($this->generateStubInNamespace($functionNode->getNode(), $functionReflection->getNamespaceName()), $extensionName);
126
    }
127
128 5
    public function generateConstantStub(string $constantName) : ?StubData
129
    {
130
        // Not supported because of resource as value
131 5
        if (in_array($constantName, ['STDIN', 'STDOUT', 'STDERR'], true)) {
132 3
            return null;
133
        }
134
135 2
        $constantData = $this->findConstantData($constantName);
136
137 2
        if ($constantData === null) {
138 1
            return null;
139
        }
140
141 1
        [$constantValue, $extensionName] = $constantData;
142
143 1
        $constantNode = $this->builderFactory->funcCall('define', [$constantName, $constantValue]);
144
145 1
        return $this->createStubData($this->generateStub($constantNode), $extensionName);
146
    }
147
148
    /**
149
     * @psalm-return ?array{0: scalar|scalar[]|null, 1:string}
150
     */
151 2
    private function findConstantData(string $constantName) : ?array
152
    {
153 2
        $constants = get_defined_constants(true);
154
155 2
        foreach ($constants as $constantExtensionName => $extensionConstants) {
156 2
            if (array_key_exists($constantName, $extensionConstants)) {
157
                return [
158 1
                    $extensionConstants[$constantName],
159 1
                    $constantExtensionName !== 'user' ? $constantExtensionName : null,
160
                ];
161
            }
162
        }
163
164 1
        return null;
165
    }
166
167
    /**
168
     * @return Class_|Interface_|Trait_
169
     */
170 204
    private function createClass(CoreReflectionClass $classReflection) : Declaration
171
    {
172 204
        if ($classReflection->isTrait()) {
173 2
            return $this->builderFactory->trait($classReflection->getShortName());
174
        }
175
176 202
        if ($classReflection->isInterface()) {
177 120
            return $this->builderFactory->interface($classReflection->getShortName());
178
        }
179
180 182
        return $this->builderFactory->class($classReflection->getShortName());
181
    }
182
183
    /**
184
     * @param Class_|Interface_|Trait_|Method|Property|Function_                                     $node
185
     * @param CoreReflectionClass|CoreReflectionMethod|CoreReflectionProperty|CoreReflectionFunction $reflection
186
     */
187 208
    private function addDocComment(Builder $node, CoreReflector $reflection) : void
188
    {
189 208
        if ($reflection->getDocComment() === false) {
190 207
            return;
191
        }
192
193 2
        $node->setDocComment(new Doc($reflection->getDocComment()));
194 2
    }
195
196 182
    private function addClassModifiers(Class_ $classNode, CoreReflectionClass $classReflection) : void
197
    {
198 182
        if (! $classReflection->isInterface() && $classReflection->isAbstract()) {
199
            // Interface \Iterator is interface and abstract
200 14
            $classNode->makeAbstract();
201
        }
202
203 182
        if (! $classReflection->isFinal()) {
204 174
            return;
205
        }
206
207 10
        $classNode->makeFinal();
208 10
    }
209
210
    /**
211
     * @param Class_|Interface_ $classNode
212
     */
213 202
    private function addExtendsAndImplements(Declaration $classNode, CoreReflectionClass $classReflection) : void
214
    {
215 202
        $parentClass = $classReflection->getParentClass();
216 202
        $interfaces  = $classReflection->getInterfaceNames();
217
218 202
        if ($parentClass) {
219 84
            $classNode->extend(new FullyQualified($parentClass->getName()));
220 84
            $interfaces = array_diff($interfaces, $parentClass->getInterfaceNames());
221
        }
222
223 202
        foreach ($classReflection->getInterfaces() as $interface) {
224 106
            $interfaces = array_diff($interfaces, $interface->getInterfaceNames());
225
        }
226
227 202
        foreach ($interfaces as $interfaceName) {
228 106
            if ($classNode instanceof Interface_) {
229 48
                $classNode->extend(new FullyQualified($interfaceName));
230
            } else {
231 100
                $classNode->implement(new FullyQualified($interfaceName));
232
            }
233
        }
234 202
    }
235
236 184
    private function addTraitUse(Declaration $classNode, CoreReflectionClass $classReflection) : void
237
    {
238 184
        $alreadyUsedTraitNames = [];
239
240 184
        foreach ($classReflection->getTraitAliases() as $methodNameAlias => $methodInfo) {
241 1
            [$traitName, $methodName] = explode('::', $methodInfo);
242 1
            $traitUseNode             = new TraitUse(
243 1
                [new FullyQualified($traitName)],
244 1
                [new TraitUseAdaptation\Alias(new FullyQualified($traitName), $methodName, null, $methodNameAlias)]
245
            );
246
247 1
            $classNode->addStmt($traitUseNode);
248
249 1
            $alreadyUsedTraitNames[] = $traitName;
250
        }
251
252 184
        foreach (array_diff($classReflection->getTraitNames(), $alreadyUsedTraitNames) as $traitName) {
253 2
            $classNode->addStmt(new TraitUse([new FullyQualified($traitName)]));
254
        }
255 184
    }
256
257 184
    private function addProperties(Declaration $classNode, CoreReflectionClass $classReflection) : void
258
    {
259 184
        $defaultProperties = $classReflection->getDefaultProperties();
260
261 184
        foreach ($classReflection->getProperties() as $propertyReflection) {
262 56
            if (! $this->isPropertyDeclaredInClass($propertyReflection, $classReflection)) {
263 34
                continue;
264
            }
265
266 56
            $propertyNode = $this->builderFactory->property($propertyReflection->getName());
267
268 56
            $this->addPropertyModifiers($propertyNode, $propertyReflection);
269 56
            $this->addDocComment($propertyNode, $propertyReflection);
270
271 56
            if (array_key_exists($propertyReflection->getName(), $defaultProperties)) {
272 56
                $propertyNode->setDefault($defaultProperties[$propertyReflection->getName()]);
273
            }
274
275 56
            $classNode->addStmt($propertyNode);
276
        }
277 184
    }
278
279 56
    private function isPropertyDeclaredInClass(CoreReflectionProperty $propertyReflection, CoreReflectionClass $classReflection) : bool
280
    {
281 56
        if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
282 33
            return false;
283
        }
284
285 56
        foreach ($classReflection->getTraits() as $trait) {
286 1
            if ($trait->hasProperty($propertyReflection->getName())) {
287 1
                return false;
288
            }
289
        }
290
291 56
        return true;
292
    }
293
294 56
    private function addPropertyModifiers(Property $propertyNode, CoreReflectionProperty $propertyReflection) : void
295
    {
296 56
        if ($propertyReflection->isStatic()) {
297 1
            $propertyNode->makeStatic();
298
        }
299
300 56
        if ($propertyReflection->isPublic()) {
301 25
            $propertyNode->makePublic();
302
        }
303
304 56
        if ($propertyReflection->isProtected()) {
305 34
            $propertyNode->makeProtected();
306
        }
307
308 56
        if (! $propertyReflection->isPrivate()) {
309 56
            return;
310
        }
311
312 34
        $propertyNode->makePrivate();
313 34
    }
314
315 204
    private function addConstants(Declaration $classNode, CoreReflectionClass $classReflection) : void
316
    {
317 204
        foreach ($classReflection->getReflectionConstants() as $constantReflection) {
318 51
            if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
319 17
                continue;
320
            }
321
322 51
            $classConstantNode = new ClassConst(
323 51
                [new Const_($constantReflection->getName(), BuilderHelpers::normalizeValue($constantReflection->getValue()))],
324 51
                $this->constantVisibilityFlags($constantReflection)
325
            );
326
327 51
            if ($constantReflection->getDocComment() !== false) {
328 1
                $classConstantNode->setDocComment(new Doc($constantReflection->getDocComment()));
329
            }
330
331 51
            $classNode->addStmt($classConstantNode);
332
        }
333 204
    }
334
335 51
    private function constantVisibilityFlags(ReflectionClassConstant $constant) : int
336
    {
337 51
        if ($constant->isPrivate()) {
338 1
            return ClassNode::MODIFIER_PRIVATE;
339
        }
340
341 51
        if ($constant->isProtected()) {
342 1
            return ClassNode::MODIFIER_PROTECTED;
343
        }
344
345 51
        return ClassNode::MODIFIER_PUBLIC;
346
    }
347
348 204
    private function addMethods(Declaration $classNode, CoreReflectionClass $classReflection) : void
349
    {
350 204
        foreach ($classReflection->getMethods() as $methodReflection) {
351 189
            if (! $this->isMethodDeclaredInClass($methodReflection, $classReflection)) {
352 92
                continue;
353
            }
354
355 188
            $methodNode = $this->builderFactory->method($methodReflection->getName());
356
357 188
            $this->addMethodFlags($methodNode, $methodReflection);
358 188
            $this->addDocComment($methodNode, $methodReflection);
359 188
            $this->addParameters($methodNode, $methodReflection);
360
361 188
            $returnType = $methodReflection->getReturnType();
362 188
            assert($returnType instanceof CoreReflectionNamedType || $returnType === null);
363
364 188
            if ($methodReflection->getReturnType() !== null) {
365 2
                $methodNode->setReturnType($this->formatType($returnType));
366
            }
367
368 188
            $classNode->addStmt($methodNode);
369
        }
370 204
    }
371
372 189
    private function isMethodDeclaredInClass(CoreReflectionMethod $methodReflection, CoreReflectionClass $classReflection) : bool
373
    {
374 189
        if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
375 92
            return false;
376
        }
377
378 188
        if (array_key_exists($methodReflection->getName(), $classReflection->getTraitAliases())) {
379 1
            return false;
380
        }
381
382 188
        foreach ($classReflection->getTraits() as $trait) {
383 2
            if ($trait->hasMethod($methodReflection->getName())) {
384 1
                return false;
385
            }
386
        }
387
388 188
        return true;
389
    }
390
391 188
    private function addMethodFlags(Method $methodNode, CoreReflectionMethod $methodReflection) : void
392
    {
393 188
        if ($methodReflection->isFinal()) {
394 59
            $methodNode->makeFinal();
395
        }
396
397 188
        if ($methodReflection->isAbstract()) {
398 112
            $methodNode->makeAbstract();
399
        }
400
401 188
        if ($methodReflection->isStatic()) {
402 41
            $methodNode->makeStatic();
403
        }
404
405 188
        if ($methodReflection->isPublic()) {
406 187
            $methodNode->makePublic();
407
        }
408
409 188
        if ($methodReflection->isProtected()) {
410 4
            $methodNode->makeProtected();
411
        }
412
413 188
        if ($methodReflection->isPrivate()) {
414 58
            $methodNode->makePrivate();
415
        }
416
417 188
        if (! $methodReflection->returnsReference()) {
418 188
            return;
419
        }
420
421 1
        $methodNode->makeReturnByRef();
422 1
    }
423
424 192
    private function addParameters(FunctionLike $functionNode, CoreReflectionFunctionAbstract $functionReflectionAbstract) : void
425
    {
426 192
        foreach ($functionReflectionAbstract->getParameters() as $parameterReflection) {
427 172
            $parameterNode = $this->builderFactory->param($parameterReflection->getName());
428
429 172
            $this->addParameterModifiers($parameterReflection, $parameterNode);
430
431 172
            if ($parameterReflection->isOptional() && ! $parameterReflection->isVariadic()) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method isVariadic() does only exist in the following sub-classes of ReflectionParameter: Roave\BetterReflection\R...ter\ReflectionParameter. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
432 130
                $parameterNode->setDefault($this->parameterDefaultValue($parameterReflection, $functionReflectionAbstract));
433
            }
434
435 172
            $functionNode->addParam($this->addParameterModifiers($parameterReflection, $parameterNode));
436
        }
437 192
    }
438
439 172
    private function addParameterModifiers(ReflectionParameter $parameterReflection, Param $parameterNode) : Param
440
    {
441 172
        if ($parameterReflection->isVariadic()) {
442 8
            $parameterNode->makeVariadic();
443
        }
444
445 172
        if ($parameterReflection->isPassedByReference()) {
446 18
            $parameterNode->makeByRef();
447
        }
448
449 172
        $parameterType = $parameterReflection->getType();
450 172
        assert($parameterType instanceof CoreReflectionNamedType || $parameterType === null);
451
452 172
        if ($parameterReflection->getType() !== null) {
453 67
            $parameterNode->setType($this->formatType($parameterType));
454
        }
455
456 172
        return $parameterNode;
457
    }
458
459
    /**
460
     * @return mixed
461
     */
462 130
    private function parameterDefaultValue(
463
        ReflectionParameter $parameterReflection,
464
        CoreReflectionFunctionAbstract $functionReflectionAbstract
465
    ) {
466 130
        if ($functionReflectionAbstract->isInternal()) {
467 129
            return null;
468
        }
469
470 1
        return $parameterReflection->getDefaultValue();
471
    }
472
473
    /**
474
     * @return Name|FullyQualified|NullableType
475
     */
476 67
    private function formatType(CoreReflectionNamedType $type) : NodeAbstract
477
    {
478 67
        $name     = $type->getName();
479 67
        $nameNode = $type->isBuiltin() || in_array($name, ['self', 'parent'], true) ? new Name($name) : new FullyQualified($name);
480
481 67
        return $type->allowsNull() ? new NullableType($nameNode) : $nameNode;
482
    }
483
484 4
    private function generateStubInNamespace(Node $node, string $namespaceName) : string
485
    {
486 4
        $namespaceBuilder = $this->builderFactory->namespace($namespaceName);
487 4
        $namespaceBuilder->addStmt($node);
488
489 4
        return $this->generateStub($namespaceBuilder->getNode());
490
    }
491
492 209
    private function generateStub(Node $node) : string
493
    {
494 209
        return "<?php\n\n" . $this->prettyPrinter->prettyPrint([$node]) . ($node instanceof Node\Expr\FuncCall ? ';' : '') . "\n";
495
    }
496
497 209
    private function createStubData(string $stub, ?string $extensionName) : StubData
498
    {
499 209
        return new StubData($stub, $extensionName);
500
    }
501
}
502