UnixFileSystem   B
last analyzed

Complexity

Total Complexity 44

Size/Duplication

Total Lines 269
Duplicated Lines 0 %

Test Coverage

Coverage 83.33%

Importance

Changes 0
Metric Value
wmc 44
eloc 79
dl 0
loc 269
ccs 75
cts 90
cp 0.8333
rs 8.8798
c 0
b 0
f 0

13 Methods

Rating   Name   Duplication   Size   Complexity  
A getPathSeparator() 0 3 1
B normalize() 0 29 7
A getSeparator() 0 3 1
A getDefaultParent() 0 3 1
A fromURIPath() 0 8 3
A isAbsolute() 0 3 1
B normalizer() 0 28 9
A resolveFile() 0 8 2
A resolve() 0 19 5
A canDelete() 0 6 1
A compare() 0 6 1
A copy() 0 16 3
B prefixLength() 0 20 9

How to fix   Complexity   

Complex Class

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

1
<?php
2
3
/**
4
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
5
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
6
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
7
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
8
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
9
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
10
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
11
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
12
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
13
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
14
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
15
 *
16
 * This software consists of voluntary contributions made by many individuals
17
 * and is licensed under the LGPL. For more information please see
18
 * <http://phing.info>.
19
 */
20
21
namespace Phing\Io;
22
23
use Exception;
24
use Phar;
25
use Phing\Phing;
26
use Phing\Util\StringHelper;
27
28
/**
29
 * UnixFileSystem class. This class encapsulates the basic file system functions
30
 * for platforms using the unix (posix)-stylish filesystem. It wraps php native
31
 * functions suppressing normal PHP error reporting and instead uses Exception
32
 * to report and error.
33
 *
34
 * This class is part of a oop based filesystem abstraction and targeted to run
35
 * on all supported php platforms.
36
 *
37
 * Note: For debugging turn track_errors on in the php.ini. The error messages
38
 * and log messages from this class will then be clearer because $php_errormsg
39
 * is passed as part of the message.
40
 *
41
 * FIXME:
42
 *  - Comments
43
 *  - Error handling reduced to min, error are handled by PhingFile mainly
44
 *
45
 * @author Andreas Aderhold, [email protected]
46
 */
