Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
 
3
/*
4
 *  $Id: FileSystem.php 362 2008-03-08 10:07:53Z mrook $
5
 *
6
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
7
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
8
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
9
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
10
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
11
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
12
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
13
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
14
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
15
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
16
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
17
 *
18
 * This software consists of voluntary contributions made by many individuals
19
 * and is licensed under the LGPL. For more information please see
20
 * <http://phing.info>.
21
 */
22
 
23
/**
24
 * This is an abstract class for platform specific filesystem implementations
25
 * you have to implement each method in the platform specific filesystem implementation
26
 * classes Your local filesytem implementation must extend this class.
27
 * You should also use this class as a template to write your local implementation
28
 * Some native PHP filesystem specific methods are abstracted here as well. Anyway
29
 * you _must_ always use this methods via a PhingFile object (that by nature uses the
30
 * *FileSystem drivers to access the real filesystem via this class using natives.
31
 *
32
 * FIXME:
33
 *  - Error handling reduced to min fallthrough runtime excetions
34
 *    more precise errorhandling is done by the PhingFile class
35
 *
36
 * @author Charlie Killian <charlie@tizac.com>
37
 * @author Hans Lellelid <hans@xmpl.org>
38
 * @version $Revision: 1.11 $
39
 * @package phing.system.io
40
 */
