Completed
Push — master ( 48c8b6...75dfa3 )
by Lars
06:29 queued 04:07
created

Url::httpBuildUrlRelative()   A

Complexity

Conditions 4
Paths 1

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 4

Importance

Changes 2
Bugs 0 Features 2
Metric Value
c 2
b 0
f 2
dl 0
loc 10
ccs 7
cts 7
cp 1
rs 9.2
cc 4
eloc 6
nc 1
nop 1
crap 4
1
<?php
2
3
/*
4
 * This file is part of the Purl package, a project by Jonathan H. Wage.
5
 *
6
 * (c) 2013 Jonathan H. Wage
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Purl;
13
14
use Pdp\Parser as PslParser;
15
use Pdp\PublicSuffixListManager;
16
17
/**
18
 * Url is a simple OO class for manipulating Urls in PHP.
19
 *
20
 * @author      Jonathan H. Wage <[email protected]>
21
 *
22
 * @property string $scheme
23
 * @property string $host
24
 * @property integer $port
25
 * @property string $user
26
 * @property string $pass
27
 * @property \Purl\Path $path
28
 * @property \Purl\Query $query
29
 * @property \Purl\Fragment $fragment
30
 * @property string $publicSuffix
31
 * @property string $registerableDomain
32
 * @property string $subdomain
33
 * @property string $canonical
34
 * @property string $resource
35
 */
