File.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  1. <?php
  2. /**
  3. * Convenience class for reading, writing and appending to files.
  4. *
  5. * PHP 5
  6. *
  7. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  9. *
  10. * Licensed under The MIT License
  11. * For full copyright and license information, please see the LICENSE.txt
  12. * Redistributions of files must retain the above copyright notice.
  13. *
  14. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  15. * @link http://cakephp.org CakePHP(tm) Project
  16. * @package Cake.Utility
  17. * @since CakePHP(tm) v 0.2.9
  18. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  19. */
  20. App::uses('Folder', 'Utility');
  21. /**
  22. * Convenience class for reading, writing and appending to files.
  23. *
  24. * @package Cake.Utility
  25. */
  26. class File {
  27. /**
  28. * Folder object of the file
  29. *
  30. * @var Folder
  31. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$Folder
  32. */
  33. public $Folder = null;
  34. /**
  35. * File name
  36. *
  37. * @var string
  38. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$name
  39. */
  40. public $name = null;
  41. /**
  42. * File info
  43. *
  44. * @var array
  45. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$info
  46. */
  47. public $info = array();
  48. /**
  49. * Holds the file handler resource if the file is opened
  50. *
  51. * @var resource
  52. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$handle
  53. */
  54. public $handle = null;
  55. /**
  56. * Enable locking for file reading and writing
  57. *
  58. * @var boolean
  59. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$lock
  60. */
  61. public $lock = null;
  62. /**
  63. * Path property
  64. *
  65. * Current file's absolute path
  66. *
  67. * @var mixed null
  68. * http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::$path
  69. */
  70. public $path = null;
  71. /**
  72. * Constructor
  73. *
  74. * @param string $path Path to file
  75. * @param boolean $create Create file if it does not exist (if true)
  76. * @param integer $mode Mode to apply to the folder holding the file
  77. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File
  78. */
  79. public function __construct($path, $create = false, $mode = 0755) {
  80. $this->Folder = new Folder(dirname($path), $create, $mode);
  81. if (!is_dir($path)) {
  82. $this->name = basename($path);
  83. }
  84. $this->pwd();
  85. $create && !$this->exists() && $this->safe($path) && $this->create();
  86. }
  87. /**
  88. * Closes the current file if it is opened
  89. */
  90. public function __destruct() {
  91. $this->close();
  92. }
  93. /**
  94. * Creates the file.
  95. *
  96. * @return boolean Success
  97. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::create
  98. */
  99. public function create() {
  100. $dir = $this->Folder->pwd();
  101. if (is_dir($dir) && is_writable($dir) && !$this->exists()) {
  102. if (touch($this->path)) {
  103. return true;
  104. }
  105. }
  106. return false;
  107. }
  108. /**
  109. * Opens the current file with a given $mode
  110. *
  111. * @param string $mode A valid 'fopen' mode string (r|w|a ...)
  112. * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
  113. * @return boolean True on success, false on failure
  114. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::open
  115. */
  116. public function open($mode = 'r', $force = false) {
  117. if (!$force && is_resource($this->handle)) {
  118. return true;
  119. }
  120. if ($this->exists() === false) {
  121. if ($this->create() === false) {
  122. return false;
  123. }
  124. }
  125. $this->handle = fopen($this->path, $mode);
  126. if (is_resource($this->handle)) {
  127. return true;
  128. }
  129. return false;
  130. }
  131. /**
  132. * Return the contents of this file as a string.
  133. *
  134. * @param string $bytes where to start
  135. * @param string $mode A `fread` compatible mode.
  136. * @param boolean $force If true then the file will be re-opened even if its already opened, otherwise it won't
  137. * @return mixed string on success, false on failure
  138. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::read
  139. */
  140. public function read($bytes = false, $mode = 'rb', $force = false) {
  141. if ($bytes === false && $this->lock === null) {
  142. return file_get_contents($this->path);
  143. }
  144. if ($this->open($mode, $force) === false) {
  145. return false;
  146. }
  147. if ($this->lock !== null && flock($this->handle, LOCK_SH) === false) {
  148. return false;
  149. }
  150. if (is_int($bytes)) {
  151. return fread($this->handle, $bytes);
  152. }
  153. $data = '';
  154. while (!feof($this->handle)) {
  155. $data .= fgets($this->handle, 4096);
  156. }
  157. if ($this->lock !== null) {
  158. flock($this->handle, LOCK_UN);
  159. }
  160. if ($bytes === false) {
  161. $this->close();
  162. }
  163. return trim($data);
  164. }
  165. /**
  166. * Sets or gets the offset for the currently opened file.
  167. *
  168. * @param integer|boolean $offset The $offset in bytes to seek. If set to false then the current offset is returned.
  169. * @param integer $seek PHP Constant SEEK_SET | SEEK_CUR | SEEK_END determining what the $offset is relative to
  170. * @return mixed True on success, false on failure (set mode), false on failure or integer offset on success (get mode)
  171. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::offset
  172. */
  173. public function offset($offset = false, $seek = SEEK_SET) {
  174. if ($offset === false) {
  175. if (is_resource($this->handle)) {
  176. return ftell($this->handle);
  177. }
  178. } elseif ($this->open() === true) {
  179. return fseek($this->handle, $offset, $seek) === 0;
  180. }
  181. return false;
  182. }
  183. /**
  184. * Prepares a ASCII string for writing. Converts line endings to the
  185. * correct terminator for the current platform. If Windows, "\r\n" will be used,
  186. * all other platforms will use "\n"
  187. *
  188. * @param string $data Data to prepare for writing.
  189. * @param boolean $forceWindows
  190. * @return string The with converted line endings.
  191. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::prepare
  192. */
  193. public static function prepare($data, $forceWindows = false) {
  194. $lineBreak = "\n";
  195. if (DIRECTORY_SEPARATOR === '\\' || $forceWindows === true) {
  196. $lineBreak = "\r\n";
  197. }
  198. return strtr($data, array("\r\n" => $lineBreak, "\n" => $lineBreak, "\r" => $lineBreak));
  199. }
  200. /**
  201. * Write given data to this file.
  202. *
  203. * @param string $data Data to write to this File.
  204. * @param string $mode Mode of writing. {@link http://php.net/fwrite See fwrite()}.
  205. * @param string $force Force the file to open
  206. * @return boolean Success
  207. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::write
  208. */
  209. public function write($data, $mode = 'w', $force = false) {
  210. $success = false;
  211. if ($this->open($mode, $force) === true) {
  212. if ($this->lock !== null) {
  213. if (flock($this->handle, LOCK_EX) === false) {
  214. return false;
  215. }
  216. }
  217. if (fwrite($this->handle, $data) !== false) {
  218. $success = true;
  219. }
  220. if ($this->lock !== null) {
  221. flock($this->handle, LOCK_UN);
  222. }
  223. }
  224. return $success;
  225. }
  226. /**
  227. * Append given data string to this file.
  228. *
  229. * @param string $data Data to write
  230. * @param string $force Force the file to open
  231. * @return boolean Success
  232. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::append
  233. */
  234. public function append($data, $force = false) {
  235. return $this->write($data, 'a', $force);
  236. }
  237. /**
  238. * Closes the current file if it is opened.
  239. *
  240. * @return boolean True if closing was successful or file was already closed, otherwise false
  241. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::close
  242. */
  243. public function close() {
  244. if (!is_resource($this->handle)) {
  245. return true;
  246. }
  247. return fclose($this->handle);
  248. }
  249. /**
  250. * Deletes the file.
  251. *
  252. * @return boolean Success
  253. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::delete
  254. */
  255. public function delete() {
  256. if (is_resource($this->handle)) {
  257. fclose($this->handle);
  258. $this->handle = null;
  259. }
  260. if ($this->exists()) {
  261. return unlink($this->path);
  262. }
  263. return false;
  264. }
  265. /**
  266. * Returns the file info as an array with the following keys:
  267. *
  268. * - dirname
  269. * - basename
  270. * - extension
  271. * - filename
  272. * - filesize
  273. * - mime
  274. *
  275. * @return array File information.
  276. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::info
  277. */
  278. public function info() {
  279. if (!$this->info) {
  280. $this->info = pathinfo($this->path);
  281. }
  282. if (!isset($this->info['filename'])) {
  283. $this->info['filename'] = $this->name();
  284. }
  285. if (!isset($this->info['filesize'])) {
  286. $this->info['filesize'] = $this->size();
  287. }
  288. if (!isset($this->info['mime'])) {
  289. $this->info['mime'] = $this->mime();
  290. }
  291. return $this->info;
  292. }
  293. /**
  294. * Returns the file extension.
  295. *
  296. * @return string The file extension
  297. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::ext
  298. */
  299. public function ext() {
  300. if (!$this->info) {
  301. $this->info();
  302. }
  303. if (isset($this->info['extension'])) {
  304. return $this->info['extension'];
  305. }
  306. return false;
  307. }
  308. /**
  309. * Returns the file name without extension.
  310. *
  311. * @return string The file name without extension.
  312. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::name
  313. */
  314. public function name() {
  315. if (!$this->info) {
  316. $this->info();
  317. }
  318. if (isset($this->info['extension'])) {
  319. return basename($this->name, '.' . $this->info['extension']);
  320. } elseif ($this->name) {
  321. return $this->name;
  322. }
  323. return false;
  324. }
  325. /**
  326. * Makes file name safe for saving
  327. *
  328. * @param string $name The name of the file to make safe if different from $this->name
  329. * @param string $ext The name of the extension to make safe if different from $this->ext
  330. * @return string $ext The extension of the file
  331. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::safe
  332. */
  333. public function safe($name = null, $ext = null) {
  334. if (!$name) {
  335. $name = $this->name;
  336. }
  337. if (!$ext) {
  338. $ext = $this->ext();
  339. }
  340. return preg_replace("/(?:[^\w\.-]+)/", "_", basename($name, $ext));
  341. }
  342. /**
  343. * Get md5 Checksum of file with previous check of Filesize
  344. *
  345. * @param integer|boolean $maxsize in MB or true to force
  346. * @return string|false md5 Checksum {@link http://php.net/md5_file See md5_file()}, or false in case of an error
  347. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::md5
  348. */
  349. public function md5($maxsize = 5) {
  350. if ($maxsize === true) {
  351. return md5_file($this->path);
  352. }
  353. $size = $this->size();
  354. if ($size && $size < ($maxsize * 1024) * 1024) {
  355. return md5_file($this->path);
  356. }
  357. return false;
  358. }
  359. /**
  360. * Returns the full path of the file.
  361. *
  362. * @return string Full path to the file
  363. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::pwd
  364. */
  365. public function pwd() {
  366. if ($this->path === null) {
  367. $this->path = $this->Folder->slashTerm($this->Folder->pwd()) . $this->name;
  368. }
  369. return $this->path;
  370. }
  371. /**
  372. * Returns true if the file exists.
  373. *
  374. * @return boolean True if it exists, false otherwise
  375. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::exists
  376. */
  377. public function exists() {
  378. $this->clearStatCache();
  379. return (file_exists($this->path) && is_file($this->path));
  380. }
  381. /**
  382. * Returns the "chmod" (permissions) of the file.
  383. *
  384. * @return string|false Permissions for the file, or false in case of an error
  385. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::perms
  386. */
  387. public function perms() {
  388. if ($this->exists()) {
  389. return substr(sprintf('%o', fileperms($this->path)), -4);
  390. }
  391. return false;
  392. }
  393. /**
  394. * Returns the file size
  395. *
  396. * @return integer|false Size of the file in bytes, or false in case of an error
  397. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::size
  398. */
  399. public function size() {
  400. if ($this->exists()) {
  401. return filesize($this->path);
  402. }
  403. return false;
  404. }
  405. /**
  406. * Returns true if the file is writable.
  407. *
  408. * @return boolean True if it's writable, false otherwise
  409. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::writable
  410. */
  411. public function writable() {
  412. return is_writable($this->path);
  413. }
  414. /**
  415. * Returns true if the File is executable.
  416. *
  417. * @return boolean True if it's executable, false otherwise
  418. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::executable
  419. */
  420. public function executable() {
  421. return is_executable($this->path);
  422. }
  423. /**
  424. * Returns true if the file is readable.
  425. *
  426. * @return boolean True if file is readable, false otherwise
  427. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::readable
  428. */
  429. public function readable() {
  430. return is_readable($this->path);
  431. }
  432. /**
  433. * Returns the file's owner.
  434. *
  435. * @return integer|false The file owner, or false in case of an error
  436. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::owner
  437. */
  438. public function owner() {
  439. if ($this->exists()) {
  440. return fileowner($this->path);
  441. }
  442. return false;
  443. }
  444. /**
  445. * Returns the file's group.
  446. *
  447. * @return integer|false The file group, or false in case of an error
  448. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::group
  449. */
  450. public function group() {
  451. if ($this->exists()) {
  452. return filegroup($this->path);
  453. }
  454. return false;
  455. }
  456. /**
  457. * Returns last access time.
  458. *
  459. * @return integer|false Timestamp of last access time, or false in case of an error
  460. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastAccess
  461. */
  462. public function lastAccess() {
  463. if ($this->exists()) {
  464. return fileatime($this->path);
  465. }
  466. return false;
  467. }
  468. /**
  469. * Returns last modified time.
  470. *
  471. * @return integer|false Timestamp of last modification, or false in case of an error
  472. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::lastChange
  473. */
  474. public function lastChange() {
  475. if ($this->exists()) {
  476. return filemtime($this->path);
  477. }
  478. return false;
  479. }
  480. /**
  481. * Returns the current folder.
  482. *
  483. * @return Folder Current folder
  484. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::Folder
  485. */
  486. public function folder() {
  487. return $this->Folder;
  488. }
  489. /**
  490. * Copy the File to $dest
  491. *
  492. * @param string $dest Destination for the copy
  493. * @param boolean $overwrite Overwrite $dest if exists
  494. * @return boolean Success
  495. * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#File::copy
  496. */
  497. public function copy($dest, $overwrite = true) {
  498. if (!$this->exists() || is_file($dest) && !$overwrite) {
  499. return false;
  500. }
  501. return copy($this->path, $dest);
  502. }
  503. /**
  504. * Get the mime type of the file. Uses the finfo extension if
  505. * its available, otherwise falls back to mime_content_type
  506. *
  507. * @return false|string The mimetype of the file, or false if reading fails.
  508. */
  509. public function mime() {
  510. if (!$this->exists()) {
  511. return false;
  512. }
  513. if (function_exists('finfo_open')) {
  514. $finfo = finfo_open(FILEINFO_MIME);
  515. $finfo = finfo_file($finfo, $this->pwd());
  516. if (!$finfo) {
  517. return false;
  518. }
  519. list($type, $charset) = explode(';', $finfo);
  520. return $type;
  521. }
  522. if (function_exists('mime_content_type')) {
  523. return mime_content_type($this->pwd());
  524. }
  525. return false;
  526. }
  527. /**
  528. * Clear PHP's internal stat cache
  529. *
  530. * For 5.3 onwards it's possible to clear cache for just a single file. Passing true
  531. * will clear all the stat cache.
  532. *
  533. * @param boolean $all Clear all cache or not
  534. * @return void
  535. */
  536. public function clearStatCache($all = false) {
  537. if ($all === false && version_compare(PHP_VERSION, '5.3.0') >= 0) {
  538. return clearstatcache(true, $this->path);
  539. }
  540. return clearstatcache();
  541. }
  542. }