Passed
Push — main ( 80ccfb...c8d2e9 )
by Dimitri
10:09 queued 07:02
created

Helpers   F

Complexity

Total Complexity 107

Size/Duplication

Total Lines 581
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 228
c 0
b 0
f 0
dl 0
loc 581
rs 2
wmc 107

18 Methods

Rating   Name   Duplication   Size   Complexity  
B isOnline() 0 12 7
A isPhp() 0 9 2
B cleanUrl() 0 32 8
C env() 0 49 17
A removeInvisibleCharacters() 0 17 3
A ensureExt() 0 11 3
A pluginSplit() 0 13 3
C h() 0 39 12
B isReallyWritable() 0 29 7
A purify() 0 33 5
A deprecationWarning() 0 22 3
A namespaceSplit() 0 8 2
B findBaseUrl() 0 25 11
A triggerWarning() 0 15 2
A stringifyAttributes() 0 19 5
A pj() 0 6 3
B esc() 0 41 11
A stripslashesDeep() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like Helpers often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Helpers, and based on these observations, apply Extract Interface, too.

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\Utilities;
13
14
use Exception;
15
use HTMLPurifier;
0 ignored issues
show
Bug introduced by
The type HTMLPurifier was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
16
use HTMLPurifier_Config;
0 ignored issues
show
Bug introduced by
The type HTMLPurifier_Config was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
17
use InvalidArgumentException;
18
19
class Helpers
20
{
21
    /**
22
     * Détermine si la version actuelle de PHP est égale ou supérieure à la valeur fournie
23
     */
24
    public static function isPhp(string $version): bool
25
    {
26
        static $_is_php;
27
28
        if (! isset($_is_php[$version])) {
29
            $_is_php[$version] = version_compare(PHP_VERSION, $version, '>=');
30
        }
31
32
        return $_is_php[$version];
33
    }
34
35
    /**
36
     * Tester si une application s'exécute en local ou en ligne
37
     */
38
    public static function isOnline(): bool
39
    {
40
        $host = explode(':', $_SERVER['HTTP_HOST'] ?? '')[0];
41
42
        return
43
            ! empty($host) // Si c'est vide, ca veut certainement dire qu'on est en CLI, or le CLI << n'est pas >> utilisé en ligne
44
            && ! in_array($host, ['localhost', '127.0.0.1'], true)
45
            && ! preg_match('#\.dev$#', $host)
46
            && ! preg_match('#\.test$#', $host)
47
            && ! preg_match('#\.lab$#', $host)
48
            && ! preg_match('#\.loc(al)?$#', $host)
49
            && ! preg_match('#^192\.168#', $host);
50
    }
51
52
    /**
53
     * Tests d'inscriptibilité des fichiers
54
     *
55
     * is_writable() renvoie TRUE sur les serveurs Windows lorsque vous ne pouvez vraiment pas écrire
56
     * le fichier, basé sur l'attribut en lecture seule. is_writable() n'est pas non plus fiable
57
     * sur les serveurs Unix si safe_mode est activé.
58
     *
59
     * @see https://bugs.php.net/bug.php?id=54709
60
     *
61
     * @throws Exception
62
     *
63
     * @codeCoverageIgnore Pas pratique à tester, car travis fonctionne sous linux
64
     */
65
    public static function isReallyWritable(string $file): bool
66
    {
67
        // If we're on a Unix server with safe_mode off we call is_writable
68
        if (DIRECTORY_SEPARATOR === '/' || ! ini_get('safe_mode')) {
69
            return is_writable($file);
70
        }
71
72
        /* Pour les serveurs Windows et les installations safe_mode "on", nous allons en fait
73
         * écrire un fichier puis le lire. Bah...
74
         */
75
        if (is_dir($file)) {
76
            $file = rtrim($file, '/') . '/' . bin2hex(random_bytes(16));
77
            if (($fp = @fopen($file, 'ab')) === false) {
78
                return false;
79
            }
80
81
            fclose($fp);
82
            @chmod($file, 0777);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

82
            /** @scrutinizer ignore-unhandled */ @chmod($file, 0777);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
83
            @unlink($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

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

83
            /** @scrutinizer ignore-unhandled */ @unlink($file);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
84
85
            return true;
86
        }
87
        if (! is_file($file) || ($fp = @fopen($file, 'ab')) === false) {
88
            return false;
89
        }
90
91
        fclose($fp);
92
93
        return true;
94
    }
95
96
    public static function cleanUrl(string $url): string
97
    {
98
        $path  = parse_url($url);
99
        $query = '';
100
101
        if (! empty($path['host'])) {
102
            $r = $path['scheme'] . '://';
103
            if (! empty($path['user'])) {
104
                $r .= $path['user'];
105
                if (! empty($path['pass'])) {
106
                    $r .= ':' . $path['pass'] . '@';
107
                }
108
                $r .= '@';
109
            }
110
            if (! empty($path['host'])) {
111
                $r .= $path['host'];
112
            }
113
            if (! empty($path['port'])) {
114
                $r .= ':' . $path['port'];
115
            }
116
            $url = $r . $path['path'];
117
            if (! empty($path['query'])) {
118
                $query = '?' . $path['query'];
119
            }
120
        }
121
        $url = str_replace('/./', '/', $url);
122
123
        while (substr_count($url, '../')) {
124
            $url = preg_replace('!/([\\w\\d]+/\\.\\.)!', '', $url);
125
        }
126
127
        return $url . $query;
128
    }
129
130
    /**
131
     * Supprimer les caractères invisibles
132
     *
133
     * Cela empêche de prendre en sandwich des caractères nuls
134
     * entre les caractères ascii, comme Java\0script.
135
     */
136
    public static function removeInvisibleCharacters(string $str, bool $url_encoded = true): string
137
    {
138
        $non_displayables = [];
139
140
        if ($url_encoded) {
141
            $non_displayables[] = '/%0[0-8bcef]/i';	// url encoded 00-08, 11, 12, 14, 15
142
            $non_displayables[] = '/%1[0-9a-f]/i';	// url encoded 16-31
143
            $non_displayables[] = '/%7f/i';	// url encoded 127
144
        }
145
146
        $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
147
148
        do {
149
            $str = preg_replace($non_displayables, '', $str, -1, $count);
150
        } while ($count);
151
152
        return $str;
153
    }
154
155
    /**
156
     * Effectue un simple échappement automatique des données pour des raisons de sécurité.
157
     * Pourrait envisager de rendre cela plus complexe à une date ultérieure.
158
     *
159
     * Si $data est une chaîne, il suffit alors de l'échapper et de la renvoyer.
160
     * Si $data est un tableau, alors il boucle dessus, s'échappant de chaque
161
     * 'valeur' des paires clé/valeur.
162
     *
163
     * Valeurs de contexte valides : html, js, css, url, attr, raw, null
164
     *
165
     * @param array|string $data
166
     *
167
     * @throws InvalidArgumentException
168
     *
169
     * @return array|string
170
     */
171
    public static function esc($data, ?string $context = 'html', ?string $encoding = null)
172
    {
173
        if (is_array($data)) {
174
            foreach ($data as $key => &$value) {
175
                $value = self::esc($value, $context);
176
            }
177
        }
178
179
        if (is_string($data)) {
180
            $context = strtolower($context);
0 ignored issues
show
Bug introduced by
It seems like $context can also be of type null; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

180
            $context = strtolower(/** @scrutinizer ignore-type */ $context);
Loading history...
181
182
            // Fournit un moyen de NE PAS échapper aux données depuis
183
            // cela pourrait être appelé automatiquement par
184
            // la bibliothèque View.
185
            if (empty($context) || $context === 'raw') {
186
                return $data;
187
            }
188
189
            if (! in_array($context, ['html', 'js', 'css', 'url', 'attr'], true)) {
190
                throw new InvalidArgumentException('Invalid escape context provided.');
191
            }
192
193
            if ($context === 'attr') {
194
                $method = 'escapeHtmlAttr';
195
            } else {
196
                $method = 'escape' . ucfirst($context);
197
            }
198
199
            static $escaper;
200
            if (! $escaper) {
201
                $escaper = new \Laminas\Escaper\Escaper($encoding);
0 ignored issues
show
Bug introduced by
The type Laminas\Escaper\Escaper was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
202
            }
203
204
            if ($encoding && $escaper->getEncoding() !== $encoding) {
205
                $escaper = new \Laminas\Escaper\Escaper($encoding);
206
            }
207
208
            $data = $escaper->{$method}($data);
209
        }
210
211
        return $data;
212
    }
213
214
    /**
215
     * Méthode pratique pour htmlspecialchars.
216
     *
217
     * @param mixed       $text    Texte à envelopper dans htmlspecialchars. Fonctionne également avec des tableaux et des objets.
218
     *                             Les tableaux seront mappés et tous leurs éléments seront échappés. Les objets seront transtypés s'ils
219
     *                             implémenter une méthode `__toString`. Sinon, le nom de la classe sera utilisé.
220
     *                             Les autres types de scalaires seront renvoyés tels quels.
221
     * @param bool        $double  Encodez les entités html existantes.
222
     * @param string|null $charset Jeu de caractères à utiliser lors de l'échappement. La valeur par défaut est la valeur de configuration dans `mb_internal_encoding()` ou 'UTF-8'.
223
     *
224
     * @return mixed Texte enveloppé.
225
     *
226
     * @credit CackePHP (https://cakephp.org)
227
     */
228
    public static function h($text, bool $double = true, ?string $charset = null)
229
    {
230
        if (is_string($text)) {
231
            // optimize for strings
232
        } elseif (is_array($text)) {
233
            $texts = [];
234
235
            foreach ($text as $k => $t) {
236
                $texts[$k] = self::h($t, $double, $charset);
237
            }
238
239
            return $texts;
240
        } elseif (is_object($text)) {
241
            if (method_exists($text, '__toString')) {
242
                $text = (string) $text;
243
            } else {
244
                $text = '(object)' . get_class($text);
245
            }
246
        } elseif ($text === null || is_scalar($text)) {
247
            return $text;
248
        }
249
250
        static $defaultCharset = false;
251
        if ($defaultCharset === false) {
252
            $defaultCharset = mb_internal_encoding();
253
            if ($defaultCharset === null) {
254
                $defaultCharset = 'UTF-8';
255
            }
256
        }
257
        if (is_string($double)) {
0 ignored issues
show
introduced by
The condition is_string($double) is always false.
Loading history...
258
            self::deprecationWarning(
259
                'Passing charset string for 2nd argument is deprecated. ' .
260
                'Use the 3rd argument instead.'
261
            );
262
            $charset = $double;
263
            $double  = true;
264
        }
265
266
        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double);
267
    }
268
269
    /**
270
     * Garantit qu'une extension se trouve à la fin d'un nom de fichier
271
     */
272
    public static function ensureExt(string $path, string $ext = 'php'): string
273
    {
274
        if ($ext) {
275
            $ext = '.' . preg_replace('#^\.#', '', $ext);
276
277
            if (substr($path, -strlen($ext)) !== $ext) {
278
                $path .= $ext;
279
            }
280
        }
281
282
        return trim($path);
283
    }
284
285
    /**
286
     * Purifiez l'entrée à l'aide de la classe autonome HTMLPurifier.
287
     * Utilisez facilement plusieurs configurations de purificateur.
288
     *
289
     * @param string|string[] $dirty_html
290
     * @param false|string    $config
291
     *
292
     * @return string|string[]
293
     */
294
    public static function purify($dirty_html, $config = false, string $charset = 'UTF-8')
295
    {
296
        if (is_array($dirty_html)) {
297
            foreach ($dirty_html as $key => $val) {
298
                $clean_html[$key] = self::purify($val, $config);
299
            }
300
        } else {
301
            switch ($config) {
302
                case 'comment':
303
                    $config = HTMLPurifier_Config::createDefault();
304
                    $config->set('Core.Encoding', $charset);
305
                    $config->set('HTML.Doctype', 'XHTML 1.0 Strict');
306
                    $config->set('HTML.Allowed', 'p,a[href|title],abbr[title],acronym[title],b,strong,blockquote[cite],code,em,i,strike');
307
                    $config->set('AutoFormat.AutoParagraph', true);
308
                    $config->set('AutoFormat.Linkify', true);
309
                    $config->set('AutoFormat.RemoveEmpty', true);
310
                    break;
311
312
                case false:
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $config of type false|string against false; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
313
                    $config = HTMLPurifier_Config::createDefault();
314
                    $config->set('Core.Encoding', $charset);
315
                    $config->set('HTML.Doctype', 'XHTML 1.0 Strict');
316
                    break;
317
318
                default:
319
                    throw new InvalidArgumentException('The HTMLPurifier configuration labeled "' . htmlspecialchars($config, ENT_QUOTES, $charset) . '" could not be found.');
320
            }
321
322
            $purifier   = new HTMLPurifier($config);
323
            $clean_html = $purifier->purify($dirty_html);
324
        }
325
326
        return $clean_html;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $clean_html does not seem to be defined for all execution paths leading up to this point.
Loading history...
327
    }
328
329
    /**
330
     * Chaîner les attributs à utiliser dans les balises HTML.
331
     *
332
     * Fonction d'assistance utilisée pour convertir une chaîne, un tableau ou un objet
333
     * d'attributs à une chaîne.
334
     *
335
     * @param array|object|string $attributes
336
     */
337
    public static function stringifyAttributes($attributes, bool $js = false): string
338
    {
339
        $atts = '';
340
341
        if (empty($attributes)) {
342
            return $atts;
343
        }
344
345
        if (is_string($attributes)) {
346
            return ' ' . $attributes;
347
        }
348
349
        $attributes = (array) $attributes;
350
351
        foreach ($attributes as $key => $val) {
352
            $atts .= ($js) ? $key . '=' . self::esc($val, 'js') . ',' : ' ' . $key . '="' . self::esc($val, 'attr') . '"';
0 ignored issues
show
Bug introduced by
Are you sure self::esc($val, 'js') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

352
            $atts .= ($js) ? $key . '=' . /** @scrutinizer ignore-type */ self::esc($val, 'js') . ',' : ' ' . $key . '="' . self::esc($val, 'attr') . '"';
Loading history...
Bug introduced by
Are you sure self::esc($val, 'attr') of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

352
            $atts .= ($js) ? $key . '=' . self::esc($val, 'js') . ',' : ' ' . $key . '="' . /** @scrutinizer ignore-type */ self::esc($val, 'attr') . '"';
Loading history...
353
        }
354
355
        return rtrim($atts, ',');
356
    }
357
358
    /**
359
     * Obtient une variable d'environnement à partir des sources disponibles et fournit une émulation
360
     * pour les variables d'environnement non prises en charge ou incohérentes (c'est-à-dire DOCUMENT_ROOT sur
361
     * IIS, ou SCRIPT_NAME en mode CGI). Expose également quelques coutumes supplémentaires
362
     * informations sur l'environnement.
363
     *
364
     * @param string     $key     Nom de la variable d'environnement
365
     * @param mixed|null $default Spécifiez une valeur par défaut au cas où la variable d'environnement n'est pas définie.
366
     *
367
     * @return string Paramétrage des variables d'environnement.
368
     *
369
     * @credit CakePHP - http://book.cakephp.org/4.0/en/core-libraries/global-constants-and-functions.html#env
370
     */
371
    public static function env(string $key, $default = null)
372
    {
373
        if ($key === 'HTTPS') {
374
            if (isset($_SERVER['HTTPS'])) {
375
                return ! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
0 ignored issues
show
Bug Best Practice introduced by
The expression return ! empty($_SERVER[...RVER['HTTPS'] !== 'off' returns the type boolean which is incompatible with the documented return type string.
Loading history...
376
            }
377
378
            return strpos((string) self::env('SCRIPT_URI'), 'https://') === 0;
0 ignored issues
show
Bug Best Practice introduced by
The expression return strpos((string)se...RI'), 'https://') === 0 returns the type boolean which is incompatible with the documented return type string.
Loading history...
379
        }
380
381
        if ($key === 'SCRIPT_NAME' && self::env('CGI_MODE') && isset($_ENV['SCRIPT_URL'])) {
382
            $key = 'SCRIPT_URL';
383
        }
384
385
        $val = $_SERVER[$key] ?? $_ENV[$key] ?? null;
386
        if ($val === null && getenv($key) !== false) {
387
            $val = getenv($key);
388
        }
389
390
        if ($key === 'REMOTE_ADDR' && $val === self::env('SERVER_ADDR')) {
391
            $addr = self::env('HTTP_PC_REMOTE_ADDR');
392
            if ($addr !== null) {
393
                $val = $addr;
394
            }
395
        }
396
397
        if ($val !== null) {
398
            return $val;
399
        }
400
401
        switch ($key) {
402
            case 'DOCUMENT_ROOT':
403
                $name     = (string) self::env('SCRIPT_NAME');
404
                $filename = (string) self::env('SCRIPT_FILENAME');
405
                $offset   = 0;
406
                if (! strpos($name, '.php')) {
407
                    $offset = 4;
408
                }
409
410
                return substr($filename, 0, -(strlen($name) + $offset));
411
412
            case 'PHP_SELF':
413
                return str_replace((string) self::env('DOCUMENT_ROOT'), '', (string) self::env('SCRIPT_FILENAME'));
414
415
            case 'CGI_MODE':
416
                return PHP_SAPI === 'cgi';
0 ignored issues
show
Bug Best Practice introduced by
The expression return BlitzPHP\Utilities\PHP_SAPI === 'cgi' returns the type boolean which is incompatible with the documented return type string.
Loading history...
417
        }
418
419
        return $default;
420
    }
421
422
    /**
423
     * Recherche l'URL de base de l'application independamment de la configuration de l'utilisateur
424
     */
425
    public static function findBaseUrl(): string
426
    {
427
        if (isset($_SERVER['SERVER_ADDR'])) {
428
            $server_addr = $_SERVER['HTTP_HOST'] ?? ((strpos($_SERVER['SERVER_ADDR'], ':') !== false) ? '[' . $_SERVER['SERVER_ADDR'] . ']' : $_SERVER['SERVER_ADDR']);
429
430
            if (isset($_SERVER['SERVER_PORT'])) {
431
                $server_addr .= ':' . ((! preg_match('#:' . $_SERVER['SERVER_PORT'] . '$#', $server_addr)) ? $_SERVER['SERVER_PORT'] : '80');
432
            }
433
434
            if (
435
                (! empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) !== 'off')
436
                || (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https')
437
                || (! empty($_SERVER['HTTP_FRONT_END_HTTPS']) && strtolower($_SERVER['HTTP_FRONT_END_HTTPS']) !== 'off')
438
            ) {
439
                $base_url = 'https';
440
            } else {
441
                $base_url = 'http';
442
            }
443
444
            $base_url .= '://' . $server_addr . dirname(substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], basename($_SERVER['SCRIPT_FILENAME']))));
445
        } else {
446
            $base_url = 'http://localhost:' . ($_SERVER['SERVER_PORT'] ?? '80');
447
        }
448
449
        return $base_url;
450
    }
451
452
    /**
453
     * Jolie fonction de commodité d'impression JSON.
454
     *
455
     * Dans les terminaux, cela agira de la même manière que json_encode() avec JSON_PRETTY_PRINT directement, lorsqu'il n'est pas exécuté sur cli
456
     * enveloppera également les balises <pre> autour de la sortie de la variable donnée. Similaire à pr().
457
     *
458
     * Cette fonction renvoie la même variable qui a été transmise.
459
     *
460
     * @param mixed $var Variable à imprimer.
461
     *
462
     * @return mixed le même $var qui a été passé à cette fonction
463
     *
464
     * @see pr()
465
     */
466
    public static function pj($var)
467
    {
468
        $template = (PHP_SAPI !== 'cli' && PHP_SAPI !== 'phpdbg') ? '<pre class="pj">%s</pre>' : "\n%s\n\n";
469
        printf($template, trim(json_encode($var, JSON_PRETTY_PRINT)));
470
471
        return $var;
472
    }
473
474
    /**
475
     * Méthode d'assistance pour générer des avertissements d'obsolescence
476
     *
477
     * @param string $message    Le message à afficher comme avertissement d'obsolescence.
478
     * @param int    $stackFrame Le cadre de pile à inclure dans l'erreur. Par défaut à 1
479
     *                           car cela devrait pointer vers le code de l'application/du plugin.
480
     *
481
     * @return void
482
     */
483
    public static function deprecationWarning(string $message, int $stackFrame = 1)
484
    {
485
        if (! (error_reporting() & E_USER_DEPRECATED)) {
486
            return;
487
        }
488
489
        $trace = debug_backtrace();
490
        if (isset($trace[$stackFrame])) {
491
            $frame = $trace[$stackFrame];
492
            $frame += ['file' => '[internal]', 'line' => '??'];
493
494
            $message = sprintf(
495
                '%s - %s, line: %s' . "\n" .
496
                ' You can disable deprecation warnings by setting `Error.errorLevel` to' .
497
                ' `E_ALL & ~E_USER_DEPRECATED` in your config/app.php.',
498
                $message,
499
                $frame['file'],
500
                $frame['line']
501
            );
502
        }
503
504
        @trigger_error($message, E_USER_DEPRECATED);
505
    }
506
507
    /**
508
     * Déclenche un E_USER_WARNING.
509
     */
510
    public static function triggerWarning(string $message)
511
    {
512
        $stackFrame = 1;
513
        $trace      = debug_backtrace();
514
        if (isset($trace[$stackFrame])) {
515
            $frame = $trace[$stackFrame];
516
            $frame += ['file' => '[internal]', 'line' => '??'];
517
            $message = sprintf(
518
                '%s - %s, line: %s',
519
                $message,
520
                $frame['file'],
521
                $frame['line']
522
            );
523
        }
524
        trigger_error($message, E_USER_WARNING);
525
    }
526
527
    /**
528
     * Divise un nom de plugin de syntaxe à points en son plugin et son nom de classe.
529
     * Si $name n'a pas de point, alors l'index 0 sera nul.
530
     *
531
     * Couramment utilisé comme
532
     * ```
533
     * list($plugin, $name) = Helpers::pluginSplit($name);
534
     * ```
535
     *
536
     * @param string      $name      Le nom que vous voulez diviser en plugin.
537
     * @param bool        $dotAppend Définir sur true si vous voulez que le plugin ait un '.' qui y est annexé.
538
     * @param string|null $plugin    Plugin optionnel par défaut à utiliser si aucun plugin n'est trouvé. La valeur par défaut est nulle.
539
     *
540
     * @return array Tableau avec 2 index. 0 => nom du plugin, 1 => nom de la classe.
541
     *
542
     * @credit <a href="https://book.cakephp.org/4/en/core-libraries/global-constants-and-functions.html#pluginSplit">CakePHP</a>
543
     *
544
     * @psalm-return array{string|null, string}
545
     */
546
    public static function pluginSplit(string $name, bool $dotAppend = false, ?string $plugin = null): array
547
    {
548
        if (strpos($name, '.') !== false) {
549
            $parts = explode('.', $name, 2);
550
            if ($dotAppend) {
551
                $parts[0] .= '.';
552
            }
553
554
            /** @psalm-var array{string, string}*/
555
            return $parts;
556
        }
557
558
        return [$plugin, $name];
559
    }
560
561
    /**
562
     * Séparez l'espace de noms du nom de classe.
563
     *
564
     * Couramment utilisé comme `list($namespace, $className) = Helpers::namespaceSplit($class);`.
565
     *
566
     * @param string $class Le nom complet de la classe, ie `BlitzPHP\Core\App`.
567
     *
568
     * @return array<string> Tableau avec 2 index. 0 => namespace, 1 => nom de la classe.
569
     */
570
    public static function namespaceSplit(string $class): array
571
    {
572
        $pos = strrpos($class, '\\');
573
        if ($pos === false) {
574
            return ['', $class];
575
        }
576
577
        return [substr($class, 0, $pos), substr($class, $pos + 1)];
578
    }
579
580
    /**
581
     * Recursively strips slashes from all values in an array
582
     *
583
     * @param array|string $values Array of values to strip slashes
584
     *
585
     * @return mixed What is returned from calling stripslashes
586
     *
587
     * @credit http://book.cakephp.org/2.0/en/core-libraries/global-constants-and-functions.html#stripslashes_deep
588
     */
589
    public static function stripslashesDeep($values)
590
    {
591
        if (is_array($values)) {
592
            foreach ($values as $key => $value) {
593
                $values[$key] = self::stripslashesDeep($value);
594
            }
595
        } else {
596
            $values = stripslashes($values);
597
        }
598
599
        return $values;
600
    }
601
}
602