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 (27 issues)

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;
0 ignored issues
show
The variable $constantValue does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
The variable $extensionName does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
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
        /** @psalm-var array<string, array<string,int|string|float|bool|null|array|resource>> $constants */
154
        $constants = get_defined_constants(true);
155 2
156 2
        foreach ($constants as $constantExtensionName => $extensionConstants) {
157
            if (array_key_exists($constantName, $extensionConstants)) {
158 1
                return [
159 1
                    $extensionConstants[$constantName],
160
                    $constantExtensionName !== 'user' ? $constantExtensionName : null,
161
                ];
162
            }
163
        }
164 1
165
        return null;
166
    }
167
168
    /**
169
     * @return Class_|Interface_|Trait_
170 204
     */
171
    private function createClass(CoreReflectionClass $classReflection) : Declaration
172 204
    {
173 2
        if ($classReflection->isTrait()) {
174
            return $this->builderFactory->trait($classReflection->getShortName());
175
        }
176 202
177 120
        if ($classReflection->isInterface()) {
178
            return $this->builderFactory->interface($classReflection->getShortName());
179
        }
180 182
181
        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 208
     */
188
    private function addDocComment(Builder $node, CoreReflector $reflection) : void
189 208
    {
190 207
        if ($reflection->getDocComment() === false) {
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionClass, ReflectionClassConstant, ReflectionFunction, ReflectionFunction, ReflectionFunctionAbstract, ReflectionFunctionAbstract, ReflectionMethod, ReflectionMethod, ReflectionObject, ReflectionObject, ReflectionProperty, ReflectionProperty, Roave\BetterReflection\R...Adapter\ReflectionClass, Roave\BetterReflection\R...ReflectionClassConstant, Roave\BetterReflection\R...pter\ReflectionFunction, Roave\BetterReflection\R...dapter\ReflectionMethod, Roave\BetterReflection\R...dapter\ReflectionObject, Roave\BetterReflection\R...pter\ReflectionProperty.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
191
            return;
192
        }
193 2
194 2
        $node->setDocComment(new Doc($reflection->getDocComment()));
0 ignored issues
show
It seems like you code against a concrete implementation and not the interface Reflector as the method getDocComment() does only exist in the following implementations of said interface: ReflectionClass, ReflectionClass, ReflectionClassConstant, ReflectionFunction, ReflectionFunction, ReflectionFunctionAbstract, ReflectionFunctionAbstract, ReflectionMethod, ReflectionMethod, ReflectionObject, ReflectionObject, ReflectionProperty, ReflectionProperty, Roave\BetterReflection\R...Adapter\ReflectionClass, Roave\BetterReflection\R...ReflectionClassConstant, Roave\BetterReflection\R...pter\ReflectionFunction, Roave\BetterReflection\R...dapter\ReflectionMethod, Roave\BetterReflection\R...dapter\ReflectionObject, Roave\BetterReflection\R...pter\ReflectionProperty.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
It seems like you code against a concrete implementation and not the interface PhpParser\Builder as the method setDocComment() does only exist in the following implementations of said interface: PhpParser\Builder\Class_, PhpParser\Builder\Declaration, PhpParser\Builder\FunctionLike, PhpParser\Builder\Function_, PhpParser\Builder\Interface_, PhpParser\Builder\Method, PhpParser\Builder\Namespace_, PhpParser\Builder\Property, PhpParser\Builder\Trait_.

Let’s take a look at an example:

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

class MyUser implements 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 implementation 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 interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
195
    }
196 182
197
    private function addClassModifiers(Class_ $classNode, CoreReflectionClass $classReflection) : void
198 182
    {
199
        if (! $classReflection->isInterface() && $classReflection->isAbstract()) {
200 14
            // Interface \Iterator is interface and abstract
201
            $classNode->makeAbstract();
202
        }
203 182
204 174
        if (! $classReflection->isFinal()) {
205
            return;
206
        }
207 10
208 10
        $classNode->makeFinal();
209
    }
210
211
    /**
212
     * @param Class_|Interface_ $classNode
213 202
     */
214
    private function addExtendsAndImplements(Declaration $classNode, CoreReflectionClass $classReflection) : void
215 202
    {
216 202
        $parentClass = $classReflection->getParentClass();
217
        $interfaces  = $classReflection->getInterfaceNames();
218 202
219 84
        if ($parentClass) {
220 84
            $classNode->extend(new FullyQualified($parentClass->getName()));
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class PhpParser\Builder\Declaration as the method extend() does only exist in the following sub-classes of PhpParser\Builder\Declaration: PhpParser\Builder\Class_, PhpParser\Builder\Interface_. 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...
221
            $interfaces = array_diff($interfaces, $parentClass->getInterfaceNames());
222
        }
223 202
224 106
        foreach ($classReflection->getInterfaces() as $interface) {
225
            $interfaces = array_diff($interfaces, $interface->getInterfaceNames());
226
        }
227 202
228 106
        foreach ($interfaces as $interfaceName) {
229 48
            if ($classNode instanceof Interface_) {
230
                $classNode->extend(new FullyQualified($interfaceName));
231 100
            } else {
232
                $classNode->implement(new FullyQualified($interfaceName));
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class PhpParser\Builder\Declaration as the method implement() does only exist in the following sub-classes of PhpParser\Builder\Declaration: PhpParser\Builder\Class_. 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...
233
            }
234 202
        }
235
    }
236 184
237
    private function addTraitUse(Declaration $classNode, CoreReflectionClass $classReflection) : void
238 184
    {
239
        $alreadyUsedTraitNames = [];
240 184
241 1
        foreach ($classReflection->getTraitAliases() as $methodNameAlias => $methodInfo) {
242 1
            [$traitName, $methodName] = explode('::', $methodInfo);
0 ignored issues
show
The variable $traitName seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
The variable $methodName does not exist. Did you mean $methodNameAlias?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
243 1
            $traitUseNode             = new TraitUse(
244 1
                [new FullyQualified($traitName)],
0 ignored issues
show
The variable $traitName seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
245
                [new TraitUseAdaptation\Alias(new FullyQualified($traitName), $methodName, null, $methodNameAlias)]
0 ignored issues
show
The variable $traitName seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
The variable $methodName does not exist. Did you mean $methodNameAlias?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
246
            );
247 1
248
            $classNode->addStmt($traitUseNode);
249 1
250
            $alreadyUsedTraitNames[] = $traitName;
0 ignored issues
show
The variable $traitName seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
251
        }
252 184
253 2
        foreach (array_diff($classReflection->getTraitNames(), $alreadyUsedTraitNames) as $traitName) {
254
            $classNode->addStmt(new TraitUse([new FullyQualified($traitName)]));
255 184
        }
256
    }
257 184
258
    private function addProperties(Declaration $classNode, CoreReflectionClass $classReflection) : void
259 184
    {
260
        $defaultProperties = $classReflection->getDefaultProperties();
261 184
262 56
        foreach ($classReflection->getProperties() as $propertyReflection) {
263 34
            if (! $this->isPropertyDeclaredInClass($propertyReflection, $classReflection)) {
264
                continue;
265
            }
266 56
267
            $propertyNode = $this->builderFactory->property($propertyReflection->getName());
268 56
269 56
            $this->addPropertyModifiers($propertyNode, $propertyReflection);
270
            $this->addDocComment($propertyNode, $propertyReflection);
271 56
272 56
            if (array_key_exists($propertyReflection->getName(), $defaultProperties)) {
273
                $propertyNode->setDefault($defaultProperties[$propertyReflection->getName()]);
274
            }
275 56
276
            $classNode->addStmt($propertyNode);
277 184
        }
278
    }
279 56
280
    private function isPropertyDeclaredInClass(CoreReflectionProperty $propertyReflection, CoreReflectionClass $classReflection) : bool
281 56
    {
282 33
        if ($propertyReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
0 ignored issues
show
Consider using $classReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
283
            return false;
284
        }
285 56
286 1
        foreach ($classReflection->getTraits() as $trait) {
287 1
            if ($trait->hasProperty($propertyReflection->getName())) {
288
                return false;
289
            }
290
        }
291 56
292
        return true;
293
    }
294 56
295
    private function addPropertyModifiers(Property $propertyNode, CoreReflectionProperty $propertyReflection) : void
296 56
    {
297 1
        if ($propertyReflection->isStatic()) {
298
            $propertyNode->makeStatic();
299
        }
300 56
301 25
        if ($propertyReflection->isPublic()) {
302
            $propertyNode->makePublic();
303
        }
304 56
305 34
        if ($propertyReflection->isProtected()) {
306
            $propertyNode->makeProtected();
307
        }
308 56
309 56
        if (! $propertyReflection->isPrivate()) {
310
            return;
311
        }
312 34
313 34
        $propertyNode->makePrivate();
314
    }
315 204
316
    private function addConstants(Declaration $classNode, CoreReflectionClass $classReflection) : void
317 204
    {
318 51
        foreach ($classReflection->getReflectionConstants() as $constantReflection) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionClass as the method getReflectionConstants() does only exist in the following sub-classes of ReflectionClass: ReflectionObject, Roave\BetterReflection\R...Adapter\ReflectionClass. 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...
319 17
            if ($constantReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
0 ignored issues
show
Consider using $classReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
320
                continue;
321
            }
322 51
323 51
            $classConstantNode = new ClassConst(
324 51
                [new Const_($constantReflection->getName(), BuilderHelpers::normalizeValue($constantReflection->getValue()))],
325
                $this->constantVisibilityFlags($constantReflection)
326
            );
327 51
328 1
            if ($constantReflection->getDocComment() !== false) {
329
                $classConstantNode->setDocComment(new Doc($constantReflection->getDocComment()));
330
            }
331 51
332
            $classNode->addStmt($classConstantNode);
333 204
        }
334
    }
