Location: PHPKode > scripts > Class Manager > class-manager/classmanager/ClassManager.php
<?php

require_once('ClassManagerException.php');

class ClassManager {	

	private static $autoCheckClassFilesExist = false;
	private static $defaultClassFileNameTemplate = '{className}.php';
	
	private static $instance = null;
	private $paths;
	private $lookupPaths;
	private $recursiveLookupPaths;
	
	private $basePath;
	private $rootPath;

	private function __construct() {
		$this->paths = array();
		$this->lookupPaths = array();
		$this->recursiveLookupPaths = array();
	}
	
	public function loadDefaultXml($rootPath = null) {
		$this->loadXml(dirname(__FILE__).'/classpath.xml', $rootPath);
	}
	
	public static function instance() {
		if(is_null(self::$instance)) {
			self::$instance = new ClassManager();
			spl_autoload_register(array(self::$instance, 'autoLoad'));
		}
		return self::$instance;	
	}
	
    public function getPath($className) {
    	if(!array_key_exists($className, $this->paths)) 
			throw new ClassManagerException("Unknown class: " + $className);
    	return $this->paths[$className];	
    }
   
    public function addPath($className, $filePath) {
    	if(array_key_exists($className, $this->paths))
    		throw new ClassManagerException('Class already defined: ' . $className);
		if(ClassManager::$autoCheckClassFilesExist) 
			$this->validateFileExists($filePath, $className);
   		$this->paths[$className] = $filePath;
    }
	
	public function addLookupPath($lookupPath, $classFileNameTemplate = null) {
		$this->addAnyLookupPath($lookupPath, $classFileNameTemplate, $this->lookupPaths);
	}

	public function addRecursiveLookupPath($lookupPath, $classFileNameTemplate = null) {
		$this->addAnyLookupPath($lookupPath, $classFileNameTemplate, $this->recursiveLookupPaths);
	}
	
	private function addAnyLookupPath($lookupPath, $classFileNameTemplate = null, &$lookupArr) {
		$lookupPath = $this->preparePath($lookupPath);
		if(!array_key_exists($lookupPath, $lookupArr)) {
			if(is_null($classFileNameTemplate)) {
				$lookupArr[$lookupPath] = self::$defaultClassFileNameTemplate;
			} else {
				if(strpos($classFileNameTemplate, '{className}')===false)
					throw new ClassManagerException('File name template doesnt contain {className} tag for lookup path: ' . $lookupPath);
				$lookupArr[$lookupPath] = $classFileNameTemplate;
			}
		} else {
			throw new ClassManagerException('Lookup path can\'t be defined twice: ' . $lookupPath);
		}
	}
    
    public function loadXml($fileName, $rootPath = null) {
		$this->rootPath = ($rootPath !== null) ? $this->preparePath($rootPath) : '';
		if(!file_exists($fileName))
			throw new ClassManagerException('Configuration XML not found: ' . $fileName);
    	$classDoc = $this->initDocument($fileName);
		$repositories = $classDoc->getElementsByTagName("repository");
		foreach($repositories as $repository) {
			$this->loadRepository($repository);
		}
    }
	
	public function checkFilesExist() {
		foreach($this->paths as $className => $filePath) {
			$this->validateFileExists($filePath, $className);
		}
	}
	
	public function requireAll() {
		foreach($this->paths as $className => $filePath) {
			$this->validateFileExists($filePath, $className);
			require_once($filePath);
		}
	}
	
	public function autoLoad($className) {
		$path = null;
		if(!array_key_exists($className, $this->paths)) {
			$path = $this->findPathFromLookups($className);
			if($path == null) $path = $this->findPathFromRecursiveLookups($className);
		} else {
			$path = $this->paths[$className];
		}
		if($path != null) {
			require_once($path);
		}
		else
			throw new ClassManagerException("Unknown class: " + $className);
	}
	
	private function findPathFromLookups($className) {
		$path = null;
		foreach($this->lookupPaths as $lookupPath => $classFileNameTemplate) {
			$pathToLook = $lookupPath . $this->prepareClassFileName($className , $classFileNameTemplate);
			if(file_exists($pathToLook)) {
				$path = $pathToLook;
				break;
			}
		}
		return $path;
	}
	
	private function findPathFromRecursiveLookups($className) {
		$path = null;
		foreach($this->recursiveLookupPaths as $lookupPath => $classFileNameTemplate) {
			$path = $this->findPathFromRecursiveLookup($className, $lookupPath, $classFileNameTemplate);
			if($path != null) {
				break;
			}
		}
		return $path;
	}
	
