Passed
Push — main ( bd4e0a...a119e3 )
by Dimitri
14:08
created

url()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 1
b 0
f 0
nc 2
nop 3
dl 0
loc 10
ccs 0
cts 3
cp 0
crap 6
rs 10
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
use BlitzPHP\Container\Services;
13
use BlitzPHP\Core\App;
14
use BlitzPHP\Exceptions\RouterException;
15
use BlitzPHP\Http\ServerRequest;
16
use BlitzPHP\Http\Uri;
17
use BlitzPHP\Http\UrlGenerator;
18
use BlitzPHP\Utilities\Helpers;
19
20
/**
21
 * FONCTIONS DE MANIPULATION D'URL
22
 *
23
 * @credit	<a href="https://codeigniter.com">CodeIgniter 4.2 - url_helper</a>
24
 */
25
26
// =================================  ================================= //
27
28
if (! function_exists('url')) {
29
    /**
30
     * Générer une url pour l'application.
31
     *
32
     * @return UrlGenerator|string
33
     */
34
    function url(?string $path = null, mixed $parameters = [], ?bool $secure = null)
35
    {
36
		/** @var UrlGenerator $generator */
37
        $generator = service(UrlGenerator::class);
38
39
        if (null === $path) {
40
            return $generator;
41
        }
42
43
        return $generator->to($path, $parameters, $secure);
44
    }
45
}
46
47
if (! function_exists('site_url')) {
48
    /**
49
     * Renvoie une URL de site telle que définie par la configuration de l'application.
50
     *
51
     * @param mixed $relativePath Chaîne d'URI ou tableau de segments d'URI
52
     */
53
    function site_url($relativePath = '', ?string $scheme = null): string
54
    {
55
        if (is_array($relativePath)) {
56 8
            $relativePath = implode('/', $relativePath);
57
        }
58
59 8
        $uri = App::getUri($relativePath);
60
61
        return Uri::createURIString(
62
            $scheme ?? $uri->getScheme(),
63
            $uri->getAuthority(),
64
            $uri->getPath(),
65
            $uri->getQuery(),
66
            $uri->getFragment()
67 8
        );
68
    }
69
}
70
71
if (! function_exists('base_url')) {
72
    /**
73
     * Renvoie l'URL de base telle que définie par la configuration de l'application.
74
     * Les URL de base sont des URL de site coupées sans la page d'index.
75
     *
76
     * @param mixed $relativePath Chaîne d'URI ou tableau de segments d'URI
77
     */
78
    function base_url($relativePath = '', ?string $scheme = null): string
79
    {
80 2
        $index_page = index_page();
81 2
        config()->set('app.index_page', '');
82
83 2
        $url = rtrim(site_url($relativePath, $scheme), '/');
84 2
        config()->set('app.index_page', $index_page);
85
86 2
        return $url;
87
    }
88
}
89
90
if (! function_exists('current_url')) {
91
    /**
92
     * Renvoie l'URL complète (y compris les segments) de la page où cette fonction est placée
93
     *
94
     * @param bool $returnObject True pour renvoyer un objet au lieu d'une chaîne
95
     *
96
     * @return \BlitzPHP\Http\Uri|string
97
     */
98
    function current_url(bool $returnObject = false, ?ServerRequest $request = null)
99
    {
100
        $request ??= Services::request();
101
        $path = $request->getPath();
102
103
        // Ajouter des chaine de requêtes et des fragments
104
        if ($query = $request->getUri()->getQuery()) {
105
            $path .= '?' . $query;
106
        }
107
        if ($fragment = $request->getUri()->getFragment()) {
108
            $path .= '#' . $fragment;
109
        }
110
111
        $uri = App::getUri($path);
112
113
        return $returnObject ? $uri : Uri::createURIString($uri->getScheme(), $uri->getAuthority(), $uri->getPath());
114
    }
115
}
116
117
if (! function_exists('previous_url')) {
118
    /**
119
     * Renvoie l'URL précédente sur laquelle se trouvait le visiteur actuel. Pour des raisons de sécurité
120
     * nous vérifions d'abord une variable de session enregistrée, si elle existe, et l'utilisons.
121
     * Si ce n'est pas disponible, cependant, nous utiliserons une URL épurée de $_SERVER['HTTP_REFERER']
122
     * qui peut être défini par l'utilisateur, il n'est donc pas fiable et n'est pas défini par certains navigateurs/serveurs.
123
     *
124
     * @return \BlitzPHP\Http\Uri|mixed|string
125
     */
126
    function previous_url(bool $returnObject = false)
127
    {
128
        $referer = url()->previous();
129
130
        return $returnObject ? Services::uri($referer) : $referer;
131
    }
132
}
133
134
if (! function_exists('uri_string')) {
135
    /**
136
     * Renvoie la partie chemin de l'URL actuelle
137
     *
138
     * @param bool $relative Si le chemin résultant doit être relatif à baseURL
139
     */
140
    function uri_string(bool $relative = false): string
141
    {
142
        return $relative
143
            ? ltrim(Services::request()->getPath(), '/')
144
            : Services::request()->getUri()->getPath();
145
    }
146
}
147
148
if (! function_exists('index_page')) {
149
    /**
150
     * Renvoie la "index_page" de votre fichier de configuration
151
     */
152
    function index_page(): string
153
    {
154 2
        return config('app.index_page');
0 ignored issues
show
Bug Best Practice introduced by
The expression return config('app.index_page') returns the type BlitzPHP\Config\Config|null which is incompatible with the type-hinted return string.
Loading history...
155
    }
156
}
157
158
if (! function_exists('anchor')) {
159
    /**
160
     * Crée une ancre basée sur l'URL locale.
161
     *
162
     * @param string             $title      le titre du lien
163
     * @param array|false|string $attributes tous les attributs
164
     * @param mixed              $uri
165
     */
166
    function anchor($uri = '', string $title = '', $attributes = ''): string
167
    {
168
        $siteUrl = is_array($uri) ? site_url($uri, null) : (preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null));
169
        $siteUrl = rtrim($siteUrl, '/');
170
171
        if ($title === '') {
172
            $title = $siteUrl;
173
        }
174
175
        if ($attributes !== '') {
176
            $attributes = stringify_attributes($attributes);
0 ignored issues
show
Bug introduced by
It seems like $attributes can also be of type false; however, parameter $attributes of stringify_attributes() does only seem to accept array|object|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

176
            $attributes = stringify_attributes(/** @scrutinizer ignore-type */ $attributes);
Loading history...
177
        }
178
179
        return '<a href="' . $siteUrl . '"' . $attributes . '>' . $title . '</a>';
180
    }