335 51
336
    private function constantVisibilityFlags(ReflectionClassConstant $constant) : int
337 51
    {
338 1
        if ($constant->isPrivate()) {
339
            return ClassNode::MODIFIER_PRIVATE;
340
        }
341 51
342 1
        if ($constant->isProtected()) {
343
            return ClassNode::MODIFIER_PROTECTED;
344
        }
345 51
346
        return ClassNode::MODIFIER_PUBLIC;
347
    }
348 204
349
    private function addMethods(Declaration $classNode, CoreReflectionClass $classReflection) : void
350 204
    {
351 189
        foreach ($classReflection->getMethods() as $methodReflection) {
352 92
            if (! $this->isMethodDeclaredInClass($methodReflection, $classReflection)) {
353
                continue;
354
            }
355 188
356
            $methodNode = $this->builderFactory->method($methodReflection->getName());
0 ignored issues
show
Consider using $methodReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
357 188
358 188
            $this->addMethodFlags($methodNode, $methodReflection);
359 188
            $this->addDocComment($methodNode, $methodReflection);
360
            $this->addParameters($methodNode, $methodReflection);
361 188
362 188
            $returnType = $methodReflection->getReturnType();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionMethod as the method getReturnType() does only exist in the following sub-classes of ReflectionMethod: Roave\BetterReflection\R...dapter\ReflectionMethod. 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...
363
            assert($returnType instanceof CoreReflectionNamedType || $returnType === null);
364 188
365 2
            if ($methodReflection->getReturnType() !== null) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionMethod as the method getReturnType() does only exist in the following sub-classes of ReflectionMethod: Roave\BetterReflection\R...dapter\ReflectionMethod. 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...
366
                $methodNode->setReturnType($this->formatType($returnType));
367
            }
368 188
369
            $classNode->addStmt($methodNode);
370 204
        }
