Passed
Push — master ( b7b77b...565dd3 )
by Glynn
05:12 queued 03:02
created

StaticLoaderCreator::getNamespace()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 33
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 23
nc 9
nop 1
dl 0
loc 33
rs 8.0555
c 2
b 0
f 0
1
<?php
2
3
/**
4
 * Creates the static loader for Pixie WPDB
5
 *
6
 * Loading this file via the PHP CLI, will see the loader file created or updated.
7
 *
8
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
9
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
10
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
11
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
12
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
13
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
14
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
15
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
16
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
17
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
18
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
 *
20
 * @author Glynn Quelch <[email protected]>
21
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
22
 * @package Gin0115\Pixie WPDB
23
 * @since 0.0.1
24
 */
25
26
class StaticLoaderCreator
27
{
28
    /** @var string[] Array of file names */
29
    protected array $files = [];
30
31
    /** @var array<int, array{class:string|null, ns:string|null, interface:string|null, trait:string|null, file:string}> Classes */
32
    protected array $classes = [];
33
34
    /** @var string Path to the SRC directory. */
35
    protected $routePath;
36
37
    public function __construct()
38
    {
39
        $this->routePath = __DIR__ . DIRECTORY_SEPARATOR . 'src';
40
    }
41
42
    /**
43
     * Static initialiser
44
     *
45
     * @return void
46
     */
47
    public static function run(): void
48
    {
49
        $instance = new self();
50
        $instance->getFiles();
51
        $instance->getClassNames();
52
        $instance->writeLoader();
53
    }
54
55
    /**
56
     * Get all filenames excluding the loader.
57
     *
58
     * @return void
59
     */
60
    public function getFiles(): void
61
    {
62
        $rii = new RecursiveIteratorIterator(
63
            new RecursiveDirectoryIterator($this->routePath)
64
        );
65
66
        foreach ($rii as $file) {
67
            if ($file->isDir() || $file->getPathname() === $this->routePath . DIRECTORY_SEPARATOR . 'loader.php') {
68
                continue;
69
            }
70
71
            $this->files[] = $file->getPathname();
72
        }
73
    }
74
75
    /**
76
     * Compiles the list of classnames with file paths.
77
     *
78
     * @return void
79
     */
80
    public function getClassNames(): void
81
    {
82
        foreach ($this->files as $file) {
83
            $classData['class'] = $this->getObjectTypeFromFile($file, T_CLASS);
84
            $classData['trait'] = $this->getObjectTypeFromFile($file, T_TRAIT);
85
            $classData['interface'] = $this->getObjectTypeFromFile($file, T_INTERFACE);
86
            $classData['ns'] = $this->getNamespace($file);
87
            $classData['file'] = str_replace($this->routePath, '', $file);
88
            $this->classes[] = $classData;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $classData seems to be defined later in this foreach loop on line 83. Are you sure it is defined here?
Loading history...
89
        }
90
91
        // Set all classes to be required last, allow traits to load first.
92
        uasort($this->classes, function ($file1, $file2) {
0 ignored issues
show
Unused Code introduced by
The parameter $file2 is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

92
        uasort($this->classes, function ($file1, /** @scrutinizer ignore-unused */ $file2) {

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

Loading history...
93
            return is_null($file1['class']) ? -1 : 1;
94
        });
95
    }
96
97
    /**
98
     * Gets the namespace from a file.
99
     *
100
     * @see https://stackoverflow.com/questions/7153000/get-class-name-from-file/44654073
101
     * @param string $file
102
     * @return string|null
103
     * @throws \Exception If file not found or empty.
104
     */
105
    public function getNamespace(string $file): ?string
106
    {
107
        $src = file_get_contents($file);
108
109
        if (false === $src) {
110
            throw new \Exception("Could not read contents of {$file}", 1);
111
        }
112
113
        $tokens = token_get_all($src);
114
        $count = count($tokens);
115
        $i = 0;
116
        $namespace = '';
117
        $namespace_ok = false;
118
        while ($i < $count) {
119
            $token = $tokens[$i];
120
            if (is_array($token) && $token[0] === T_NAMESPACE) {
121
                // Found namespace declaration
122
                while (++$i < $count) {
123
                    if ($tokens[$i] === ';') {
124
                        $namespace_ok = true;
125
                        $namespace = trim($namespace);
126
                        break;
127
                    }
128
                    $namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
129
                }
130
                break;
131
            }
132
            $i++;
133
        }
134
        if (!$namespace_ok) {
135
            return null;
136
        } else {
137
            return $namespace;
138
        }
139
    }
140
141
    /**
142
     * get the class name form file path using token
143
     *
144
     * @see https://stackoverflow.com/questions/7153000/get-class-name-from-file/44654073
145
     * @param string $file
146
     * @param int $type
147
     * @return string|null
148
     */
149
    protected function getObjectTypeFromFile(string $file, int $type = T_CLASS): ?string
150
    {
151
        $src = file_get_contents($file);
152
153
        if (false === $src) {
154
            throw new \Exception("Could not read contents of {$file}", 1);
155
        }
156
157
        $classes = array();
158
        $tokens = token_get_all($src);
159
        $count = count($tokens);
160
        for ($i = 2; $i < $count; $i++) {
161
            if (
162
                $tokens[$i - 2][0] == $type
163
                && $tokens[$i - 1][0] == T_WHITESPACE
164
                && $tokens[$i][0] == T_STRING
165
            ) {
166
                $class_name = $tokens[$i][1];
167
                $classes[] = $class_name;
168
            }
169
        }
170
171
        return array_key_exists(0, $classes) ? $classes[0] : null;
172
    }
173
174
    /**
175
     * Writes the loader.php file.
176
     *
177
     * @return void
178
     */
179
    public function writeLoader(): void
180
    {
181
        $newLine = PHP_EOL;
182
        $header = "<?php{$newLine}{$newLine}{$newLine}
183
/**
184
 * Pixie WPDB Static Loader
185
 *
186
 * Just include this file in your theme or plugin to have Pixie loaded and ready to go
187
 *
188
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
189
 * \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
190
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
191
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
192
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
193
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
194
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
195
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
196
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
197
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
198
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
199
 *
200
 * @author Glynn Quelch <[email protected]>
201
 * @license http://www.opensource.org/licenses/mit-license.html  MIT License
202
 * @package Gin0115\Pixie WPDB
203
 * @since 0.0.1
204
 */
205
206
// Generated code start...";
207
208
        $contents = array_map(function ($file, $class) {
209
             return sprintf(
210
                 "if (!%s(%s::class)) {
211
    require_once __DIR__ . '%s';
212
}",
213
                 $this->getMethodFromToken($class),
214
                 $this->getFullTokenName($class),
215
                 $class['file']
216
             );
217
        }, array_keys($this->classes), $this->classes);
218
219
        $footer = sprintf("// CREATED ON %s", date('D jS F Y', time()));
220
221
        $file = __DIR__ . DIRECTORY_SEPARATOR . 'src' . DIRECTORY_SEPARATOR . 'loader.php';
222
        touch($file);
223
224
        file_put_contents(
225
            $file,
226
            join(PHP_EOL, [
227
                $header,
228
                join(PHP_EOL, $contents),
229
                $footer, ''
230
            ])
231
        );
232
    }
233
234
    /**
235
     * Gets the *_exists() method based on the token type.
236
     *
237
     * @param array{class:string|null, ns:string|null, interface:string|null, trait:string|null, file:string} $token
238
     * @return string
239
     */
240
    public function getMethodFromToken(array $token): string
241
    {
242
        switch (true) {
243
            case ! is_null($token['trait']):
244
                return 'trait_exists';
245
246
            case ! is_null($token['interface']):
247
                return 'interface_exists';
248
249
            default:
250
                return 'class_exists';
251
        }
252
    }
253
254
    /**
255
     * Returns the full (namespaced) token name
256
     *
257
     * @param array{class:string|null, ns:string|null, interface:string|null, trait:string|null, file:string} $token
258
     * @return string
259
     */
260
    public function getFullTokenName(array $token): string
261
    {
262
        switch (true) {
263
            case ! array_key_exists('ns', $token):
264
                return '';
265
266
            case ! is_null($token['trait']):
267
                return $token['ns'] . '\\' . $token['trait'];
268
269
            case ! is_null($token['interface']):
270
                return $token['ns'] . '\\' . $token['interface'];
271
272
            case ! is_null($token['class']):
273
                return $token['ns'] . '\\' . $token['class'];
274
275
            default:
276
                return '';
277
        }
278
    }
279
}
280
281
StaticLoaderCreator::run();
282