Completed
Push — develop ( 114abf...08add9 )
by Jaap
03:44
created

Dsn::parse()   B

Complexity

Conditions 6
Paths 11

Size

Total Lines 40
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 22
nc 11
nop 1
dl 0
loc 40
rs 8.439
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of phpDocumentor.
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 *
8
 * @copyright 2010-2015 Mike van Riel<[email protected]>
9
 * @license   http://www.opensource.org/licenses/mit-license.php MIT
10
 * @link      http://phpdoc.org
11
 */
12
13
namespace phpDocumentor\DomainModel;
14
15
/**
16
 * Value Object for DSN.
17
 */
18
final class Dsn
19
{
20
    /** @var string */
21
    private $dsn;
22
23
    /** @var string */
24
    private $scheme;
25
26
    /** @var string */
27
    private $host;
28
29
    /** @var int */
30
    private $port;
31
32
    /** @var string */
33
    private $user;
34
35
    /** @var string */
36
    private $password;
37
38
    /** @var string */
39
    private $path;
40
41
    /** @var string[]  */
42
    private $query = [];
43
44
    /** @var string[]  */
45
    private $parameters = [];
46
47
    /**
48
     * Initializes the Dsn
49
     *
50
     * @param string $dsn
51
     */
52
    public function __construct($dsn)
53
    {
54
        $this->parse($dsn);
55
    }
56
57
    /**
58
     * Returns a string representation of the DSN.
59
     *
60
     * @return string
61
     */
62
    public function __toString()
63
    {
64
        return $this->dsn;
65
    }
66
67
    /**
68
     * Returns the scheme part of the DSN
69
     *
70
     * @return string
71
     */
72
    public function getScheme()
73
    {
74
        return $this->scheme;
75
    }
76
77
    /**
78
     * Returns the host part of the DSN
79
     *
80
     * @return string
81
     */
82
    public function getHost()
83
    {
84
        return $this->host;
85
    }
86
87
    /**
88
     * Returns the port part of the DSN
89
     *
90
     * @return int
91
     */
92
    public function getPort()
93
    {
94
        return $this->port;
95
    }
96
97
    /**
98
     * Returns the username part of the DSN
99
     *
100
     * @return string
101
     */
102
    public function getUsername()
103
    {
104
        return $this->user;
105
    }
106
107
    /**
108
     * Returns the password part of the DSN
109
     *
110
     * @return string
111
     */
112
    public function getPassword()
113
    {
114
        return $this->password;
115
    }
116
117
    /**
118
     * Returns the path part of the DSN
119
     *
120
     * @return string
121
     */
122
    public function getPath()
123
    {
124
        return new Path($this->path);
125
    }
126
127
    /**
128
     * Returns the query part of the DSN
129
     *
130
     * @return string[]
131
     */
132
    public function getQuery()
133
    {
134
        return $this->query;
135
    }
136
137
    /**
138
     * Returns the parameters part of the DSN
139
     *
140
     * @return string[]
141
     */
142
    public function getParameters()
143
    {
144
        return $this->parameters;
145
    }
146
147
    /**
148
     * Parses the given DSN
149
     *
150
     * @param string $dsn
151
     * @return void
152
     */
153
    private function parse($dsn)
0 ignored issues
show
Complexity introduced by
This operation has 200 execution paths which exceeds the configured maximum of 200.

A high number of execution paths generally suggests many nested conditional statements and make the code less readible. This can usually be fixed by splitting the method into several smaller methods.

You can also find more information in the “Code” section of your repository.

Loading history...
154
    {
155
        if (! is_string($dsn)) {
156
            throw new \InvalidArgumentException(
157
                sprintf('"%s" is not a valid DSN.', var_export($dsn, true))
158
            );
159
        }
160
161
        $dsnParts = explode(';', $dsn);
162
        $location = $dsnParts[0];
163
        unset($dsnParts[0]);
164
        $locationParts = parse_url($location);
165
166
        if (! isset($locationParts['scheme'])) {
167
            $locationParts['scheme'] = 'file';
168
            $location = 'file://' . $location;
169
        }
170
171
        if (! filter_var($location, FILTER_VALIDATE_URL)) {
172
            throw new \InvalidArgumentException(
173
                sprintf('"%s" is not a valid DSN.', $dsn)
174
            );
175
        }
176
177
        $this->parseDsn($location, $dsnParts);
178
179
        $this->parseScheme($locationParts);
0 ignored issues
show
Documentation introduced by
$locationParts is of type array<string,string>|false, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
180
181
        $this->parseHostAndPath($locationParts);
0 ignored issues
show
Documentation introduced by
$locationParts is of type array<string,string>|false, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
182
183
        $this->parsePort($locationParts);
0 ignored issues
show
Documentation introduced by
$locationParts is of type array<string,string>|false, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
184
185
        $this->user = isset($locationParts['user']) ? $locationParts['user'] : "";
186
187
        $this->password = isset($locationParts['pass']) ? $locationParts['pass'] : "";
188
189
        $this->parseQuery($locationParts);
0 ignored issues
show
Documentation introduced by
$locationParts is of type array<string,string>|false, but the function expects a array<integer,string>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
190
191
        $this->parseParameters($dsnParts);
192
    }
193
194
    /**
195
     * Reconstructs the original DSN but
196
     * when scheme was omitted in the original DSN, it will now be file://
197
     *
198
     * @param string $location
199
     * @param string[] $dsnParts
200
     * @return void
201
     */
202
    private function parseDsn($location, array $dsnParts)
203
    {
204
        array_splice($dsnParts, 0, 0, $location);
205
        $this->dsn = implode(";", $dsnParts);
206
    }
207
208
    /**
209
     * validates and sets the scheme property
210
     *
211
     * @param string[] $locationParts
212
     * @return void
213
     */
214
    private function parseScheme(array $locationParts)
215
    {
216
        $validSchemes = ['file', 'git+http', 'git+https'];
217
        if (! in_array(strtolower($locationParts['scheme']), $validSchemes)) {
218
            throw new \InvalidArgumentException(
219
                sprintf('"%s" is not a valid scheme.', $locationParts['scheme'])
220
            );
221
        }
222
        $this->scheme = strtolower($locationParts['scheme']);
223
    }
224
225
    /**
226
     * Validates and sets the host and path properties
227
     *
228
     * @param string[] $locationParts
229
     * @return void
230
     */
231
    private function parseHostAndPath(array $locationParts)
232
    {
233
        $path = isset($locationParts['path']) ? $locationParts['path'] : "";
234
        $host = isset($locationParts['host']) ? $locationParts['host'] : "";
235
236
        if ($this->getScheme() === 'file') {
237
            $this->path = $host . $path;
238
        } else {
239
            $this->host = $host;
240
            $this->path = $path;
241
        }
242
    }
243
244
    /**
245
     * Validates and sets the port property
246
     *
247
     * @param string[] $locationParts
248
     * @return void
249
     */
250
    private function parsePort(array $locationParts)
251
    {
252
        if (! isset($locationParts['port'])) {
253
            if ($this->getScheme() === 'git+http') {
254
                $this->port = 80;
255
            } elseif ($this->getScheme() === 'git+https') {
256
                $this->port = 443;
257
            } else {
258
                $this->port = 0;
259
            }
260
        } else {
261
            $this->port = $locationParts['port'];
0 ignored issues
show
Documentation Bug introduced by
The property $port was declared of type integer, but $locationParts['port'] is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
262
        }
263
    }
264
265
    /**
266
     * validates and sets the query property
267
     *
268
     * @param string[] $locationParts
269
     * @return void
270
     */
271
    private function parseQuery(array $locationParts)
272
    {
273
        if (isset($locationParts['query'])) {
274
            $queryParts = explode('&', $locationParts['query']);
275
            foreach ($queryParts as $part) {
276
                $option = $this->splitKeyValuePair($part);
277
278
                $this->query[$option[0]] = $option[1];
279
            }
280
        }
281
    }
282
283
    /**
284
     * validates and sets the parameters property
285
     *
286
     * @param string[] $dsnParts
287
     * @return void
288
     */
289
    private function parseParameters(array $dsnParts)
290
    {
291
        foreach ($dsnParts as $part) {
292
            $option = $this->splitKeyValuePair($part);
293
294
            $this->parameters[$option[0]] = $option[1];
295
        }
296
    }
297
298
    /**
299
     * Splits a key-value pair
300
     *
301
     * @param string $pair
302
     * @return string[] $option
303
     */
304
    private function splitKeyValuePair($pair)
305
    {
306
        $option = explode('=', $pair);
307
        if (count($option) !== 2) {
308
            throw new \InvalidArgumentException(
309
                sprintf('"%s" is not a valid query or parameter.', $pair)
310
            );
311
        }
312
313
        return $option;
314
    }
315
}
316