[ Index ]

PHP Cross Reference of PHK Manager

title

Body

[close]

/PHK/Stream/ -> Wrapper.php (source)

   1  <?php
   2  //=============================================================================
   3  //
   4  //   Licensed under the Apache License, Version 2.0 (the "License");
   5  //   you may not use this file except in compliance with the License.
   6  //   You may obtain a copy of the License at
   7  //
   8  //       http://www.apache.org/licenses/LICENSE-2.0
   9  //
  10  //   Unless required by applicable law or agreed to in writing, software
  11  //   distributed under the License is distributed on an "AS IS" BASIS,
  12  //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  //   See the License for the specific language governing permissions and
  14  //   limitations under the License.
  15  //
  16  //=============================================================================
  17  /**
  18  * @copyright Francois Laupretre <phk@tekwire.net>
  19  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0
  20  * @category PHK
  21  * @package PHK
  22  *///==========================================================================
  23  
  24  namespace PHK\Stream {
  25  
  26  if (!class_exists('PHK\Stream\Wrapper',false))
  27  {
  28  //=============================================================================
  29  /**
  30  * The PHK stream wrapper
  31  *
  32  * Handles every file access to a 'phk://...' URI
  33  *
  34  * Note: Always catch exceptions before returning to PHP.
  35  *
  36  * API status: Private
  37  * Included in the PHK PHP runtime: Yes
  38  * Implemented in the extension: Yes
  39  *///==========================================================================
  40  
  41  class Wrapper
  42  {
  43  
  44  private $uri;
  45  
  46  private $mnt;        // Mount point (string)
  47  private $path;        // File path in PHK (string|null)
  48  private $command;    // Command (string|null)
  49  private $params;    // Command parameter (array|null)
  50  
  51  private $data;    // File data (regular file) or dir content
  52  private $size;    // Size of buffer or number of dir entries
  53  private $position; // Byte position or position in array
  54  
  55  private $raiseErrors=true;
  56  
  57  //---------------------------------
  58  // Display a warning if they are allowed
  59  
  60  private function raiseWarning($msg)
  61  {
  62  if ($this->raiseErrors) trigger_error("PHK: $msg",E_USER_WARNING);
  63  }
  64  
  65  //---------------------------------
  66  
  67  public static function getFile($dir,$uri,$mnt,$command,$params,$path,$cache=null)
  68  {
  69  $cacheID=\PHK\Cache::cacheID('node',$uri);
  70  \PHK\Tools\Util::trace("get_file: Cache ID=<$cacheID>");//TRACE
  71  
  72  if (is_null($data=\PHK\Cache::get($cacheID)))    // Miss
  73      {
  74      $can_cache=true;
  75      
  76      if (is_null($data=($dir ?
  77          Backend::getDirData($mnt,$command,$params,$path)
  78          : Backend::getFileData($mnt,$command,$params,$path
  79              ,$can_cache)))) throw new \Exception("$uri: File not found");
  80  
  81      if ($can_cache && (($cache===true) || (is_null($cache)
  82          && \PHK\Mgr::cacheEnabled($mnt,$command,$params,$path))))
  83              \PHK\Cache::set($cacheID,$data);
  84      }
  85  
  86  if ($dir && (!is_array($data))) throw new \Exception('Not a directory');
  87  if ((!$dir) && (!is_string($data))) throw new \Exception('Not a regular file');
  88  
  89  return $data;
  90  }
  91  
  92  //---------------------------------
  93  // Open a file - only read mode is supported
  94  // STREAM_USE_PATH is ignored
  95  // Fast path: Yes (only when data is found in the cache)
  96  //
  97  // We must check the mount point validity because we can be called for an
  98  // unmounted path. In this case, we must return false before searching the
  99  // cache, or the behavior will be different when the data is in the cache
 100  // or not. Note that we check for global commands before validating the mount
 101  // point.
 102  
 103  public function stream_open($uri,$mode,$options,&$opened_path)
 104  {
 105  \PHK\Tools\Util::trace("Starting stream_open: uri=$uri");//TRACE
 106  
 107  try
 108  {
 109  $this->uri=$uri;
 110  $this->raiseErrors=($options & STREAM_REPORT_ERRORS);
 111  if ($options & STREAM_USE_PATH) $opened_path=$uri;
 112  
 113  if (($mode!='r')&&($mode!='rb'))
 114      throw new \Exception($mode.': unsupported mode (Read only)');
 115  
 116  self::parseURI($uri,$this->command,$this->params,$this->mnt,$this->path);
 117  
 118  if (!is_null($this->mnt)) \PHK\Mgr::validate($this->mnt);
 119  
 120  $this->data=self::getFile(false,$uri,$this->mnt,$this->command
 121      ,$this->params,$this->path);
 122  
 123  $this->size=strlen($this->data);
 124  $this->position=0;
 125  }
 126  catch (\Exception $e)
 127      {
 128      $msg=$uri.': Open error - '.$e->getMessage();
 129      $this->raiseWarning($msg);
 130      return false;
 131      }
 132  \PHK\Tools\Util::trace("Exiting stream_open: uri=$uri");//TRACE
 133  return true;
 134  }
 135  
 136  //---------------------------------
 137  // Read on an open file
 138  
 139  public function stream_read($nb)
 140  {
 141  \PHK\Tools\Util::trace("Starting stream_read: uri=".$this->uri." - nb=$nb - position=".$this->position." size=".$this->size);//TRACE
 142  
 143  if ($this->position==$this->size) return false;
 144  $max=$this->size-($pos=$this->position);
 145  if ($nb > $max) $nb=$max;
 146  $this->position+=$nb;
 147  
 148  return substr($this->data,$pos,$nb);
 149  }
 150  
 151  //---------------------------------
 152  // Are we at the end of an open file ?
 153  
 154  public function stream_eof()
 155  {
 156  return ($this->position==$this->size);
 157  }
 158  
 159  //---------------------------------
 160  // Return current position in an open stream
 161  
 162  public function stream_tell()
 163  {
 164  return $this->position;
 165  }
 166  
 167  //---------------------------------
 168  // Seek on an open file
 169  
 170  public function stream_seek($offset,$whence)
 171  {
 172  \PHK\Tools\Util::trace("Starting stream_seek: uri=".$this->uri." - offset=$offset - whence=$whence");//TRACE
 173  
 174  switch($whence)
 175      {
 176      case SEEK_CUR: $this->position+=$offset; break;
 177      case SEEK_END: $this->position=$this->size+$offset; break;
 178      default: $this->position=$offset; break;
 179      }
 180  if ($this->position > $this->size) $this->position=$this->size;
 181  if ($this->position < 0) $this->position=0;
 182  return true;
 183  }
 184  
 185  //---------------------------------
 186  // Open a directory
 187  //
 188  // We must check the mount point validity because we can be called for an
 189  // unmounted path. In this case, we must return false before searching the
 190  // cache, or the behavior will be different when the data is in the cache
 191  // or not. Note that we check for global commands before validating the mount
 192  // point.
 193  
 194  public function dir_opendir($uri,$options)
 195  {
 196  try
 197  {
 198  $this->uri=$uri;
 199  $this->raiseErrors=($options & STREAM_REPORT_ERRORS);
 200  
 201  self::parseURI($uri,$this->command,$this->params,$this->mnt
 202      ,$this->path);
 203  
 204  if (!is_null($this->mnt)) \PHK\Mgr::validate($this->mnt);
 205  
 206  $this->data=self::getFile(true,$uri,$this->mnt,$this->command
 207      ,$this->params,$this->path);
 208  
 209  $this->size=count($this->data);
 210  $this->position=0;
 211  }
 212  catch (\Exception $e)
 213      {
 214      $msg=$uri.': PHK opendir error - '.$e->getMessage();
 215      $this->raiseWarning($msg);
 216      return false;
 217      }
 218  return true;
 219  }
 220  
 221  //---------------------------------
 222  // Get next directory entry
 223  
 224  public function dir_readdir()
 225  {
 226  if ($this->position==$this->size) return false;
 227  return $this->data[$this->position++];
 228  }
 229  
 230  //---------------------------------
 231  // Set open directory index to 0
 232  
 233  public function dir_rewinddir()
 234  {
 235  $this->position=0;
 236  }
 237  
 238  //---------------------------------
 239  // Utility function called by stream_stat and url_stat
 240  
 241  private static function statArray($mode,$size,$mtime)
 242  {
 243  return array(
 244      'dev' => 0,
 245      'ino' => 0,
 246      'mode' => $mode,
 247      'nlink' => 1,
 248      'uid' => 0,
 249      'gid' => 0,
 250      'rdev' => -1,
 251      'size' => $size,
 252      'atime' => $mtime,
 253      'mtime' => $mtime,
 254      'ctime' => $mtime,
 255      'blksize' => 8192,
 256      'blocks' => 1);
 257  }
 258  
 259  //---------------------------------
 260  // Stat an open file (fstat)
 261  
 262  public function stream_stat()
 263  {
 264  \PHK\Tools\Util::trace("Entering stream_stat");//TRACE
 265  
 266  return $this->url_stat($this->uri,0,true);
 267  }
 268  
 269  //---------------------------------
 270  // Stat an open or closed path - PHP streams API
 271  //
 272  // url_stat does not throw exceptions. It must just return false.
 273  //
 274  // This method must not modify properties (except for parsing the URI),
 275  // because it can be called on an open path.
 276  //
 277  // We must check the mount point validity because we can be called for an
 278  // unmounted path. In this case, we must return false before searching the
 279  // cache, or the behavior will be different when the data is in the cache
 280  // or not. Note that we check for global commands before validating the mount
 281  // point.
 282  
 283  public function url_stat($uri,$flags,$fstat=false)
 284  {
 285  \PHK\Tools\Util::trace("Entering url_stat($uri,$flags,$fstat)");//TRACE
 286  
 287  try
 288  {
 289  $this->raiseErrors=!($flags & STREAM_URL_STAT_QUIET);
 290  
 291  // If we are coming from stream_fstat(), the uri is already parsed.
 292  
 293  if (!$fstat)
 294      {
 295      self::parseURI($uri,$this->command,$this->params,$this->mnt
 296          ,$this->path);
 297  
 298      if (!is_null($this->mnt)) \PHK\Mgr::validate($this->mnt);
 299      }
 300  
 301  $cacheID=\PHK\Cache::cacheID('stat',$uri);
 302  if (is_null($data=\PHK\Cache::get($cacheID)))    // Miss - Slow path
 303      {
 304      \PHK\Tools\Util::trace("url_stat($uri): not found in cache");//TRACE
 305      try
 306          {
 307          $cache=true;
 308          $mode=$size=$mtime=null;
 309          Backend::getStatData($this->mnt,$this->command
 310              ,$this->params,$this->path,$cache,$mode,$size,$mtime);
 311          $data=array($mode,$size,$mtime);
 312          }
 313      catch (\Exception $e) // Mark entry as non-existent
 314          {
 315          \PHK\Tools\Util::trace("url_stat($uri): lookup failed");//TRACE
 316          $data='';
 317          }
 318  
 319      if ($cache && (!is_null($this->mnt)) && \PHK\Mgr::cacheEnabled($this->mnt
 320          ,$this->command,$this->params,$this->path))
 321          {
 322          \PHK\Cache::set($cacheID,$data);
 323          }
 324      }
 325  
 326  if (is_array($data))
 327      {
 328      list($mode,$size,$mtime)=$data;
 329      return self::statArray($mode,$size,$mtime);
 330      }
 331  else throw new \Exception('File not found');    // Negative hit
 332  }
 333  catch (\Exception $e)
 334      {
 335      $msg=$uri.': PHK Stat error - '.$e->getMessage();
 336      $this->raiseWarning($msg);
 337      return false;
 338      }
 339  }
 340  
 341  //---------------------------------
 342  /**
 343  * Parses an URI and splits it into four sub-parts : mount point, command name,
 344  * command parameters, and path. Each of these components is optional.
 345  *
 346  * URI syntax: phk://[<mnt>[/path]][?command[&par=val&...]]
 347  *
 348  * On return, if no command: command=params=null
 349  * Global command: phk://?command[&par=val&...] => path=mnt=null
 350  * mnt=null => global command
 351  *
 352  * Test cases :
 353  * phk://   Error
 354  * phk://mnt1   mnt=mnt1, path='', command=params=null
 355  * phk://mnt1/p1/p2      mnt=mnt1, path=p1/p2, command=params=null
 356  * phk://mnt1/p1/p2?cmd&par1=2&par2=3   mnt=mnt1, path=p1/p2, command=cmd,
 357  *      params=array(par1 => 2, par2 => 3)
 358  * phk://?gcmd  mnt=path=null, command=gcmd, params=null
 359  *
 360  * @param string $uri
 361  * @param string|null $command Return value
 362  * @param array|null $params Return value
 363  * @param string|null $mnt Return value - Null only if global command
 364  * @param string|null $path Return value - Null only if global command
 365  * @return void
 366  * @throws \Exception on invalid syntax
 367  */
 368  
 369  public static function parseURI($uri,&$command,&$params,&$mnt,&$path)
 370  {
 371  \PHK\Tools\Util::trace("Entering parseURI($uri)");//TRACE
 372  
 373  if (! \PHK\Mgr::isPhkUri($uri=str_replace('\\','/',$orig_uri=$uri)))
 374      throw new \Exception('Not a PHK URI');
 375  $uri=substr($uri,6);    // Remove 'phk://'
 376  
 377  if (($pos=strpos($uri,'?'))!==false)    // If the uri contains a command
 378      {
 379      $cmd=\PHK\Tools\Util::substr($uri,$pos+1);
 380      $uri=substr($uri,0,$pos);
 381      if (($pos=strpos($cmd,'&'))!==false)    // params present
 382          {
 383          $command=substr($cmd,0,$pos);
 384          parse_str(\PHK\Tools\Util::substr($cmd,$pos+1),$params); // Get parameters
 385          }
 386      else $command=$cmd;
 387      if ($command=='') throw new \Exception('Empty command');
 388      }
 389  
 390  $uri=trim($uri,'/');    // Suppress leading and trailing '/'
 391  if ($uri!='') // Not a global command
 392      {
 393      $a=explode('/',$uri,2);    //-- Separate mnt and path
 394      $mnt=$a[0];
 395      $path=isset($a[1]) ? $a[1] : '';
 396      }
 397  
 398  if (is_null($command) && is_null($mnt)) throw new \Exception('Empty URI');
 399  }
 400  
 401  //---------------------------------
 402  } // End of class
 403  //=============================================================================
 404  // Register the PHK wrapper
 405  
 406  stream_wrapper_register('phk','PHK\Stream\Wrapper');
 407  
 408  //=============================================================================
 409  } // End of class_exists
 410  //=============================================================================
 411  } // End of namespace
 412  //===========================================================================
 413  ?>


Generated: Thu Jun 4 18:33:15 2015 Cross-referenced by PHPXref 0.7.1