/libraries/joomla/filesystem/stream.php

https://bitbucket.org/kraymitchell/fcd · PHP · 1453 lines · 769 code · 181 blank · 503 comment · 92 complexity · b4f6fb75a31db9adf038151597f17a13 MD5 · raw file

  1. <?php
  2. /**
  3. * @package Joomla.Platform
  4. * @subpackage FileSystem
  5. *
  6. * @copyright Copyright (C) 2005 - 2012 Open Source Matters, Inc. All rights reserved.
  7. * @license GNU General Public License version 2 or later; see LICENSE
  8. */
  9. defined('JPATH_PLATFORM') or die;
  10. jimport('joomla.filesystem.helper');
  11. jimport('joomla.utilities.utility');
  12. /**
  13. * Joomla! Stream Interface
  14. *
  15. * The Joomla! stream interface is designed to handle files as streams
  16. * where as the legacy JFile static class treated files in a rather
  17. * atomic manner.
  18. *
  19. * @package Joomla.Platform
  20. * @subpackage FileSystem
  21. *
  22. * This class adheres to the stream wrapper operations:
  23. *
  24. * @see http://php.net/manual/en/function.stream-get-wrappers.php
  25. * @see http://php.net/manual/en/intro.stream.php PHP Stream Manual
  26. * @see http://php.net/manual/en/wrappers.php Stream Wrappers
  27. * @see http://php.net/manual/en/filters.php Stream Filters
  28. * @see http://php.net/manual/en/transports.php Socket Transports (used by some options, particularly HTTP proxy)
  29. * @since 11.1
  30. */
  31. class JStream extends JObject
  32. {
  33. // Publicly settable vars (protected to let our parent read them)
  34. /**
  35. * File Mode
  36. * @var integer
  37. * @since 11.1
  38. * */
  39. protected $filemode = 0644;
  40. /**
  41. * Directory Mode
  42. * @var integer
  43. * @since 11.1
  44. * */
  45. protected $dirmode = 0755;
  46. /**
  47. * Default Chunk Size
  48. * @var integer
  49. * @since 11.1
  50. */
  51. protected $chunksize = 8192;
  52. /**
  53. * Filename
  54. * @var string
  55. * @since 11.1
  56. */
  57. protected $filename;
  58. /**
  59. * Prefix of the connection for writing
  60. * @var string
  61. * @since 11.1
  62. */
  63. protected $writeprefix;
  64. /**
  65. * Prefix of the connection for reading
  66. * @var string
  67. * @since 11.1
  68. */
  69. protected $readprefix;
  70. /**
  71. *
  72. *Read Processing method
  73. * @var string gz, bz, f
  74. * If a scheme is detected, fopen will be defaulted
  75. * To use compression with a network stream use a filter
  76. * @since 11.1
  77. */
  78. protected $processingmethod = 'f';
  79. /**
  80. * Filters applied to the current stream
  81. * @var array
  82. * @since 11.1
  83. */
  84. protected $filters = array();
  85. /**
  86. * File Handle
  87. * @var array
  88. * @since 11.1
  89. */
  90. protected $_fh;
  91. /**
  92. * File size
  93. * @var integer
  94. * @since 11.1
  95. */
  96. protected $_filesize;
  97. /**
  98. *Context to use when opening the connection
  99. * @var
  100. * @since 11.1
  101. */
  102. protected $_context = null;
  103. /**
  104. * Context options; used to rebuild the context
  105. * @var
  106. * @since 11.1
  107. */
  108. protected $_contextOptions;
  109. /**
  110. * The mode under which the file was opened
  111. * @var
  112. * @since 11.1
  113. */
  114. protected $_openmode;
  115. /**
  116. * Constructor
  117. *
  118. * @param string $writeprefix Prefix of the stream (optional). Unlike the JPATH_*, this has a final path separator!
  119. * @param string $readprefix The read prefix (optional).
  120. * @param array $context The context options (optional).
  121. *
  122. * @since 11.1
  123. */
  124. public function __construct($writeprefix = '', $readprefix = '', $context = array())
  125. {
  126. $this->writeprefix = $writeprefix;
  127. $this->readprefix = $readprefix;
  128. $this->_contextOptions = $context;
  129. $this->_buildContext();
  130. }
  131. /**
  132. * Destructor
  133. *
  134. * @since 11.1
  135. */
  136. public function __destruct()
  137. {
  138. // Attempt to close on destruction if there is a file handle
  139. if ($this->_fh)
  140. {
  141. @$this->close();
  142. }
  143. }
  144. /**
  145. * Generic File Operations
  146. *
  147. * Open a stream with some lazy loading smarts
  148. *
  149. * @param string $filename Filename
  150. * @param string $mode Mode string to use
  151. * @param boolean $use_include_path Use the PHP include path
  152. * @param resource $context Context to use when opening
  153. * @param boolean $use_prefix Use a prefix to open the file
  154. * @param boolean $relative Filename is a relative path (if false, strips JPATH_ROOT to make it relative)
  155. * @param boolean $detectprocessingmode Detect the processing method for the file and use the appropriate function
  156. * to handle output automatically
  157. *
  158. * @return boolean
  159. *
  160. * @since 11.1
  161. */
  162. public function open($filename, $mode = 'r', $use_include_path = false, $context = null,
  163. $use_prefix = false, $relative = false, $detectprocessingmode = false)
  164. {
  165. $filename = $this->_getFilename($filename, $mode, $use_prefix, $relative);
  166. if (!$filename)
  167. {
  168. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILENAME'));
  169. return false;
  170. }
  171. $this->filename = $filename;
  172. $this->_openmode = $mode;
  173. $url = parse_url($filename);
  174. $retval = false;
  175. if (isset($url['scheme']))
  176. {
  177. // If we're dealing with a Joomla! stream, load it
  178. if (JFilesystemHelper::isJoomlaStream($url['scheme']))
  179. {
  180. require_once dirname(__FILE__) . '/streams/' . $url['scheme'] . '.php';
  181. }
  182. // We have a scheme! force the method to be f
  183. $this->processingmethod = 'f';
  184. }
  185. elseif ($detectprocessingmode)
  186. {
  187. $ext = strtolower(JFile::getExt($this->filename));
  188. switch ($ext)
  189. {
  190. case 'tgz':
  191. case 'gz':
  192. case 'gzip':
  193. $this->processingmethod = 'gz';
  194. break;
  195. case 'tbz2':
  196. case 'bz2':
  197. case 'bzip2':
  198. $this->processingmethod = 'bz';
  199. break;
  200. default:
  201. $this->processingmethod = 'f';
  202. break;
  203. }
  204. }
  205. // Capture PHP errors
  206. $php_errormsg = 'Error Unknown whilst opening a file';
  207. $track_errors = ini_get('track_errors');
  208. ini_set('track_errors', true);
  209. // Decide which context to use:
  210. switch ($this->processingmethod)
  211. {
  212. // gzip doesn't support contexts or streams
  213. case 'gz':
  214. $this->_fh = gzopen($filename, $mode, $use_include_path);
  215. break;
  216. // bzip2 is much like gzip except it doesn't use the include path
  217. case 'bz':
  218. $this->_fh = bzopen($filename, $mode);
  219. break;
  220. // fopen can handle streams
  221. case 'f':
  222. default:
  223. // One supplied at open; overrides everything
  224. if ($context)
  225. {
  226. $this->_fh = fopen($filename, $mode, $use_include_path, $context);
  227. }
  228. // One provided at initialisation
  229. elseif ($this->_context)
  230. {
  231. $this->_fh = fopen($filename, $mode, $use_include_path, $this->_context);
  232. }
  233. // No context; all defaults
  234. else
  235. {
  236. $this->_fh = fopen($filename, $mode, $use_include_path);
  237. }
  238. break;
  239. }
  240. if (!$this->_fh)
  241. {
  242. $this->setError($php_errormsg);
  243. }
  244. else
  245. {
  246. $retval = true;
  247. }
  248. // Restore error tracking to what it was before
  249. ini_set('track_errors', $track_errors);
  250. // Return the result
  251. return $retval;
  252. }
  253. /**
  254. * Attempt to close a file handle
  255. *
  256. * Will return false if it failed and true on success
  257. * If the file is not open the system will return true, this function destroys the file handle as well
  258. *
  259. * @return boolean
  260. *
  261. * @since 11.1
  262. */
  263. public function close()
  264. {
  265. if (!$this->_fh)
  266. {
  267. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  268. return true;
  269. }
  270. $retval = false;
  271. // Capture PHP errors
  272. $php_errormsg = 'Error Unknown';
  273. $track_errors = ini_get('track_errors');
  274. ini_set('track_errors', true);
  275. switch ($this->processingmethod)
  276. {
  277. case 'gz':
  278. $res = gzclose($this->_fh);
  279. break;
  280. case 'bz':
  281. $res = bzclose($this->_fh);
  282. break;
  283. case 'f':
  284. default:
  285. $res = fclose($this->_fh);
  286. break;
  287. }
  288. if (!$res)
  289. {
  290. $this->setError($php_errormsg);
  291. }
  292. else
  293. {
  294. // reset this
  295. $this->_fh = null;
  296. $retval = true;
  297. }
  298. // If we wrote, chmod the file after it's closed
  299. if ($this->_openmode[0] == 'w')
  300. {
  301. $this->chmod();
  302. }
  303. // Restore error tracking to what it was before
  304. ini_set('track_errors', $track_errors);
  305. // Return the result
  306. return $retval;
  307. }
  308. /**
  309. * Work out if we're at the end of the file for a stream
  310. *
  311. * @return boolean
  312. *
  313. * @since 11.1
  314. */
  315. public function eof()
  316. {
  317. if (!$this->_fh)
  318. {
  319. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  320. return false;
  321. }
  322. // Capture PHP errors
  323. $php_errormsg = '';
  324. $track_errors = ini_get('track_errors');
  325. ini_set('track_errors', true);
  326. switch ($this->processingmethod)
  327. {
  328. case 'gz':
  329. $res = gzeof($this->_fh);
  330. break;
  331. case 'bz':
  332. case 'f':
  333. default:
  334. $res = feof($this->_fh);
  335. break;
  336. }
  337. if ($php_errormsg)
  338. {
  339. $this->setError($php_errormsg);
  340. }
  341. // Restore error tracking to what it was before
  342. ini_set('track_errors', $track_errors);
  343. // Return the result
  344. return $res;
  345. }
  346. /**
  347. * Retrieve the file size of the path
  348. *
  349. * @return mixed
  350. *
  351. * @since 11.1
  352. */
  353. public function filesize()
  354. {
  355. if (!$this->filename)
  356. {
  357. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  358. return false;
  359. }
  360. $retval = false;
  361. // Capture PHP errors
  362. $php_errormsg = '';
  363. $track_errors = ini_get('track_errors');
  364. ini_set('track_errors', true);
  365. $res = @filesize($this->filename);
  366. if (!$res)
  367. {
  368. $tmp_error = '';
  369. if ($php_errormsg)
  370. {
  371. // Something went wrong.
  372. // Store the error in case we need it.
  373. $tmp_error = $php_errormsg;
  374. }
  375. $res = JFilesystemHelper::remotefsize($this->filename);
  376. if (!$res)
  377. {
  378. if ($tmp_error)
  379. {
  380. // Use the php_errormsg from before
  381. $this->setError($tmp_error);
  382. }
  383. else
  384. {
  385. // Error but nothing from php? How strange! Create our own
  386. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_SIZE'));
  387. }
  388. }
  389. else
  390. {
  391. $this->_filesize = $res;
  392. $retval = $res;
  393. }
  394. }
  395. else
  396. {
  397. $this->_filesize = $res;
  398. $retval = $res;
  399. }
  400. // Restore error tracking to what it was before.
  401. ini_set('track_errors', $track_errors);
  402. // return the result
  403. return $retval;
  404. }
  405. /**
  406. * Get a line from the stream source.
  407. *
  408. * @param integer $length The number of bytes (optional) to read.
  409. *
  410. * @return mixed
  411. *
  412. * @since 11.1
  413. */
  414. public function gets($length = 0)
  415. {
  416. if (!$this->_fh)
  417. {
  418. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  419. return false;
  420. }
  421. $retval = false;
  422. // Capture PHP errors
  423. $php_errormsg = 'Error Unknown';
  424. $track_errors = ini_get('track_errors');
  425. ini_set('track_errors', true);
  426. switch ($this->processingmethod)
  427. {
  428. case 'gz':
  429. $res = $length ? gzgets($this->_fh, $length) : gzgets($this->_fh);
  430. break;
  431. case 'bz':
  432. case 'f':
  433. default:
  434. $res = $length ? fgets($this->_fh, $length) : fgets($this->_fh);
  435. break;
  436. }
  437. if (!$res)
  438. {
  439. $this->setError($php_errormsg);
  440. }
  441. else
  442. {
  443. $retval = $res;
  444. }
  445. // Restore error tracking to what it was before
  446. ini_set('track_errors', $track_errors);
  447. // return the result
  448. return $retval;
  449. }
  450. /**
  451. * Read a file
  452. *
  453. * Handles user space streams appropriately otherwise any read will return 8192
  454. *
  455. * @param integer $length Length of data to read
  456. *
  457. * @return mixed
  458. *
  459. * @see http://php.net/manual/en/function.fread.php
  460. * @since 11.1
  461. */
  462. public function read($length = 0)
  463. {
  464. if (!$this->_filesize && !$length)
  465. {
  466. // Get the filesize
  467. $this->filesize();
  468. if (!$this->_filesize)
  469. {
  470. // Set it to the biggest and then wait until eof
  471. $length = -1;
  472. }
  473. else
  474. {
  475. $length = $this->_filesize;
  476. }
  477. }
  478. if (!$this->_fh)
  479. {
  480. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  481. return false;
  482. }
  483. $retval = false;
  484. // Capture PHP errors
  485. $php_errormsg = 'Error Unknown';
  486. $track_errors = ini_get('track_errors');
  487. ini_set('track_errors', true);
  488. $remaining = $length;
  489. do
  490. {
  491. // Do chunked reads where relevant
  492. switch ($this->processingmethod)
  493. {
  494. case 'bz':
  495. $res = ($remaining > 0) ? bzread($this->_fh, $remaining) : bzread($this->_fh, $this->chunksize);
  496. break;
  497. case 'gz':
  498. $res = ($remaining > 0) ? gzread($this->_fh, $remaining) : gzread($this->_fh, $this->chunksize);
  499. break;
  500. case 'f':
  501. default:
  502. $res = ($remaining > 0) ? fread($this->_fh, $remaining) : fread($this->_fh, $this->chunksize);
  503. break;
  504. }
  505. if (!$res)
  506. {
  507. $this->setError($php_errormsg);
  508. $remaining = 0; // jump from the loop
  509. }
  510. else
  511. {
  512. if (!$retval)
  513. {
  514. $retval = '';
  515. }
  516. $retval .= $res;
  517. if (!$this->eof())
  518. {
  519. $len = strlen($res);
  520. $remaining -= $len;
  521. }
  522. else
  523. {
  524. // If it's the end of the file then we've nothing left to read; reset remaining and len
  525. $remaining = 0;
  526. $length = strlen($retval);
  527. }
  528. }
  529. }
  530. while ($remaining || !$length);
  531. // Restore error tracking to what it was before
  532. ini_set('track_errors', $track_errors);
  533. // Return the result
  534. return $retval;
  535. }
  536. /**
  537. * Seek the file
  538. *
  539. * Note: the return value is different to that of fseek
  540. *
  541. * @param integer $offset Offset to use when seeking.
  542. * @param integer $whence Seek mode to use.
  543. *
  544. * @return boolean True on success, false on failure
  545. *
  546. * @see http://php.net/manual/en/function.fseek.php
  547. * @since 11.1
  548. */
  549. public function seek($offset, $whence = SEEK_SET)
  550. {
  551. if (!$this->_fh)
  552. {
  553. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  554. return false;
  555. }
  556. $retval = false;
  557. // Capture PHP errors
  558. $php_errormsg = '';
  559. $track_errors = ini_get('track_errors');
  560. ini_set('track_errors', true);
  561. switch ($this->processingmethod)
  562. {
  563. case 'gz':
  564. $res = gzseek($this->_fh, $offset, $whence);
  565. break;
  566. case 'bz':
  567. case 'f':
  568. default:
  569. $res = fseek($this->_fh, $offset, $whence);
  570. break;
  571. }
  572. // Seek, interestingly, returns 0 on success or -1 on failure.
  573. if ($res == -1)
  574. {
  575. $this->setError($php_errormsg);
  576. }
  577. else
  578. {
  579. $retval = true;
  580. }
  581. // Restore error tracking to what it was before
  582. ini_set('track_errors', $track_errors);
  583. // Return the result
  584. return $retval;
  585. }
  586. /**
  587. * Returns the current position of the file read/write pointer.
  588. *
  589. * @return mixed
  590. *
  591. * @since 11.1
  592. */
  593. public function tell()
  594. {
  595. if (!$this->_fh)
  596. {
  597. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  598. return false;
  599. }
  600. $res = false;
  601. // Capture PHP errors
  602. $php_errormsg = '';
  603. $track_errors = ini_get('track_errors');
  604. ini_set('track_errors', true);
  605. switch ($this->processingmethod)
  606. {
  607. case 'gz':
  608. $res = gztell($this->_fh);
  609. break;
  610. case 'bz':
  611. case 'f':
  612. default:
  613. $res = ftell($this->_fh);
  614. break;
  615. }
  616. // May return 0 so check if it's really false
  617. if ($res === false)
  618. {
  619. $this->setError($php_errormsg);
  620. }
  621. // Restore error tracking to what it was before
  622. ini_set('track_errors', $track_errors);
  623. // Return the result
  624. return $res;
  625. }
  626. /**
  627. * File write
  628. *
  629. * Whilst this function accepts a reference, the underlying fwrite
  630. * will do a copy! This will roughly double the memory allocation for
  631. * any write you do. Specifying chunked will get around this by only
  632. * writing in specific chunk sizes. This defaults to 8192 which is a
  633. * sane number to use most of the time (change the default with
  634. * JStream::set('chunksize', newsize);)
  635. * Note: This doesn't support gzip/bzip2 writing like reading does
  636. *
  637. * @param string &$string Reference to the string to write.
  638. * @param integer $length Length of the string to write.
  639. * @param integer $chunk Size of chunks to write in.
  640. *
  641. * @return boolean
  642. *
  643. * @see http://php.net/manual/en/function.fwrite.php
  644. * @since 11.1
  645. */
  646. public function write(&$string, $length = 0, $chunk = 0)
  647. {
  648. if (!$this->_fh)
  649. {
  650. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  651. return false;
  652. }
  653. // If the length isn't set, set it to the length of the string.
  654. if (!$length)
  655. {
  656. $length = strlen($string);
  657. }
  658. // If the chunk isn't set, set it to the default.
  659. if (!$chunk)
  660. {
  661. $chunk = $this->chunksize;
  662. }
  663. $retval = true;
  664. // Capture PHP errors
  665. $php_errormsg = '';
  666. $track_errors = ini_get('track_errors');
  667. ini_set('track_errors', true);
  668. $remaining = $length;
  669. do
  670. {
  671. // If the amount remaining is greater than the chunk size, then use the chunk
  672. $amount = ($remaining > $chunk) ? $chunk : $remaining;
  673. $res = fwrite($this->_fh, $string, $amount);
  674. // Returns false on error or the number of bytes written
  675. if ($res === false)
  676. {
  677. // Returned error
  678. $this->setError($php_errormsg);
  679. $retval = false;
  680. $remaining = 0;
  681. }
  682. elseif ($res === 0)
  683. {
  684. // Wrote nothing?
  685. $remaining = 0;
  686. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_NO_DATA_WRITTEN'));
  687. }
  688. else
  689. {
  690. // Wrote something
  691. $remaining -= $res;
  692. }
  693. }
  694. while ($remaining);
  695. // Restore error tracking to what it was before.
  696. ini_set('track_errors', $track_errors);
  697. // Return the result
  698. return $retval;
  699. }
  700. /**
  701. * Chmod wrapper
  702. *
  703. * @param string $filename File name.
  704. * @param mixed $mode Mode to use.
  705. *
  706. * @return boolean
  707. *
  708. * @since 11.1
  709. */
  710. public function chmod($filename = '', $mode = 0)
  711. {
  712. if (!$filename)
  713. {
  714. if (!isset($this->filename) || !$this->filename)
  715. {
  716. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILENAME'));
  717. return false;
  718. }
  719. $filename = $this->filename;
  720. }
  721. // If no mode is set use the default
  722. if (!$mode)
  723. {
  724. $mode = $this->filemode;
  725. }
  726. $retval = false;
  727. // Capture PHP errors
  728. $php_errormsg = '';
  729. $track_errors = ini_get('track_errors');
  730. ini_set('track_errors', true);
  731. $sch = parse_url($filename, PHP_URL_SCHEME);
  732. // Scheme specific options; ftp's chmod support is fun.
  733. switch ($sch)
  734. {
  735. case 'ftp':
  736. case 'ftps':
  737. $res = JFilesystemHelper::ftpChmod($filename, $mode);
  738. break;
  739. default:
  740. $res = chmod($filename, $mode);
  741. break;
  742. }
  743. // Seek, interestingly, returns 0 on success or -1 on failure
  744. if (!$res)
  745. {
  746. $this->setError($php_errormsg);
  747. }
  748. else
  749. {
  750. $retval = true;
  751. }
  752. // Restore error tracking to what it was before.
  753. ini_set('track_errors', $track_errors);
  754. // Return the result
  755. return $retval;
  756. }
  757. /**
  758. * Get the stream metadata
  759. *
  760. * @return array header/metadata
  761. *
  762. * @see http://php.net/manual/en/function.stream-get-meta-data.php
  763. * @since 11.1
  764. */
  765. public function get_meta_data()
  766. {
  767. if (!$this->_fh)
  768. {
  769. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_FILE_NOT_OPEN'));
  770. return false;
  771. }
  772. return stream_get_meta_data($this->_fh);
  773. }
  774. /**
  775. * Stream contexts
  776. * Builds the context from the array
  777. *
  778. * @return mixed
  779. *
  780. * @since 11.1
  781. */
  782. public function _buildContext()
  783. {
  784. // According to the manual this always works!
  785. if (count($this->_contextOptions))
  786. {
  787. $this->_context = @stream_context_create($this->_contextOptions);
  788. }
  789. else
  790. {
  791. $this->_context = null;
  792. }
  793. }
  794. /**
  795. * Updates the context to the array
  796. *
  797. * Format is the same as the options for stream_context_create
  798. *
  799. * @param array $context Options to create the context with
  800. *
  801. * @return void
  802. *
  803. * @see http://php.net/stream_context_create
  804. * @since 11.1
  805. */
  806. public function setContextOptions($context)
  807. {
  808. $this->_contextOptions = $context;
  809. $this->_buildContext();
  810. }
  811. /**
  812. * Adds a particular options to the context
  813. *
  814. * @param string $wrapper The wrapper to use
  815. * @param string $name The option to set
  816. * @param string $value The value of the option
  817. *
  818. * @return void
  819. *
  820. * @see http://php.net/stream_context_create Stream Context Creation
  821. * @see http://php.net/manual/en/context.php Context Options for various streams
  822. * @since 11.1
  823. */
  824. public function addContextEntry($wrapper, $name, $value)
  825. {
  826. $this->_contextOptions[$wrapper][$name] = $value;
  827. $this->_buildContext();
  828. }
  829. /**
  830. * Deletes a particular setting from a context
  831. *
  832. * @param string $wrapper The wrapper to use
  833. * @param string $name The option to unset
  834. *
  835. * @return void
  836. *
  837. * @see http://php.net/stream_context_create
  838. * @since 11.1
  839. */
  840. public function deleteContextEntry($wrapper, $name)
  841. {
  842. // Check whether the wrapper is set
  843. if (isset($this->_contextOptions[$wrapper]))
  844. {
  845. // Check that entry is set for that wrapper
  846. if (isset($this->_contextOptions[$wrapper][$name]))
  847. {
  848. // Unset the item
  849. unset($this->_contextOptions[$wrapper][$name]);
  850. // Check that there are still items there
  851. if (!count($this->_contextOptions[$wrapper]))
  852. {
  853. // Clean up an empty wrapper context option
  854. unset($this->_contextOptions[$wrapper]);
  855. }
  856. }
  857. }
  858. // Rebuild the context and apply it to the stream
  859. $this->_buildContext();
  860. }
  861. /**
  862. * Applies the current context to the stream
  863. *
  864. * Use this to change the values of the context after you've opened a stream
  865. *
  866. * @return mixed
  867. *
  868. * @since 11.1
  869. */
  870. public function applyContextToStream()
  871. {
  872. $retval = false;
  873. if ($this->_fh)
  874. {
  875. // Capture PHP errors
  876. $php_errormsg = 'Unknown error setting context option';
  877. $track_errors = ini_get('track_errors');
  878. ini_set('track_errors', true);
  879. $retval = @stream_context_set_option($this->_fh, $this->_contextOptions);
  880. if (!$retval)
  881. {
  882. $this->setError($php_errormsg);
  883. }
  884. // restore error tracking to what it was before
  885. ini_set('track_errors', $track_errors);
  886. }
  887. return $retval;
  888. }
  889. /**
  890. * Stream filters
  891. * Append a filter to the chain
  892. *
  893. * @param string $filtername The key name of the filter.
  894. * @param integer $read_write Optional. Defaults to STREAM_FILTER_READ.
  895. * @param array $params An array of params for the stream_filter_append call.
  896. *
  897. * @return mixed
  898. *
  899. * @see http://php.net/manual/en/function.stream-filter-append.php
  900. * @since 11.1
  901. */
  902. public function appendFilter($filtername, $read_write = STREAM_FILTER_READ, $params = array())
  903. {
  904. $res = false;
  905. if ($this->_fh)
  906. {
  907. // Capture PHP errors
  908. $php_errormsg = '';
  909. $track_errors = ini_get('track_errors');
  910. ini_set('track_errors', true);
  911. $res = @stream_filter_append($this->_fh, $filtername, $read_write, $params);
  912. if (!$res && $php_errormsg)
  913. {
  914. $this->setError($php_errormsg);
  915. }
  916. else
  917. {
  918. $this->filters[] = &$res;
  919. }
  920. // Restore error tracking to what it was before.
  921. ini_set('track_errors', $track_errors);
  922. }
  923. return $res;
  924. }
  925. /**
  926. * Prepend a filter to the chain
  927. *
  928. * @param string $filtername The key name of the filter.
  929. * @param integer $read_write Optional. Defaults to STREAM_FILTER_READ.
  930. * @param array $params An array of params for the stream_filter_prepend call.
  931. *
  932. * @return mixed
  933. *
  934. * @see http://php.net/manual/en/function.stream-filter-prepend.php
  935. * @since 11.1
  936. */
  937. public function prependFilter($filtername, $read_write = STREAM_FILTER_READ, $params = array())
  938. {
  939. $res = false;
  940. if ($this->_fh)
  941. {
  942. // Capture PHP errors
  943. $php_errormsg = '';
  944. $track_errors = ini_get('track_errors');
  945. ini_set('track_errors', true);
  946. $res = @stream_filter_prepend($this->_fh, $filtername, $read_write, $params);
  947. if (!$res && $php_errormsg)
  948. {
  949. $this->setError($php_errormsg); // set the error msg
  950. }
  951. else
  952. {
  953. array_unshift($res, '');
  954. $res[0] = &$this->filters;
  955. }
  956. // Restore error tracking to what it was before.
  957. ini_set('track_errors', $track_errors);
  958. }
  959. return $res;
  960. }
  961. /**
  962. * Remove a filter, either by resource (handed out from the append or prepend function)
  963. * or via getting the filter list)
  964. *
  965. * @param resource &$resource The resource.
  966. * @param boolean $byindex The index of the filter.
  967. *
  968. * @return boolean Result of operation
  969. *
  970. * @since 11.1
  971. */
  972. public function removeFilter(&$resource, $byindex = false)
  973. {
  974. $res = false;
  975. // Capture PHP errors
  976. $php_errormsg = '';
  977. $track_errors = ini_get('track_errors');
  978. ini_set('track_errors', true);
  979. if ($byindex)
  980. {
  981. $res = stream_filter_remove($this->filters[$resource]);
  982. }
  983. else
  984. {
  985. $res = stream_filter_remove($resource);
  986. }
  987. if ($res && $php_errormsg)
  988. {
  989. $this->setError($php_errormsg);
  990. }
  991. // Restore error tracking to what it was before.
  992. ini_set('track_errors', $track_errors);
  993. return $res;
  994. }
  995. /**
  996. * Copy a file from src to dest
  997. *
  998. * @param string $src The file path to copy from.
  999. * @param string $dest The file path to copy to.
  1000. * @param resource $context A valid context resource (optional) created with stream_context_create.
  1001. * @param boolean $use_prefix Controls the use of a prefix (optional).
  1002. * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped.
  1003. *
  1004. * @return mixed
  1005. *
  1006. * @since 11.1
  1007. */
  1008. public function copy($src, $dest, $context = null, $use_prefix = true, $relative = false)
  1009. {
  1010. $res = false;
  1011. // Capture PHP errors
  1012. $php_errormsg = '';
  1013. $track_errors = ini_get('track_errors');
  1014. ini_set('track_errors', true);
  1015. $chmodDest = $this->_getFilename($dest, 'w', $use_prefix, $relative);
  1016. $exists = file_exists($dest);
  1017. $context_support = version_compare(PHP_VERSION, '5.3', '>='); // 5.3 provides context support
  1018. if ($exists && !$context_support)
  1019. {
  1020. // The file exists and there is no context support.
  1021. // This could cause a failure as we may need to overwrite the file.
  1022. // So we write our own copy function that will work with a stream
  1023. // context; php 5.3 will fix this for us (yay!).
  1024. // Note: Since open processes the filename for us we won't worry about
  1025. // calling _getFilename
  1026. $res = $this->open($src);
  1027. if ($res)
  1028. {
  1029. $reader = $this->_fh;
  1030. $res = $this->open($dest, 'w');
  1031. if ($res)
  1032. {
  1033. $res = stream_copy_to_stream($reader, $this->_fh);
  1034. $tmperror = $php_errormsg; // save this in case fclose throws an error
  1035. @fclose($reader);
  1036. $php_errormsg = $tmperror; // restore after fclose
  1037. }
  1038. else
  1039. {
  1040. @fclose($reader); // close the reader off
  1041. $php_errormsg = JText::sprintf('JLIB_FILESYSTEM_ERROR_STREAMS_FAILED_TO_OPEN_WRITER', $this->getError());
  1042. }
  1043. }
  1044. else
  1045. {
  1046. if (!$php_errormsg)
  1047. {
  1048. $php_errormsg = JText::sprintf('JLIB_FILESYSTEM_ERROR_STREAMS_FAILED_TO_OPEN_READER', $this->getError());
  1049. }
  1050. }
  1051. }
  1052. else
  1053. {
  1054. // Since we're going to open the file directly we need to get the filename.
  1055. // We need to use the same prefix so force everything to write.
  1056. $src = $this->_getFilename($src, 'w', $use_prefix, $relative);
  1057. $dest = $this->_getFilename($dest, 'w', $use_prefix, $relative);
  1058. if ($context_support && $context)
  1059. {
  1060. // Use the provided context
  1061. $res = @copy($src, $dest, $context);
  1062. }
  1063. elseif ($context_support && $this->_context)
  1064. {
  1065. // Use the objects context
  1066. $res = @copy($src, $dest, $this->_context);
  1067. }
  1068. else
  1069. {
  1070. // Don't use any context
  1071. $res = @copy($src, $dest);
  1072. }
  1073. }
  1074. if (!$res && $php_errormsg)
  1075. {
  1076. $this->setError($php_errormsg);
  1077. }
  1078. else
  1079. {
  1080. $this->chmod($chmodDest);
  1081. }
  1082. // Restore error tracking to what it was before
  1083. ini_set('track_errors', $track_errors);
  1084. return $res;
  1085. }
  1086. /**
  1087. * Moves a file
  1088. *
  1089. * @param string $src The file path to move from.
  1090. * @param string $dest The file path to move to.
  1091. * @param resource $context A valid context resource (optional) created with stream_context_create.
  1092. * @param boolean $use_prefix Controls the use of a prefix (optional).
  1093. * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped.
  1094. *
  1095. * @return mixed
  1096. *
  1097. * @since 11.1
  1098. */
  1099. public function move($src, $dest, $context = null, $use_prefix = true, $relative = false)
  1100. {
  1101. $res = false;
  1102. // Capture PHP errors
  1103. $php_errormsg = '';
  1104. $track_errors = ini_get('track_errors');
  1105. ini_set('track_errors', true);
  1106. $src = $this->_getFilename($src, 'w', $use_prefix, $relative);
  1107. $dest = $this->_getFilename($dest, 'w', $use_prefix, $relative);
  1108. if ($context)
  1109. {
  1110. // Use the provided context
  1111. $res = @rename($src, $dest, $context);
  1112. }
  1113. elseif ($this->_context)
  1114. {
  1115. // Use the object's context
  1116. $res = @rename($src, $dest, $this->_context);
  1117. }
  1118. else
  1119. {
  1120. // Don't use any context
  1121. $res = @rename($src, $dest);
  1122. }
  1123. if (!$res && $php_errormsg)
  1124. {
  1125. $this->setError($php_errormsg());
  1126. }
  1127. $this->chmod($dest);
  1128. // Restore error tracking to what it was before
  1129. ini_set('track_errors', $track_errors);
  1130. return $res;
  1131. }
  1132. /**
  1133. * Delete a file
  1134. *
  1135. * @param string $filename The file path to delete.
  1136. * @param resource $context A valid context resource (optional) created with stream_context_create.
  1137. * @param boolean $use_prefix Controls the use of a prefix (optional).
  1138. * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped.
  1139. *
  1140. * @return mixed
  1141. *
  1142. * @since 11.1
  1143. */
  1144. public function delete($filename, $context = null, $use_prefix = true, $relative = false)
  1145. {
  1146. $res = false;
  1147. // Capture PHP errors
  1148. $php_errormsg = '';
  1149. $track_errors = ini_get('track_errors');
  1150. ini_set('track_errors', true);
  1151. $filename = $this->_getFilename($filename, 'w', $use_prefix, $relative);
  1152. if ($context)
  1153. {
  1154. // Use the provided context
  1155. $res = @unlink($filename, $context);
  1156. }
  1157. elseif ($this->_context)
  1158. {
  1159. // Use the object's context
  1160. $res = @unlink($filename, $this->_context);
  1161. }
  1162. else
  1163. {
  1164. // Don't use any context
  1165. $res = @unlink($filename);
  1166. }
  1167. if (!$res && $php_errormsg)
  1168. {
  1169. $this->setError($php_errormsg());
  1170. }
  1171. // Restore error tracking to what it was before.
  1172. ini_set('track_errors', $track_errors);
  1173. return $res;
  1174. }
  1175. /**
  1176. * Upload a file
  1177. *
  1178. * @param string $src The file path to copy from (usually a temp folder).
  1179. * @param string $dest The file path to copy to.
  1180. * @param resource $context A valid context resource (optional) created with stream_context_create.
  1181. * @param boolean $use_prefix Controls the use of a prefix (optional).
  1182. * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped.
  1183. *
  1184. * @return mixed
  1185. *
  1186. * @since 11.1
  1187. */
  1188. public function upload($src, $dest, $context = null, $use_prefix = true, $relative = false)
  1189. {
  1190. if (is_uploaded_file($src))
  1191. {
  1192. // Make sure it's an uploaded file
  1193. return $this->copy($src, $dest, $context, $use_prefix, $relative);
  1194. }
  1195. else
  1196. {
  1197. $this->setError(JText::_('JLIB_FILESYSTEM_ERROR_STREAMS_NOT_UPLOADED_FILE'));
  1198. return false;
  1199. }
  1200. }
  1201. /**
  1202. * Writes a chunk of data to a file.
  1203. *
  1204. * @param string $filename The file name.
  1205. * @param string &$buffer The data to write to the file.
  1206. *
  1207. * @return boolean
  1208. *
  1209. * @since 11.1
  1210. */
  1211. public function writeFile($filename, &$buffer)
  1212. {
  1213. if ($this->open($filename, 'w'))
  1214. {
  1215. $result = $this->write($buffer);
  1216. $this->chmod();
  1217. $this->close();
  1218. return $result;
  1219. }
  1220. return false;
  1221. }
  1222. /**
  1223. * Determine the appropriate 'filename' of a file
  1224. *
  1225. * @param string $filename Original filename of the file
  1226. * @param string $mode Mode string to retrieve the filename
  1227. * @param boolean $use_prefix Controls the use of a prefix
  1228. * @param boolean $relative Determines if the filename given is relative. Relative paths do not have JPATH_ROOT stripped.
  1229. *
  1230. * @return string
  1231. *
  1232. * @since 11.1
  1233. */
  1234. public function _getFilename($filename, $mode, $use_prefix, $relative)
  1235. {
  1236. if ($use_prefix)
  1237. {
  1238. // Get rid of binary or t, should be at the end of the string
  1239. $tmode = trim($mode, 'btf123456789');
  1240. // Check if it's a write mode then add the appropriate prefix
  1241. // Get rid of JPATH_ROOT (legacy compat) along the way
  1242. if (in_array($tmode, JFilesystemHelper::getWriteModes()))
  1243. {
  1244. if (!$relative && $this->writeprefix)
  1245. {
  1246. $filename = str_replace(JPATH_ROOT, '', $filename);
  1247. }
  1248. $filename = $this->writeprefix . $filename;
  1249. }
  1250. else
  1251. {
  1252. if (!$relative && $this->readprefix)
  1253. {
  1254. $filename = str_replace(JPATH_ROOT, '', $filename);
  1255. }
  1256. $filename = $this->readprefix . $filename;
  1257. }
  1258. }
  1259. return $filename;
  1260. }
  1261. /**
  1262. * Return the internal file handle
  1263. *
  1264. * @return File handler
  1265. *
  1266. * @since 11.1
  1267. */
  1268. public function getFileHandle()
  1269. {
  1270. return $this->_fh;
  1271. }
  1272. }