Url   B
last analyzed

Complexity

Total Complexity 49

Size/Duplication

Total Lines 218
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 75.64%

Importance

Changes 0
Metric Value
wmc 49
lcom 1
cbo 0
dl 0
loc 218
ccs 59
cts 78
cp 0.7564
rs 8.48
c 0
b 0
f 0

17 Methods

Rating   Name   Duplication   Size   Complexity  
A getSchemeAndAuthority() 0 5 3
A getScheme() 0 4 2
A getAuthority() 0 4 2
A getUser() 0 4 2
A getPassword() 0 4 3
A getPort() 0 4 2
A getQuery() 0 7 2
A getLdapQuery() 0 7 2
A getFragment() 0 4 2
A __construct() 0 14 4
A __toString() 0 18 6
A __get() 0 11 3
A __set() 0 15 4
A __clone() 0 6 2
A getPath() 0 13 5
A getFilePath() 0 10 3
A importUrlComponents() 0 6 2

How to fix   Complexity   

Complex Class

Complex classes like Url often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Url, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/*
4
 * This file is part of the Ariadne Component Library.
5
 *
6
 * (c) Muze <[email protected]>
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 arc\url;
13
14
/**
15
 * Url parses a URL string and returns an object with the seperate parts. You can change
16
 * these and when cast to a string Url will regenerate the URL string and make sure it
17
 * is valid.
18
 *
19
 * Usage:
20
 *    $url = new \arc\url\Url( 'http://www.ariadne-cms.org/' );
21
 *    $url->path = '/docs/search/';
22
 *    $url->query = 'a=1&a=2';
23
 *    echo $url; // => 'http://www.ariadne-cms.org/docs/search/?a=1&a=2'
24
 * @property Query $query The query arguments
25
 */
