[ Index ]

PHP Cross Reference of PHK Manager

title

Body

[close]

/PHK/ -> Mgr.php (source)

   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\Mgr',false))
  29  {
  30  //=============================================================================
  31  /**
  32  * The PHK manager
  33  *
  34  * This class manages the table of currently mounted packages.
  35  *
  36  * Each package is uniquely identified by a 'mount point' (a string computed
  37  * at mount time). A given package file always gets the same mount point in
  38  * every request, as long as it is not modified.
  39  *
  40  * Among others, this class allows to mount and umount packages.
  41  *
  42  * Runtime code -> 100% read-only.
  43  *
  44  * Static-only
  45  * API status: Public
  46  * Included in the PHK PHP runtime: Yes
  47  * Implemented in the extension: Yes
  48  *///==========================================================================
  49  
  50  class Mgr
  51  {
  52  //-- Global static properties
  53  
  54  /** @var array        Currently mounted PHK instances */
  55  
  56  private static $phk_tab=array(); // Key=mount ID, value=PHK instance
  57  
  58  /**
  59  * @var array        Proxy objects for each currently mounted package. As
  60  * long as the proxy object is not instantiated through the proxy() method,
  61  * the corresponding value is null in this array. Contains exactly the same
  62  * keys as $phk_tab.
  63  */
  64  
  65  private static $proxy_tab=array(); // Key=mount ID, value=PHK\Proxy|null
  66  
  67  /* @var int Running value for \PHK\Build\Creator mount points */
  68  
  69  private static $tmp_mnt_num=0;
  70  
  71  /* @var boolean|null Global cache toggle. If null, per-instance logic is used */
  72  
  73  private static $caching=null;
  74  
  75  //-----
  76  /**
  77  * Checks if a mount point is valid (if it corresponds to a currently mounted
  78  * package)
  79  *
  80  * @param string $mnt Mount point to check
  81  * @return boolean
  82  */
  83  
  84  public static function isMounted($mnt)
  85  {
  86  return isset(self::$phk_tab[$mnt]);
  87  }
  88  
  89  //-----
  90  /**
  91  * Same as isMounted but throws an exception is the mount point is invalid.
  92  *
  93  * Returns the mount point so that it can be embedded in a call string.
  94  *
  95  * @param string $mnt Mount point to check
  96  * @return string mount point (not modified)
  97  * @throws \Exception if mount point is invalid
  98  */
  99  
 100  public static function validate($mnt)
 101  {
 102  if (!self::isMounted($mnt)) throw new \Exception($mnt.': Invalid mount point');
 103  
 104  return $mnt;
 105  }
 106  
 107  //-----
 108  /**
 109  * Returns the PHK object corresponding to a given mount point
 110  *
 111  * @param string $mnt Mount point
 112  * @return PHK instance
 113  * @throws \Exception if mount point is invalid
 114  */
 115  
 116  public static function instance($mnt)
 117  {
 118  self::validate($mnt);
 119  
 120  return self::$phk_tab[$mnt];
 121  }
 122  
 123  //-----
 124  /**
 125  * Returns the \PHK\Proxy object corresponding to a given mount point
 126  *
 127  * If the corresponding \PHK\Proxy object does not exist yet, it is created.
 128  *
 129  * @param string $mnt Mount point
 130  * @return \PHK\Proxy proxy object
 131  * @throws \Exception if mount point is invalid
 132  */
 133  
 134  public static function proxy($mnt)
 135  {
 136  self::validate($mnt);
 137  
 138  if (is_null(self::$proxy_tab[$mnt]))
 139      {
 140      $phk=self::instance($mnt);
 141      self::$proxy_tab[$mnt]=new \PHK\Proxy($phk->path(),$phk->flags());
 142      }
 143  
 144  return self::$proxy_tab[$mnt];
 145  }
 146  
 147  //-----
 148  /**
 149  * Returns the list of the defined mount points.
 150  *
 151  * @return array
 152  */
 153  
 154  public static function mntList()
 155  {
 156  return array_keys(self::$phk_tab);
 157  }
 158  
 159  //---------
 160  /**
 161  * Sets the global caching toggle
 162  *
 163  * Normally, the global cache toggle is always null, except in 'webinfo'
 164  * mode, where it is false to inhibit any caching of webinfo information
 165  * (2 reasons: useless in terms of performance, and could use the same keys as
 166  * the 'non-webinfo' mode, so we would have to use another key prefix).
 167  *
 168  * @param boolean|null $caching True: always cache, False: never cache,
 169  * null: use per-instance logic.
 170  * @return void
 171  */
 172  
 173  public static function setCache($caching)
 174  {
 175  self::$caching=$caching;
 176  }
 177  
 178  //---------
 179  /**
 180  * Determines if a an URI can be cached.
 181  *
 182  * For performance reasons, the input URI is splitted.
 183  *
 184  * Called by the wrapper to know if it should cache the data it got from its
 185  * backend.
 186  *
 187  * The global cache toggle is checked first. If it is null, control is
 188  * transferred to the instance logic.
 189  *
 190  * @param string|null $mnt Mount point or null if global command
 191  * @param string|null $command command if defined
 192  * @param array|null $params Command parameters if defined
 193  * @param string $path Path
 194  * @return boolean whether the data should be cached or not
 195  * @throws \Exception if mount point is invalid
 196  */
 197  
 198  
 199  public static function cacheEnabled($mnt,$command,$params,$path)
 200  {
 201  if (!is_null(self::$caching)) return self::$caching;
 202  
 203  if (is_null($mnt)) return false;
 204  
 205  return self::instance($mnt)->cacheEnabled($command,$params,$path);
 206  }
 207  
 208  //---------
 209  /**
 210  * Given a file path, tries to determine if it is currently mounted. If it is
 211  * the case, the corresponding mount point is returned. If not, an exception is
 212  * thrown.
 213  *
 214  * Note: when dealing with sub-packages, the input path parameter can be a PHK
 215  * URI.
 216  *
 217  * @param string $path Path of a PHK package
 218  * @return the corresponding mount point
 219  * @throws \Exception if the file is not currently mounted
 220  */
 221  
 222  public static function pathToMnt($path)
 223  {
 224  $dummy1=$mnt=$dummy2=null;
 225  self::computeMnt($path,$dummy1,$mnt,$dummy2);
 226  
 227  if (self::isMounted($mnt)) return $mnt;
 228  
 229  throw new \Exception($path.': path is not mounted');
 230  }
 231  
 232  //---------
 233  /**
 234  * Given a PHK uri, returns the path of the first-level package for
 235  * this path. The function recurses until it finds a physical path.
 236  *
 237  * @param string $path A path typically set as '__FILE__'
 238  * @return the physical path of the 1st-level package containing this path
 239  */
 240  
 241  public static function topLevelPath($path)
 242  {
 243  while (self::isPhkUri($path))
 244      {
 245      $mnt=self::uriToMnt($path);
 246      $map=self::instance($mnt);
 247      $path=$map->path();
 248      }
 249  return $path;
 250  }
 251  
 252  //---------
 253  /**
 254  * Mount a PHK package and returns the new (or previous, if already loaded)
 255  * PHK mount point.
 256  *
 257  * Can also create empty \PHK\Build\Creator instances (when the 'CREATOR' flag is set).
 258  *
 259  * @param string $path The path of an existing PHK archive, or the path of the
 260  *                     archive to create if ($flags & \PHK::IS_CREATOR)
 261  * @param int $flags Or-ed combination of PHK mount flags.
 262  * @return string the mount point
 263  */
 264  
 265  public static function mount($path,$flags=0)
 266  {
 267  try
 268  {
 269  if ($flags & \PHK::IS_CREATOR)
 270      {
 271      $mnt='_tmp_mnt_'.(self::$tmp_mnt_num++);
 272      self::$proxy_tab[$mnt]=null;
 273      self::$phk_tab[$mnt]=new \PHK\Build\Creator($mnt,$path,$flags);
 274      }
 275  else    // Mount an existing archive
 276      {
 277      $parentMnt=$mnt=$mtime=$options=$buildInfo=null;
 278      self::computeMnt($path,$parentMnt,$mnt,$mtime);
 279      if (self::isMounted($mnt)) return $mnt;
 280  
 281      self::$proxy_tab[$mnt]=null;
 282      self::$phk_tab[$mnt]=$phk=new \PHK($parentMnt,$mnt,$path,$flags,$mtime);
 283  
 284      self::getStoreData($mnt,$options,$buildInfo);
 285      $phk->init($options,$buildInfo);
 286      }
 287  }
 288  catch (\Exception $e)
 289      {
 290      if (isset($mnt) && self::isMounted($mnt)) unset(self::$phk_tab[$mnt]);
 291      throw new \Exception($path.': Cannot mount - '.$e->getMessage());
 292      }
 293  
 294  return $mnt;
 295  }
 296  
 297  //-----
 298  /**
 299  * Checks the PHK version this package requires against the current version.
 300  * Then, retrieves the 'options' and 'buildInfo' arrays.
 301  *
 302  * This function is separated from mount() to mimic the behavior of the PHK
 303  * extension, where this data is cached in persistent memory.
 304  *
 305  * @param string $mnt the mount point
 306  * @param array $options on return, contains the options array
 307  * @param array $buildInfo on return, contains the buildInfo array
 308  * @return void
 309  */
 310  
 311  private static function getStoreData($mnt,&$options,&$buildInfo)
 312  {
 313  $caching=(is_null(self::$caching) ? true : self::$caching);
 314  
 315  // Must check this first
 316  
 317  $mv=\PHK\Tools\Util::getMinVersion($mnt,$caching);
 318  
 319  if (version_compare($mv,\PHK::RUNTIME_VERSION) > 0)
 320      {
 321      \PHK\Tools\Util::formatError('Cannot understand this version. '
 322          .'Requires at least PHK version '.$mv);
 323      }
 324  
 325  $options=\PHK\Tools\Util::getOptions($mnt,$caching);
 326  $buildInfo=\PHK\Tools\Util::getBuildInfo($mnt,$caching);
 327  }
 328  
 329  //---------------------------------
 330  /**
 331  * Computes the mount point corresponding to a given path.
 332  *
 333  * Also returns the parent mount point (for sub-packages), and the modification
 334  * time (allows to call stat() only once).
 335  *
 336  * Mount point uniqueness is based on a combination of device+inode+mtime.
 337  *
 338  * When dealing with sub-packages, the input path is a PHK URI.
 339  *
 340  * Sub-packages inherit their parent's modification time.
 341  *
 342  * @param string $path The path to be mounted
 343  * @param $parentMnt string|null returns the parent mount point. Not
 344  * null only for sub-packages.
 345  * @param string $mnt returns the computed mount point
 346  * @param int $mtime returns the modification time
 347  * @return void
 348  * @throws \Exception with message 'File not found' if unable to stat($path).
 349  */
 350  
 351  private static function computeMnt($path,&$parentMnt,&$mnt,&$mtime)
 352  {
 353  if (self::isPhkUri($path)) // Sub-package
 354      {
 355      $dummy1=$dummy2=$subpath=$parentMnt=null;
 356      \PHK\Stream\Wrapper::parseURI($path,$dummy1,$dummy2,$parentMnt,$subpath);
 357      self::validate($parentMnt);
 358      $mnt=$parentMnt.'#'.str_replace('/','*',$subpath);
 359      $mtime=self::instance($parentMnt)->mtime(); // Inherit mtime
 360      }
 361  else
 362      {
 363      $mnt=\PHK\Tools\Util::pathUniqueID('p',$path,$mtime);
 364      $parentMnt=null;
 365      }
 366  }
 367  
 368  //---------------------------------
 369  /**
 370  * Umounts a mounted package and any mounted descendant.
 371  *
 372  * We dont use __destruct because :
 373  *    1. We don't want this to be called on script shutdown
 374  *    2. \Exceptions cannot be caught when sent from a destructor.
 375  *
 376  * Accepts to remove a non registered mount point without error
 377  *
 378  * @param string $mnt The mount point to umount
 379  */
 380  
 381  public static function umount($mnt)
 382  {
 383  if (self::isMounted($mnt))
 384      {
 385      // Umount children
 386  
 387      foreach (array_keys(self::$phk_tab) as $dmnt)
 388          {
 389          if (isset(self::$phk_tab[$dmnt])
 390              && self::$phk_tab[$dmnt]->parentMnt()===$mnt)
 391                  self::umount($dmnt);
 392          }
 393  
 394      // Call instance's umount procedure
 395  
 396      self::$phk_tab[$mnt]->umount();
 397  
 398      // Remove from the list
 399  
 400      unset(self::$phk_tab[$mnt]);
 401      unset(self::$proxy_tab[$mnt]);
 402      }
 403  }
 404  
 405  //---------
 406  /**
 407  * Builds a 'phk://' uri, from a mount ID and a path
 408  *
 409  * @param string $mnt The mount point
 410  * @param string $path The path
 411  * @return string The computed URI
 412  */
 413  
 414  public static function uri($mnt,$path)
 415  {
 416  return self::baseURI($mnt).ltrim($path,'/');
 417  }
 418  
 419  //-----
 420  /** Checks if a string is a PHK URI
 421  *
 422  * @param string $uri
 423  * @return boolean
 424  */
 425  
 426  public static function isPhkUri($uri)
 427  {
 428  $u=$uri.'      ';
 429  
 430  // Much faster this way
 431  
 432  return ($u{0}=='p' && $u{1}=='h' && $u{2}=='k' && $u{3}==':'
 433      && $u{4}=='/' && $u{5}=='/'); 
 434  
 435  //return (strlen($uri) >= 6) && (substr($uri,0,6)=='phk://');
 436  }
 437  
 438  //-----
 439  /**
 440  * Returns the base string used to build URIs for a given mount point.
 441  *
 442  * The base URI has the form : phk://<mount point>/
 443  *
 444  * @param string $mnt A mount point
 445  * @return string
 446  */
 447  
 448  public static function baseURI($mnt)
 449  {
 450  return 'phk://'.$mnt.'/';
 451  }
 452  
 453  //---------
 454  /**
 455  * Returns a 'command' URI, given a mount point and a 'command' string
 456  *
 457  * Command URIs have the form : phk://<mount point>/?<command>
 458  *
 459  * @param string $mnt A mount point
 460  * @param string $command Command string
 461  * @return string
 462  */
 463  
 464  public static function commandURI($mnt,$command)
 465  {
 466  return self::uri($mnt,'?'.$command);
 467  }
 468  
 469  //---------
 470  /**
 471  * Returns the URI allowing to retrieve a section.
 472  *
 473  * Section URIs have the form : phk://<mount point>/?section&name=<section>
 474  *
 475  * @param string $mnt A mount point
 476  * @param string $section The section to retrieve
 477  * @return string
 478  */
 479  
 480  public static function sectionURI($mnt,$section)
 481  {
 482  return self::commandURI($mnt,'section&name='.$section);
 483  }
 484  
 485  //-----
 486  /**
 487  * Returns the URI of the Automap map if it is defined
 488  *
 489  * @param string $mnt A mount point
 490  * @return string|null returns null if the package does not define an automap.
 491  */
 492  
 493  public static function automapURI($mnt)
 494  {
 495  if ((!self::isMounted($mnt))||(!self::instance($mnt)->mapDefined()))
 496      return null;
 497  
 498  return self::sectionURI($mnt,'AUTOMAP');
 499  }
 500  
 501  //---------------------------------
 502  /**
 503  * Replaces '\' characters by '/' in a URI.
 504  *
 505  * @param string $uri
 506  * @return string
 507  */
 508  
 509  public static function normalizeURI($uri)
 510  {
 511  return str_replace('\\','/',$uri);
 512  }
 513  
 514  //-----
 515  /**
 516  * Returns the mount ID of a subfile's phk uri.
 517  * Allows to reference other subfiles in the same package if you don't want
 518  * or cannot use Automap (the preferred method) or a relative path.
 519  * Example : include(\PHK\Mgr::uri(\PHK\Mgr::uriToMnt(__FILE__),<path>));
 520  *
 521  * @param string $uri
 522  * @return string a mount point
 523  * @throws \Exception if the input URI is not a PHK URI
 524  */
 525  
 526  public static function uriToMnt($uri)
 527  {
 528  if (! self::isPhkUri($uri))
 529      throw new \Exception($uri.': Not a PHK URI');
 530  
 531  $buf=substr(self::normalizeURI($uri),6);
 532  $buf=substr($buf,0,strcspn($buf,'/'));
 533  return trim($buf);
 534  }
 535  
 536  //---------
 537  /**
 538  * Check if the current PHP version is supported.
 539  *
 540  * Note that, if PHP version < 5.3, parsing fails because of namespaces and we
 541  * don't even start execution. So, this test is only executed when PHP version
 542  * >= 5.3
 543  *
 544  * As a side effect, until we require a version > 5.3, this function
 545  * never fails.
 546  *
 547  * Calls exit(1) if PHP version is not supported by the PHK runtime
 548  *
 549  * @return void
 550  */
 551  
 552  public static function checkPhpVersion()
 553  {
 554  if (version_compare(PHP_VERSION,'5.3.0') < 0)
 555      {
 556      echo PHP_VERSION.': Unsupported PHP version '
 557          .'- PHK needs at least version 5.3.0';
 558      exit(1);
 559      }
 560  }
 561  
 562  //---
 563  } // End of class
 564  //===========================================================================
 565  } // End of class_exists
 566  //===========================================================================
 567  } // End of namespace
 568  //===========================================================================
 569  ?>


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