	private function findPathFromRecursiveLookup($className, $lookupPath, $classFileNameTemplate) {
		$path = null;
		$pathToLook = $lookupPath . $this->prepareClassFileName($className , $classFileNameTemplate);
		if(file_exists($pathToLook)) {
			$path = $pathToLook;
		}
		else if(is_dir($lookupPath)) {
			if($handle = opendir($lookupPath)) {
				while($path === null && $file = readdir($handle)) {
					if((strpos($file, '.') === false))
						$path = $this->findPathFromRecursiveLookup($className, $lookupPath . $file . '/', $classFileNameTemplate);
				}
				closedir($handle);
			}
		}
		return $path;
	}
	
	private function prepareClassFileName($className, $classFileNameTemplate) {
		return str_replace('{className}', $className, $classFileNameTemplate);
	}
	
	private function validateFileExists($filePath, $className) {
		if(!file_exists($filePath))
			throw new ClassManagerException('File doesn\'t exist: ' . $filePath . ' for class: ' . $className);
	}
	
	private function loadRepository(&$repository) {
		$this->loadConfig($repository);
		$this->loadClasses($repository);
	}
	
	private function loadConfig(&$repository) {
		if(!$repository->hasAttribute('basePath'))
			throw new ClassManagerException('Base path not specified for repository');
		$this->basePath = $repository->getAttribute('basePath');
		if($repository->getAttribute('autoLookup') == 'true') {	
			$template = $repository->hasAttribute('lookupTemplate') ? $repository->getAttribute('lookupTemplate') : null;
			if($repository->getAttribute('recursive') == 'true')
				$this->addRecursiveLookupPath($this->rootPath.$this->basePath, $template);
			else
				$this->addLookupPath($this->rootPath.$this->basePath, $template);
		}
	}

	private function readConfigTag(&$repository) {
		$configs = $repository->getElementsByTagName("config");
		if(count($configs) != 1)
			$this->validateXmlError('config tag missing');
		$config = $configs->item(0);
		return $config;
	}
	
	private function readBasePath(&$config) {
		if(!$config->hasAttribute('basePath'))
			$this->validateXmlError('base path not specified in config');
		return $config->getAttribute('basePath');
	}
	
	private function loadClasses(&$repository) {
		$classes = $repository->getElementsByTagName("class");
		foreach($classes as $class) {
			$this->loadClass($class);
		}
	}
	
	private function loadClass(&$class) {
		$className = $class->getAttribute('name');
		$path = $class->getAttribute('path');
		$this->validateClass($class);
		$this->addClass($class);
	}
	
	private function validateClass(&$class) {
		if($class->getAttribute('name') == null) $this->validateXmlError('name of class must be specified');
		if($class->getAttribute('path') == null) $this->validateXmlError('path must be specified for class: ' . $class->getAttribute('name'));
	}
	
	private function validateXmlError($msg) {
		throw new ClassManagerException('Error in classpath XML: '.$msg);
	}
	
	private function addClass($class) {
		$this->addPath($class->getAttribute('name'), $this->rootPath.$this->basePath.$class->getAttribute('path'));
	}
	
	private function initDocument($fileName) {
		$classDoc = new DOMDocument();
		$classDoc->validateOnParse = true;
		if(!@$classDoc->load($fileName)) {
			$errors = error_get_last();
			throw new ClassManagerException('Classpath xml parsing failed: ' . $errors['message']);
		}
		return $classDoc;
	}
	
	public function generatePhpClassPathDump() {
		$dumpStr = "";
		$dumpStr .= '$cm = ClassManager::instance();'."\n";
		foreach($this->paths as $className => $filePath)
			$dumpStr .= '$cm->addPath("'.$className.'", "'.$filePath.'");'."\n";
		foreach($this->lookupPaths as $lookupPath => $classFileNameTemplate)
			$dumpStr .= '$cm->addLookupPath("'.$lookupPath.'", "'.$classFileNameTemplate.'");'."\n";
		foreach($this->recursiveLookupPaths as $lookupPath => $classFileNameTemplate) 
			$dumpStr .= '$cm->addRecursiveLookupPath("'.$lookupPath.'", "'.$classFileNameTemplate.'");'."\n";
		return $dumpStr;
	}
	
	private function preparePath($path) {
		$path = $path === null ? "" : trim($path);
		$length = strlen($path);
		if($length > 0) {
			$lastChar = substr($path, $length-1, 1);
			$path = ($lastChar == '/' || $lastChar == '\\') ? $path : $path.'/';
		} else {
			$path = "/";
		}
		return $path;
	}
}

?>
Return current item: Class Manager