[ 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\Virtual { 27 28 if (!class_exists('PHK\Virtual\Tree',false)) 29 { 30 //============================================================================ 31 /** 32 * A virtual tree 33 * 34 * A virtual tree contains virtual nodes (files and directories) 35 * 36 * API status: Private 37 * Included in the PHK PHP runtime: Yes 38 * Implemented in the extension: No 39 *///========================================================================== 40 41 class Tree 42 { 43 44 public $fspace; // Associated filespace 45 46 private $edata; // Exported data. Always contains a key for every node, even 47 // in creator mode. 48 49 private $nodes; // Tree nodes (key=path, value=Node object). Contains 50 // only the unserialized nodes. In creator mode, contains 51 // every node. 52 53 private static $char_to_class=array( 'D' => 'Dir', 'F' => 'File'); 54 55 //--- 56 // Create a tree from the edata stored in the PHK archive 57 58 public static function createFromEdata($serial_edata,\PHK\PkgFileSpace $fspace) 59 { 60 $tree=new self($fspace); 61 62 $tree->edata=unserialize($serial_edata); 63 64 return $tree; 65 } 66 67 //--- 68 69 public function pathList() 70 { 71 return array_keys($this->edata); 72 } 73 74 //--- 75 76 public function pathExists($rpath) 77 { 78 return array_key_exists($rpath,$this->edata); 79 } 80 81 //--- 82 83 public function count() 84 { 85 return count($this->edata); 86 } 87 88 //--- 89 90 public function walk($method) 91 { 92 $args=func_get_args(); 93 array_shift($args); 94 95 foreach($this->pathList() as $path) 96 { 97 $node=$this->rlookup($path); 98 call_user_func_array(array($node,$method),$args); 99 } 100 } 101 102 //--- 103 // Reduce the path to a canonical path - suppress '..' and '.' components 104 // Root='' 105 // Non-root: /xxx[/yyy...] 106 107 private function realpath($path) 108 { 109 $a=explode('/',trim($path,'/')); 110 $ra=array(); 111 foreach($a as $comp) 112 { 113 switch($comp) 114 { 115 case '': 116 case '.': 117 break; 118 case '..': 119 if (count($ra)) array_pop($ra); 120 break; 121 default: 122 $ra[]=$comp; 123 } 124 } 125 if (!count($ra)) return ''; 126 return '/'.implode('/',$ra); 127 } 128 129 //--- 130 131 public function lookup($path,$exception_flag=true) 132 { 133 return $this->rlookup(self::realpath($path),$exception_flag); 134 } 135 136 //--- 137 // Lookup without path canonicalization - faster if self::realpath() has 138 // already been called 139 140 private function rlookup($path,$exception_flag=true) 141 { 142 if (array_key_exists($path,$this->edata)) 143 { 144 if (!array_key_exists($path,$this->nodes)) 145 { 146 $edata=$this->edata[$path]; 147 $class=__NAMESPACE__.'\\'.self::$char_to_class[$edata{0}]; 148 $node=$this->nodes[$path]=new $class($path,$this); 149 $node->import(substr($edata,1)); 150 } 151 return $this->nodes[$path]; 152 } 153 154 //echo "Lookup failed : <$path> <$rpath>\n";//TRACE 155 //print_r(array_keys($this->nodes));//TRACE 156 157 if ($exception_flag) throw new \Exception($path.': path not found'); 158 else return null; 159 } 160 161 //--- 162 163 public function lookupFile($path,$exception_flag=true) 164 { 165 $f=$this->lookup($path,$exception_flag); 166 167 if ((!is_null($f)) && (!($f instanceof File))) 168 { 169 if ($exception_flag) throw new \Exception($path.': No such file'); 170 else return null; 171 } 172 173 return $f; 174 } 175 176 //--- 177 178 public function displayHeader($html) 179 { 180 if ($html) echo '<table border=1 bordercolor="#BBBBBB" cellpadding=3 ' 181 .'cellspacing=0 style="border-collapse: collapse"><tr><th>T</th>' 182 .'<th>Name</th><th>Size</th><th>Flags</th></tr>'; 183 } 184 185 //--- 186 187 public function displayFooter($html) 188 { 189 if ($html) echo '</table>'; 190 } 191 192 //--- 193 // $link = wether we display an hyperlink on file names (in HTML mode) 194 195 public function display($link) 196 { 197 $html=\PHK\Tools\Util::envIsWeb(); 198 199 $this->displayHeader($html); 200 $this->walk('display',$html,$link); 201 $this->displayFooter($html); 202 } 203 204 //--- 205 206 public function displayPackages() 207 { 208 $html=\PHK\Tools\Util::envIsWeb(); 209 210 ob_start(); 211 $this->walk('displayPackage',$html); 212 $data=ob_get_clean(); 213 214 if ($data!=='') 215 { 216 $this->displayHeader($html); 217 $this->walk('displayPackage',$html); 218 $this->displayFooter($html); 219 } 220 } 221 222 //--- 223 224 public function dump($base) 225 { 226 $this->walk('dump',$base); 227 } 228 229 //--- 230 // Same as dirname() function except: 231 // - Always use '/' as separator 232 // - Returns '' for 1st level paths ('/xxx') 233 234 public static function dirBaseName($path) 235 { 236 $dir=preg_replace(',/[^/]*$,','',$path); 237 $base=preg_replace(',^.*/,','',$path); 238 return array($dir,$base); 239 } 240 241 //--- 242 // called from createEmpty() or createFromEdata() only => private 243 244 private function __construct($fspace) 245 { 246 $this->fspace=$fspace; 247 $this->nodes=array(); 248 } 249 250 // <CREATOR> //--------------- 251 252 // Check for a list of forbidden chars in node names. Especially important for 253 // '#*' which can create conflicts in mount points (for subpackages), and ';' 254 // which is used as separator when exporting the list of dir children. 255 256 public function addNode($path,$node) 257 { 258 $path=self::realpath($path); 259 260 if (strpbrk($path,'#*?!&~"|`\^@[]={}$;,<>')!==false) 261 throw new \Exception("$path: Invalid characters in path"); 262 263 if ($path != '') 264 { 265 list($dir,$basename)=self::dirBaseName($path); 266 267 $dirnode=$this->rlookup($dir,false); 268 if (is_null($dirnode)) $dirnode=$this->mkdir($dir); 269 270 if (!($dirnode instanceof Dir)) 271 throw new \Exception("Cannot add node over a non-directory node ($dir)"); 272 273 $dirnode->addChild($basename); 274 } 275 276 // Add the node 277 278 $this->edata[$path]=null; 279 $this->nodes[$path]=$node; 280 } 281 282 //--- 283 // Create an empty tree 284 285 public static function createEmpty() 286 { 287 $tree=new self(null); 288 $tree->addNode('',new Dir('',$tree)); 289 290 return $tree; 291 } 292 293 //--- 294 295 public function export(\PHK\Build\Creator $phk,$map=null) 296 { 297 $edata=array(); 298 $stacker=new \PHK\Build\DataStacker(); 299 300 foreach($this->nodes as $path => $node) 301 { 302 $edata[$path]=array_search(substr(get_class($node),strlen(__NAMESPACE__)+1),self::$char_to_class) 303 .$node->export($phk,$stacker,$map); 304 } 305 ksort($edata); // To display files in the right order 306 307 return array(serialize($edata),$stacker->data); 308 } 309 310 //--- 311 // target: absolute target path. '&' is replaced by source basename 312 // sapath: Absolute source path 313 // modifiers: array received from \PHK\Build\PSF\CmdOptions 314 315 public function mergeIntoFileTree($target,$sapath,$modifiers) 316 { 317 if (!file_exists($sapath)) 318 throw new \Exception($sapath.': Path not found'); 319 320 $target=self::realpath(str_replace('&',basename($sapath),$target)); 321 322 switch($type=filetype($sapath)) 323 { 324 case 'file': 325 if ($target=='') throw new \Exception('Cannot replace root dir with a file'); 326 $this->remove($target); 327 $this->mkfile($target,\PHK\Tools\Util::readFile($sapath),$modifiers); 328 break; 329 330 case 'dir': 331 foreach(\PHK\Tools\Util::scandir($sapath) as $subname) 332 { 333 $this->mergeIntoFileTree($target.'/'.$subname,$sapath.'/'.$subname 334 ,$modifiers); 335 } 336 break; 337 338 default: 339 \Phool\Display::info("$sapath : Unsupported file type ($type) - Ignored"); 340 } 341 } 342 343 //--- 344 345 private function getSubtree($path) 346 { 347 $rpath=self::realpath($path); 348 349 if ($rpath=='') return $this->pathList(); 350 351 $result=array(); 352 $prefix=$rpath.'/'; 353 $len=strlen($prefix); 354 foreach($this->pathList() as $p) 355 { 356 if (($p==$rpath)||((strlen($p)>=$len)&&(substr($p,0,$len)==$prefix))) 357 $result[]=$p; 358 } 359 return $result; 360 } 361 362 //--- 363 364 public function modify($path,$modifiers) 365 { 366 $path=self::realpath($path); 367 368 foreach ($this->getSubtree($path) as $subpath) 369 { 370 $this->lookup($subpath)->modify($modifiers); 371 } 372 } 373 374 //--- 375 // If parent dir does not exist, addNode() will call us back to create it, 376 // and it goes on recursively until the root node is reached. 377 378 public function mkdir($path,$modifiers=array()) 379 { 380 $rpath=self::realpath($path); 381 382 if (is_null($node=$this->rlookup($rpath,false))) // If node does not exist 383 { 384 $node=new Dir($path,$this); 385 $node->modify($modifiers); 386 $this->addNode($path,$node); 387 } 388 else // If node already exists, check that it is a directory 389 { 390 if (($type=$node->type())!='dir') 391 throw new \Exception("mkdir: $path is already a $type"); 392 } 393 return $node; 394 } 395 396 //--- 397 398 public function mkfile($path,$data,$modifiers=array()) 399 { 400 $rpath=self::realpath($path); 401 402 $node=new File($rpath,$this); 403 $node->setData($data); 404 $node->modify($modifiers); 405 406 $this->addNode($rpath,$node); 407 408 return $node; 409 } 410 411 //--- 412 413 public function remove($path) 414 { 415 $rpath=self::realpath($path); 416 if ($rpath=='') throw new \Exception('Cannot remove root directory'); 417 418 if (is_null($this->rlookup($rpath,false))) return; // Path does not exist 419 420 foreach($this->getSubtree($rpath) as $p) 421 { 422 unset($this->nodes[$p]); 423 unset($this->edata[$p]); 424 } 425 426 list($dir,$name)=self::dirBaseName($rpath); 427 $this->rlookup($dir)->removeChild($name); 428 } 429 430 // </CREATOR> //--------------- 431 432 //--- 433 } // End of class 434 //=========================================================================== 435 } // End of class_exists 436 //=========================================================================== 437 } // End of namespace 438 //=========================================================================== 439 ?>
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 |