Completed
Branch develop (c2aa4c)
by Anton
05:17
created

Tokenizer::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 11
rs 9.4286
cc 1
eloc 8
nc 1
nop 3
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
namespace Spiral\Tokenizer;
9
10
use Spiral\Core\Component;
11
use Spiral\Core\Container\InjectorInterface;
12
use Spiral\Core\Container\SingletonInterface;
13
use Spiral\Core\HippocampusInterface;
14
use Spiral\Debug\Traits\BenchmarkTrait;
15
use Spiral\Debug\Traits\LoggerTrait;
16
use Spiral\Files\FilesInterface;
17
use Spiral\Tokenizer\Configs\TokenizerConfig;
18
use Spiral\Tokenizer\Reflections\ReflectionFile;
19
use Symfony\Component\Finder\Finder;
20
21
/**
22
 * Default implementation of spiral tokenizer support while and blacklisted directories and etc.
23
 */
24
class Tokenizer extends Component implements SingletonInterface, TokenizerInterface, InjectorInterface
25
{
26
    /**
27
     * Required traits.
28
     */
29
    use LoggerTrait, BenchmarkTrait;
30
31
    /**
32
     * Declares to IoC that component instance should be treated as singleton.
33
     */
34
    const SINGLETON = self::class;
35
36
    /**
37
     * Memory section.
38
     */
39
    const MEMORY = 'tokenizer';
40
41
    /**
42
     * Cache of already processed file reflections, used to speed up lookup.
43
     *
44
     * @invisible
45
     * @var array
46
     */
47
    private $cache = [];
48
49
    /**
50
     * @var TokenizerConfig
51
     */
52
    protected $config = null;
53
54
    /**
55
     * @invisible
56
     * @var FilesInterface
57
     */
58
    protected $files = null;
59
60
    /**
61
     * @invisible
62
     * @var HippocampusInterface
63
     */
64
    protected $memory = null;
65
66
    /**
67
     * Tokenizer constructor.
68
     *
69
     * @param FilesInterface       $files
70
     * @param TokenizerConfig      $config
71
     * @param HippocampusInterface $runtime
72
     */
73
    public function __construct(
74
        FilesInterface $files,
75
        TokenizerConfig $config,
76
        HippocampusInterface $runtime
77
    ) {
78
        $this->files = $files;
79
        $this->config = $config;
80
        $this->memory = $runtime;
81
82
        $this->cache = $this->memory->loadData(static::MEMORY);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->memory->loadData(static::MEMORY) can also be of type string or null. However, the property $cache is declared as type array. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
83
    }
84
85
    /**
86
     * {@inheritdoc}
87
     */
88
    public function fetchTokens($filename)
89
    {
90
        $tokens = token_get_all($this->files->read($filename));
91
92
        $line = 0;
93
        foreach ($tokens as &$token) {
94
            if (isset($token[self::LINE])) {
95
                $line = $token[self::LINE];
96
            }
97
98
            if (!is_array($token)) {
99
                $token = [$token, $token, $line];
100
            }
101
102
            unset($token);
103
        }
104
105
        return $tokens;
106
    }
107
108
    /**
109
     * {@inheritdoc}
110
     */
111
    public function fileReflection($filename)
112
    {
113
        $fileMD5 = $this->files->md5($filename = $this->files->normalizePath($filename));
114
115
        if (isset($this->cache[$filename]) && $this->cache[$filename]['md5'] == $fileMD5) {
116
            //We can speed up reflection via tokenization cache
117
            return new ReflectionFile($filename, $this, $this->cache[$filename]);
118
        }
119
120
        $reflection = new ReflectionFile($filename, $this);
121
122
        //Let's save to cache
123
        $this->cache[$filename] = ['md5' => $fileMD5] + $reflection->exportSchema();
124
        $this->memory->saveData(static::MEMORY, $this->cache);
125
126
        return $reflection;
127
    }
128
129
    /**
130
     * Get pre-configured class locator.
131
     *
132
     * @param array  $directories
133
     * @param array  $exclude
134
     * @param Finder $finder
135
     * @return ClassLocator
136
     */
137 View Code Duplication
    public function classLocator(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
138
        array $directories = [],
139
        array $exclude = [],
140
        Finder $finder = null
141
    ) {
142
        $finder = !empty($finder) ?: new Finder();
143
144
        if (empty($directories)) {
145
            $directories = $this->config->getDirectories();
146
        }
147
148
        if (empty($exclude)) {
149
            $exclude = $this->config->getExcludes();
150
        }
151
152
        //Configuring finder
153
        return new ClassLocator(
154
            $this,
155
            $finder->files()->in($directories)->exclude($exclude)->name('*.php')
156
        );
157
    }
158
159
    /**
160
     * Get pre-configured invocation locator.
161
     *
162
     * @param array  $directories
163
     * @param array  $exclude
164
     * @param Finder $finder
165
     * @return ClassLocator
166
     */
167 View Code Duplication
    public function invocationLocator(
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
168
        array $directories = [],
169
        array $exclude = [],
170
        Finder $finder = null
171
    ) {
172
        $finder = !empty($finder) ?: new Finder();
173
174
        if (empty($directories)) {
175
            $directories = $this->config->getDirectories();
176
        }
177
178
        if (empty($exclude)) {
179
            $exclude = $this->config->getExcludes();
180
        }
181
182
        //Configuring finder
183
        return new InvocationLocator(
184
            $this,
185
            $finder->files()->in($directories)->exclude($exclude)->name('*.php')
186
        );
187
    }
188
189
    /**
190
     * {@inheritdoc}
191
     */
192
    public function createInjection(\ReflectionClass $class, $context = null)
193
    {
194
        if ($class->isSubclassOf(ClassLocatorInterface::class)) {
195
            return $this->classLocator();
196
        } else {
197
            return $this->invocationLocator();
198
        }
199
    }
200
}