seosazi /
path-converter
| 1 | <?php |
||||
| 2 | |||||
| 3 | |||||
| 4 | namespace seosazi\PathConverter; |
||||
| 5 | |||||
| 6 | |||||
| 7 | /** |
||||
| 8 | * Convert paths relative with base tag and domain to absolute path |
||||
| 9 | * |
||||
| 10 | * E.g. |
||||
| 11 | * href="style.css" |
||||
| 12 | * and base tag "https://www.seosazi.com/assets/" |
||||
| 13 | * and web page address "https://www.seosazi.com" |
||||
| 14 | * becomes |
||||
| 15 | * https://www.seosazi.com/assets/style.css |
||||
| 16 | * |
||||
| 17 | * Or |
||||
| 18 | *E.g. |
||||
| 19 | * href="../../style.css" |
||||
| 20 | * and base tag "https://www.seosazi.com/assets/files/" |
||||
| 21 | * and web page address "https://www.seosazi.com" |
||||
| 22 | * becomes |
||||
| 23 | * https://www.seosazi.com/style.css |
||||
| 24 | * |
||||
| 25 | * |
||||
| 26 | * Please report bugs on https://github.com/seosazi/path-converter/issues |
||||
| 27 | * |
||||
| 28 | * @author Pouya Pormohamad <[email protected]> |
||||
| 29 | * @copyright Copyright (c) 2020, Pouya Pormohamad. All rights reserved |
||||
| 30 | * @license MIT License |
||||
| 31 | */ |
||||
| 32 | class ConvertToAbsolutePath implements ConverterInterface |
||||
| 33 | { |
||||
| 34 | |||||
| 35 | /** |
||||
| 36 | * @var string |
||||
| 37 | */ |
||||
| 38 | private $pagePath; |
||||
| 39 | /** |
||||
| 40 | * @var string|null |
||||
| 41 | */ |
||||
| 42 | private $baseTag = null; |
||||
| 43 | /** |
||||
| 44 | * @var string|null |
||||
| 45 | */ |
||||
| 46 | private $starterPath = null; |
||||
| 47 | /** |
||||
| 48 | * @var array|false|int|string|null |
||||
| 49 | */ |
||||
| 50 | private $baseTagParsing; |
||||
| 51 | /** |
||||
| 52 | * @var array|false|int|string|null |
||||
| 53 | */ |
||||
| 54 | private $pagePathParsing; |
||||
| 55 | /** |
||||
| 56 | * @var string |
||||
| 57 | */ |
||||
| 58 | private $domain; |
||||
| 59 | /** |
||||
| 60 | * @var string |
||||
| 61 | */ |
||||
| 62 | private $scheme; |
||||
| 63 | |||||
| 64 | /** |
||||
| 65 | * ConvertToAbsolutePath constructor. |
||||
| 66 | * @param $pagePath |
||||
| 67 | * @throws \Exception |
||||
| 68 | */ |
||||
| 69 | public function __construct($pagePath) |
||||
| 70 | { |
||||
| 71 | if ($this->isCorrectUrl(parse_url($pagePath))) { |
||||
| 72 | $this->setPagePath($pagePath); |
||||
| 73 | }else{ |
||||
| 74 | throw new \Exception('path is not correct url'); |
||||
| 75 | } |
||||
| 76 | } |
||||
| 77 | |||||
| 78 | /** |
||||
| 79 | * @inheritDoc |
||||
| 80 | */ |
||||
| 81 | public function convert($path) |
||||
| 82 | { |
||||
| 83 | // Skip converting if the relative url like http://... or android-app://... etc. |
||||
| 84 | if ($this->isPathAbsoluteOrForAnotherApp($path)) { |
||||
| 85 | return $this->checkPathIsAbsoluteOrForAnotherApp($path); |
||||
| 86 | } |
||||
| 87 | // Treat path as invalid if it is like javascript:... etc. |
||||
| 88 | if ($this->isPathJavaScript($path)) { |
||||
| 89 | return ''; |
||||
| 90 | } |
||||
| 91 | // Convert //www.google.com to http://www.google.com |
||||
| 92 | if ($this->isPathWithoutScheme($path)) { |
||||
| 93 | return 'http:' . $path; |
||||
| 94 | } |
||||
| 95 | |||||
| 96 | // If the path is a fragment or query string, |
||||
| 97 | // it will be appended to the base url |
||||
| 98 | if ($this->isHaveQueryOrFragment($path)) { |
||||
| 99 | return $this->getStarterPath() . $path; |
||||
| 100 | } |
||||
| 101 | // Treat paths with doc root, i.e, /about |
||||
| 102 | if ($this->isPathStartWithSlash($path)) { |
||||
| 103 | return $this->onlySitePath($this->getStarterPath()) . $path; |
||||
| 104 | } |
||||
| 105 | // For paths like ./foo, it will be appended to the furthest directory |
||||
| 106 | if ($this->isPathStartWithPointSlash($path)) { |
||||
| 107 | return $this->uptoLastDir($this->getStarterPath()) . substr($path, 2); |
||||
| 108 | } |
||||
| 109 | // Convert paths like ../foo or ../../bar |
||||
| 110 | if ($this->isPathStartWithTwoPointSlash($path)) { |
||||
| 111 | return (new RemovePathWithPointPointSlash($this, $path))->compute(); |
||||
| 112 | } |
||||
| 113 | if (empty($path)) { |
||||
| 114 | return $this->getPagePath(); |
||||
| 115 | } |
||||
| 116 | // else |
||||
| 117 | return $this->uptoLastDir($this->getStarterPath()) . $path; |
||||
| 118 | } |
||||
| 119 | |||||
| 120 | public function onlySitePath($url) { |
||||
| 121 | $parseUrl = parse_url($url); |
||||
| 122 | if ($this->isCorrectUrl($parseUrl)) { |
||||
| 123 | return $parseUrl['scheme'] . '://' . $parseUrl['host']; |
||||
| 124 | } elseif(isset($parseUrl['host'])) { |
||||
| 125 | return $parseUrl['host']; |
||||
| 126 | } else { |
||||
| 127 | return ''; |
||||
| 128 | } |
||||
| 129 | } |
||||
| 130 | |||||
| 131 | // Get the path with last directory |
||||
| 132 | // http://example.com/some/fake/path/page.html => http://example.com/some/fake/path/ |
||||
| 133 | public function upToLastDir($url) { |
||||
| 134 | $parseUrl = parse_url($url); |
||||
| 135 | $path = ''; |
||||
| 136 | if(isset($parseUrl['path'])) { |
||||
| 137 | $path = preg_replace('/\/([^\/]+)$/i', '', $parseUrl['path']); |
||||
| 138 | } |
||||
| 139 | return rtrim($parseUrl['scheme'] . '://' . $parseUrl['host'] . $path, '/') . '/'; |
||||
| 140 | } |
||||
| 141 | |||||
| 142 | |||||
| 143 | public function getPagePath() |
||||
| 144 | { |
||||
| 145 | return $this->pagePath; |
||||
| 146 | } |
||||
| 147 | |||||
| 148 | /** |
||||
| 149 | * @param string $pagePath |
||||
| 150 | */ |
||||
| 151 | public function setPagePath(string $pagePath): void |
||||
| 152 | { |
||||
| 153 | $this->pagePath = $pagePath; |
||||
| 154 | } |
||||
| 155 | |||||
| 156 | |||||
| 157 | public function getBaseTag() |
||||
| 158 | { |
||||
| 159 | return $this->baseTag; |
||||
| 160 | } |
||||
| 161 | |||||
| 162 | /** |
||||
| 163 | * @param string $baseTag |
||||
| 164 | */ |
||||
| 165 | public function setBaseTag(string $baseTag): void |
||||
| 166 | { |
||||
| 167 | $this->baseTag = $baseTag; |
||||
| 168 | } |
||||
| 169 | |||||
| 170 | /** |
||||
| 171 | * @return string |
||||
| 172 | */ |
||||
| 173 | public function getStarterPath(): string |
||||
| 174 | { |
||||
| 175 | if ($this->starterPath===null) { |
||||
| 176 | if ($this->getBaseTag() === null) { |
||||
| 177 | $this->starterPath =$this->getPagePath(); |
||||
| 178 | } elseif (array_key_exists('scheme', $this->getBaseTagParsing())) { |
||||
|
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
| 179 | $this->starterPath = $this->getBaseTag() ; |
||||
| 180 | } else { |
||||
| 181 | $this->starterPath = $this->getPagePathParsing()['scheme'] . '://' . $this->getPagePathParsing()['host'] . |
||||
| 182 | '/' . trim($this->getBaseTag(), '/') . '/'; |
||||
| 183 | } |
||||
| 184 | } |
||||
| 185 | return $this->starterPath; |
||||
| 186 | } |
||||
| 187 | |||||
| 188 | public function getDomain() |
||||
| 189 | { |
||||
| 190 | if ($this->domain === null) { |
||||
| 191 | if ($this->getBaseTag() === null) { |
||||
| 192 | $this->domain = $this->getPagePathParsing()['host'] . '/'; |
||||
| 193 | }elseif (array_key_exists('scheme', $this->getBaseTagParsing())){ |
||||
|
0 ignored issues
–
show
It seems like
$this->getBaseTagParsing() can also be of type integer and string and true; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 194 | $this->domain = $this->getBaseTagParsing()['host'] . '/'; |
||||
| 195 | }else { |
||||
| 196 | $this->domain = $this->getPagePathParsing()['host'] . '/'; |
||||
| 197 | } |
||||
| 198 | } |
||||
| 199 | return $this->domain; |
||||
| 200 | } |
||||
| 201 | |||||
| 202 | public function getScheme() |
||||
| 203 | { |
||||
| 204 | if ($this->scheme === null) { |
||||
| 205 | if ($this->getBaseTag() === null) { |
||||
| 206 | $this->scheme = $this->getPagePathParsing()['scheme']; |
||||
| 207 | }elseif (array_key_exists('scheme', $this->getBaseTagParsing())){ |
||||
|
0 ignored issues
–
show
It seems like
$this->getBaseTagParsing() can also be of type integer and string and true; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
Loading history...
|
|||||
| 208 | $this->scheme = $this->getBaseTagParsing()['scheme']; |
||||
| 209 | }else { |
||||
| 210 | $this->scheme = $this->getPagePathParsing()['scheme']; |
||||
| 211 | } |
||||
| 212 | } |
||||
| 213 | return $this->scheme; |
||||
| 214 | } |
||||
| 215 | |||||
| 216 | public function getBaseTagParsing() |
||||
| 217 | { |
||||
| 218 | if ($this->baseTagParsing == null) { |
||||
| 219 | if (is_string($this->getBaseTag())) { |
||||
| 220 | $this->baseTagParsing = parse_url($this->getBaseTag()); |
||||
| 221 | } else { |
||||
| 222 | $this->baseTagParsing = []; |
||||
| 223 | } |
||||
| 224 | } |
||||
| 225 | return $this->baseTagParsing; |
||||
| 226 | } |
||||
| 227 | |||||
| 228 | public function getPagePathParsing() |
||||
| 229 | { |
||||
| 230 | if ($this->pagePathParsing == null) { |
||||
| 231 | $this->pagePathParsing = parse_url($this->getPagePath()); |
||||
| 232 | } |
||||
| 233 | return $this->pagePathParsing; |
||||
| 234 | } |
||||
| 235 | |||||
| 236 | /** |
||||
| 237 | * @param $path |
||||
| 238 | * @return bool |
||||
| 239 | */ |
||||
| 240 | public function isHaveQueryOrFragment($path): bool |
||||
| 241 | { |
||||
| 242 | return substr($path, 0, 1) == '#' || substr($path, 0, 1) == '?'; |
||||
| 243 | } |
||||
| 244 | |||||
| 245 | /** |
||||
| 246 | * @param $parseUrl |
||||
| 247 | * @return bool |
||||
| 248 | */ |
||||
| 249 | public function isCorrectUrl($parseUrl): bool |
||||
| 250 | { |
||||
| 251 | return isset($parseUrl['scheme']) AND isset($parseUrl['host']); |
||||
| 252 | } |
||||
| 253 | |||||
| 254 | /** |
||||
| 255 | * @param $path |
||||
| 256 | * @return string |
||||
| 257 | */ |
||||
| 258 | public function checkPathIsAbsoluteOrForAnotherApp($path): string |
||||
| 259 | { |
||||
| 260 | if (preg_match('/services:\/\//i', $path)) { |
||||
| 261 | return ''; |
||||
| 262 | } |
||||
| 263 | if (preg_match('/whatsapp:\/\//i', $path)) { |
||||
| 264 | return ''; |
||||
| 265 | } |
||||
| 266 | if (preg_match('/tel:/i', $path)) { |
||||
| 267 | return ''; |
||||
| 268 | } |
||||
| 269 | return $path; |
||||
| 270 | } |
||||
| 271 | |||||
| 272 | /** |
||||
| 273 | * @param $path |
||||
| 274 | * @return false|int |
||||
| 275 | */ |
||||
| 276 | public function isPathAbsoluteOrForAnotherApp($path) |
||||
| 277 | { |
||||
| 278 | return preg_match('/[a-z0-9-]{1,}(:\/\/)/i', $path); |
||||
| 279 | } |
||||
| 280 | |||||
| 281 | /** |
||||
| 282 | * @param $path |
||||
| 283 | * @return false|int |
||||
| 284 | */ |
||||
| 285 | public function isPathJavaScript($path) |
||||
| 286 | { |
||||
| 287 | return preg_match('/^[a-zA-Z]{0,}:[^\/]{0,1}/i', $path); |
||||
| 288 | } |
||||
| 289 | |||||
| 290 | /** |
||||
| 291 | * @param $path |
||||
| 292 | * @return bool |
||||
| 293 | */ |
||||
| 294 | public function isPathWithoutScheme($path): bool |
||||
| 295 | { |
||||
| 296 | return substr($path, 0, 2) == '//'; |
||||
| 297 | } |
||||
| 298 | |||||
| 299 | /** |
||||
| 300 | * @param $path |
||||
| 301 | * @return bool |
||||
| 302 | */ |
||||
| 303 | public function isPathStartWithSlash($path): bool |
||||
| 304 | { |
||||
| 305 | return substr($path, 0, 1) == '/'; |
||||
| 306 | } |
||||
| 307 | |||||
| 308 | /** |
||||
| 309 | * @param $path |
||||
| 310 | * @return bool |
||||
| 311 | */ |
||||
| 312 | public function isPathStartWithPointSlash($path): bool |
||||
| 313 | { |
||||
| 314 | return substr($path, 0, 2) == './'; |
||||
| 315 | } |
||||
| 316 | |||||
| 317 | /** |
||||
| 318 | * @param $path |
||||
| 319 | * @return bool |
||||
| 320 | */ |
||||
| 321 | public function isPathStartWithTwoPointSlash($path): bool |
||||
| 322 | { |
||||
| 323 | return substr($path, 0, 3) == '../'; |
||||
| 324 | } |
||||
| 325 | } |