Location: PHPKode > projects > Recess PHP Framework > recess/recess/lang/Library.class.php
<?php
require_once($_ENV['dir.recess'] . 'recess/diagnostics/Diagnostics.class.php');
require_once($_ENV['dir.recess'] . 'recess/cache/Cache.class.php');

/**
 * Library is an important low level utility in Recess used to make importing class files
 * less painful. Library is also an important level of indirection between PHP's native
 * include/require functions. Library is how Recess dynamically 'compiles' all classes
 * into a single PHP file.
 * 
 * @author Kris Jordan <hide@address.com>
 * @copyright 2008, 2009 Kris Jordan
 * @package Recess PHP Framework
 * @license MIT
 * @link http://www.recessframework.org/
 * 
 * @todo Allow framework to register packages/shortcuts? i.e.: Library::import('recess.framework.models.Model') vs. Library::import('recess','Model')
 */
abstract class Library {
	static private $classesByClass = array();	// = array( 'Inflector' => array( 'recess.lang.Inflector', 0 );
	static private $classesByFull = array();	// 'recess.lang.Inflector'
	static private $dirtyClasses = false;
	
	static private $paths = array();
	static private $dirtyPaths = false;
	
	static private $loaded = array('Libary' => true);
	
	const dotSeparator = '.';
	const pathSeparator = '/';
	
	const CLASSES_X_CLASS_CACHE_KEY = 'Recess::*::Library::$classesByClass';
	const CLASSES_X_FULL_CACHE_KEY = 'Recess::*::Library::$classesByFull';
	const PATHS_CACHE_KEY = 'Recess::*::Library::$paths';
	const NAMED_RUNS_PATH = '';
	const PHP_EXTENSION = '.php';
	const CLASS_FILE_EXTENSION = '.class.php';
	const CLASS_FILE_EXTENSION_LENGTH = 10;
	
	const NAME = 0;
	const PATH = 1;
	
	static public $useNamedRuns = false;
	static private $namedRun;
	static private $namedRuns = array();
	static private $inNamedRunImport = false;
	static private $namedRunFileSize = 0;
	
	static function beginNamedRun($name) {
		if(!self::$useNamedRuns || !isset($_ENV['dir.temp'])) return;
		
		self::$namedRun = $name;
		if(!isset(self::$namedRuns[$name])) {
			self::$namedRuns[$name] = array();
		} else {
			if(!is_array(self::$namedRuns[$name])) {
				self::$namedRuns[$name] = array();
			}
		}
		
		$namedRunFile = $_ENV['dir.temp'] . self::NAMED_RUNS_PATH . $name . self::PHP_EXTENSION;
		if(file_exists($namedRunFile)) {
			self::$inNamedRunImport = true;
			include($namedRunFile);
			self::$namedRunFileSize = filesize($namedRunFile);
			self::$inNamedRunImport = false;
		}
	}
	
	static function namedRunMissed($class) {
		if($class != 'Smarty')
			self::$namedRuns[self::$namedRun][] = $class;
	}
	
	static function persistNamedRuns() {	
		if(!isset($_ENV['dir.temp'])) return;
		$tempDir = $_ENV['dir.temp'];
		foreach(self::$namedRuns as $namedRun => $missedClasses) {
			$namedRunDir = $_ENV['dir.temp'] . self::NAMED_RUNS_PATH;
			$namedRunFile = $namedRunDir . $namedRun . self::PHP_EXTENSION;
			
			if(!empty($missedClasses)) {
				$tempNamedRunFile = '';
				$tries = 0;
				do {
					$tempNamedRunFile = $namedRunFile . '.' . rand(0,32768);	
				} while(file_exists($tempNamedRunFile));
					
				if(file_exists($namedRunFile)) {
					copy($namedRunFile, $tempNamedRunFile);
					$file = fopen($tempNamedRunFile,'a');
					if(filesize($tempNamedRunFile) !== self::$namedRunFileSize) {
						fclose($file);
						unlink($tempNamedRunFile);
						return;
					}
				} else {
					if(!file_exists($namedRunDir)) {
						mkdir($tempNamedRunFile);
					}
					$file = fopen($tempNamedRunFile,'w');
				}
			
				foreach($missedClasses as $class) {
					$classInfo = self::$classesByClass[$class];
					$fullName = $classInfo[self::NAME];
					$path = self::$paths[$classInfo[self::PATH]];
					$fileName = str_replace(self::dotSeparator,self::pathSeparator, $fullName) . self::CLASS_FILE_EXTENSION;
					$classFile = $path . $fileName;
					$code = rtrim(file_get_contents($classFile));
					if(substr($code,-2)!='?'.'>') $code .= ('?'.'>');
					fwrite($file, $code);
				}
				
				fclose($file);
				
				copy($tempNamedRunFile, $namedRunFile);
				
				unlink($tempNamedRunFile);
			}
		}
	}
	
	static function init() {		
		$paths = Cache::get(self::PATHS_CACHE_KEY);
		if($paths !== false) self::$paths = $paths;
		
		$classesByClass = Cache::get(self::CLASSES_X_CLASS_CACHE_KEY);
		if($classesByClass !== false) self::$classesByClass = $classesByClass;
		
		$classesByFull = Cache::get(self::CLASSES_X_FULL_CACHE_KEY);
		if($classesByFull !== false) self::$classesByFull = $classesByFull;
		
		self::$dirtyPaths = false;
		self::$dirtyClasses = false;

	}
	
	static function shutdown() {

		if(self::$dirtyPaths) {
			Cache::set(self::PATHS_CACHE_KEY, self::$paths);
		}
		
		if(self::$dirtyClasses) {
			Cache::set(self::CLASSES_X_CLASS_CACHE_KEY, self::$classesByClass);
			Cache::set(self::CLASSES_X_FULL_CACHE_KEY, self::$classesByFull);
		}

		self::persistNamedRuns();
	}
	
