ConvertToAbsolutePath   B
last analyzed

Complexity

Total Complexity 51

Size/Duplication

Total Lines 292
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 94
c 5
b 0
f 0
dl 0
loc 292
rs 7.92
wmc 51

22 Methods

Rating   Name   Duplication   Size   Complexity  
A getPagePath() 0 3 1
A getBaseTag() 0 3 1
A setBaseTag() 0 3 1
A setPagePath() 0 3 1
A upToLastDir() 0 7 2
B convert() 0 37 9
A isPathJavaScript() 0 3 1
A isPathAbsoluteOrForAnotherApp() 0 3 1
A getBaseTagParsing() 0 10 3
A getStarterPath() 0 13 4
A isPathStartWithSlash() 0 3 1
A getScheme() 0 12 4
A isPathStartWithPointSlash() 0 3 1
A isCorrectUrl() 0 3 2
A onlySitePath() 0 8 3
A getPagePathParsing() 0 6 2
A getDomain() 0 12 4
A isPathWithoutScheme() 0 3 1
A isHaveQueryOrFragment() 0 3 2
A __construct() 0 6 2
A isPathStartWithTwoPointSlash() 0 3 1
A checkPathIsAbsoluteOrForAnotherApp() 0 12 4

How to fix   Complexity   

Complex Class

Complex classes like ConvertToAbsolutePath 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 ConvertToAbsolutePath, and based on these observations, apply Extract Interface, too.

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
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 ignore-type  annotation

178
            } elseif (array_key_exists('scheme', /** @scrutinizer ignore-type */ $this->getBaseTagParsing())) {
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
Bug introduced by
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 ignore-type  annotation

193
            }elseif (array_key_exists('scheme', /** @scrutinizer ignore-type */ $this->getBaseTagParsing())){
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
Bug introduced by
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 ignore-type  annotation

207
            }elseif (array_key_exists('scheme', /** @scrutinizer ignore-type */ $this->getBaseTagParsing())){
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
}