676 lines
22 KiB
PHP
676 lines
22 KiB
PHP
<?php
|
|
/**
|
|
* File containing the ezcBase class.
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* @package Base
|
|
* @version //autogentag//
|
|
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0
|
|
*/
|
|
/**
|
|
* Base class implements the methods needed to use the eZ components.
|
|
*
|
|
* @package Base
|
|
* @version //autogentag//
|
|
* @mainclass
|
|
*/
|
|
class ezcBase
|
|
{
|
|
/**
|
|
* Used for dependency checking, to check for a PHP extension.
|
|
*/
|
|
const DEP_PHP_EXTENSION = "extension";
|
|
|
|
/**
|
|
* Used for dependency checking, to check for a PHP version.
|
|
*/
|
|
const DEP_PHP_VERSION = "version";
|
|
|
|
/**
|
|
* Denotes the production mode
|
|
*/
|
|
const MODE_PRODUCTION = 0;
|
|
|
|
/**
|
|
* Denotes the development mode
|
|
*/
|
|
const MODE_DEVELOPMENT = 1;
|
|
|
|
/**
|
|
* Indirectly it determines the path where the autoloads are stored.
|
|
*
|
|
* @var string
|
|
*/
|
|
private static $libraryMode = "devel";
|
|
|
|
/**
|
|
* Contains the current working directory, which is used when the
|
|
* $libraryMode is set to "custom".
|
|
*
|
|
* @var string
|
|
*/
|
|
private static $currentWorkingDirectory = null;
|
|
|
|
/**
|
|
* The full path to the autoload directory.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected static $packageDir = null;
|
|
|
|
/**
|
|
* Contains which development mode is used. It's "development" by default,
|
|
* because of backwards compatibility reasons.
|
|
*/
|
|
private static $runMode = self::MODE_DEVELOPMENT;
|
|
|
|
/**
|
|
* Stores info with additional paths where autoload files and classes for
|
|
* autoloading could be found. Each item of $repositoryDirs looks like
|
|
* array( autoloadFileDir, baseDir ). The array key is the prefix belonging
|
|
* to classes within that repository - if provided when calling
|
|
* addClassRepository(), or an autoincrement integer otherwise.
|
|
*
|
|
* @var array(string=>array)
|
|
*/
|
|
protected static $repositoryDirs = array();
|
|
|
|
/**
|
|
* This variable stores all the elements from the autoload arrays. When a
|
|
* new autoload file is loaded, their files are added to this array.
|
|
*
|
|
* @var array(string=>string)
|
|
*/
|
|
protected static $autoloadArray = array();
|
|
|
|
/**
|
|
* This variable stores all the elements from the autoload arrays for
|
|
* external repositories. When a new autoload file is loaded, their files
|
|
* are added to this array.
|
|
*
|
|
* @var array(string=>string)
|
|
*/
|
|
protected static $externalAutoloadArray = array();
|
|
|
|
/**
|
|
* Options for the ezcBase class.
|
|
*
|
|
* @var ezcBaseOptions
|
|
*/
|
|
static private $options;
|
|
|
|
/**
|
|
* Associates an option object with this static class.
|
|
*
|
|
* @param ezcBaseAutoloadOptions $options
|
|
*/
|
|
static public function setOptions( ezcBaseAutoloadOptions $options )
|
|
{
|
|
self::$options = $options;
|
|
}
|
|
|
|
/**
|
|
* Tries to autoload the given className. If the className could be found
|
|
* this method returns true, otherwise false.
|
|
*
|
|
* This class caches the requested class names (including the ones who
|
|
* failed to load).
|
|
*
|
|
* @param string $className The name of the class that should be loaded.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function autoload( $className )
|
|
{
|
|
ezcBase::setPackageDir();
|
|
|
|
// Check whether the classname is already in the cached autoloadArray.
|
|
if ( array_key_exists( $className, ezcBase::$autoloadArray ) )
|
|
{
|
|
// Is it registered as 'unloadable'?
|
|
if ( ezcBase::$autoloadArray[$className] == false )
|
|
{
|
|
return false;
|
|
}
|
|
ezcBase::loadFile( ezcBase::$autoloadArray[$className] );
|
|
|
|
return true;
|
|
}
|
|
|
|
// Check whether the classname is already in the cached autoloadArray
|
|
// for external repositories.
|
|
if ( array_key_exists( $className, ezcBase::$externalAutoloadArray ) )
|
|
{
|
|
// Is it registered as 'unloadable'?
|
|
if ( ezcBase::$externalAutoloadArray[$className] == false )
|
|
{
|
|
return false;
|
|
}
|
|
ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] );
|
|
|
|
return true;
|
|
}
|
|
|
|
// Not cached, so load the autoload from the package.
|
|
// Matches the first and optionally the second 'word' from the classname.
|
|
$fileNames = array();
|
|
if ( preg_match( "/^([a-z0-9]*)([A-Z][a-z0-9]*)?([A-Z][a-z0-9]*)?/", $className, $matches ) !== false )
|
|
{
|
|
$autoloadFile = "";
|
|
// Try to match with both names, if available.
|
|
switch ( sizeof( $matches ) )
|
|
{
|
|
case 4:
|
|
// check for x_y_autoload.php
|
|
$autoloadFile = strtolower( "{$matches[2]}_{$matches[3]}_autoload.php" );
|
|
$fileNames[] = $autoloadFile;
|
|
if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
|
|
{
|
|
return true;
|
|
}
|
|
// break intentionally missing.
|
|
|
|
case 3:
|
|
// check for x_autoload.php
|
|
$autoloadFile = strtolower( "{$matches[2]}_autoload.php" );
|
|
$fileNames[] = $autoloadFile;
|
|
if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
|
|
{
|
|
return true;
|
|
}
|
|
// break intentionally missing.
|
|
|
|
case 2:
|
|
// check for autoload.php
|
|
$autoloadFile = 'autoload.php';
|
|
$fileNames[] = $autoloadFile;
|
|
if ( ezcBase::requireFile( $autoloadFile, $className, $matches[1] ) )
|
|
{
|
|
return true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Maybe there is another autoload available.
|
|
// Register this classname as false.
|
|
ezcBase::$autoloadArray[$className] = false;
|
|
}
|
|
|
|
$path = ezcBase::$packageDir . 'autoload/';
|
|
$realPath = realpath( $path );
|
|
|
|
if ( $realPath == '' )
|
|
{
|
|
// Can not be tested, because if this happens, then the autoload
|
|
// environment has not been set-up correctly.
|
|
trigger_error( "Couldn't find autoload directory '$path'", E_USER_ERROR );
|
|
}
|
|
|
|
$dirs = self::getRepositoryDirectories();
|
|
if ( ezcBase::$options && ezcBase::$options->debug )
|
|
{
|
|
throw new ezcBaseAutoloadException( $className, $fileNames, $dirs );
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Sets the current working directory to $directory.
|
|
*
|
|
* @param string $directory
|
|
*/
|
|
public static function setWorkingDirectory( $directory )
|
|
{
|
|
self::$libraryMode = 'custom';
|
|
self::$currentWorkingDirectory = $directory;
|
|
}
|
|
|
|
/**
|
|
* Figures out the base path of the Zeta Components installation.
|
|
*
|
|
* It stores the path that it finds in a static member variable. The path
|
|
* depends on the installation method of the Zeta Components. The SVN version
|
|
* has a different path than the PEAR installed version.
|
|
*/
|
|
protected static function setPackageDir()
|
|
{
|
|
if ( ezcBase::$packageDir !== null )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Get the path to the components.
|
|
$baseDir = dirname( __FILE__ );
|
|
|
|
switch ( ezcBase::$libraryMode )
|
|
{
|
|
case "custom":
|
|
ezcBase::$packageDir = self::$currentWorkingDirectory . '/';
|
|
break;
|
|
case "devel":
|
|
case "tarball":
|
|
ezcBase::$packageDir = $baseDir. "/../../";
|
|
break;
|
|
case "pear";
|
|
ezcBase::$packageDir = $baseDir. "/../";
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Tries to load the autoload array and, if loaded correctly, includes the class.
|
|
*
|
|
* @param string $fileName Name of the autoload file.
|
|
* @param string $className Name of the class that should be autoloaded.
|
|
* @param string $prefix The prefix of the class repository.
|
|
*
|
|
* @return bool True is returned when the file is correctly loaded.
|
|
* Otherwise false is returned.
|
|
*/
|
|
protected static function requireFile( $fileName, $className, $prefix )
|
|
{
|
|
$autoloadDir = ezcBase::$packageDir . "autoload/";
|
|
|
|
// We need the full path to the fileName. The method file_exists() doesn't
|
|
// automatically check the (php.ini) library paths. Therefore:
|
|
// file_exists( "ezc/autoload/$fileName" ) doesn't work.
|
|
if ( $prefix === 'ezc' && file_exists( "$autoloadDir$fileName" ) )
|
|
{
|
|
$array = require( "$autoloadDir$fileName" );
|
|
|
|
if ( is_array( $array) && array_key_exists( $className, $array ) )
|
|
{
|
|
// Add the array to the cache, and include the requested file.
|
|
ezcBase::$autoloadArray = array_merge( ezcBase::$autoloadArray, $array );
|
|
if ( ezcBase::$options !== null && ezcBase::$options->preload && !preg_match( '/Exception$/', $className ) )
|
|
{
|
|
foreach ( $array as $loadClassName => $file )
|
|
{
|
|
if ( $loadClassName !== 'ezcBase' && !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false ) && !preg_match( '/Exception$/', $loadClassName ) /*&& !class_exists( $loadClassName, false ) && !interface_exists( $loadClassName, false )*/ )
|
|
{
|
|
ezcBase::loadFile( ezcBase::$autoloadArray[$loadClassName] );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ezcBase::loadFile( ezcBase::$autoloadArray[$className] );
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// It is not in components autoload/ dir.
|
|
// try to search in additional dirs.
|
|
foreach ( ezcBase::$repositoryDirs as $repositoryPrefix => $extraDir )
|
|
{
|
|
if ( gettype( $repositoryPrefix ) === 'string' && $repositoryPrefix !== $prefix )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( file_exists( $extraDir['autoloadDirPath'] . '/' . $fileName ) )
|
|
{
|
|
$array = array();
|
|
$originalArray = require( $extraDir['autoloadDirPath'] . '/' . $fileName );
|
|
|
|
// Building paths.
|
|
// Resulting path to class definition file consists of:
|
|
// path to extra directory with autoload file +
|
|
// basePath provided for current extra directory +
|
|
// path to class definition file stored in autoload file.
|
|
foreach ( $originalArray as $class => $classPath )
|
|
{
|
|
$array[$class] = $extraDir['basePath'] . '/' . $classPath;
|
|
}
|
|
|
|
if ( is_array( $array ) && array_key_exists( $className, $array ) )
|
|
{
|
|
// Add the array to the cache, and include the requested file.
|
|
ezcBase::$externalAutoloadArray = array_merge( ezcBase::$externalAutoloadArray, $array );
|
|
ezcBase::loadExternalFile( ezcBase::$externalAutoloadArray[$className] );
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Nothing found :-(.
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Loads, require(), the given file name. If we are in development mode,
|
|
* "/src/" is inserted into the path.
|
|
*
|
|
* @param string $file The name of the file that should be loaded.
|
|
*/
|
|
protected static function loadFile( $file )
|
|
{
|
|
switch ( ezcBase::$libraryMode )
|
|
{
|
|
case "devel":
|
|
case "tarball":
|
|
list( $first, $second ) = explode( '/', $file, 2 );
|
|
$file = $first . "/src/" . $second;
|
|
break;
|
|
|
|
case "custom":
|
|
list( $first, $second ) = explode( '/', $file, 2 );
|
|
// Add the "src/" after the package name.
|
|
if ( $first == 'Base' || $first == 'UnitTest' )
|
|
{
|
|
list( $first, $second ) = explode( '/', $file, 2 );
|
|
$file = $first . "/src/" . $second;
|
|
}
|
|
else
|
|
{
|
|
list( $first, $second, $third ) = explode( '/', $file, 3 );
|
|
$file = $first . '/' . $second . "/src/" . $third;
|
|
}
|
|
break;
|
|
|
|
case "pear":
|
|
/* do nothing, it's already correct */
|
|
break;
|
|
}
|
|
|
|
if ( file_exists( ezcBase::$packageDir . $file ) )
|
|
{
|
|
require( ezcBase::$packageDir . $file );
|
|
}
|
|
else
|
|
{
|
|
// Can not be tested, because if this happens, then one of the
|
|
// components has a broken autoload file.
|
|
throw new ezcBaseFileNotFoundException( ezcBase::$packageDir.$file );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Loads, require(), the given file name from an external package.
|
|
*
|
|
* @param string $file The name of the file that should be loaded.
|
|
*/
|
|
protected static function loadExternalFile( $file )
|
|
{
|
|
if ( file_exists( $file ) )
|
|
{
|
|
require( $file );
|
|
}
|
|
else
|
|
{
|
|
throw new ezcBaseFileNotFoundException( $file );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks for dependencies on PHP versions or extensions
|
|
*
|
|
* The function as called by the $component component checks for the $type
|
|
* dependency. The dependency $type is compared against the $value. The
|
|
* function aborts the script if the dependency is not matched.
|
|
*
|
|
* @param string $component
|
|
* @param int $type
|
|
* @param mixed $value
|
|
*/
|
|
public static function checkDependency( $component, $type, $value )
|
|
{
|
|
switch ( $type )
|
|
{
|
|
case self::DEP_PHP_EXTENSION:
|
|
if ( extension_loaded( $value ) )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Can not be tested as it would abort the PHP script.
|
|
die( "\nThe {$component} component depends on the default PHP extension '{$value}', which is not loaded.\n" );
|
|
}
|
|
break;
|
|
|
|
case self::DEP_PHP_VERSION:
|
|
$phpVersion = phpversion();
|
|
if ( version_compare( $phpVersion, $value, '>=' ) )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
// Can not be tested as it would abort the PHP script.
|
|
die( "\nThe {$component} component depends on the PHP version '{$value}', but the current version is '{$phpVersion}'.\n" );
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the list of directories that contain class repositories.
|
|
*
|
|
* The path to the eZ components directory is always included in the result
|
|
* array. Each element in the returned array has the format of:
|
|
* packageDirectory => ezcBaseRepositoryDirectory
|
|
*
|
|
* @return array(string=>ezcBaseRepositoryDirectory)
|
|
*/
|
|
public static function getRepositoryDirectories()
|
|
{
|
|
$autoloadDirs = array();
|
|
ezcBase::setPackageDir();
|
|
$repositoryDir = self::$currentWorkingDirectory ? self::$currentWorkingDirectory : ( realpath( dirname( __FILE__ ) . '/../../' ) );
|
|
$autoloadDirs['ezc'] = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_INTERNAL, $repositoryDir, $repositoryDir . "/autoload" );
|
|
|
|
foreach ( ezcBase::$repositoryDirs as $extraDirKey => $extraDirArray )
|
|
{
|
|
$repositoryDirectory = new ezcBaseRepositoryDirectory( ezcBaseRepositoryDirectory::TYPE_EXTERNAL, realpath( $extraDirArray['basePath'] ), realpath( $extraDirArray['autoloadDirPath'] ) );
|
|
$autoloadDirs[$extraDirKey] = $repositoryDirectory;
|
|
}
|
|
|
|
return $autoloadDirs;
|
|
}
|
|
|
|
/**
|
|
* Adds an additional class repository.
|
|
*
|
|
* Used for adding class repositoryies outside the eZ components to be
|
|
* loaded by the autoload system.
|
|
*
|
|
* This function takes two arguments: $basePath is the base path for the
|
|
* whole class repository and $autoloadDirPath the path where autoload
|
|
* files for this repository are found. The paths in the autoload files are
|
|
* relative to the package directory as specified by the $basePath
|
|
* argument. I.e. class definition file will be searched at location
|
|
* $basePath + path to the class definition file as stored in the autoload
|
|
* file.
|
|
*
|
|
* addClassRepository() should be called somewhere in code before external classes
|
|
* are used.
|
|
*
|
|
* Example:
|
|
* Take the following facts:
|
|
* <ul>
|
|
* <li>there is a class repository stored in the directory "./repos"</li>
|
|
* <li>autoload files for that repository are stored in "./repos/autoloads"</li>
|
|
* <li>there are two components in this repository: "Me" and "You"</li>
|
|
* <li>the "Me" component has the classes "erMyClass1" and "erMyClass2"</li>
|
|
* <li>the "You" component has the classes "erYourClass1" and "erYourClass2"</li>
|
|
* </ul>
|
|
*
|
|
* In this case you would need to create the following files in
|
|
* "./repos/autoloads". Please note that the part before _autoload.php in
|
|
* the filename is the first part of the <b>classname</b>, not considering
|
|
* the all lower-case letter prefix.
|
|
*
|
|
* "my_autoload.php":
|
|
* <code>
|
|
* <?php
|
|
* return array (
|
|
* 'erMyClass1' => 'Me/myclass1.php',
|
|
* 'erMyClass2' => 'Me/myclass2.php',
|
|
* );
|
|
* ?>
|
|
* </code>
|
|
*
|
|
* "your_autoload.php":
|
|
* <code>
|
|
* <?php
|
|
* return array (
|
|
* 'erYourClass1' => 'You/yourclass1.php',
|
|
* 'erYourClass2' => 'You/yourclass2.php',
|
|
* );
|
|
* ?>
|
|
* </code>
|
|
*
|
|
* The directory structure for the external repository is then:
|
|
* <code>
|
|
* ./repos/autoloads/my_autoload.php
|
|
* ./repos/autoloads/you_autoload.php
|
|
* ./repos/Me/myclass1.php
|
|
* ./repos/Me/myclass2.php
|
|
* ./repos/You/yourclass1.php
|
|
* ./repos/You/yourclass2.php
|
|
* </code>
|
|
*
|
|
* To use this repository with the autoload mechanism you have to use the
|
|
* following code:
|
|
* <code>
|
|
* <?php
|
|
* ezcBase::addClassRepository( './repos', './repos/autoloads' );
|
|
* $myVar = new erMyClass2();
|
|
* ?>
|
|
* </code>
|
|
*
|
|
* @throws ezcBaseFileNotFoundException if $autoloadDirPath or $basePath do not exist.
|
|
* @param string $basePath
|
|
* @param string $autoloadDirPath
|
|
* @param string $prefix
|
|
*/
|
|
public static function addClassRepository( $basePath, $autoloadDirPath = null, $prefix = null )
|
|
{
|
|
// check if base path exists
|
|
if ( !is_dir( $basePath ) )
|
|
{
|
|
throw new ezcBaseFileNotFoundException( $basePath, 'base directory' );
|
|
}
|
|
|
|
// calculate autoload path if it wasn't given
|
|
if ( is_null( $autoloadDirPath ) )
|
|
{
|
|
$autoloadDirPath = $basePath . '/autoload';
|
|
}
|
|
|
|
|
|
// check if autoload dir exists
|
|
if ( !is_dir( $autoloadDirPath ) )
|
|
{
|
|
throw new ezcBaseFileNotFoundException( $autoloadDirPath, 'autoload directory' );
|
|
}
|
|
|
|
// add info to $repositoryDirs
|
|
if ( $prefix === null )
|
|
{
|
|
$array = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
|
|
|
|
// add info to the list of extra dirs
|
|
ezcBase::$repositoryDirs[] = $array;
|
|
}
|
|
else
|
|
{
|
|
if ( array_key_exists( $prefix, ezcBase::$repositoryDirs ) )
|
|
{
|
|
throw new ezcBaseDoubleClassRepositoryPrefixException( $prefix, $basePath, $autoloadDirPath );
|
|
}
|
|
|
|
// add info to the list of extra dirs, and use the prefix to identify the new repository.
|
|
ezcBase::$repositoryDirs[$prefix] = array( 'basePath' => $basePath, 'autoloadDirPath' => $autoloadDirPath );
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the base path of the Zeta Components installation
|
|
*
|
|
* This method returns the base path, including a trailing directory
|
|
* separator.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getInstallationPath()
|
|
{
|
|
self::setPackageDir();
|
|
|
|
$path = realpath( self::$packageDir );
|
|
if ( substr( $path, -1 ) !== DIRECTORY_SEPARATOR )
|
|
{
|
|
$path .= DIRECTORY_SEPARATOR;
|
|
}
|
|
return $path;
|
|
}
|
|
|
|
/**
|
|
* Sets the development mode to the one specified.
|
|
*
|
|
* @param int $runMode
|
|
*/
|
|
public static function setRunMode( $runMode )
|
|
{
|
|
if ( !in_array( $runMode, array( ezcBase::MODE_PRODUCTION, ezcBase::MODE_DEVELOPMENT ) ) )
|
|
{
|
|
throw new ezcBaseValueException( 'runMode', $runMode, 'ezcBase::MODE_PRODUCTION or ezcBase::MODE_DEVELOPMENT' );
|
|
}
|
|
|
|
self::$runMode = $runMode;
|
|
}
|
|
|
|
/**
|
|
* Returns the current development mode.
|
|
*
|
|
* @return int
|
|
*/
|
|
public static function getRunMode()
|
|
{
|
|
return self::$runMode;
|
|
}
|
|
|
|
/**
|
|
* Returns true when we are in development mode.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public static function inDevMode()
|
|
{
|
|
return self::$runMode == ezcBase::MODE_DEVELOPMENT;
|
|
}
|
|
|
|
/**
|
|
* Returns the installation method
|
|
*
|
|
* Possible return values are 'custom', 'devel', 'tarball' and 'pear'. Only
|
|
* 'tarball' and 'pear' are returned for user-installed versions.
|
|
*
|
|
* @return string
|
|
*/
|
|
public static function getInstallMethod()
|
|
{
|
|
return self::$libraryMode;
|
|
}
|
|
}
|
|
?>
|