47
class UnixFileSystem extends FileSystem
48
{
49
    /**
50
     * returns OS dependent path separator char.
51
     *
52
     * @return string
53
     */
54 510
    public function getSeparator()
55
    {
56 510
        return '/';
57
    }
58
59
    /**
60
     * returns OS dependent directory separator char.
61
     *
62
     * @return string
63
     */
64 7
    public function getPathSeparator()
65
    {
66 7
        return ':';
67
    }
68
69
    /**
70
     * A normal Unix pathname contains no duplicate slashes and does not end
71
     * with a slash.  It may be the empty string.
72
     *
73
     * Check that the given pathname is normal.  If not, invoke the real
74
     * normalizer on the part of the pathname that requires normalization.
75
     * This way we iterate through the whole pathname string only once.
76
     *
77
     * NOTE: this method no longer expands the tilde (~) character!
78
     *
79
     * @param string $strPathname
80
     *
81
     * @return string
82
     */
83 957
    public function normalize($strPathname)
84
    {
85 957
        if (!strlen($strPathname)) {
86 3
            return '';
87
        }
88
89
        // Start normalising after any scheme that is present.
90
        // This prevents phar:///foo being normalised into phar:/foo
91
        // Use a regex as some paths may not by parsed by parse_url().
92 957
        if (preg_match('{^[a-z][a-z0-9+\-\.]+://}', $strPathname)) {
93 11
            $i = strpos($strPathname, '://') + 3;
94
        } else {
95 946
            $i = 0;
96
        }
97
98 957
        $n = strlen($strPathname);
99 957
        $prevChar = 0;
100 957
        for (; $i < $n; ++$i) {
101 957
            $c = $strPathname[$i];
102 957
            if (('/' === $prevChar) && ('/' === $c)) {
103 12
                return self::normalizer($strPathname, $n, $i - 1);
0 ignored issues
show
Bug Best Practice introduced by
The method Phing\Io\UnixFileSystem::normalizer() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

103
                return self::/** @scrutinizer ignore-call */ normalizer($strPathname, $n, $i - 1);
Loading history...
104
            }
105 957
            $prevChar = $c;
106
        }
107 957
        if ('/' === $prevChar) {
108 24
            return self::normalizer($strPathname, $n, $n - 1);
109
        }
110
111 953
        return $strPathname;
112
    }
113
114
    /**
115
     * Compute the length of the pathname string's prefix.  The pathname
116
     * string must be in normal form.
117
     *
118
     * @param string $pathname
119
     *
120
     * @return int
121
     */
122 954
    public function prefixLength($pathname)
123
    {
124 954
        if (0 === strlen($pathname)) {
125 1
            return 0;
126
        }
127
128 954
        if (class_exists('Phar', false) && method_exists('Phar', 'running')) {
129 954
            $phar = Phar::running();
130 954
            $pharAlias = 'phar://' . Phing::PHAR_ALIAS;
131
132 954
            if ($phar && 0 === strpos($pathname, $phar)) {
133
                return strlen($phar);
134
            }
135
136 954
            if ($phar && 0 === strpos($pathname, $pharAlias)) {
137
                return strlen($pharAlias);
138
            }
139
        }
140
141 954
        return ('/' === $pathname[0]) ? 1 : 0;
142
    }
143
144
    /**
145
     * Resolve the child pathname string against the parent.
146
     * Both strings must be in normal form, and the result
147
     * will be in normal form.
148
     *
149
     * @param string $parent
150
     * @param string $child
151
     *
152
     * @return string
153
     */
154 498
    public function resolve($parent, $child)
155
    {
156 498
        if ('' === $child) {
157 2
            return $parent;
158
        }
159
160 498
        if ('/' === $child[0]) {
161
            if ('/' === $parent) {
162
                return $child;
163
            }
164
165
            return $parent . $child;
166
        }
167
168 498
        if ('/' === $parent) {
169
            return $parent . $child;
170
        }
171
172 498
        return $parent . '/' . $child;
173
    }
174
175
    /**
176
     * @return string
177
     */
178
    public function getDefaultParent()
179
    {
180
        return '/';
181
    }
182
183
    /**
184
     * @return bool
185
     */
186 936
    public function isAbsolute(File $f)
187
    {
188 936
        return 0 !== $f->getPrefixLength();
189
    }
190
191
    /**
192
     * the file resolver.
193
     *
194
     * @return string
195
     */
196 936
    public function resolveFile(File $f)
197
    {
198
        // resolve if parent is a file oject only
199 936
        if ($this->isAbsolute($f)) {
200 936
            return $f->getPath();
201
        }
202
203 112
        return $this->resolve(Phing::getProperty('user.dir'), $f->getPath());
204
    }
205
206
    // -- most of the following is mapped to the php natives wrapped by FileSystem
207
208
    // -- Attribute accessors --
209
210
    /**
211
     * compares file paths lexicographically.
212
     *
213
     * @return int
214
     */
215 39
    public function compare(File $f1, File $f2)
216
    {
217 39
        $f1Path = $f1->getPath();
218 39
        $f2Path = $f2->getPath();
219
220 39
        return strcmp((string) $f1Path, (string) $f2Path);
221
    }
222
223
    /**
224
     * Copy a file, takes care of symbolic links.
225
     *
226
     * @param File $src  source path and name file to copy
227
     * @param File $dest destination path and name of new file
228
     *
229
     * @throws Exception if file cannot be copied
230
     */
231 29
    public function copy(File $src, File $dest)
232
    {
233 29
        if (!$src->isLink()) {
234 27
            parent::copy($src, $dest);
235
236 27
            return;
237
        }
238
239 3
        $srcPath = $src->getAbsolutePath();
0 ignored issues
show
Unused Code introduced by
The assignment to $srcPath is dead and can be removed.
Loading history...
240 3
        $destPath = $dest->getAbsolutePath();
241
242 3
        $linkTarget = $src->getLinkTarget();
243 3
        if (false === @symlink($linkTarget, $destPath)) {
244
            $msg = "FileSystem::copy() FAILED. Cannot create symlink from {$destPath} to {$linkTarget}.";
245
246
            throw new Exception($msg);
247
        }
248
    }
249
250
    /**
251
     * @param string $p
252
     *
253
     * @return string
254
     */
255
    public function fromURIPath($p)
256
    {
257
        if (StringHelper::endsWith('/', $p) && (strlen($p) > 1)) {
258
            // "/foo/" --> "/foo", but "/" --> "/"
259
            $p = substr($p, 0, strlen($p) - 1);
260
        }
261
262
        return $p;
263
    }
264
265
    /**
266
     * Whether file can be deleted.
267
     *
268
     * @return bool
269
     */
270 234
    public function canDelete(File $f)
271
    {
272 234
        @clearstatcache();
0 ignored issues
show
Bug introduced by
Are you sure the usage of clearstatcache() is correct as it seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
273 234
        $dir = dirname($f->getAbsolutePath());
274
275 234
        return @is_writable($dir);
276
    }
277
278
    /**
279
     * Normalize the given pathname, whose length is $len, starting at the given
280
     * $offset; everything before this offset is already normal.
281
     *
282
     * @param string $pathname
283
     * @param int    $len
284
     * @param int    $offset
285
     *
286
     * @return string
287
     */
288 32
    protected function normalizer($pathname, $len, $offset)
289
    {
290 32
        if (0 === $len) {
291
            return $pathname;
292
        }
293 32
        $n = (int) $len;
294 32
        while (($n > 0) && ('/' === $pathname[$n - 1])) {
295 24
            --$n;
296
        }
297 32
        if (0 === $n) {
298 8
            return '/';
299
        }
300 27
        $sb = '';
301
302 27
        if ($offset > 0) {
303 24
            $sb .= substr($pathname, 0, $offset);
304
        }
305 27
        $prevChar = 0;
306 27
        for ($i = $offset; $i < $n; ++$i) {
307 12
            $c = $pathname[$i];
308 12
            if (('/' === $prevChar) && ('/' === $c)) {
309 12
                continue;
310
            }
311 12
            $sb .= $c;
312 12
            $prevChar = $c;
313
        }
314
315 27
        return $sb;
316
    }
317
}
318