File.class.php 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * file upload functions
  5. *
  6. * @package PhpMyAdmin
  7. */
  8. if (! defined('PHPMYADMIN')) {
  9. exit;
  10. }
  11. /**
  12. * File wrapper class
  13. *
  14. * @todo when uploading a file into a blob field, should we also consider using
  15. * chunks like in import? UPDATE `table` SET `field` = `field` + [chunk]
  16. *
  17. * @package PhpMyAdmin
  18. */
  19. class PMA_File
  20. {
  21. /**
  22. * @var string the temporary file name
  23. * @access protected
  24. */
  25. var $_name = null;
  26. /**
  27. * @var string the content
  28. * @access protected
  29. */
  30. var $_content = null;
  31. /**
  32. * @var string the error message
  33. * @access protected
  34. */
  35. var $_error_message = '';
  36. /**
  37. * @var bool whether the file is temporary or not
  38. * @access protected
  39. */
  40. var $_is_temp = false;
  41. /**
  42. * @var string type of compression
  43. * @access protected
  44. */
  45. var $_compression = null;
  46. /**
  47. * @var integer
  48. */
  49. var $_offset = 0;
  50. /**
  51. * @var integer size of chunk to read with every step
  52. */
  53. var $_chunk_size = 32768;
  54. /**
  55. * @var resource file handle
  56. */
  57. var $_handle = null;
  58. /**
  59. * @var boolean whether to decompress content before returning
  60. */
  61. var $_decompress = false;
  62. /**
  63. * @var string charset of file
  64. */
  65. var $_charset = null;
  66. /**
  67. * constructor
  68. *
  69. * @param string $name file name
  70. *
  71. * @access public
  72. */
  73. public function __construct($name = false)
  74. {
  75. if ($name) {
  76. $this->setName($name);
  77. }
  78. }
  79. /**
  80. * destructor
  81. *
  82. * @see PMA_File::cleanUp()
  83. * @access public
  84. */
  85. public function __destruct()
  86. {
  87. $this->cleanUp();
  88. }
  89. /**
  90. * deletes file if it is temporary, usally from a moved upload file
  91. *
  92. * @access public
  93. * @return boolean success
  94. */
  95. public function cleanUp()
  96. {
  97. if ($this->isTemp()) {
  98. return $this->delete();
  99. }
  100. return true;
  101. }
  102. /**
  103. * deletes the file
  104. *
  105. * @access public
  106. * @return boolean success
  107. */
  108. public function delete()
  109. {
  110. return unlink($this->getName());
  111. }
  112. /**
  113. * checks or sets the temp flag for this file
  114. * file objects with temp flags are deleted with object destruction
  115. *
  116. * @param boolean $is_temp sets the temp flag
  117. *
  118. * @return boolean PMA_File::$_is_temp
  119. * @access public
  120. */
  121. public function isTemp($is_temp = null)
  122. {
  123. if (null !== $is_temp) {
  124. $this->_is_temp = (bool) $is_temp;
  125. }
  126. return $this->_is_temp;
  127. }
  128. /**
  129. * accessor
  130. *
  131. * @param string $name file name
  132. *
  133. * @return void
  134. * @access public
  135. */
  136. public function setName($name)
  137. {
  138. $this->_name = trim($name);
  139. }
  140. /**
  141. * Gets file content
  142. *
  143. * @param boolean $as_binary whether to return content as binary
  144. * @param integer $offset starting offset
  145. * @param integer $length length
  146. *
  147. * @return mixed the binary file content as a string,
  148. * or false if no content
  149. *
  150. * @access public
  151. */
  152. public function getContent($as_binary = true, $offset = 0, $length = null)
  153. {
  154. if (null === $this->_content) {
  155. if ($this->isUploaded() && ! $this->checkUploadedFile()) {
  156. return false;
  157. }
  158. if (! $this->isReadable()) {
  159. return false;
  160. }
  161. if (function_exists('file_get_contents')) {
  162. $this->_content = file_get_contents($this->getName());
  163. } elseif ($size = filesize($this->getName())) {
  164. $this->_content = fread(fopen($this->getName(), 'rb'), $size);
  165. }
  166. }
  167. if (! empty($this->_content) && $as_binary) {
  168. return '0x' . bin2hex($this->_content);
  169. }
  170. if (null !== $length) {
  171. return substr($this->_content, $offset, $length);
  172. } elseif ($offset > 0) {
  173. return substr($this->_content, $offset);
  174. }
  175. return $this->_content;
  176. }
  177. /**
  178. * Whether file is uploaded.
  179. *
  180. * @access public
  181. *
  182. * @return bool
  183. */
  184. public function isUploaded()
  185. {
  186. return is_uploaded_file($this->getName());
  187. }
  188. /**
  189. * accessor
  190. *
  191. * @access public
  192. * @return string PMA_File::$_name
  193. */
  194. public function getName()
  195. {
  196. return $this->_name;
  197. }
  198. /**
  199. * Initializes object from uploaded file.
  200. *
  201. * @param string $name name of file uploaded
  202. *
  203. * @return boolean success
  204. * @access public
  205. */
  206. public function setUploadedFile($name)
  207. {
  208. $this->setName($name);
  209. if (! $this->isUploaded()) {
  210. $this->setName(null);
  211. $this->_error_message = __('File was not an uploaded file.');
  212. return false;
  213. }
  214. return true;
  215. }
  216. /**
  217. * Loads uploaded file from table change request.
  218. *
  219. * @param string $key the md5 hash of the column name
  220. * @param string $rownumber
  221. *
  222. * @return boolean success
  223. * @access public
  224. */
  225. public function setUploadedFromTblChangeRequest($key, $rownumber)
  226. {
  227. if (! isset($_FILES['fields_upload'])
  228. || empty($_FILES['fields_upload']['name']['multi_edit'][$rownumber][$key])
  229. ) {
  230. return false;
  231. }
  232. $file = PMA_File::fetchUploadedFromTblChangeRequestMultiple(
  233. $_FILES['fields_upload'],
  234. $rownumber,
  235. $key
  236. );
  237. // check for file upload errors
  238. switch ($file['error']) {
  239. // we do not use the PHP constants here cause not all constants
  240. // are defined in all versions of PHP - but the correct constants names
  241. // are given as comment
  242. case 0: //UPLOAD_ERR_OK:
  243. return $this->setUploadedFile($file['tmp_name']);
  244. break;
  245. case 4: //UPLOAD_ERR_NO_FILE:
  246. break;
  247. case 1: //UPLOAD_ERR_INI_SIZE:
  248. $this->_error_message = __('The uploaded file exceeds the upload_max_filesize directive in php.ini.');
  249. break;
  250. case 2: //UPLOAD_ERR_FORM_SIZE:
  251. $this->_error_message = __('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.');
  252. break;
  253. case 3: //UPLOAD_ERR_PARTIAL:
  254. $this->_error_message = __('The uploaded file was only partially uploaded.');
  255. break;
  256. case 6: //UPLOAD_ERR_NO_TMP_DIR:
  257. $this->_error_message = __('Missing a temporary folder.');
  258. break;
  259. case 7: //UPLOAD_ERR_CANT_WRITE:
  260. $this->_error_message = __('Failed to write file to disk.');
  261. break;
  262. case 8: //UPLOAD_ERR_EXTENSION:
  263. $this->_error_message = __('File upload stopped by extension.');
  264. break;
  265. default:
  266. $this->_error_message = __('Unknown error in file upload.');
  267. } // end switch
  268. return false;
  269. }
  270. /**
  271. * strips some dimension from the multi-dimensional array from $_FILES
  272. *
  273. * <code>
  274. * $file['name']['multi_edit'][$rownumber][$key] = [value]
  275. * $file['type']['multi_edit'][$rownumber][$key] = [value]
  276. * $file['size']['multi_edit'][$rownumber][$key] = [value]
  277. * $file['tmp_name']['multi_edit'][$rownumber][$key] = [value]
  278. * $file['error']['multi_edit'][$rownumber][$key] = [value]
  279. *
  280. * // becomes:
  281. *
  282. * $file['name'] = [value]
  283. * $file['type'] = [value]
  284. * $file['size'] = [value]
  285. * $file['tmp_name'] = [value]
  286. * $file['error'] = [value]
  287. * </code>
  288. *
  289. * @param array $file the array
  290. * @param string $rownumber
  291. * @param string $key
  292. *
  293. * @return array
  294. * @access public
  295. * @static
  296. */
  297. public function fetchUploadedFromTblChangeRequestMultiple($file, $rownumber, $key)
  298. {
  299. $new_file = array(
  300. 'name' => $file['name']['multi_edit'][$rownumber][$key],
  301. 'type' => $file['type']['multi_edit'][$rownumber][$key],
  302. 'size' => $file['size']['multi_edit'][$rownumber][$key],
  303. 'tmp_name' => $file['tmp_name']['multi_edit'][$rownumber][$key],
  304. 'error' => $file['error']['multi_edit'][$rownumber][$key],
  305. );
  306. return $new_file;
  307. }
  308. /**
  309. * sets the name if the file to the one selected in the tbl_change form
  310. *
  311. * @param string $key the md5 hash of the column name
  312. * @param string $rownumber
  313. *
  314. * @return boolean success
  315. * @access public
  316. */
  317. public function setSelectedFromTblChangeRequest($key, $rownumber = null)
  318. {
  319. if (! empty($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
  320. && is_string($_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key])
  321. ) {
  322. // ... whether with multiple rows ...
  323. return $this->setLocalSelectedFile(
  324. $_REQUEST['fields_uploadlocal']['multi_edit'][$rownumber][$key]
  325. );
  326. } else {
  327. return false;
  328. }
  329. }
  330. /**
  331. * Returns possible error message.
  332. *
  333. * @access public
  334. * @return string error message
  335. */
  336. public function getError()
  337. {
  338. return $this->_error_message;
  339. }
  340. /**
  341. * Checks whether there was any error.
  342. *
  343. * @access public
  344. * @return boolean whether an error occured or not
  345. */
  346. public function isError()
  347. {
  348. return ! empty($this->_error_message);
  349. }
  350. /**
  351. * checks the superglobals provided if the tbl_change form is submitted
  352. * and uses the submitted/selected file
  353. *
  354. * @param string $key the md5 hash of the column name
  355. * @param string $rownumber
  356. *
  357. * @return boolean success
  358. * @access public
  359. */
  360. public function checkTblChangeForm($key, $rownumber)
  361. {
  362. if ($this->setUploadedFromTblChangeRequest($key, $rownumber)) {
  363. // well done ...
  364. $this->_error_message = '';
  365. return true;
  366. } elseif ($this->setSelectedFromTblChangeRequest($key, $rownumber)) {
  367. // well done ...
  368. $this->_error_message = '';
  369. return true;
  370. }
  371. // all failed, whether just no file uploaded/selected or an error
  372. return false;
  373. }
  374. /**
  375. * Sets named file to be read from UploadDir.
  376. *
  377. * @param string $name file name
  378. *
  379. * @return boolean success
  380. * @access public
  381. */
  382. public function setLocalSelectedFile($name)
  383. {
  384. if (empty($GLOBALS['cfg']['UploadDir'])) {
  385. return false;
  386. }
  387. $this->setName(
  388. PMA_Util::userDir($GLOBALS['cfg']['UploadDir']) . PMA_securePath($name)
  389. );
  390. if (is_link($this->getName())) {
  391. $this->_error_message = __('File is a symbolic link');
  392. $this->setName(null);
  393. return false;
  394. }
  395. if (! $this->isReadable()) {
  396. $this->_error_message = __('File could not be read');
  397. $this->setName(null);
  398. return false;
  399. }
  400. return true;
  401. }
  402. /**
  403. * Checks whether file can be read.
  404. *
  405. * @access public
  406. * @return boolean whether the file is readable or not
  407. */
  408. public function isReadable()
  409. {
  410. // suppress warnings from being displayed, but not from being logged
  411. // any file access outside of open_basedir will issue a warning
  412. ob_start();
  413. $is_readable = is_readable($this->getName());
  414. ob_end_clean();
  415. return $is_readable;
  416. }
  417. /**
  418. * If we are on a server with open_basedir, we must move the file
  419. * before opening it. The FAQ 1.11 explains how to create the "./tmp"
  420. * directory - if needed
  421. *
  422. * @todo move check of $cfg['TempDir'] into PMA_Config?
  423. * @access public
  424. * @return boolean whether uploaded fiel is fine or not
  425. */
  426. public function checkUploadedFile()
  427. {
  428. if ($this->isReadable()) {
  429. return true;
  430. }
  431. if (empty($GLOBALS['cfg']['TempDir'])
  432. || ! is_writable($GLOBALS['cfg']['TempDir'])
  433. ) {
  434. // cannot create directory or access, point user to FAQ 1.11
  435. $this->_error_message = __('Error moving the uploaded file, see [doc@faq1-11]FAQ 1.11[/doc]');
  436. return false;
  437. }
  438. $new_file_to_upload = tempnam(
  439. realpath($GLOBALS['cfg']['TempDir']),
  440. basename($this->getName())
  441. );
  442. // suppress warnings from being displayed, but not from being logged
  443. // any file access outside of open_basedir will issue a warning
  444. ob_start();
  445. $move_uploaded_file_result = move_uploaded_file(
  446. $this->getName(),
  447. $new_file_to_upload
  448. );
  449. ob_end_clean();
  450. if (! $move_uploaded_file_result) {
  451. $this->_error_message = __('Error while moving uploaded file.');
  452. return false;
  453. }
  454. $this->setName($new_file_to_upload);
  455. $this->isTemp(true);
  456. if (! $this->isReadable()) {
  457. $this->_error_message = __('Cannot read (moved) upload file.');
  458. return false;
  459. }
  460. return true;
  461. }
  462. /**
  463. * Detects what compression filse uses
  464. *
  465. * @todo move file read part into readChunk() or getChunk()
  466. * @todo add support for compression plugins
  467. * @access protected
  468. * @return string MIME type of compression, none for none
  469. */
  470. protected function detectCompression()
  471. {
  472. // suppress warnings from being displayed, but not from being logged
  473. // f.e. any file access outside of open_basedir will issue a warning
  474. ob_start();
  475. $file = fopen($this->getName(), 'rb');
  476. ob_end_clean();
  477. if (! $file) {
  478. $this->_error_message = __('File could not be read');
  479. return false;
  480. }
  481. /**
  482. * @todo
  483. * get registered plugins for file compression
  484. foreach (PMA_getPlugins($type = 'compression') as $plugin) {
  485. if (call_user_func_array(array($plugin['classname'], 'canHandle'), array($this->getName()))) {
  486. $this->setCompressionPlugin($plugin);
  487. break;
  488. }
  489. }
  490. */
  491. $test = fread($file, 4);
  492. $len = strlen($test);
  493. fclose($file);
  494. if ($len >= 2 && $test[0] == chr(31) && $test[1] == chr(139)) {
  495. $this->_compression = 'application/gzip';
  496. } elseif ($len >= 3 && substr($test, 0, 3) == 'BZh') {
  497. $this->_compression = 'application/bzip2';
  498. } elseif ($len >= 4 && $test == "PK\003\004") {
  499. $this->_compression = 'application/zip';
  500. } else {
  501. $this->_compression = 'none';
  502. }
  503. return $this->_compression;
  504. }
  505. /**
  506. * Sets whether the content should be decompressed before returned
  507. *
  508. * @param boolean $decompress whether to decompres
  509. *
  510. * @return void
  511. */
  512. public function setDecompressContent($decompress)
  513. {
  514. $this->_decompress = (bool) $decompress;
  515. }
  516. /**
  517. * Returns the file handle
  518. *
  519. * @return object file handle
  520. */
  521. public function getHandle()
  522. {
  523. if (null === $this->_handle) {
  524. $this->open();
  525. }
  526. return $this->_handle;
  527. }
  528. /**
  529. * Sets the file handle
  530. *
  531. * @param object $handle file handle
  532. *
  533. * @return void
  534. */
  535. public function setHandle($handle)
  536. {
  537. $this->_handle = $handle;
  538. }
  539. /**
  540. * Sets error message for unsupported compression.
  541. *
  542. * @return void
  543. */
  544. public function errorUnsupported()
  545. {
  546. $this->_error_message = sprintf(
  547. __('You attempted to load file with unsupported compression (%s). Either support for it is not implemented or disabled by your configuration.'),
  548. $this->getCompression()
  549. );
  550. }
  551. /**
  552. * Attempts to open the file.
  553. *
  554. * @return bool
  555. */
  556. public function open()
  557. {
  558. if (! $this->_decompress) {
  559. $this->_handle = @fopen($this->getName(), 'r');
  560. }
  561. switch ($this->getCompression()) {
  562. case false:
  563. return false;
  564. case 'application/bzip2':
  565. if ($GLOBALS['cfg']['BZipDump'] && @function_exists('bzopen')) {
  566. $this->_handle = @bzopen($this->getName(), 'r');
  567. } else {
  568. $this->errorUnsupported();
  569. return false;
  570. }
  571. break;
  572. case 'application/gzip':
  573. if ($GLOBALS['cfg']['GZipDump'] && @function_exists('gzopen')) {
  574. $this->_handle = @gzopen($this->getName(), 'r');
  575. } else {
  576. $this->errorUnsupported();
  577. return false;
  578. }
  579. break;
  580. case 'application/zip':
  581. if ($GLOBALS['cfg']['ZipDump'] && @function_exists('zip_open')) {
  582. include_once './libraries/zip_extension.lib.php';
  583. $result = PMA_getZipContents($this->getName());
  584. if (! empty($result['error'])) {
  585. $this->_error_message = PMA_Message::rawError($result['error']);
  586. return false;
  587. } else {
  588. $this->content_uncompressed = $result['data'];
  589. }
  590. unset($result);
  591. } else {
  592. $this->errorUnsupported();
  593. return false;
  594. }
  595. break;
  596. case 'none':
  597. $this->_handle = @fopen($this->getName(), 'r');
  598. break;
  599. default:
  600. $this->errorUnsupported();
  601. return false;
  602. break;
  603. }
  604. return true;
  605. }
  606. /**
  607. * Returns the character set of the file
  608. *
  609. * @return string character set of the file
  610. */
  611. public function getCharset()
  612. {
  613. return $this->_charset;
  614. }
  615. /**
  616. * Sets the character set of the file
  617. *
  618. * @param string $charset character set of the file
  619. *
  620. * @return void
  621. */
  622. public function setCharset($charset)
  623. {
  624. $this->_charset = $charset;
  625. }
  626. /**
  627. * Returns compression used by file.
  628. *
  629. * @return string MIME type of compression, none for none
  630. * @access public
  631. */
  632. public function getCompression()
  633. {
  634. if (null === $this->_compression) {
  635. return $this->detectCompression();
  636. }
  637. return $this->_compression;
  638. }
  639. /**
  640. * advances the file pointer in the file handle by $length bytes/chars
  641. *
  642. * @param integer $length numbers of chars/bytes to skip
  643. *
  644. * @return boolean
  645. * @todo this function is unused
  646. */
  647. public function advanceFilePointer($length)
  648. {
  649. while ($length > 0) {
  650. $this->getNextChunk($length);
  651. $length -= $this->getChunkSize();
  652. }
  653. }
  654. /**
  655. * http://bugs.php.net/bug.php?id=29532
  656. * bzip reads a maximum of 8192 bytes on windows systems
  657. *
  658. * @param int $max_size maximum size of the next chunk to be returned
  659. *
  660. * @return bool|string
  661. * @todo this function is unused
  662. */
  663. public function getNextChunk($max_size = null)
  664. {
  665. if (null !== $max_size) {
  666. $size = min($max_size, $this->getChunkSize());
  667. } else {
  668. $size = $this->getChunkSize();
  669. }
  670. // $result = $this->handler->getNextChunk($size);
  671. $result = '';
  672. switch ($this->getCompression()) {
  673. case 'application/bzip2':
  674. $result = '';
  675. while (strlen($result) < $size - 8192 && ! feof($this->getHandle())) {
  676. $result .= bzread($this->getHandle(), $size);
  677. }
  678. break;
  679. case 'application/gzip':
  680. $result = gzread($this->getHandle(), $size);
  681. break;
  682. case 'application/zip':
  683. /*
  684. * if getNextChunk() is used some day,
  685. * replace this code by code similar to the one
  686. * in open()
  687. *
  688. include_once './libraries/unzip.lib.php';
  689. $import_handle = new SimpleUnzip();
  690. $import_handle->ReadFile($this->getName());
  691. if ($import_handle->Count() == 0) {
  692. $this->_error_message = __('No files found inside ZIP archive!');
  693. return false;
  694. } elseif ($import_handle->GetError(0) != 0) {
  695. $this->_error_message = __('Error in ZIP archive:')
  696. . ' ' . $import_handle->GetErrorMsg(0);
  697. return false;
  698. } else {
  699. $result = $import_handle->GetData(0);
  700. }
  701. */
  702. break;
  703. case 'none':
  704. $result = fread($this->getHandle(), $size);
  705. break;
  706. default:
  707. return false;
  708. }
  709. if ($GLOBALS['charset_conversion']) {
  710. $result = PMA_convert_string($this->getCharset(), 'utf-8', $result);
  711. } else {
  712. /**
  713. * Skip possible byte order marks (I do not think we need more
  714. * charsets, but feel free to add more, you can use wikipedia for
  715. * reference: <http://en.wikipedia.org/wiki/Byte_Order_Mark>)
  716. *
  717. * @todo BOM could be used for charset autodetection
  718. */
  719. if ($this->getOffset() === 0) {
  720. // UTF-8
  721. if (strncmp($result, "\xEF\xBB\xBF", 3) == 0) {
  722. $result = substr($result, 3);
  723. // UTF-16 BE, LE
  724. } elseif (strncmp($result, "\xFE\xFF", 2) == 0
  725. || strncmp($result, "\xFF\xFE", 2) == 0) {
  726. $result = substr($result, 2);
  727. }
  728. }
  729. }
  730. $this->_offset += $size;
  731. if (0 === $result) {
  732. return true;
  733. }
  734. return $result;
  735. }
  736. /**
  737. * Returns the offset
  738. *
  739. * @return integer the offset
  740. */
  741. public function getOffset()
  742. {
  743. return $this->_offset;
  744. }
  745. /**
  746. * Returns the chunk size
  747. *
  748. * @return integer the chunk size
  749. */
  750. public function getChunkSize()
  751. {
  752. return $this->_chunk_size;
  753. }
  754. /**
  755. * Sets the chunk size
  756. *
  757. * @param integer $chunk_size the chunk size
  758. *
  759. * @return void
  760. */
  761. public function setChunkSize($chunk_size)
  762. {
  763. $this->_chunk_size = (int) $chunk_size;
  764. }
  765. /**
  766. * Returns the length of the content in the file
  767. *
  768. * @return integer the length of the file content
  769. */
  770. public function getContentLength()
  771. {
  772. return strlen($this->_content);
  773. }
  774. /**
  775. * Returns whether the end of the file has been reached
  776. *
  777. * @return boolean whether the end of the file has been reached
  778. */
  779. public function eof()
  780. {
  781. if ($this->getHandle()) {
  782. return feof($this->getHandle());
  783. } else {
  784. return ($this->getOffset() >= $this->getContentLength());
  785. }
  786. }
  787. }
  788. ?>