371
    }
372 189
373
    private function isMethodDeclaredInClass(CoreReflectionMethod $methodReflection, CoreReflectionClass $classReflection) : bool
374 189
    {
375 92
        if ($methodReflection->getDeclaringClass()->getName() !== $classReflection->getName()) {
0 ignored issues
show
Consider using $classReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
376
            return false;
377
        }
378 188
379 1
        if (array_key_exists($methodReflection->getName(), $classReflection->getTraitAliases())) {
0 ignored issues
show
Consider using $methodReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
380
            return false;
381
        }
382 188
383 2
        foreach ($classReflection->getTraits() as $trait) {
384 1
            if ($trait->hasMethod($methodReflection->getName())) {
0 ignored issues
show
Consider using $methodReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
385
                return false;
386
            }
387
        }
388 188
389
        return true;
390
    }
391 188
392
    private function addMethodFlags(Method $methodNode, CoreReflectionMethod $methodReflection) : void
393 188
    {
394 59
        if ($methodReflection->isFinal()) {
395
            $methodNode->makeFinal();
396
        }
397 188
398 112
        if ($methodReflection->isAbstract()) {
399
            $methodNode->makeAbstract();
400
        }
401 188
402 41
        if ($methodReflection->isStatic()) {
403
            $methodNode->makeStatic();
404
        }
405 188
406 187
        if ($methodReflection->isPublic()) {
407
            $methodNode->makePublic();
408
        }
409 188
410 4
        if ($methodReflection->isProtected()) {
411
            $methodNode->makeProtected();
412
        }
413 188
414 58
        if ($methodReflection->isPrivate()) {
415
            $methodNode->makePrivate();
416
        }
417 188
418 188
        if (! $methodReflection->returnsReference()) {
419
            return;
420
        }
421 1
422 1
        $methodNode->makeReturnByRef();
423
    }
