[ 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 * This class creates a map file 29 * 30 * Usage: 31 * $map=new \Automap\Build\Creator(); 32 * ...[populate using the different methods]... 33 * $map->save(<path>); 34 * 35 * API status: Public 36 * Included in the PHK PHP runtime: No 37 * Implemented in the extension: No 38 *///========================================================================== 39 40 namespace Automap\Build { 41 42 if (!class_exists('Automap\Build\Creator',false)) 43 { 44 class Creator 45 { 46 const VERSION='3.0.0'; // Version set into the maps I produce 47 const MIN_RUNTIME_VERSION='3.0.0'; // Minimum version of runtime able to understand the maps I produce 48 49 //--------- 50 51 private $symbols=array(); // array($key => array('T' => <symbol type> 52 // , 'n' => <case-sensitive symbol name> 53 // , 't' => <target type>, 'p' => <target path>)) 54 private $options=array(); 55 56 private $php_file_ext=array('php','inc','hh'); 57 58 private $parser; // Must implement \Automap\Build\ParserInterface 59 60 //--------- 61 62 public function __construct($parser=null) 63 { 64 $this->setParser($parser); 65 } 66 67 //--------- 68 69 public function setParser($parser=null) 70 { 71 if (is_null($parser)) $parser=new Parser(); 72 $this->parser=$parser; 73 } 74 75 //--------- 76 77 public function option($opt) 78 { 79 return (isset($this->options[$opt]) ? $this->options[$opt] : null); 80 } 81 82 //--------- 83 84 public function setOption($option,$value) 85 { 86 \Phool\Display::trace("Setting option $option=$value"); 87 88 $this->options[$option]=$value; 89 } 90 91 //--------- 92 93 public function unsetOption($option) 94 { 95 \Phool\Display::trace("Unsetting option $option"); 96 97 if (isset($this->options[$option])) unset($this->options[$option]); 98 } 99 100 //--------- 101 /** 102 * Set the list of file suffixes recognized as PHP source scripts 103 * 104 * Default list is 'php, 'inc, 'hh'. 105 * 106 * @param array|string If array, replace the list, otherwise add a suffix to the list 107 * @return null 108 */ 109 110 public function setPhpFileExt($a) 111 { 112 if (is_array($a)) $this->php_file_ext=$a; 113 else $this->php_file_ext[]=$a; 114 } 115 116 //--------- 117 118 private function addEntry($va) 119 { 120 $key=\Automap\Map::key($va['T'],$va['n']); 121 122 // Filter namespace if filter specified 123 124 if (isset($va['f'])) 125 { 126 $ns_list=$va['f']; 127 if (is_string($ns_list)) $ns_list=array($ns_list); 128 $ns=\Automap\Map::nsKey($va['n']); 129 $ok=false; 130 foreach($ns_list as $item) 131 { 132 $item=trim($item,'\\'); 133 if ((($item=='')&&($ns==''))||($item!='')&&(strpos($ns.'\\',$item.'\\')===0)) 134 { 135 $ok=true; 136 break; 137 } 138 } 139 if (!$ok) 140 { 141 \Phool\Display::debug("$key rejected by namespace filter"); 142 return; 143 } 144 } 145 146 // Add symbol to map if no conflict 147 148 \Phool\Display::debug("Adding symbol (key=<$key>, name=".$va['n'] 149 .", target=".$va['p'].' ('.$va['t'].')'); 150 151 if (isset($this->symbols[$key])) 152 { 153 $entry=$this->symbols[$key]; 154 // If same target, it's OK 155 if (($entry['t']!=$va['t'])||($entry['p']!=$va['p'])) 156 { 157 echo "** Warning: Symbol multiply defined: " 158 .\Automap\Mgr::typeToString($va['T']) 159 .' '.$va['n']."\n Previous location (kept): " 160 .\Automap\Mgr::typeToString($entry['t']) 161 .' '.$entry['p']."\n New location (discarded): " 162 .\Automap\Mgr::typeToString($va['t']) 163 .' '.$va['p']."\n"; 164 } 165 } 166 else $this->symbols[$key]=$va; 167 } 168 169 //--------- 170 171 private function addTSEntry($stype,$sname,$va) 172 { 173 $va['T']=$stype; 174 $va['n']=$sname; 175 $this->addEntry($va); 176 } 177 178 //--------- 179 180 public function symbolCount() 181 { 182 return count($this->symbols); 183 } 184 185 //--------- 186 // Build an array containing only target information 187 188 private static function mkVarray($ftype,$fpath,$ns_filter=null) 189 { 190 $a=array('t' => $ftype, 'p' => $fpath); 191 if (!is_null($ns_filter)) $a['f']=$ns_filter; 192 return $a; 193 } 194 195 //--------- 196 197 public function addSymbol($stype,$sname,$ftype,$fpath) 198 { 199 $va=self::mkVarray($ftype,$fpath); 200 $this->addTSEntry($stype,$sname,$va); 201 } 202 203 //--------- 204 // Remove the entries matching a given target 205 206 private function unregisterTarget($va) 207 { 208 $type=$va['t']; 209 $path=$va['p']; 210 \Phool\Display::debug("Unregistering path (type=$type, path=$path)"); 211 212 foreach(array_keys($this->symbols) as $key) 213 { 214 if (($this->symbols[$key]['t']===$type)&&($this->symbols[$key]['p']===$path)) 215 { 216 \Phool\Display::debug("Removing $key from symbol table"); 217 unset($this->symbols[$key]); 218 } 219 } 220 } 221 222 //--------- 223 // Using adler32 as it is supposed to be the fastest algo. That's more than 224 // enough for a CRC check. 225 // Symbols are supposed to be normalized (no leading/trailing '\'). 226 227 public function serialize() 228 { 229 //-- Store symbols in namespace slots 230 231 $slots=array(); 232 foreach($this->symbols as $key => $va) 233 { 234 $target=$va['t'].$va['p']; 235 $ns=\Automap\Map::nsKey($va['n']); 236 if (!array_key_exists($ns,$slots)) $slots[$ns]=array(); 237 $slots[$ns][$key]=$target; 238 } 239 240 //-- Serialize 241 242 foreach(array_keys($slots) as $ns) 243 { 244 $slots[$ns]=serialize($slots[$ns]); 245 } 246 247 $data=serialize(array('map' => $slots, 'options' => $this->options)); 248 249 //-- Dump to file 250 251 $buf=\Automap\Map::MAGIC 252 .str_pad(self::MIN_RUNTIME_VERSION,12) 253 .str_pad(self::VERSION,12) 254 .str_pad(strlen($data)+70,8) 255 .'00000000' 256 .str_pad(count($this->symbols),8) 257 .str_pad(strlen($data),8) 258 .$data; 259 260 return substr_replace($buf,hash('adler32',$buf),46,8); // Insert CRC 261 } 262 263 //--------- 264 265 public function save($path) 266 { 267 if (is_null($path)) throw new \Exception('No path provided'); 268 269 $data=$this->serialize(); 270 271 \Phool\Display::trace("$path: Writing map file"); 272 \Phool\File::atomicWrite($path,$data); 273 } 274 275 //--------- 276 // Register an extension in current map. 277 // $file=extension file (basename) 278 279 public function registerExtensionFile($file) 280 { 281 \Phool\Display::trace("Registering extension : $file"); 282 283 $va=self::mkVarray(\Automap\Mgr::F_EXTENSION,$file); 284 $this->unregisterTarget($va); 285 286 foreach($this->parser->parseExtension($file) as $sym) 287 { 288 $this->addTSEntry($sym['type'],$sym['name'],$va); 289 } 290 } 291 292 //--------- 293 // Register every extension files in the extension directory 294 // We do several passes, as there are dependencies between extensions which 295 // must be loaded in a given order. We stop when a pass cannot load any file. 296 297 public function registerExtensionDir() 298 { 299 $ext_dir=ini_get('extension_dir'); 300 \Phool\Display::trace("Scanning extensions directory ($ext_dir)\n"); 301 302 //-- Multiple passes because of possible dependencies 303 //-- Loop until everything is loaded or we cannot load anything more 304 305 $f_to_load=array(); 306 $pattern='/\.'.PHP_SHLIB_SUFFIX.'$/'; 307 foreach(scandir($ext_dir) as $ext_file) 308 { 309 if (is_dir($ext_dir.DIRECTORY_SEPARATOR.$ext_file)) continue; 310 if (preg_match($pattern,$ext_file)) $f_to_load[]=$ext_file; 311 } 312 313 while(true) 314 { 315 $f_failed=array(); 316 foreach($f_to_load as $key => $ext_file) 317 { 318 try { $this->registerExtensionFile($ext_file); } 319 catch (\Exception $e) { $f_failed[]=$ext_file; } 320 } 321 //-- If we could load everything or if we didn't load anything, break 322 if ((count($f_failed)==0)||(count($f_failed)==count($f_to_load))) break; 323 $f_to_load=$f_failed; 324 } 325 326 if (count($f_failed)) 327 { 328 foreach($f_failed as $file) 329 \Phool\Display::warning("$file: This extension was not registered (load failed)"); 330 } 331 } 332 333 //--------------------------------- 334 /** 335 * Normalize a destination path 336 * 337 * 1. Replace backslashes with forward slashes. 338 * 2. Remove trailing slashes 339 * 340 * @param string $rpath the path to normalize 341 * @return string the normalized path 342 */ 343 344 private static function normalizePath($path) 345 { 346 $path=rtrim(str_replace('\\','/',$path),'/'); 347 if ($path=='') $path='/'; 348 return $path; 349 } 350 351 //--------- 352 353 public function registerScriptFile($fpath,$rpath,$ns_filter=null) 354 { 355 \Phool\Display::trace("Registering script $fpath as $rpath"); 356 357 // Force relative path 358 359 $va=self::mkVarray(\Automap\Mgr::F_SCRIPT,self::normalizePath($rpath),$ns_filter); 360 $this->unregisterTarget($va); 361 362 foreach($this->parser->parseScriptFile($fpath) as $sym) 363 { 364 $this->addTSEntry($sym['type'],$sym['name'],$va); 365 } 366 } 367 368 //--------- 369 /** 370 * Recursively scan a path and records symbols 371 * 372 * Scan retains PHP source files and phk packages only (based on file suffix) 373 * 374 * Only dirs and regular files are considered. Other types are ignored. 375 * 376 * @param string $fpath Path to register 377 * @param string $rpath Path to register to in map for $fpath 378 * @param string|array|null $ns_filter 379 * List of authorized namespaces (empty string means no namespace) 380 * If null, no filtering. 381 * @param string|null $file_pattern 382 * File path preg pattern (File paths not matching this pattern are ignored) 383 */ 384 385 public function registerPath($fpath,$rpath,$ns_filter=null,$file_pattern=null) 386 { 387 \Phool\Display::trace("Registering path <$fpath> as <$rpath>"); 388 389 switch($type=filetype($fpath)) 390 { 391 case 'dir': 392 foreach(\Phool\File::scandir($fpath) as $entry) 393 { 394 $this->registerPath($fpath.'/'.$entry,$rpath.'/'.$entry,$ns_filter); 395 } 396 break; 397 398 case 'file': 399 if ((!is_null($file_pattern)) && (!preg_match($file_pattern, $fpath))) return; 400 $suffix=strtolower(\Phool\File::fileSuffix($fpath)); 401 if ($suffix=='phk') 402 $this->registerPhkPkg($fpath,$rpath); 403 elseif (array_search($suffix,$this->php_file_ext)!==false) 404 $this->registerScriptFile($fpath,$rpath,$ns_filter); 405 else 406 \Phool\Display::trace("Ignoring file $fpath (not a PHP script)"); 407 break; 408 } 409 } 410 411 //--------- 412 413 public function readMapFile($fpath) 414 { 415 \Phool\Display::trace("Reading map file ($fpath)"); 416 417 $map=new \Automap\Map($fpath); 418 $this->options=$map->options(); 419 $this->symbols=array(); 420 $this->mergeMapSymbols($map); 421 } 422 423 //--------- 424 /** 425 * Merge an existing map file into the current map 426 * 427 * Import symbols only. Options are ignored (including base path). 428 * 429 * @param string $fpath Path of the map to merge (input) 430 * @param Relative path to prepend to map target paths 431 * @return null 432 */ 433 434 public function mergeMapFile($fpath,$rpath) 435 { 436 \Phool\Display::debug("Merging map file from $fpath (rpath=$rpath)"); 437 438 $map=new \Automap\Map($fpath); 439 $this->mergeMapSymbols($map,$rpath); 440 } 441 442 //--------- 443 444 public function mergeMapSymbols($map,$rpath='.') 445 { 446 foreach($map->symbols() as $va) 447 { 448 $va['rpath']=\Phool\File::combinePath($rpath,$va['rpath']); 449 $this->addEntry($va); 450 } 451 } 452 453 //--------- 454 // Register a PHK package 455 456 public function registerPhkPkg($fpath,$rpath) 457 { 458 \Phool\Display::trace("Registering PHK package $fpath as $rpath"); 459 460 $rpath=self::normalizePath($rpath); 461 \Phool\Display::debug("Registering PHK package (path=$fpath, rpath=$rpath)"); 462 $va=self::mkVarray(\Automap\Mgr::F_PACKAGE,$rpath); 463 $this->unregisterTarget($va); 464 465 $mnt=\PHK\Mgr::mount($fpath,\PHK::NO_MOUNT_SCRIPT); 466 $pkg=\PHK\Mgr::instance($mnt); 467 $id=$pkg->automapID(); 468 if ($id) // If package has an automap 469 { 470 foreach(\Automap\Mgr::map($id)->symbols() as $sym) 471 $this->addTSEntry($sym['stype'],$sym['symbol'],$va); 472 } 473 } 474 475 //--------- 476 477 public function import($path=null) 478 { 479 if (is_null($path)) $path="php://stdin"; 480 481 \Phool\Display::trace("Importing map from $path"); 482 483 $fp=fopen($path,'r'); 484 if (!$fp) throw new \Exception("$path: Cannot open for reading"); 485 486 while(($line=fgets($fp))!==false) 487 { 488 if (($line=trim($line))==='') continue; 489 list($stype,$sname,$ftype,$fname)=explode('|',$line); 490 $va=self::mkVarray($ftype,$fname); 491 $this->addTSEntry($stype,$sname,$va); 492 } 493 fclose($fp); 494 } 495 496 //--- 497 } // End of class 498 //=========================================================================== 499 } // End of class_exists 500 //=========================================================================== 501 } // End of namespace 502 //=========================================================================== 503 ?>
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 |