Issues (138)

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
        /** @psalm-var array<string, array<string,int|string|float|bool|null|array|resource>> $constants */
154 2
        $constants = get_defined_constants(true);
155
156 2
        foreach ($constants as $constantExtensionName => $extensionConstants) {
157 2
            if (array_key_exists($constantName, $extensionConstants)) {
158
                return [
159 1
                    $extensionConstants[$constantName],
160 1
                    $constantExtensionName !== 'user' ? $constantExtensionName : null,
161
                ];
162
            }
163
        }
164
165 1
        return null;
166
    }
167
168
    /**
169
     * @return Class_|Interface_|Trait_
170
     */
171 204
    private function createClass(CoreReflectionClass $classReflection) : Declaration
172
    {
173 204
        if ($classReflection->isTrait()) {
174 2
            return $this->builderFactory->trait($classReflection->getShortName());
175
        }
176
177 202
        if ($classReflection->isInterface()) {
178 120
            return $this->builderFactory->interface($classReflection->getShortName());
179
        }
180
181 182
        return $this->builderFactory->class($classReflection->getShortName());
182
    }
183
184
    /**
185
     * @param Class_|Interface_|Trait_|Method|Property|Function_                                     $node
186
     * @param CoreReflectionClass|CoreReflectionMethod|CoreReflectionProperty|CoreReflectionFunction $reflection
187
     */
188 208
    private function addDocComment(Builder $node, CoreReflector $reflection) : void
189
    {
190 208
        if ($reflection->getDocComment() === false) {
191 207
            return;
192
        }
193
194 2
        $node->setDocComment(new Doc($reflection->getDocComment()));
195 2
    }
196
197 182
    private function addClassModifiers(Class_ $classNode, CoreReflectionClass $classReflection) : void
198
    {
199 182
        if (! $classReflection->isInterface() && $classReflection->isAbstract()) {
200
            // Interface \Iterator is interface and abstract
201 14
            $classNode->makeAbstract();
202
        }
203
204 182
        if (! $classReflection->isFinal()) {
205 174
            return;
206
        }
207
208 10
        $classNode->makeFinal();
209 10
    }
210
211
    /**
212
     * @param Class_|Interface_ $classNode
213
     */
214 202
    private function addExtendsAndImplements(Declaration $classNode, CoreReflectionClass $classReflection) : void
215
    {
216 202
        $parentClass = $classReflection->getParentClass();
217 202
        $interfaces  = $classReflection->getInterfaceNames();
218
219 202
        if ($parentClass) {
220 84
            $classNode->extend(new FullyQualified($parentClass->getName()));
221 84
            $interfaces = array_diff($interfaces, $parentClass->getInterfaceNames());
222
        }
223
224 202
        foreach ($classReflection->getInterfaces() as $interface) {
225 106
            $interfaces = array_diff($interfaces, $interface->getInterfaceNames());
226
        }
227
228 202
        foreach ($interfaces as $interfaceName) {
229 106
            if ($classNode instanceof Interface_) {
230 48
                $classNode->extend(new FullyQualified($interfaceName));
231
            } else {
232 100
                $classNode->implement(new FullyQualified($interfaceName));
233
            }
234
        }
235 202
    }
236
237 184
    private function addTraitUse(Declaration $classNode, CoreReflectionClass $classReflection) : void
238
    {
239 184
        $alreadyUsedTraitNames = [];
240
241 184
        foreach ($classReflection->getTraitAliases() as $methodNameAlias => $methodInfo) {
242 1
            [$traitName, $methodName] = explode('::', $methodInfo);
243 1
            $traitUseNode             = new TraitUse(
244 1
                [new FullyQualified($traitName)],
245 1
                [new TraitUseAdaptation\Alias(new FullyQualified($traitName), $methodName, null, $methodNameAlias)]
246
            );
247
248 1
            $classNode->addStmt($traitUseNode);
249
250 1
            $alreadyUsedTraitNames[] = $traitName;
251
        }
252
253 184
        foreach (array_diff($classReflection->getTraitNames(), $alreadyUsedTraitNames) as $traitName) {
254 2
            $classNode->addStmt(new TraitUse([new FullyQualified($traitName)]));
255
        }
256 184
    }
257
258 184
    private function addProperties(Declaration $classNode, CoreReflectionClass $classReflection) : void