181
}
182
183
if (! function_exists('anchor_popup')) {
184
    /**
185
     * Lien d'ancrage - Version contextuelle
186
     *
187
     * Crée une ancre basée sur l'URL locale. Le lien
188
     * ouvre une nouvelle fenêtre basée sur les attributs spécifiés.
189
     *
190
     * @param string             $title      le titre du lien
191
     * @param array|false|string $attributes tous les attributs
192
     */
193
    function anchor_popup(string $uri = '', string $title = '', $attributes = false): string
194
    {
195
        $siteUrl = preg_match('#^(\w+:)?//#i', $uri) ? $uri : site_url($uri, null);
196
        $siteUrl = rtrim($siteUrl, '/');
197
198
        if ($title === '') {
199
            $title = $siteUrl;
200
        }
201
202
        if ($attributes === false) {
203
            return '<a href="' . $siteUrl . '" onclick="window.open(\'' . $siteUrl . "', '_blank'); return false;\">" . $title . '</a>';
204
        }
205
206
        if (! is_array($attributes)) {
207
            $attributes = [$attributes];
208
209
            // Ref: http://www.w3schools.com/jsref/met_win_open.asp
210
            $windowName = '_blank';
211
        } elseif (! empty($attributes['window_name'])) {
212
            $windowName = $attributes['window_name'];
213
            unset($attributes['window_name']);
214
        } else {
215
            $windowName = '_blank';
216
        }
217
218
        foreach (['width' => '800', 'height' => '600', 'scrollbars' => 'yes', 'menubar' => 'no', 'status' => 'yes', 'resizable' => 'yes', 'screenx' => '0', 'screeny' => '0'] as $key => $val) {
219
            $atts[$key] = $attributes[$key] ?? $val;
220
            unset($attributes[$key]);
221
        }
222
223
        $attributes = stringify_attributes($attributes);
224
225
        return '<a href="' . $siteUrl
226
                . '" onclick="window.open(\'' . $siteUrl . "', '" . $windowName . "', '" . stringify_attributes($atts, true) . "'); return false;\""
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $atts seems to be defined by a foreach iteration on line 218. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
227
                . $attributes . '>' . $title . '</a>';
228
    }
229
}
230
231
if (! function_exists('mailto')) {
232
    /**
233
     * Lien Mailto
234
     *
235
     * @param string       $title      le titre du lien
236
     * @param array|string $attributes tous les attributs
237
     */
238
    function mailto(string $email, string $title = '', $attributes = ''): string
239
    {
240
        if (trim($title) === '') {
241
            $title = $email;
242
        }
243
244
        return '<a href="mailto:' . $email . '"' . stringify_attributes($attributes) . '>' . $title . '</a>';
245
    }
246
}
247
248
if (! function_exists('safe_mailto')) {
249
    /**
250
     * Lien Mailto codé
251
     *
252
     * Créer un lien mailto protégé contre les spams écrit en Javascript
253
     *
254
     * @param string $title      le titre du lien
255
     * @param mixed  $attributes tous les attributs
256
     */
257
    function safe_mailto(string $email, string $title = '', $attributes = ''): string
258
    {
259
        if (trim($title) === '') {
260
            $title = $email;
261
        }
262
263
        $x = str_split('<a href="mailto:', 1);
264
265
        for ($i = 0, $l = strlen($email); $i < $l; $i++) {
266
            $x[] = '|' . ord($email[$i]);
267
        }
268
269
        $x[] = '"';
270
271
        if ($attributes !== '') {
272
            if (is_array($attributes)) {
273
                foreach ($attributes as $key => $val) {
274
                    $x[] = ' ' . $key . '="';
275
276
                    for ($i = 0, $l = strlen($val); $i < $l; $i++) {
277
                        $x[] = '|' . ord($val[$i]);
278
                    }
279
280
                    $x[] = '"';
281
                }
282
            } else {
283
                for ($i = 0, $l = mb_strlen($attributes); $i < $l; $i++) {
284
                    $x[] = mb_substr($attributes, $i, 1);
285
                }
286
            }
287
        }
288
289
        $x[] = '>';
290
291
        $temp = [];
292
293
        for ($i = 0, $l = strlen($title); $i < $l; $i++) {
294
            $ordinal = ord($title[$i]);
295
296
            if ($ordinal < 128) {
297
                $x[] = '|' . $ordinal;
298
            } else {
299
                if (empty($temp)) {
300
                    $count = ($ordinal < 224) ? 2 : 3;
301
                } else {
302
                    $count = 0;
303
                }
304
305
                $temp[] = $ordinal;
306
307
                if (count($temp) === $count) {
308
                    $number = ($count === 3) ? (($temp[0] % 16) * 4096) + (($temp[1] % 64) * 64) + ($temp[2] % 64) : (($temp[0] % 32) * 64) + ($temp[1] % 64);
309
                    $x[]    = '|' . $number;
310
                    $count  = 1;
311
                    $temp   = [];
312
                }
313
            }
314
        }
315
316
        $x[] = '<';
317
        $x[] = '/';
318
        $x[] = 'a';
319
        $x[] = '>';
320
321
        $x = array_reverse($x);
0 ignored issues
show
Bug introduced by
It seems like $x can also be of type true; however, parameter $array of array_reverse() does only seem to accept array, 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

321
        $x = array_reverse(/** @scrutinizer ignore-type */ $x);
Loading history...
322
323
        // améliore l'obscurcissement en éliminant les retours à la ligne et les espaces
324
        $output = '<script type="text/javascript">'
325
                . 'var l=new Array();';
326
327
        foreach ($x as $i => $value) {
328
            $output .= 'l[' . $i . "] = '" . $value . "';";
329
        }
330
331
        return $output . ('for (var i = l.length-1; i >= 0; i=i-1) {'
332
                . "if (l[i].substring(0, 1) === '|') document.write(\"&#\"+unescape(l[i].substring(1))+\";\");"
333
                . 'else document.write(unescape(l[i]));'
334
                . '}'
335
                . '</script>');
336
    }
337
}
338
339
if (! function_exists('auto_link')) {
340
    /**
341
     * Lien automatique
342
     *
343
     * Liens automatiquement URL et adresses e-mail.
344
     * Remarque : il y a un peu de code supplémentaire ici à gérer
345
     * URL ou e-mails se terminant par un point. Nous allons les dépouiller
346
     * off et ajoutez-les après le lien.
347
     *
348
     * @param string $type  le type : email, url, ou les deux
349
     * @param bool   $popup s'il faut créer des liens contextuels
350
     */
351
    function auto_link(string $str, string $type = 'both', bool $popup = false): string
352
    {
353
        // Recherche et remplace tous les URLs.
354
        if ($type !== 'email' && preg_match_all('#(\w*://|www\.)[^\s()<>;]+\w#i', $str, $matches, PREG_OFFSET_CAPTURE | PREG_SET_ORDER)) {
355
            // Définissez notre HTML cible si vous utilisez des liens contextuels.
356
            $target = ($popup) ? ' target="_blank"' : '';
357
358
            // Nous traitons les liens dans l'ordre inverse (dernier -> premier) de sorte que
359
            // les décalages de chaîne renvoyés par preg_match_all() ne sont pas
360
            // déplacées au fur et à mesure que nous ajoutons plus de HTML.
361
            foreach (array_reverse($matches) as $match) {
362
                // $match[0] est la chaîne/le lien correspondant
363
                // $match[1] est soit un préfixe de protocole soit 'www.'
364
                //
365
                // Avec PREG_OFFSET_CAPTURE, les deux éléments ci-dessus sont un tableau,
366
                // où la valeur réelle est contenue dans [0] et son décalage à l'index [1].
367
                $a   = '<a href="' . (strpos($match[1][0], '/') ? '' : 'http://') . $match[0][0] . '"' . $target . '>' . $match[0][0] . '</a>';
368
                $str = substr_replace($str, $a, $match[0][1], strlen($match[0][0]));
369
            }
370
        }
371
372
        // Recherche et remplace tous les e-mails.
373
        if ($type !== 'url' && preg_match_all('#([\w\.\-\+]+@[a-z0-9\-]+\.[a-z0-9\-\.]+[^[:punct:]\s])#i', $str, $matches, PREG_OFFSET_CAPTURE)) {
0 ignored issues
show
Unused Code introduced by
The call to preg_match_all() has too many arguments starting with PREG_OFFSET_CAPTURE. ( Ignorable by Annotation )

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

373
        if ($type !== 'url' && /** @scrutinizer ignore-call */ preg_match_all('#([\w\.\-\+]+@[a-z0-9\-]+\.[a-z0-9\-\.]+[^[:punct:]\s])#i', $str, $matches, PREG_OFFSET_CAPTURE)) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
374
            foreach (array_reverse($matches[0]) as $match) {
375
                if (filter_var($match[0], FILTER_VALIDATE_EMAIL) !== false) {
376
                    $str = substr_replace($str, safe_mailto($match[0]), $match[1], strlen($match[0]));
377
                }
378
            }
379
        }
380
381
        return $str;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $str could return the type array which is incompatible with the type-hinted return string. Consider adding an additional type-check to rule them out.
Loading history...
382
    }
383
}
384
385
if (! function_exists('prep_url')) {
386
    /**
387
     * Ajoute simplement la partie http:// ou https:// si aucun schéma n'est inclus.
388
     *
389
     * @param bool $secure définissez true si vous voulez forcer https://
390
     */
391
    function prep_url(string $str = '', bool $secure = false): string
392
    {
393
        if (in_array($str, ['http://', 'https://', '//', ''], true)) {
394
            return '';
395
        }
396
397
        if (parse_url($str, PHP_URL_SCHEME) === null) {
398
            $str = 'http://' . ltrim($str, '/');
399
        }
400
401
        // force le remplacement de http:// par https://
402
        if ($secure) {
403
            $str = preg_replace('/^(?:http):/i', 'https:', $str);
404
        }
405
406
        return $str;
407
    }
408
}
409
410
if (! function_exists('url_title')) {
411
    /**
412
     * Créer un titre d'URL
413
     *
414
     * Prend une chaîne de "titre" en entrée et crée un
415
     * Chaîne d'URL conviviale avec une chaîne de "séparateur"
416
     * comme séparateur de mots.
417
     *
418
     * @param string $separator Séparateur de mots (généralement '-' ou '_')
419
     * @param bool   $lowercase Indique s'il faut transformer la chaîne de sortie en minuscules
420
     */
421
    function url_title(string $str, string $separator = '-', bool $lowercase = false): string
422
    {
423
        $qSeparator = preg_quote($separator, '#');
424
425
        $trans = [
426
            '&.+?;'                  => '',
427
            '[^\w\d\pL\pM _-]'       => '',
428
            '\s+'                    => $separator,
429
            '(' . $qSeparator . ')+' => $separator,
430
        ];
431
432
        $str = strip_tags($str);
433
434
        foreach ($trans as $key => $val) {
435
            $str = preg_replace('#' . $key . '#iu', $val, $str);
436
        }
437
438
        if ($lowercase === true) {
439
            $str = mb_strtolower($str);
440
        }
441
442
        return trim(trim($str, $separator));
443
    }
444
}
445
446
if (! function_exists('mb_url_title')) {
447
    /**
448
     * Créer un titre d'URL qui prend en compte les caractères accentués
449
     *
450
     * Prend une chaîne de "titre" en entrée et crée un
451
     * Chaîne d'URL conviviale avec une chaîne de "séparateur"
452
     * comme séparateur de mots.
453
     *
454
     * @param string $separator Séparateur de mots (généralement '-' ou '_')
455
     * @param bool   $lowercase Indique s'il faut transformer la chaîne de sortie en minuscules
456
     */
457
    function mb_url_title(string $str, string $separator = '-', bool $lowercase = false): string
458
    {
459
        helper('scl');
460
461
        return url_title(scl_moveSpecialChar($str), $separator, $lowercase);
462
    }
463
}
464
465
if (! function_exists('url_to')) {
466
    /**
467
     * Obtenir l'URL complète et absolue d'une méthode de contrôleur
468
     * (avec arguments supplémentaires)
469
     *
470
     * REMARQUE : Cela nécessite que le contrôleur/la méthode ait une route définie dans le fichier de configuration des routes.
471
     *
472
     * @param mixed ...$args
473
     *
474
     * @throws RouterException
475
     */
476
    function url_to(string $controller, ...$args): string
477
    {
478
        if (! $route = route($controller, ...$args)) {
479
            $explode = explode('::', $controller);
480
481
            if (isset($explode[1])) {
482
                throw RouterException::controllerNotFound($explode[0], $explode[1]);
483
            }
484
485
            throw RouterException::invalidRoute($controller);
486
        }
487
488
        return site_url($route);
489
    }
490
}
491
492
if (! function_exists('route')) {
493
    /**
494
     * Tente de rechercher une route en fonction de sa destination.
495
     *
496
     * @return false|string
497
     */
498
    function route(string $method, ...$params)
499
    {
500
        return Services::routes()->reverseRoute($method, ...$params);
501
    }
502
}
503
504
if (! function_exists('action')) {
505
    /**
506
     * Obtenir l'URL d'une action du contrôleur.
507
     *
508
     * @return false|string
509
     */
510
    function action(array|string $action, array $parameters = [])
511
    {
512
		return url()->action($action, $parameters);
513
    }
514
}
515
516
if (! function_exists('url_is')) {
517
    /**
518
     * Détermine si le chemin d'URL actuel contient le chemin donné.
519
     * Il peut contenir un caractère générique (*) qui autorisera tout caractère valide.
520
     *
521
     * Exemple:
522
     *   if (url_is('admin*)) ...
523
     */
524
    function url_is(string $path): bool
525
    {
526
        // Configurez notre regex pour autoriser les caractères génériques
527
        $path        = '/' . trim(str_replace('*', '(\S)*', $path), '/ ');
528
        $currentPath = '/' . trim(uri_string(true), '/ ');
529
530
        return (bool) preg_match("|^{$path}$|", $currentPath, $matches);
531
    }
532
}
533
534
if (! function_exists('link_active')) {
535
    /**
536
     * Lien actif dans la navbar
537
     * Un peut comme le router-active-link de vuejs
538
     */
539
    function link_active(array|string $path, string $active_class = 'active', bool $exact = false): string
540
    {
541
        if (is_array($path)) {
0 ignored issues
show
introduced by
The condition is_array($path) is always true.
Loading history...
542
            foreach ($path as $p) {
543
                if ($active_class === link_active($p, $active_class, $exact)) {
544
                    return $active_class;
545
                }
546
            }
547
548
            return '';
549
        }
550
551
        $current_url     = trim(current_url(false), '/');
552
        $current_section = trim(str_replace(trim(site_url(), '/'), '', $current_url), '/');
553
554
        if ($current_section === $path || $current_url === $path) {
555
            return $active_class;
556
        }
557
558
        if (! $exact && preg_match('#^' . $path . '/?#i', $current_section)) {
559
            return $active_class;
560
        }
561
562
        if (trim(link_to($path), '/') === $current_url) {
563
            return $active_class;
564
        }
565
566
        return '';
567
    }
568
}
569
570
if (! function_exists('clean_url')) {
571
    function clean_url(string $url): string
572
    {
573
        return Helpers::cleanUrl($url);
574
    }
575
}
576
577
if (! function_exists('is_absolute_link')) {
578
    /**
579
     * Verifies si un chemin donnée est une url absolue ou relative
580
     */
581
    function is_absolute_link(string $url): bool
582
    {
583
        return Helpers::isAbsoluteUrl($url);
584
    }
585
}
586