Passed
Push — main ( d71b68...a743f7 )
by Dimitri
08:12 queued 04:09
created

FileLocator   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 200
Duplicated Lines 0 %

Test Coverage

Coverage 32%

Importance

Changes 0
Metric Value
eloc 81
c 0
b 0
f 0
dl 0
loc 200
ccs 16
cts 50
cp 0.32
rs 9.68
wmc 34

5 Methods

Rating   Name   Duplication   Size   Complexity  
A verifyPreferApp() 0 8 2
C schema() 0 55 13
C helper() 0 69 12
A getBasename() 0 8 2
A model() 0 16 5
1
<?php
2
3
/**
4
 * This file is part of Blitz PHP framework.
5
 *
6
 * (c) 2022 Dimitri Sitchet Tomkeu <[email protected]>
7
 *
8
 * For the full copyright and license information, please view
9
 * the LICENSE file that was distributed with this source code.
10
 */
11
12
namespace BlitzPHP\Loader;
13
14
use BlitzPHP\Container\Services;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, BlitzPHP\Loader\Services. Consider defining an alias.

Let?s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let?s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
15
use BlitzPHP\Contracts\Database\ConnectionInterface;
16
use BlitzPHP\Exceptions\LoadException;
17
use BlitzPHP\Utilities\String\Text;
18
use Nette\Schema\Expect;
19
use Nette\Schema\Schema;
20
21
class FileLocator
22
{
23
    /**
24
     * Charge un fichier d'aide en mémoire.
25
     * Prend en charge les helpers d'espace de noms, à la fois dans et hors du répertoire 'helpers' d'un répertoire d'espace de noms.
26
     *
27
     * Chargera TOUS les helpers du nom correspondant, dans l'ordre suivant :
28
     *   1. app/Helpers
29
     *   2. {namespace}/Helpers
30
     *   3. system/Helpers
31
     *
32
     * @throws LoadException
33
     */
34
    public static function helper(array|string $filenames)
35
    {
36 34
        static $loaded = [];
37
38 34
        $loader = Services::locator();
39
40
        if (! is_array($filenames)) {
0 ignored issues
show
introduced by
The condition is_array($filenames) is always true.
Loading history...
41 34
            $filenames = [$filenames];
42
        }
43
44
        // Enregistrez une liste de tous les fichiers à inclure...
45 34
        $includes = [];
46
47
        foreach ($filenames as $filename) {
48
            // Stockez nos versions d'helpers système et d'application afin que nous puissions contrôler l'ordre de chargement.
49 34
            $systemHelper  = null;
50 34
            $appHelper     = null;
51 34
            $localIncludes = [];
52
53
            // Vérifiez si ce helper a déjà été chargé
54
            if (in_array($filename, $loaded, true)) {
55 32
                continue;
56
            }
57
58
            // Si le fichier est dans un espace de noms, nous allons simplement saisir ce fichier et ne pas en rechercher d'autres
59
            if (str_contains($filename, '\\')) {
60 6
                $path = $loader->locateFile($filename, 'Helpers');
61
62
                if (empty($path)) {
63
                    throw LoadException::helperNotFound($filename);
64
                }
65
66
                $includes[] = $path;
67
                $loaded[]   = $filename;
68
            } else {
69
                // Pas d'espaces de noms, donc recherchez dans tous les emplacements disponibles
70 6
                $paths = $loader->search('Helpers/' . $filename);
71
72
                foreach ($paths as $path) {
73
                    if (str_starts_with($path, APP_PATH . 'Helpers' . DS)) {
74
                        $appHelper = $path;
75
                    } elseif (str_starts_with($path, SYST_PATH . 'Helpers' . DS)) {
76 6
                        $systemHelper = $path;
77
                    } else {
78
                        $localIncludes[] = $path;
79
                        $loaded[]        = $filename;
80
                    }
81
                }
82
83
                // Les helpers au niveau de l'application doivent remplacer tous les autres
84
                if (! empty($appHelper)) {
85 6
                    $includes[] = $appHelper;
86
                    $loaded[]   = $filename;
87
                }
88
89
                // Tous les fichiers avec espace de noms sont ajoutés ensuite
90 6
                $includes = [...$includes, ...$localIncludes];
91
92
                // Et celui par défaut du système doit être ajouté en dernier.
93
                if (! empty($systemHelper)) {
94 6
                    $includes[] = $systemHelper;
95 6
                    $loaded[]   = $filename;
96
                }
97
            }
98
        }
99
100
        // Incluez maintenant tous les fichiers
101
        foreach ($includes as $path) {
102 6
            include_once $path;
103
        }
104
    }
105
106
    /**
107
     * Charge un fichier d'aide en mémoire.
108
     * Prend en charge les helpers d'espace de noms, à la fois dans et hors du répertoire 'helpers' d'un répertoire d'espace de noms.
109
     */
110
    public static function schema(string $name): Schema
111
    {
112
        static $loadedSchema = [];
113
114
        $loader = Services::locator();
115
116
        // Stockez nos versions de schame système et d'application afin que nous puissions contrôler l'ordre de chargement.
117
        $systemSchema = null;
118
        $appSchema    = null;
119
        $vendorSchema = null;
120
121
        // Le fichier de schema qui sera finalement utiliser
122
        $file = null;
123
124
        // Vérifiez si ce schama a déjà été chargé
125
        if (in_array($name, $loadedSchema, true)) {
126
            return $loadedSchema[$name];
127
        }
128
129
        // Si le fichier est dans un espace de noms, nous allons simplement saisir ce fichier et ne pas en rechercher d'autres
130
        if (str_contains($name, '\\')) {
131
            if (! empty($path = $loader->locateFile($name, 'schemas'))) {
132
                $file = $path;
133
            }
134
        } else {
135
            // Pas d'espaces de noms, donc recherchez dans tous les emplacements disponibles
136
            $paths = $loader->search('schemas/' . $name);
137
138
            foreach ($paths as $path) {
139
                if (str_starts_with($path, CONFIG_PATH . 'schemas' . DS)) {
140
                    $appSchema = $path;
141
                } elseif (str_starts_with($path, SYST_PATH . 'Constants' . DS . 'schemas' . DS)) {
142
                    $systemSchema = $path;
143
                } else {
144
                    $vendorSchema = $path;
145
                }
146
            }
147
148
            // Les schema des vendor sont prioritaire, ensuite vienne ceux de l'application
149
            if (! empty($vendorSchema)) {
0 ignored issues
show
introduced by
The condition empty($vendorSchema) is always false.
Loading history...
150
                $file = $vendorSchema;
151
            } elseif (! empty($appSchema)) {
152
                $file = $appSchema;
153
            } elseif (! empty($systemSchema)) {
154
                $file = $systemSchema;
155
            }
156
        }
157
158
        $schema = ! empty($file) ? require $file : null;
159
160
        if (empty($schema) || ! ($schema instanceof Schema)) {
161
            $schema = Expect::mixed();
162
        }
163
164
        return $loadedSchema[$name] = $schema;
165
    }
166
167
    /**
168
     * Cree et renvoi un model donné
169
     *
170
     * @template T
171
     *
172
     * @param class-string<T> $model
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<T> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<T>.
Loading history...
173
     *
174
     * @return T
175
     */
176
    public static function model(string $model, ?ConnectionInterface $connection = null)
177
    {
178
        if (! class_exists($model) && ! Text::endsWith($model, 'Model')) {
0 ignored issues
show
Bug introduced by
'Model' of type string is incompatible with the type iterable expected by parameter $needles of BlitzPHP\Utilities\String\Text::endsWith(). ( Ignorable by Annotation )

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

178
        if (! class_exists($model) && ! Text::endsWith($model, /** @scrutinizer ignore-type */ 'Model')) {
Loading history...
179
            $model .= 'Model';
180
        }
181
182
        if (! class_exists($model)) {
183
            $model = str_replace(APP_NAMESPACE . '\\Models\\', '', $model);
184
            $model = APP_NAMESPACE . '\\Models\\' . $model;
185
        }
186
187
        if (! class_exists($model)) {
188
            throw LoadException::modelNotFound($model);
189
        }
190
191
        return Services::container()->make($model, ['db' => $connection]);
192
    }
193
194
    /**
195
     * Recupere le nom de base a partir du nom de la classe, namespacé ou non.
196
     */
197
    public static function getBasename(string $name): string
198
    {
199
        // Determine le basename
200
        if ($basename = strrchr($name, '\\')) {
201
            return substr($basename, 1);
202
        }
203
204
        return $name;
205
    }
206
207
    /**
208
     * Verifie si la classe satisfait l'option "preferApp"
209
     *
210
     * @param array  $options directives specifier pqr le composant
211
     * @param string $name    Nom de la classe, namespace optionel
212
     */
213
    protected static function verifyPreferApp(array $options, string $name): bool
214
    {
215
        // Tout element sans restriction passe
216
        if (! $options['preferApp']) {
217
            return true;
218
        }
219
220
        return str_starts_with($name, APP_NAMESPACE);
221
    }
222
}
223