Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/*
3
 *  $Id: FileUtils.php 325 2007-12-20 15:44:58Z hans $
4
 *
5
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
6
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
7
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
8
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
9
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
10
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
11
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
12
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
13
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
14
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
15
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
16
 *
17
 * This software consists of voluntary contributions made by many individuals
18
 * and is licensed under the LGPL. For more information please see
19
 * <http://phing.info>.
20
 */
21
 
22
include_once 'phing/system/lang/Character.php';
23
include_once 'phing/util/StringHelper.php';
24
include_once 'phing/system/io/BufferedReader.php';
25
include_once 'phing/system/io/BufferedWriter.php';
26
include_once 'phing/filters/util/ChainReaderHelper.php';
27
include_once 'phing/system/io/PhingFile.php';
28
 
29
/**
30
 * File utility class.
31
 * - handles os independent stuff etc
32
 * - mapper stuff
33
 * - filter stuff
34
 *
35
 * @package  phing.util
36
 * @version  $Revision: 1.10 $
37
 */
38
class FileUtils {
39
 
40
    /**
41
     * Returns a new Reader with filterchains applied.  If filterchains are empty,
42
     * simply returns passed reader.
43
     *
44
     * @param Reader $in Reader to modify (if appropriate).
45
     * @param array &$filterChains filter chains to apply.
46
     * @param Project $project
47
     * @return Reader Assembled Reader (w/ filter chains).
48
     */
49
    public static function getChainedReader(Reader $in, &$filterChains, Project $project) {
50
        if (!empty($filterChains)) {
51
            $crh = new ChainReaderHelper();
52
            $crh->setBufferSize(65536); // 64k buffer, but isn't being used (yet?)
53
            $crh->setPrimaryReader($in);
54
            $crh->setFilterChains($filterChains);
55
            $crh->setProject($project);
56
            $rdr = $crh->getAssembledReader();
57
            return $rdr;
58
        } else {
59
            return $in;
60
        }
61
    }
62
 
63
    /**
64
     * Copies a file using filter chains.
65
     *
66
     * @param PhingFile $sourceFile
67
     * @param PhingFile $destFile
68
     * @param boolean $overwrite
69
     * @param boolean $preserveLastModified
70
     * @param array $filterChains
71
     * @param Project $project
72
     * @return void
73
     */
74
    function copyFile(PhingFile $sourceFile, PhingFile $destFile, $overwrite = false, $preserveLastModified = true, &$filterChains = null, Project $project) {
75
 
76
        if ($overwrite || !$destFile->exists() || $destFile->lastModified() < $sourceFile->lastModified()) {
77
            if ($destFile->exists() && $destFile->isFile()) {
78
                $destFile->delete();
79
            }
80
 
81
            // ensure that parent dir of dest file exists!
82
            $parent = $destFile->getParentFile();
83
            if ($parent !== null && !$parent->exists()) {
84
                $parent->mkdirs();
85
            }
86
 
87
            if ((is_array($filterChains)) && (!empty($filterChains))) {
88
 
89
                $in = self::getChainedReader(new BufferedReader(new FileReader($sourceFile)), $filterChains, $project);
90
                $out = new BufferedWriter(new FileWriter($destFile));
91
 
92
                // New read() methods returns a big buffer.
93
                while(-1 !== ($buffer = $in->read())) { // -1 indicates EOF
94
                    $out->write($buffer);
95
                }
96
 
97
                if ( $in !== null )
98
                    $in->close();
99
                if ( $out !== null )
100
                    $out->close();
101
            } else {
102
                // simple copy (no filtering)
103
                $sourceFile->copyTo($destFile);
104
            }
105
 
106
            if ($preserveLastModified) {
107
                $destFile->setLastModified($sourceFile->lastModified());
108
            }
109
 
110
        }
111
    }
112
 
113
    /**
114
     * Interpret the filename as a file relative to the given file -
115
     * unless the filename already represents an absolute filename.
116
     *
117
     * @param  $file the "reference" file for relative paths. This
118
     *         instance must be an absolute file and must not contain
119
     *         ./ or ../ sequences (same for \ instead of /).
120
     * @param  $filename a file name
121
     *
122
     * @return PhingFile A PhingFile object pointing to an absolute file that doesn't contain ./ or ../ sequences
123
     *         and uses the correct separator for the current platform.
124
     */
125
    function resolveFile($file, $filename) {
126
        // remove this and use the static class constant File::seperator
127
        // as soon as ZE2 is ready
128
        $fs = FileSystem::getFileSystem();
129
 
130
        $filename = str_replace('/', $fs->getSeparator(), str_replace('\\', $fs->getSeparator(), $filename));
131
 
132
        // deal with absolute files
133
        if (StringHelper::startsWith($fs->getSeparator(), $filename) ||
134
                (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':')) {
135
            return new PhingFile($this->normalize($filename));
136
        }
137
 
138
        if (strlen($filename) >= 2 && Character::isLetter($filename{0}) && $filename{1} === ':') {
139
            return new PhingFile($this->normalize($filename));
140
        }
141
 
142
        $helpFile = new PhingFile($file->getAbsolutePath());
143
 
144
        $tok = strtok($filename, $fs->getSeparator());
145
        while ($tok !== false) {
146
            $part = $tok;
147
            if ($part === '..') {
148
                $parentFile = $helpFile->getParent();
149
                if ($parentFile === null) {
150
                    $msg = "The file or path you specified ($filename) is invalid relative to ".$file->getPath();
151
                    throw new IOException($msg);
152
                }
153
                $helpFile = new PhingFile($parentFile);
154
            } else if ($part === '.') {
155
                // Do nothing here
156
            } else {
157
                $helpFile = new PhingFile($helpFile, $part);
158
            }
159
            $tok = strtok($fs->getSeparator());
160
        }
161
        return new PhingFile($helpFile->getAbsolutePath());
162
    }
163
 
164
    /**
165
     * Normalize the given absolute path.
166
     *
167
     * This includes:
168
     *   - Uppercase the drive letter if there is one.
169
     *   - Remove redundant slashes after the drive spec.
170
     *   - resolve all ./, .\, ../ and ..\ sequences.
171
     *   - DOS style paths that start with a drive letter will have
172
     *     \ as the separator.
173
     * @param string $path Path to normalize.
174
     * @return string
175
     */
176
    function normalize($path) {
177
 
178
        $path = (string) $path;
179
        $orig = $path;
180
 
181
        $path = str_replace('/', DIRECTORY_SEPARATOR, str_replace('\\', DIRECTORY_SEPARATOR, $path));
182
 
183
        // make sure we are dealing with an absolute path
184
        if (!StringHelper::startsWith(DIRECTORY_SEPARATOR, $path)
185
                && !(strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':')) {
186
            throw new IOException("$path is not an absolute path");
187
        }
188
 
189
        $dosWithDrive = false;
190
        $root = null;
191
 
192
        // Eliminate consecutive slashes after the drive spec
193
 
194
        if (strlen($path) >= 2 && Character::isLetter($path{0}) && $path{1} === ':') {
195
            $dosWithDrive = true;
196
 
197
            $ca = str_replace('/', '\\', $path);
198
            $ca = StringHelper::toCharArray($ca);
199
 
200
            $path = strtoupper($ca[0]).':';
201
 
202
            for ($i=2, $_i=count($ca); $i < $_i; $i++) {
203
                if (($ca[$i] !== '\\') ||
204
                        ($ca[$i] === '\\' && $ca[$i - 1] !== '\\')
205
                   ) {
206
                    $path .= $ca[$i];
207
                }
208
            }
209
 
210
            $path = str_replace('\\', DIRECTORY_SEPARATOR, $path);
211
 
212
            if (strlen($path) == 2) {
213
                $root = $path;
214
                $path = "";
215
            } else {
216
                $root = substr($path, 0, 3);
217
                $path = substr($path, 3);
218
            }
219
 
220
        } else {
221
            if (strlen($path) == 1) {
222
                $root = DIRECTORY_SEPARATOR;
223
                $path = "";
224
            } else if ($path{1} == DIRECTORY_SEPARATOR) {
225
                // UNC drive
226
                $root = DIRECTORY_SEPARATOR.DIRECTORY_SEPARATOR;
227
                $path = substr($path, 2);
228
            }
229
            else {
230
                $root = DIRECTORY_SEPARATOR;
231
                $path = substr($path, 1);
232
            }
233
        }
234
 
235
        $s = array();
236
        array_push($s, $root);
237
        $tok = strtok($path, DIRECTORY_SEPARATOR);
238
        while ($tok !== false) {
239
            $thisToken = $tok;
240
            if ("." === $thisToken) {
241
                $tok = strtok(DIRECTORY_SEPARATOR);
242
                continue;
243
            } elseif (".." === $thisToken) {
244
                if (count($s) < 2) {
245
                    // using '..' in path that is too short
246
                    throw new IOException("Cannot resolve path: $orig");
247
                } else {
248
                    array_pop($s);
249
                }
250
            } else { // plain component
251
                array_push($s, $thisToken);
252
            }
253
            $tok = strtok(DIRECTORY_SEPARATOR);
254
        }
255
 
256
        $sb = "";
257
        for ($i=0,$_i=count($s); $i < $_i; $i++) {
258
            if ($i > 1) {
259
                // not before the filesystem root and not after it, since root
260
                // already contains one
261
                $sb .= DIRECTORY_SEPARATOR;
262
            }
263
            $sb .= (string) $s[$i];
264
        }
265
 
266
 
267
        $path = (string) $sb;
268
        if ($dosWithDrive === true) {
269
            $path = str_replace('/', '\\', $path);
270
        }
271
        return $path;
272
    }
273
 
274
    /**
275
     * @return boolean Whether contents of two files is the same.
276
     */
277
    public function contentEquals(PhingFile $file1, PhingFile $file2) {
278
 
279
        if (!($file1->exists() || $file2->exists())) {
280
            return false;
281
        }
282
 
283
        if (!($file1->canRead() || $file2->canRead())) {
284
            return false;
285
        }
286
 
287
        $c1 = file_get_contents($file1->getAbsolutePath());
288
        $c2 = file_get_contents($file2->getAbsolutePath());
289
 
290
        return trim($c1) == trim($c2);
291
    }
292
 
293
}
294