36
class Url extends AbstractPart
37
{
38
    /**
39
     * @var string The original url string.
40
     */
41
    private $url;
42
43
    /**
44
     * @var ParserInterface
45
     */
46
    private $parser;
47
48
    /**
49
     * Construct a new Url instance.
50
     *
51
     * @param string $url
52
     * @param ParserInterface $parser
53
     */
54 61
    public function __construct($url = null, ParserInterface $parser = null)
55
    {
56 61
        $this->data = array(
57 61
            'scheme'             => null,
58 61
            'host'               => null,
59 61
            'port'               => null,
60 61
            'user'               => null,
61 61
            'pass'               => null,
62 61
            'path'               => null,
63 61
            'query'              => null,
64 61
            'fragment'           => null,
65 61
            'publicSuffix'       => null,
66 61
            'registerableDomain' => null,
67 61
            'subdomain'          => null,
68 61
            'canonical'          => null,
69
            'resource'           => null
70 61
        );
71
72 61
        $this->partClassMap = array(
73 61
            'path' => 'Purl\Path',
74 61
            'query' => 'Purl\Query',
75
            'fragment' => 'Purl\Fragment'
76 61
        );
77
78 61
        $this->url = $url;
79 61
        $this->parser = $parser;
80 61
    }
81
82
    /**
83
     * Static convenience method for creating a new Url instance.
84
     *
85
     * @param string $url
86
     * @return Url
87
     */
88 4
    public static function parse($url)
89
    {
90 4
        return new self($url);
91
    }
92
93
    /**
94
     * Extracts urls from a string of text.
95
     *
96
     * @param string $string
97
     * @return array $urls
98
     */
99 1
    public static function extract($string)
100
    {
101 1
        $regex = "/(\bhttps?:\/\/[^\s()<>]+(?:\([\w\d]+\)|[^[:punct:]\s]|\/))/i";
102
103 1
        preg_match_all($regex, $string, $matches);
104 1
        $urls = array();
105 1
        foreach ($matches[0] as $url) {
106 1
            $urls[] = self::parse($url);
107 1
        }
108
109 1
        return $urls;
110
    }
111
112
    /**
113
     * Creates an Url instance based on data available on $_SERVER variable.
114
     *
115
     * @return Url
116
     */
117 1
    public static function fromCurrent()
0 ignored issues
show
Coding Style introduced by
fromCurrent uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
118
    {
119
        $scheme = (
120 1
            !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off'
121 1
            ||
122 1
            isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443) ? 'https' : 'http';
123
124 1
        $host = $_SERVER['HTTP_HOST'];
125 1
        $baseUrl = "$scheme://$host";
126
127 1
        $url = new self($baseUrl);
128
129 1
        if (!empty($_SERVER['REQUEST_URI'])) {
130
131 1
            if (strpos($_SERVER['REQUEST_URI'], '?') !== false) {
132 1
                list($path, $query) = array_pad(explode('?', $_SERVER['REQUEST_URI'], 2), 2, null);
133 1
            } else {
134 1
                $path = $_SERVER['REQUEST_URI'];
135 1
                $query = '';
136
            }
137
138 1
            $url->set('path', $path);
139 1
            $url->set('query', $query);
140 1
        }
141
142
        // Only set port if different from default (80 or 443)
143 1
        if (!empty($_SERVER['SERVER_PORT'])) {
144 1
            $port = $_SERVER['SERVER_PORT'];
145
146
            if (
147 1
                ($scheme == 'http' && $port != 80)
148
                ||
149 1
                ($scheme == 'https' && $port != 443)
150 1
            ) {
151 1
                $url->set('port', $port);
152 1
            }
153 1
        }
154
155
        // Authentication
156 1
        if (!empty($_SERVER['PHP_AUTH_USER'])) {
157 1
            $url->set('user', $_SERVER['PHP_AUTH_USER']);
158 1
            if (!empty($_SERVER['PHP_AUTH_PW'])) {
159 1
                $url->set('pass', $_SERVER['PHP_AUTH_PW']);
160 1
            }
161 1
        }
162
163 1
        return $url;
164
    }
165
166
    /**
167
     * Gets the ParserInterface instance used to parse this Url instance.
168
     *
169
     * @return ParserInterface
170
     */
171 60
    public function getParser()
172
    {
173 60
        if ($this->parser === null) {
174 59
            $this->parser = self::createDefaultParser();
175 59
        }
176
177 60
        return $this->parser;
178
    }
179
180
    /**
181
     * Sets the ParserInterface instance to use to parse this Url instance.
182
     *
183
     * @param ParserInterface $parser
184
     */
185 1
    public function setParser(ParserInterface $parser)
186
    {
187 1
        $this->parser = $parser;
188 1
    }
189
190
    /**
191
     * Join this Url instance together with another Url instance or a string url.
192
     *
193
     * @param Url|string $url
194
     * @return Url
195
     */
196 1
    public function join($url)
197
    {
198 1
        $this->initialize();
199 1
        $parts = $this->getParser()->parseUrl($url);
200
201 1
        foreach ($parts as $partsKey => $partsValue) {
202 1
            if ($partsValue !== null) {
203 1
                $this->data[$partsKey] = $partsValue;
204 1
            }
205 1
        }
206
207 1
        foreach ($this->data as $key => &$value) {
208 1
            $value = $this->preparePartValue($key, $value);
209 1
        }
210
211 1
        return $this;
212
    }
213
214
    /** @noinspection PhpMissingParentCallCommonInspection */
215
    /**
216
     * @inheritDoc
217
     * @override
218
     */
219 8
    public function set($key, $value)
220
    {
221 8
        $this->initialize();
222 8
        $this->data[$key] = $this->preparePartValue($key, $value);
223
224 8
        return $this;
225
    }
226
227
    /**
228
     * @param $string
229
     */
230 1
    public function setPathString($string)
231
    {
232 1
      $this->set('path', $string);
233 1
    }
234
235
    /**
236
     * Set the Path instance.
237
     *
238
     * @param Path $path
239
     *
240
     * @return $this
241
     */
242 1
    public function setPath(Path $path)
243
    {
244 1
        $this->data['path'] = $path;
245
246 1
        return $this;
247
    }
248
249
    /**
250
     * Get the Path instance.
251
     *
252
     * @return Path
253
     */
254
    public function getPath()
255
    {
256
        $this->initialize();
257
        return $this->data['path'];
258
    }
259
260
    /**
261
     * @param $string
262
     */
263 1
    public function setQueryString($string)
264
    {
265 1
      $this->set('query', $string);
266 1
    }
267
268
    /**
269
     * Set the Query instance.
270
     *
271
     * @param Query $query
272
     *
273
     * @return $this
274
     */
275 2
    public function setQuery(Query $query)
276
    {
277 2
        $this->data['query'] = $query;
278
279 2
        return $this;
280
    }
281
282
    /**
283
     * Get the Query instance.
284
     *
285
     * @return Query
286
     */
287
    public function getQuery()
288
    {
289
        $this->initialize();
290
        return $this->data['query'];
291
    }
292
293
    /**
294
     * @param $string
295
     */
296 1
    public function setFragmentString($string)
297
    {
298 1
        $this->set('fragment', $string);
299 1
    }
300
301
    /**
302
     * Set the Fragment instance.
303
     *
304
     * @param Fragment $fragment
305
     *
306
     * @return $this
307
     */
308 2
    public function setFragment(Fragment $fragment)
309
    {
310 2
        $this->data['fragment'] = $fragment;
311
312 2
        return $this;
313
    }
314
315
    /**
316
     * Get the Fragment instance.
317
     *
318
     * @return Fragment
319
     */
320
    public function getFragment()
321
    {
322
        $this->initialize();
323
        return $this->data['fragment'];
324
    }
325
326
    /**
327
     * Gets the netloc part of the Url. It is the user, pass, host and port returned as a string.
328
     *
329
     * @return string
330
     */
331 1
    public function getNetloc()
332
    {
333 1
        $this->initialize();
334
335 1
        return ($this->user && $this->pass
336 1
            ? $this->user.($this->pass
337 1
                ? ':'.$this->pass : '').'@'
338 1
            : '').$this->host.($this->port
339 1
                ? ':'.$this->port : '');
340
    }
341
342
    /**
343
     * Builds a string url from this Url instance internal data and returns it.
344
     *
345
     * @return string
346
     */
347 18
    public function getUrl()
348
    {
349 18
        $this->initialize();
350
351 18
        $parts = array_map(
352 18
            function ($value) {
353 18
                return (string) $value;
354 18
            },
355 18
            $this->data
356 18
        );
357
358 18
        if (!$this->isAbsolute()) {
359 1
            return self::httpBuildUrlRelative($parts);
360
        }
361
362 17
        return self::httpBuildUrl($parts);
363
    }
364
365
    /**
366
     * Set the string url for this Url instance and sets initialized to false.
367
     *
368
     * @param string
369
     */
370 2
    public function setUrl($url)
371
    {
372 2
        $this->initialized = false;
373 2
        $this->data = array();
374 2
        $this->url = $url;
375 2
    }
376
377
    /**
378
     * Checks if the Url instance is absolute or not.
379
     *
380
     * @return boolean
381
     */
382 19
    public function isAbsolute()
383
    {
384 19
        $this->initialize();
385
386 19
        return $this->scheme && $this->host;
387
    }
388
389
    /**
390
     * @inheritDoc
391
     */
392 15
    public function __toString()
393
    {
394 15
        return $this->getUrl();
395
    }
396
397
    /**
398
     * @inheritDoc
399
     */
400 59
    protected function doInitialize()
401
    {
402 59
        $parts = $this->getParser()->parseUrl($this->url);
403
404 59
        foreach ($parts as $k => $v) {
405 59
            if (!isset($this->data[$k])) {
406 59
                $this->data[$k] = $v;
407 59
            }
408 59
        }
409
410 59
        foreach ($this->data as $key => &$value) {
411 59
            $value = $this->preparePartValue($key, $value);
412 59
        }
413 59
    }
414
415
    /**
416
     * Reconstructs a string URL from an array of parts.
417
     *
418
     * @param array $parts
419
     * @return string $url
420
     */
421 17
    private static function httpBuildUrl(array $parts)
422
    {
423 17
        $relative = self::httpBuildUrlRelative($parts);
424
425 17
        return sprintf('%s://%s%s%s%s',
426 17
            $parts['scheme'],
427 17
            $parts['user'] ? sprintf('%s%s@', $parts['user'], $parts['pass'] ? sprintf(':%s', $parts['pass']) : '') : '',
428 17
            $parts['host'],
429 17
            $parts['port'] ? sprintf(':%d', $parts['port']) : '',
430
            $relative
431 17
        );
432
    }
433
434
    /**
435
     * Reconstructs relative part of URL from an array of parts.
436
     *
437
     * @param array $parts
438
     * @return string $url
439
     */
440 18
    private static function httpBuildUrlRelative(array $parts)
441
    {
442 18
        $parts['path'] = ltrim($parts['path'], '/');
443
444 18
        return sprintf('/%s%s%s',
445 18
            $parts['path'] ? $parts['path'] : '',
446 18
            $parts['query'] ? '?'.$parts['query'] : '',
447 18
            $parts['fragment'] ? '#'.$parts['fragment'] : ''
448 18
        );
449
    }
450
451
    /**
452
     * Creates the default Parser instance to parse urls.
453
     *
454
     * @return Parser
455
     */
456 59
    private static function createDefaultParser()
457
    {
458 59
        $pslManager = new PublicSuffixListManager(dirname(dirname(__DIR__)) . '/data');
459 59
        $pslParser = new PslParser($pslManager->getList());
460
461 59
        return new Parser($pslParser);
462
    }
463
}
464