[ Index ]

PHP Cross Reference of PHK Manager

title

Body

[close]

/PHK/ -> Proxy.php (source)

   1  <?php
   2  //=============================================================================
   3  //
   4  //   Licensed under the Apache License, Version 2.0 (the "License");
   5  //   you may not use this file except in compliance with the License.
   6  //   You may obtain a copy of the License at
   7  //
   8  //       http://www.apache.org/licenses/LICENSE-2.0
   9  //
  10  //   Unless required by applicable law or agreed to in writing, software
  11  //   distributed under the License is distributed on an "AS IS" BASIS,
  12  //   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13  //   See the License for the specific language governing permissions and
  14  //   limitations under the License.
  15  //
  16  //=============================================================================
  17  /**
  18  * @copyright Francois Laupretre <phk@tekwire.net>
  19  * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0
  20  * @category PHK
  21  * @package PHK
  22  *///==========================================================================
  23  
  24  namespace PHK {
  25  
  26  if (!class_exists('PHK\Proxy',false))
  27  {
  28  //=============================================================================
  29  /**
  30  * The 'back-end' object managing physical access to the package file. This
  31  * object is created and called by the stream wrapper on cache misses.
  32  *
  33  * Runtime code -> 100% read-only except setBufferInterp().
  34  *
  35  * API status: Private
  36  * Included in the PHK PHP runtime: Yes
  37  * Implemented in the extension: No
  38  *///==========================================================================
  39  
  40  class Proxy
  41  {
  42  //========== Class constants ===============
  43  
  44  /** The size of the interp line (fixed size) */
  45  
  46  const INTERP_LEN=64;
  47  
  48  /** The size of a version string in the magic block - Up to 12 bytes */
  49  
  50  const VERSION_SIZE=12;
  51  
  52  /** The size of each offset field. As offset are limited to 11 bytes, the
  53  * theoritical max size of a PHK archive is 100 G bytes. Not tested :) */
  54  
  55  const OFFSET_SIZE=11;
  56  
  57  /** The magic string. This is the string we identify to recognize a PHK archive */
  58  
  59  const MAGIC_STRING="#PHK M\024\x8\6\3";
  60  const MAGIC_STRING_LEN=10;
  61  
  62  /** INTERP_LEN + 6 */
  63  
  64  const MAGIC_STRING_OFFSET=70;
  65  
  66  /** The size of the magic block */
  67  
  68  const MAGIC_LINE_LEN=177;
  69  
  70  /** The name of the optional Automap section, when it exists */
  71  
  72  const AUTOMAP_SECTION='AUTOMAP';
  73  
  74  /** The offset of the CRC field, from the beginning of the archive file.
  75  * The CRC field is 8 bytes long (32 bits in hexadecimal) */
  76  
  77  const CRC_OFFSET=200;
  78  
  79  //========== Instance data ===============
  80  
  81  /** @var string Package path */
  82  
  83  private    $path;
  84  
  85  /** @var PHK\Virtual\Tree    Section tree */
  86  
  87  protected    $stree=null;
  88  
  89  /** @var PHK\Virtual\Tree    File tree */
  90  
  91  public        $ftree=null;
  92  
  93  /** @var integer    Mount flags */
  94  
  95  protected    $flags;
  96  
  97  /** @var PHK\PkgFileSpace    File Handler */
  98  
  99  protected    $fspace;
 100  
 101  /** @var array        Magic values */
 102  
 103  private        $magic=null;
 104  
 105  //========== Class methods ===============
 106  
 107  /**
 108  * Constructor
 109  *
 110  * This method must be called only from PHK\Mgr::proxy()
 111  *
 112  * @param string mount point
 113  *
 114  * @throws \Exception
 115  */
 116  
 117  public function __construct($path,$flags)
 118  {
 119  try
 120  {
 121  Tools\Util::slowPath();
 122  
 123  //Tools\Util::trace("Starting proxy init");//TRACE
 124  
 125  $this->path=$path;
 126  $this->flags=$flags;
 127  
 128  if (!($this->flags & \PHK::IS_CREATOR))
 129      {
 130      // fileIsPackage() moved here from \PHK\Mgr::computeMnt() because we don't
 131      // need to check this if data is already in cache.
 132  
 133      if (! self::fileIsPackage($path))
 134          throw new \Exception($path.'is not a PHK package');
 135  
 136      $this->fspace= new PkgFileSpace($path,$flags);
 137      $this->fspace->open();
 138  
 139      // Get magic block
 140  
 141      $this->getMagicValues();
 142  
 143      // Check that file size corresponds to the value stored in the magic block.
 144      // Done only once in slow path because, if the file size changes, the
 145      // modification date will change too, and thus the mount point.
 146  
 147      if ($this->fspace->size()!=$this->magic['fs']) // Check file size
 148          Tools\Util::formatError('Invalid file size. Should be '.$this->magic['fs']);
 149  
 150      // Import section tree
 151  
 152      $this->stree=Virtual\Tree::createFromEdata(
 153          $this->fspace->readBlock($this->magic['sso']
 154              ,$this->magic['sto']-$this->magic['sso'])
 155          ,new PkgFileSpace($this->fspace,$this->magic['sto']
 156              ,$this->magic['fto']-$this->magic['sto']));
 157  
 158      $this->ftree=Virtual\Tree::createFromEdata($this->section('FTREE')
 159          ,new PkgFileSpace($this->fspace,$this->magic['fto']
 160              ,$this->magic['sio']-$this->magic['fto']));
 161  
 162      $this->fspace->close(); // We keep the file open during init phase
 163      }
 164  else
 165      {
 166      $this->ftree=Virtual\Tree::createEmpty();
 167      $this->stree=Virtual\Tree::createEmpty();
 168      }
 169  }
 170  catch (\Exception $e)
 171      {
 172      throw new \Exception('While initializing PHK proxy - '.$e->getMessage());
 173      }
 174  //Tools\Util::trace("Ending init - path=$path");//TRACE
 175  }
 176  
 177  //---------
 178  
 179  public function crcCheck()
 180  {
 181  try
 182      {
 183      self::checkCrcBuffer($this->fspace->readBlock());
 184      }
 185  catch(\Exception $e)
 186      {
 187      throw new \Exception($this->path.': file is corrupted - '.$e->getMessage());
 188      }
 189  }
 190  
 191  //---------------------------------
 192  /**
 193  * Inserts or clears a CRC in a memory buffer
 194  *
 195  * @static
 196  * @param string $buffer    The original buffer whose CRC will be overwritten
 197  * @param string $crc    If set, the CRC as an 8-char string (in hexadecimal). If
 198  *    not set, we clear the CRC (set it to '00000000').
 199  * @return string    The modified buffer
 200  */
 201  
 202  public static function insertCrc($buffer,$crc)
 203  {
 204  return substr_replace($buffer,$crc,self::CRC_OFFSET,8);
 205  }
 206  
 207  //--------------------------------
 208  /**
 209  * Returns the CRC extracted from a memory buffer (not the computed one)
 210  *
 211  * @param string $buffer
 212  * @return string The extracted 8-char hex CRC
 213  */
 214  
 215  private static function getCrc($buffer)
 216  {
 217  return substr($buffer,self::CRC_OFFSET,8);
 218  }
 219  
 220  //---------------------------------
 221  /**
 222  * Computes a CRC from a given memory buffer
 223  *
 224  * As the given buffer already contains a CRC, we first clear it.
 225  *
 226  * @param string $buffer
 227  * @return string The computed 8-char hex CRC
 228  */
 229  
 230  private static function computeCrc($buffer)
 231  {
 232  return hash('adler32',self::insertCrc($buffer,'00000000'));
 233  }
 234  
 235  //---------------------------------
 236  /**
 237  * Checks a memory buffer's CRC
 238  *
 239  * The memory buffer is supposed to contain a whole PHK archive.
 240  *
 241  * No return value: if the CRC check fails, an exception is thrown.
 242  *
 243  * @param string $buffer
 244  * @return void
 245  * @throws \Exception
 246  */
 247  
 248  public static function checkCrcBuffer($buffer)
 249  {
 250  if (self::computeCrc($buffer) !== self::getCrc($buffer))
 251      throw new \Exception('CRC check failed');
 252  }
 253  
 254  //---------------------------------
 255  /**
 256  * Computes and inserts a CRC in a memory buffer
 257  *
 258  * @param string $buffer
 259  * @return string    The modified buffer
 260  */
 261  
 262  public static function fixCrc($buffer)
 263  {
 264  return self::insertCrc($buffer,self::computeCrc($buffer));
 265  }
 266  
 267  //---------
 268  /**
 269  * Check if a given path contains a PHK package
 270  *
 271  * @param string $path    path to check (can be virtual)
 272  * @return boolean
 273  */
 274  
 275  public static function fileIsPackage($path)
 276  {
 277  if (filesize($path)< (self::INTERP_LEN+self::MAGIC_LINE_LEN)) return false;
 278  if (($fp=fopen($path,'rb',false))===false) return false;
 279  if (fseek($fp,self::MAGIC_STRING_OFFSET) != 0) return false;
 280  if (($m=fread($fp,self::MAGIC_STRING_LEN))===false) return false;
 281  fclose($fp);
 282  return ($m===self::MAGIC_STRING);
 283  }
 284  
 285  //---------
 286  /**
 287  * Check if a data buffer contains a PHK package
 288  *
 289  * @param string $data    data buffer to check
 290  * @return boolean
 291  */
 292  
 293  public static function dataIsPackage($data)
 294  {
 295  if (strlen($data) < (self::INTERP_LEN+self::MAGIC_LINE_LEN)) return false;
 296  return (substr($data,self::MAGIC_STRING_OFFSET,self::MAGIC_STRING_LEN)
 297      ===self::MAGIC_STRING);
 298  }
 299  
 300  //---------------------------------
 301  /**
 302  * Extracts the values out of a magic line buffer
 303  *
 304  * Note: A package is signed if (Signature offset != File size)
 305  *
 306  * @param string $buf A magic line content
 307  * @return array An array containing the magic values
 308  */
 309  
 310  public function getMagicValues()
 311  {
 312  $buf=$this->fspace->readBlock(self::INTERP_LEN,self::MAGIC_LINE_LEN);
 313  
 314  $fsize=(int)substr($buf,47,self::OFFSET_SIZE);
 315  $sio=(int)substr($buf,121,self::OFFSET_SIZE);
 316  $crc=null;
 317  sscanf(substr($buf,136,8),'%08x',$crc);
 318  
 319  $this->magic=array(
 320      'mv'  => trim(substr($buf,18,self::VERSION_SIZE)),    // Minimum required version
 321      'v'      => trim(substr($buf,32,self::VERSION_SIZE)),    // Version
 322      'fs'  => $fsize,                                    // File size
 323      'po'  => (int)substr($buf,61,self::OFFSET_SIZE),    // Prolog offset
 324      'sso' => (int)substr($buf,76,self::OFFSET_SIZE),    // Serialized sections offset
 325      'sto' => (int)substr($buf,91,self::OFFSET_SIZE),    // Section table offset
 326      'fto' => (int)substr($buf,106,self::OFFSET_SIZE),    // File table offset
 327      'sio' => $sio,                                        // Signature offset
 328      'pco' => (int)substr($buf,148,self::OFFSET_SIZE),    // PHP code offset
 329      'pcs' => (int)substr($buf,163,self::OFFSET_SIZE),    // PHP code length
 330      'crc' => $crc,
 331      'signed' => ($sio != $fsize));
 332  }
 333  
 334  //---------------------------------
 335  
 336  public function magicField($name)
 337  {
 338  return $this->magic[$name];
 339  }
 340  
 341  //---------------------------------
 342  /**
 343  * Brings all data in memory
 344  *
 345  * After this function has run, we never access the package file any more.
 346  *
 347  * @see \PHK\Virtual\DC implements the data cache
 348  *
 349  * @return void
 350  */
 351  
 352  private function cacheData()
 353  {
 354  $this->stree->walk('read');
 355  $this->ftree->walk('read');
 356  }
 357  
 358  //---------------------------------
 359  /**
 360  * Clears the data cache
 361  *
 362  * @see \PHK\Virtual\DC implements the data cache
 363  *
 364  * @return void
 365  */
 366  
 367  private function clearCache()
 368  {
 369  $this->stree->walk('clear_cache');
 370  $this->ftree->walk('clear_cache');
 371  }
 372  
 373  //---------------------------------
 374  
 375  public function pathList()
 376  {
 377  return $this->ftree->pathList();
 378  }
 379  
 380  //---------------------------------
 381  
 382  public function sectionList()
 383  {
 384  return $this->stree->pathList();
 385  }
 386  
 387  
 388  //---------------------------------
 389  /**
 390  * Is this package digitally signed ?
 391  *
 392  * @return boolean
 393  */
 394  
 395  public function signed()
 396  {
 397  return $this->magic['signed'];
 398  }
 399  
 400  //-----
 401  /**
 402  * Gets interpreter string
 403  *
 404  * If the interpreter is defined, returns it. Else, returns an empty string
 405  *
 406  * @return string
 407  * @throws \Exception if the interpreter string is invalid
 408  */
 409  
 410  public function interp()
 411  {
 412  $block=$this->fspace->readBlock(0,self::INTERP_LEN);
 413  
 414  if ((($block{0}!='#')||($block{1}!='!')) && (($block{0}!='<')||($block{1}!='?')))
 415      throw new \Exception('Invalid interpreter block');
 416  return ($block{0}=='#') ? trim(substr($block,2)) : '';
 417  }
 418  
 419  //-----
 420  /**
 421  * Builds an interpreter block from an interpreter string
 422  *
 423  * Note: can be applied to a signed package as the signature ignores the
 424  * interpreter block and the CRC.
 425  
 426  * @param string $interp Interpreter to set or empty string to clear
 427  * @return string Interpreter block (INTERP_LEN). Including trailing '\n'
 428  */
 429  
 430  public static function interpBlock($interp)
 431  {
 432  if (($interp!=='') && (strlen($interp) > (\PHK\Proxy::INTERP_LEN-3)))
 433      throw new \Exception('Length of interpreter string is limited to '
 434          .(\PHK\Proxy::INTERP_LEN-3).' bytes');
 435  
 436  // Keep '<?'.'php' or it will be translated when building the runtime code
 437  
 438  if ($interp==='') return str_pad('<?'.'php',\PHK\Proxy::INTERP_LEN-2).'?'.'>';
 439  else return '#!'.str_pad($interp,\PHK\Proxy::INTERP_LEN-3)."\n";
 440  }
 441  
 442  //-----
 443  /**
 444  * Inserts a new interpreter block in a file's content
 445  *
 446  * Allows a PHK user to change its interpreter string without
 447  * having to use the \PHK\Build\Creator kit.
 448  *
 449  * Note: can be applied to a signed package as the signature ignores the
 450  * interpreter block and the CRC.
 451  *
 452  * @param string $path PHK archive's path
 453  * @param string $interp Interpreter string to set (empty to clear)
 454  * @return string The modified buffer (the file is not overwritten)
 455  */
 456  
 457  public static function setBufferInterp($path,$interp='')
 458  {
 459  return self::fixCrc(substr_replace(Tools\Util::readFile($path)
 460      ,self::interpBlock($interp),0,\PHK\Proxy::INTERP_LEN));
 461  }
 462  
 463  //-----
 464  /**
 465  * The version of the \PHK\Build\Creator tool this package was created from
 466  *
 467  * @return string Version
 468  */
 469  
 470  public function version()
 471  {
 472  return $this->magic['v'];
 473  }
 474  
 475  //-----
 476  /**
 477  * Returns the $path property
 478  *
 479  * @return string
 480  */
 481  
 482  public function path()
 483  {
 484  return $this->fspace->path();
 485  }
 486  
 487  //-----
 488  /**
 489  * Get a section's content
 490  *
 491  * @param string $name The section name
 492  * @return string The section's content
 493  * @throws \Exception if section does not exist or cannot be read
 494  */
 495  
 496  public function section($name)
 497  {
 498  try { $node=$this->stree->lookupFile($name); }
 499  catch (\Exception $e) { throw new \Exception($name.': Unknown section'); }
 500  
 501  try { return $node->read(); }
 502  catch (\Exception $e)
 503      { throw new \Exception($name.': Cannot read section - '.$e->getMessage()); }
 504  }
 505  
 506  //-----
 507  
 508  public function ftree()
 509  {
 510  return $this->ftree;
 511  }
 512  
 513  //-----
 514  
 515  public function stree()
 516  {
 517  return $this->stree;
 518  }
 519  
 520  //-----
 521  /**
 522  * Returns the $flags property
 523  *
 524  * @return integer
 525  */
 526  
 527  public function flags()
 528  {
 529  return $this->flags;
 530  }
 531  
 532  //---------------------------------
 533  
 534  public function displayPackages()
 535  {
 536  $this->ftree->displayPackages();
 537  }
 538  
 539  //---------------------------------
 540  // Display the file tree
 541  
 542  public function showfiles()
 543  {
 544  $this->ftree->display(true);
 545  }
 546  
 547  //---
 548  } // End of class
 549  //===========================================================================
 550  } // End of class_exists
 551  //===========================================================================
 552  } // End of namespace
 553  //===========================================================================
 554  ?>


Generated: Thu Jun 4 18:33:15 2015 Cross-referenced by PHPXref 0.7.1