[ Index ] |
PHP Cross Reference of PHK Manager |
[Summary view] [Print] [Text view]
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 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jun 4 18:33:15 2015 | Cross-referenced by PHPXref 0.7.1 |