[ Index ] |
PHP Cross Reference of Automap |
[Summary view] [Print] [Text view]
1 <?php 2 //============================================================================= 3 // 4 // Copyright Francois Laupretre <automap@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 <automap@tekwire.net> 21 * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0 22 * @category Automap 23 * @package Automap 24 *///========================================================================== 25 26 //============================================================================= 27 /** 28 * A map instance (created from an existing map file) 29 * 30 * When the PECL extension is not present, this class is instantiated when the 31 * map is loaded, and it is used by the autoloader. 32 * 33 * When the extension is present, this class is instantiated only when explicitely 34 * referenced and is not used by the autoloader. 35 * 36 * API status: Public 37 * Included in the PHK PHP runtime: Yes 38 * Implemented in the extension: No 39 *///========================================================================== 40 41 namespace Automap { 42 43 if (!class_exists('Automap\Map',false)) 44 { 45 class Map 46 { 47 /** Runtime API version */ 48 49 const VERSION='3.0.0'; 50 51 /** We cannot load maps older than this version */ 52 53 const MIN_MAP_VERSION='3.0.0'; 54 55 /** Map files start with this string */ 56 57 const MAGIC="AUTOMAP M\024\x8\6\3";// Magic value for map files (offset 0) 58 59 //-------------------------- 60 /** The absolute path of the map file */ 61 62 private $path; 63 64 /** @var array(<key> => <target>) The symbol table (filled from slots) */ 65 66 private $symbols; 67 68 /** @var array(<ns> => <slot data>) The symbols not loaded in the symbol table yet */ 69 70 private $slots; 71 72 /** @var integer Symbol count of this map */ 73 74 private $symcount; 75 76 /** @var array(<name> => <value>) The map options */ 77 78 private $options; 79 80 /** @var string The version of \Automap\Build\Creator that created the map file */ 81 82 private $version; 83 84 /** @var string The minimum runtime version needed to understand the map file */ 85 86 private $minVersion; 87 88 /** @var integer Load flags */ 89 90 private $flags; 91 92 /** @var string Absolute base path */ 93 94 private $basePath; 95 96 //----- 97 /** 98 * Construct a map object from an existing map file (real or virtual) 99 * 100 * @param string $path Path of the map file to read 101 * @param integer $flags Combination of Automap load flags (@see Automap) 102 * @param string Reserved for internal use (PHK). Never set this. 103 */ 104 105 public function __construct($path,$flags=0,$_bp=null) 106 { 107 $this->path=self::mkAbsolutePath($path); 108 $this->flags=$flags; 109 110 try 111 { 112 //-- Get file content 113 114 if (($buf=@file_get_contents($this->path))===false) 115 throw new \Exception('Cannot read map file'); 116 $bufsize=strlen($buf); 117 if ($bufsize<70) throw new \Exception("Short file (size=$bufsize)"); 118 119 //-- Check magic 120 121 if (substr($buf,0,14)!=self::MAGIC) throw new \Exception('Bad Magic'); 122 123 //-- Check min runtime version required by map 124 125 $this->minVersion=trim(substr($buf,14,12)); 126 if (version_compare($this->minVersion,self::VERSION) > 0) 127 throw new \Exception($this->path.': Cannot understand this map.'. 128 ' Requires at least Automap version '.$this->minVersion); 129 130 //-- Check if the map format is not too old 131 132 $this->version=trim(substr($buf,26,12)); 133 if (strlen($this->version)==0) 134 throw new \Exception('Invalid empty map version'); 135 if (version_compare($this->version,self::MIN_MAP_VERSION) < 0) 136 throw new \Exception('Cannot understand this map. Format too old.'); 137 $map_major_version=$this->version{0}; 138 139 //-- Check file size 140 141 if (strlen($buf)!=($sz=(int)substr($buf,38,8))) 142 throw new \Exception('Invalid file size. '.$sz.' should be '.strlen($buf)); 143 144 //-- Check CRC 145 146 if (!($flags & Mgr::CRC_CHECK)) 147 { 148 $crc=substr($buf,46,8); 149 $buf=substr_replace($buf,'00000000',46,8); 150 if ($crc!==hash('adler32',$buf)) throw new \Exception('CRC error'); 151 } 152 153 //-- Symbol count 154 155 $this->symcount=(int)substr($buf,54,8); 156 157 //-- Read data 158 159 $dsize=(int)substr($buf,62,8); 160 if (($buf=unserialize(substr($buf,70,$dsize)))===false) 161 throw new \Exception('Cannot unserialize data from map file'); 162 if (!is_array($buf)) 163 throw new \Exception('Map file should contain an array'); 164 if (!array_key_exists('options',$buf)) throw new \Exception('No options array'); 165 if (!is_array($this->options=$buf['options'])) 166 throw new \Exception('Options should be an array'); 167 if (!array_key_exists('map',$buf)) throw new \Exception('No symbol table'); 168 if (!is_array($this->slots=$buf['map'])) 169 throw new \Exception('Slot table should contain an array'); 170 $this->symbols=array(); 171 172 //-- Compute base path 173 174 if (!is_null($_bp)) $this->basePath=$_bp; 175 else $this->basePath=self::combinePath(dirname($this->path) 176 ,$this->option('basePath'),true); 177 178 } 179 catch (\Exception $e) 180 { 181 $this->symbols=array(); // No retry later 182 throw new \Exception($path.': Cannot load map - '.$e->getMessage()); 183 } 184 } 185 186 //--------- 187 // Check if a given file is a map file 188 189 public function isMapFile($path) 190 { 191 return (substr(file_get_contents($path),0,strlen(self::MAGIC))===self::MAGIC); 192 } 193 194 //--------- 195 /** 196 * Combines a type and a symbol in a 'key' 197 * 198 * Starting with version 3.0, Automap is fully case-sensitive. This allows for 199 * higher performance and cleaner code. 200 * 201 * Do not use this method (reserved for use by other Automap classes) 202 * 203 * @param string $type one of the 'T_' constants 204 * @param string $name The symbol value (case sensitive) 205 * @return string Symbol key 206 */ 207 208 public static function key($type,$name) 209 { 210 return $type.trim($name,'\\'); 211 } 212 213 //--------- 214 /** 215 * Load a slot into the symbol table 216 * 217 * @param string $ns Normalized namespace. Must correspond to an existing slot (no check) 218 * @return null 219 */ 220 221 private function loadSlot($ns) 222 { 223 $this->symbols=array_merge($this->symbols,unserialize($this->slots[$ns])); 224 unset($this->slots[$ns]); 225 } 226 227 //--------- 228 /** 229 * Extracts the namespace from a symbol name 230 * 231 * The returned value has no leading/trailing separator. 232 * 233 * Do not use: access reserved for Automap classes 234 * 235 * @param string $name The symbol value (case sensitive) 236 * @return string Namespace. If no namespace, returns an empty string. 237 */ 238 239 public static function nsKey($name) 240 { 241 $name=trim($name,'\\'); 242 $pos=strrpos($name,'\\'); 243 if ($pos!==false) return substr($name,0,$pos); 244 else return ''; 245 } 246 247 //--- 248 // These utility functions return 'read-only' properties 249 250 public function path() { return $this->path; } 251 public function flags() { return $this->flags; } 252 public function options() { return $this->options; } 253 public function version() { return $this->version; } 254 public function minVersion() { return $this->minVersion; } 255 public function basePath() { return $this->basePath; } 256 257 //--- 258 259 public function option($opt) 260 { 261 return (isset($this->options[$opt]) ? $this->options[$opt] : null); 262 } 263 264 //--- 265 266 public function symbolCount() 267 { 268 return $this->symcount; 269 } 270 271 //--- 272 // The entry we are exporting must be in the symbol table (no check) 273 // We need to use combinePath() because the registered path (rpath) can be absolute 274 275 private function exportEntry($key) 276 { 277 $entry=$this->symbols[$key]; 278 279 $a=array( 280 'stype' => $key{0}, 281 'symbol' => substr($key,1), 282 'ptype' => $entry{0}, 283 'rpath' => substr($entry,1) 284 ); 285 286 $a['path']=(($a['ptype']===Mgr::F_EXTENSION) ? $a['rpath'] 287 : self::combinePath($this->basePath,$a['rpath'])); 288 289 return $a; 290 } 291 292 //--- 293 294 public function getSymbol($type,$symbol) 295 { 296 $key=self::key($type,$symbol); 297 if (!($found=array_key_exists($key,$this->symbols))) 298 { 299 if (count($this->slots)) 300 { 301 $ns=self::nsKey($symbol); 302 if (array_key_exists($ns,$this->slots)) $this->loadSlot($ns); 303 $found=array_key_exists($key,$this->symbols); 304 } 305 } 306 return ($found ? $this->exportEntry($key) : false); 307 } 308 309 //------- 310 /** 311 * Try to resolve a symbol using this map 312 * 313 * For performance reasons, we trust the map and don't check if the symbol is 314 * defined after loading the script/extension/package. 315 * 316 * @param string $type One of the \Automap\Mgr::T_xxx symbol types 317 * @param string Symbol name including namespace (no leading '\') 318 * @param integer $id Used to return the ID of the map where the symbol was found 319 * @return exported entry if found, false if not found 320 */ 321 322 public function resolve($type,$name,&$id) 323 { 324 if (($this->flags & Mgr::NO_AUTOLOAD) 325 || (($entry=$this->getSymbol($type,$name))===false)) return false; 326 327 //-- Found 328 329 $path=$entry['path']; // Absolute path 330 switch($entry['ptype']) 331 { 332 case Mgr::F_EXTENSION: 333 if (!dl($path)) return false; 334 break; 335 336 case Mgr::F_SCRIPT: 337 //echo("Loading script file : $path\n");//TRACE 338 { require($path); } 339 break; 340 341 case Mgr::F_PACKAGE: 342 // Remove E_NOTICE messages if the test script is a package - workaround 343 // to PHP bug #39903 ('__COMPILER_HALT_OFFSET__ already defined') 344 // In case of embedded packages and maps, the returned ID corresponds to 345 // the map where the symbol was finally found. 346 347 error_reporting(($errlevel=error_reporting()) & ~E_NOTICE); 348 $mnt=require($path); 349 error_reporting($errlevel); 350 $pkg=\PHK\_Mgr::instance($mnt); 351 $id=$pkg->automapID(); 352 return Mgr::map($id)->resolve($type,$name,$id); 353 break; 354 355 default: 356 throw new \Exception('<'.$entry['ptype'].'>: Unknown target type'); 357 } 358 return $entry; 359 } 360 361 //--- 362 363 public function symbols() 364 { 365 /* First, load every remaining slot */ 366 367 foreach(array_keys($this->slots) as $ns) $this->loadSlot($ns); 368 369 /* Then, convert every entry to the export format */ 370 371 $ret=array(); 372 foreach(array_keys($this->symbols) as $key) $ret[]=$this->exportEntry($key); 373 374 return $ret; 375 } 376 377 //--- 378 // Proxy to \Automap\Tools\Display::show() 379 380 public function show($format=null,$subfile_to_url_function=null) 381 { 382 return Tools\Display::show($this,$format,$subfile_to_url_function); 383 } 384 385 //--- 386 // Proxy to \Automap\Tools\Check::check() 387 388 public function check() 389 { 390 return Tools\Check::check($this); 391 } 392 393 //--- 394 395 public function export($path=null) 396 { 397 if (is_null($path)) $path="php://stdout"; 398 $fp=fopen($path,'w'); 399 if (!$fp) throw new \Exception("$path: Cannot open for writing"); 400 401 foreach($this->symbols() as $s) 402 { 403 fwrite($fp,$s['stype'].'|'.$s['symbol'].'|'.$s['ptype'].'|'.$s['rpath']."\n"); 404 } 405 406 fclose($fp); 407 } 408 409 //--------------------------------- 410 /** 411 * Transmits map elements to the PECL extension 412 * 413 * Reserved for internal use 414 * 415 * The first time a given map file is loaded, it is read by Automap\Map and 416 * transmitted to the extension. On subsequent requests, it is retrieved from 417 * persistent memory. This allows to code complex features in PHP and maintain 418 * the code in a single location without impacting performance. 419 * 420 * @param string $version The version of data to transmit (reserved for future use) 421 * @return array 422 */ 423 424 public function _peclGetMap($version) 425 { 426 $st=array(); 427 foreach($this->symbols() as $s) 428 { 429 $st[]=array($s['stype'],$s['symbol'],$s['ptype'],$s['path']); 430 } 431 432 return $st; 433 } 434 435 //============ Utilities (taken from external libs) ============ 436 // We need to duplicate these methods here because this class is included in the 437 // PHK PHP runtime, which does not include the \Phool\xxx classes. 438 439 //----- Taken from \Phool\File 440 /** 441 * Combines a base path with another path 442 * 443 * The base path can be relative or absolute. 444 * 445 * The 2nd path can also be relative or absolute. If absolute, it is returned 446 * as-is. If it is a relative path, it is combined to the base path. 447 * 448 * Uses '/' as separator (to be compatible with stream-wrapper URIs). 449 * 450 * @param string $base The base path 451 * @param string|null $path The path to combine 452 * @param bool $separ true: add trailing sep, false: remove it 453 * @return string The resulting path 454 */ 455 456 private static function combinePath($base,$path,$separ=false) 457 { 458 if (($base=='.') || ($base=='') || self::isAbsolutePath($path)) 459 $res=$path; 460 elseif (($path=='.') || is_null($path)) 461 $res=$base; 462 else //-- Relative path : combine it to base 463 $res=rtrim($base,'/\\').'/'.$path; 464 465 return self::trailingSepar($res,$separ); 466 } 467 468 /** 469 * Adds or removes a trailing separator in a path 470 * 471 * @param string $path Input 472 * @param bool $flag true: add trailing sep, false: remove it 473 * @return bool The result path 474 */ 475 476 private static function trailingSepar($path, $separ) 477 { 478 $path=rtrim($path,'/\\'); 479 if ($path=='') return '/'; 480 if ($separ) $path=$path.'/'; 481 return $path; 482 } 483 484 /** 485 * Determines if a given path is absolute or relative 486 * 487 * @param string $path The path to check 488 * @return bool True if the path is absolute, false if relative 489 */ 490 491 private static function isAbsolutePath($path) 492 { 493 return ((strpos($path,':')!==false) 494 ||(strpos($path,'/')===0) 495 ||(strpos($path,'\\')===0)); 496 } 497 498 /** 499 * Build an absolute path from a given (absolute or relative) path 500 * 501 * If the input path is relative, it is combined with the current working 502 * directory. 503 * 504 * @param string $path The path to make absolute 505 * @param bool $separ True if the resulting path must contain a trailing separator 506 * @return string The resulting absolute path 507 */ 508 509 private static function mkAbsolutePath($path,$separ=false) 510 { 511 if (!self::isAbsolutePath($path)) $path=self::combinePath(getcwd(),$path); 512 return self::trailingSepar($path,$separ); 513 } 514 515 //--- 516 } // End of class 517 //=========================================================================== 518 } // End of class_exists 519 //=========================================================================== 520 } // End of namespace 521 //=========================================================================== 522 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Jun 4 18:32:29 2015 | Cross-referenced by PHPXref 0.7.1 |