259
    {
260 184
        $defaultProperties = $classReflection->getDefaultProperties();
261
262 184
        foreach ($classReflection->getProperties() as $propertyReflection) {
263 56
            if (! $this->isPropertyDeclaredInClass($propertyReflection, $classReflection)) {
264 34
                continue;
265
            }
266
267 56
            $propertyNode = $this->builderFactory->property($propertyReflection->getName());
268
269 56
            $this->addPropertyModifiers($propertyNode, $propertyReflection);
270 56
            $this->addDocComment($propertyNode, $propertyReflection);
271
272 56
            if (array_key_exists($propertyReflection->getName(), $defaultProperties)) {
273 56
                $propertyNode->setDefault($defaultProperties[$propertyReflection->getName()]);
274
            }
275
276 56
            $classNode->addStmt($propertyNode);
277
        }
278 184
    }
279
280 56
    private function isPropertyDeclaredInClass(CoreReflectionProperty $propertyReflection, CoreReflectionClass $classReflection) : bool
281
    {
282 56
        if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
283 33
            return false;
284
        }
285
286 56
        foreach ($classReflection->getTraits() as $trait) {
287 1
            if ($trait->hasProperty($propertyReflection->getName())) {
288 1
                return false;
289
            }
290
        }
291
292 56
        return true;
293
    }
294
295 56
    private function addPropertyModifiers(Property $propertyNode, CoreReflectionProperty $propertyReflection) : void
296
    {
297 56
        if ($propertyReflection->isStatic()) {
298 1
            $propertyNode->makeStatic();
299
        }
300
301 56
        if ($propertyReflection->isPublic()) {
302 25
            $propertyNode->makePublic();
303
        }
304
305 56
        if ($propertyReflection->isProtected()) {
306 34
            $propertyNode->makeProtected();
307
        }
308
309 56
        if (! $propertyReflection->isPrivate()) {
310 56
            return;
311
        }
312
313 34
        $propertyNode->makePrivate();
314 34
    }
315
316 204
    private function addConstants(Declaration $classNode, CoreReflectionClass $classReflection) : void
317
    {
318 204
        foreach ($classReflection->getReflectionConstants() as $constantReflection) {
319 51
            if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
320 17
                continue;
321
            }
322
323 51
            $classConstantNode = new ClassConst(
324 51
                [new Const_($constantReflection->getName(), BuilderHelpers::normalizeValue($constantReflection->getValue()))],
325 51
                $this->constantVisibilityFlags($constantReflection)
326
            );
327
328 51
            if ($constantReflection->getDocComment() !== false) {
329 1
                $classConstantNode->setDocComment(new Doc($constantReflection->getDocComment()));
330
            }
331
332 51
            $classNode->addStmt($classConstantNode);
333
        }
334 204
    }
335
336 51
    private function constantVisibilityFlags(ReflectionClassConstant $constant) : int
337
    {
338 51
        if ($constant->isPrivate()) {
339 1
            return ClassNode::MODIFIER_PRIVATE;
340
        }
341
342 51
        if ($constant->isProtected()) {
343 1
            return ClassNode::MODIFIER_PROTECTED;
344
        }
345
346 51
        return ClassNode::MODIFIER_PUBLIC;
347
    }
348
349 204
    private function addMethods(Declaration $classNode, CoreReflectionClass $classReflection) : void
350
    {
351 204
        foreach ($classReflection->getMethods() as $methodReflection) {
352 189
            if (! $this->isMethodDeclaredInClass($methodReflection, $classReflection)) {
353 92
                continue;
354
            }
355
356 188
            $methodNode = $this->builderFactory->method($methodReflection->getName());
357
358 188
            $this->addMethodFlags($methodNode, $methodReflection);
359 188
            $this->addDocComment($methodNode, $methodReflection);
360 188
            $this->addParameters($methodNode, $methodReflection);
361
362 188
            $returnType = $methodReflection->getReturnType();
363 188
            assert($returnType instanceof CoreReflectionNamedType || $returnType === null);
364
365 188
            if ($methodReflection->getReturnType() !== null) {
366 2
                $methodNode->setReturnType($this->formatType($returnType));
367
            }
368
369 188
            $classNode->addStmt($methodNode);
370
        }
371 204
    }
372
373 189
    private function isMethodDeclaredInClass(CoreReflectionMethod $methodReflection, CoreReflectionClass $classReflection) : bool
374
    {
375 189
        if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
376 92
            return false;
377
        }
378
379 188
        if (array_key_exists($methodReflection->getName(), $classReflection->getTraitAliases())) {
380 1
            return false;
381
        }
