Passed
Push — main ( 01fb06...c551fb )
by Siad
06:07
created

UnixFileSystem::normalize()   B

Complexity

Conditions 7
Paths 11

Size

Total Lines 29
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 16
nc 11
nop 1
dl 0
loc 29
ccs 16
cts 16
cp 1
crap 7
rs 8.8333
c 0
b 0
f 0
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 465
    public function getSeparator()
55
    {
56 465
        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 868
    public function normalize($strPathname)
84
    {
85 868
        if (!strlen($strPathname)) {
86 2
            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 868
        if (preg_match('{^[a-z][a-z0-9+\-\.]+://}', $strPathname)) {
93 11
            $i = strpos($strPathname, '://') + 3;
94
        } else {
95 857
            $i = 0;
96
        }
97
98 868
        $n = strlen($strPathname);
99 868
        $prevChar = 0;
100 868
        for (; $i < $n; ++$i) {
101 868
            $c = $strPathname[$i];
102 868
            if (('/' === $prevChar) && ('/' === $c)) {
103 4
                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 868
            $prevChar = $c;
106
        }
107 868
        if ('/' === $prevChar) {
108 21
            return self::normalizer($strPathname, $n, $n - 1);
109
        }
110
111 865
        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 865
    public function prefixLength($pathname)
123
    {
124 865
        if (0 === strlen($pathname)) {
125
            return 0;
126
        }
127
128 865
        if (class_exists('Phar', false) && method_exists('Phar', 'running')) {
129 865
            $phar = Phar::running();
130 865
            $pharAlias = 'phar://' . Phing::PHAR_ALIAS;
131
132 865
            if ($phar && 0 === strpos($pathname, $phar)) {
133
                return strlen($phar);
134
            }
135
136 865
            if ($phar && 0 === strpos($pathname, $pharAlias)) {
137
                return strlen($pharAlias);
138
            }
139
        }
140
141 865
        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 450
    public function resolve($parent, $child)
155
    {
156 450
        if ('' === $child) {
157 2
            return $parent;
158
        }
159
160 450
        if ('/' === $child[0]) {
161
            if ('/' === $parent) {
162
                return $child;
163
            }
164
165
            return $parent . $child;
166
        }
167
168 450
        if ('/' === $parent) {
169
            return $parent . $child;
170
        }
171
172 450
        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 847
    public function isAbsolute(File $f)
187
    {
188 847
        return 0 !== $f->getPrefixLength();
189
    }
190
191
    /**
192
     * the file resolver.
193
     *
194
     * @return string
195
     */
196 847
    public function resolveFile(File $f)
197
    {
198
        // resolve if parent is a file oject only
199 847
        if ($this->isAbsolute($f)) {
200 847
            return $f->getPath();
201
        }
202
203 102
        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 36
    public function compare(File $f1, File $f2)
216
    {
217 36
        $f1Path = $f1->getPath();
218 36
        $f2Path = $f2->getPath();
219
220 36
        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 24
    public function copy(File $src, File $dest)
232
    {
233 24
        if (!$src->isLink()) {
234 22
            parent::copy($src, $dest);
235
236 22
            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 3
    }
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 204
    public function canDelete(File $f)
271
    {
272 204
        @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 204
        $dir = dirname($f->getAbsolutePath());
274
275 204
        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 22
    protected function normalizer($pathname, $len, $offset)
289
    {
290 22
        if (0 === $len) {
291
            return $pathname;
292
        }
293 22
        $n = (int) $len;
294 22
        while (($n > 0) && ('/' === $pathname[$n - 1])) {
295 21
            --$n;
296
        }
297 22
        if (0 === $n) {
298 7
            return '/';
299
        }
300 18
        $sb = '';
301
302 18
        if ($offset > 0) {
303 15
            $sb .= substr($pathname, 0, $offset);
304
        }
305 18
        $prevChar = 0;
306 18
        for ($i = $offset; $i < $n; ++$i) {
307 4
            $c = $pathname[$i];
308 4
            if (('/' === $prevChar) && ('/' === $c)) {
309 4
                continue;
310
            }
311 4
            $sb .= $c;
312 4
            $prevChar = $c;
313
        }
314
315 18
        return $sb;
316
    }
317
}
318