PageRenderTime 57ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 1ms

/Base/src/base.php

https://github.com/Yannix/zetacomponents
PHP | 674 lines | 337 code | 52 blank | 285 comment | 47 complexity | 2bf3fc010f4d55c2084595fc5cee3914 MD5 | raw file
  1. <?php
  2. /**
  3. * File containing the ezcBase class.
  4. *
  5. * Licensed to the Apache Software Foundation (ASF) under one
  6. * or more contributor license agreements. See the NOTICE file
  7. * distributed with this work for additional information
  8. * regarding copyright ownership. The ASF licenses this file
  9. * to you under the Apache License, Version 2.0 (the
  10. * "License"); you may not use this file except in compliance
  11. * with the License. You may obtain a copy of the License at
  12. *
  13. * http://www.apache.org/licenses/LICENSE-2.0
  14. *
  15. * Unless required by applicable law or agreed to in writing,
  16. * software distributed under the License is distributed on an
  17. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  18. * KIND, either express or implied. See the License for the
  19. * specific language governing permissions and limitations
  20. * under the License.
  21. *
  22. * @package Base
  23. * @version //autogentag//
  24. * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
  25. */
  26. /**
  27. * Base class implements the methods needed to use the eZ components.
  28. *
  29. * @package Base
  30. * @version //autogentag//
  31. * @mainclass
  32. */
  33. class ezcBase
  34. {
  35. /**
  36. * Used for dependency checking, to check for a PHP extension.
  37. */
  38. const DEP_PHP_EXTENSION = "extension";
  39. /**
  40. * Used for dependency checking, to check for a PHP version.
  41. */
  42. const DEP_PHP_VERSION = "version";
  43. /**
  44. * Denotes the production mode
  45. */
  46. const MODE_PRODUCTION = 0;
  47. /**
  48. * Denotes the development mode
  49. */
  50. const MODE_DEVELOPMENT = 1;
  51. /**
  52. * Indirectly it determines the path where the autoloads are stored.
  53. *
  54. * @var string
  55. */
  56. private static $libraryMode = "devel";
  57. /**
  58. * Contains the current working directory, which is used when the
  59. * $libraryMode is set to "custom".
  60. *
  61. * @var string
  62. */
  63. private static $currentWorkingDirectory = null;
  64. /**
  65. * The full path to the autoload directory.
  66. *
  67. * @var string
  68. */
  69. protected static $packageDir = null;
  70. /**
  71. * Contains which development mode is used. It's "development" by default,
  72. * because of backwards compatibility reasons.
  73. */
  74. private static $runMode = self::MODE_DEVELOPMENT;
  75. /**
  76. * Stores info with additional paths where autoload files and classes for
  77. * autoloading could be found. Each item of $repositoryDirs looks like
  78. * array( autoloadFileDir, baseDir ). The array key is the prefix belonging
  79. * to classes within that repository - if provided when calling
  80. * addClassRepository(), or an autoincrement integer otherwise.
  81. *
  82. * @var array(string=>array)
  83. */
  84. protected static $repositoryDirs = array();
  85. /**
  86. * This variable stores all the elements from the autoload arrays. When a
  87. * new autoload file is loaded, their files are added to this array.
  88. *
  89. * @var array(string=>string)
  90. */
  91. protected static $autoloadArray = array();
  92. /**
  93. * This variable stores all the elements from the autoload arrays for
  94. * external repositories. When a new autoload file is loaded, their files
  95. * are added to this array.
  96. *
  97. * @var array(string=>string)
  98. */
  99. protected static $externalAutoloadArray = array();
  100. /**
  101. * Options for the ezcBase class.
  102. *
  103. * @var ezcBaseOptions
  104. */
  105. static private $options;
  106. /**
  107. * Associates an option object with this static class.
  108. *
  109. * @param ezcBaseAutoloadOptions $options
  110. */
  111. static public function setOptions( ezcBaseAutoloadOptions $options )
  112. {
  113. self::$options = $options;
  114. }
  115. /**
  116. * Tries to autoload the given className. If the className could be found
  117. * this method returns true, otherwise false.
  118. *
  119. * This class caches the requested class names (including the ones who
  120. * failed to load).
  121. *
  122. * @param string $className The name of the class that should be loaded.
  123. *
  124. * @return bool
  125. */
  126. public static function autoload( $className )
  127. {
  128. ezcBase::setPackageDir();
  129. // Check whether the classname is already in the cached autoloadArray.
  130. if ( array_key_exists( $className, ezcBase::$autoloadArray ) )
  131. {
  132. // Is it registered as 'unloadable'?
  133. if ( ezcBase::$autoloadArray[$className] == false )
  134. {
  135. return false;
  136. }
  137. ezcBase::loadFile( ezcBase::$autoloadArray[$className] );
  138. return true;
  139. }
  140. // Check whether the classname is already in the cached autoloadArray
  141. // for external repositories.
  142. if ( array_key_exists( $className, ezcBase::$externalAutoloadArray ) )
  143. {
  144. // Is it registered as 'unloadable'?
  145. if ( ezcBase::$externalAutoloadArray[$className] == false )
  146. {
  147. return false;
  148. }
  149. ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] );
  150. return true;
  151. }
  152. // Not cached, so load the autoload from the package.
  153. // Matches the first and optionally the second 'word' from the classname.
  154. $fileNames = array();
  155. if ( preg_match( "/^([a-z0-9]*)([A-Z][a-z0-9]*)?([A-Z][a-z0-9]*)?/", $className, $matches ) !== false )
  156. {
  157. $autoloadFile = "";
  158. // Try to match with both names, if available.
  159. switch ( sizeof( $matches ) )
  160. {
  161. case 4:
  162. // check for x_y_autoload.php
  163. $autoloadFile = strtolower( "{$matches[2]}_{$matches[3]}_autoload.php" );
  164. $fileNames[] = $autoloadFile;
  165. if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
  166. {
  167. return true;
  168. }
  169. // break intentionally missing.
  170. case 3:
  171. // check for x_autoload.php
  172. $autoloadFile = strtolower( "{$matches[2]}_autoload.php" );
  173. $fileNames[] = $autoloadFile;
  174. if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
  175. {
  176. return true;
  177. }
  178. // break intentionally missing.
  179. case 2:
  180. // check for autoload.php
  181. $autoloadFile = 'autoload.php';
  182. $fileNames[] = $autoloadFile;
  183. if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
  184. {
  185. return true;
  186. }
  187. break;
  188. }
  189. // Maybe there is another autoload available.
  190. // Register this classname as false.
  191. ezcBase::$autoloadArray[$className] = false;
  192. }
  193. $path = ezcBase::$packageDir . 'autoload/';
  194. $realPath = realpath( $path );
  195. if ( $realPath == '' )
  196. {
  197. // Can not be tested, because if this happens, then the autoload
  198. // environment has not been set-up correctly.
  199. trigger_error( "Couldn't find autoload directory '$path'", E_USER_ERROR );
  200. }
  201. $dirs = self::getRepositoryDirectories();
  202. if ( ezcBase::$options && ezcBase::$options->debug )
  203. {
  204. throw new ezcBaseAutoloadException( $className, $fileNames, $dirs );
  205. }
  206. return false;
  207. }
  208. /**
  209. * Sets the current working directory to $directory.
  210. *
  211. * @param string $directory
  212. */
  213. public static function setWorkingDirectory( $directory )
  214. {
  215. self::$libraryMode = 'custom';
  216. self::$currentWorkingDirectory = $directory;
  217. }
  218. /**
  219. * Figures out the base path of the Zeta Components installation.
  220. *
  221. * It stores the path that it finds in a static member variable. The path
  222. * depends on the installation method of the Zeta Components. The SVN version
  223. * has a different path than the PEAR installed version.
  224. */
  225. protected static function setPackageDir()
  226. {
  227. if ( ezcBase::$packageDir !== null )
  228. {
  229. return;
  230. }
  231. // Get the path to the components.
  232. $baseDir = dirname( __FILE__ );
  233. switch ( ezcBase::$libraryMode )
  234. {
  235. case "custom":
  236. ezcBase::$packageDir = self::$currentWorkingDirectory . '/';
  237. break;
  238. case "devel":
  239. case "tarball":
  240. ezcBase::$packageDir = $baseDir. "/../../";
  241. break;
  242. case "pear";
  243. ezcBase::$packageDir = $baseDir. "/../";
  244. break;
  245. }
  246. }
  247. /**
  248. * Tries to load the autoload array and, if loaded correctly, includes the class.
  249. *
  250. * @param string $fileName Name of the autoload file.
  251. * @param string $className Name of the class that should be autoloaded.
  252. * @param string $prefix The prefix of the class repository.
  253. *
  254. * @return bool True is returned when the file is correctly loaded.
  255. * Otherwise false is returned.
  256. */
  257. protected static function requireFile( $fileName, $className, $prefix )
  258. {
  259. $autoloadDir = ezcBase::$packageDir . "autoload/";
  260. // We need the full path to the fileName. The method file_exists() doesn't
  261. // automatically check the (php.ini) library paths. Therefore:
  262. // file_exists( "ezc/autoload/$fileName" ) doesn't work.
  263. if ( $prefix === 'ezc' && file_exists( "$autoloadDir$fileName" ) )
  264. {
  265. $array = require( "$autoloadDir$fileName" );
  266. if ( is_array( $array) && array_key_exists( $className, $array ) )
  267. {
  268. // Add the array to the cache, and include the requested file.
  269. ezcBase::$autoloadArray = array_merge( ezcBase::$autoloadArray, $array );
  270. if ( ezcBase::$options !== null && ezcBase::$options->preload && !preg_match( '/Exception$/', $className ) )
  271. {
  272. foreach ( $array as $loadClassName => $file )
  273. {
  274. if ( $loadClassName !== 'ezcBase' && !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false ) && !preg_match( '/Exception$/', $loadClassName ) /*&& !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false )*/ )
  275. {
  276. ezcBase::loadFile( ezcBase::$autoloadArray[$loadClassName] );
  277. }
  278. }
  279. }
  280. else
  281. {
  282. ezcBase::loadFile( ezcBase::$autoloadArray[$className] );
  283. }
  284. return true;
  285. }
  286. }
  287. // It is not in components autoload/ dir.
  288. // try to search in additional dirs.
  289. foreach ( ezcBase::$repositoryDirs as $repositoryPrefix => $extraDir )
  290. {
  291. if ( gettype( $repositoryPrefix ) === 'string' && $repositoryPrefix !== $prefix )
  292. {
  293. continue;
  294. }
  295. if ( file_exists( $extraDir['autoloadDirPath'] . '/' . $fileName ) )
  296. {
  297. $array = array();
  298. $originalArray = require( $extraDir['autoloadDirPath'] . '/' . $fileName );
  299. // Building paths.
  300. // Resulting path to class definition file consists of:
  301. // path to extra directory with autoload file +
  302. // basePath provided for current extra directory +
  303. // path to class definition file stored in autoload file.
  304. foreach ( $originalArray as $class => $classPath )
  305. {
  306. $array[$class] = $extraDir['basePath'] . '/' . $classPath;
  307. }
  308. if ( is_array( $array ) && array_key_exists( $className, $array ) )
  309. {
  310. // Add the array to the cache, and include the requested file.
  311. ezcBase::$externalAutoloadArray = array_merge( ezcBase::$externalAutoloadArray, $array );
  312. ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] );
  313. return true;
  314. }
  315. }
  316. }
  317. // Nothing found :-(.
  318. return false;
  319. }
  320. /**
  321. * Loads, require(), the given file name. If we are in development mode,
  322. * "/src/" is inserted into the path.
  323. *
  324. * @param string $file The name of the file that should be loaded.
  325. */
  326. protected static function loadFile( $file )
  327. {
  328. switch ( ezcBase::$libraryMode )
  329. {
  330. case "devel":
  331. case "tarball":
  332. list( $first, $second ) = explode( '/', $file, 2 );
  333. $file = $first . "/src/" . $second;
  334. break;
  335. case "custom":
  336. list( $first, $second ) = explode( '/', $file, 2 );
  337. // Add the "src/" after the package name.
  338. if ( $first == 'Base' || $first == 'UnitTest' )
  339. {
  340. list( $first, $second ) = explode( '/', $file, 2 );
  341. $file = $first . "/src/" . $second;
  342. }
  343. else
  344. {
  345. list( $first, $second, $third ) = explode( '/', $file, 3 );
  346. $file = $first . '/' . $second . "/src/" . $third;
  347. }
  348. break;
  349. case "pear":
  350. /* do nothing, it's already correct */
  351. break;
  352. }
  353. if ( file_exists( ezcBase::$packageDir . $file ) )
  354. {
  355. require( ezcBase::$packageDir . $file );
  356. }
  357. else
  358. {
  359. // Can not be tested, because if this happens, then one of the
  360. // components has a broken autoload file.
  361. throw new ezcBaseFileNotFoundException( ezcBase::$packageDir.$file );
  362. }
  363. }
  364. /**
  365. * Loads, require(), the given file name from an external package.
  366. *
  367. * @param string $file The name of the file that should be loaded.
  368. */
  369. protected static function loadExternalFile( $file )
  370. {
  371. if ( file_exists( $file ) )
  372. {
  373. require( $file );
  374. }
  375. else
  376. {
  377. throw new ezcBaseFileNotFoundException( $file );
  378. }
  379. }
  380. /**
  381. * Checks for dependencies on PHP versions or extensions
  382. *
  383. * The function as called by the $component component checks for the $type
  384. * dependency. The dependency $type is compared against the $value. The
  385. * function aborts the script if the dependency is not matched.
  386. *
  387. * @param string $component
  388. * @param int $type
  389. * @param mixed $value
  390. */
  391. public static function checkDependency( $component, $type, $value )
  392. {
  393. switch ( $type )
  394. {
  395. case self::DEP_PHP_EXTENSION:
  396. if ( extension_loaded( $value ) )
  397. {
  398. return;
  399. }
  400. else
  401. {
  402. // Can not be tested as it would abort the PHP script.
  403. die( "\nThe {$component} component depends on the default PHP extension '{$value}', which is not loaded.\n" );
  404. }
  405. break;
  406. case self::DEP_PHP_VERSION:
  407. $phpVersion = phpversion();
  408. if ( version_compare( $phpVersion, $value, '>=' ) )
  409. {
  410. return;
  411. }
  412. else
  413. {
  414. // Can not be tested as it would abort the PHP script.
  415. die( "\nThe {$component} component depends on the PHP version '{$value}', but the current version is '{$phpVersion}'.\n" );
  416. }
  417. break;
  418. }
  419. }
  420. /**
  421. * Return the list of directories that contain class repositories.
  422. *
  423. * The path to the eZ components directory is always included in the result
  424. * array. Each element in the returned array has the format of:
  425. * packageDirectory => ezcBaseRepositoryDirectory
  426. *
  427. * @return array(string=>ezcBaseRepositoryDirectory)
  428. */
  429. public static function getRepositoryDirectories()
  430. {
  431. $autoloadDirs = array();
  432. ezcBase::setPackageDir();
  433. $repositoryDir = self::$currentWorkingDirectory ? self::$currentWorkingDirectory : ( realpath( dirname( __FILE__ ) . '/../../' ) );
  434. $autoloadDirs['ezc'] = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_INTERNAL, $repositoryDir, $repositoryDir . "/autoload" );
  435. foreach ( ezcBase::$repositoryDirs as $extraDirKey => $extraDirArray )
  436. {
  437. $repositoryDirectory = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_EXTERNAL, realpath( $extraDirArray['basePath'] ), realpath( $extraDirArray['autoloadDirPath'] ) );
  438. $autoloadDirs[$extraDirKey] = $repositoryDirectory;
  439. }
  440. return $autoloadDirs;
  441. }
  442. /**
  443. * Adds an additional class repository.
  444. *
  445. * Used for adding class repositoryies outside the eZ components to be
  446. * loaded by the autoload system.
  447. *
  448. * This function takes two arguments: $basePath is the base path for the
  449. * whole class repository and $autoloadDirPath the path where autoload
  450. * files for this repository are found. The paths in the autoload files are
  451. * relative to the package directory as specified by the $basePath
  452. * argument. I.e. class definition file will be searched at location
  453. * $basePath + path to the class definition file as stored in the autoload
  454. * file.
  455. *
  456. * addClassRepository() should be called somewhere in code before external classes
  457. * are used.
  458. *
  459. * Example:
  460. * Take the following facts:
  461. * <ul>
  462. * <li>there is a class repository stored in the directory "./repos"</li>
  463. * <li>autoload files for that repository are stored in "./repos/autoloads"</li>
  464. * <li>there are two components in this repository: "Me" and "You"</li>
  465. * <li>the "Me" component has the classes "erMyClass1" and "erMyClass2"</li>
  466. * <li>the "You" component has the classes "erYourClass1" and "erYourClass2"</li>
  467. * </ul>
  468. *
  469. * In this case you would need to create the following files in
  470. * "./repos/autoloads". Please note that the part before _autoload.php in
  471. * the filename is the first part of the <b>classname</b>, not considering
  472. * the all lower-case letter prefix.
  473. *
  474. * "my_autoload.php":
  475. * <code>
  476. * <?php
  477. * return array (
  478. * 'erMyClass1' => 'Me/myclass1.php',
  479. * 'erMyClass2' => 'Me/myclass2.php',
  480. * );
  481. * ?>
  482. * </code>
  483. *
  484. * "your_autoload.php":
  485. * <code>
  486. * <?php
  487. * return array (
  488. * 'erYourClass1' => 'You/yourclass1.php',
  489. * 'erYourClass2' => 'You/yourclass2.php',
  490. * );
  491. * ?>
  492. * </code>
  493. *
  494. * The directory structure for the external repository is then:
  495. * <code>
  496. * ./repos/autoloads/my_autoload.php
  497. * ./repos/autoloads/you_autoload.php
  498. * ./repos/Me/myclass1.php
  499. * ./repos/Me/myclass2.php
  500. * ./repos/You/yourclass1.php
  501. * ./repos/You/yourclass2.php
  502. * </code>
  503. *
  504. * To use this repository with the autoload mechanism you have to use the
  505. * following code:
  506. * <code>
  507. * <?php
  508. * ezcBase::addClassRepository( './repos', './repos/autoloads' );
  509. * $myVar = new erMyClass2();
  510. * ?>
  511. * </code>
  512. *
  513. * @throws ezcBaseFileNotFoundException if $autoloadDirPath or $basePath do not exist.
  514. * @param string $basePath
  515. * @param string $autoloadDirPath
  516. * @param string $prefix
  517. */
  518. public static function addClassRepository( $basePath, $autoloadDirPath = null, $prefix = null )
  519. {
  520. // check if base path exists
  521. if ( !is_dir( $basePath ) )
  522. {
  523. throw new ezcBaseFileNotFoundException( $basePath, 'base directory' );
  524. }
  525. // calculate autoload path if it wasn't given
  526. if ( is_null( $autoloadDirPath ) )
  527. {
  528. $autoloadDirPath = $basePath . '/autoload';
  529. }
  530. // check if autoload dir exists
  531. if ( !is_dir( $autoloadDirPath ) )
  532. {
  533. throw new ezcBaseFileNotFoundException( $autoloadDirPath, 'autoload directory' );
  534. }
  535. // add info to $repositoryDirs
  536. if ( $prefix === null )
  537. {
  538. $array = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
  539. // add info to the list of extra dirs
  540. ezcBase::$repositoryDirs[] = $array;
  541. }
  542. else
  543. {
  544. if ( array_key_exists( $prefix, ezcBase::$repositoryDirs ) )
  545. {
  546. throw new ezcBaseDoubleClassRepositoryPrefixException( $prefix, $basePath, $autoloadDirPath );
  547. }
  548. // add info to the list of extra dirs, and use the prefix to identify the new repository.
  549. ezcBase::$repositoryDirs[$prefix] = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
  550. }
  551. }
  552. /**
  553. * Returns the base path of the Zeta Components installation
  554. *
  555. * This method returns the base path, including a trailing directory
  556. * separator.
  557. *
  558. * @return string
  559. */
  560. public static function getInstallationPath()
  561. {
  562. self::setPackageDir();
  563. $path = realpath( self::$packageDir );
  564. if ( substr( $path, -1 ) !== DIRECTORY_SEPARATOR )
  565. {
  566. $path .= DIRECTORY_SEPARATOR;
  567. }
  568. return $path;
  569. }
  570. /**
  571. * Sets the development mode to the one specified.
  572. *
  573. * @param int $runMode
  574. */
  575. public static function setRunMode( $runMode )
  576. {
  577. if ( !in_array( $runMode, array( ezcBase::MODE_PRODUCTION, ezcBase::MODE_DEVELOPMENT ) ) )
  578. {
  579. throw new ezcBaseValueException( 'runMode', $runMode, 'ezcBase::MODE_PRODUCTION or ezcBase::MODE_DEVELOPMENT' );
  580. }
  581. self::$runMode = $runMode;
  582. }
  583. /**
  584. * Returns the current development mode.
  585. *
  586. * @return int
  587. */
  588. public static function getRunMode()
  589. {
  590. return self::$runMode;
  591. }
  592. /**
  593. * Returns true when we are in development mode.
  594. *
  595. * @return bool
  596. */
  597. public static function inDevMode()
  598. {
  599. return self::$runMode == ezcBase::MODE_DEVELOPMENT;
  600. }
  601. /**
  602. * Returns the installation method
  603. *
  604. * Possible return values are 'custom', 'devel', 'tarball' and 'pear'. Only
  605. * 'tarball' and 'pear' are returned for user-installed versions.
  606. *
  607. * @return string
  608. */
  609. public static function getInstallMethod()
  610. {
  611. return self::$libraryMode;
  612. }
  613. }
  614. ?>