Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F726892
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Mon, Jan 6, 2:36 AM
Size
24 KB
Mime Type
text/x-diff
Expires
Wed, Jan 8, 2:36 AM (2 d, 1 h ago)
Engine
blob
Format
Raw Data
Handle
536981
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/5.3.x/core/kernel/utility/unit_config_cloner.php
===================================================================
--- branches/5.3.x/core/kernel/utility/unit_config_cloner.php (revision 16176)
+++ branches/5.3.x/core/kernel/utility/unit_config_cloner.php (revision 16177)
@@ -1,172 +1,173 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2013 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kUnitConfigCloner extends kBase implements kiCacheable
{
/**
* Unit config reader.
*
* @var kUnitConfigReader
*/
protected $reader;
/**
* Cloned unit config difference compared to original unit config.
*
* @var array
*/
protected $clones = array();
/**
* Creates cloner.
*/
public function __construct(kUnitConfigReader $reader)
{
parent::__construct();
$this->reader = $reader;
}
/**
* Sets data from cache to object
*
* @param Array $data
*/
public function setFromCache(&$data)
{
$this->clones = $data['ConfigCloner.clones'];
}
/**
* Gets object data for caching
*
* @return Array
*/
public function getToCache()
{
return array(
'ConfigCloner.clones' => $this->clones,
);
}
/**
* Creates unit configs, based on 'Clones' option.
*
- * @param string $prefix Unit config prefix.
+ * @param string $prefix Unit config prefix.
+ * @param boolean $with_cached Process also cached clones.
*
* @return array
*/
- public function extrude($prefix)
+ public function extrude($prefix, $with_cached = true)
{
$main_config = $this->reader->getUnitConfig($prefix);
$sub_configs = $main_config->getSetting('Clones', array());
- if ( isset($this->clones[$prefix]) ) {
+ if ( $with_cached && isset($this->clones[$prefix]) ) {
$sub_configs = array_merge($sub_configs, $this->clones[$prefix]);
}
if ( !$sub_configs ) {
return array();
}
$processed = array();
$main_config->setSetting('Clones', null);
unset($this->clones[$prefix]);
if ( !isset($this->clones[$prefix]) ) {
$this->clones[$prefix] = array();
}
foreach ( $sub_configs as $sub_prefix => $sub_config_data ) {
$this->clones[$prefix][$sub_prefix] = $sub_config_data;
$sub_config_data['Prefix'] = $sub_prefix;
$sub_config = $this->prepareClone($main_config, $sub_config_data);
$this->reader->add($sub_config, $this->reader->getPrefixFile($prefix));
array_push($processed, $sub_prefix);
}
if ( !$prefix ) {
// configs, that used only for cloning & not used itself
$this->reader->remove($prefix);
}
return array_unique($processed);
}
/**
* Prepares data for of cloned unit config.
*
* @param kUnitConfig $main_config Main unit config.
* @param array $sub_config_data Sub config data.
*
* @return kUnitConfig
*/
protected function prepareClone(kUnitConfig $main_config, $sub_config_data)
{
$ret = kUtil::array_merge_recursive($main_config->getRaw(), $sub_config_data);
foreach ($sub_config_data as $key => $value) {
if ( !$value && is_array($value) ) {
unset($ret[$key]);
}
}
return new kUnitConfig($sub_config_data['Prefix'], $ret);
}
/**
* Creates clones (where needed) and parses them.
*
* @param array $prefixes Unit config prefixes.
*
* @return array
*/
public function extrudeAndParse(array $prefixes)
{
$cloned_prefixes = array();
foreach ( $prefixes as $prefix ) {
$cloned_prefixes = array_merge($cloned_prefixes, $this->extrude($prefix));
}
foreach ( $cloned_prefixes as $cloned_prefix ) {
$this->reader->getUnitConfig($cloned_prefix)->parse();
}
return array_unique($cloned_prefixes);
}
/**
* Process clones, that were defined via OnAfterConfigRead event
*
* @return void
*/
public function processDynamicallyAdded()
{
$new_clones = $this->extrudeAndParse($this->reader->getPrefixes());
// execute delayed methods for cloned unit configs
$this->Application->cacheManager->applyDelayedUnitProcessing();
// call OnAfterConfigRead for cloned configs
foreach ( $new_clones as $prefix ) {
$this->reader->runAfterConfigRead($prefix);
}
}
}
Index: branches/5.3.x/core/kernel/utility/unit_config_reader.php
===================================================================
--- branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16176)
+++ branches/5.3.x/core/kernel/utility/unit_config_reader.php (revision 16177)
@@ -1,729 +1,730 @@
<?php
/**
* @version $Id$
* @package In-Portal
* @copyright Copyright (C) 1997 - 2009 Intechnic. All rights reserved.
* @license GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/
defined('FULL_PATH') or die('restricted access!');
class kUnitConfigReader extends kBase implements kiCacheable {
/**
* Unit config data storage.
*
* @var kUnitConfig[]
*/
protected $configData = array();
/**
* List of discovered unit config files.
*
* @var array
*/
protected $configFiles = array();
/**
* Mapping between unit config prefixes and files, where they data is stored.
*
* @var array
*/
protected $prefixFiles = array();
/**
* Tells, that it's final stage of application initialization, where OnAfterConfigRead events can be called.
*
* @var boolean
*/
protected $finalStage = false;
/**
* Determines if cache should be stored.
*
* @var boolean
*/
protected $storeCache = false;
/**
* List of unit configs, that have called their OnAfterConfigRead event.
*
* @var array
*/
protected $afterConfigProcessed = array();
/**
* Escaped directory separator for using in regular expressions
*
* @var string
*/
protected $directorySeparator = '';
/**
* Regular expression for detecting module folder
*
* @var string
*/
protected $moduleFolderRegExp = '';
/**
* Folders to skip during unit config search
*
* @var array
*/
protected $skipFolders = array('CVS', '.svn', 'admin_templates', 'libchart');
/**
* Cloner.
*
* @var kUnitConfigCloner
*/
protected $cloner;
/**
* Creates instance of unit config reader.
*/
public function __construct()
{
parent::__construct();
$this->directorySeparator = preg_quote(DIRECTORY_SEPARATOR);
$editor_path = explode('/', trim(EDITOR_PATH, '/'));
$this->skipFolders[] = array_pop($editor_path); // last of cmseditor folders
$this->moduleFolderRegExp = '#' . $this->directorySeparator . '(core|modules' . $this->directorySeparator . '.*?)' . $this->directorySeparator . '#';
$this->cloner = $this->Application->makeClass('kUnitConfigCloner', array($this));
}
/**
* Sets data from cache to object
*
* @param Array $data
*/
public function setFromCache(&$data)
{
$this->cloner->setFromCache($data);
$this->prefixFiles = $data['ConfigReader.prefixFiles'];
}
/**
* Gets object data for caching
*
* @return Array
*/
public function getToCache()
{
return array_merge(
$this->cloner->getToCache(),
array(
'ConfigReader.prefixFiles' => $this->prefixFiles,
)
);
}
public function scanModules($folder_path, $cache = true)
{
if ( defined('IS_INSTALL') && IS_INSTALL && !defined('FORCE_CONFIG_CACHE') ) {
// disable config caching during installation
$cache = false;
}
if ( $cache ) {
$restored = $this->Application->cacheManager->LoadUnitCache();
if ( $restored ) {
if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendHTML('UnitConfigReader: Restoring Cache');
}
return;
}
}
if ( defined('DEBUG_MODE') && DEBUG_MODE && $this->Application->isDebugMode() ) {
$this->Application->Debugger->appendHTML('UnitConfigReader: Generating Cache');
}
// === lines below would only executed on cold start (no unit config cache) ===
// no cache found -> include all unit configs to create it !
$this->includeConfigFiles($folder_path, $cache);
$this->parseConfigs();
$this->sortRouters();
// tell AfterConfigRead to store cache if needed
// can't store it here because AfterConfigRead needs ability to change config data
$this->storeCache = $cache;
if ( !$this->Application->InitDone ) {
// scanModules is called multiple times during installation process
$this->Application->InitManagers();
}
$this->Application->cacheManager->applyDelayedUnitProcessing();
}
/**
* Locates (recursively) and reads all unit configs at given path.
*
* @param string $folder_path Folder path.
* @param boolean $cache Store information to cache.
*
* @throws Exception When unit config file is missing a prefix defined inside it.
*/
protected function includeConfigFiles($folder_path, $cache = true)
{
$this->Application->refreshModuleInfo();
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$data = $this->Application->getCache('master:config_files', false, $cache ? CacheSettings::$unitCacheRebuildTime : 0);
}
else {
$data = $this->Application->getDBCache('config_files', $cache ? CacheSettings::$unitCacheRebuildTime : 0);
}
if ( $data ) {
$this->configFiles = unserialize($data);
if ( !(defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) ) {
shuffle($this->configFiles);
}
}
else {
$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // search from "core" directory
$this->findConfigFiles($folder_path); // search from "modules" directory
if ( $cache ) {
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->setCache('master:config_files', serialize($this->configFiles));
}
else {
$this->Application->setDBCache('config_files', serialize($this->configFiles));
}
}
}
foreach ( $this->configFiles as $filename ) {
$prefix = $this->PreloadConfigFile($filename);
if ( !$prefix ) {
throw new Exception('Prefix not defined in config file <strong>' . $filename . '</strong>');
}
}
// TODO: needed?
if ( $cache ) {
unset($this->configFiles);
}
}
/**
* Recursively searches for unit configs in given folder.
*
* @param string $folder_path Path to the folder.
* @param int $level Deep level of the folder.
*
* @return void
*/
protected function findConfigFiles($folder_path, $level = 0)
{
// if FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted
$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
$folder_path = preg_replace($reg_exp, '', $folder_path, 1); // this make sense, since $folderPath may NOT contain FULL_PATH
$base_folder = FULL_PATH . $folder_path . DIRECTORY_SEPARATOR;
$sub_folders = glob($base_folder . '*', GLOB_ONLYDIR);
if ( !$sub_folders ) {
return;
}
if ( $level == 0 ) {
// don't scan Front-End themes because of extensive directory structure
$sub_folders = array_diff($sub_folders, array($base_folder . 'themes', $base_folder . 'tools'));
}
foreach ( $sub_folders as $full_path ) {
$sub_folder = substr($full_path, strlen($base_folder));
if ( in_array($sub_folder, $this->skipFolders) || preg_match('/^\./', $sub_folder) ) {
// don't scan skipped or hidden folders
continue;
}
$config_name = $this->getConfigName($folder_path . DIRECTORY_SEPARATOR . $sub_folder);
if ( file_exists(FULL_PATH . $config_name) ) {
$this->configFiles[] = $config_name;
}
$this->findConfigFiles($full_path, $level + 1);
}
}
/**
* Process all read config files - called ONLY when there is no cache!
*
* @return void
*/
protected function parseConfigs()
{
$this->parseUnitConfigs($this->getUnitConfigsWithoutPriority());
$this->parseUnitConfigs($this->getUnitConfigsWithPriority());
}
/**
* Parses unit config sub-set.
*
* @param array $prefixes Unit config prefixes.
*
* @return array
*/
protected function parseUnitConfigs(array $prefixes)
{
foreach ( $prefixes as $prefix ) {
$this->configData[$prefix]->parse();
}
$this->cloner->extrudeAndParse($prefixes);
}
/**
* Returns unit configs prefixes without priority defined.
*
* @return array
*/
protected function getUnitConfigsWithoutPriority()
{
$ret = array();
foreach ( $this->configData as $prefix => $config ) {
if ( $config->getConfigPriority() === false ) {
$ret[] = $prefix;
}
}
return $ret;
}
/**
* Returns unit configs prefixes with priority defined.
*
* @return array
*/
protected function getUnitConfigsWithPriority()
{
$ret = array();
foreach ( $this->configData as $prefix => $config ) {
$priority = $config->getConfigPriority();
if ( $priority !== false ) {
$ret[$prefix] = $priority;
}
}
asort($ret);
return array_keys($ret);
}
public function AfterConfigRead($store_cache = null)
{
$this->finalStage = true;
foreach ($this->configData as $prefix => $config) {
$this->runAfterConfigRead($prefix);
}
if ( !isset($store_cache) ) {
// $store_cache not overridden -> use global setting
$store_cache = $this->storeCache;
}
if ( $store_cache || (defined('IS_INSTALL') && IS_INSTALL) ) {
// cache is not stored during install, but dynamic clones should be processed in any case
$this->cloner->processDynamicallyAdded();
$this->retrieveCollections();
}
if ( $store_cache ) {
$this->Application->HandleEvent(new kEvent('adm:OnAfterCacheRebuild'));
$this->Application->cacheManager->UpdateUnitCache();
if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS ) {
// validate configs here to have changes from OnAfterConfigRead hooks to prefixes
foreach ( $this->configData as $config ) {
if ( !$config->getTableName() ) {
continue;
}
$config->validate();
}
}
}
}
/**
* Sort routers according to their weight (non-prioritized routers goes first).
*
* @return void
*/
protected function sortRouters()
{
$sorted_routers = array();
$prioritized_routers = array();
$routers = $this->collectRouters();
// Process non-prioritized routers.
foreach ( $routers as $prefix => $router_data ) {
if ( $router_data['priority'] === false ) {
$sorted_routers[$prefix] = $router_data;
}
else {
$prioritized_routers[$prefix] = $router_data['priority'];
}
}
// Process prioritized routers.
asort($prioritized_routers, SORT_NUMERIC);
foreach ( $prioritized_routers as $prefix => $priority ) {
$sorted_routers[$prefix] = $routers[$prefix];
}
$this->Application->routers = $sorted_routers;
}
/**
* Collects routers.
*
* @return array
*/
protected function collectRouters()
{
$routers = array();
$router_classes = $this->Application->getSubClasses('AbstractRouter');
foreach ( $router_classes as $router_class ) {
if ( !class_exists($router_class) ) {
// This can happen, when:
// - router class (coming from cache) was renamed;
// - new cache is built based on outdated class map.
continue;
}
/** @var AbstractRouter $router */
$router = new $router_class();
$routers[$router->getPrefix()] = array(
'class' => $router_class,
'priority' => $router->getWeight(),
);
}
return $routers;
}
/**
* Re-reads all configs.
*
* @return void
*/
public function ReReadConfigs()
{
// don't reset prefix file, since file scanning could slow down the process
$prefix_files_backup = $this->prefixFiles;
$this->Application->cacheManager->EmptyUnitCache();
$this->prefixFiles = $prefix_files_backup;
// parse all configs
$this->afterConfigProcessed = array();
$this->includeConfigFiles(MODULES_PATH, false);
$this->parseConfigs();
$this->sortRouters();
$this->AfterConfigRead(false);
$this->cloner->processDynamicallyAdded();
$this->retrieveCollections();
}
/**
* Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events
*
*/
protected function retrieveCollections()
{
foreach ( $this->configData as $prefix => $config ) {
// collect replacement templates
if ( $config->getReplacementTemplates() ) {
$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config->getReplacementTemplates());
}
}
}
public function loadConfig($prefix)
{
$preloaded_prefix = $this->PreloadConfigFile($this->getPrefixFile($prefix));
if ( $this->finalStage ) {
// run prefix OnAfterConfigRead so all hooks to it can define their clones
$this->runAfterConfigRead($preloaded_prefix);
}
- $clones = $this->cloner->extrude($preloaded_prefix);
+ // Only use cached clones for calls in the middle of initialization (e.g. url parsing).
+ $clones = $this->cloner->extrude($preloaded_prefix, !$this->finalStage);
if ( $this->finalStage ) {
foreach ( $clones as $a_prefix ) {
$this->runAfterConfigRead($a_prefix);
}
}
}
/**
* Runs OnAfterConfigRead event for given prefix once.
*
* @param string $prefix Unit config prefix.
*
* @return void
*/
public function runAfterConfigRead($prefix)
{
if ( in_array($prefix, $this->afterConfigProcessed) ) {
return;
}
$this->Application->HandleEvent(new kEvent($prefix . ':OnAfterConfigRead'));
if ( !(defined('IS_INSTALL') && IS_INSTALL) ) {
// allow to call OnAfterConfigRead multiple times during install
array_push($this->afterConfigProcessed, $prefix);
}
}
/**
* Loads unit config file contents from disk.
*
* @param string $filename Unit config filename.
*
* @return string
*/
protected function PreloadConfigFile($filename)
{
$config_found = file_exists(FULL_PATH . $filename) && $this->configAllowed($filename);
if ( defined('DEBUG_MODE') && DEBUG_MODE && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) {
if ( in_array($filename, get_included_files()) ) {
return '';
}
global $debugger;
if ( $config_found ) {
$file = FULL_PATH . $filename;
$file_crc = crc32($file);
$debugger->ProfileStart('inc_' . $file_crc, $file);
include_once($file);
$debugger->ProfileFinish('inc_' . $file_crc);
$debugger->profilerAddTotal('includes', 'inc_' . $file_crc);
}
}
elseif ( $config_found ) {
include_once(FULL_PATH . $filename);
}
if ( $config_found ) {
/* @var $config kUnitConfig|Array */
if ( isset($config) && $config ) {
// config file is included for 1st time -> save it's content for future processing
if ( !is_object($config) ) {
$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
$config = new kUnitConfig($prefix, $config);
}
else {
$prefix = $config->getPrefix();
}
preg_match($this->moduleFolderRegExp, $filename, $regs);
$config->setModuleFolder(str_replace(DIRECTORY_SEPARATOR, '/', $regs[1]));
$config->setBasePath(dirname(FULL_PATH . $filename));
if ( $config->getAdminTemplatePath() !== false ) {
// append template base folder for admin templates path of this prefix
$module_templates = $regs[1] == 'core' ? '' : substr($regs[1], 8) . '/';
$config->setAdminTemplatePath($module_templates . $config->getAdminTemplatePath());
}
if ( array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename) ) {
trigger_error(
'Single unit config prefix "<strong>' . $prefix . '</strong>" ' .
'is used in multiple unit config files: ' .
'"<strong>' . $this->prefixFiles[$prefix] . '</strong>", "<strong>' . $filename . '</strong>"',
E_USER_WARNING
);
}
$this->add($config, $filename);
return $prefix;
}
else {
$prefix = array_search($filename, $this->prefixFiles);
if ( $prefix ) {
// attempt is made to include config file twice or more, but include_once prevents that,
// but file exists on hdd, then it is already saved to all required arrays, just return it's prefix
return $prefix;
}
}
}
return 'dummy';
}
/**
* Sets a file, for a given prefix.
*
* @param kUnitConfig $config Unit config.
* @param string $filename File.
*
* @return void
*/
public function add(kUnitConfig $config, $filename)
{
$config->setFilename($filename);
$prefix = $config->getPrefix();
$this->configData[$prefix] = $config;
$this->prefixFiles[$prefix] = $filename;
}
/**
* Removes unit config.
*
* @param string $prefix Unit config prefix.
*
* @return void
*/
public function remove($prefix)
{
unset($this->configData[$prefix], $this->prefixFiles[$prefix]);
}
/**
* Returns unit config for given prefix
*
* @param string $prefix
* @return kUnitConfig
*/
public function getUnitConfig($prefix = null)
{
if ( !isset($this->configData[$prefix]) ) {
$this->loadConfig($prefix);
}
return $this->configData[$prefix];
}
/**
* Returns prefixes of unit configs, that were registered
*
* @return Array
*/
public function getPrefixes()
{
return array_keys($this->configData);
}
/**
* Get's config file name based
* on folder name supplied
*
* @param string $folder_path
* @return string
*/
protected function getConfigName($folder_path)
{
return $folder_path . DIRECTORY_SEPARATOR . basename($folder_path) . '_config.php';
}
/**
* Checks if config file is allowed for inclusion (if module of config is installed).
*
* @param string $config_path Relative path from In-Portal directory.
*
* @return boolean
*/
protected function configAllowed($config_path)
{
static $module_paths = null;
if ( defined('IS_INSTALL') && IS_INSTALL ) {
// at installation start no modules in db and kernel configs could not be read
return true;
}
if ( preg_match('#^' . $this->directorySeparator . 'core#', $config_path) ) {
// always allow to include configs from "core" module's folder
return true;
}
if ( !$this->Application->ModuleInfo ) {
return false;
}
if ( !isset($module_paths) ) {
$module_paths = array();
foreach ( $this->Application->ModuleInfo as $module_info ) {
$module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/'));
}
$module_paths = array_unique($module_paths);
}
preg_match($this->moduleFolderRegExp, $config_path, $regs);
// config file path starts with module folder path
return in_array($regs[1], $module_paths);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix Unit config prefix.
*
* @return boolean
*/
public function prefixRegistered($prefix)
{
return isset($this->prefixFiles[$prefix]);
}
/**
* Returns unit config file location by it's prefix.
*
* @param string $prefix Unit config prefix.
*
* @return string
* @throws Exception When unit config is not found.
*/
public function getPrefixFile($prefix)
{
if ( !isset($this->prefixFiles[$prefix]) ) {
throw new Exception('Configuration file for prefix "<strong>' . $prefix . '</strong>" is unknown');
}
return $this->prefixFiles[$prefix];
}
}
Event Timeline
Log In to Comment