Passed
Push — main ( addc21...bb891e )
by Michiel
06:30
created

UnixFileSystem::resolveFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

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