_directorySeparator = preg_quote(DIRECTORY_SEPARATOR);
$this->_skipFolders[] = basename(EDITOR_PATH);
$this->_moduleFolderRegExp = '#' . $this->_directorySeparator . '(core|modules' . $this->_directorySeparator . '.*?)' . $this->_directorySeparator . '#';
}
/**
* Sets data from cache to object
*
* @param Array $data
* @access public
*/
public function setFromCache(&$data)
{
$this->prefixFiles = $data['ConfigReader.prefixFiles'];
}
/**
* Gets object data for caching
*
* @access public
* @return Array
*/
public function getToCache()
{
return Array (
'ConfigReader.prefixFiles' => $this->prefixFiles,
);
}
function scanModules($folderPath, $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');
}
$this->ProcessAllConfigs = true;
$this->includeConfigFiles($folderPath, $cache);
$this->ParseConfigs();
// 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();
// get build-in rewrite listeners ONLY to be able to parse mod-rewrite url when unit config cache is missing
$this->retrieveCollections();
$this->_sortRewriteListeners();
}
$this->Application->cacheManager->applyDelayedUnitProcessing();
if ( !$this->Application->InitDone && $cache ) {
// Allow hooks to modify "m:QueryString" before URL parsing is started during cold start.
$this->runAfterConfigRead('m');
}
}
function findConfigFiles($folderPath, $level = 0)
{
// If FULL_PATH = "/" ensure, that all "/" in $folderPath are not deleted.
$reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/';
// This make sense, since $folder_path may NOT contain FULL_PATH.
$folderPath = preg_replace($reg_exp, '', $folderPath, 1);
$base_folder = FULL_PATH . $folderPath . DIRECTORY_SEPARATOR;
$sub_folders = glob($base_folder . '*', GLOB_ONLYDIR);
if (!$sub_folders) {
return ;
}
foreach ($sub_folders as $full_path) {
$sub_folder = substr($full_path, strlen($base_folder));
if (in_array($sub_folder, $this->_skipFolders)) {
continue;
}
if (substr($sub_folder, 0, 1) == '.') {
// Don't scan ".folders".
continue;
}
$config_name = $this->getConfigName($folderPath . DIRECTORY_SEPARATOR . $sub_folder);
if (file_exists(FULL_PATH . $config_name)) {
$this->configFiles[] = $config_name;
}
$this->findConfigFiles($full_path, $level + 1);
}
}
function includeConfigFiles($folderPath, $cache = true)
{
$data = false;
$this->Application->refreshModuleInfo();
if ( $cache ) {
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$data = $this->Application->getCache(
'master:config_files',
false,
CacheSettings::$unitCacheRebuildTime
);
}
else {
$data = $this->Application->getDBCache(
'config_files',
CacheSettings::$unitCacheRebuildTime
);
}
}
if ( $data ) {
$this->configFiles = unserialize($data);
if ( !(defined('DBG_VALIDATE_CONFIGS') && DBG_VALIDATE_CONFIGS) ) {
shuffle($this->configFiles);
}
}
else {
/*if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileStart('fcf');
}*/
$this->findConfigFiles(FULL_PATH . DIRECTORY_SEPARATOR . 'core'); // Search from core directory.
$this->findConfigFiles($folderPath); // Search from modules directory.
/*if ( $this->Application->isDebugMode() ) {
$this->Application->Debugger->profileFinish(
'fcf',
'findConfigFiles [' . count($this->configFiles) . ']'
);
}*/
if ( $cache ) {
if ( $this->Application->isCachingType(CACHING_TYPE_MEMORY) ) {
$this->Application->setCache('master:config_files', serialize($this->configFiles), 0);
}
else {
$this->Application->setDBCache('config_files', serialize($this->configFiles), 0);
}
}
}
foreach ($this->configFiles as $filename) {
$prefix = $this->PreloadConfigFile($filename);
if (!$prefix) {
throw new Exception('Prefix not defined in config file ' . $filename . '');
}
}
if ($cache) {
unset($this->configFiles);
}
}
/**
* Process all read config files - called ONLY when there is no cache!
*
*/
function ParseConfigs()
{
// 1. process normal configs
$prioritized_configs = array();
foreach ($this->configData as $prefix => $config) {
if (isset($config['ConfigPriority'])) {
$prioritized_configs[$prefix] = $config['ConfigPriority'];
continue;
}
$this->parseConfig($prefix);
}
foreach ($this->configData as $prefix => $config) {
$this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
}
// 2. process prioritized configs
asort($prioritized_configs);
foreach ($prioritized_configs as $prefix => $priority) {
$this->parseConfig($prefix);
}
}
function AfterConfigRead($store_cache = null)
{
// if (!$this->ProcessAllConfigs) return ;
$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->processDynamicClones();
$this->retrieveCollections();
}
if ($store_cache) {
$this->_sortRewriteListeners();
$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 $prefix => $config) {
if (!isset($config['TableName'])) continue;
$this->ValidateConfig($prefix);
}
}
}
}
/**
* Sort rewrite listeners according to RewritePriority (non-prioritized listeners goes first)
*
*/
function _sortRewriteListeners()
{
$listeners = Array ();
$prioritized_listeners = Array ();
// process non-prioritized listeners
foreach ($this->Application->RewriteListeners as $prefix => $listener_data) {
if ($listener_data['priority'] === false) {
$listeners[$prefix] = $listener_data;
}
else {
$prioritized_listeners[$prefix] = $listener_data['priority'];
}
}
// process prioritized listeners
asort($prioritized_listeners, SORT_NUMERIC);
foreach ($prioritized_listeners as $prefix => $priority) {
$listeners[$prefix] = $this->Application->RewriteListeners[$prefix];
}
$this->Application->RewriteListeners = $listeners;
}
/**
* Re-reads all configs
*
*/
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->ProcessAllConfigs = true;
$this->AfterConfigProcessed = Array ();
$this->includeConfigFiles(MODULES_PATH, false);
$this->ParseConfigs();
$this->AfterConfigRead(false);
$this->processDynamicClones();
// don't call kUnitConfigReader::retrieveCollections since it
// will overwrite what we already have in kApplication class instance
}
/**
* Process clones, that were defined via OnAfterConfigRead event
*
*/
function processDynamicClones()
{
$new_clones = Array();
foreach ($this->configData as $prefix => $config) {
$clones = $this->postProcessConfig($prefix, 'Clones', 'prefix');
if ($clones) {
$new_clones = array_merge($new_clones, $clones);
}
}
// execute delayed methods for cloned unit configs
$this->Application->cacheManager->applyDelayedUnitProcessing();
// call OnAfterConfigRead for cloned configs
$new_clones = array_unique($new_clones);
foreach ($new_clones as $prefix) {
$this->runAfterConfigRead($prefix);
}
}
/**
* Process all collectible unit config options here to also catch ones, defined from OnAfterConfigRead events
*
*/
function retrieveCollections()
{
foreach ($this->configData as $prefix => $config) {
// collect replacement templates
if (array_key_exists('ReplacementTemplates', $config) && $config['ReplacementTemplates']) {
$this->Application->ReplacementTemplates = array_merge($this->Application->ReplacementTemplates, $config['ReplacementTemplates']);
}
// collect rewrite listeners
if (array_key_exists('RewriteListener', $config) && $config['RewriteListener']) {
$rewrite_listeners = $config['RewriteListener'];
if (!is_array($rewrite_listeners)) {
// when one method is used to build and parse url
$rewrite_listeners = Array ($rewrite_listeners, $rewrite_listeners);
}
foreach ($rewrite_listeners as $index => $rewrite_listener) {
if (strpos($rewrite_listener, ':') === false) {
$rewrite_listeners[$index] = $prefix . '_EventHandler:' . $rewrite_listener;
}
}
$rewrite_priority = array_key_exists('RewritePriority', $config) ? $config['RewritePriority'] : false;
$this->Application->RewriteListeners[$prefix] = Array ('listener' => $rewrite_listeners, 'priority' => $rewrite_priority);
}
}
}
/**
* Register nessasary classes
* This method should only process the data which is cached!
*
* @param string $prefix
* @access private
*/
function parseConfig($prefix)
{
$this->parseClasses($prefix);
$this->parseScheduledTasks($prefix);
$this->parseHooks($prefix);
$this->parseAggregatedTags($prefix);
}
protected function parseClasses($prefix)
{
$config =& $this->configData[$prefix];
$register_classes = $this->getClasses($prefix);
foreach ($register_classes as $class_info) {
$this->Application->registerClass(
$class_info['class'],
$config['BasePath'] . DIRECTORY_SEPARATOR . $class_info['file'],
$class_info['pseudo']
);
if ( isset($class_info['build_event']) && $class_info['build_event'] ) {
$this->Application->delayUnitProcessing('registerBuildEvent', Array ($class_info['pseudo'], $class_info['build_event']));
}
}
}
protected function parseScheduledTasks($prefix)
{
$config =& $this->configData[$prefix];
if ( !isset($config['ScheduledTasks']) || !$config['ScheduledTasks'] ) {
return ;
}
$scheduled_tasks = $config['ScheduledTasks'];
foreach ($scheduled_tasks as $short_name => $scheduled_task_info) {
$event_status = array_key_exists('Status', $scheduled_task_info) ? $scheduled_task_info['Status'] : STATUS_ACTIVE;
$this->Application->delayUnitProcessing('registerScheduledTask', Array ( $short_name, $config['Prefix'] . ':' . $scheduled_task_info['EventName'], $scheduled_task_info['RunSchedule'], $event_status ));
}
}
protected function parseHooks($prefix)
{
$config =& $this->configData[$prefix];
if ( !isset($config['Hooks']) || !$config['Hooks'] ) {
return ;
}
$hooks = $config['Hooks'];
foreach ($hooks as $hook) {
// Don't attempt to register a hook to a module isn't installed.
if ( isset($hook['HookToModule']) && !isset($this->Application->ModuleInfo[$hook['HookToModule']]) ) {
continue;
}
if ( isset($config['ParentPrefix']) && ($hook['HookToPrefix'] == $config['ParentPrefix']) ) {
trigger_error('Depricated Hook Usage [prefix: ' . $config['Prefix'] . '; do_prefix: ' . $hook['DoPrefix'] . '] use #PARENT# as HookToPrefix value, where HookToPrefix is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
}
if ($hook['HookToPrefix'] == '') {
// new: set hooktoprefix to current prefix if not set
$hook['HookToPrefix'] = $config['Prefix'];
}
if ( isset($config['ParentPrefix']) ) {
// new: allow to set hook to parent prefix what ever it is
if ($hook['HookToPrefix'] == '#PARENT#') {
$hook['HookToPrefix'] = $config['ParentPrefix'];
}
if ($hook['DoPrefix'] == '#PARENT#') {
$hook['DoPrefix'] = $config['ParentPrefix'];
}
}
elseif ($hook['HookToPrefix'] == '#PARENT#' || $hook['DoPrefix'] == '#PARENT#') {
// we need parent prefix but it's not set !
continue;
}
$hook_events = (array)$hook['HookToEvent'];
$do_prefix = $hook['DoPrefix'] == '' ? $config['Prefix'] : $hook['DoPrefix'];
foreach ($hook_events as $hook_event) {
$hook_event = $hook['HookToPrefix'] . '.' . $hook['HookToSpecial'] . ':' . $hook_event;
$do_event = $do_prefix . '.' . $hook['DoSpecial'] . ':' . $hook['DoEvent'];
$this->Application->delayUnitProcessing('registerHook', Array ($hook_event, $do_event, $hook['Mode'], $hook['Conditional']));
}
}
}
protected function parseAggregatedTags($prefix)
{
$config =& $this->configData[$prefix];
$aggregated_tags = isset($config['AggregateTags']) ? $config['AggregateTags'] : Array ();
foreach ($aggregated_tags as $aggregate_tag) {
if ( isset($config['ParentPrefix']) ) {
if ($aggregate_tag['AggregateTo'] == $config['ParentPrefix']) {
trigger_error('Depricated Aggregate Tag Usage [prefix: '.$config['Prefix'].'; AggregateTo: '.$aggregate_tag['AggregateTo'].'] use #PARENT# as AggregateTo value, where AggregateTo is same as ParentPrefix', defined('E_USER_DEPRECATED') ? E_USER_DEPRECATED : E_USER_NOTICE);
}
if ($aggregate_tag['AggregateTo'] == '#PARENT#') {
$aggregate_tag['AggregateTo'] = $config['ParentPrefix'];
}
}
$aggregate_tag['LocalPrefix'] = $config['Prefix'];
$this->Application->delayUnitProcessing('registerAggregateTag', Array ($aggregate_tag));
}
}
function ValidateConfig($prefix)
{
global $debugger;
$config =& $this->configData[$prefix];
$tablename = $config['TableName'];
$float_types = Array ('float', 'double', 'numeric');
$table_found = $this->Conn->Query('SHOW TABLES LIKE "'.$tablename.'"');
if (!$table_found) {
// config present, but table missing, strange
kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
$debugger->appendHTML("Config Warning: Table $tablename missing, but prefix ".$config['Prefix']." requires it!");
$debugger->WarningCount++;
return ;
}
$res = $this->Conn->Query('DESCRIBE '.$tablename);
$config_link = $debugger->getFileLink(FULL_PATH.$this->prefixFiles[$config['Prefix']], 1, $config['Prefix']);
$error_messages = Array (
'field_not_found' => 'Field %s exists in the database, but is not defined in config',
'default_missing' => 'Default value for field %s not set in config',
'not_null_error1' => 'Field %s is NOT NULL in the database, but is not configured as not_null', // or required',
'not_null_error2' => 'Field %s is described as NOT NULL in config, but does not have DEFAULT value',
'not_null_error3' => 'Field %s is described as NOT NULL in config, but is NULL in db',
'invalid_default' => 'Default value for field %s%s not sync. to db (in config = %s, in db = %s)',
'date_column_not_null_error' => 'Field %s must be NULL in config and database, since it contains date',
'user_column_default_error' => 'Field %s must be have NULL as default value, since it holds user id',
'type_missing' => 'Type definition for field %s missing in config',
'virtual_type_missing' => 'Type definition for virtual field %s missing in config',
'virtual_default_missing' => 'Default value for virtual field %s not set in config',
'virtual_not_null_error' => 'Virtual field %s cannot be not null, since it doesn\'t exist in database',
'invalid_calculated_field' => 'Calculated field %s is missing corresponding virtual field',
);
$config_errors = Array ();
$tablename = preg_replace('/^'.preg_quote(TABLE_PREFIX, '/').'(.*)/', '\\1', $tablename); // remove table prefix
// validate unit config field declaration in relation to database table structure
foreach ($res as $field) {
$f_name = $field['Field'];
if (getArrayValue($config, 'Fields')) {
if (preg_match('/l[\d]+_[\w]/', $f_name)) {
// skip multilingual fields
continue;
}
if (!array_key_exists ($f_name, $config['Fields'])) {
$config_errors[] = sprintf($error_messages['field_not_found'], $f_name);
}
else {
$db_default = $field['Default'];
if (is_numeric($db_default)) {
$db_default = preg_match('/[\.,]/', $db_default) ? (float)$db_default : (int)$db_default;
}
$default_missing = false;
$options = $config['Fields'][$f_name];
$not_null = isset($options['not_null']) && $options['not_null'];
$formatter = array_key_exists('formatter', $options) ? $options['formatter'] : false;
if (!array_key_exists('default', $options)) {
$config_errors[] = sprintf($error_messages['default_missing'], $f_name);
$default_missing = true;
}
if ($field['Null'] != 'YES') {
// field is NOT NULL in database (MySQL5 for null returns "NO", but MySQL4 returns "")
if ( $f_name != $config['IDField'] && !isset($options['not_null']) /*&& !isset($options['required'])*/ ) {
$config_errors[] = sprintf($error_messages['not_null_error1'], $f_name);
}
if ($not_null && !isset($options['default']) ) {
$config_errors[] = sprintf($error_messages['not_null_error2'], $f_name);
}
}
elseif ($not_null) {
$config_errors[] = sprintf($error_messages['not_null_error3'], $f_name);
}
if (($formatter == 'kDateFormatter') && $not_null) {
$config_errors[] = sprintf($error_messages['date_column_not_null_error'], $f_name);
}
// columns, holding userid should have NULL as default value
if (array_key_exists('type', $options) && !$default_missing) {
// both type and default value set
if (preg_match('/ById$/', $f_name) && $options['default'] !== null) {
$config_errors[] = sprintf($error_messages['user_column_default_error'], $f_name);
}
}
if (!array_key_exists('type', $options)) {
$config_errors[] = sprintf($error_messages['type_missing'], $f_name);
}
if (!$default_missing && ($field['Type'] != 'text')) {
if ( is_null($db_default) && $not_null ) {
$db_default = $options['type'] == 'string' ? '' : 0;
}
if ($f_name == $config['IDField'] && $options['type'] != 'string' && $options['default'] !== 0) {
$config_errors[] = sprintf($error_messages['invalid_default'], 'IDField ', $f_name, $this->varDump($options['default']), $this->varDump($field['Default']));
}
else if (((string)$options['default'] != '#NOW#') && ($db_default !== $options['default']) && !in_array($options['type'], $float_types)) {
$config_errors[] = sprintf($error_messages['invalid_default'], '', $f_name, $this->varDump($options['default']), $this->varDump($db_default));
}
}
}
}
}
// validate virtual fields
if ( array_key_exists('VirtualFields', $config) ) {
foreach ($config['VirtualFields'] as $f_name => $options) {
if (!array_key_exists('type', $options)) {
$config_errors[] = sprintf($error_messages['virtual_type_missing'], $f_name);
}
if (array_key_exists('not_null', $options)) {
$config_errors[] = sprintf($error_messages['virtual_not_null_error'], $f_name);
}
if (!array_key_exists('default', $options)) {
$config_errors[] = sprintf($error_messages['virtual_default_missing'], $f_name);
}
}
}
// validate calculated fields
if ( array_key_exists('CalculatedFields', $config) ) {
foreach ($config['CalculatedFields'] as $special => $calculated_fields) {
foreach ($calculated_fields as $calculated_field => $calculated_field_expr) {
if ( !isset($config['VirtualFields'][$calculated_field]) ) {
$config_errors[] = sprintf($error_messages['invalid_calculated_field'], $calculated_field);
}
}
}
$config_errors = array_unique($config_errors);
}
if ($config_errors) {
$error_prefix = 'Config Error'.(count($config_errors) > 1 ? 's' : '').': for prefix '.$config_link.' ('.$tablename.') in unit config:
';
$config_errors = $error_prefix.' '.implode('
', $config_errors);
kUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 1);
$debugger->appendHTML($config_errors);
$debugger->WarningCount++;
}
}
function varDump($value)
{
return ''.var_export($value, true).' of '.gettype($value);
}
function postProcessConfig($prefix, $config_key, $dst_prefix_var)
{
$main_config =& $this->configData[$prefix];
$sub_configs = isset($main_config[$config_key]) && $main_config[$config_key] ? $main_config[$config_key] : Array ();
if ( !$sub_configs ) {
return Array ();
}
unset($main_config[$config_key]);
$processed = array();
foreach ($sub_configs as $sub_prefix => $sub_config) {
if ($config_key == 'AggregateConfigs' && !isset($this->configData[$sub_prefix])) {
$this->loadConfig($sub_prefix);
}
$sub_config['Prefix'] = $sub_prefix;
$this->configData[$sub_prefix] = kUtil::array_merge_recursive($this->configData[$$dst_prefix_var], $sub_config);
// when merging empty array to non-empty results non-empty array, but empty is required
foreach ($sub_config as $sub_key => $sub_value) {
if (!$sub_value) {
unset($this->configData[$sub_prefix][$sub_key]);
}
}
if ($config_key == 'Clones') {
$this->prefixFiles[$sub_prefix] = $this->prefixFiles[$prefix];
}
$this->postProcessConfig($sub_prefix, $config_key, $dst_prefix_var);
if ($config_key == 'AggregateConfigs') {
$processed = array_merge($this->postProcessConfig($sub_prefix, 'Clones', 'prefix'), $processed);
}
elseif ($this->ProcessAllConfigs) {
$this->parseConfig($sub_prefix);
}
array_push($processed, $sub_prefix);
}
if (!$prefix) {
// configs, that used only for cloning & not used ifself
unset($this->configData[$prefix]);
}
return array_unique($processed);
}
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) {
if (isset($config) && $config) {
// config file is included for 1st time -> save it's content for future processing
$prefix = array_key_exists('Prefix', $config) ? $config['Prefix'] : '';
preg_match($this->_moduleFolderRegExp, $filename, $rets);
$config['ModuleFolder'] = str_replace(DIRECTORY_SEPARATOR, '/', $rets[1]);
$config['BasePath'] = dirname(FULL_PATH . $filename);
if (array_key_exists('AdminTemplatePath', $config)) {
// append template base folder for admin templates path of this prefix
$module_templates = $rets[1] == 'core' ? '' : substr($rets[1], 8) . '/';
$config['AdminTemplatePath'] = $module_templates . $config['AdminTemplatePath'];
}
if (array_key_exists($prefix, $this->prefixFiles) && ($this->prefixFiles[$prefix] != $filename)) {
trigger_error(
'Single unit config prefix "' . $prefix . '" ' .
'is used in multiple unit config files: ' .
'"' . $this->prefixFiles[$prefix] . '", "' . $filename . '"',
E_USER_WARNING
);
}
$this->configData[$prefix] = $config;
$this->prefixFiles[$prefix] = $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';
}
function loadConfig($prefix)
{
if ( !isset($this->prefixFiles[$prefix]) ) {
throw new Exception('Configuration file for prefix "' . $prefix . '" is unknown');
return ;
}
$file = $this->prefixFiles[$prefix];
$prefix = $this->PreloadConfigFile($file);
if ( $this->FinalStage || $prefix == 'm' ) {
// Run prefix OnAfterConfigRead so all hooks to it can define their clones.
// Allow hooks to modify "m:QueryString" before URL parsing is started during warm start.
$this->runAfterConfigRead($prefix);
}
$clones = $this->postProcessConfig($prefix, 'AggregateConfigs', 'sub_prefix');
$clones = array_merge($this->postProcessConfig($prefix, 'Clones', 'prefix'), $clones);
if ($this->FinalStage) {
$clones = array_unique($clones);
foreach ($clones as $a_prefix) {
$this->runAfterConfigRead($a_prefix);
}
}
}
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);
}
}
/**
* Reads unit (specified by $prefix)
* option specified by $option
*
* @param string $prefix
* @param string $name
* @param mixed $default
* @return string
* @access public
*/
function getUnitOption($prefix, $name, $default = false)
{
if (preg_match('/(.*)\.(.*)/', $prefix, $rets)) {
if (!isset($this->configData[$rets[1]])) {
$this->loadConfig($rets[1]);
}
$ret = isset($this->configData[$rets[1]][$name][$rets[2]]) ? $this->configData[$rets[1]][$name][$rets[2]] : false;
// $ret = getArrayValue($this->configData, $rets[1], $name, $rets[2]);
}
else {
if (!isset($this->configData[$prefix])) {
$this->loadConfig($prefix);
}
$ret = isset($this->configData[$prefix][$name]) ? $this->configData[$prefix][$name] : false;
// $ret = getArrayValue($this->configData, $prefix, $name);
}
return $ret === false ? $default : $ret;
}
/**
* Read all unit with $prefix options
*
* @param string $prefix
* @return Array
* @access public
*/
function getUnitOptions($prefix)
{
if (!isset($this->configData[$prefix])) {
$this->loadConfig($prefix);
}
return $this->configData[$prefix];
}
/**
* Set's new unit option value
*
* @param string $prefix
* @param string $name
* @param string $value
* @access public
*/
function setUnitOption($prefix, $name, $value)
{
if ( preg_match('/(.*)\.(.*)/', $prefix, $rets) ) {
if ( !isset($this->configData[$rets[1]]) ) {
$this->loadConfig($rets[1]);
}
$this->configData[$rets[1]][$name][$rets[2]] = $value;
}
else {
if ( !isset($this->configData[$prefix]) ) {
$this->loadConfig($prefix);
}
$this->configData[$prefix][$name] = $value;
}
}
protected function getClasses($prefix)
{
$config =& $this->configData[$prefix];
$class_params = Array ('ItemClass', 'ListClass', 'EventHandlerClass', 'TagProcessorClass');
$register_classes = isset($config['RegisterClasses']) ? $config['RegisterClasses'] : Array ();
foreach ($class_params as $param_name) {
if ( !isset($config[$param_name]) ) {
continue;
}
$config[$param_name]['pseudo'] = $this->getPseudoByOptionName($param_name, $prefix);
$register_classes[] = $config[$param_name];
}
return $register_classes;
}
protected function getPseudoByOptionName($option_name, $prefix)
{
$pseudo_class_map = Array (
'ItemClass' => '%s',
'ListClass' => '%s_List',
'EventHandlerClass' => '%s_EventHandler',
'TagProcessorClass' => '%s_TagProcessor'
);
return sprintf($pseudo_class_map[$option_name], $prefix);
}
/**
* Get's config file name based
* on folder name supplied
*
* @param string $folderPath
* @return string
* @access private
*/
function getConfigName($folderPath)
{
return $folderPath . DIRECTORY_SEPARATOR . basename($folderPath) . '_config.php';
}
/**
* Checks if config file is allowed for includion (if module of config is installed)
*
* @param string $config_path relative path from in-portal directory
*/
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_name => $module_info) {
$module_paths[] = str_replace('/', DIRECTORY_SEPARATOR, rtrim($module_info['Path'], '/'));
}
$module_paths = array_unique($module_paths);
}
preg_match($this->_moduleFolderRegExp, $config_path, $rets);
// config file path starts with module folder path
return in_array($rets[1], $module_paths);
}
/**
* Returns true if config exists and is allowed for reading
*
* @param string $prefix
* @return bool
*/
function prefixRegistred($prefix)
{
return isset($this->prefixFiles[$prefix]) ? true : false;
}
/**
* Returns config file for given prefix
*
* @param string $prefix
* @return string
*/
function getPrefixFile($prefix)
{
return array_key_exists($prefix, $this->prefixFiles) ? $this->prefixFiles[$prefix] : false;
}
function iterateConfigs($callback_function, $params)
{
$this->includeConfigFiles(MODULES_PATH); //make sure to re-read all configs
$this->AfterConfigRead();
foreach ($this->configData as $prefix => $config_data) {
call_user_func_array(
$callback_function,
array($prefix, &$config_data, $params)
);
}
}
}