Completed
Push — master ( 89abc8...a320a7 )
by Siad
16:11
created

FileUtils::copyFile()   D

Complexity

Conditions 19
Paths 121

Size

Total Lines 60
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 23
CRAP Score 19.0261

Importance

Changes 0
Metric Value
cc 19
eloc 24
nc 121
nop 8
dl 0
loc 60
ccs 23
cts 24
cp 0.9583
crap 19.0261
rs 4.3416
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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