Passed
Push — main ( 5a020f...60a874 )
by Dimitri
04:06 queued 12s
created

Helpers::isConnected()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 0
dl 0
loc 10
rs 10
c 0
b 0
f 0
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
     * Verifie si la requete est executee en ajax
37
     */
38
    public static function isAjaxRequest(): bool
39
    {
40
        return ! empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest';
41
    }
42
43
    /**
44
     * Verifie si l'utilisateur a une connexion internet active.
45
     */
46
    public static function isConnected(): bool
47
    {
48
        $connected = @fsockopen('www.google.com', 80);
49
        if ($connected) {
0 ignored issues
show
introduced by
$connected is of type false|resource, thus it always evaluated to false.
Loading history...
50
            fclose($connected);
51
52
            return true;
53
        }
54
55
        return false;
56
    }
57
58
    /**
59
     * Tester si une application s'exécute en local ou en ligne
60
     */
61
    public static function isOnline(): bool
62
    {
63
        $host = explode(':', $_SERVER['HTTP_HOST'] ?? '')[0];
64
65
        return
66
            ! empty($host) // Si c'est vide, ca veut certainement dire qu'on est en CLI, or le CLI << n'est pas >> utilisé en ligne
67
            && ! in_array($host, ['localhost', '127.0.0.1'], true)
68
            && ! preg_match('#\.dev$#', $host)
69
            && ! preg_match('#\.test$#', $host)
70
            && ! preg_match('#\.lab$#', $host)
71
            && ! preg_match('#\.loc(al)?$#', $host)
72
            && ! preg_match('#^192\.168#', $host);
73
    }
74
75
    /**
76
     * Tests d'inscriptibilité des fichiers
77
     *
78
     * is_writable() renvoie TRUE sur les serveurs Windows lorsque vous ne pouvez vraiment pas écrire
79
     * le fichier, basé sur l'attribut en lecture seule. is_writable() n'est pas non plus fiable
80
     * sur les serveurs Unix si safe_mode est activé.
81
     *
82
     * @see https://bugs.php.net/bug.php?id=54709
83
     *
84
     * @throws Exception
85
     *
86
     * @codeCoverageIgnore Pas pratique à tester, car travis fonctionne sous linux
87
     */
88
    public static function isReallyWritable(string $file): bool
