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