| Total Complexity | 43 |
| Total Lines | 266 |
| Duplicated Lines | 0 % |
| Changes | 1 | ||
| Bugs | 0 | Features | 0 |
Complex classes like Path 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.
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 Path, and based on these observations, apply Extract Interface, too.
| 1 | <?php |
||
| 26 | class Path |
||
| 27 | { |
||
| 28 | /** |
||
| 29 | * Path::$string |
||
| 30 | * |
||
| 31 | * @var string |
||
| 32 | */ |
||
| 33 | private $string; |
||
| 34 | |||
| 35 | /** |
||
| 36 | * Path::__construct |
||
| 37 | * |
||
| 38 | * @var array |
||
| 39 | */ |
||
| 40 | private $segments; |
||
| 41 | |||
| 42 | // ------------------------------------------------------------------------ |
||
| 43 | |||
| 44 | /** |
||
| 45 | * Path::__construct |
||
| 46 | * |
||
| 47 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||
| 48 | */ |
||
| 49 | public function __construct() |
||
| 50 | { |
||
| 51 | if (function_exists('config')) { |
||
| 52 | $protocol = strtoupper(config('uri')->offsetGet('protocol')); |
||
| 53 | } |
||
| 54 | |||
| 55 | empty($protocol) && $protocol = 'REQUEST_URI'; |
||
| 56 | |||
| 57 | switch ($protocol) { |
||
| 58 | case 'AUTO': |
||
| 59 | case 'REQUEST_URI': |
||
| 60 | $this->string = $this->parseRequestUri(); |
||
| 61 | break; |
||
| 62 | case 'QUERY_STRING': |
||
| 63 | $this->string = $this->parseQueryString(); |
||
| 64 | break; |
||
| 65 | case 'PATH_INFO': |
||
| 66 | default: |
||
| 67 | $this->string = isset($_SERVER[ $protocol ]) |
||
| 68 | ? $_SERVER[ $protocol ] |
||
| 69 | : $this->parseRequestUri(); |
||
| 70 | break; |
||
| 71 | } |
||
| 72 | |||
| 73 | // Filter out control characters and trim slashes |
||
| 74 | $this->string = trim(remove_invisible_characters($this->string, false), '/'); |
||
| 75 | $this->setSegments(explode('/', $this->string)); |
||
| 76 | } |
||
| 77 | |||
| 78 | // ------------------------------------------------------------------------ |
||
| 79 | |||
| 80 | /** |
||
| 81 | * Path::parseRequestUri |
||
| 82 | * |
||
| 83 | * Parse REQUEST_URI |
||
| 84 | * |
||
| 85 | * Will parse REQUEST_URI and automatically detect the URI from it, |
||
| 86 | * while fixing the query string if necessary. |
||
| 87 | * |
||
| 88 | * @access protected |
||
| 89 | * @return string |
||
| 90 | */ |
||
| 91 | protected function parseRequestUri() |
||
| 134 | } |
||
| 135 | |||
| 136 | // ------------------------------------------------------------------------ |
||
| 137 | |||
| 138 | /** |
||
| 139 | * Path::removeRelativeDirectory |
||
| 140 | * |
||
| 141 | * Remove relative directory (../) and multi slashes (///) |
||
| 142 | * |
||
| 143 | * Do some final cleaning of the URI and return it, currently only used in self::parseRequestURI() |
||
| 144 | * |
||
| 145 | * @param string $uri URI String |
||
| 146 | * |
||
| 147 | * @access protected |
||
| 148 | * @return string |
||
| 149 | */ |
||
| 150 | protected function removeRelativeDirectory($uri) |
||
| 151 | { |
||
| 152 | $segments = []; |
||
| 153 | $segment = strtok($uri, '/'); |
||
| 154 | |||
| 155 | $base_dirs = explode('/', str_replace('\\', '/', PATH_ROOT)); |
||
|
|
|||
| 156 | |||
| 157 | while ($segment !== false) { |
||
| 158 | if (( ! empty($segment) || $segment === '0') AND |
||
| 159 | $segment !== '..' AND |
||
| 160 | ! in_array( |
||
| 161 | $segment, |
||
| 162 | $base_dirs |
||
| 163 | ) |
||
| 164 | ) { |
||
| 165 | $segments[] = $segment; |
||
| 166 | } |
||
| 167 | $segment = strtok('/'); |
||
| 168 | } |
||
| 169 | |||
| 170 | return implode('/', $segments); |
||
| 171 | } |
||
| 172 | |||
| 173 | // ------------------------------------------------------------------------ |
||
| 174 | |||
| 175 | /** |
||
| 176 | * Path::parseQueryString |
||
| 177 | * |
||
| 178 | * Parse QUERY_STRING |
||
| 179 | * |
||
| 180 | * Will parse QUERY_STRING and automatically detect the URI from it. |
||
| 181 | * |
||
| 182 | * @access protected |
||
| 183 | * @return string |
||
| 184 | */ |
||
| 185 | protected function parseQueryString() |
||
| 186 | { |
||
| 187 | $uri = isset($_SERVER[ 'QUERY_STRING' ]) |
||
| 188 | ? $_SERVER[ 'QUERY_STRING' ] |
||
| 189 | : @getenv('QUERY_STRING'); |
||
| 190 | |||
| 191 | if (trim($uri, '/') === '') { |
||
| 192 | return ''; |
||
| 193 | } elseif (strncmp($uri, '/', 1) === 0) { |
||
| 194 | $uri = explode('?', $uri, 2); |
||
| 195 | $_SERVER[ 'QUERY_STRING' ] = isset($uri[ 1 ]) |
||
| 196 | ? $uri[ 1 ] |
||
| 197 | : ''; |
||
| 198 | $uri = rawurldecode($uri[ 0 ]); |
||
| 199 | } |
||
| 200 | |||
| 201 | parse_str($_SERVER[ 'QUERY_STRING' ], $_GET); |
||
| 202 | |||
| 203 | return $this->removeRelativeDirectory($uri); |
||
| 204 | } |
||
| 205 | |||
| 206 | // -------------------------------------------------------------------- |
||
| 207 | |||
| 208 | /** |
||
| 209 | * Path::setSegments |
||
| 210 | * |
||
| 211 | * @param array $segments |
||
| 212 | * |
||
| 213 | * @throws \O2System\Spl\Exceptions\RuntimeException |
||
| 214 | */ |
||
| 215 | public function setSegments(array $segments) |
||
| 216 | { |
||
| 217 | $validSegments = []; |
||
| 218 | |||
| 219 | if (count($segments)) { |
||
| 220 | foreach ($segments as $key => $segment) { |
||
| 221 | // Filter segments for security |
||
| 222 | if ($segment = trim($this->filterSegment($segment))) { |
||
| 223 | if (false !== ($language = language()->registered($segment))) { |
||
| 224 | language()->setDefault($segment); |
||
| 225 | |||
| 226 | continue; |
||
| 227 | } else { |
||
| 228 | $validSegments[] = $segment; |
||
| 229 | } |
||
| 230 | } |
||
| 231 | } |
||
| 232 | } |
||
| 233 | |||
| 234 | $validSegments = array_filter($validSegments); |
||
| 235 | array_unshift($validSegments, null); |
||
| 236 | |||
| 237 | unset($validSegments[ 0 ]); |
||
| 238 | |||
| 239 | $this->segments = $validSegments; |
||
| 240 | $this->string = implode('/', $this->segments); |
||
| 241 | } |
||
| 242 | |||
| 243 | // ------------------------------------------------------------------------ |
||
| 244 | |||
| 245 | /** |
||
| 246 | * Path::filterSegment |
||
| 247 | * |
||
| 248 | * Filters segments for malicious characters. |
||
| 249 | * |
||
| 250 | * @param string $segment URI String |
||
| 251 | * |
||
| 252 | * @return mixed |
||
| 253 | * @throws RuntimeException |
||
| 254 | */ |
||
| 255 | protected function filterSegment($segment) |
||
| 256 | { |
||
| 257 | if (function_exists('config')) { |
||
| 258 | $config = config('uri'); |
||
| 259 | } else { |
||
| 260 | $config = new SplArrayObject([ |
||
| 261 | 'permittedChars' => 'a-z 0-9~%.:_\-@#', |
||
| 262 | 'suffix' => null, |
||
| 263 | ]); |
||
| 264 | } |
||
| 265 | |||
| 266 | if ( ! empty($segment) AND |
||
| 267 | ! empty($config->offsetGet('permittedChars')) AND |
||
| 268 | ! preg_match('/^[' . $config->offsetGet('permittedChars') . ']+$/i', $segment) AND |
||
| 269 | ! is_cli() |
||
| 270 | ) { |
||
| 271 | throw new RuntimeException('E_URI_HAS_DISALLOWED_CHARACTERS', 105); |
||
| 272 | } |
||
| 273 | |||
| 274 | // Convert programatic characters to entities and return |
||
| 275 | return str_replace( |
||
| 276 | ['$', '(', ')', '%28', '%29', $config->offsetGet('suffix')], // Bad |
||
| 277 | ['$', '(', ')', '(', ')', ''], // Good |
||
| 278 | $segment |
||
| 279 | ); |
||
| 280 | } |
||
| 281 | |||
| 282 | // ------------------------------------------------------------------------ |
||
| 283 | |||
| 284 | /** |
||
| 285 | * Path::getTotalSegments |
||
| 286 | * |
||
| 287 | * @return int |
||
| 288 | */ |
||
| 289 | public function getTotalSegments() |
||
| 292 | } |
||
| 293 | } |