UnixFileSystem.php 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. <?php
  2. /*
  3. * $Id: UnixFileSystem.php 905 2010-10-05 16:28:03Z mrook $
  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. include_once 'phing/system/io/FileSystem.php';
  22. /**
  23. * UnixFileSystem class. This class encapsulates the basic file system functions
  24. * for platforms using the unix (posix)-stylish filesystem. It wraps php native
  25. * functions suppressing normal PHP error reporting and instead uses Exception
  26. * to report and error.
  27. *
  28. * This class is part of a oop based filesystem abstraction and targeted to run
  29. * on all supported php platforms.
  30. *
  31. * Note: For debugging turn track_errors on in the php.ini. The error messages
  32. * and log messages from this class will then be clearer because $php_errormsg
  33. * is passed as part of the message.
  34. *
  35. * FIXME:
  36. * - Comments
  37. * - Error handling reduced to min, error are handled by PhingFile mainly
  38. *
  39. * @author Andreas Aderhold, andi@binarycloud.com
  40. * @version $Revision: 905 $
  41. * @package phing.system.io
  42. */
  43. class UnixFileSystem extends FileSystem {
  44. /**
  45. * returns OS dependant path separator char
  46. */
  47. function getSeparator() {
  48. return '/';
  49. }
  50. /**
  51. * returns OS dependant directory separator char
  52. */
  53. function getPathSeparator() {
  54. return ':';
  55. }
  56. /**
  57. * A normal Unix pathname contains no duplicate slashes and does not end
  58. * with a slash. It may be the empty string.
  59. *
  60. * Check that the given pathname is normal. If not, invoke the real
  61. * normalizer on the part of the pathname that requires normalization.
  62. * This way we iterate through the whole pathname string only once.
  63. */
  64. function normalize($strPathname) {
  65. if (!strlen($strPathname)) {
  66. return;
  67. }
  68. // Resolve home directories. We assume /home is where all home
  69. // directories reside, b/c there is no other way to do this with
  70. // PHP AFAIK.
  71. if ($strPathname{0} === "~") {
  72. if ($strPathname{1} === "/") { // like ~/foo => /home/user/foo
  73. $strPathname = "/home/" . get_current_user() . substr($strPathname, 1);
  74. } else { // like ~foo => /home/foo
  75. $pos = strpos($strPathname, "/");
  76. $name = substr($strPathname, 1, $pos - 2);
  77. $strPathname = "/home/" . $name . substr($strPathname, $pos);
  78. }
  79. }
  80. $n = strlen($strPathname);
  81. $prevChar = 0;
  82. for ($i=0; $i < $n; $i++) {
  83. $c = $strPathname{$i};
  84. if (($prevChar === '/') && ($c === '/')) {
  85. return self::normalizer($strPathname, $n, $i - 1);
  86. }
  87. $prevChar = $c;
  88. }
  89. if ($prevChar === '/') {
  90. return self::normalizer($strPathname, $n, $n - 1);
  91. }
  92. return $strPathname;
  93. }
  94. /**
  95. * Normalize the given pathname, whose length is $len, starting at the given
  96. * $offset; everything before this offset is already normal.
  97. */
  98. protected function normalizer($pathname, $len, $offset) {
  99. if ($len === 0) {
  100. return $pathname;
  101. }
  102. $n = (int) $len;
  103. while (($n > 0) && ($pathname{$n-1} === '/')) {
  104. $n--;
  105. }
  106. if ($n === 0) {
  107. return '/';
  108. }
  109. $sb = "";
  110. if ($offset > 0) {
  111. $sb .= substr($pathname, 0, $offset);
  112. }
  113. $prevChar = 0;
  114. for ($i = $offset; $i < $n; $i++) {
  115. $c = $pathname{$i};
  116. if (($prevChar === '/') && ($c === '/')) {
  117. continue;
  118. }
  119. $sb .= $c;
  120. $prevChar = $c;
  121. }
  122. return $sb;
  123. }
  124. /**
  125. * Compute the length of the pathname string's prefix. The pathname
  126. * string must be in normal form.
  127. */
  128. function prefixLength($pathname) {
  129. if (strlen($pathname === 0)) {
  130. return 0;
  131. }
  132. return (($pathname{0} === '/') ? 1 : 0);
  133. }
  134. /**
  135. * Resolve the child pathname string against the parent.
  136. * Both strings must be in normal form, and the result
  137. * will be in normal form.
  138. */
  139. function resolve($parent, $child) {
  140. if ($child === "") {
  141. return $parent;
  142. }
  143. if ($child{0} === '/') {
  144. if ($parent === '/') {
  145. return $child;
  146. }
  147. return $parent.$child;
  148. }
  149. if ($parent === '/') {
  150. return $parent.$child;
  151. }
  152. return $parent.'/'.$child;
  153. }
  154. function getDefaultParent() {
  155. return '/';
  156. }
  157. function isAbsolute(PhingFile $f) {
  158. return ($f->getPrefixLength() !== 0);
  159. }
  160. /**
  161. * the file resolver
  162. */
  163. function resolveFile(PhingFile $f) {
  164. // resolve if parent is a file oject only
  165. if ($this->isAbsolute($f)) {
  166. return $f->getPath();
  167. } else {
  168. return $this->resolve(Phing::getProperty("user.dir"), $f->getPath());
  169. }
  170. }
  171. /* -- most of the following is mapped to the php natives wrapped by FileSystem */
  172. /* -- Attribute accessors -- */
  173. function getBooleanAttributes(&$f) {
  174. //$rv = getBooleanAttributes0($f);
  175. $name = $f->getName();
  176. $hidden = (strlen($name) > 0) && ($name{0} == '.');
  177. return ($hidden ? $this->BA_HIDDEN : 0);
  178. }
  179. /**
  180. * set file readonly on unix
  181. */
  182. function setReadOnly($f) {
  183. if ($f instanceof File) {
  184. $strPath = (string) $f->getPath();
  185. $perms = (int) (@fileperms($strPath) & 0444);
  186. return FileSystem::Chmod($strPath, $perms);
  187. } else {
  188. throw new Exception("IllegalArgutmentType: Argument is not File");
  189. }
  190. }
  191. /**
  192. * compares file paths lexicographically
  193. */
  194. function compare($f1, $f2) {
  195. if ( ($f1 instanceof PhingFile) && ($f2 instanceof PhingFile) ) {
  196. $f1Path = $f1->getPath();
  197. $f2Path = $f2->getPath();
  198. return (boolean) strcmp((string) $f1Path, (string) $f2Path);
  199. } else {
  200. throw new Exception("IllegalArgutmentType: Argument is not PhingFile");
  201. }
  202. }
  203. /**
  204. * Copy a file, takes care of symbolic links
  205. *
  206. * @param PhingFile $src Source path and name file to copy.
  207. * @param PhingFile $dest Destination path and name of new file.
  208. *
  209. * @return void
  210. * @throws Exception if file cannot be copied.
  211. */
  212. function copy(PhingFile $src, PhingFile $dest) {
  213. global $php_errormsg;
  214. if (!$src->isLink())
  215. {
  216. return parent::copy($src, $dest);
  217. }
  218. $srcPath = $src->getAbsolutePath();
  219. $destPath = $dest->getAbsolutePath();
  220. $linkTarget = $src->getLinkTarget();
  221. if (false === @symlink($linkTarget, $destPath))
  222. {
  223. $msg = "FileSystem::copy() FAILED. Cannot create symlink from $destPath to $linkTarget.";
  224. throw new Exception($msg);
  225. }
  226. }
  227. /* -- fs interface --*/
  228. function listRoots() {
  229. if (!$this->checkAccess('/', false)) {
  230. die ("Can not access root");
  231. }
  232. return array(new PhingFile("/"));
  233. }
  234. /**
  235. * returns the contents of a directory in an array
  236. */
  237. function lister($f) {
  238. $dir = @opendir($f->getAbsolutePath());
  239. if (!$dir) {
  240. throw new Exception("Can't open directory " . $f->__toString());
  241. }
  242. $vv = array();
  243. while (($file = @readdir($dir)) !== false) {
  244. if ($file == "." || $file == "..") {
  245. continue;
  246. }
  247. $vv[] = (string) $file;
  248. }
  249. @closedir($dir);
  250. return $vv;
  251. }
  252. function fromURIPath($p) {
  253. if (StringHelper::endsWith("/", $p) && (strlen($p) > 1)) {
  254. // "/foo/" --> "/foo", but "/" --> "/"
  255. $p = substr($p, 0, strlen($p) - 1);
  256. }
  257. return $p;
  258. }
  259. /**
  260. * Whether file can be deleted.
  261. * @param PhingFile $f
  262. * @return boolean
  263. */
  264. function canDelete(PhingFile $f)
  265. {
  266. @clearstatcache();
  267. $dir = dirname($f->getAbsolutePath());
  268. return (bool) @is_writable($dir);
  269. }
  270. }