Completed
Push — master ( 296743...12eab9 )
by Kirill
36:17
created

Compiler::boot()   B

Complexity

Conditions 7
Paths 18

Size

Total Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 7

Importance

Changes 0
Metric Value
dl 0
loc 52
ccs 20
cts 20
cp 1
rs 8.1138
c 0
b 0
f 0
cc 7
nc 18
nop 2
crap 7

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of Railt package.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
declare(strict_types=1);
9
10
namespace Railt\SDL\Reflection\Builder\Process;
11
12
use Railt\Parser\Ast\NodeInterface;
13
use Railt\Parser\Ast\RuleInterface;
14
use Railt\SDL\Contracts\Definitions\Definition;
15
use Railt\SDL\Contracts\Definitions\TypeDefinition;
16
use Railt\SDL\Contracts\Dependent\DependentDefinition;
17
use Railt\SDL\Contracts\Document;
18
use Railt\SDL\Contracts\Invocations\Invocable;
19
use Railt\SDL\Reflection\Builder\DocumentBuilder;
20
use Railt\SDL\Reflection\Validation\Base\ValidatorInterface;
21
use Railt\SDL\Reflection\Validation\Definitions;
22
use Railt\SDL\Reflection\Validation\Uniqueness;
23
use Railt\SDL\Schema\CompilerInterface;
24
use Railt\SDL\Schema\Configuration;
25
26
/**
27
 * Trait Compiler
28
 */
29
trait Compiler
30
{
31
    /**
32
     * @var int
33
     */
34
    protected $offset = 0;
35
36
    /**
37
     * @var NodeInterface
38
     */
39
    private $ast;
40
41
    /**
42
     * @var array|string[]
43
     */
44
    private $siblingActions = [];
45
46
    /**
47
     * @var bool
48
     */
49
    private $completed = false;
50
51
    /**
52
     * @return void
53
     */
54 6576
    public function compile(): void
55
    {
56 6576
        if ($this->completed === false) {
57 6576
            $this->completed = true;
58
59 6576
            foreach ($this->getAst()->getChildren() as $child) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\Parser\Ast\NodeInterface as the method getChildren() does only exist in the following implementations of said interface: Railt\Compiler\Grammar\Delegate\IncludeDelegate, Railt\Compiler\Grammar\Delegate\RuleDelegate, Railt\Compiler\Grammar\Delegate\TokenDelegate, Railt\Parser\Ast\Rule.

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...
60 6576
                if ($this->compileSiblings($child)) {
61 6479
                    continue;
62
                }
63
64 6576
                if ($this->onCompile($child)) {
65 6576
                    continue;
66
                }
67
            }
68
        }
69 6576
    }
70
71
    /**
72
     * @return NodeInterface|RuleInterface
73
     */
74 6576
    public function getAst(): NodeInterface
75
    {
76 6576
        return $this->ast;
77
    }
78
79
    /**
80
     * @param NodeInterface $child
81
     * @return bool
82
     */
83 6576
    protected function compileSiblings(NodeInterface $child): bool
84
    {
85 6576
        foreach ($this->siblingActions as $action) {
86 6576
            if ($this->$action($child)) {
87 6576
                return true;
88
            }
89
        }
90
91 6576
        return false;
92
    }
93
94
    /**
95
     * @return Document|DocumentBuilder
96
     */
97 6582
    public function getDocument(): Document
98
    {
99 6582
        return $this->document;
0 ignored issues
show
Bug introduced by
The property document does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
100
    }
101
102
    /**
103
     * @return CompilerInterface|Configuration
104
     */
105 6569
    public function getCompiler(): CompilerInterface
106
    {
107 6569
        return $this->getDocument()->getCompiler();
0 ignored issues
show
Bug introduced by
The method getCompiler does only exist in Railt\SDL\Reflection\Builder\DocumentBuilder, but not in Railt\SDL\Contracts\Document.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
108
    }
109
110
    /**
111
     * @param string $group
112
     * @return ValidatorInterface
113
     * @throws \OutOfBoundsException
114
     */
115 6576
    public function getValidator(string $group = null): ValidatorInterface
116
    {
117 6576
        return $this->getCompiler()->getValidator($group);
0 ignored issues
show
Bug introduced by
The method getValidator does only exist in Railt\SDL\Schema\Configuration, but not in Railt\SDL\Schema\CompilerInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
118
    }
119
120
    /**
121
     * @return array
122
     */
123 1181
    public function __sleep(): array
124
    {
125 1181
        $result = ['offset'];
126
127 1181
        if (\method_exists(parent::class, '__sleep')) {
128 1181
            return \array_merge(parent::__sleep(), $result);
129
        }
130
131
        return $result;
132
    }
133
134
    /**
135
     * @return void
136
     */
137 655
    public function __wakeup(): void
138
    {
139 655
        $this->completed = true;
140 655
    }