	static function addClassPath($newPath) {
		if(!in_array($newPath, self::$paths)) {
			self::$paths[] = $newPath;
			self::$dirtyPaths = true;
		}
	}
	
	static function addClass($fullName) {
		$className = self::getClassName($fullName);
		self::$classesByFull[$fullName] = $className;
		self::$classesByClass[$className] = array( $fullName, -1 );
	}
	
	static function import($fullName, $forceLoad = false) {
		if(!isset(self::$classesByFull[$fullName])) {
			self::addClass($fullName);
		}
		
		$className = self::$classesByFull[$fullName];
	
		if(!isset(self::$loaded[$className])) {
			self::$loaded[$className] = false;
		}
		
		if($forceLoad) {
			self::load($className);
		}
	}
	
	static function importAndInstantiate($fullName) {
		// TODO: REDUCE DUPLICATION WITH IMPORT
		if(isset(self::$classesByFull[$fullName])) {
			$className = self::$classesByFull[$fullName];
		} else {
			$className = self::getClassName($fullName);
			self::addClass($fullName, $className);
		}
	
		if(!isset(self::$loaded[$className])) {
			self::$loaded[$className] = false;
		}
		
		return new $className;
	}
	
	static function classExists($fullName) {
		if(!isset(self::$classesByFull[$fullName])) {
			self::addClass($fullName);
		}
		
		$class = self::$classesByFull[$fullName];
		if(class_exists($class, false) || interface_exists($class, false)) return true;
		
		$pathIndex = self::$classesByClass[$class][self::PATH];
		$file = str_replace(self::dotSeparator,self::pathSeparator, $fullName) . self::CLASS_FILE_EXTENSION;
		
		if($pathIndex == -1) {
			for($index = count(self::$paths) - 1; $index >= 0 ; $index--) {
				$path = self::$paths[$index];
				if(file_exists($path . $file)) {
					include($path . $file);
					if(class_exists($class, false) || interface_exists($class, false)) {
						if(isset(self::$namedRun)) { self::namedRunMissed($class); }
						self::$dirtyClasses = true;
						self::$classesByClass[$class][self::PATH] = $index;
						self::$loaded[$class] = true;
						return true;
					}
				}
			}
			return false;
		} else {
			$path = self::$paths[$pathIndex] . $file;
			include_once($path);
			self::$loaded[$class] = true;
		}
	
		if(isset(self::$namedRun)) { self::namedRunMissed($class); }
		
		return class_exists($class, false) || interface_exists($class, false);
	}
	
	static function load($className) {
		if(self::$inNamedRunImport) return;
				
		if(!isset(self::$loaded[$className])) {
			throw new LibraryException($className . ' has not been imported.');
		}
		
		if(self::$loaded[$className]) { return; }
		
		// Search through paths to find requested file
		if(class_exists($className, false) || self::classExists(self::$classesByClass[$className][self::NAME])) {
			return;	
		} else {
			// Could not load the desired class
			$paths = '';
			foreach(self::$paths as $path) {
				$paths .= $path . '<br />';				
			}
			throw new LibraryException('Library cannot find class "' . self::$classesByClass[$className][self::NAME] . '". Searched in: <br />' . $paths);
		}
	}
	
	static function getClassName($fullName) {
		if(isset(self::$classesByFull[$fullName])) {
			return self::$classesByFull[$fullName];
		}
		
		$lastDotPosition = strrpos($fullName, self::dotSeparator);
		if($lastDotPosition === false) {
			self::$classesByFull[$fullName] = $fullName;
			return $fullName;
		} else {
			$className = substr($fullName, $lastDotPosition + 1);
			self::$classesByFull[$fullName] = $className;
			return $className;
		}
	}
	
	static function getPackage($className) {
		if(isset(self::$classesByClass[$className])) {
			return str_replace('.' . $className, '', self::$classesByClass[$className][self::NAME]);
		} else {
			return '';
		}
	}
	
	static function getFullyQualifiedClassName($className) {
		if(isset(self::$classesByClass[$className])) {
			return self::$classesByClass[$className][self::NAME];
		} else {
			return $className;
		}
	}
	
	static function findClassesIn($package) {
		$package = str_replace(self::dotSeparator,self::pathSeparator, $package);
		$classes = array();
		foreach(self::$paths as $path) {
			if(file_exists($path . self::pathSeparator . $package)) {
				$dir = dir($path . self::pathSeparator . $package);
				while(false !== ($entry = $dir->read())) {
					$extensionPosition = strpos($entry, self::CLASS_FILE_EXTENSION);
					if($extensionPosition === strlen($entry) - self::CLASS_FILE_EXTENSION_LENGTH) {
						$classes[] = substr($entry, 0, $extensionPosition);
					}
				}
			}
		}
		return $classes;
	}
}

function __autoload($class) {
	try {
		Library::load($class);
	} catch (Exception $exception) {
		Diagnostics::handleException($exception);
		die('Could not autoload ' . $class);
	}
}

class Make {
	static private $classes = array();
	
	static function a($class) { $args = func_get_args(); return self::makeByArray($args); }
	static function an($class) { $args = func_get_args(); return self::makeByArray($args); }
	
	static private function makeByArray($args) {
		$class = array_shift($args);
		if($class != null) {
			if(!isset(self::$classes[$class])) {
				self::$classes[$class] = new ReflectionClass($class);
			}
			return self::$classes[$class]->newInstanceArgs($args);
		}
	}
}

register_shutdown_function(array('Library','shutdown'));

class LibraryException extends ErrorException {}

?>
Return current item: Recess PHP Framework