26
class Url
27
{
28
    /**
29
     * All parts of the URL format, as returned by parse_url.
30
     * scheme://user:pass@host:port/path?query#fragment
31
     */
32
    public $scheme, $user, $pass, $host, $port, $path, $fragment;
33
    private $query;
34
35
    /**
36
     * @param string $url The URL to parse, the query part will remain a string.
37
     * @param QueryInterface queryObject Optional. An object that parses the query string.
38
     */
39 48
    public function __construct($url, $queryObject = null)
40
    {
41
        $componentList = [
42 48
            'scheme', 'host', 'port', 'user', 'pass', 'path', 'query', 'fragment'
43
        ];
44 48
        $this->importUrlComponents( parse_url( $url ), $componentList );
45 48
        if ($this->scheme!='ldap' && strpos($this->host, ':')) {
46
            // parse_url allows ':' in host when it occurs more than once\
47 2
            $this->host = substr($this->host, 0, strpos($this->host, ':'));
48
        }
49 48
        if ( isset( $queryObject ) ) {
50 48
            $this->query = $queryObject->import( $this->query );
51
        }
52 48
    }
53
54
    /**
55
     * @return string
56
     */
57 42
    public function __toString()
58
    {
59 42
        switch ($this->scheme) {
60 42
            case 'file':
61 2
                return $this->getScheme() . '//' . $this->host . $this->getFilePath();
62
            break;
63 42
            case 'mailto':
64 42
            case 'news':
65 2
                return ( $this->path ? $this->getScheme() . $this->getPath() : '' );
66
            break;
67 42
            case 'ldap':
68 2
                return $this->getSchemeAndAuthority() . $this->getPath() . $this->getLdapQuery();
69
            break;
70
            default:
71 42
                return $this->getSchemeAndAuthority() . $this->getPath() . $this->getQuery() . $this->getFragment();
72
            break;
73
        }
74
    }
75
76
    /**
77
     * @param $name
78
     * @return mixed
79
     */
80 20
    public function __get($name)
81
    {
82 20
        switch ( (string) $name ) {
83 20
            case 'password':
84
                return $this->pass;
85
            break;
86 20
            case 'query':
87 20
                return $this->query;
88
            break;
89
        }
90
    }
91
92
    /**
93
     * @param $name
94
     * @param $value
95
     */
96
    public function __set($name, $value)
97
    {
98
        switch ( (string) $name ) {
99
            case 'password':
100
                $this->pass = $value;
101
            break;
102
            case 'query':
103
                if ( is_object( $this->query ) ) {
104
                    $this->query->reset()->import( $value );
105
                } else {
106
                    $this->query = $value;
107
                }
108
            break;
109
        }
110
    }
111
112
    /**
113
     *
114
     */
115
    public function __clone()
116
    {
117
        if ( is_object( $this->query ) ) {
118
            $this->query = clone $this->query;
119
        }
120
    }
121
122
    /**
123
     * @param $components
124
     * @param $validComponents
125
     */
126 48
    private function importUrlComponents($components, $validComponents)
127
    {
128 48
        array_walk( $validComponents, function ($componentName) use ($components) {
129 48
            $this->{$componentName} = ( isset( $components[$componentName] ) ? $components[$componentName] : '' );
130 48
        } );
131 48
    }
132
133
    /**
134
     * @return string
135
     */
136 42
    private function getSchemeAndAuthority()
137
    {
138
        // note: both '//google.com/' and 'file:///C:/' are valid URL's - so if either a scheme or host is set, add the // part
139 42
        return ( ( $this->scheme || $this->host ) ? $this->getScheme() . $this->getAuthority() : '' );
140
    }
141
142
    /**
143
     * @return string
144
     */
145 38
    private function getScheme()
146
    {
147 38
        return ( $this->scheme ? $this->scheme . ':' : '' );
148
    }
149
150
    /**
151
     * @return string
152
     */
153 38
    private function getAuthority()
154
    {
155 38
        return ( $this->host ? '//' . $this->getUser() . $this->host . $this->getPort() : '' );
156
    }
157
158
    /**
159
     * @return string
160
     */
161 38
    private function getUser()
162
    {
163 38
        return ( $this->user ? rawurlencode( $this->user ) . $this->getPassword() . '@' : '' );
164
    }
165
166
    /**
167
     * @return string
168
     */
169 6
    private function getPassword()
170
    {
171 6
        return ( $this->user && $this->pass ?  ':' . rawurlencode( $this->pass ) : '' );
172
    }
173
174
    /**
175
     * @return string
176
     */
177 38
    private function getPort()
178
    {
179 38
        return ( $this->port ? ':' . (int) $this->port : '' );
180
    }
181
182
    /**
183
     * @return mixed|string
184
     */
185 42
    private function getPath()
186
    {
187 42
        if (!$this->path) {
188 6
            return '';
189
        }
190 38
        $path = $this->path;
191 38
        if ( $this->host && ( !$path || $path[0] !== '/' ) ) {
192
            // note: if a host is set, the path _must_ be made absolute or the URL will be invalid
193
            $path = '/' . $path;
194
        }
195
        // urlencode encodes too many characters for the path part, so we decode them back to get readable urls.
196 38
        return str_replace( [ '%3D', '%2B', '%3A', '%40', '%7E'], [ '=', '+', ':', '@', '~'], $path = join( '/', array_map( 'urlencode', explode( '/', $path ) ) ) );
197
    }
198
199
    /**
200
     * @return mixed|string
201
     */
202 2
    private function getFilePath()
203
    {
204
        // in the file: scheme, a path must start with a '/' even if no host is set. This contradicts with the email: scheme.
205 2
        $path = $this->getPath();
206 2
        if ($path && $path[0]!=='/') {
207 2
            $path = '/' . $path;
208
        }
209
210 2
        return $path;
211
    }
212
213
    /**
214
     * @return string
215
     */
216 42
    private function getQuery()
217
    {
218
        // queries are assumed to handle themselves, so no encoding here.
219 42
        $query = (string) $this->query; // convert explicitly to string first, because the query object may exist but still return an empty string
220
221 42
        return ( $query ? '?' . $query : '' );
222
    }
223
224
    /**
225
     * @return string
226
     */
227 2
    private function getLdapQuery()
228
    {
229
        // ldap queries may contain multiple ? tokens - so these are unencoded here.
230 2
        $query = (string) $this->query; // convert explicitly to string first, because the query object may exist but still return an empty string
231
232 2
        return ( $query ? '?' . str_replace( '%3F', '?', $query ) : '' );
233
    }
234
235
    /**
236
     * @return string
237
     */
238 42
    private function getFragment()
239
    {
240 42
        return ( $this->fragment ? '#' . urlencode($this->fragment) : '' );
241
    }
242
243
}
244