Location: PHPKode > projects > Swat > Swat-1.4.108/Swat-1.4.108/SwatDB/SwatDBClassMap.php
<?php

require_once 'Swat/SwatObject.php';
require_once 'Swat/exceptions/SwatException.php';
require_once 'Swat/exceptions/SwatInvalidClassException.php';

/**
 * Maps class names to overridden class names
 *
 * This is a static class and should not be instantiated.
 *
 * This class also attempts to require class-definition files for mapped
 * class names. You can modify the behaviour of automatic class-definition
 * file requiring using the {@link SwatDBClassMap::addPath()} and
 * {@link SwatDBClassMap::removePath()} methods.
 *
 * @package   SwatDB
 * @copyright 2006-2007 silverorange
 * @license   http://www.gnu.org/copyleft/lesser.html LGPL License 2.1
 */
class SwatDBClassMap extends SwatObject
{
	// {{{ private properties

	/**
	 * An associative array of class-mappings
	 *
	 * The array has keys of the 'from class name' and values of the
	 * 'to class name'.
	 *
	 * @var array
	 */
	private static $map = array();

	/**
	 * Paths to search for class-definition files
	 *
	 * @var array
	 */
	private static $search_paths = array('.');

	// }}}
	// {{{ public static function add()

	/**
	 * Maps a class name to another class name
	 *
	 * Subsequent calls to {@link SwatDBClassMap::get()} using the
	 * <i>$from_class_name</i> will return the <i>$to_class_name</i>. Class
	 * names may be mapped to already mapped class names. For example:
	 *
	 * <code>
	 * SwatDBClassMap::add('Object', 'MyObject');
	 * SwatDBClassMap::add('MyObject', 'MyOtherObject');
	 * echo SwatDBClassMap::get('Object'); // MyOtherObject
	 * </code>
	 *
	 * If a circular dependency is created, an exception is thrown. If the
	 * <i>$from_class_name</i> is already mapped to another class the old
	 * mapping is overwritten.
	 *
	 * @param string $from_class_name the class name to map from.
	 * @param string $to_class_name the class name to map to. The mapped class
	 *                               must be a subclass of the
	 *                               <i>$from_class_name</i> otherwise class
	 *                               resolution using
	 *                               {@link SwatDBClassMap::get()} will throw
	 *                               an exception.
	 *
	 * @throws SwatException if the added mapping creates a circular dependency.
	 */
	public static function add($from_class_name, $to_class_name)
	{
		// check for circular dependency
		if (array_key_exists($to_class_name, self::$map)) {
			$class_name = $to_class_name;
			$child_class_names = array($class_name);
			while (array_key_exists($class_name, self::$map)) {
				$class_name = self::$map[$class_name];
				$child_class_names[] = $class_name;
			}

			if (in_array($from_class_name, $child_class_names)) {
				throw new SwatException(sprintf(
					'Circular class dependency detected: %s => %s',
					$from_class_name, implode(' => ', $child_class_names)));
			}
		}

		self::$map[$from_class_name] = $to_class_name;
	}

	// }}}
	// {{{ public static function get()

	/**
	 * Resolves a class name from the class map
	 *
	 * @param string $from_class_name the name of the class to resolve.
	 *
	 * @return string the resolved class name. If no class mapping exists for
	 *                 for the given class name, the same class name is
	 *                 returned.
	 *
	 * @throws SwatInvalidClassException if a mapped class is not a subclass of
	 *                                    its original class.
	 */
	public static function get($from_class_name)
	{
		$include_paths = explode(':', get_include_path());

		$to_class_name = $from_class_name;

		while (array_key_exists($from_class_name, self::$map)) {
			$to_class_name = self::$map[$from_class_name];

			// try to load class definition for $to_class_name
			if (!class_exists($to_class_name) &&
				count(self::$search_paths) > 0) {
				foreach (self::$search_paths as $search_path) {
					// check if search path is relative
					if ($search_path[0] == '/') {
						$filename = sprintf('%s/%s.php',
							$search_path, $to_class_name);

						if (file_exists($filename)) {
							require_once $filename;
							break;
						}
					} else {
						foreach ($include_paths as $include_path) {
							$filename = sprintf('%s/%s/%s.php',
								$include_path, $search_path, $to_class_name);

							if (file_exists($filename)) {
								require_once $filename;
								break 2;
							}
						}
					}
				}
			}

			if (!is_subclass_of($to_class_name, $from_class_name))
				throw new SwatInvalidClassException(sprintf('Invalid '.
					'class-mapping detected. %s is not a subclass of %s.',
					$to_class_name, $from_class_name));

			$from_class_name = $to_class_name;
		}

		return $to_class_name;
	}

