Completed
Push — master ( cee824...b1d97d )
by Robbert
06:12 queued 02:02
created

Url   B

Complexity

Total Complexity 47

Size/Duplication

Total Lines 214
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 0

Test Coverage

Coverage 85%

Importance

Changes 10
Bugs 4 Features 3
Metric Value
wmc 47
c 10
b 4
f 3
lcom 1
cbo 0
dl 0
loc 214
ccs 51
cts 60
cp 0.85
rs 8.439

17 Methods

Rating   Name   Duplication   Size   Complexity  
A importUrlComponents() 0 6 2
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 10 2
B __toString() 0 18 6
A __get() 0 11 3
A __set() 0 15 4
A __clone() 0 6 2
B getPath() 0 13 5
A getFilePath() 0 10 3

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