• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

timber / timber / 5690057835

pending completion
5690057835

push

github

nlemoine
Merge branch '2.x' of github.com:timber/timber into 2.x-refactor-file-models

# Conflicts:
#	src/Attachment.php
#	src/ExternalImage.php
#	src/FileSize.php
#	src/URLHelper.php

1134 of 1134 new or added lines in 55 files covered. (100.0%)

3923 of 4430 relevant lines covered (88.56%)

59.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.3
/src/URLHelper.php
1
<?php
2

3
namespace Timber;
4

5
/**
6
 * Class URLHelper
7
 *
8
 * @api
9
 */
10
class URLHelper
11
{
12
    /**
13
     * Get the current URL of the page
14
     *
15
     * @api
16
     * @return string
17
     */
18
    public static function get_current_url()
19
    {
20
        $page_url = self::get_scheme() . '://';
4✔
21
        if (isset($_SERVER["SERVER_PORT"]) && $_SERVER["SERVER_PORT"] && $_SERVER["SERVER_PORT"] != "80" && $_SERVER["SERVER_PORT"] != "443") {
4✔
22
            $page_url .= self::get_host() . ":" . $_SERVER["SERVER_PORT"] . $_SERVER["REQUEST_URI"];
1✔
23
        } else {
24
            $page_url .= self::get_host() . $_SERVER['REQUEST_URI'];
3✔
25
        }
26
        return $page_url;
4✔
27
    }
28

29
    /**
30
     * Get url scheme
31
     *
32
     * @api
33
     * @return string
34
     */
35
    public static function get_scheme()
36
    {
37
        return \is_ssl() ? 'https' : 'http';
49✔
38
    }
39

40
    /**
41
     * Check to see if the URL begins with the string in question
42
     * Because it's a URL we don't care about protocol (HTTP vs HTTPS)
43
     * Or case (so it's cAsE iNsEnSeTiVe)
44
     *
45
     * @api
46
     * @return boolean
47
     */
48
    public static function starts_with($haystack, $starts_with)
49
    {
50
        $haystack = \str_replace('https', 'http', \strtolower($haystack));
67✔
51
        $starts_with = \str_replace('https', 'http', \strtolower($starts_with));
67✔
52
        if (0 === \strpos($haystack, $starts_with)) {
67✔
53
            return true;
65✔
54
        }
55
        return false;
6✔
56
    }
57

58
    /**
59
     * @api
60
     * @param string $url
61
     * @return bool
62
     */
63
    public static function is_url($url)
64
    {
65
        if (!\is_string($url)) {
81✔
66
            return false;
1✔
67
        }
68
        $url = \strtolower($url);
81✔
69
        if (\strstr($url, '://')) {
81✔
70
            return true;
1✔
71
        }
72
        return false;
81✔
73
    }
74

75
    /**
76
     * @api
77
     * @return string
78
     */
79
    public static function get_path_base()
80
    {
81
        $struc = \get_option('permalink_structure');
1✔
82
        $struc = \explode('/', $struc);
1✔
83
        $p = '/';
1✔
84
        foreach ($struc as $s) {
1✔
85
            if (!\strstr($s, '%') && \strlen($s)) {
1✔
86
                $p .= $s . '/';
×
87
            }
88
        }
89
        return $p;
1✔
90
    }
91

92
    /**
93
     * @api
94
     * @param string $url
95
     * @param bool   $force
96
     * @return string
97
     */
98
    public static function get_rel_url($url, $force = false)
99
    {
100
        $url_info = \parse_url($url);
20✔
101
        if (isset($url_info['host']) && $url_info['host'] != self::get_host() && !$force) {
20✔
102
            return $url;
1✔
103
        }
104
        $link = '';
20✔
105
        if (isset($url_info['path'])) {
20✔
106
            $link = $url_info['path'];
20✔
107
        }
108
        if (isset($url_info['query']) && \strlen($url_info['query'])) {
20✔
109
            $link .= '?' . $url_info['query'];
3✔
110
        }
111
        if (isset($url_info['fragment']) && \strlen($url_info['fragment'])) {
20✔
112
            $link .= '#' . $url_info['fragment'];
1✔
113
        }
114
        $link = self::remove_double_slashes($link);
20✔
115
        return $link;
20✔
116
    }
117

118
    /**
119
     * Some setups like HTTP_HOST, some like SERVER_NAME, it's complicated
120
     *
121
     * @api
122
     * @link http://stackoverflow.com/questions/2297403/http-host-vs-server-name
123
     *
124
     * @return string the HTTP_HOST or SERVER_NAME
125
     */
126
    public static function get_host()
127
    {
128
        if (isset($_SERVER['HTTP_HOST']) && $_SERVER['HTTP_HOST']) {
68✔
129
            return $_SERVER['HTTP_HOST'];
66✔
130
        }
131
        if (isset($_SERVER['SERVER_NAME']) && $_SERVER['SERVER_NAME']) {
2✔
132
            return $_SERVER['SERVER_NAME'];
1✔
133
        }
134
        return '';
1✔
135
    }
136

137
    /**
138
     * Checks whether a URL or domain is local.
139
     *
140
     * True if `$url` has a host name matching the server’s host name. False if
141
     * a relative URL or if it’s a subdomain.
142
     *
143
     * @api
144
     *
145
     * @param string $url URL to check.
146
     * @return bool
147
     */
148
    public static function is_local(string $url): bool
149
    {
150
        $host = \wp_parse_url($url, \PHP_URL_HOST);
5✔
151
        if (null === $host || false === $host) {
5✔
152
            $host = $url;
1✔
153
        }
154

155
        $wp_host = self::get_host();
5✔
156

157
        return $wp_host && $wp_host === $host;
5✔
158
    }
159

160
    /**
161
     * @api
162
     *
163
     * @param string $src
164
     * @return string
165
     */
166
    public static function get_full_path($src)
167
    {
168
        $root = ABSPATH;
3✔
169
        $old_root_path = $root . $src;
3✔
170
        $old_root_path = \str_replace('//', '/', $old_root_path);
3✔
171
        return $old_root_path;
3✔
172
    }
173

174
    /**
175
     * Takes a url and figures out its place based in the file system based on path
176
     * NOTE: Not fool-proof, makes a lot of assumptions about the file path
177
     * matching the URL path
178
     *
179
     * @api
180
     *
181
     * @param string $url
182
     * @return string
183
     */
184
    public static function url_to_file_system($url)
185
    {
186
        $url_parts = \parse_url($url);
21✔
187

188
        /**
189
         * Filters the path of a parsed URL.
190
         *
191
         * This filter is used by the WPML integration.
192
         *
193
         * @todo Add description, parameter description.
194
         *
195
         * @see \Timber\URLHelper::url_to_file_system()
196
         * @since 1.3.2
197
         *
198
         * @param string $path
199
         */
200
        $url_parts['path'] = \apply_filters('timber/url_helper/url_to_file_system/path', $url_parts['path']);
21✔
201

202
        /**
203
         * Filters the path of a parsed URL.
204
         *
205
         * @deprecated 2.0.0, use `timber/url_helper/url_to_file_system/path`
206
         */
207
        $url_parts['path'] = \apply_filters_deprecated(
21✔
208
            'timber/URLHelper/url_to_file_system/path',
21✔
209
            [$url_parts['path']],
21✔
210
            '2.0.0',
21✔
211
            'timber/url_helper/url_to_file_system/path'
21✔
212
        );
21✔
213

214
        $path = ABSPATH . $url_parts['path'];
21✔
215
        $path = \str_replace('//', '/', $path);
21✔
216
        return $path;
21✔
217
    }
218

219
    /**
220
     * @api
221
     * @param string $fs
222
     * @return string
223
     */
224
    public static function file_system_to_url($fs)
225
    {
226
        $relative_path = self::get_rel_path($fs);
36✔
227
        $home = \home_url('/' . $relative_path);
36✔
228

229
        /**
230
         * Filters the home URL …
231
         *
232
         * This filter is used by the WPML integration.
233
         *
234
         * @todo Complete summary, add description.
235
         *
236
         * @see \Timber\URLHelper::file_system_to_url()
237
         * @since 1.3.2
238
         *
239
         * @param string $home The home URL.
240
         */
241
        $home = \apply_filters('timber/url_helper/file_system_to_url', $home);
36✔
242

243
        /**
244
         * Filters the home URL …
245
         *
246
         * @todo Complete summary.
247
         *
248
         * @deprecated 2.0.0, use `timber/url_helper/file_system_to_url`
249
         */
250
        $home = \apply_filters_deprecated(
36✔
251
            'timber/URLHelper/file_system_to_url',
36✔
252
            [$home],
36✔
253
            '2.0.0',
36✔
254
            'timber/url_helper/file_system_to_url'
36✔
255
        );
36✔
256
        return $home;
36✔
257
    }
258

259
    /**
260
     * Get the path to the content directory relative to the site.
261
     * This replaces the WP_CONTENT_SUBDIR constant
262
     *
263
     * @api
264
     *
265
     * @return string (ex: /wp-content or /content)
266
     */
267
    public static function get_content_subdir()
268
    {
269
        $home_url = \get_home_url();
3✔
270

271
        /**
272
         * Filters the home URL that is used to get the path relative to the content directory.
273
         *
274
         * @since 1.3.2
275
         *
276
         * @param string $home_url The URL to use as the base for getting the content subdirectory.
277
         *                         Default value of `home_url()`.
278
         */
279
        $home_url = \apply_filters('timber/url_helper/get_content_subdir/home_url', $home_url);
3✔
280

281
        /**
282
         * Filters the home URL that is used to get the path relative to the content directory.
283
         *
284
         * @deprecated 2.0.0, use `timber/url_helper/get_content_subdir/home_url`
285
         */
286
        $home_url = \apply_filters_deprecated(
3✔
287
            'timber/URLHelper/get_content_subdir/home_url',
3✔
288
            [$home_url],
3✔
289
            '2.0.0',
3✔
290
            'timber/url_helper/get_content_subdir/home_url'
3✔
291
        );
3✔
292

293
        return \str_replace($home_url, '', WP_CONTENT_URL);
3✔
294
    }
295

296
    /**
297
     * @api
298
     * @param string $src
299
     * @return string
300
     */
301
    public static function get_rel_path($src)
302
    {
303
        if (\str_contains($src, ABSPATH)) {
36✔
304
            return \str_replace(ABSPATH, '', $src);
36✔
305
        }
306
        // its outside the WordPress directory, alternate setups:
307
        $src = \str_replace(WP_CONTENT_DIR, '', $src);
×
308
        return self::get_content_subdir() . $src;
×
309
    }
310

311
    /**
312
     * Look for accidental slashes in a URL and remove them
313
     *
314
     * @api
315
     * @param  string $url to process (ex: http://nytimes.com//news/article.html)
316
     * @return string the result (ex: http://nytimes.com/news/article.html)
317
     */
318
    public static function remove_double_slashes($url)
319
    {
320
        $url = \str_replace('//', '/', $url);
115✔
321
        $schemes_whitelist = \apply_filters('timber/url/schemes-whitelist', ['http', 'https', 's3', 'gs']);
115✔
322
        foreach ($schemes_whitelist as $scheme) {
115✔
323
            if (\strstr($url, $scheme . ':') && !\strstr($url, $scheme . '://')) {
115✔
324
                $url = \str_replace($scheme . ':/', $scheme . '://', $url);
15✔
325
            }
326
        }
327
        return $url;
115✔
328
    }
329

330
    /**
331
     * Add something to the start of the path in an URL
332
     *
333
     * @api
334
     * @param  string $url a URL that you want to manipulate (ex: 'https://nytimes.com/news/article.html').
335
     * @param  string $path the path you want to insert ('/2017').
336
     * @return string the result (ex 'https://nytimes.com/2017/news/article.html')
337
     */
338
    public static function prepend_to_url($url, $path)
339
    {
340
        if (\strstr(\strtolower($url), 'http')) {
12✔
341
            $url_parts = \wp_parse_url($url);
11✔
342
            $url = $url_parts['scheme'] . '://' . $url_parts['host'];
11✔
343

344
            if (isset($url_parts['port'])) {
11✔
345
                $url .= ':' . $url_parts['port'];
1✔
346
            }
347

348
            $url .= $path;
11✔
349

350
            if (isset($url_parts['path'])) {
11✔
351
                $url .= $url_parts['path'];
11✔
352
            }
353
            if (isset($url_parts['query'])) {
11✔
354
                $url .= '?' . $url_parts['query'];
9✔
355
            }
356
            if (isset($url_parts['fragment'])) {
11✔
357
                $url .= '#' . $url_parts['fragment'];
11✔
358
            }
359
        } else {
360
            $url = $url . $path;
1✔
361
        }
362
        return self::remove_double_slashes($url);
12✔
363
    }
364

365
    /**
366
     * Add slash (if not already present) to a path
367
     *
368
     * @api
369
     * @param  string $path to process.
370
     * @return string
371
     */
372
    public static function preslashit($path)
373
    {
374
        if (\strpos($path, '/') !== 0) {
2✔
375
            $path = '/' . $path;
1✔
376
        }
377
        return $path;
2✔
378
    }
379

380
    /**
381
     * Remove slashes (if found) from a path
382
     *
383
     * @api
384
     * @param  string $path to process.
385
     * @return string
386
     */
387
    public static function unpreslashit($path)
388
    {
389
        return \ltrim($path, '/');
1✔
390
    }
391

392
    /**
393
     * This will evaluate wheter a URL is at an aboslute location (like http://example.org/whatever)
394
     *
395
     * @param string $path
396
     * @return boolean true if $path is an absolute url, false if relative.
397
     */
398
    public static function is_absolute($path)
399
    {
400
        return (bool) (\strstr($path, 'http'));
138✔
401
    }
402

403
    /**
404
     * This function is slightly different from the one below in the case of:
405
     * an image hosted on the same domain BUT on a different site than the
406
     * WordPress install will be reported as external content.
407
     *
408
     * @api
409
     * @param  string $url a URL to evaluate against
410
     * @return boolean if $url points to an external location returns true
411
     */
412
    public static function is_external_content($url)
413
    {
414
        $is_external = self::is_absolute($url) && !self::is_internal_content($url);
82✔
415

416
        return $is_external;
82✔
417
    }
418

419
    /**
420
     * @param string $url
421
     */
422
    private static function is_internal_content($url)
423
    {
424
        // using content_url() instead of site_url or home_url is IMPORTANT
425
        // otherwise you run into errors with sites that:
426
        // 1. use WPML plugin
427
        // 2. or redefine content directory.
428
        $is_content_url = \strstr($url, \content_url());
50✔
429

430
        // this case covers when the upload directory has been redefined.
431
        $upload_dir = \wp_upload_dir();
50✔
432
        $is_upload_url = \strstr($url, $upload_dir['baseurl']);
50✔
433

434
        return $is_content_url || $is_upload_url;
50✔
435
    }
436

437
    /**
438
     * Checks whether a URL or domain is external.
439
     *
440
     * True if the `$url` host name does not match the server’s host name.
441
     * Otherwise, false.
442
     *
443
     * @api
444
     * @param  string $url URL to evalute.
445
     * @return bool
446
     */
447
    public static function is_external(string $url): bool
448
    {
449
        $has_scheme = \str_starts_with($url, '//') || \wp_parse_url($url, \PHP_URL_SCHEME);
2✔
450

451
        if ($has_scheme) {
2✔
452
            return !self::is_local($url);
2✔
453
        }
454

455
        // Check with added scheme.
456
        return !self::is_local('//' . $url);
1✔
457
    }
458

459
    /**
460
     * Pass links through untrailingslashit unless they are a single /
461
     *
462
     * @api
463
     * @param  string $link the URL to process.
464
     * @return string
465
     */
466
    public static function remove_trailing_slash($link)
467
    {
468
        if ($link != '/') {
1✔
469
            $link = \untrailingslashit($link);
1✔
470
        }
471
        return $link;
1✔
472
    }
473

474
    /**
475
     * Removes the subcomponent of a URL regardless of protocol
476
     *
477
     * @api
478
     * @since  1.3.3
479
     * @author jarednova
480
     * @param string $haystack ex: http://example.org/wp-content/uploads/dog.jpg
481
     * @param string $needle ex: http://example.org/wp-content
482
     * @return string
483
     */
484
    public static function remove_url_component($haystack, $needle)
485
    {
486
        $haystack = \str_replace($needle, '', $haystack);
89✔
487
        $needle = self::swap_protocol($needle);
89✔
488
        return \str_replace($needle, '', $haystack);
89✔
489
    }
490

491
    /**
492
     * Swaps whatever protocol of a URL is sent. http becomes https and vice versa
493
     *
494
     * @api
495
     * @since  1.3.3
496
     * @author jarednova
497
     *
498
     * @param  string $url ex: http://example.org/wp-content/uploads/dog.jpg.
499
     * @return string ex: https://example.org/wp-content/uploads/dog.jpg
500
     */
501
    public static function swap_protocol($url)
502
    {
503
        if (\stristr($url, 'http:')) {
91✔
504
            return \str_replace('http:', 'https:', $url);
62✔
505
        }
506
        if (\stristr($url, 'https:')) {
29✔
507
            return \str_replace('https:', 'http:', $url);
1✔
508
        }
509
        return $url;
28✔
510
    }
511

512
    /**
513
     * Pass links through user_trailingslashit handling query strings properly
514
     *
515
     * @api
516
     * @param  string $link the URL to process.
517
     * @return string
518
     */
519
    public static function user_trailingslashit($link)
520
    {
521
        $link_parts = \wp_parse_url($link);
40✔
522

523
        if (!$link_parts) {
40✔
524
            return $link;
1✔
525
        }
526

527
        if (isset($link_parts['path']) && '/' !== $link_parts['path']) {
39✔
528
            $new_path = \user_trailingslashit($link_parts['path']);
36✔
529
            if ($new_path !== $link_parts['path']) {
36✔
530
                $link = \str_replace($link_parts['path'], $new_path, $link);
32✔
531
            }
532
        }
533
        return $link;
39✔
534
    }
535

536
    /**
537
     * Returns the url path parameters, or a single parameter if given an index.
538
     * Normalizes REQUEST_URI to lower-case. Returns false if given a
539
     * non-existent index.
540
     *
541
     * @example
542
     * ```php
543
     * // Given a $_SERVER["REQUEST_URI"] of:
544
     * // http://example.org/blog/post/news/2014/whatever
545
     *
546
     * $params = URLHelper::get_params();
547
     * // => ["blog", "post", "news", "2014", "whatever"]
548
     *
549
     * $third = URLHelper::get_params(2);
550
     * // => "news"
551
     *
552
     * // get_params() supports negative indices:
553
     * $last = URLHelper::get_params(-1);
554
     * // => "whatever"
555
     *
556
     * $nada = URLHelper::get_params(99);
557
     * // => false
558
     * ```
559
     *
560
     * @api
561
     * @param boolean|int $i the position of the parameter to grab.
562
     * @return array|string|false
563
     */
564
    public static function get_params($i = false)
565
    {
566
        $uri = \trim(\strtolower($_SERVER['REQUEST_URI']));
2✔
567
        $params = \array_values(\array_filter(\explode('/', $uri)));
2✔
568

569
        if (false === $i) {
2✔
570
            return $params;
1✔
571
        }
572

573
        // Support negative indices.
574
        if ($i < 0) {
2✔
575
            $i = \count($params) + $i;
1✔
576
        }
577

578
        return $params[$i] ?? false;
2✔
579
    }
580

581
    /**
582
     * Secures an URL based on the current environment.
583
     *
584
     * @param  string $url The URL to evaluate.
585
     *
586
     * @return string An URL with or without http/https, depending on what’s appropriate for server.
587
     */
588
    public static function maybe_secure_url($url)
589
    {
590
        if (\is_ssl() && \strpos($url, 'https') !== 0 && \strpos($url, 'http') === 0) {
7✔
591
            $url = 'https' . \substr($url, \strlen('http'));
×
592
        }
593

594
        return $url;
7✔
595
    }
596
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc