Completed
Push — master ( 20b0ec...0fa80a )
by Siad
15:26
created

FileUtils::resolveFile()   C

Complexity

Conditions 12
Paths 7

Size

Total Lines 45
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 12.2812

Importance

Changes 0
Metric Value
cc 12
eloc 26
c 0
b 0
f 0
nc 7
nop 2
dl 0
loc 45
ccs 21
cts 24
cp 0.875
crap 12.2812
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the LGPL. For more information please see
17
 * <http://phing.info>.
18
 */
19
20
/**
21
 * File utility class.
22
 * - handles os independent stuff etc
23
 * - mapper stuff
24
 * - filter stuff
25
 *
26
 * @package phing.util
27
 */
28
class FileUtils
29
{
30
    /**
31
     * Returns the default file/dir creation mask value
32
     * (The mask value is prepared w.r.t the current user's file-creation mask value)
33
     *
34
     * @param boolean $dirmode Directory creation mask to select
35
     *
36
     * @return int  Creation Mask in octal representation
37
     */
38
    public static function getDefaultFileCreationMask($dirmode = false)
39
    {
40
41
        // Preparing the creation mask base permission
42
        $permission = ($dirmode === true) ? 0777 : 0666;
43
44
        // Default mask information
45
        $defaultmask = sprintf('%03o', ($permission & ($permission - (int) sprintf('%04o', umask()))));
46
47
        return octdec($defaultmask);
48
    }
49
50
    /**
51
     * Returns a new Reader with filterchains applied.  If filterchains are empty,
52
     * simply returns passed reader.
53
     *
54
     * @param  Reader $in Reader to modify (if appropriate).
55
     * @param  array   &$filterChains filter chains to apply.
56
     * @param  Project $project
57
     * @return Reader  Assembled Reader (w/ filter chains).
58
     */
59 36
    public static function getChainedReader(Reader $in, &$filterChains, Project $project)
60
    {
61 36
        if (!empty($filterChains)) {
62 27
            $crh = new ChainReaderHelper();
63 27
            $crh->setBufferSize(65536); // 64k buffer, but isn't being used (yet?)
64 27
            $crh->setPrimaryReader($in);
65 27
            $crh->setFilterChains($filterChains);
66 27
            $crh->setProject($project);
67 27
            $rdr = $crh->getAssembledReader();
68
69 27
            return $rdr;
70
        }
71
72 10
        return $in;
73
    }
74
75
    /**
76
     * Copies a file using filter chains.
77
     *
78
     * @param  PhingFile $sourceFile
79
     * @param  PhingFile $destFile
80
     * @param  boolean $overwrite
81
     * @param  boolean $preserveLastModified
82
     * @param  array $filterChains
83
     * @param  Project $project
84
     * @param  integer $mode
85
     * @param  bool $preservePermissions
86
     * @throws Exception
87
     * @throws IOException
88
     * @return void
89
     */
90 34
    public function copyFile(
91
        PhingFile $sourceFile,
92
        PhingFile $destFile,
93
        Project $project,
94
        $overwrite = false,
95
        $preserveLastModified = true,
96
        &$filterChains = null,
97
        $mode = 0755,
98
        $preservePermissions = true
99
    ) {
100 34
        if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) {
101 33
            if ($destFile->exists() && ($destFile->isFile() || $destFile->isLink())) {
102 1
                $destFile->delete();
103
            }
104
105
            // ensure that parent dir of dest file exists!
106 33
            $parent = $destFile->getParentFile();
107 33
            if ($parent !== null && !$parent->exists()) {
108
                // Setting source directory permissions to target
109
                // (On permissions preservation, the target directory permissions
110
                // will be inherited from the source directory, otherwise the 'mode'
111
                // will be used)
112 2
                $dirMode = ($preservePermissions ? $sourceFile->getParentFile()->getMode() : $mode);
113
114 2
                $parent->mkdirs($dirMode);
115
            }
116
117 33
            if ((is_array($filterChains)) && (!empty($filterChains))) {
118 21
                $in = self::getChainedReader(new BufferedReader(new FileReader($sourceFile)), $filterChains, $project);
119 21
                $out = new BufferedWriter(new FileWriter($destFile));
120
121
                // New read() methods returns a big buffer.
122 21
                while (-1 !== ($buffer = $in->read())) { // -1 indicates EOF
123 21
                    $out->write($buffer);
124
                }
125
126 21
                if ($in !== null) {
127 21
                    $in->close();
128
                }
129 21
                if ($out !== null) {
130 21
                    $out->close();
131
                }
132
133
                // Set/Copy the permissions on the target
134 21
                if ($preservePermissions === true) {
135 21
                    $destFile->setMode($sourceFile->getMode());
136
                }
137
            } else {
138
                // simple copy (no filtering)
139 12
                $sourceFile->copyTo($destFile);
140
141
                // By default, PHP::Copy also copies the file permissions. Therefore,
142
                // re-setting the mode with the "user file-creation mask" information.
143 12
                if ($preservePermissions === false) {
144
                    $destFile->setMode(FileUtils::getDefaultFileCreationMask(false));
145
                }
146
            }
147
148 33
            if ($preserveLastModified && !$destFile->isLink()) {
149 1
                $destFile->setLastModified($sourceFile->lastModified());
150
            }
151
        }
152 34
    }
153
154
155
    /**
156
     * Attempts to rename a file from a source to a destination.
157
     * If overwrite is set to true, this method overwrites existing file even if the destination file is newer.
158
     * Otherwise, the source file is renamed only if the destination file is older than it.
159
     *
160
     * @param  PhingFile $sourceFile
161
     * @param  PhingFile $destFile
162
     * @return boolean
163
     */
164 1
    public function renameFile(PhingFile $sourceFile, PhingFile $destFile, $overwrite = false)
165
    {
166
        // ensure that parent dir of dest file exists!
167 1
        $parent = $destFile->getParentFile();
168 1
        if ($parent !== null) {
169 1
            if (!$parent->exists()) {
170
                $parent->mkdirs();
171
            }
172
        }
173
174 1
        if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) {
175 1
            if ($destFile->exists()) {
176
                try {
177
                    $destFile->delete();
178
                } catch (Exception $e) {
179
                    throw new BuildException(
180
                        "Unable to remove existing file " . $destFile->__toString() . ": " . $e->getMessage()
181
                    );
182
                }
183
            }
184
        }
185
186 1
        $sourceFile->renameTo($destFile);
187 1
    }
188
189
    /**
190
     * Interpret the filename as a file relative to the given file -
191
     * unless the filename already represents an absolute filename.
192
     *
193
     * @param PhingFile $file the "reference" file for relative paths. This
194
     *                             instance must be an absolute file and must
195
     *                             not contain ./ or ../ sequences (same for \
196
     *                             instead of /).
197
     * @param string $filename a file name
198
     *
199
     * @throws IOException
200
     *
201
     * @return PhingFile A PhingFile object pointing to an absolute file that doesn't contain ./ or ../ sequences
202
     *                   and uses the correct separator for the current platform.
203
     */
204 388
    public function resolveFile($file, $filename)
205
    {
206
        // remove this and use the static class constant File::separator
207
        // as soon as ZE2 is ready
208 388
        $fs = FileSystem::getFileSystem();
209
210 388
        $filename = str_replace(array('\\', '/'), $fs->getSeparator(), $filename);
211
212
        // deal with absolute files
213
        if (
214 388
            StringHelper::startsWith($fs->getSeparator(), $filename)
215 363
            || (strlen($filename) >= 2
216 320
            && Character::isLetter($filename[0])
217 259
            && $filename[1] === ':')
218
        ) {
219 61
            return new PhingFile($this->normalize($filename));
220
        }
221
222 363
        if (strlen($filename) >= 2 && Character::isLetter($filename[0]) && $filename[1] === ':') {
223
            return new PhingFile($this->normalize($filename));
224
        }
225
226 363
        $helpFile = new PhingFile($file->getAbsolutePath());
227
228 363
        $tok = strtok($filename, $fs->getSeparator());
229 363
        while ($tok !== false) {
230 363
            $part = $tok;
231 363
            if ($part === '..') {
232 154
                $parentFile = $helpFile->getParent();
233 154
                if ($parentFile === null) {
234
                    $msg = "The file or path you specified ($filename) is invalid relative to " . $file->getPath();
235
                    throw new IOException($msg);
236
                }
237 154
                $helpFile = new PhingFile($parentFile);
238
            } else {
239 363
                if ($part === '.') {
240
                    // Do nothing here
241
                } else {
242 319
                    $helpFile = new PhingFile($helpFile, $part);
243
                }
244
            }
245 363
            $tok = strtok($fs->getSeparator());
246
        }
247
248 363
        return new PhingFile($helpFile->getAbsolutePath());
249
    }
250
251
    /**
252
     * Normalize the given absolute path.
253
     *
254
     * This includes:
255
     *   - Uppercase the drive letter if there is one.
256
     *   - Remove redundant slashes after the drive spec.
257
     *   - resolve all ./, .\, ../ and ..\ sequences.
258
     *   - DOS style paths that start with a drive letter will have
259
     *     \ as the separator.
260
     *
261
     * @param string $path Path to normalize.
262
     *
263
     * @throws IOException
264
     *
265
     * @return string
266
     */
267 763
    public function normalize($path)
268
    {
269 763
        $path = (string) $path;
270 763
        $orig = $path;
271
272 763
        $path = str_replace(array('\\', '/'), DIRECTORY_SEPARATOR, $path);
273
274
        // make sure we are dealing with an absolute path
275
        if (
276 763
            !StringHelper::startsWith(DIRECTORY_SEPARATOR, $path)
277
            && !(strlen($path) >= 2
278
            && Character::isLetter($path[0])
279
            && $path[1] === ':')
280
        ) {
281
            throw new IOException("$path is not an absolute path");
282
        }
283
284 763
        $dosWithDrive = false;
285 763
        $root = null;
286
287
        // Eliminate consecutive slashes after the drive spec
288
289 763
        if (strlen($path) >= 2 && Character::isLetter($path[0]) && $path[1] === ':') {
290
            $dosWithDrive = true;
291
292
            $ca = str_replace('/', '\\', $path);
293
294
            $path = strtoupper($ca[0]) . ':';
295
296
            for ($i = 2, $_i = strlen($ca); $i < $_i; $i++) {
297
                if (
298
                    ($ca[$i] !== '\\')
299
                    || ($ca[$i] === '\\'
300
                    && $ca[$i - 1] !== '\\')
301
                ) {
302
                    $path .= $ca[$i];
303
                }
304
            }
305
306
            $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
307
308
            if (strlen($path) == 2) {
309
                $root = $path;
310
                $path = "";
311
            } else {
312
                $root = substr($path, 0, 3);
313
                $path = substr($path, 3);
314
            }
315
        } else {
316 763
            if (strlen($path) == 1) {
317 3
                $root = DIRECTORY_SEPARATOR;
318 3
                $path = "";
319
            } else {
320 760
                if ($path[1] == DIRECTORY_SEPARATOR) {
321
                    // UNC drive
322
                    $root = DIRECTORY_SEPARATOR . DIRECTORY_SEPARATOR;
323
                    $path = substr($path, 2);
324
                } else {
325 760
                    $root = DIRECTORY_SEPARATOR;
326 760
                    $path = substr($path, 1);
327
                }
328
            }
329
        }
330
331 763
        $s = [];
332 763
        $s[] = $root;
333 763
        $tok = strtok($path, DIRECTORY_SEPARATOR);
334 763
        while ($tok !== false) {
335 760
            $thisToken = $tok;
336 760
            if ("." === $thisToken) {
337
                $tok = strtok(DIRECTORY_SEPARATOR);
338
                continue;
339
            }
340
341 760
            if (".." === $thisToken) {
342 1
                if (count($s) < 2) {
343
                    // using '..' in path that is too short
344
                    throw new IOException("Cannot resolve path: $orig");
345
                }
346
347 1
                array_pop($s);
348
            } else { // plain component
349 760
                $s[] = $thisToken;
350
            }
351 760
            $tok = strtok(DIRECTORY_SEPARATOR);
352
        }
353
354 763
        $sb = "";
355 763
        for ($i = 0, $_i = count($s); $i < $_i; $i++) {
356 763
            if ($i > 1) {
357
                // not before the filesystem root and not after it, since root
358
                // already contains one
359 760
                $sb .= DIRECTORY_SEPARATOR;
360
            }
361 763
            $sb .= (string) $s[$i];
362
        }
363
364
365 763
        $path = (string) $sb;
366 763
        if ($dosWithDrive === true) {
367
            $path = str_replace('/', '\\', $path);
368
        }
369
370 763
        return $path;
371
    }