41
abstract class FileSystem {
42
 
43
    /* properties for simple boolean attributes */
44
    const BA_EXISTS    = 0x01;
45
    const BA_REGULAR   = 0x02;
46
    const BA_DIRECTORY = 0x04;
47
    const BA_HIDDEN    = 0x08;
48
 
49
    /** Instance for getFileSystem() method. */
50
    private static $fs;
51
 
52
    /**
53
     * Static method to return the FileSystem singelton representing
54
     * this platform's local filesystem driver.
55
     * @return FileSystem
56
     */
57
    public static function getFileSystem() {
58
        if (self::$fs === null) {
59
            switch(Phing::getProperty('host.fstype')) {
60
                case 'UNIX':
61
                    include_once 'phing/system/io/UnixFileSystem.php';
62
                    self::$fs = new UnixFileSystem();
63
                break;
64
                case 'WIN32':
65
                    include_once 'phing/system/io/Win32FileSystem.php';
66
                    self::$fs = new Win32FileSystem();
67
                break;
68
                case 'WINNT':
69
                    include_once 'phing/system/io/WinNTFileSystem.php';
70
                    self::$fs = new WinNTFileSystem();
71
                break;
72
                default:
73
                    throw new Exception("Host uses unsupported filesystem, unable to proceed");
74
            }
75
        }
76
        return self::$fs;
77
    }
78
 
79
    /* -- Normalization and construction -- */
80
 
81
    /**
82
     * Return the local filesystem's name-separator character.
83
     */
84
    abstract function getSeparator();
85
 
86
    /**
87
     * Return the local filesystem's path-separator character.
88
     */
89
    abstract function getPathSeparator();
90
 
91
    /**
92
     * Convert the given pathname string to normal form.  If the string is
93
     * already in normal form then it is simply returned.
94
     */
95
    abstract function normalize($strPath);
96
 
97
    /**
98
     * Compute the length of this pathname string's prefix.  The pathname
99
     * string must be in normal form.
100
     */
101
    abstract function prefixLength($pathname);
102
 
103
    /**
104
     * Resolve the child pathname string against the parent.
105
     * Both strings must be in normal form, and the result
106
     * will be a string in normal form.
107
     */
108
    abstract function resolve($parent, $child);
109
 
110
    /**
111
     * Resolve the given abstract pathname into absolute form.  Invoked by the
112
     * getAbsolutePath and getCanonicalPath methods in the PhingFile class.
113
     */
114
    abstract function resolveFile(PhingFile $f);
115
 
116
    /**
117
     * Return the parent pathname string to be used when the parent-directory
118
     * argument in one of the two-argument PhingFile constructors is the empty
119
     * pathname.
120
     */
121
    abstract function getDefaultParent();
122
 
123
    /**
124
     * Post-process the given URI path string if necessary.  This is used on
125
     * win32, e.g., to transform "/c:/foo" into "c:/foo".  The path string
126
     * still has slash separators; code in the PhingFile class will translate them
127
     * after this method returns.
128
     */
129
    abstract function fromURIPath($path);
130
 
131
    /* -- Path operations -- */
132
 
133
    /**
134
     * Tell whether or not the given abstract pathname is absolute.
135
     */
136
    abstract function isAbsolute(PhingFile $f);
137
 
138
    /**
139
     * canonicalize filename by checking on disk
140
     * @return mixed Canonical path or false if the file doesn't exist.
141
     */
142
    function canonicalize($strPath) {
143
        return @realpath($strPath);
144
    }
145
 
146
    /* -- Attribute accessors -- */
147
 
148
    /**
149
     * Return the simple boolean attributes for the file or directory denoted
150
     * by the given abstract pathname, or zero if it does not exist or some
151
     * other I/O error occurs.
152
     */
153
    function getBooleanAttributes($f) {
154
        throw new Exception("SYSTEM ERROR method getBooleanAttributes() not implemented by fs driver");
155
    }
156
 
157
    /**
158
     * Check whether the file or directory denoted by the given abstract
159
     * pathname may be accessed by this process.  If the second argument is
160
     * false, then a check for read access is made; if the second
161
     * argument is true, then a check for write (not read-write)
162
     * access is made.  Return false if access is denied or an I/O error
163
     * occurs.
164
     */
165
    function checkAccess(PhingFile $f, $write = false) {
166
        // we clear stat cache, its expensive to look up from scratch,
167
        // but we need to be sure
168
        @clearstatcache();
169
 
170
 
171
        // Shouldn't this be $f->GetAbsolutePath() ?
172
        // And why doesn't GetAbsolutePath() work?
173
 
174
        $strPath = (string) $f->getPath();
175
 
176
        // FIXME
177
        // if file object does denote a file that yet not existst
178
        // path rights are checked
179
        if (!@file_exists($strPath) && !is_dir($strPath)) {
180
            $strPath = $f->getParent();
181
            if ($strPath === null || !is_dir($strPath)) {
182
                $strPath = Phing::getProperty("user.dir");
183
            }
184
            //$strPath = dirname($strPath);
185
        }
186
 
187
        if (!$write) {
188
            return (boolean) @is_readable($strPath);
189
        } else {
190
            return (boolean) @is_writable($strPath);
191
        }
192
    }
193
 
194
    /**
195
     * Whether file can be deleted.
196
     * @param PhingFile $f
197
     * @return boolean
198
     */
199
    function canDelete(PhingFile $f)
200
    {
201
    	clearstatcache();
202
 		$dir = dirname($f->getAbsolutePath());
203
 		return (bool) @is_writable($dir);
204
    }
205
 
206
    /**
207
     * Return the time at which the file or directory denoted by the given
208
     * abstract pathname was last modified, or zero if it does not exist or
209
     * some other I/O error occurs.
210
     */
211
    function getLastModifiedTime(PhingFile $f) {
212
 
213
        if (!$f->exists()) {
214
            return 0;
215
        }
216
 
217
        @clearstatcache();
218
        $strPath = (string) $f->getPath();
219
        $mtime = @filemtime($strPath);
220
        if (false === $mtime) {
221
            // FAILED. Log and return err.
222
            $msg = "FileSystem::Filemtime() FAILED. Cannot can not get modified time of $strPath. $php_errormsg";
223
            throw new Exception($msg);
224
        } else {
225
            return (int) $mtime;
226
        }
227
    }
228
 
229
    /**
230
     * Return the length in bytes of the file denoted by the given abstract
231
     * pathname, or zero if it does not exist, is a directory, or some other
232
     * I/O error occurs.
233
     */
234
    function getLength(PhingFile $f) {
235
        $strPath = (string) $f->getAbsolutePath();
236
        $fs = filesize((string) $strPath);
237
        if ($fs !== false) {
238
            return $fs;
239
        } else {
240
            $msg = "FileSystem::Read() FAILED. Cannot get filesize of $strPath. $php_errormsg";
241
            throw new Exception($msg);
242
        }
243
    }
244
 
245
    /* -- File operations -- */
246
 
247
    /**
248
     * Create a new empty file with the given pathname.  Return
249
     * true if the file was created and false if a
250
     * file or directory with the given pathname already exists.  Throw an
251
     * IOException if an I/O error occurs.
252
     *
253
     * @param       string      Path of the file to be created.
254
     *
255
     * @throws      IOException
256
     */
257
    function createNewFile($strPathname) {
258
        if (@file_exists($strPathname))
259
            return false;
260
 
261
        // Create new file
262
        $fp = @fopen($strPathname, "w");
263
        if ($fp === false) {
264
            throw new IOException("The file \"$strPathname\" could not be created");
265
        }
266
        @fclose($fp);
267
        return true;
268
    }
269
 
270
    /**
271
     * Delete the file or directory denoted by the given abstract pathname,
272
     * returning true if and only if the operation succeeds.
273
     */
274
    function delete(PhingFile $f) {
275
        if ($f->isDirectory()) {
276
            return $this->rmdir($f->getPath());
277
        } else {
278
            return $this->unlink($f->getPath());
279
        }
280
    }
281
 
282
    /**
283
     * Arrange for the file or directory denoted by the given abstract
284
     * pathname to be deleted when Phing::shutdown is called, returning
285
    * true if and only if the operation succeeds.
286
     */
287
    function deleteOnExit($f) {
288
        throw new Exception("deleteOnExit() not implemented by local fs driver");
289
    }
290
 
291
    /**
292
     * List the elements of the directory denoted by the given abstract
293
     * pathname.  Return an array of strings naming the elements of the
294
     * directory if successful; otherwise, return <code>null</code>.
295
     */
296
    function listDir(PhingFile $f) {
297
        $strPath = (string) $f->getAbsolutePath();
298
        $d = @dir($strPath);
299
        if (!$d) {
300
            return null;
301
        }
302
        $list = array();
303
        while($entry = $d->read()) {
304
            if ($entry != "." && $entry != "..") {
305
                array_push($list, $entry);
306
            }
307
        }
308
        $d->close();
309
        unset($d);
310
        return $list;
311
    }
312
 
313
    /**
314
     * Create a new directory denoted by the given abstract pathname,
315
     * returning true if and only if the operation succeeds.
316
     */
317
    function createDirectory(&$f) {
318
        return @mkdir($f->getAbsolutePath(),0755);
319
    }
320
 
321
    /**
322
     * Rename the file or directory denoted by the first abstract pathname to
323
     * the second abstract pathname, returning true if and only if
324
     * the operation succeeds.
325
     *
326
     * @param PhingFile $f1 abstract source file
327
     * @param PhingFile $f2 abstract destination file
328
     * @return void
329
     * @throws Exception if rename cannot be performed
330
     */
331
    function rename(PhingFile $f1, PhingFile $f2) {
332
        // get the canonical paths of the file to rename
333
        $src = $f1->getAbsolutePath();
334
        $dest = $f2->getAbsolutePath();
335
        if (false === @rename($src, $dest)) {
336
            $msg = "Rename FAILED. Cannot rename $src to $dest. $php_errormsg";
337
            throw new Exception($msg);
338
        }
339
    }
340
 
341
    /**
342
     * Set the last-modified time of the file or directory denoted by the
343
     * given abstract pathname returning true if and only if the
344
     * operation succeeds.
345
     * @return void
346
     * @throws Exception
347
     */
348
    function setLastModifiedTime(PhingFile $f, $time) {
349
        $path = $f->getPath();
350
        $success = @touch($path, $time);
351
        if (!$success) {
352
            throw new Exception("Could not touch '" . $path . "' due to: $php_errormsg");
353
        }
354
    }
355
 
356
    /**
357
     * Mark the file or directory denoted by the given abstract pathname as
358
     * read-only, returning <code>true</code> if and only if the operation
359
     * succeeds.
360
     */
361
    function setReadOnly($f) {
362
        throw new Exception("setReadonle() not implemented by local fs driver");
363
    }
364
 
365
    /* -- Filesystem interface -- */
366
 
367
    /**
368
     * List the available filesystem roots, return array of PhingFile objects
369
     */
370
    function listRoots() {
371
        throw new Exception("SYSTEM ERROR [listRoots() not implemented by local fs driver]");
372
    }
373
 
374
    /* -- Basic infrastructure -- */
375
 
376
    /**
377
     * Compare two abstract pathnames lexicographically.
378
     */
379
    function compare($f1, $f2) {
380
        throw new Exception("SYSTEM ERROR [compare() not implemented by local fs driver]");
381
    }
382
 
383
    /**
384
     * Copy a file.
385
     *
386
     * @param PhingFile $src Source path and name file to copy.
387
     * @param PhingFile $dest Destination path and name of new file.
388
     *
389
     * @return void
390
     * @throws Exception if file cannot be copied.
391
     */
392
    function copy(PhingFile $src, PhingFile $dest) {
393
        global $php_errormsg;
394
        $srcPath  = $src->getAbsolutePath();
395
        $destPath = $dest->getAbsolutePath();
396
 
397
        if (false === @copy($srcPath, $destPath)) { // Copy FAILED. Log and return err.
398
            // Add error from php to end of log message. $php_errormsg.
399
            $msg = "FileSystem::copy() FAILED. Cannot copy $srcPath to $destPath. $php_errormsg";
400
            throw new Exception($msg);
401
        }
402
 
403
        try {
404
            $dest->setMode($src->getMode());
405
        } catch(Exception $exc) {
406
            // [MA] does chmod returns an error on systems that do not support it ?
407
            // eat it up for now.
408
        }
409
    }
410
 
411
	/**
412
	 * Change the ownership on a file or directory.
413
	 *
414
	 * @param    string $pathname Path and name of file or directory.
415
	 * @param    string $user The user name or number of the file or directory. See http://us.php.net/chown
416
	 *
417
	 * @return void
418
	 * @throws Exception if operation failed.
419
	 */
420
	function chown($pathname, $user) {
421
		if (false === @chown($pathname, $user)) {// FAILED.
422
			$msg = "FileSystem::chown() FAILED. Cannot chown $pathname. User $user." . (isset($php_errormsg) ? ' ' . $php_errormsg : "");
423
			throw new Exception($msg);
424
		}
425
    }
426
 
427
    /**
428
     * Change the permissions on a file or directory.
429
     *
430
     * @param    pathname    String. Path and name of file or directory.
431
     * @param    mode        Int. The mode (permissions) of the file or
432
     *                        directory. If using octal add leading 0. eg. 0777.
433
     *                        Mode is affected by the umask system setting.
434
     *
435
     * @return void
436
     * @throws Exception if operation failed.
437
     */
438
    function chmod($pathname, $mode) {
439
        $str_mode = decoct($mode); // Show octal in messages.
440
        if (false === @chmod($pathname, $mode)) {// FAILED.
441
            $msg = "FileSystem::chmod() FAILED. Cannot chmod $pathname. Mode $str_mode." . (isset($php_errormsg) ? ' ' . $php_errormsg : "");
442
            throw new Exception($msg);
443
        }
444
    }
445
 
446
    /**
447
     * Locks a file and throws an Exception if this is not possible.
448
     * @return void
449
     * @throws Exception
450
     */
451
    function lock(PhingFile $f) {
452
        $filename = $f->getPath();
453
        $fp = @fopen($filename, "w");
454
        $result = @flock($fp, LOCK_EX);
455
        @fclose($fp);
456
        if (!$result) {
457
            throw new Exception("Could not lock file '$filename'");
458
        }
459
    }
460
 
461
    /**
462
     * Unlocks a file and throws an IO Error if this is not possible.
463
     *
464
     * @throws Exception
465
     * @return void
466
     */
467
    function unlock(PhingFile $f) {
468
        $filename = $f->getPath();
469
        $fp = @fopen($filename, "w");
470
        $result = @flock($fp, LOCK_UN);
471
        fclose($fp);
472
        if (!$result) {
473
            throw new Exception("Could not unlock file '$filename'");
474
        }
475
    }
476
 
477
    /**
478
     * Delete a file.
479
     *
480
     * @param    file    String. Path and/or name of file to delete.
481
     *
482
     * @return void
483
     * @throws Exception - if an error is encountered.
484
     */
485
    function unlink($file) {
486
        global $php_errormsg;
487
        if (false === @unlink($file)) {
488
            $msg = "FileSystem::unlink() FAILED. Cannot unlink '$file'. $php_errormsg";
489
            throw new Exception($msg);
490
        }
491
    }
492
 
493
    /**
494
     * Symbolically link a file to another name.
495
     *
496
     * Currently symlink is not implemented on Windows. Don't use if the application is to be portable.
497
     *
498
     * @param string $target Path and/or name of file to link.
499
     * @param string $link Path and/or name of link to be created.
500
     * @return void
501
     */
502
    function symlink($target, $link) {
503
 
504
        // If Windows OS then symlink() will report it is not supported in
505
        // the build. Use this error instead of checking for Windows as the OS.
506
 
507
        if (false === @symlink($target, $link)) {
508
            // Add error from php to end of log message. $php_errormsg.
509
            $msg = "FileSystem::Symlink() FAILED. Cannot symlink '$target' to '$link'. $php_errormsg";
510
            throw new Exception($msg);
511
        }
512
 
513
    }
514
 
515
    /**
516
     * Set the modification and access time on a file to the present time.
517
     *
518
     * @param string $file Path and/or name of file to touch.
519
     * @param int $time
520
     * @return void
521
     */
522
    function touch($file, $time = null) {
523
        global $php_errormsg;
524
 
525
        if (null === $time) {
526
            $error = @touch($file);
527
        } else {
528
            $error = @touch($file, $time);
529
        }
530
 
531
        if (false === $error) { // FAILED.
532
            // Add error from php to end of log message. $php_errormsg.
533
            $msg = "FileSystem::touch() FAILED. Cannot touch '$file'. $php_errormsg";
534
            throw new Exception($msg);
535
        }
536
    }
537
 
538
    /**
539
     * Delete an empty directory OR a directory and all of its contents.
540
     *
541
     * @param    dir    String. Path and/or name of directory to delete.
542
     * @param    children    Boolean.    False: don't delete directory contents.
543
     *                                    True: delete directory contents.
544
     *
545
     * @return void
546
     */
547
    function rmdir($dir, $children = false) {
548
        global $php_errormsg;
549
 
550
        // If children=FALSE only delete dir if empty.
551
        if (false === $children) {
552
 
553
            if (false === @rmdir($dir)) { // FAILED.
554
                // Add error from php to end of log message. $php_errormsg.
555
                $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg";
556
                throw new Exception($msg);
557
            }
558
 
559
        } else { // delete contents and dir.
560
 
561
            $handle = @opendir($dir);
562
 
563
            if (false === $handle) { // Error.
564
 
565
                $msg = "FileSystem::rmdir() FAILED. Cannot opendir() $dir. $php_errormsg";
566
                throw new Exception($msg);
567
 
568
            } else { // Read from handle.
569
 
570
                // Don't error on readdir().
571
                while (false !== ($entry = @readdir($handle))) {
572
 
573
                    if ($entry != '.' && $entry != '..') {
574
 
575
                        // Only add / if it isn't already the last char.
576
                        // This ONLY serves the purpose of making the Logger
577
                        // output look nice:)
578
 
579
                        if (strpos(strrev($dir), DIRECTORY_SEPARATOR) === 0) {// there is a /
580
                            $next_entry = $dir . $entry;
581
                        } else { // no /
582
                            $next_entry = $dir . DIRECTORY_SEPARATOR . $entry;
583
                        }
584
 
585
                        // NOTE: As of php 4.1.1 is_dir doesn't return FALSE it
586
                        // returns 0. So use == not ===.
587
 
588
                        // Don't error on is_dir()
589
                        if (false == @is_dir($next_entry)) { // Is file.
590
 
591
                            try {
592
                                self::unlink($next_entry); // Delete.
593
                            } catch (Exception $e) {
594
                                $msg = "FileSystem::Rmdir() FAILED. Cannot FileSystem::Unlink() $next_entry. ". $e->getMessage();
595
                                throw new Exception($msg);
596
                            }
597
 
598
                        } else { // Is directory.
599
 
600
                            try {
601
                                self::rmdir($next_entry, true); // Delete
602
                            } catch (Exception $e) {
603
                                $msg = "FileSystem::rmdir() FAILED. Cannot FileSystem::rmdir() $next_entry. ". $e->getMessage();
604
                                throw new Exception($msg);
605
                            }
606
 
607
                        } // end is_dir else
608
                    } // end .. if
609
                } // end while
610
            } // end handle if
611
 
612
            // Don't error on closedir()
613
            @closedir($handle);
614
 
615
            if (false === @rmdir($dir)) { // FAILED.
616
                // Add error from php to end of log message. $php_errormsg.
617
                $msg = "FileSystem::rmdir() FAILED. Cannot rmdir $dir. $php_errormsg";
618
                throw new Exception($msg);
619
            }
620
 
621
        }
622
 
623
    }
624
 
625
    /**
626
     * Set the umask for file and directory creation.
627
     *
628
     * @param    mode    Int. Permissions ususally in ocatal. Use leading 0 for
629
     *                    octal. Number between 0 and 0777.
630
     *
631
     * @return void
632
     * @throws Exception if there is an error performing operation.
633
     */
634
    function umask($mode) {
635
        global $php_errormsg;
636
 
637
        // CONSIDERME:
638
        // Throw a warning if mode is 0. PHP converts illegal octal numbers to
639
        // 0 so 0 might not be what the user intended.
640
 
641
        $str_mode = decoct($mode); // Show octal in messages.
642
 
643
        if (false === @umask($mode)) { // FAILED.
644
            // Add error from php to end of log message. $php_errormsg.
645
            $msg = "FileSystem::Umask() FAILED. Value $mode. $php_errormsg";
646
            throw new Exception($msg);
647
        }
648
    }
649
 
650
    /**
651
     * Compare the modified time of two files.
652
     *
653
     * @param    file1    String. Path and name of file1.
654
     * @param    file2    String. Path and name of file2.
655
     *
656
     * @return    Int.     1 if file1 is newer.
657
     *                 -1 if file2 is newer.
658
     *                  0 if files have the same time.
659
     *                  Err object on failure.
660
     *
661
     * @throws Exception - if cannot get modified time of either file.
662
     */
663
    function compareMTimes($file1, $file2) {
664
 
665
        $mtime1 = filemtime($file1);
666
        $mtime2 = filemtime($file2);
667
 
668
        if ($mtime1 === false) { // FAILED. Log and return err.
669
            // Add error from php to end of log message. $php_errormsg.
670
            $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file1.";
671
            throw new Exception($msg);
672
        } elseif ($mtime2 === false) { // FAILED. Log and return err.
673
            // Add error from php to end of log message. $php_errormsg.
674
            $msg = "FileSystem::compareMTimes() FAILED. Cannot can not get modified time of $file2.";
675
            throw new Exception($msg);
676
        } else { // Worked. Log and return compare.
677
            // Compare mtimes.
678
            if ($mtime1 == $mtime2) {
679
                return 0;
680
            } else {
681
                return ($mtime1 < $mtime2) ? -1 : 1;
682
            } // end compare
683
        }
684
    }
685
 
686
}