141
142
    /**
143
     * @return int
144
     */
145 3291
    public function getDeclarationLine(): int
146
    {
147 3291
        return $this->getDocument()->getFile()->getPosition($this->offset)->getLine();
148
    }
149
150
    /**
151
     * @return int
152
     */
153 3291
    public function getDeclarationColumn(): int
154
    {
155 3291
        return $this->getDocument()->getFile()->getPosition($this->offset)->getColumn();
156
    }
157
158
    /**
159
     * @param NodeInterface $ast
160
     * @param Document $document
161
     * @return void
162
     */
163 6576
    protected function boot(NodeInterface $ast, Document $document): void
164
    {
165 6576
        $this->ast      = $ast;
166 6576
        $this->document = $document;
167
168
        // Generate identifier if id does not initialized
169 6576
        $this->getUniqueId();
0 ignored issues
show
Bug introduced by
The method getUniqueId() does not exist on Railt\SDL\Reflection\Builder\Process\Compiler. Did you maybe mean unique()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
170
171
        // Collect sibling methods
172 6576
        foreach (\class_uses_recursive(static::class) as $sibling) {
173 6576
            $method = 'compile' . \class_basename($sibling);
174
175 6576
            if (\method_exists($sibling, $method)) {
176 6576
                $this->siblingActions[] = $method;
177
            }
178
        }
179
180
        /**
181
         * Initialize the name of the type, if it is an independent
182
         * unique definition of the type of GraphQL.
183
         */
184 6576
        if ($this instanceof Definition) {
185 6576
            $this->resolveTypeName();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Definitions\Definition as the method resolveTypeName() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...itions\DirectiveBuilder, Railt\SDL\Reflection\Bui...Definitions\EnumBuilder, Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...efinitions\InputBuilder, Railt\SDL\Reflection\Bui...itions\InterfaceBuilder, Railt\SDL\Reflection\Bui...finitions\ObjectBuilder, Railt\SDL\Reflection\Bui...finitions\ScalarBuilder, Railt\SDL\Reflection\Bui...finitions\SchemaBuilder, Railt\SDL\Reflection\Bui...efinitions\UnionBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Builder\DocumentBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder, Railt\SDL\Reflection\Bui...ocessable\ExtendBuilder.

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...
186
        }
187
188 6576
        if ($this instanceof Compilable && $this instanceof Invocable) {
189 1686
            $this->getDocument()->future($this);
190
191 1686
            return;
192
        }
193
194
        /**
195
         * If the type is not initialized by the Document, but
196
         * is a child of the root, then the lazy assembly is not needed.
197
         *
198
         * In this case we run it forcibly, and then we check its state.
199
         */
200 6576
        if ($this instanceof DependentDefinition) {
201 5975
            $this->getCompiler()->getCallStack()->push($this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Dependent\DependentDefinition as the method getCompiler() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder.

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...
202
203
            // Force compile dependent definition
204 5975
            $this->compile();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Dependent\DependentDefinition as the method compile() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder.

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...
205
206
            // Normalize types
207 5975
            $this->getCompiler()->getTypeCoercion()->apply($this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Dependent\DependentDefinition as the method getCompiler() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder.

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...
208
209
            // Verify type
210 5975
            $this->getValidator(Definitions::class)->validate($this);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Dependent\DependentDefinition as the method getValidator() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder.

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...
211
212 5972
            $this->getCompiler()->getCallStack()->pop();
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Contracts\Dependent\DependentDefinition as the method getCompiler() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Bui...tions\Enum\ValueBuilder, Railt\SDL\Reflection\Bui...pendent\ArgumentBuilder, Railt\SDL\Reflection\Bui...\Dependent\FieldBuilder, Railt\SDL\Reflection\Bui...ectiveInvocationBuilder, Railt\SDL\Reflection\Bui...\InputInvocationBuilder.

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...
213
        }
214 6576
    }
215
216
    /**
217
     * @param string $name
218
     * @param string $desc
219
     * @return void
220
     */
221 6576
    private function resolveTypeName(string $name = 'Name', string $desc = 'Description'): void
222
    {
223 6576
        foreach ($this->getAst()->getChildren() as $child) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\Parser\Ast\NodeInterface as the method getChildren() does only exist in the following implementations of said interface: Railt\Compiler\Grammar\Delegate\IncludeDelegate, Railt\Compiler\Grammar\Delegate\RuleDelegate, Railt\Compiler\Grammar\Delegate\TokenDelegate, Railt\Parser\Ast\Rule.

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...
224 6576
            switch ($child->getName()) {
225 6576
                case $name:
226 6558
                    $node                        = $child->getChild(0);
227 6558
                    [$this->name, $this->offset] = [$node->getValue(), $node->getOffset()];
0 ignored issues
show
Bug introduced by
The property name does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
228 6558
                    break;
229
230 6576
                case $desc:
231
                    $this->description = $this->parseDescription($child);
0 ignored issues
show
Bug introduced by
The property description does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
232 6576
                    break;
233
            }
234
        }
235 6576
    }
236
237
    /**
238
     * @param NodeInterface|RuleInterface $ast
239
     * @return string
240
     */
241
    private function parseDescription(NodeInterface $ast): string
242
    {
243
        $description = \trim($this->parseValue(
244
            $ast->getChild(0),
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\Parser\Ast\NodeInterface as the method getChild() does only exist in the following implementations of said interface: Railt\Compiler\Grammar\Delegate\IncludeDelegate, Railt\Compiler\Grammar\Delegate\RuleDelegate, Railt\Compiler\Grammar\Delegate\TokenDelegate, Railt\Parser\Ast\Rule.

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...
245
            'String'
246
        ));
247
248
        return $description
249
            ? \preg_replace('/^\h*#?\h+(.*?)\h*$/imsu', '$1', $description)
250
            : $description;
251
    }
252
253
    /**
254
     * @param NodeInterface $ast
255
     * @param string $type
256
     * @param array $path
257
     * @return array|float|int|null|string
258
     */
259 5326
    protected function parseValue(NodeInterface $ast, string $type, array $path = [])
260
    {
261 5326
        return $this->getDocument()->getValueBuilder()->parse($ast, $type, $path);
0 ignored issues
show
Bug introduced by
The method getValueBuilder does only exist in Railt\SDL\Reflection\Builder\DocumentBuilder, but not in Railt\SDL\Contracts\Document.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
262
    }
263
264
    /**
265
     * @param NodeInterface $ast
266
     * @return bool
267
     */
268 6167
    protected function onCompile(NodeInterface $ast): bool
0 ignored issues
show
Unused Code introduced by
The parameter $ast is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
269
    {
270 6167
        return false;
271
    }
272
273
    /**
274
     * @param string $type
275
     * @return TypeDefinition
276
     */
277 6563
    protected function load(string $type): TypeDefinition
278
    {
279 6563
        return $this->getCompiler()->getDictionary()->get($type, $this);
0 ignored issues
show
Bug introduced by
The method getDictionary does only exist in Railt\SDL\Schema\Configuration, but not in Railt\SDL\Schema\CompilerInterface.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
280
    }
281
282
    /**
283
     * @param array|TypeDefinition|null $field
284
     * @param TypeDefinition $definition
285
     * @return TypeDefinition|array
286
     */
287 6576
    protected function unique($field, TypeDefinition $definition)
288
    {
289 6576
        $this->getValidator(Uniqueness::class)->validate($field, $definition);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Railt\SDL\Reflection\Val...Base\ValidatorInterface as the method validate() does only exist in the following implementations of said interface: Railt\SDL\Reflection\Validation\Base\Factory, Railt\SDL\Reflection\Val...\Base\ValidatorsFactory, Railt\SDL\Reflection\Validation\Definitions, Railt\SDL\Reflection\Val...tions\ArgumentValidator, Railt\SDL\Reflection\Val...BaseDefinitionValidator, Railt\SDL\Reflection\Val...tiveDefinitionValidator, Railt\SDL\Reflection\Val...tiveInvocationValidator, Railt\SDL\Reflection\Val...finitions\EnumValidator, Railt\SDL\Reflection\Val...nitions\ObjectValidator, Railt\SDL\Reflection\Val...assedArgumentsValidator, Railt\SDL\Reflection\Val...nitions\SchemaValidator, Railt\SDL\Reflection\Validation\Inheritance, Railt\SDL\Reflection\Val...aseInheritanceValidator, Railt\SDL\Reflection\Val...heritance\EnumValidator, Railt\SDL\Reflection\Val...eritance\InputValidator, Railt\SDL\Reflection\Val...ance\InterfaceValidator, Railt\SDL\Reflection\Val...ritance\ObjectValidator, Railt\SDL\Reflection\Val...ritance\ScalarValidator, Railt\SDL\Reflection\Val...eritance\UnionValidator, Railt\SDL\Reflection\Val...itance\WrapperValidator, Railt\SDL\Reflection\Validation\Uniqueness, Railt\SDL\Reflection\Val...ar\UniqueValueValidator, Railt\SDL\Reflection\Val...iqueCollectionValidator, Railt\SDL\Reflection\Val...iqueDefinitionValidator.

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...
290
291 6576
        if (\is_array($field)) {
292 6576
            $field[$definition->getName()] = $definition;
293
294 6576
            return $field;
295
        }
296
297 144
        return $definition;
298
    }
299
300
    /**
301
     * @param string $keyword
302
     * @return int
303
     */
304 6558
    private function offsetPrefixedBy(string $keyword): int
305
    {
306 6558
        return $this->offset - \strlen($keyword) - 1;
307
    }
308
}
309