Changeset View
Changeset View
Standalone View
Standalone View
branches/5.3.x/core/kernel/utility/factory.php
Show All 10 Lines | |||||
* or other free or open source software licenses. | * or other free or open source software licenses. | ||||
* See http://www.in-portal.org/license for copyright notices and details. | * See http://www.in-portal.org/license for copyright notices and details. | ||||
*/ | */ | ||||
use InPortal\Core\kernel\utility\ClassDiscovery\ClassMapBuilder; | use InPortal\Core\kernel\utility\ClassDiscovery\ClassMapBuilder; | ||||
defined('FULL_PATH') or die('restricted access!'); | defined('FULL_PATH') or die('restricted access!'); | ||||
class kFactory extends kBase implements kiCacheable { | class kFactory extends kBase implements kiCacheable | ||||
{ | |||||
const TYPE_CLASS = 1; | const TYPE_CLASS = 1; | ||||
const TYPE_INTERFACE = 2; | const TYPE_INTERFACE = 2; | ||||
const TYPE_TRAIT = 3; | const TYPE_TRAIT = 3; | ||||
const MODIFIER_ABSTRACT = 1; | const MODIFIER_ABSTRACT = 1; | ||||
const MODIFIER_FINAL = 2; | const MODIFIER_FINAL = 2; | ||||
/** | /** | ||||
* Mapping between class name and file name | * Mapping between class name and file name where class definition can be found. | ||||
* where class definition can be found. | * | ||||
* File path absolute! | * File path absolute! | ||||
* | * | ||||
* @var Array | * @var array | ||||
* @access protected | |||||
*/ | */ | ||||
protected $classMap = Array (); | protected $classMap = array(); | ||||
/** | /** | ||||
* Class information. | * Class information. | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $classInfo = array(); | protected $classInfo = array(); | ||||
/** | /** | ||||
* Information about class usage among other classes. | * Information about class usage among other classes. | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $classTree = array(); | protected $classTree = array(); | ||||
/** | /** | ||||
* The PSR-4 compliant namespace-to-folder mapping. | * The PSR-4 compliant namespace-to-folder mapping. | ||||
* | * | ||||
* @var array | * @var array | ||||
*/ | */ | ||||
protected $namespaceMap = array(); | protected $namespaceMap = array(); | ||||
/** | /** | ||||
* Map class names to their pseudo | * Map class names to their pseudo class names, e.g. key=pseudo_class, value=real_class_name. | ||||
* class names, e.g. key=pseudo_class, | * | ||||
* value=real_class_name | * @var array | ||||
*/ | |||||
protected $realClasses = array(); | |||||
/** | |||||
* Where all created objects are stored. | |||||
* | |||||
* @var kBase[] | |||||
*/ | |||||
protected $storage = array(); | |||||
/** | |||||
* Debug factory internal processing. | |||||
* | * | ||||
* @var Array | * @var boolean | ||||
* @access protected | |||||
*/ | */ | ||||
protected $realClasses = Array (); | private $_debugFactory = false; | ||||
/** | /** | ||||
* Where all created objects are stored | * Profile memory usage during class file inclusion. | ||||
* | * | ||||
* @var Array|kBase[] | * @var boolean | ||||
* @access protected | |||||
*/ | */ | ||||
protected $Storage = Array (); | private $_profileMemory = false; | ||||
/** | |||||
* Creates factory instance. | |||||
*/ | |||||
public function __construct() | public function __construct() | ||||
{ | { | ||||
parent::__construct(); | parent::__construct(); | ||||
spl_autoload_register(array($this, 'autoload'), true, true); | spl_autoload_register(array($this, 'autoload'), true, true); | ||||
if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) { | |||||
$this->_debugFactory = defined('DBG_FACTORY') && DBG_FACTORY; | |||||
$this->_profileMemory = defined('DBG_PROFILE_MEMORY') && DBG_PROFILE_MEMORY; | |||||
} | |||||
} | } | ||||
/** | /** | ||||
* Configures module-based autoloader. | * Configures module-based autoloader. | ||||
* | * | ||||
* @return void | * @return void | ||||
*/ | */ | ||||
public function configureAutoloader() | public function configureAutoloader() | ||||
Show All 39 Lines | |||||
$this->classTree[$extends_class][] = $class; | $this->classTree[$extends_class][] = $class; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
/** | /** | ||||
* Sets data from cache to object | * Sets data from cache to object. | ||||
* | |||||
* @param array $data Data. | |||||
* | * | ||||
* @param Array $data | |||||
* @return void | * @return void | ||||
* @access public | |||||
*/ | */ | ||||
public function setFromCache(&$data) | public function setFromCache(&$data) | ||||
{ | { | ||||
$this->classMap = $data['Factory.Files']; | $this->classMap = $data['Factory.Files']; | ||||
$this->classInfo = $data['Factory.ClassInfo']; | $this->classInfo = $data['Factory.ClassInfo']; | ||||
$this->classTree = $data['Factory.ClassTree']; | $this->classTree = $data['Factory.ClassTree']; | ||||
$this->namespaceMap = $data['Factory.Namespaces']; | $this->namespaceMap = $data['Factory.Namespaces']; | ||||
$this->realClasses = $data['Factory.realClasses']; | $this->realClasses = $data['Factory.realClasses']; | ||||
} | } | ||||
/** | /** | ||||
* Performs automatic loading of classes registered with the factory | * Performs automatic loading of classes registered with the factory. | ||||
* | |||||
* @param string $class Class. | |||||
* | * | ||||
* @param string $class | * @return boolean|null | ||||
* @return bool|null | |||||
* @access public | |||||
*/ | */ | ||||
public function autoload($class) | public function autoload($class) | ||||
{ | { | ||||
$file = $this->findFile($class); | $file = $this->findFile($class); | ||||
if ( $file ) { | if ( $file ) { | ||||
kUtil::includeOnce(FULL_PATH . $file); | kUtil::includeOnce(FULL_PATH . $file); | ||||
return true; | return true; | ||||
} | } | ||||
return null; | return null; | ||||
} | } | ||||
/** | /** | ||||
* Finds the path to the file where the class is defined. | * Finds the path to the file where the class is defined. | ||||
* | * | ||||
* @param string $class The name of the class | * @param string $class The name of the class. | ||||
* @return string|bool The path if found, false otherwise | * | ||||
* @access protected | * @return string|boolean The path if found, false otherwise. | ||||
*/ | */ | ||||
protected function findFile($class) | protected function findFile($class) | ||||
{ | { | ||||
if ( $class[0] == '\\' ) { | if ( $class[0] == '\\' ) { | ||||
$class = substr($class, 1); | $class = substr($class, 1); | ||||
} | } | ||||
if ( isset($this->classMap[$class]) ) { | if ( isset($this->classMap[$class]) ) { | ||||
return $this->classMap[$class]; | return $this->classMap[$class]; | ||||
} | } | ||||
$pos = strrpos($class, '\\'); | $pos = strrpos($class, '\\'); | ||||
if ( $pos !== false ) { | if ( $pos !== false ) { | ||||
// namespaced class name | // Namespaced class name. | ||||
$class_path = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; | $class_path = str_replace('\\', DIRECTORY_SEPARATOR, substr($class, 0, $pos)) . DIRECTORY_SEPARATOR; | ||||
$class_name = substr($class, $pos + 1); | $class_name = substr($class, $pos + 1); | ||||
} | } | ||||
else { | else { | ||||
// PEAR-like class name | // PEAR-like class name. | ||||
$class_path = null; | $class_path = null; | ||||
$class_name = $class; | $class_name = $class; | ||||
} | } | ||||
$class_path .= str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; | $class_path .= str_replace('_', DIRECTORY_SEPARATOR, $class_name) . '.php'; | ||||
foreach ( $this->namespaceMap as $namespace_prefix => $namespace_path ) { | foreach ( $this->namespaceMap as $namespace_prefix => $namespace_path ) { | ||||
if ( strpos($class, $namespace_prefix) === 0 ) { | if ( strpos($class, $namespace_prefix) === 0 ) { | ||||
$test_class_path = str_replace( | $test_class_path = str_replace( | ||||
str_replace('\\', DIRECTORY_SEPARATOR, $namespace_prefix), | str_replace('\\', DIRECTORY_SEPARATOR, $namespace_prefix), | ||||
$namespace_path, | $namespace_path, | ||||
$class_path | $class_path | ||||
); | ); | ||||
if ( file_exists(FULL_PATH . DIRECTORY_SEPARATOR . $test_class_path) ) { | if ( file_exists(FULL_PATH . DIRECTORY_SEPARATOR . $test_class_path) ) { | ||||
return DIRECTORY_SEPARATOR . $test_class_path; | return DIRECTORY_SEPARATOR . $test_class_path; | ||||
} | } | ||||
} | } | ||||
} | } | ||||
return $this->classMap[$class] = false; | return $this->classMap[$class] = false; | ||||
} | } | ||||
/** | /** | ||||
* Gets object data for caching | * Gets object data for caching. | ||||
* | * | ||||
* @return Array | * @return array | ||||
* @access public | |||||
*/ | */ | ||||
public function getToCache() | public function getToCache() | ||||
{ | { | ||||
ksort($this->classMap); | ksort($this->classMap); | ||||
ksort($this->classInfo); | ksort($this->classInfo); | ||||
ksort($this->classTree); | ksort($this->classTree); | ||||
ksort($this->namespaceMap); | ksort($this->namespaceMap); | ||||
ksort($this->realClasses); | ksort($this->realClasses); | ||||
return Array ( | return array( | ||||
'Factory.Files' => $this->classMap, | 'Factory.Files' => $this->classMap, | ||||
'Factory.ClassInfo' => $this->classInfo, | 'Factory.ClassInfo' => $this->classInfo, | ||||
'Factory.ClassTree' => $this->classTree, | 'Factory.ClassTree' => $this->classTree, | ||||
'Factory.Namespaces' => $this->namespaceMap, | 'Factory.Namespaces' => $this->namespaceMap, | ||||
'Factory.realClasses' => $this->realClasses, | 'Factory.realClasses' => $this->realClasses, | ||||
); | ); | ||||
} | } | ||||
/** | /** | ||||
* Splits any mixing of prefix and | * Splits any mixing of prefix and special into correct ones. | ||||
* special into correct ones | |||||
* | * | ||||
* @param string $prefix_special | * @param string $prefix_special Prefix-special. | ||||
* @return Array | * | ||||
* @access public | * @return array | ||||
*/ | */ | ||||
public function processPrefix($prefix_special) | public function processPrefix($prefix_special) | ||||
{ | { | ||||
// l.pick, l, m.test_TagProcessor | // Example: "l.pick", "l", "m.test_TagProcessor". | ||||
//preg_match("/(.*)\.*(.*)(_*)(.*)/", $prefix_special, $regs); | |||||
//return Array('prefix'=>$regs[1].$regs[3].$regs[4], 'special'=>$regs[2]); | |||||
$tmp = explode('_', $prefix_special, 2); | $tmp = explode('_', $prefix_special, 2); | ||||
$tmp[0] = explode('.', $tmp[0]); | $tmp[0] = explode('.', $tmp[0]); | ||||
$prefix = $tmp[0][0]; | $prefix = $tmp[0][0]; | ||||
$prefix_special = $prefix; // new1 | $prefix_special = $prefix; | ||||
if ( isset($tmp[1]) ) { | if ( isset($tmp[1]) ) { | ||||
$prefix .= '_' . $tmp[1]; | $prefix .= '_' . $tmp[1]; | ||||
} | } | ||||
$special = isset($tmp[0][1]) ? $tmp[0][1] : ''; | $special = isset($tmp[0][1]) ? $tmp[0][1] : ''; | ||||
$prefix_special .= '.' . $special; // new2 | $prefix_special .= '.' . $special; | ||||
return Array ('prefix' => $prefix, 'special' => $special, 'prefix_special' => $prefix_special); | return array('prefix' => $prefix, 'special' => $special, 'prefix_special' => $prefix_special); | ||||
} | } | ||||
/** | /** | ||||
* Returns object using params specified, creates it if is required. | * Returns object using params specified, creates it if is required. | ||||
* | * | ||||
* @param string $name Object name in factory. | * @param string $name Object name in factory. | ||||
* @param string $pseudo_class Pseudo class. | * @param string $pseudo_class Pseudo class. | ||||
* @param Array $event_params Event params. | * @param array $event_params Event params. | ||||
* @param Array $arguments Constructor arguments. | * @param array $arguments Constructor arguments. | ||||
* | * | ||||
* @return kBase | * @return kBase | ||||
*/ | */ | ||||
public function getObject($name, $pseudo_class = '', $event_params = Array (), $arguments = Array ()) | public function getObject($name, $pseudo_class = '', array $event_params = array(), array $arguments = array()) | ||||
{ | { | ||||
$name = rtrim($name, '.'); | $name = rtrim($name, '.'); | ||||
if ( isset($this->Storage[$name]) ) { | if ( isset($this->storage[$name]) ) { | ||||
return $this->Storage[$name]; | return $this->storage[$name]; | ||||
} | } | ||||
$ret = $this->processPrefix($name); | $ret = $this->processPrefix($name); | ||||
if ( !$pseudo_class ) { | if ( !$pseudo_class ) { | ||||
$pseudo_class = $ret['prefix']; | $pseudo_class = $ret['prefix']; | ||||
} | } | ||||
if ( defined('DEBUG_MODE') && defined('DBG_FACTORY') && DBG_FACTORY && $this->Application->isDebugMode() ) { | if ( $this->_debugFactory ) { | ||||
$this->Application->Debugger->appendHTML('<b>Creating object:</b> Pseudo class: ' . $pseudo_class . ' Prefix: ' . $name); | $this->Application->Debugger->appendHTML( | ||||
'<strong>Creating object:</strong> Pseudo class: ' . $pseudo_class . ' Prefix: ' . $name | |||||
); | |||||
$this->Application->Debugger->appendTrace(); | $this->Application->Debugger->appendTrace(); | ||||
} | } | ||||
$this->Storage[$name] = $this->makeClass($pseudo_class, $arguments); | $this->storage[$name] = $this->makeClass($pseudo_class, $arguments); | ||||
$this->Storage[$name]->Init($ret['prefix'], $ret['special']); | $this->storage[$name]->Init($ret['prefix'], $ret['special']); | ||||
$this->Application->EventManager->runBuildEvent($ret['prefix_special'], $pseudo_class, $event_params); | $this->Application->EventManager->runBuildEvent($ret['prefix_special'], $pseudo_class, $event_params); | ||||
return $this->Storage[$name]; | return $this->storage[$name]; | ||||
} | } | ||||
/** | /** | ||||
* Removes object from storage, so next time it could be created from scratch | * Removes object from storage, so next time it could be created from scratch. | ||||
* | |||||
* @param string $name Object's name in the Storage. | |||||
* | * | ||||
* @param string $name Object's name in the Storage | |||||
* @return void | * @return void | ||||
* @access public | |||||
*/ | */ | ||||
public function DestroyObject($name) | public function DestroyObject($name) | ||||
{ | { | ||||
unset($this->Storage[$name]); | unset($this->storage[$name]); | ||||
} | } | ||||
/** | /** | ||||
* Checks if object with prefix passes was already created in factory | * Checks if object with prefix passes was already created in factory. | ||||
* | |||||
* @param string $name Object pseudo_class, prefix. | |||||
* | * | ||||
* @param string $name object pseudo_class, prefix | * @return boolean | ||||
* @return bool | |||||
* @access public | |||||
*/ | */ | ||||
public function hasObject($name) | public function hasObject($name) | ||||
{ | { | ||||
return isset($this->Storage[$name]); | return isset($this->storage[$name]); | ||||
} | } | ||||
/** | /** | ||||
* Get's real class name for pseudo class, includes class file and creates class instance. | * Get's real class name for pseudo class, includes class file and creates class instance. | ||||
* | * | ||||
* Pattern: Factory Method | * Pattern: Factory Method | ||||
* | * | ||||
* @param string $pseudo_class Pseudo class. | * @param string $pseudo_class Pseudo class. | ||||
* @param array $arguments Constructor arguments. | * @param array $arguments Constructor arguments. | ||||
* | * | ||||
* @return kBase | * @return kBase | ||||
* @throws kFactoryException When class not found. | * @throws kFactoryException When class not found. | ||||
*/ | */ | ||||
public function makeClass($pseudo_class, $arguments = Array ()) | public function makeClass($pseudo_class, array $arguments = array()) | ||||
{ | { | ||||
if ( !isset($this->realClasses[$pseudo_class]) ) { | if ( !isset($this->realClasses[$pseudo_class]) ) { | ||||
$error_msg = 'RealClass not defined for "<strong>' . $pseudo_class . '</strong>" pseudo_class.'; | $error_msg = 'RealClass not defined for "<strong>' . $pseudo_class . '</strong>" pseudo_class.'; | ||||
$error_msg .= ' Please use "<strong>in-portal classmap:rebuild</strong>" command to discover new classes.'; | $error_msg .= ' Please use "<strong>in-portal classmap:rebuild</strong>" command to discover new classes.'; | ||||
if ( $this->Application->isInstalled() ) { | if ( $this->Application->isInstalled() ) { | ||||
throw new kFactoryException($error_msg); | throw new kFactoryException($error_msg); | ||||
} | } | ||||
Show All 11 Lines | |||||
$real_class = $this->realClasses[$pseudo_class]; | $real_class = $this->realClasses[$pseudo_class]; | ||||
$mem_before = memory_get_usage(); | $mem_before = memory_get_usage(); | ||||
$time_before = microtime(true); | $time_before = microtime(true); | ||||
$arguments = (array)$arguments; | $arguments = (array)$arguments; | ||||
if ( !$arguments ) { | if ( !$arguments ) { | ||||
$class = new $real_class(); | $object = new $real_class(); | ||||
} | } | ||||
else { | else { | ||||
$reflection = new ReflectionClass($real_class); | $reflection = new ReflectionClass($real_class); | ||||
$class = $reflection->newInstanceArgs($arguments); | $object = $reflection->newInstanceArgs($arguments); | ||||
} | } | ||||
if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_MEMORY') && DBG_PROFILE_MEMORY && $this->Application->isDebugMode() ) { | if ( $this->_profileMemory ) { | ||||
$mem_after = memory_get_usage(); | $mem_after = memory_get_usage(); | ||||
$time_after = microtime(true); | $time_after = microtime(true); | ||||
$mem_used = $mem_after - $mem_before; | $mem_used = $mem_after - $mem_before; | ||||
$time_used = $time_after - $time_before; | $mem_used_formatted = round($mem_used / 1024, 3); | ||||
$time_used = round($time_after - $time_before, 5); | |||||
$this->Application->Debugger->appendHTML('Factroy created <b>' . $real_class . '</b> - used ' . round($mem_used / 1024, 3) . 'Kb time: ' . round($time_used, 5)); | $this->Application->Debugger->appendHTML( | ||||
'Factory created <b>' . $real_class . '</b> - used ' . $mem_used_formatted . 'Kb time: ' . $time_used | |||||
); | |||||
$this->Application->Debugger->profilerAddTotal('objects', null, $mem_used); | $this->Application->Debugger->profilerAddTotal('objects', null, $mem_used); | ||||
} | } | ||||
return $class; | return $object; | ||||
} | } | ||||
/** | /** | ||||
* Returns sub-classes of given ancestor class. | * Returns sub-classes of given ancestor class. | ||||
* | * | ||||
* @param string $ancestor_class Ancestor class. | * @param string $ancestor_class Ancestor class. | ||||
* @param boolean $concrete_only Return only non-abstract classes. | * @param boolean $concrete_only Return only non-abstract classes. | ||||
* | * | ||||
▲ Show 20 Lines • Show All 48 Lines • ▼ Show 20 Line(s) | |||||
protected function classHasModifier($class, $modifier) | protected function classHasModifier($class, $modifier) | ||||
{ | { | ||||
return ($this->classInfo[$class]['modifiers'] & $modifier) == $modifier; | return ($this->classInfo[$class]['modifiers'] & $modifier) == $modifier; | ||||
} | } | ||||
/** | /** | ||||
* Registers new class in the factory | * Registers new class in the factory | ||||
* | * | ||||
* @param string $real_class Real name of class as in class declaration | * @param string $real_class Real name of class as in class declaration. | ||||
* @param string $file Filename in what $real_class is declared | * @param string $file Filename in what $real_class is declared. | ||||
* @param string $pseudo_class Name under this class object will be accessed using getObject method | * @param string $pseudo_class Name under this class object will be accessed using getObject method. | ||||
* | |||||
* @return void | * @return void | ||||
* @access public | |||||
*/ | */ | ||||
public function registerClass($real_class, $file, $pseudo_class = null) | public function registerClass($real_class, $file, $pseudo_class = null) | ||||
{ | { | ||||
if ( !isset($pseudo_class) ) { | if ( !isset($pseudo_class) ) { | ||||
$pseudo_class = $real_class; | $pseudo_class = $real_class; | ||||
} | } | ||||
if ( !isset($this->classMap[$real_class]) ) { | if ( !isset($this->classMap[$real_class]) ) { | ||||
$this->classMap[$real_class] = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $file, 1); | $this->classMap[$real_class] = preg_replace('/^' . preg_quote(FULL_PATH, '/') . '/', '', $file, 1); | ||||
} | } | ||||
$this->realClasses[$pseudo_class] = $real_class; | $this->realClasses[$pseudo_class] = $real_class; | ||||
} | } | ||||
/** | /** | ||||
* Unregisters existing class from factory | * Unregisters existing class from factory | ||||
* | * | ||||
* @param string $real_class Real name of class as in class declaration | * @param string $real_class Real name of class as in class declaration. | ||||
* @param string $pseudo_class Name under this class object is accessed using getObject method | * @param string $pseudo_class Name under this class object is accessed using getObject method. | ||||
* | |||||
* @return void | * @return void | ||||
* @access public | |||||
*/ | */ | ||||
public function unregisterClass($real_class, $pseudo_class = null) | public function unregisterClass($real_class, $pseudo_class = null) | ||||
{ | { | ||||
unset($this->classMap[$real_class]); | unset($this->classMap[$real_class]); | ||||
} | } | ||||
} | |||||
} | |||||
class kFactoryException extends Exception { | class kFactoryException extends Exception | ||||
{ | |||||
} | } |