372
373
    /**
374
     * Create a temporary file in a given directory.
375
     *
376
     * <p>The file denoted by the returned abstract pathname did not
377
     * exist before this method was invoked, any subsequent invocation
378
     * of this method will yield a different file name.</p>
379
     *
380
     * @param  string $prefix prefix before the random number.
381
     * @param  string $suffix file extension; include the '.'.
382
     * @param  PhingFile $parentDir Directory to create the temporary file in;
383
     *                                sys_get_temp_dir() used if not specified.
384
     * @param  boolean $deleteOnExit whether to set the tempfile for deletion on
385
     *                                normal exit.
386
     * @param  boolean $createFile true if the file must actually be created. If false
387
     *                                chances exist that a file with the same name is
388
     *                                created in the time between invoking this method
389
     *                                and the moment the file is actually created. If
390
     *                                possible set to true.
391
     * @return PhingFile            a File reference to the new temporary file.
392
     * @throws BuildException
393
     */
394 1
    public function createTempFile(
395
        $prefix,
396
        $suffix,
397
        PhingFile $parentDir = null,
398
        $deleteOnExit = false,
399
        $createFile = false
400
    ) {
401 1
        $result = null;
402 1
        $parent = ($parentDir === null) ? sys_get_temp_dir() : $parentDir->getPath();
403
404 1
        if ($createFile) {
405
            try {
406
                $result = PhingFile::createTempFile($prefix, $suffix, new PhingFile($parent));
407
            } catch (IOException $e) {
408
                throw new BuildException("Could not create tempfile in " . $parent, $e);
409
            }
410
        } else {
411
            do {
412 1
                $result = new PhingFile($parent, $prefix . substr(md5((string) time()), 0, 8) . $suffix);
413 1
            } while ($result->exists());
414
        }
415
416 1
        if ($deleteOnExit) {
417
            $result->deleteOnExit();
418
        }
419
420 1
        return $result;
421
    }
422
423
    /**
424
     * @param PhingFile $file1
425
     * @param PhingFile $file2
426
     *
427
     * @return boolean Whether contents of two files is the same.
428
     */
429 14
    public function contentEquals(PhingFile $file1, PhingFile $file2)
430
    {
431 14
        if (!($file1->exists() && $file2->exists())) {
432 1
            return false;
433
        }
434
435 14
        if (!($file1->canRead() && $file2->canRead())) {
436
            return false;
437
        }
438
439 14
        if ($file1->isDirectory() || $file2->isDirectory()) {
440 2
            return false;
441
        }
442
443 13
        $c1 = file_get_contents($file1->getAbsolutePath());
444 13
        $c2 = file_get_contents($file2->getAbsolutePath());
445
446 13
        return trim($c1) == trim($c2);
447
    }
448
}
449