Completed
Push — master ( b0bce0...c1c6ff )
by ignace nyamagana
04:32
created

src/Modifiers/Relativize.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * League.Uri (http://uri.thephpleague.com)
4
 *
5
 * @package   League.uri
6
 * @author    Ignace Nyamagana Butera <[email protected]>
7
 * @copyright 2016 Ignace Nyamagana Butera
8
 * @license   https://github.com/thephpleague/uri/blob/master/LICENSE (MIT License)
9
 * @version   4.2.0
10
 * @link      https://github.com/thephpleague/uri/
11
 */
12
namespace League\Uri\Modifiers;
13
14
use League\Uri\Interfaces\Uri;
15
use Psr\Http\Message\UriInterface;
16
17
/**
18
 * Resolve an URI according to a base URI using
19
 * RFC3986 rules
20
 *
21
 * @package League.uri
22
 * @author  Ignace Nyamagana Butera <[email protected]>
23
 * @since   4.2.0
24
 */
25
class Relativize extends AbstractUriModifier
26
{
27
    /**
28
     * Base URI
29
     *
30
     * @var Uri|UriInterface
31
     */
32
    protected $uri;
33
34
    /**
35
     * New instance
36
     *
37
     * @param Uri|UriInterface $uri
38
     */
39 102
    public function __construct($uri)
40
    {
41 102
        $this->assertUriObject($uri);
42 102
        $this->uri = $this->hostToAscii($uri);
0 ignored issues
show
Documentation Bug introduced by
It seems like $this->hostToAscii($uri) can also be of type object<League\Uri\Interfaces\Uri>. However, the property $uri is declared as type object<Psr\Http\Message\UriInterface>. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

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

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
43 102
    }
44
45
    /**
46
     * Convert the Uri host component to its ascii version
47
     *
48
     * @param Uri|UriInterface $uri
49
     *
50
     * @return Uri|UriInterface
51
     */
52 102
    protected function hostToAscii($uri)
53
    {
54 102
        static $modifier;
55 102
        if (null === $modifier) {
56 3
            $modifier = new HostToAscii();
57 2
        }
58
59 102
        return $modifier($uri);
60
    }
61
62
    /**
63
     * Return a Uri object modified according to the modifier
64
     *
65
     * @param Uri|UriInterface $payload
66
     *
67
     * @return Uri|UriInterface
68
     */
69 102
    public function __invoke($payload)
70
    {
71 102
        $this->assertUriObject($payload);
72 102
        $payload_normalized = $this->hostToAscii($payload);
73
74 102
        if ($this->uri->getScheme() !== $payload_normalized->getScheme()
75 102
            || $this->uri->getAuthority() !== $payload_normalized->getAuthority()
76 68
        ) {
77 33
            return $payload;
78
        }
79
80 69
        $path = $this->relativizePath($payload->getPath());
81
82
        return $payload
83 69
            ->withScheme('')
84 69
            ->withPort(null)
85 69
            ->withUserInfo('')
86 69
            ->withHost('')
87 69
            ->withPath($this->formatPath($path));
88
    }
89
90
    /**
91
     * Relative the URI for a authority-less payload URI
92
     *
93
     * @param string $path
94
     *
95
     * @return string
96
     */
97 69
    protected function relativizePath($path)
98
    {
99 69
        $segments = $this->getSegments($path);
100 69
        $basename = array_pop($segments);
101 69
        $basePath = $this->uri->getPath();
102 69
        if ($basePath === $path) {
103 18
            return $basename;
104
        }
105
106 51
        $baseSegments = $this->getSegments($basePath);
107 51
        array_pop($baseSegments);
108 51
        foreach ($baseSegments as $offset => $segment) {
109 48
            if (!isset($segments[$offset]) || $segment !== $segments[$offset]) {
110 24
                break;
111
            }
112 33
            unset($baseSegments[$offset], $segments[$offset]);
113 34
        }
114 51
        $segments[] = $basename;
115
116 51
        return str_repeat('../', count($baseSegments)).implode('/', $segments);
117
    }
118
119
    /**
120
     * returns the path segments
121
     *
122
     * @param string $path
123
     *
124
     * @return array
125
     */
126 69
    protected function getSegments($path)
127
    {
128 69
        if ('' !== $path && '/' === $path[0]) {
129 66
            $path = substr($path, 1);
130 44
        }
131
132 69
        return explode('/', $path);
133
    }
134
135
    /**
136
     * Post formatting the path to keep a valid URI
137
     *
138
     * @param string $path
139
     *
140
     * @return string
141
     */
142 69
    protected function formatPath($path)
143
    {
144 69
        if ('' === $path) {
145 9
            $basePath = $this->uri->getPath();
146
147 9
            return in_array($basePath, ['', '/']) ? $basePath : './';
148
        }
149
150 60
        if (false === ($colonPos = strpos($path, ':'))) {
151 54
            return $path;
152
        }
153
154 6
        $slashPos = strpos($path, '/');
155 6
        if (false === $slashPos || $colonPos < $slashPos) {
156 3
            return "./$path";
157
        }
158
159 3
        return $path;
160
    }
161
}
162