Completed
Push — master ( e53be3...abc481 )
by Lars
03:12
created

Url::fromCurrent()   F

Complexity

Conditions 14
Paths 270

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 14

Importance

Changes 0
Metric Value
dl 0
loc 49
ccs 24
cts 24
cp 1
rs 3.8215
c 0
b 0
f 0
cc 14
eloc 29
nc 270
nop 0
crap 14

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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