	// }}}
	// {{{ public static function addPath()

	/**
	 * Adds a search path for class-definition files
	 *
	 * When an undefined class is resolved, the class map attempts to find
	 * and require a class-definition file for the class.
	 *
	 * All search paths are relative to the PHP include path. The empty search
	 * path ('.') is included by default.
	 *
	 * @param string $search_path the path to search for class-definition files.
	 *
	 * @see SwatDBClassMap::removePath()
	 */
	public static function addPath($search_path)
	{
		if (!in_array($search_path, self::$search_paths, true)) {
			// add path to front of array since it is more likely we will find
			// class-definitions in manually added search paths
			array_unshift(self::$search_paths, $search_path);
		}
	}

	// }}}
	// {{{ public static function removePath()

	/**
	 * Removes a search path for class-definition files
	 *
	 * @param string $search_path the path to remove.
	 *
	 * @see SwatDBClassMap::addPath()
	 */
	public static function removePath($search_path)
	{
		$index = array_search($search_path, self::$search_paths);
		if ($index !== false)
			array_splice(self::$search_paths, $index, 1);
	}

	// }}}
	// {{{ private function __construct()

	/**
	 * The class map is a static object and should not be instantiated
	 */
	private function __construct()
	{
	}

	// }}}

	// deprecated API
	// {{{ private properties

	/**
	 * Singleton instance
	 *
	 * @var SwatDBClassMap
	 *
	 * @deprecated Use static methods instead of instantiating this class.
	 */
	private static $instance = null;

	/**
	 * An associative array of class-mappings
	 *
	 * The array is of the form 'PackageClassName' => 'SiteSpecificOverrideClassName'.
	 *
	 * @var array
	 *
	 * @deprecated Use static methods instead of instantiating this class.
	 */
	private $mappings = array();

	/**
	 * The path to search for site-specific class files
	 *
	 * @var string
	 *
	 * @deprecated Use static methods instead of instantiating this class.
	 */
	private $path = null;

	// }}}
	// {{{ public static function instance()

	/**
	 * Gets the singleton instance of the class-mapping object
	 *
	 * @return SwatDBClassMap the singleton instance of the class-
	 *                                  mapping object.
	 *
	 * @deprecated Use static methods instead of instantiating this class.
	 */
	public static function instance()
	{
		if (self::$instance === null)
			self::$instance = new self();

		return self::$instance;
	}

	// }}}
	// {{{ public function addMapping()

	/**
	 * Adds a class-mapping to the class-mapping object
	 *
	 * @param string $package_class_name the name of the package class to
	 *                                    override.
	 * @param string $class_name the name of the site-specific class.
	 *
	 * @deprecated Use the static method {@link SwatDBClassMap::add()}.
	 */
	public function addMapping($package_class_name, $class_name)
	{
		$this->mappings[$package_class_name] = $class_name;
		SwatDBClassMap::add($package_class_name, $class_name);
	}

	// }}}
	// {{{ public function resolveClass()

	/**
	 * Gets the appropriate class name for a given package class name
	 *
	 * @param string $name the name of the package class to get the
	 *                      mapped name of.
	 *
	 * @return string the appropriate class name for site-specific code. If
	 *                 the site-specific code has overridden a package
	 *                 class, the site-specific overridden value is
	 *                 returned. Otherwise, the package default class
	 *                 name is returned.
	 *
	 * @deprecated Use the static method {@link SwatDBClassMap::get()}.
	 */
	public function resolveClass($name)
	{
		return SwatDBClassMap::get($name);
	}

	// }}}
	// {{{ public function setPath()

	/**
	 * Sets the path to search for site-specific class files
	 *
	 * @param string $path the path to search for site-specific class files.
	 *
	 * @deprecated Use the static methods {@link SwatDBClassMap::addPath()} and
	 *             {@link SwatDBClassMap::removePath()}.
	 */
	public function setPath($path)
	{
		$this->path = $path;
		SwatDBClassMap::addPath($path);
	}

	// }}}
}

?>
Return current item: Swat