|
Size: | 11652 |
Storage flags: |
<?php
//=============================================================================
//
// Copyright Francois Laupretre <automap@tekwire.net>
//
// Licensed 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.
//
//=============================================================================
/**
* @copyright Francois Laupretre <automap@tekwire.net>
* @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, V 2.0
* @category Automap
* @package Automap
*///==========================================================================
//=============================================================================
/**
* The Automap (PHP) manager.
*
* Methods use map IDs. A map ID is a non null positive number, uniquely
* identifying a loaded map.
*
* Static-only
* API status: Public
* Included in the PHK PHP runtime: Yes
* Implemented in the extension: Yes
*///==========================================================================
namespace Automap {
if (!class_exists('Automap\Mgr',false))
{
class Mgr
{
/** Symbol types */
const T_FUNCTION='F';
const T_CONSTANT='C';
const T_CLASS='L';
const T_EXTENSION='E';
/** Target types */
const F_SCRIPT='S';
const F_EXTENSION='X';
const F_PACKAGE='P';
/* Load flags */
/** Autoloader ignores maps loaded with this flag */
const NO_AUTOLOAD=1;
/** Check CRC */
const CRC_CHECK=2;
/** Load is done by the PECL extension - Reserved for internal use */
const PECL_LOAD=4;
/** @var array Fixed value array containing a readable string for each
* symbol/target type
*/
private static $type_strings=array(
self::T_FUNCTION => 'function',
self::T_CONSTANT => 'constant',
self::T_CLASS => 'class',
self::T_EXTENSION => 'extension',
self::F_SCRIPT => 'script',
self::F_EXTENSION => 'extension file',
self::F_PACKAGE => 'package'
);
/** @var array(callables) Registered failure handlers */
private static $failureHandlers=array();
/** @var array(callables) Registered success handlers */
private static $successHandlers=array();
/** @var bool Whether the PHP engine is able to autoload constants */
private static $supportConstantAutoload; //
/** @var bool Whether the PHP engine is able to autoload functions */
private static $supportFunctionAutoload; //
/** @var array(<map ID> => <\Automap\Map>) Array of active maps */
private static $maps=array();
/** @var integer The map ID of the next map load */
private static $load_index=1;
//================== Map manager (static methods) =======================
//--------------
/**
* Undocumented - Internal use only
*/
public static function init()
{
// Determines if function/constant autoloading is supported
$f=new \ReflectionFunction('function_exists');
self::$supportFunctionAutoload=($f->getNumberOfParameters()==2);
$f=new \ReflectionFunction('defined');
self::$supportConstantAutoload=($f->getNumberOfParameters()==2);
}
//=============== User handlers ===============
/**
* Register a failure handler
*
* Once registered, the failure handler is called each time a symbol resolution
* fails.
*
* There is no limit on the number of failure handlers that can be registered.
*
* Handlers cannot be unregistered.
*
* @param callable $callable
* @return null
*/
public static function registerFailureHandler($callable)
{
self::$failureHandlers[]=$callable;
}
//--------------
/**
* Call every registered failure handlers
*
* Call provides two arguments : the symbol type (one of the 'T_' constants)
* and the symbol name.
*
* Handlers are called in registration order.
*
* @param string $type one of the 'T_' constants
* @param string $name The symbol name
* @return null
*/
private static function callFailureHandlers($type,$name)
{
foreach (self::$failureHandlers as $callable) $callable($type,$name);
}
//--------------
/**
* Register a success handler
*
* Once registered, the success handler is called each time a symbol resolution
* succeeds.
*
* The success handler receives two arguments : An array as returned by the
* getSymbol() method, and the ID of the map where the symbol was found.
*
* There is no limit on the number of success handlers that can be registered.
*
* Handlers cannot be unregistered.
*
* @param callable $callable
* @return null
*/
public static function registerSuccessHandler($callable)
{
self::$successHandlers[]=$callable;
}
//---
private static function callSuccessHandlers($entry,$id)
{
foreach (self::$successHandlers as $callable)
$callable($entry,$id);
}
//-------- Key management -----------
/**
* Combines a type and a symbol in a 'key'
*
* Starting with version 3.0, Automap is fully case-sensitive. This allows for
* higher performance and cleaner code.
*
* Do not use: access reserved for Automap classes
*
* @param string $type one of the 'T_' constants
* @param string $name The symbol value (case sensitive)
* @return string Symbol key
*/
public static function key($type,$name)
{
return $type.trim($name,'\\');
}
//---------
public static function typeToString($type)
{
if (!isset(self::$type_strings[$type]))
throw new \Exception("$type: Invalid type");
return self::$type_strings[$type];
}
//---------
public static function stringToType($string)
{
$type=array_search($string,self::$type_strings,true);
if ($type===false) throw new \Exception("$type: Invalid type");
return $type;
}
//-------- Map loading/unloading -----------
/**
* Checks if a map ID is active (if it corresponds to a loaded map)
*
* @param integer $id ID to check
* @return boolean
*/
public static function isActiveID($id)
{
return isset(self::$maps[$id]);
}
//-----
/**
* Same as isActiveID() but throws an exception if the map ID is invalid
*
* Returns the map ID so that it can be embedded in a call string.
*
* @param integer $id ID to check
* @return integer ID (not modified)
* @throws \Exception if the ID is invalid (not loaded)
*/
private static function validate($id)
{
if (!self::isActiveID($id)) throw new \Exception($id.': Invalid map ID');
return $id;
}
//-----
/**
* Returns the \Automap\Map object corresponding to an active map ID
*
* @param string $id The map ID
* @return \Automap\Map instance
* @throws \Exception if map ID is invalid
*/
public static function map($id)
{
self::validate($id);
return self::$maps[$id];
}
//-----
/**
* Returns the list of currently active IDs.
*
* @return array
*/
public static function activeIDs()
{
return array_keys(self::$maps);
}
//---------
/**
* Loads a map file and returns its ID.
*
* @param string $path The path of the map file to load
* @param integer $flags Load flags
* @param string $_bp Reserved for internal operations. Never set this param.
* @return int the map ID
*/
public static function load($path,$flags=0,$_bp=null)
{
$map=new \Automap\Map($path,$flags,$_bp);
$id=self::$load_index++;
self::$maps[$id]=$map;
// \Phool\Display::info("Loaded $path as ID $id");//TRACE
return $id;
}
//---------------------------------
/**
* Unloads a map
*
* We dont use __destruct because :
* 1. We don't want this to be called on script shutdown
* 2. Exceptions cannot be caught when sent from a destructor.
*
* If the input ID is invalid, it is silently ignored.
*
* @param string $id The map ID to unload
* @return null
*/
public static function unload($id)
{
self::validate($id);
unset(self::$maps[$id]);
// \Phool\Display::info("Unloaded ID $id");//TRACE
}
//---------------------------------
public static function usingAccelerator()
{
return false;
}
//-------- Symbol resolution -----------
private static function symbolIsDefined($type,$name)
{
switch($type)
{
case self::T_CONSTANT: return (self::$supportConstantAutoload ?
defined($name,false) : defined($name));
case self::T_FUNCTION: return (self::$supportFunctionAutoload ?
function_exists($name,false) : function_exists($name));
case self::T_CLASS: return class_exists($name,false)
|| interface_exists($name,false)
|| (function_exists('trait_exists') && trait_exists($name,false));
case self::T_EXTENSION: return extension_loaded($name);
}
}
//---------
/**
* The autoload handler
*
* Reserved for internal use
*
* @param string $name Symbol name
* @param string Symbol type. One of the T_xxx constants. The default type is 'class',
* and cannot be anything else as long as PHP does not support function/constant
* autoloading.
*/
public static function autoloadHook($name,$type=self::T_CLASS)
{
self::resolve($type,$name,true,false);
}
//---------
/**
* Resolve a symbol
*
* , i.e. load what needs to be loaded for the symbol to be
* defined.
*
* In order to optimize the PHK case, maps are searched in reverse order
* (newest first).
*
* Warning: Autoload mechanism is not reentrant. This function cannot reference
* an unknow class (like \Phool\Display).
*
* @param string $type Symbol type
* @param string $name Symbol name
* @param bool $autoloading Whether this was called by the PHP autoloader
* @param bool $exception Whether we must throw an exception if the resolution fails
* @return true on success / false if unable to resolve symbol
* @throw \Exception
*/
private static function resolve($type,$name,$autoloading=false
,$exception=false)
{
//echo "Resolving $type$name\n";//TRACE
if ((!$autoloading)&&(self::symbolIsDefined($type,$name))) return true;
foreach(array_reverse(self::$maps,true) as $id => $map)
{
if (($entry=$map->resolve($type,$name,$id))===false) continue;
//echo "Symbol $name was resolved from ID $id\n";
self::callSuccessHandlers($entry,$id);
return true;
}
// Failure
self::callFailureHandlers($type,$name);
if ($exception) throw new \Exception('Automap: Unknown '
.self::typeToString($type).': '.$name);
return false;
}
//---------
// Methods for explicit resolutions
public static function getFunction($name)
{ return self::resolve(self::T_FUNCTION,$name,false,false); }
public static function getConstant($name)
{ return self::resolve(self::T_CONSTANT,$name,false,false); }
public static function getClass($name)
{ return self::resolve(self::T_CLASS,$name,false,false); }
public static function getExtension($name)
{ return self::resolve(self::T_EXTENSION,$name,false,false); }
//---------
public static function requireFunction($name)
{ return self::resolve(self::T_FUNCTION,$name,false,true); }
public static function requireConstant($name)
{ return self::resolve(self::T_CONSTANT,$name,false,true); }
public static function requireClass($name)
{ return self::resolve(self::T_CLASS,$name,false,true); }
public static function requireExtension($name)
{ return self::resolve(self::T_EXTENSION,$name,false,true); }
//---
} // End of class
//===========================================================================
// Registers the automap callback (append)
if (!defined('_AUTOMAP_DISABLE_REGISTER'))
{
if (!extension_loaded('spl'))
throw new \Exception("Automap requires the SPL extension");
spl_autoload_register('\Automap\Mgr::autoloadHook');
}
Mgr::init();
//---
} // End of class_exists
//===========================================================================
} // End of namespace
//===========================================================================
?>