[ Index ] |
PHP Cross Reference of PHK Manager |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================================= 3 // 4 // Copyright Francois Laupretre <phk@tekwire.net> 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // 10 // http://www.apache.org/licenses/LICENSE-2.0 11 // 12 // Unless required by applicable law or agreed to in writing, software 13 // distributed under the License is distributed on an "AS IS" BASIS, 14 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 // See the License for the specific language governing permissions and 16 // limitations under the License. 17 // 18 //============================================================================= 19 /** 20 * @copyright Francois Laupretre <phk@tekwire.net> 21 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0 22 * @category PHK 23 * @package PHK 24 *///========================================================================== 25 26 namespace PHK { 27 28 if (!class_exists('PHK\Mgr',false)) 29 { 30 //============================================================================= 31 /** 32 * The PHK manager 33 * 34 * This class manages the table of currently mounted packages. 35 * 36 * Each package is uniquely identified by a 'mount point' (a string computed 37 * at mount time). A given package file always gets the same mount point in 38 * every request, as long as it is not modified. 39 * 40 * Among others, this class allows to mount and umount packages. 41 * 42 * Runtime code -> 100% read-only. 43 * 44 * Static-only 45 * API status: Public 46 * Included in the PHK PHP runtime: Yes 47 * Implemented in the extension: Yes 48 *///========================================================================== 49 50 class Mgr 51 { 52 //-- Global static properties 53 54 /** @var array Currently mounted PHK instances */ 55 56 private static $phk_tab=array(); // Key=mount ID, value=PHK instance 57 58 /** 59 * @var array Proxy objects for each currently mounted package. As 60 * long as the proxy object is not instantiated through the proxy() method, 61 * the corresponding value is null in this array. Contains exactly the same 62 * keys as $phk_tab. 63 */ 64 65 private static $proxy_tab=array(); // Key=mount ID, value=PHK\Proxy|null 66 67 /* @var int Running value for \PHK\Build\Creator mount points */ 68 69 private static $tmp_mnt_num=0; 70 71 /* @var boolean|null Global cache toggle. If null, per-instance logic is used */ 72 73 private static $caching=null; 74 75 //----- 76 /** 77 * Checks if a mount point is valid (if it corresponds to a currently mounted 78 * package) 79 * 80 * @param string $mnt Mount point to check 81 * @return boolean 82 */ 83 84 public static function isMounted($mnt) 85 { 86 return isset(self::$phk_tab[$mnt]); 87 } 88 89 //----- 90 /** 91 * Same as isMounted but throws an exception is the mount point is invalid. 92 * 93 * Returns the mount point so that it can be embedded in a call string. 94 * 95 * @param string $mnt Mount point to check 96 * @return string mount point (not modified) 97 * @throws \Exception if mount point is invalid 98 */ 99 100 public static function validate($mnt) 101 { 102 if (!self::isMounted($mnt)) throw new \Exception($mnt.': Invalid mount point'); 103 104 return $mnt; 105 } 106 107 //----- 108 /** 109 * Returns the PHK object corresponding to a given mount point 110 * 111 * @param string $mnt Mount point 112 * @return PHK instance 113 * @throws \Exception if mount point is invalid 114 */ 115 116 public static function instance($mnt) 117 { 118 self::validate($mnt); 119 120 return self::$phk_tab[$mnt]; 121 } 122 123 //----- 124 /** 125 * Returns the \PHK\Proxy object corresponding to a given mount point 126 * 127 * If the corresponding \PHK\Proxy object does not exist yet, it is created. 128 * 129 * @param string $mnt Mount point 130 * @return \PHK\Proxy proxy object 131 * @throws \Exception if mount point is invalid 132 */ 133 134 public static function proxy($mnt) 135 { 136 self::validate($mnt); 137 138 if (is_null(self::$proxy_tab[$mnt])) 139 { 140 $phk=self::instance($mnt); 141 self::$proxy_tab[$mnt]=new \PHK\Proxy($phk->path(),$phk->flags()); 142 } 143 144 return self::$proxy_tab[$mnt]; 145 } 146 147 //----- 148 /** 149 * Returns the list of the defined mount points. 150 * 151 * @return array 152 */ 153 154 public static function mntList() 155 { 156 return array_keys(self::$phk_tab); 157 } 158 159 //--------- 160 /** 161 * Sets the global caching toggle 162 * 163 * Normally, the global cache toggle is always null, except in 'webinfo' 164 * mode, where it is false to inhibit any caching of webinfo information 165 * (2 reasons: useless in terms of performance, and could use the same keys as 166 * the 'non-webinfo' mode, so we would have to use another key prefix). 167 * 168 * @param boolean|null $caching True: always cache, False: never cache, 169 * null: use per-instance logic. 170 * @return void 171 */ 172 173 public static function setCache($caching) 174 { 175 self::$caching=$caching; 176 } 177 178 //--------- 179 /** 180 * Determines if a an URI can be cached. 181 * 182 * For performance reasons, the input URI is splitted. 183 * 184 * Called by the wrapper to know if it should cache the data it got from its 185 * backend. 186 * 187 * The global cache toggle is checked first. If it is null, control is 188 * transferred to the instance logic. 189 * 190 * @param string|null $mnt Mount point or null if global command 191 * @param string|null $command command if defined 192 * @param array|null $params Command parameters if defined 193 * @param string $path Path 194 * @return boolean whether the data should be cached or not 195 * @throws \Exception if mount point is invalid 196 */ 197 198 199 public static function cacheEnabled($mnt,$command,$params,$path) 200 { 201 if (!is_null(self::$caching)) return self::$caching; 202 203 if (is_null($mnt)) return false; 204 205 return self::instance($mnt)->cacheEnabled($command,$params,$path); 206 } 207 208 //--------- 209 /** 210 * Given a file path, tries to determine if it is currently mounted. If it is 211 * the case, the corresponding mount point is returned. If not, an exception is 212 * thrown. 213 * 214 * Note: when dealing with sub-packages, the input path parameter can be a PHK 215 * URI. 216 * 217 * @param string $path Path of a PHK package 218 * @return the corresponding mount point 219 * @throws \Exception if the file is not currently mounted 220 */ 221 222 public static function pathToMnt($path) 223 { 224 $dummy1=$mnt=$dummy2=null; 225 self::computeMnt($path,$dummy1,$mnt,$dummy2); 226 227 if (self::isMounted($mnt)) return $mnt; 228 229 throw new \Exception($path.': path is not mounted'); 230 } 231 232 //--------- 233 /** 234 * Given a PHK uri, returns the path of the first-level package for 235 * this path. The function recurses until it finds a physical path. 236 * 237 * @param string $path A path typically set as '__FILE__' 238 * @return the physical path of the 1st-level package containing this path 239 */ 240 241 public static function topLevelPath($path) 242 { 243 while (self::isPhkUri($path)) 244 { 245 $mnt=self::uriToMnt($path); 246 $map=self::instance($mnt); 247 $path=$map->path(); 248 } 249 return $path; 250 } 251 252 //--------- 253 /** 254 * Mount a PHK package and returns the new (or previous, if already loaded) 255 * PHK mount point. 256 * 257 * Can also create empty \PHK\Build\Creator instances (when the 'CREATOR' flag is set). 258 * 259 * @param string $path The path of an existing PHK archive, or the path of the 260 * archive to create if ($flags & \PHK::IS_CREATOR) 261 * @param int $flags Or-ed combination of PHK mount flags. 262 * @return string the mount point 263 */ 264 265 public static function mount($path,$flags=0) 266 { 267 try 268 { 269 if ($flags & \PHK::IS_CREATOR) 270 { 271 $mnt='_tmp_mnt_'.(self::$tmp_mnt_num++); 272 self::$proxy_tab[$mnt]=null; 273 self::$phk_tab[$mnt]=new \PHK\Build\Creator($mnt,$path,$flags); 274 } 275 else // Mount an existing archive 276 { 277 $parentMnt=$mnt=$mtime=$options=$buildInfo=null; 278 self::computeMnt($path,$parentMnt,$mnt,$mtime); 279 if (self::isMounted($mnt)) return $mnt; 280 281 self::$proxy_tab[$mnt]=null; 282 self::$phk_tab[$mnt]=$phk=new \PHK($parentMnt,$mnt,$path,$flags,$mtime); 283 284 self::getStoreData($mnt,$options,$buildInfo); 285 $phk->init($options,$buildInfo); 286 } 287 } 288 catch (\Exception $e) 289 { 290 if (isset($mnt) && self::isMounted($mnt)) unset(self::$phk_tab[$mnt]); 291 throw new \Exception($path.': Cannot mount - '.$e->getMessage()); 292 } 293 294 return $mnt; 295 } 296 297 //----- 298 /** 299 * Checks the PHK version this package requires against the current version. 300 * Then, retrieves the 'options' and 'buildInfo' arrays. 301 * 302 * This function is separated from mount() to mimic the behavior of the PHK 303 * extension, where this data is cached in persistent memory. 304 * 305 * @param string $mnt the mount point 306 * @param array $options on return, contains the options array 307 * @param array $buildInfo on return, contains the buildInfo array 308 * @return void 309 */ 310 311 private static function getStoreData($mnt,&$options,&$buildInfo) 312 { 313 $caching=(is_null(self::$caching) ? true : self::$caching); 314 315 // Must check this first 316 317 $mv=\PHK\Tools\Util::getMinVersion($mnt,$caching); 318 319 if (version_compare($mv,\PHK::RUNTIME_VERSION) > 0) 320 { 321 \PHK\Tools\Util::formatError('Cannot understand this version. ' 322 .'Requires at least PHK version '.$mv); 323 } 324 325 $options=\PHK\Tools\Util::getOptions($mnt,$caching); 326 $buildInfo=\PHK\Tools\Util::getBuildInfo($mnt,$caching); 327 } 328 329 //--------------------------------- 330 /** 331 * Computes the mount point corresponding to a given path. 332 * 333 * Also returns the parent mount point (for sub-packages), and the modification 334 * time (allows to call stat() only once). 335 * 336 * Mount point uniqueness is based on a combination of device+inode+mtime. 337 * 338 * When dealing with sub-packages, the input path is a PHK URI. 339 * 340 * Sub-packages inherit their parent's modification time. 341 * 342 * @param string $path The path to be mounted 343 * @param $parentMnt string|null returns the parent mount point. Not 344 * null only for sub-packages. 345 * @param string $mnt returns the computed mount point 346 * @param int $mtime returns the modification time 347 * @return void 348 * @throws \Exception with message 'File not found' if unable to stat($path). 349 */ 350 351 private static function computeMnt($path,&$parentMnt,&$mnt,&$mtime) 352 { 353 if (self::isPhkUri($path)) // Sub-package 354 { 355 $dummy1=$dummy2=$subpath=$parentMnt=null; 356 \PHK\Stream\Wrapper::parseURI($path,$dummy1,$dummy2,$parentMnt,$subpath); 357 self::validate($parentMnt); 358 $mnt=$parentMnt.'#'.str_replace('/','*',$subpath); 359 $mtime=self::instance($parentMnt)->mtime(); // Inherit mtime 360 } 361 else 362 { 363 $mnt=\PHK\Tools\Util::pathUniqueID('p',$path,$mtime); 364 $parentMnt=null; 365 } 366 } 367 368 //--------------------------------- 369 /** 370 * Umounts a mounted package and any mounted descendant. 371 * 372 * We dont use __destruct because : 373 * 1. We don't want this to be called on script shutdown 374 * 2. \Exceptions cannot be caught when sent from a destructor. 375 * 376 * Accepts to remove a non registered mount point without error 377 * 378 * @param string $mnt The mount point to umount 379 */ 380 381 public static function umount($mnt) 382 { 383 if (self::isMounted($mnt)) 384 { 385 // Umount children 386 387 foreach (array_keys(self::$phk_tab) as $dmnt) 388 { 389 if (isset(self::$phk_tab[$dmnt]) 390 && self::$phk_tab[$dmnt]->parentMnt()===$mnt) 391 self::umount($dmnt); 392 } 393 394 // Call instance's umount procedure 395 396 self::$phk_tab[$mnt]->umount(); 397 398 // Remove from the list 399 400 unset(self::$phk_tab[$mnt]); 401 unset(self::$proxy_tab[$mnt]); 402 } 403 } 404 405 //--------- 406 /** 407 * Builds a 'phk://' uri, from a mount ID and a path 408 * 409 * @param string $mnt The mount point 410 * @param string $path The path 411 * @return string The computed URI 412 */ 413 414 public static function uri($mnt,$path) 415 { 416 return self::baseURI($mnt).ltrim($path,'/'); 417 } 418 419 //----- 420 /** Checks if a string is a PHK URI 421 * 422 * @param string $uri 423 * @return boolean 424 */ 425 426 public static function isPhkUri($uri) 427 { 428 $u=$uri.' '; 429 430 // Much faster this way 431 432 return ($u{0}=='p' && $u{1}=='h' && $u{2}=='k' && $u{3}==':' 433 && $u{4}=='/' && $u{5}=='/'); 434 435 //return (strlen($uri) >= 6) && (substr($uri,0,6)=='phk://'); 436 } 437 438 //----- 439 /** 440 * Returns the base string used to build URIs for a given mount point. 441 * 442 * The base URI has the form : phk://<mount point>/ 443 * 444 * @param string $mnt A mount point 445 * @return string 446 */ 447 448 public static function baseURI($mnt) 449 { 450 return 'phk://'.$mnt.'/'; 451 } 452 453 //--------- 454 /** 455 * Returns a 'command' URI, given a mount point and a 'command' string 456 * 457 * Command URIs have the form : phk://<mount point>/?<command> 458 * 459 * @param string $mnt A mount point 460 * @param string $command Command string 461 * @return string 462 */ 463 464 public static function commandURI($mnt,$command) 465 { 466 return self::uri($mnt,'?'.$command); 467 } 468 469 //--------- 470 /** 471 * Returns the URI allowing to retrieve a section. 472 * 473 * Section URIs have the form : phk://<mount point>/?section&name=<section> 474 * 475 * @param string $mnt A mount point 476 * @param string $section The section to retrieve 477 * @return string 478 */ 479 480 public static function sectionURI($mnt,$section) 481 { 482 return self::commandURI($mnt,'section&name='.$section); 483 } 484 485 //----- 486 /** 487 * Returns the URI of the Automap map if it is defined 488 * 489 * @param string $mnt A mount point 490 * @return string|null returns null if the package does not define an automap. 491 */ 492 493 public static function automapURI($mnt) 494 { 495 if ((!self::isMounted($mnt))||(!self::instance($mnt)->mapDefined())) 496 return null; 497 498 return self::sectionURI($mnt,'AUTOMAP'); 499 } 500 501 //--------------------------------- 502 /** 503 * Replaces '\' characters by '/' in a URI. 504 * 505 * @param string $uri 506 * @return string 507 */ 508 509 public static function normalizeURI($uri) 510 { 511 return str_replace('\\','/',$uri); 512 } 513 514 //----- 515 /** 516 * Returns the mount ID of a subfile's phk uri. 517 * Allows to reference other subfiles in the same package if you don't want 518 * or cannot use Automap (the preferred method) or a relative path. 519 * Example : include(\PHK\Mgr::uri(\PHK\Mgr::uriToMnt(__FILE__),<path>)); 520 * 521 * @param string $uri 522 * @return string a mount point 523 * @throws \Exception if the input URI is not a PHK URI 524 */ 525 526 public static function uriToMnt($uri) 527 { 528 if (! self::isPhkUri($uri)) 529 throw new \Exception($uri.': Not a PHK URI'); 530 531 $buf=substr(self::normalizeURI($uri),6); 532 $buf=substr($buf,0,strcspn($buf,'/')); 533 return trim($buf); 534 } 535 536 //--------- 537 /** 538 * Check if the current PHP version is supported. 539 * 540 * Note that, if PHP version < 5.3, parsing fails because of namespaces and we 541 * don't even start execution. So, this test is only executed when PHP version 542 * >= 5.3 543 * 544 * As a side effect, until we require a version > 5.3, this function 545 * never fails. 546 * 547 * Calls exit(1) if PHP version is not supported by the PHK runtime 548 * 549 * @return void 550 */ 551 552 public static function checkPhpVersion() 553 { 554 if (version_compare(PHP_VERSION,'5.3.0') < 0) 555 { 556 echo PHP_VERSION.': Unsupported PHP version ' 557 .'- PHK needs at least version 5.3.0'; 558 exit(1); 559 } 560 } 561 562 //--- 563 } // End of class 564 //=========================================================================== 565 } // End of class_exists 566 //=========================================================================== 567 } // End of namespace 568 //=========================================================================== 569 ?>
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 |