89
    {
90
        // If we're on a Unix server with safe_mode off we call is_writable
91
        if (DIRECTORY_SEPARATOR === '/' || ! ini_get('safe_mode')) {
92
            return is_writable($file);
93
        }
94
95
        /* Pour les serveurs Windows et les installations safe_mode "on", nous allons en fait
96
         * écrire un fichier puis le lire. Bah...
97
         */
98
        if (is_dir($file)) {
99
            $file = rtrim($file, '/') . '/' . bin2hex(random_bytes(16));
100
            if (($fp = @fopen($file, 'ab')) === false) {
101
                return false;
102
            }
103
104
            fclose($fp);
105
            @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

105
            /** @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...
106
            @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

106
            /** @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...
107
108
            return true;
109
        }
110
        if (! is_file($file) || ($fp = @fopen($file, 'ab')) === false) {
111
            return false;
112
        }
113
114
        fclose($fp);
115
116
        return true;
117
    }
118
119
    public static function cleanUrl(string $url): string
120
    {
121
        $path  = parse_url($url);
122
        $query = '';
123
124
        if (! empty($path['host'])) {
125
            $r = $path['scheme'] . '://';
126
            if (! empty($path['user'])) {
127
                $r .= $path['user'];
128
                if (! empty($path['pass'])) {
129
                    $r .= ':' . $path['pass'] . '@';
130
                }
131
                $r .= '@';
132
            }
133
            if (! empty($path['host'])) {
134
                $r .= $path['host'];
135
            }
136
            if (! empty($path['port'])) {
137
                $r .= ':' . $path['port'];
138
            }
139
            $url = $r . $path['path'];
140
            if (! empty($path['query'])) {
141
                $query = '?' . $path['query'];
142
            }
143
        }
144
        $url = str_replace('/./', '/', $url);
145
146
        while (substr_count($url, '../')) {
147
            $url = preg_replace('!/([\\w\\d]+/\\.\\.)!', '', $url);
148
        }
149
150
        return $url . $query;
151
    }
152
153
    /**
154
     * Supprimer les caractères invisibles
155
     *
156
     * Cela empêche de prendre en sandwich des caractères nuls
157
     * entre les caractères ascii, comme Java\0script.
158
     */
159
    public static function removeInvisibleCharacters(string $str, bool $url_encoded = true): string
160
    {
161
        $non_displayables = [];
162
163
        if ($url_encoded) {
164
            $non_displayables[] = '/%0[0-8bcef]/i';	// url encoded 00-08, 11, 12, 14, 15
165
            $non_displayables[] = '/%1[0-9a-f]/i';	// url encoded 16-31
166
            $non_displayables[] = '/%7f/i';	// url encoded 127
167
        }
168
169
        $non_displayables[] = '/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]+/S';	// 00-08, 11, 12, 14-31, 127
170
171
        do {
172
            $str = preg_replace($non_displayables, '', $str, -1, $count);
173
        } while ($count);
174
175
        return $str;
176
    }
177
178
    /**
179
     * Effectue un simple échappement automatique des données pour des raisons de sécurité.
180
     * Pourrait envisager de rendre cela plus complexe à une date ultérieure.
181
     *
182
     * Si $data est une chaîne, il suffit alors de l'échapper et de la renvoyer.
183
     * Si $data est un tableau, alors il boucle dessus, s'échappant de chaque
184
     * 'valeur' des paires clé/valeur.
185
     *
186
     * Valeurs de contexte valides : html, js, css, url, attr, raw, null
187
     *
188
     * @param array|string $data
189
     *
190
     * @throws InvalidArgumentException
191
     *
192
     * @return array|string
193
     */
194
    public static function esc($data, ?string $context = 'html', ?string $encoding = null)
195
    {
196
        if (is_array($data)) {
197
            foreach ($data as $key => &$value) {
198
                $value = self::esc($value, $context);
199
            }
200
        }
201
202
        if (is_string($data)) {
203
            $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

203
            $context = strtolower(/** @scrutinizer ignore-type */ $context);
Loading history...
204
205
            // Fournit un moyen de NE PAS échapper aux données depuis
206
            // cela pourrait être appelé automatiquement par
207
            // la bibliothèque View.
208
            if (empty($context) || $context === 'raw') {
209
                return $data;
210
            }
211
212
            if (! in_array($context, ['html', 'js', 'css', 'url', 'attr'], true)) {
213
                throw new InvalidArgumentException('Invalid escape context provided.');
214
            }
215
216
            if ($context === 'attr') {
217
                $method = 'escapeHtmlAttr';
218
            } else {
219
                $method = 'escape' . ucfirst($context);
220
            }
221
222
            static $escaper;
223
            if (! $escaper) {
224
                $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...
225
            }
226
227
            if ($encoding && $escaper->getEncoding() !== $encoding) {
228
                $escaper = new \Laminas\Escaper\Escaper($encoding);
229
            }
230
231
            $data = $escaper->{$method}($data);
232
        }
233
234
        return $data;
235
    }
236
237
    /**
238
     * Méthode pratique pour htmlspecialchars.
239
     *
240
     * @param mixed       $text    Texte à envelopper dans htmlspecialchars. Fonctionne également avec des tableaux et des objets.
241
     *                             Les tableaux seront mappés et tous leurs éléments seront échappés. Les objets seront transtypés s'ils
242
     *                             implémenter une méthode `__toString`. Sinon, le nom de la classe sera utilisé.
243
     *                             Les autres types de scalaires seront renvoyés tels quels.
244
     * @param bool        $double  Encodez les entités html existantes.
245
     * @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'.
246
     *
247
     * @return mixed Texte enveloppé.
248
     *
249
     * @credit CackePHP (https://cakephp.org)
250
     */
251
    public static function h($text, bool $double = true, ?string $charset = null)
252
    {
253
        if (is_string($text)) {
254
            // optimize for strings
255
        } elseif (is_array($text)) {
256
            $texts = [];
257
258
            foreach ($text as $k => $t) {
259
                $texts[$k] = self::h($t, $double, $charset);
260
            }
261
262
            return $texts;
263
        } elseif (is_object($text)) {
264
            if (method_exists($text, '__toString')) {
265
                $text = (string) $text;
266
            } else {
267
                $text = '(object)' . get_class($text);
268
            }
269
        } elseif ($text === null || is_scalar($text)) {
270
            return $text;
271
        }
272
273
        static $defaultCharset = false;
274
        if ($defaultCharset === false) {
275
            $defaultCharset = mb_internal_encoding();
276
            if ($defaultCharset === null) {
277
                $defaultCharset = 'UTF-8';
278
            }
279
        }
280
        if (is_string($double)) {
0 ignored issues
show
introduced by
The condition is_string($double) is always false.
Loading history...
281
            self::deprecationWarning(
282
                'Passing charset string for 2nd argument is deprecated. ' .
283
                'Use the 3rd argument instead.'
284
            );
285
            $charset = $double;
286
            $double  = true;
287
        }
288
289
        return htmlspecialchars($text, ENT_QUOTES | ENT_SUBSTITUTE, $charset ?: $defaultCharset, $double);
290
    }
291
292
    /**
293
     * Garantit qu'une extension se trouve à la fin d'un nom de fichier
294
     */
295
    public static function ensureExt(string $path, string $ext = 'php'): string
296
    {
297
        if ($ext) {
298
            $ext = '.' . preg_replace('#^\.#', '', $ext);
299
300
            if (substr($path, -strlen($ext)) !== $ext) {
301
                $path .= $ext;
302
            }
303
        }
304
305
        return trim($path);
306
    }
307
308
    /**
309
     * Purifiez l'entrée à l'aide de la classe autonome HTMLPurifier.
310
     * Utilisez facilement plusieurs configurations de purificateur.
311
     *
312
     * @param string|string[] $dirty_html
313
     * @param false|string    $config
314
     *
315
     * @return string|string[]
316
     */
317
    public static function purify($dirty_html, $config = false, string $charset = 'UTF-8')
318
    {
319
        if (is_array($dirty_html)) {
320
            foreach ($dirty_html as $key => $val) {
321
                $clean_html[$key] = self::purify($val, $config);
322
            }
323
        } else {
324
            switch ($config) {
325
                case 'comment':
326
                    $config = HTMLPurifier_Config::createDefault();
327
                    $config->set('Core.Encoding', $charset);
328
                    $config->set('HTML.Doctype', 'XHTML 1.0 Strict');
329
                    $config->set('HTML.Allowed', 'p,a[href|title],abbr[title],acronym[title],b,strong,blockquote[cite],code,em,i,strike');
330
                    $config->set('AutoFormat.AutoParagraph', true);
331
                    $config->set('AutoFormat.Linkify', true);
332
                    $config->set('AutoFormat.RemoveEmpty', true);
333
                    break;
334
335
                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...
336
                    $config = HTMLPurifier_Config::createDefault();
337
                    $config->set('Core.Encoding', $charset);
338
                    $config->set('HTML.Doctype', 'XHTML 1.0 Strict');
339
                    break;
340
341
                default:
342
                    throw new InvalidArgumentException('The HTMLPurifier configuration labeled "' . htmlspecialchars($config, ENT_QUOTES, $charset) . '" could not be found.');
343
            }
344
345
            $purifier   = new HTMLPurifier($config);
346
            $clean_html = $purifier->purify($dirty_html);
347
        }
348
349
        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...
350
    }
351
352
    /**
353
     * Chaîner les attributs à utiliser dans les balises HTML.
354
     *
355
     * Fonction d'assistance utilisée pour convertir une chaîne, un tableau ou un objet
356
     * d'attributs à une chaîne.
357
     *
358
     * @param array|object|string $attributes
359
     */
360
    public static function stringifyAttributes($attributes, bool $js = false): string
361
    {
362
        $atts = '';
363
364
        if (empty($attributes)) {
365
            return $atts;
366
        }
367
368
        if (is_string($attributes)) {
369
            return ' ' . $attributes;
370
        }
371
372
        $attributes = (array) $attributes;
373
374
        foreach ($attributes as $key => $val) {
375
            $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, '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

375
            $atts .= ($js) ? $key . '=' . self::esc($val, 'js') . ',' : ' ' . $key . '="' . /** @scrutinizer ignore-type */ self::esc($val, 'attr') . '"';
Loading history...
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

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