Source for file stream.php

Documentation is available at stream.php

  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$lengthgzgets($this->fh);
  505.                 break;
  506.  
  507.             case 'bz':
  508.             case 'f':
  509.             default:
  510.                 $res $length fgets($this->fh$lengthfgets($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 0bzread($this->fh$remainingbzread($this->fh$this->chunksize);
  582.                     break;
  583.  
  584.                 case 'gz':
  585.                     $res ($remaining 0gzread($this->fh$remaininggzread($this->fh$this->chunksize);
  586.                     break;
  587.  
  588.                 case 'f':
  589.                 default:
  590.                     $res ($remaining 0fread($this->fh$remainingfread($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->fhsubstr($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($filenamePHP_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($tmodeJFilesystemHelper::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. }

Documentation generated on Tue, 19 Nov 2013 15:14:28 +0100 by phpDocumentor 1.4.3