File.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4: */
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.0 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Richard Heyes <richard@php.net> |
  17. // | Tal Peer <tal@php.net> |
  18. // +----------------------------------------------------------------------+
  19. /*
  20. * Example 1:
  21. * $stdin = File::readAll('php://stdin');
  22. *
  23. * Example 2:
  24. * while ($data = File::read('/blaat/bar')) {
  25. * // Code...
  26. * }
  27. */
  28. require_once('PEAR.php');
  29. /**
  30. * The default number of bytes for reading
  31. * @const FILE_DEFAULT_READSIZE
  32. */
  33. define('FILE_DEFAULT_READSIZE', 1024, true);
  34. /**
  35. * Mode to use for reading from files
  36. * @const FILE_MODE_READ
  37. */
  38. define('FILE_MODE_READ', 'rb', true);
  39. /**
  40. * Mode to use for truncating files, then writing
  41. * @const FILE_MODE_WRITE
  42. */
  43. define('FILE_MODE_WRITE', 'wb', true);
  44. /**
  45. * Mode to use for appending to files
  46. * @const FILE_MODE_APPEND
  47. */
  48. define('FILE_MODE_APPEND', 'ab', true);
  49. /**
  50. * Use this when a shared (read) lock is required
  51. * @const FILE_LOCK_SHARED
  52. */
  53. define('FILE_LOCK_SHARED', LOCK_SH, true);
  54. /**
  55. * Use this when an exclusive (write) lock is required
  56. * @const FILE_LOCK_EXCLUSIVE
  57. */
  58. define('FILE_LOCK_EXCLUSIVE', LOCK_EX, true);
  59. /**
  60. * Class for handling files
  61. *
  62. * A class with common functions for writing,
  63. * reading and handling files and directories
  64. *
  65. *
  66. * @author Richard Heyes <richard@php.net>
  67. * @author Tal Peer <tal@php.net>
  68. * @access public
  69. * @version 0.9
  70. * @package File
  71. */
  72. class File extends PEAR
  73. {
  74. /**
  75. * Destructor
  76. *
  77. * Unlocks any locked file pointers and closes all filepointers
  78. * @access private
  79. */
  80. function _File()
  81. {
  82. $locks = &PEAR::getStaticProperty('File', 'locks');
  83. $filePointers = &PEAR::getStaticProperty('File', 'filePointers');
  84. for ($i = 0; $i < count($locks); $i++) {
  85. flock($this->locks[$i], LOCK_UN);
  86. }
  87. if (!empty($filePointers)) {
  88. foreach ($filePointers as $fname => $value) {
  89. foreach ($value as $mode => $value) {
  90. @fclose($filePointers[$fname][$mode]);
  91. }
  92. }
  93. }
  94. }
  95. /**
  96. * Handles file pointers. If a file pointer needs to be opened,
  97. * it will be. If it already exists (based on filename and mode)
  98. * then the existing one will be returned.
  99. *
  100. * @access private
  101. * @param string $filename Filename to be used
  102. * @param string $mode Mode to open the file in
  103. * @param mixed $lock Type of lock to use
  104. * @return mixed PEAR_Error on error and file pointer resource on success
  105. */
  106. function &_getFilePointer($filename, $mode, $lock = false)
  107. {
  108. $filePointers = &PEAR::getStaticProperty('File', 'filePointers');
  109. // Need to open first...
  110. if (!isset($filePointers[$filename][$mode]) OR !is_resource($filePointers[$filename][$mode])) {
  111. // Check it exists
  112. if (FILE_MODE_READ == $mode AND !preg_match('/^(http|https|ftp|php):\/\//i', $filename) AND !file_exists($filename)) {
  113. return PEAR::raiseError('File does not exist: ' . $filename);
  114. // Writeable?
  115. } elseif ( (FILE_MODE_WRITE == $mode OR FILE_MODE_APPEND == $mode)
  116. AND !file_exists($filename)
  117. AND !is_writeable(dirname($filename))) {
  118. return PEAR::raiseError('Could not create file: ' . $filename);
  119. } elseif ( (FILE_MODE_WRITE == $mode OR FILE_MODE_APPEND == $mode)
  120. AND !is_writeable(dirname($filename))) {
  121. return PEAR::raiseError('File is not writeable: ' . $filename);
  122. }
  123. $filePointers[$filename][$mode] = @fopen($filename, $mode);
  124. if (false === $filePointers[$filename][$mode]) {
  125. return PEAR::raiseError('Failed to open file: ' . $filename);
  126. }
  127. }
  128. // Lock it?
  129. if ($lock) {
  130. $locks = &PEAR::getStaticProperty('File', 'locks');
  131. if (flock($filePointers[$filename][$mode], $lock)) {
  132. $this->locks[] = &$filePointers[$filename][$mode];
  133. }
  134. }
  135. return $filePointers[$filename][$mode];
  136. }
  137. /**
  138. * Reads an entire file and returns it.
  139. *
  140. * @access public
  141. * @param string $filename Name of file to read from
  142. * @param mixed $lock Type of lock to use
  143. * @return mixed PEAR_Error if an error has occured or a string with the contents of the the file
  144. */
  145. function readAll($filename, $lock = false)
  146. {
  147. $file = '';
  148. while (($tmp = File::read($filename, FILE_DEFAULT_READSIZE, $lock)) !== FALSE) {
  149. if (PEAR::isError($tmp)) {
  150. return $tmp;
  151. }
  152. $file .= $tmp;
  153. }
  154. return $file;
  155. }
  156. /**
  157. * Returns a specified number of bytes of a file. Defaults to 1024.
  158. *
  159. * @access public
  160. * @param string $filename Name of file to read from
  161. * @param integer $size Bytes to read
  162. * @param mixed $lock Type of lock to use
  163. * @return mixed PEAR_Error on error or a string which contains the data read
  164. * Will also return false upon EOF
  165. */
  166. function read($filename, $size = FILE_DEFAULT_READSIZE, $lock = false)
  167. {
  168. static $filePointers; // Used to prevent unnecessary calls to _getFilePointer()
  169. if (0 == $size) {
  170. return File::readAll($filename);
  171. }
  172. if (!isset($filePointers[$filename]) OR !is_resource($filePointers[$filename])) {
  173. if (PEAR::isError($fp = &File::_getFilePointer($filename, FILE_MODE_READ, $lock))) {
  174. return $fp;
  175. }
  176. $filePointers[$filename] = &$fp;
  177. } else {
  178. $fp = &$filePointers[$filename];
  179. }
  180. return !feof($fp) ? fread($fp, $size) : false;
  181. }
  182. /**
  183. * Writes the given data to the given filename. Defaults to no lock, append mode.
  184. *
  185. * @access public
  186. * @param string $filename Name of file to write to
  187. * @param string $data Data to write to file
  188. * @param string $mode Mode to open file in
  189. * @param mixed $lock Type of lock to use
  190. * @return mixed PEAR_Error on error or number of bytes written to file.
  191. */
  192. function write($filename, $data, $mode = FILE_MODE_APPEND, $lock = false)
  193. {
  194. if (!PEAR::isError($fp = &File::_getFilePointer($filename, $mode, $lock))) {
  195. if (($bytes = fwrite($fp, $data, strlen($data))) == -1) {
  196. return PEAR::raiseError(sprintf('fwrite() call failed to write data: "%s" to file: "%s"', $data, $filename));
  197. } else {
  198. return $bytes;
  199. }
  200. }
  201. return $fp;
  202. }
  203. /**
  204. * Reads and returns a single character from given filename
  205. *
  206. * @access public
  207. * @param string $filename Name of file to read from
  208. * @param mixed $lock Type of lock to use
  209. * @return mixed PEAR_Error on error or one character of the specified file
  210. */
  211. function readChar($filename, $lock = false)
  212. {
  213. return File::read($filename, 1, $lock);
  214. }
  215. /**
  216. * Writes a single character to a file
  217. *
  218. * @access public
  219. * @param string $filename Name of file to write to
  220. * @param string $char Character to write
  221. * @param string $mode Mode to use when writing
  222. * @param mixed $lock Type of lock to use
  223. * @return mixed PEAR_Error on error, or 1 on success
  224. */
  225. function writeChar($filename, $char, $mode = FILE_MODE_APPEND, $lock = false)
  226. {
  227. if (!PEAR::isError($fp = &File::_getFilePointer($filename, $mode, $lock))) {
  228. if (fwrite($fp, $char, 1) == -1) {
  229. return PEAR::raiseError(sprintf('fwrite() call failed to write data: "%s" to file: "%s"', $data, $filename));
  230. } else {
  231. return 1;
  232. }
  233. }
  234. return $fp;
  235. }
  236. /**
  237. * Returns a line of the file
  238. *
  239. * @access public
  240. * @param string $filename Name of file to read from
  241. * @param boolean $lock Type of lock to use
  242. * @return mixed PEAR_Error on error or a string containing the line read from file
  243. */
  244. function readLine($filename, $lock = false)
  245. {
  246. static $filePointers; // Used to prevent unnecessary calls to _getFilePointer()
  247. if (!isset($filePointers[$filename]) OR !is_resource($filePointers[$filename])) {
  248. if (PEAR::isError($fp = &File::_getFilePointer($filename, FILE_MODE_READ, $lock))) {
  249. return $fp;
  250. }
  251. $filePointers[$filename] = &$fp;
  252. } else {
  253. $fp = &$filePointers[$filename];
  254. }
  255. if (feof($fp)) {
  256. return false;
  257. }
  258. $fileString = '';
  259. while (($fileChar = fgetc($fp)) != "\n" AND !feof($fp)) {
  260. $fileString .= $fileChar;
  261. }
  262. return substr($fileString, -1) == "\r" ? substr($fileString, 0, -1) : $fileString;
  263. }
  264. /**
  265. * Writes a single line, appending a LF (by default)
  266. *
  267. * @access public
  268. * @param string $filename Name of file to write to
  269. * @param string $line Line of data to be written to file
  270. * @param string $mode Write mode, can be either FILE_MODE_WRITE or FILE_MODE_APPEND
  271. * @param string $crlf The CRLF your system is using. UNIX = \n Windows = \r\n Mac = \r
  272. * @param mixed $lock Type of lock to use
  273. * @return mixed PEAR_Error on error or number of bytes written to file (including appended crlf)
  274. */
  275. function writeLine($filename, $line, $mode = FILE_MODE_APPEND, $crlf = "\n", $lock = false)
  276. {
  277. if(!PEAR::isError($fp = &File::_getFilePointer($filename, $mode, $lock))){
  278. if (($bytes = fwrite($fp, $line . $crlf)) == -1) {
  279. return PEAR::raiseError(sprintf('fwrite() call failed to write data: "%s" to file: "%s"', $data, $filename));
  280. } else {
  281. return $bytes;
  282. }
  283. }
  284. return $fp;
  285. }
  286. /**
  287. * This rewinds a filepointer to the start of a file
  288. *
  289. * @access public
  290. * @param string $filename The filename
  291. * @param string $mode Mode the file was opened in
  292. * @return mixed PEAR Error on error, true on success
  293. */
  294. function rewind($filename, $mode)
  295. {
  296. if (!PEAR::isError($fp = &File::_getFilePointer($filename, $mode))) {
  297. return rewind($fp) ? true : PEAR::raiseError('Failed to rewind file: ' . $filename);
  298. }
  299. return $fp;
  300. }
  301. /**
  302. * This closes an open file pointer
  303. *
  304. * @access public
  305. * @param string $filename The filename that was opened
  306. * @param string $mode Mode the file was opened in
  307. * @return mixed PEAR Error on error, true otherwise
  308. */
  309. function close($filename, $mode)
  310. {
  311. if (!PEAR::isError($fp = &File::_getFilePointer($filename, $mode))) {
  312. $filePointers = &PEAR::getStaticProperty('File', 'filePointers');
  313. unset($filePointers[$filename][$mode]);
  314. return fclose($fp) ? true : PEAR::raiseError('Failed to close file: ' . $filename);
  315. }
  316. return $fp;
  317. }
  318. /**
  319. * This unlocks a locked file pointer.
  320. *
  321. * @access public
  322. * @param string $filename The filename that was opened
  323. * @param string $mode Mode the file was opened in
  324. * @return mixed PEAR Error on error, true otherwise
  325. */
  326. function unlock($filename, $mode)
  327. {
  328. if (!PEAR::isError($fp = &FILE::_getFilePointer($filename, $mode))) {
  329. return flock($fp, LOCK_UN) ? true : PEAR::raiseError('Failed to unlock file: ' . $filename);
  330. }
  331. return $fp;
  332. }
  333. /**
  334. * Returns a string path built from the array $pathParts. Where a join occurs
  335. * multiple separators are removed. Joins using the optional separator, defaulting
  336. * to the PHP DIRECTORY_SEPARATOR constant.
  337. *
  338. * @access public
  339. * @param array $parts Array containing the parts to be joined
  340. * @param string $separator The system directory seperator
  341. */
  342. function buildPath($parts, $separator = DIRECTORY_SEPARATOR)
  343. {
  344. for ($i = 0; $i < count($parts); $i++) {
  345. if (0 == $i) {
  346. $parts[$i] = File::stripTrailingSeparators($parts[$i], $separator);
  347. } elseif(count($parts) - 1 == $i) {
  348. $parts[$i] = File::stripLeadingSeparators($parts[$i], $separator);
  349. } else {
  350. $parts[$i] = File::stripTrailingSeparators($parts[$i], $separator);
  351. $parts[$i] = File::stripLeadingSeparators($parts[$i], $separator);
  352. }
  353. }
  354. return implode($separator, $parts);
  355. }
  356. /**
  357. * Strips trailing separators from the given path
  358. *
  359. * @access public
  360. * @param string $path Path to use
  361. * @param string $separator Separator to look for
  362. * @return string Resulting path
  363. */
  364. function stripTrailingSeparators($path, $separator = DIRECTORY_SEPARATOR)
  365. {
  366. while (substr($path, -1) == $separator) {
  367. $path = substr($path, 0, -1);
  368. }
  369. return $path;
  370. }
  371. /**
  372. * Strips leading separators from the given path
  373. *
  374. * @access public
  375. * @param string $path Path to use
  376. * @param string $separator Separator to look for
  377. * @return string Resulting path
  378. */
  379. function stripLeadingSeparators($path, $separator = DIRECTORY_SEPARATOR)
  380. {
  381. while (substr($path, 0, 1) == $separator) {
  382. $path = substr($path, 1);
  383. }
  384. return $path;
  385. }
  386. /**
  387. * Returns a path without leading / or C:\. If this is not
  388. * present the path is returned as is.
  389. *
  390. * @access public
  391. * @param string $path The path to be processed
  392. * @return string The processed path or the path as is
  393. */
  394. function skipRoot($path)
  395. {
  396. if (File::isAbsolute($path)) {
  397. if (DIRECTORY_SEPARATOR == "/") {
  398. return substr($path,1);
  399. } elseif(DIRECTORY_SEPARATOR == "\\") {
  400. return substr($path, 3);
  401. }
  402. } else {
  403. return $path;
  404. }
  405. }
  406. /**
  407. * Returns the temp directory according to either the TMP, TMPDIR, or TEMP env
  408. * variables. If these are not set it will also check for the existence of
  409. * /tmp, %WINDIR%\temp
  410. *
  411. * @access public
  412. * @return string The system tmp directory
  413. */
  414. function getTempDir()
  415. {
  416. if (OS_WINDOWS){
  417. if (isset($_ENV['TEMP'])) {
  418. return $_ENV['TEMP'];
  419. }
  420. if (isset($_ENV['TMP'])) {
  421. return $_ENV['TMP'];
  422. }
  423. if (isset($_ENV['windir'])) {
  424. return $_ENV['windir'] . '\temp';
  425. }
  426. return $_ENV['SystemRoot'] . '\temp';
  427. }
  428. if (isset($_ENV['TMPDIR'])) {
  429. return $_ENV['TMPDIR'];
  430. }
  431. return '/tmp';
  432. }
  433. /**
  434. * Returns a temporary filename using tempnam() and the above getTmpDir() function.
  435. *
  436. * @access public
  437. * @param string $dirname Optional directory name for the tmp file
  438. * @return string Filename and path of the tmp file
  439. */
  440. function getTempFile($dirname = NULL)
  441. {
  442. if (is_null($dirname)) {
  443. $dirname = File::getTempDir();
  444. }
  445. return tempnam($dirname, 'temp.');
  446. }
  447. /**
  448. * Returns boolean based on whether given path is absolute or not.
  449. *
  450. * @access public
  451. * @param string $path Given path
  452. * @return boolean True if the path is absolute, false if it is not
  453. */
  454. function isAbsolute($path)
  455. {
  456. if (preg_match("/\.\./", $path)) {
  457. return false;
  458. }
  459. if (DIRECTORY_SEPARATOR == '/' AND (substr($path, 0, 1) == '/' OR substr($path, 0, 1) == '~')) {
  460. return true;
  461. } elseif(DIRECTORY_SEPARATOR == '\\' AND preg_match('/^[a-z]:\\\/i', $path)) {
  462. return true;
  463. }
  464. return false;
  465. }
  466. }
  467. PEAR::registerShutdownFunc(array('File', '_File'));
  468. ?>