382
383 188
        foreach ($classReflection->getTraits() as $trait) {
384 2
            if ($trait->hasMethod($methodReflection->getName())) {
385 1
                return false;
386
            }
387
        }
388
389 188
        return true;
390
    }
391
392 188
    private function addMethodFlags(Method $methodNode, CoreReflectionMethod $methodReflection) : void
393
    {
394 188
        if ($methodReflection->isFinal()) {
395 59
            $methodNode->makeFinal();
396
        }
397
398 188
        if ($methodReflection->isAbstract()) {
399 112
            $methodNode->makeAbstract();
400
        }
401
402 188
        if ($methodReflection->isStatic()) {
403 41
            $methodNode->makeStatic();
404
        }
405
406 188
        if ($methodReflection->isPublic()) {
407 187
            $methodNode->makePublic();
408
        }
409
410 188
        if ($methodReflection->isProtected()) {
411 4
            $methodNode->makeProtected();
412
        }
413
414 188
        if ($methodReflection->isPrivate()) {
415 58
            $methodNode->makePrivate();
416
        }
417
418 188
        if (! $methodReflection->returnsReference()) {
419 188
            return;
420
        }
421
422 1
        $methodNode->makeReturnByRef();
423 1
    }
424
425 192
    private function addParameters(FunctionLike $functionNode, CoreReflectionFunctionAbstract $functionReflectionAbstract) : void
426
    {
427 192
        foreach ($functionReflectionAbstract->getParameters() as $parameterReflection) {
428 172
            $parameterNode = $this->builderFactory->param($parameterReflection->getName());
429
430 172
            $this->addParameterModifiers($parameterReflection, $parameterNode);
431
432 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...
433 130
                $parameterNode->setDefault($this->parameterDefaultValue($parameterReflection, $functionReflectionAbstract));
434
            }
435
436 172
            $functionNode->addParam($this->addParameterModifiers($parameterReflection, $parameterNode));
437
        }
438 192
    }
439
440 172
    private function addParameterModifiers(ReflectionParameter $parameterReflection, Param $parameterNode) : Param
441
    {
442 172
        if ($parameterReflection->isVariadic()) {
443 8
            $parameterNode->makeVariadic();
444
        }
445
446 172
        if ($parameterReflection->isPassedByReference()) {
447 18
            $parameterNode->makeByRef();
448
        }
449
450 172
        $parameterType = $parameterReflection->getType();
451 172
        assert($parameterType instanceof CoreReflectionNamedType || $parameterType === null);
452
453 172
        if ($parameterReflection->getType() !== null) {
454 67
            $parameterNode->setType($this->formatType($parameterType));
455
        }
456
457 172
        return $parameterNode;
458
    }
459
460
    /**
461
     * @return mixed
462
     */
463 130
    private function parameterDefaultValue(
464
        ReflectionParameter $parameterReflection,
465
        CoreReflectionFunctionAbstract $functionReflectionAbstract
466
    ) {
467 130
        if ($functionReflectionAbstract->isInternal()) {
468 129
            return null;
469
        }
470
471 1
        return $parameterReflection->getDefaultValue();
472
    }
473
474
    /**
475
     * @return Name|FullyQualified|NullableType
476
     */
477 67
    private function formatType(CoreReflectionNamedType $type) : NodeAbstract
478
    {
479 67
        $name     = $type->getName();
480 67
        $nameNode = $type->isBuiltin() || in_array($name, ['self', 'parent'], true) ? new Name($name) : new FullyQualified($name);
481
482 67
        return $type->allowsNull() ? new NullableType($nameNode) : $nameNode;
483
    }
484
485 4
    private function generateStubInNamespace(Node $node, string $namespaceName) : string
486
    {
487 4
        $namespaceBuilder = $this->builderFactory->namespace($namespaceName);
488 4
        $namespaceBuilder->addStmt($node);
489
490 4
        return $this->generateStub($namespaceBuilder->getNode());
491
    }
492
493 209
    private function generateStub(Node $node) : string
494
    {
495 209
        return "<?php\n\n" . $this->prettyPrinter->prettyPrint([$node]) . ($node instanceof Node\Expr\FuncCall ? ';' : '') . "\n";
496
    }
497
498 209
    private function createStubData(string $stub, ?string $extensionName) : StubData
499
    {
500 209
        return new StubData($stub, $extensionName);
501
    }
502
}
503