PluginProbe
FlyWP Helper – Page Cache, Page Optimization, Emails for FlyWP Server Control Panel / 1.8.0
FlyWP Helper – Page Cache, Page Optimization, Emails for FlyWP Server Control Panel v1.8.0
1.8.0 1.7.1 1.7.0 1.6.0 1.5.2 trunk 0.1 0.2.0 0.2.1 0.3 0.3.1 0.3.2 0.3.3 0.3.4 0.4 0.4.1 0.4.2 0.4.3 1.0 1.1 1.2 1.3.1 1.4.0 1.4.1 1.5.0 All 27 releases
flywp / includes / StaticSite / Engine / Discovery / Url.php

Url.php in FlyWP Helper – Page Cache, Page Optimization, Emails for FlyWP Server Control Panel 1.8.0, at includes/StaticSite/Engine/Discovery/Url.php

225 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FlyWP\StaticSite\Engine\Discovery;
4
5 /**
6 * String URL helpers for the redirect sources. String URLs in and out.
7 *
8 * @since 1.8.0
9 */
10 class Url {
11
12 /**
13 * Parts of a URL. Missing parts are empty strings, a missing port is null.
14 *
15 * @param string $url URL.
16 *
17 * @return array{scheme: string, user: string, pass: string, host: string, port: int|null, path: string, query: string, fragment: string}
18 */
19 public static function parts( $url ) {
20 $parts = parse_url( (string) $url );
21
22 if ( ! is_array( $parts ) ) {
23 $parts = [];
24 }
25
26 return [
27 'scheme' => strtolower( isset( $parts['scheme'] ) ? $parts['scheme'] : '' ),
28 'user' => isset( $parts['user'] ) ? $parts['user'] : '',
29 'pass' => isset( $parts['pass'] ) ? $parts['pass'] : '',
30 'host' => strtolower( isset( $parts['host'] ) ? $parts['host'] : '' ),
31 'port' => isset( $parts['port'] ) ? (int) $parts['port'] : null,
32 'path' => isset( $parts['path'] ) ? $parts['path'] : '',
33 'query' => isset( $parts['query'] ) ? $parts['query'] : '',
34 'fragment' => isset( $parts['fragment'] ) ? $parts['fragment'] : '',
35 ];
36 }
37
38 /**
39 * Build a URL from parts.
40 *
41 * @param array $parts Result of parts().
42 *
43 * @return string
44 */
45 public static function build( array $parts ) {
46 $url = '';
47
48 if ( $parts['scheme'] !== '' ) {
49 $url .= $parts['scheme'] . ':';
50 }
51
52 $authority = self::authority( $parts );
53
54 if ( $authority !== '' || $parts['scheme'] === 'http' || $parts['scheme'] === 'https' ) {
55 $url .= '//' . $authority;
56 }
57
58 $path = $parts['path'];
59
60 if ( $authority !== '' && $path !== '' && $path[0] !== '/' ) {
61 $path = '/' . $path;
62 }
63
64 $url .= $path;
65
66 if ( $parts['query'] !== '' ) {
67 $url .= '?' . $parts['query'];
68 }
69
70 if ( $parts['fragment'] !== '' ) {
71 $url .= '#' . $parts['fragment'];
72 }
73
74 return $url;
75 }
76
77 /**
78 * Host and port. The default port of the scheme is not included.
79 *
80 * @param array $parts Result of parts().
81 *
82 * @return string
83 */
84 public static function authority( array $parts ) {
85 $authority = $parts['host'];
86
87 if ( $parts['user'] !== '' ) {
88 $authority = $parts['user'] . ( $parts['pass'] !== '' ? ':' . $parts['pass'] : '' ) . '@' . $authority;
89 }
90
91 $port = self::effective_port( $parts );
92
93 if ( $port !== null ) {
94 $authority .= ':' . $port;
95 }
96
97 return $authority;
98 }
99
100 /**
101 * Whether a URL is absolute with an http or https scheme and a host.
102 *
103 * @param string $url URL.
104 *
105 * @return bool
106 */
107 public static function is_http( $url ) {
108 $parts = self::parts( $url );
109
110 return in_array( $parts['scheme'], [ 'http', 'https' ], true ) && $parts['host'] !== '';
111 }
112
113 /**
114 * Resolve a reference against a base URL (RFC 3986 section 5.2).
115 *
116 * @param string $base Absolute base URL.
117 * @param string $reference Reference, for example a Location header.
118 *
119 * @return string
120 */
121 public static function resolve( $base, $reference ) {
122 $reference = trim( (string) $reference );
123
124 if ( $reference === '' ) {
125 return $base;
126 }
127
128 $base_parts = self::parts( $base );
129 $rel = self::parts( $reference );
130
131 if ( $rel['scheme'] !== '' ) {
132 $rel['path'] = self::remove_dot_segments( $rel['path'] );
133
134 return self::build( $rel );
135 }
136
137 $target = $base_parts;
138
139 if ( strpos( $reference, '//' ) === 0 ) {
140 $rel['scheme'] = $base_parts['scheme'];
141 $rel['path'] = self::remove_dot_segments( $rel['path'] );
142
143 return self::build( $rel );
144 }
145
146 if ( $rel['path'] === '' ) {
147 $target['query'] = $rel['query'] !== '' ? $rel['query'] : $base_parts['query'];
148 } else {
149 if ( $rel['path'][0] === '/' ) {
150 $path = $rel['path'];
151 } elseif ( $base_parts['host'] !== '' && $base_parts['path'] === '' ) {
152 $path = '/' . $rel['path'];
153 } else {
154 $last_slash = strrpos( $base_parts['path'], '/' );
155 $path = $last_slash === false ? $rel['path'] : substr( $base_parts['path'], 0, $last_slash + 1 ) . $rel['path'];
156 }
157
158 $target['path'] = self::remove_dot_segments( $path );
159 $target['query'] = $rel['query'];
160 }
161
162 $target['fragment'] = $rel['fragment'];
163
164 return self::build( $target );
165 }
166
167 /**
168 * Remove "." and ".." segments from a path.
169 *
170 * @param string $path URL path.
171 *
172 * @return string
173 */
174 public static function remove_dot_segments( $path ) {
175 if ( $path === '' || $path === '/' ) {
176 return $path;
177 }
178
179 $results = [];
180 $segment = '';
181
182 foreach ( explode( '/', $path ) as $segment ) {
183 if ( $segment === '..' ) {
184 array_pop( $results );
185 } elseif ( $segment !== '.' ) {
186 $results[] = $segment;
187 }
188 }
189
190 $new_path = implode( '/', $results );
191
192 if ( $path[0] === '/' && ( $new_path === '' || $new_path[0] !== '/' ) ) {
193 $new_path = '/' . $new_path;
194 } elseif ( $new_path !== '' && ( $segment === '.' || $segment === '..' ) ) {
195 $new_path .= '/';
196 }
197
198 return $new_path;
199 }
200
201 /**
202 * Port, or null when it is the default port of the scheme.
203 *
204 * @param array $parts Result of parts().
205 *
206 * @return int|null
207 */
208 private static function effective_port( array $parts ) {
209 $defaults = [
210 'http' => 80,
211 'https' => 443,
212 ];
213
214 if ( $parts['port'] === null ) {
215 return null;
216 }
217
218 if ( isset( $defaults[ $parts['scheme'] ] ) && $defaults[ $parts['scheme'] ] === $parts['port'] ) {
219 return null;
220 }
221
222 return $parts['port'];
223 }
224 }
225