Completed
Push — master ( ee9170...1dcd4f )
by Kirill
03:45
created

Compiler::memomize()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 2
dl 0
loc 10
ccs 0
cts 8
cp 0
crap 12
rs 9.9332
c 0
b 0
f 0
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;
11
12
use Railt\Io\Readable;
13
use Railt\Parser\Ast\RuleInterface;
14
use Railt\Parser\Environment;
15
use Railt\Parser\Exception\UnexpectedTokenException;
16
use Railt\Parser\Exception\UnrecognizedTokenException;
17
use Railt\Reflection\Contracts\Definition;
18
use Railt\Reflection\Contracts\Dictionary;
19
use Railt\Reflection\Contracts\Document as DocumentInterface;
20
use Railt\Reflection\Contracts\Reflection as ReflectionInterface;
21
use Railt\Reflection\Dictionary\CallbackDictionary;
22
use Railt\Reflection\Document;
23
use Railt\Reflection\Reflection;
24
use Railt\SDL\Compiler\Compilable;
25
use Railt\SDL\Compiler\Pipeline;
26
use Railt\SDL\Exception\CompilerException;
27
use Railt\SDL\Exception\SyntaxException;
28
29
/**
30
 * Class Compiler
31
 */
32
class Compiler
33
{
34
    /**
35
     * @var ReflectionInterface
36
     */
37
    private $reflection;
38
39
    /**
40
     * @var Dictionary|CallbackDictionary
41
     */
42
    private $dictionary;
43
44
    /**
45
     * @var Parser
46
     */
47
    private $parser;
48
49
    /**
50
     * @var Environment
51
     */
52
    private $env;
53
54
    /**
55
     * @var CallStack
56
     */
57
    private $stack;
58
59
    /**
60
     * @var Pipeline
61
     */
62
    private $pipeline;
63
64
    /**
65
     * Compiler constructor.
66
     * @throws \Railt\Io\Exception\ExternalFileException
67
     * @throws \Railt\Reflection\Exception\TypeConflictException
68
     */
69
    public function __construct()
70
    {
71
        $this->parser     = new Parser();
72
        $this->stack      = new CallStack();
73
        $this->pipeline   = new Pipeline();
74
        $this->dictionary = new CallbackDictionary();
75
        $this->reflection = new Reflection($this->dictionary);
76
77
        $this->env = $this->parser->env();
78
    }
79
80
    /**
81
     * @param \Closure $then
82
     */
83
    public function autoload(\Closure $then): void
84
    {
85
        $this->dictionary->onTypeNotFound(function (string $type, ?Definition $from) use ($then): void {
0 ignored issues
show
Bug introduced by
The method onTypeNotFound does only exist in Railt\Reflection\Dictionary\CallbackDictionary, but not in Railt\Reflection\Contracts\Dictionary.

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...
86
            if (($file = $then($type, $from)) instanceof Readable) {
87
                $this->compile($file);
88
            }
89
        });
90
    }
91
92
    /**
93
     * @param Document $document
94
     * @param Readable $file
95
     * @return Compiler
96
     */
97
    private function load(Document $document, Readable $file): self
98
    {
99
        $this->env->share(Dictionary::class, $this->dictionary);
100
        $this->env->share(ReflectionInterface::class, $this->reflection);
101
        $this->env->share(CallStack::class, $this->stack);
102
        $this->env->share(DocumentInterface::class, $document);
103
        $this->env->share(Readable::class, $file);
104
        $this->env->share(Pipeline::class, $this->pipeline);
105
106
        return $this;
107
    }
108
109
    /**
110
     * @param Readable $file
111
     * @return DocumentInterface
112
     * @throws CompilerException
113
     */
114
    public function compile(Readable $file): DocumentInterface
115
    {
116
        $document = new Document($this->reflection, $file);
0 ignored issues
show
Compatibility introduced by
$this->reflection of type object<Railt\Reflection\Contracts\Reflection> is not a sub-type of object<Railt\Reflection\Reflection>. It seems like you assume a concrete implementation of the interface Railt\Reflection\Contracts\Reflection to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
117
118
        $this->parse($document, $file);
119
120
        foreach ($this->pipeline as $invocation) {
121
            $invocation();
122
        }
123
124
        return $document;
125
    }
126
127
    /**
128
     * @param Document $document
129
     * @param Readable $file
130
     * @return RuleInterface
131
     * @throws CompilerException
132
     */
133
    private function parse(Document $document, Readable $file): RuleInterface
134
    {
135
        try {
136
            return $this->load($document, $file)->parser->parse($file);
137
        } catch (UnexpectedTokenException | UnrecognizedTokenException $e) {
138
            $error = new SyntaxException($e->getMessage());
139
            $error->using($this->stack);
140
            $error->throwsIn($file, $e->getLine(), $e->getColumn());
141
142
            throw $error;
143
        }
144
    }
145
}
146