[ 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\Base',false)) 29 { 30 //============================================================================= 31 /** 32 * A mounted PHK archive file 33 * 34 * This file contains the front-end class for PHK object. This class gets its 35 * data from the stream wrapper, which retrieves them from the cache or from 36 * PHK\Proxy. This class is also used as base class for PHK\Build\Creator instances. 37 * 38 * This class does never access the package file directly. 39 * 40 * Note: When dealing with a sub-package, the mounted archive file is a 'phk://' 41 * virtual file contained in a higher-level PHK archive (which, itself, can be 42 * virtual). There is no limit to the nesting level of sub-packages. 43 * 44 * Runtime code -> 100% read-only 45 * 46 * PHK objects are created and destructed from the PHK manager (PHK\Mgr class). 47 * 48 * You get the PHK object instance by calling \PHK\Mgr::instance with a mount 49 * point. This mount point is generally returned either by an include, 50 * when including a package, or by \PHK\Mgr::uriToMnt(). 51 * 52 * @see \PHK\Mgr 53 * @see \PHK\Proxy 54 * 55 * API status: Public 56 * Included in the PHK PHP runtime: Yes 57 * Implemented in the extension: Yes. Not used when extension is active. 58 *///========================================================================== 59 60 abstract class Base 61 { 62 //========== Class constants =============== 63 64 /** Version. Checked against package's min runtime version */ 65 /* Keep this value in sync with SOFTWARE_VERSION defined in make.common */ 66 67 const RUNTIME_VERSION='3.0.0'; 68 69 //----- 70 // Mount flags 71 /* The values defined here must be the same as in the accelerator extension */ 72 /* PHK mount flags must not conflict with \Automap\Mgr::T_xx load flags as they can be */ 73 /* combined. */ 74 75 /** Mount flag - If set, force a CRC check when creating the PHK instance */ 76 77 const CRC_CHECK=16; 78 79 /** Mount flag - If set, don't call mount/umount scripts */ 80 81 const NO_MOUNT_SCRIPT=32; 82 83 /** Mount flag - If set, create a \PHK\Build\Creator object, instead of a PHK object */ 84 85 const IS_CREATOR=64; 86 87 //========== Class properties =============== 88 89 /** @var bool Whether instance is valid or not (unmounted) */ 90 91 private $valid; 92 93 /** @var string Current mount point */ 94 95 protected $mnt; 96 97 /** @var string Parent mount point (for a subpackage) or null */ 98 99 protected $parentMnt; 100 101 /** @var array Package options */ 102 103 protected $options=null; // Array 104 105 /** @var array Build-time information */ 106 107 protected $buildInfo=null; 108 109 /** @var integer Mount flags */ 110 111 protected $flags; 112 113 /** @var string|null Automap load ID (if a map is present) */ 114 115 protected $automapID; 116 117 /** @var integer Package path (URI when subpackage) */ 118 119 protected $path; 120 121 /** @var Object|null Plugin object, if defined */ 122 123 protected $plugin=null; 124 125 /** @var boolean|null Allows to temporarily enable/disable caching */ 126 127 protected $caching=null; 128 129 /** @var int The modification time for every subfiles */ 130 131 protected $mtime; 132 133 /** @var \PHK\Backend The slow backend object (created only when needed) */ 134 135 protected $backend=null; 136 137 /** @var array File extension to mime type (constant) 138 * 139 * Would be cleaner if PHP class constants could contain arrays */ 140 141 protected static $mimeTable=array( 142 '' => 'text/plain', 143 'gif' => 'image/gif', 144 'jpeg' => 'image/jpeg', 145 'jpg' => 'image/jpeg', 146 'png' => 'image/png', 147 'psd' => 'image/psd', 148 'bmp' => 'image/bmp', 149 'tif' => 'image/tiff', 150 'tiff' => 'image/tiff', 151 'iff' => 'image/iff', 152 'wbmp' => 'image/vnd.wap.wbmp', 153 'ico' => 'image/x-icon', 154 'xbm' => 'image/xbm', 155 'txt' => 'text/plain', 156 'htm' => 'text/html', 157 'html' => 'text/html', 158 'css' => 'text/css', 159 'php' => 'application/x-httpd-php', 160 'phk' => 'application/x-httpd-php', 161 'inc' => 'application/x-httpd-php', 162 'hh' => 'application/x-httpd-php', 163 'pdf' => 'application/pdf', 164 'js' => 'application/x-javascript', 165 'swf' => 'application/x-shockwave-flash', 166 'xml' => 'application/xml', 167 'xsl' => 'application/xml', 168 'xslt' => 'application/xslt+xml', 169 'mp3' => 'audio/mpeg', 170 'ram' => 'audio/x-pn-realaudio', 171 'svg' => 'image/svg+xml' 172 ); 173 174 //========== Class methods =============== 175 176 // Methods to get read-only properties 177 178 public function mnt() { $this->validate(); return $this->mnt; } 179 public function flags() { $this->validate(); return $this->flags; } 180 public function path() { $this->validate(); return $this->path; } 181 public function mtime() { $this->validate(); return $this->mtime; } 182 public function automapID() { $this->validate(); return $this->automapID; } 183 public function options() { $this->validate(); return $this->options; } 184 public function parentMnt() { $this->validate(); return $this->parentMnt; } 185 public function plugin() { $this->validate(); return $this->plugin; } 186 187 //----- 188 189 public function __construct($parentMnt,$mnt,$path,$flags,$mtime) 190 { 191 $this->valid=true; 192 $this->parentMnt=$parentMnt; 193 $this->mnt=$mnt; 194 $this->path=$path; 195 $this->flags=$flags; 196 $this->mtime=$mtime; 197 } 198 199 //----- 200 201 public function validate() 202 { 203 if (!$this->valid) 204 throw new \Exception("Accessing invalid or unmounted object"); 205 } 206 207 //----- 208 209 public function init($options,$buildInfo) 210 { 211 try 212 { 213 $this->options=$options; 214 $this->buildInfo=$buildInfo; 215 216 $this->supportsPhpVersion(); 217 218 if ($this->option('crc_check') || ($this->flags & self::CRC_CHECK)) 219 $this->crcCheck(); 220 221 // As required extensions are added to the enclosing package when a subpackage 222 // is inserted, we don't have to check subpackages for required extensions. 223 224 if (is_null($this->parentMnt)) 225 { 226 if (!is_null($extensions=$this->option('required_extensions'))) 227 \PHK\Tools\Util::loadExtensions($extensions); 228 } 229 230 if ($this->mapDefined()) 231 { 232 // Transmit PHK mount flags to Automap 233 $this->automapID=\Automap\Mgr::load($this->automapURI() 234 ,$this->flags,$this->baseURI()); 235 } 236 else $this->automapID=0; 237 238 //-- Call the mount script - if the mount script wants to refuse the mount, 239 //-- it throws an exception. 240 241 if (!($this->flags & \PHK::NO_MOUNT_SCRIPT) 242 && (!is_null($mpath=$this->option('mount_script')))) 243 { require $this->uri($mpath); } 244 245 //-- Create the plugin_object 246 247 if (!is_null($c=$this->option('plugin_class'))) 248 $this->plugin=new $c($this->mnt); 249 } 250 catch (\Exception $e) 251 { 252 throw new \Exception('While initializing PHK instance - '.$e->getMessage()); 253 } 254 } 255 256 //--------- 257 258 public function mapDefined() 259 { 260 $this->validate(); 261 262 if ($this->flags & \PHK::IS_CREATOR) return false; 263 264 return $this->buildInfo('map_defined'); 265 } 266 267 //--------- 268 269 public function setCache($toggle) 270 { 271 $this->validate(); 272 273 $this->caching=$toggle; 274 } 275 276 //--------- 277 /** 278 * Check if a given path contains a PHK package 279 * 280 * @param string $path path to check (can be virtual) 281 * @return boolean 282 */ 283 284 public static function fileIsPackage($path) 285 { 286 return \PHK\Proxy::fileIsPackage($path); 287 } 288 289 //--------- 290 /** 291 * Check if a data buffer contains a PHK package 292 * 293 * @param string $data data buffer to check 294 * @return boolean 295 */ 296 297 public static function dataIsPackage($data) 298 { 299 return \PHK\Proxy::dataIsPackage($data); 300 } 301 302 //----- 303 304 public function cacheEnabled($command,$params,$path) 305 { 306 $this->validate(); 307 308 if ($this->flags & \PHK::IS_CREATOR) return false; 309 310 if ($this->option('no_cache')===true) return false; 311 312 if (!\PHK\Cache::cachePresent()) return false; 313 314 if (!is_null($this->caching)) return $this->caching; 315 316 return true; 317 } 318 319 //----- 320 // Umount this entry. 321 // We dont use __destruct because : 322 // 1. We don't want this to be called on script shutdown 323 // 2. Exceptions cannot be caught when sent from a destructor. 324 325 public function umount() 326 { 327 $this->validate(); 328 329 //-- Destroy the plugin 330 331 if (!is_null($this->plugin)) unset($this->plugin); 332 333 //-- Call the umount script 334 335 if (!($this->flags & \PHK::NO_MOUNT_SCRIPT)) // Call the umount script 336 { 337 if (!is_null($upath=$this->option('umount_script'))) 338 { require($this->uri($upath)); } 339 } 340 341 //-- Unload the automap 342 343 if ($this->automapID) \Automap\Mgr::unload($this->automapID); 344 345 $this->valid=false; 346 } 347 348 //----- 349 350 public function uri($path) 351 { 352 $this->validate(); 353 return \PHK\Mgr::uri($this->mnt,$path); 354 } 355 356 //----- 357 358 public function sectionURI($section) 359 { 360 $this->validate(); 361 return \PHK\Mgr::sectionURI($this->mnt,$section); 362 } 363 364 //----- 365 366 public function commandURI($command) 367 { 368 $this->validate(); 369 return \PHK\Mgr::commandURI($this->mnt,$command); 370 } 371 372 //----- 373 374 public function baseURI() 375 { 376 $this->validate(); 377 return \PHK\Mgr::baseURI($this->mnt); 378 } 379 380 //----- 381 /** 382 * Returns the URI of the map 383 * 384 * @return string 385 */ 386 387 public function automapURI() 388 { 389 $this->validate(); 390 return \PHK\Mgr::automapURI($this->mnt); 391 } 392 393 //----- 394 /** 395 * Returns an option 396 * 397 * If the option is not set, returns null. 398 * 399 * The 'OPTIONS' section is mandatory in a package. 400 * 401 * @param string $key The option name 402 * @return any|null Option value or null if the requested option is not set 403 */ 404 405 406 public function option($key) 407 { 408 $this->validate(); 409 return (isset($this->options[$key]) ? $this->options[$key] : null); 410 } 411 412 //--------------------------------- 413 414 public function webAccessAllowed($path) 415 { 416 $this->validate(); 417 $plen=strlen($path); 418 419 foreach(\PHK\Tools\Util::mkArray($this->option('web_access')) as $apath) 420 { 421 if ($apath=='/') return true; 422 $alen=strlen($apath); 423 if (($plen >= $alen) && (substr($path,0,$alen)==$apath) 424 && (($alen==$plen)||($path{$alen}=='/'))) 425 return true; 426 } 427 428 return false; 429 } 430 431 //--------------------------------- 432 // Transfer control to main script (web mode) 433 // Two methods: redirect or transparently execute main script. 434 435 private function gotoMain($web_run_script) 436 { 437 if ($this->option('web_main_redirect')) 438 { 439 \PHK\Tools\Util::http301Redirect($web_run_script); 440 } 441 else return 'require(\''.$this->uri($web_run_script).'\');'; 442 } 443 444 //--------------------------------- 445 // Returns the code to display or execute a subfile from the calling code. We 446 // cannot directly include the subfile from this function because the variable 447 // scope must be the calling one. 448 // Use as : eval($phk->webTunnel([$path [,webinfo mode]])); 449 // This function is supposed to transfer control in as transparent a manner as 450 // possible. 451 // If the given path is a directory, tries to find an index.[htm|html|php] file. 452 // This function does not support subpaths in PHK subfiles. 453 454 public function webTunnel($path=null,$webinfo=false) 455 { 456 $this->validate(); 457 if (is_null($path)) $path=\PHK::setSubpath(); 458 $last_slash=(substr($path,-1)=='/'); 459 if ($path!='/') $path=rtrim($path,'/'); 460 $web_run_script=$this->option('web_run_script'); 461 $mnt=$this->mnt(); 462 463 if ($path=='') 464 { 465 if (!is_null($web_run_script)) return $this->gotoMain($web_run_script); 466 else \PHK\Tools\Util::http301Redirect('/'); // Redirect to the virtual root dir 467 } 468 469 // If a package use a path as both file and http paths, we can receive 470 // a PHK URI. Handle this. Ugly: to be suppressed when PHP makes 471 // current directory compatible with stream wrappers. 472 // We check for one or two '/' between 'phk:' and $mnt because Apache removes 473 // the 2nd '/'. 474 // Suppressed in v 1.4.0: don't know if still useful ? 475 476 //$path=str_replace('phk:/'.$mnt.'/','',$path); 477 //$path=str_replace('phk://'.$mnt.'/','',$path); 478 479 // Access enabled ? If not in the enabled paths, go to the main script 480 // Allows to support a mod_rewrite-like feature where a single entry point 481 // gets every request. 482 483 if ((!$webinfo) && (!$this->webAccessAllowed($path)) 484 && ($path!==$web_run_script)) 485 { 486 if (!is_null($web_run_script)) return $this->gotoMain($web_run_script); 487 else \PHK\Tools\Util::http403Fail(); // Returns 'Forbidden' 488 } 489 490 // File exists ? 491 492 $uri=$this->uri($path); 493 494 if (($a=@stat($uri))===false) \PHK\Tools\Util::http404Fail(); 495 496 if (($a['mode'] & 0170000) == 040000) // Special case for directory 497 { 498 $file_path=null; 499 if ($last_slash) // Search a DirectoryIndex 500 { 501 foreach(array('index.htm', 'index.html', 'index.php') as $fname) 502 { 503 if (is_file($this->uri($path.'/'.$fname))) 504 { 505 $file_path=$path.'/'.$fname; 506 break; 507 } 508 } 509 if (is_null($file_path)) \PHK\Tools\Util::http404Fail(); // No Directory Index 510 } 511 else \PHK\Tools\Util::http301Redirect($path.'/'); 512 } 513 else $file_path=$path; 514 515 // Now, we return the string which will be used by the calling environment 516 // to execute the file if it is a PHP source, or to output its content 517 // with the correct mime type. Execution is disabled in webinfo mode 518 519 if ((!$webinfo) && ($this->isPHPSourcePath($file_path))) 520 { 521 return "require('".$this->uri($file_path)."');"; 522 } 523 else 524 { 525 return "\PHK\Mgr::instance('".$this->mnt."')->mimeHeader('$file_path');\n" 526 ."readfile('".$this->uri($file_path)."');"; 527 } 528 } 529 530 //--------------------------------- 531 /** 532 * Sends a mime header corresponding to a path 533 * 534 * Actually, we use only the file suffix (the path can correspond to an existing 535 * node or not). 536 * 537 * If the suffix does not correspond to anything we know, nothing is sent 538 * (defaults to text/html on Apache, don't know if it can change on another 539 * SAPI). 540 * 541 * @param string $path 542 * @return void 543 */ 544 545 public function mimeHeader($path) 546 { 547 $this->validate(); 548 if (!is_null($type=$this->mimeType($path))) header('Content-type: '.$type); 549 } 550 551 //--------- 552 /** 553 * Returns the mime-type corresponding to a given path, or null if the 554 * suffix does not correspond to anything we know 555 * 556 * Searches : 557 * 558 * 1. The 'mime-types' option 559 * 2. The built-in mime table 560 * 3. If the suffix contains 'php', sets the type to 'application/x-httpd-php' 561 * 562 * @param string $path 563 * @return string|null The mime type or null if file suffix is unknown 564 */ 565 566 public function mimeType($path) 567 { 568 $this->validate(); 569 $ext=\PHK\Tools\Util::fileSuffix($path); 570 571 if ((!is_null($mtab=$this->option('mime_types'))) && isset($mtab[$ext])) 572 return $mtab[$ext]; 573 574 if (isset(self::$mimeTable[$ext])) return self::$mimeTable[$ext]; 575 576 if (strpos($ext,'php')!==false) return 'application/x-httpd-php'; 577 578 return null; 579 } 580 581 //--------- 582 /** 583 * Should we consider this path as a PHP source file ? 584 * 585 * In order to be identified as PHP source, a path must be associated with Mime 586 * type 'application/x-httpd-php'. 587 * 588 * @param string $path 589 * @return boolean 590 */ 591 592 public function isPHPSourcePath($path) 593 { 594 $this->validate(); 595 return ($this->mimeType($path)==='application/x-httpd-php'); 596 } 597 598 //--------- 599 600 public function proxy() 601 { 602 $this->validate(); 603 return \PHK\Mgr::proxy($this->mnt); 604 } 605 606 //--------- 607 /** 608 * Checks the CRC of the PHK archive file 609 * 610 * Generates an exception if the check fails 611 * 612 * @return void 613 * @throws \Exception 614 */ 615 616 public function crcCheck() 617 { 618 $this->validate(); 619 $this->proxy()->crcCheck(); 620 } 621 622 //--------- 623 624 private function supportsPhpVersion() 625 { 626 $this->validate(); 627 if ((!is_null($minv=$this->option('min_php_version'))) 628 && (version_compare(PHP_VERSION,$minv) < 0)) 629 throw new \Exception("PHP minimum supported version: $minv (current is ".PHP_VERSION.")"); 630 631 if ((!is_null($maxv=$this->option('max_php_version'))) 632 && (version_compare(PHP_VERSION,$maxv) > 0)) 633 throw new \Exception("PHP maximum supported version: $maxv (current is ".PHP_VERSION.")"); 634 } 635 636 //----- 637 /** 638 * Is the PHK accelerator in use or not ? 639 * 640 * @return boolean 641 */ 642 643 public static function acceleratorIsPresent() 644 { 645 return false; 646 } 647 648 //----- 649 /** 650 * Returns a build-time information field or the whole array 651 * 652 * Unlike options, an unknown key throws an error 653 * 654 * @param string|null $name Field name 655 * @return array|string|null The field's content or null if it does not exist 656 */ 657 658 public function buildInfo($name=null) 659 { 660 $this->validate(); 661 if (is_null($name)) return $this->buildInfo; 662 663 if (!isset($this->buildInfo[$name])) 664 throw new \Exception($name.': unknown build info'); 665 666 return $this->buildInfo[$name]; 667 } 668 669 //--------------------------------- 670 671 public static function subpathURL($path) 672 { 673 return \PHK\Backend::subpathURL($path); 674 } 675 676 //--------------------------------- 677 // Get the sub-path from an URL. Because of the problems with CGI mode, we 678 // have to support 2 syntaxes : 679 // http://<site>/.../<phk_file><path> 680 // http://<site>/.../<phk_file>?_phk_path=<path> 681 682 public static function setSubpath() 683 { 684 $path=''; 685 686 if (isset($_REQUEST['_phk_path'])) $path=urldecode($_REQUEST['_phk_path']); 687 else 688 { 689 $path=isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : ''; 690 if ($path=='' && isset($_SERVER['ORIG_PATH_INFO'])) 691 $path=$_SERVER['ORIG_PATH_INFO']; 692 } 693 694 if (($path!='') && ($path{0}!='/')) $path='/'.$path; 695 696 return $path; 697 } 698 699 //---- 700 // Undocumented 701 702 private function backend() 703 { 704 $this->validate(); 705 if (is_null($this->backend)) $this->backend=new \PHK\Backend($this); 706 707 return $this->backend; 708 } 709 710 //-------------- 711 // Forward unknown method calls to the slow backend 712 713 public function __call($method,$args) 714 { 715 $this->validate(); 716 return \PHK\Tools\Util::callMethod($this->backend(),$method,$args); 717 } 718 719 //--------- 720 721 public static function prolog($file,&$cmd,&$ret) 722 { 723 # Do we run in CLI mode ? 724 725 if ($cli=(!\PHK\Tools\Util::envIsWeb())) 726 { 727 ini_set('display_errors',true); 728 ini_set('memory_limit','1024M'); // Only in CLI mode 729 } 730 731 \PHK\Mgr::checkPhpVersion(); //-- Check PHP version - if unsupported, no return 732 733 //----- 734 // Mount the PHK file (or get the mount point if previously mounted) 735 736 $mnt=\PHK\Mgr::mount($file); 737 $phk=\PHK\Mgr::instance($mnt); 738 739 //\PHK\Tools\Util::trace("Prolog mounted $file on $mnt");//TRACE 740 741 //----- 742 // Am I a main script ? 743 // When there are symbolic links in the path, get_included_files() returns 744 // 2 paths, the logical one first, and then the real one. 745 746 $tmp=get_included_files(); 747 $main=(($tmp[0]===$file) || (realpath($tmp[0]) === $file)); 748 749 if (!$main) // Not main script 750 { 751 if (!is_null($script=$phk->option('lib_run_script'))) 752 { require($phk->uri($script)); } 753 754 if ($phk->option('auto_umount')) 755 { 756 \PHK\Mgr::umount($mnt); 757 $ret=''; 758 } 759 else $ret=$mnt; 760 return; 761 } 762 763 //----------------- 764 // Main script - Dispatch 765 766 if ($cli) 767 { 768 if (($_SERVER['argc']>1) && ($_SERVER['argv'][1]!='') 769 && ($_SERVER['argv'][1]{0}=='@')) 770 { 771 $ret=$phk->builtinProlog($file); 772 return; 773 } 774 775 // Not a command: call cli_run 776 777 if (!is_null($run_path=$phk->option('cli_run_script'))) 778 { 779 $cmd="\$_phk_ret=require('".$phk->uri($run_path)."');"; 780 } 781 return; 782 } 783 else // HTTP mode 784 { 785 if (file_exists($file.'.webinfo')) // Slow path 786 { 787 \PHK\Tools\Util::runWebInfo($phk); 788 } 789 else 790 { 791 $cmd=$phk->webTunnel(); 792 } 793 } 794 } 795 796 //--- 797 } // End of class 798 //=========================================================================== 799 } // End of class_exists 800 //=========================================================================== 801 } // End of namespace 802 //=========================================================================== 803 ?>
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 |