424 192
425
    private function addParameters(FunctionLike $functionNode, CoreReflectionFunctionAbstract $functionReflectionAbstract) : void
426 192
    {
427 172
        foreach ($functionReflectionAbstract->getParameters() as $parameterReflection) {
428
            $parameterNode = $this->builderFactory->param($parameterReflection->getName());
0 ignored issues
show
Consider using $parameterReflection->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
429 172
430
            $this->addParameterModifiers($parameterReflection, $parameterNode);
431 172
432 130
            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
                $parameterNode->setDefault($this->parameterDefaultValue($parameterReflection, $functionReflectionAbstract));
434
            }
435 172
436
            $functionNode->addParam($this->addParameterModifiers($parameterReflection, $parameterNode));
437 192
        }
438
    }
439 172
440
    private function addParameterModifiers(ReflectionParameter $parameterReflection, Param $parameterNode) : Param
441 172
    {
442 8
        if ($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...
443
            $parameterNode->makeVariadic();
444
        }
445 172
446 18
        if ($parameterReflection->isPassedByReference()) {
447
            $parameterNode->makeByRef();
448
        }
449 172
450 172
        $parameterType = $parameterReflection->getType();
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method getType() 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...
451
        assert($parameterType instanceof CoreReflectionNamedType || $parameterType === null);
452 172
453 67
        if ($parameterReflection->getType() !== null) {
0 ignored issues
show
It seems like you code against a specific sub-type and not the parent class ReflectionParameter as the method getType() 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...
454
            $parameterNode->setType($this->formatType($parameterType));
455
        }
456 172
457
        return $parameterNode;
458
    }
459
460
    /**
461
     * @return mixed
462 130
     */
463
    private function parameterDefaultValue(
464
        ReflectionParameter $parameterReflection,
465
        CoreReflectionFunctionAbstract $functionReflectionAbstract
466 130
    ) {
467 129
        if ($functionReflectionAbstract->isInternal()) {
468
            return null;
469
        }
470 1
471
        return $parameterReflection->getDefaultValue();
472
    }
473
474
    /**
475
     * @return Name|FullyQualified|NullableType
476 67
     */
477
    private function formatType(CoreReflectionNamedType $type) : NodeAbstract
478 67
    {
479 67
        $name     = $type->getName();
480
        $nameNode = $type->isBuiltin() || in_array($name, ['self', 'parent'], true) ? new Name($name) : new FullyQualified($name);
481 67
482
        return $type->allowsNull() ? new NullableType($nameNode) : $nameNode;
483
    }
484 4
485
    private function generateStubInNamespace(Node $node, string $namespaceName) : string
486 4
    {
487 4
        $namespaceBuilder = $this->builderFactory->namespace($namespaceName);
488
        $namespaceBuilder->addStmt($node);
489 4
490
        return $this->generateStub($namespaceBuilder->getNode());
491
    }
492 209
493
    private function generateStub(Node $node) : string
494 209
    {
495
        return "<?php\n\n" . $this->prettyPrinter->prettyPrint([$node]) . ($node instanceof Node\Expr\FuncCall ? ';' : '') . "\n";
496
    }
497 209
498
    private function createStubData(string $stub, ?string $extensionName) : StubData
499 209
    {
500
        return new StubData($stub, $extensionName);
501
    }
502
}
503