Property changes on: branches/5.2.x/LICENSE ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/LICENSE:r15962 Property changes on: branches/5.2.x/robots.txt ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/robots.txt:r15962 Index: branches/5.2.x/core/kernel/startup.php =================================================================== --- branches/5.2.x/core/kernel/startup.php (revision 16434) +++ branches/5.2.x/core/kernel/startup.php (revision 16435) @@ -1,212 +1,209 @@ getData(); + } + catch ( kSystemConfigException $e ) { + echo 'In-Portal is probably not installed, or configuration file is missing.
'; + echo 'Please use the installation script to fix the problem.

'; - $admin_directory = isset($vars['AdminDirectory']) ? $vars['AdminDirectory'] : '/admin'; - define('ADMIN_DIRECTORY', $admin_directory); + $base_path = rtrim(str_replace('\\', '/', dirname($_SERVER['PHP_SELF'])), '/'); + echo 'Go to installation script

'; + flush(); + exit; + } - $admin_Presets_directory = isset($vars['AdminPresetsDirectory']) ? $vars['AdminPresetsDirectory'] : ADMIN_DIRECTORY; - define('ADMIN_PRESETS_DIRECTORY', $admin_Presets_directory); + define('CHARSET', $vars['WebsiteCharset']); + define('ADMIN_DIRECTORY', $vars['AdminDirectory']); + define('ADMIN_PRESETS_DIRECTORY', $vars['AdminPresetsDirectory']); $https_mark = getArrayValue($_SERVER, 'HTTPS'); define('PROTOCOL', ($https_mark == 'on') || ($https_mark == '1') ? 'https://' : 'http://'); if ( isset($_SERVER['HTTP_HOST']) ) { // accessed from browser $http_host = $_SERVER['HTTP_HOST']; } else { // accessed from command line $http_host = $vars['Domain']; $_SERVER['HTTP_HOST'] = $vars['Domain']; } $port = isset($_SERVER['SERVER_PORT']) ? $_SERVER['SERVER_PORT'] : false; if ($port) { if ( (PROTOCOL == 'http://' && $port != '80') || (PROTOCOL == 'https://' && $port != '443') ) { // if non-standard port is used, then define it define('PORT', $port); } $http_host = preg_replace('/:' . $port . '$/', '', $http_host); } define('SERVER_NAME', $http_host); - if (!$vars) { - echo 'In-Portal is probably not installed, or configuration file is missing.
'; - echo 'Please use the installation script to fix the problem.

'; - - $base_path = rtrim(preg_replace('/'.preg_quote(rtrim($admin_directory, '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'); - echo 'Go to installation script

'; - flush(); - exit; - } - if ( !file_exists(FULL_PATH . '/vendor/autoload.php') ) { echo 'Cannot find an "/vendor/autoload.php" file, have you executed "composer install" command?
'; flush(); exit; } // variable WebsitePath is auto-detected once during installation/upgrade define('BASE_PATH', $vars['WebsitePath']); - define('APPLICATION_CLASS', isset($vars['ApplicationClass']) ? $vars['ApplicationClass'] : 'kApplication'); - define('APPLICATION_PATH', isset($vars['ApplicationPath']) ? $vars['ApplicationPath'] : '/core/kernel/application.php'); + define('APPLICATION_CLASS', $vars['ApplicationClass']); + define('APPLICATION_PATH', $vars['ApplicationPath']); if (isset($vars['WriteablePath'])) { define('WRITEABLE', FULL_PATH . $vars['WriteablePath']); define('WRITEBALE_BASE', $vars['WriteablePath']); } if ( isset($vars['RestrictedPath']) ) { define('RESTRICTED', FULL_PATH . $vars['RestrictedPath']); } define('SQL_TYPE', $vars['DBType']); define('SQL_SERVER', $vars['DBHost']); define('SQL_USER', $vars['DBUser']); define('SQL_PASS', $vars['DBUserPassword']); define('SQL_DB', $vars['DBName']); if (isset($vars['DBCollation']) && isset($vars['DBCharset'])) { define('SQL_COLLATION', $vars['DBCollation']); // utf8_general_ci define('SQL_CHARSET', $vars['DBCharset']); // utf8 } define('TABLE_PREFIX', $vars['TablePrefix']); define('DOMAIN', getArrayValue($vars, 'Domain')); ini_set('memory_limit', '50M'); define('MODULES_PATH', FULL_PATH . DIRECTORY_SEPARATOR . 'modules'); define('EXPORT_BASE_PATH', WRITEBALE_BASE . '/export'); define('EXPORT_PATH', FULL_PATH . EXPORT_BASE_PATH); define('GW_CLASS_PATH', MODULES_PATH . '/in-commerce/units/gateways/gw_classes'); // Payment Gateway Classes Path define('SYNC_CLASS_PATH', FULL_PATH . '/sync'); // path for 3rd party user syncronization scripts define('ENV_VAR_NAME','env'); define('IMAGES_PATH', WRITEBALE_BASE . '/images/'); define('IMAGES_PENDING_PATH', IMAGES_PATH . 'pending/'); define('MAX_UPLOAD_SIZE', min(ini_get('upload_max_filesize'), ini_get('post_max_size'))*1024*1024); - define('EDITOR_PATH', isset($vars['EditorPath']) ? $vars['EditorPath'] : '/core/ckeditor/'); + define('EDITOR_PATH', $vars['EditorPath']); // caching types define('CACHING_TYPE_NONE', 0); define('CACHING_TYPE_MEMORY', 1); define('CACHING_TYPE_TEMPORARY', 2); class CacheSettings { static public $unitCacheRebuildTime; static public $structureTreeRebuildTime; static public $cmsMenuRebuildTime; static public $templateMappingRebuildTime; static public $sectionsParsedRebuildTime; static public $domainsParsedRebuildTime; } - CacheSettings::$unitCacheRebuildTime = isset($vars['UnitCacheRebuildTime']) ? $vars['UnitCacheRebuildTime'] : 10; - CacheSettings::$structureTreeRebuildTime = isset($vars['StructureTreeRebuildTime']) ? $vars['StructureTreeRebuildTime'] : 10; - CacheSettings::$cmsMenuRebuildTime = isset($vars['CmsMenuRebuildTime']) ? $vars['CmsMenuRebuildTime'] : 10; - CacheSettings::$templateMappingRebuildTime = isset($vars['TemplateMappingRebuildTime']) ? $vars['TemplateMappingRebuildTime'] : 5; - CacheSettings::$sectionsParsedRebuildTime = isset($vars['SectionsParsedRebuildTime']) ? $vars['SectionsParsedRebuildTime'] : 10; - CacheSettings::$domainsParsedRebuildTime = isset($vars['DomainsParsedRebuildTime']) ? $vars['DomainsParsedRebuildTime'] : 2; + CacheSettings::$unitCacheRebuildTime = $vars['UnitCacheRebuildTime']; + CacheSettings::$structureTreeRebuildTime = $vars['StructureTreeRebuildTime']; + CacheSettings::$cmsMenuRebuildTime = $vars['CmsMenuRebuildTime']; + CacheSettings::$templateMappingRebuildTime = $vars['TemplateMappingRebuildTime']; + CacheSettings::$sectionsParsedRebuildTime = $vars['SectionsParsedRebuildTime']; + CacheSettings::$domainsParsedRebuildTime = $vars['DomainsParsedRebuildTime']; class MaintenanceMode { const NONE = 0; const SOFT = 1; const HARD = 2; } unset($vars); // just in case someone will be still using it if (ini_get('safe_mode')) { // safe mode will be removed at all in PHP6 define('SAFE_MODE', 1); } if (file_exists(WRITEABLE . '/debug.php')) { include_once(WRITEABLE . '/debug.php'); if (array_key_exists('DEBUG_MODE', $dbg_options) && $dbg_options['DEBUG_MODE']) { $debugger_start = microtime(true); include_once(KERNEL_PATH . '/utility/debugger.php'); $debugger_end = microtime(true); if (isset($debugger) && kUtil::constOn('DBG_PROFILE_INCLUDES')) { $debugger->profileStart('inc_globals', KERNEL_PATH . '/globals.php', $globals_start); $debugger->profileFinish('inc_globals', KERNEL_PATH . '/globals.php', $globals_end); $debugger->profilerAddTotal('includes', 'inc_globals'); $debugger->profileStart('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_start); $debugger->profileFinish('inc_debugger', KERNEL_PATH . '/utility/debugger.php', $debugger_end); $debugger->profilerAddTotal('includes', 'inc_debugger'); } } } kUtil::safeDefine('SILENT_LOG', 0); // can be set in "debug.php" too $includes = Array( KERNEL_PATH . "/interfaces/cacheable.php", KERNEL_PATH . '/application.php', FULL_PATH . APPLICATION_PATH, KERNEL_PATH . "/kbase.php", KERNEL_PATH . '/db/i_db_connection.php', KERNEL_PATH . '/db/db_connection.php', KERNEL_PATH . '/db/db_load_balancer.php', KERNEL_PATH . '/utility/event.php', KERNEL_PATH . '/utility/logger.php', KERNEL_PATH . "/utility/factory.php", KERNEL_PATH . "/languages/phrases_cache.php", KERNEL_PATH . "/db/dblist.php", KERNEL_PATH . "/db/dbitem.php", KERNEL_PATH . "/event_handler.php", KERNEL_PATH . '/db/db_event_handler.php', FULL_PATH . '/vendor/autoload.php', ); foreach ($includes as $a_file) { kUtil::includeOnce($a_file); } if (defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger)) { $debugger->AttachToApplication(); } if( !function_exists('adodb_mktime') ) { include_once(KERNEL_PATH . '/utility/adodb-time.inc.php'); } // system users define('USER_ROOT', -1); define('USER_GUEST', -2); Index: branches/5.2.x/core/kernel/application.php =================================================================== --- branches/5.2.x/core/kernel/application.php (revision 16434) +++ branches/5.2.x/core/kernel/application.php (revision 16435) @@ -1,3098 +1,3099 @@ * The class encapsulates the main run-cycle of the script, provide access to all other objects in the framework.
*
* The class is a singleton, which means that there could be only one instance of kApplication in the script.
* This could be guaranteed by NOT calling the class constructor directly, but rather calling kApplication::Instance() method, * which returns an instance of the application. The method guarantees that it will return exactly the same instance for any call.
* See singleton pattern by GOF. */ class kApplication implements kiCacheable { /** * Location of module helper class (used in installator too) */ const MODULE_HELPER_PATH = '/../units/helpers/modules_helper.php'; /** * Is true, when Init method was called already, prevents double initialization * * @var bool */ public $InitDone = false; /** * Holds internal NParser object * * @var NParser * @access public */ public $Parser; /** * Holds parser output buffer * * @var string * @access protected */ protected $HTML = ''; /** * The main Factory used to create * almost any class of kernel and * modules * * @var kFactory * @access protected */ protected $Factory; /** * Template names, that will be used instead of regular templates * * @var Array * @access public */ public $ReplacementTemplates = Array (); /** * Mod-Rewrite listeners used during url building and parsing * * @var Array * @access public */ public $RewriteListeners = Array (); /** * Reference to debugger * * @var Debugger * @access public */ public $Debugger = null; /** * Holds all phrases used * in code and template * * @var PhrasesCache * @access public */ public $Phrases; /** * Modules table content, key - module name * * @var Array * @access public */ public $ModuleInfo = Array (); /** * Holds DBConnection * * @var IDBConnection * @access public */ public $Conn = null; /** * Reference to event log * * @var Array|kLogger * @access public */ protected $_logger = Array (); // performance needs: /** * Holds a reference to httpquery * * @var kHttpQuery * @access public */ public $HttpQuery = null; /** * Holds a reference to UnitConfigReader * * @var kUnitConfigReader * @access public */ public $UnitConfigReader = null; /** * Holds a reference to Session * * @var Session * @access public */ public $Session = null; /** * Holds a ref to kEventManager * * @var kEventManager * @access public */ public $EventManager = null; /** * Holds a ref to kUrlManager * * @var kUrlManager * @access public */ public $UrlManager = null; /** * Ref for TemplatesCache * * @var TemplatesCache * @access public */ public $TemplatesCache = null; /** * Holds current NParser tag while parsing, can be used in error messages to display template file and line * * @var _BlockTag * @access public */ public $CurrentNTag = null; /** * Object of unit caching class * * @var kCacheManager * @access public */ public $cacheManager = null; /** * Tells, that administrator has authenticated in administrative console * Should be used to manipulate data change OR data restrictions! * * @var bool * @access public */ public $isAdminUser = false; /** * Tells, that admin version of "index.php" was used, nothing more! * Should be used to manipulate data display! * * @var bool * @access public */ public $isAdmin = false; /** * Instance of site domain object * * @var kDBItem * @access public * @todo move away into separate module */ public $siteDomain = null; /** * Prevent kApplication class to be created directly, only via Instance method * * @access private */ private function __construct() { } final private function __clone() {} /** * Returns kApplication instance anywhere in the script. * * This method should be used to get single kApplication object instance anywhere in the * Kernel-based application. The method is guaranteed to return the SAME instance of kApplication. * Anywhere in the script you could write: * * $application =& kApplication::Instance(); * * or in an object: * * $this->Application =& kApplication::Instance(); * * to get the instance of kApplication. Note that we call the Instance method as STATIC - directly from the class. * To use descendant of standard kApplication class in your project you would need to define APPLICATION_CLASS constant * BEFORE calling kApplication::Instance() for the first time. If APPLICATION_CLASS is not defined the method would * create and return default KernelApplication instance. * * Pattern: Singleton * * @static * @return kApplication * @access public */ public static function &Instance() { static $instance = false; if ( !$instance ) { $class = defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication'; $instance = new $class(); } return $instance; } /** * Initializes the Application * * @param string $factory_class * @return bool Was Init actually made now or before * @access public * @see kHTTPQuery * @see Session * @see TemplatesCache */ public function Init($factory_class = 'kFactory') { if ( $this->InitDone ) { return false; } if ( preg_match('/utf-8/i', CHARSET) ) { setlocale(LC_ALL, 'en_US.UTF-8'); mb_internal_encoding('UTF-8'); } $this->isAdmin = kUtil::constOn('ADMIN'); if ( !kUtil::constOn('SKIP_OUT_COMPRESSION') ) { ob_start(); // collect any output from method (other then tags) into buffer } if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Init:'); } $this->_logger = new kLogger($this->_logger); $this->Factory = new $factory_class(); $this->registerDefaultClasses(); - $vars = kUtil::parseConfig(true); + $system_config = new kSystemConfig(true); + $vars = $system_config->getData(); $db_class = isset($vars['Databases']) ? 'kDBLoadBalancer' : ($this->isDebugMode() ? 'kDBConnectionDebug' : 'kDBConnection'); $this->Conn = $this->Factory->makeClass($db_class, Array (SQL_TYPE, Array ($this->_logger, 'handleSQLError'))); $this->Conn->setup($vars); $this->cacheManager = $this->makeClass('kCacheManager'); $this->cacheManager->InitCache(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Before UnitConfigReader'); } // init config reader and all managers $this->UnitConfigReader = $this->makeClass('kUnitConfigReader'); $this->UnitConfigReader->scanModules(MODULES_PATH); // will also set RewriteListeners when existing cache is read $this->registerModuleConstants(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('After UnitConfigReader'); } define('MOD_REWRITE', $this->ConfigValue('UseModRewrite') && !$this->isAdmin ? 1 : 0); // start processing request $this->HttpQuery = $this->recallObject('HTTPQuery'); $this->HttpQuery->process(); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed HTTPQuery initial'); } $this->Session = $this->recallObject('Session'); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed Session'); } $this->Session->ValidateExpired(); // needs mod_rewrite url already parsed to keep user at proper template after session expiration if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed HTTPQuery AfterInit'); } $this->cacheManager->LoadApplicationCache(); $site_timezone = $this->ConfigValue('Config_Site_Time'); if ( $site_timezone ) { date_default_timezone_set($site_timezone); } if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Loaded cache and phrases'); } $this->ValidateLogin(); // must be called before AfterConfigRead, because current user should be available there $this->UnitConfigReader->AfterConfigRead(); // will set RewriteListeners when missing cache is built first time if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendTimestamp('Processed AfterConfigRead'); } if ( $this->GetVar('m_cat_id') === false ) { $this->SetVar('m_cat_id', 0); } if ( !$this->RecallVar('curr_iso') && !(defined('IS_INSTALL') && IS_INSTALL) ) { $this->StoreVar('curr_iso', $this->GetPrimaryCurrency(), true); // true for optional } $visit_id = $this->RecallVar('visit_id'); if ( $visit_id !== false ) { $this->SetVar('visits_id', $visit_id); } if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->profileFinish('kernel4_startup'); } $this->InitDone = true; if ( PHP_SAPI !== 'cli' && !$this->isAdmin ) { $this->HandleEvent(new kEvent('adm:OnStartup')); } return true; } /** * Performs initialization of manager classes, that can be overridden from unit configs * * @return void * @access public * @throws Exception */ public function InitManagers() { if ( $this->InitDone ) { throw new Exception('Duplicate call of ' . __METHOD__, E_USER_ERROR); return; } $this->UrlManager = $this->makeClass('kUrlManager'); $this->EventManager = $this->makeClass('EventManager'); $this->Phrases = $this->makeClass('kPhraseCache'); $this->RegisterDefaultBuildEvents(); } /** * Returns module information. Searches module by requested field * * @param string $field * @param mixed $value * @param string $return_field field value to returns, if not specified, then return all fields * @return Array */ public function findModule($field, $value, $return_field = null) { $found = $module_info = false; foreach ($this->ModuleInfo as $module_info) { if ( strtolower($module_info[$field]) == strtolower($value) ) { $found = true; break; } } if ( $found ) { return isset($return_field) ? $module_info[$return_field] : $module_info; } return false; } /** * Refreshes information about loaded modules * * @return void * @access public */ public function refreshModuleInfo() { if ( defined('IS_INSTALL') && IS_INSTALL && !$this->TableFound('Modules', true) ) { $this->registerModuleConstants(); return; } // use makeClass over recallObject, since used before kApplication initialization during installation $modules_helper = $this->makeClass('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $this->Conn->nextQueryCachable = true; $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'Modules WHERE ' . $modules_helper->getWhereClause() . ' ORDER BY LoadOrder'; $this->ModuleInfo = $this->Conn->Query($sql, 'Name'); $this->registerModuleConstants(); } /** * Checks if passed language id if valid and sets it to primary otherwise * * @return void * @access public */ public function VerifyLanguageId() { /** @var LanguagesItem $lang */ $lang = $this->recallObject('lang.current'); if ( !$lang->isLoaded() || (!$this->isAdmin && !$lang->GetDBField('Enabled')) ) { if ( !defined('IS_INSTALL') ) { $this->ApplicationDie('Unknown or disabled language'); } } } /** * Checks if passed theme id if valid and sets it to primary otherwise * * @return void * @access public */ public function VerifyThemeId() { if ( $this->isAdmin ) { kUtil::safeDefine('THEMES_PATH', '/core/admin_templates'); return; } $path = $this->GetFrontThemePath(); if ( $path === false ) { $this->ApplicationDie('No Primary Theme Selected or Current Theme is Unknown or Disabled'); } kUtil::safeDefine('THEMES_PATH', $path); } /** * Returns relative path to current front-end theme * * @param bool $force * @return string * @access public */ public function GetFrontThemePath($force = false) { static $path = null; if ( !$force && isset($path) ) { return $path; } /** @var ThemeItem $theme */ $theme = $this->recallObject('theme.current'); if ( !$theme->isLoaded() || !$theme->GetDBField('Enabled') ) { return false; } // assign & then return, since it's static variable $path = '/themes/' . $theme->GetDBField('Name'); return $path; } /** * Returns primary front/admin language id * * @param bool $init * @return int * @access public */ public function GetDefaultLanguageId($init = false) { $cache_key = 'primary_language_info[%LangSerial%]'; $language_info = $this->getCache($cache_key); if ( $language_info === false ) { // cache primary language info first $table = $this->getUnitOption('lang', 'TableName'); $id_field = $this->getUnitOption('lang', 'IDField'); $this->Conn->nextQueryCachable = true; $sql = 'SELECT ' . $id_field . ', IF(AdminInterfaceLang, "Admin", "Front") AS LanguageKey FROM ' . $table . ' WHERE (AdminInterfaceLang = 1 OR PrimaryLang = 1) AND (Enabled = 1)'; $language_info = $this->Conn->GetCol($sql, 'LanguageKey'); if ( $language_info !== false ) { $this->setCache($cache_key, $language_info); } } $language_key = ($this->isAdmin && $init) || count($language_info) == 1 ? 'Admin' : 'Front'; if ( array_key_exists($language_key, $language_info) && $language_info[$language_key] > 0 ) { // get from cache return $language_info[$language_key]; } $language_id = $language_info && array_key_exists($language_key, $language_info) ? $language_info[$language_key] : false; if ( !$language_id && defined('IS_INSTALL') && IS_INSTALL ) { $language_id = 1; } return $language_id; } /** * Returns front-end primary theme id (even, when called from admin console) * * @param bool $force_front * @return int * @access public */ public function GetDefaultThemeId($force_front = false) { static $theme_id = 0; if ( $theme_id > 0 ) { return $theme_id; } if ( kUtil::constOn('DBG_FORCE_THEME') ) { $theme_id = DBG_FORCE_THEME; } elseif ( !$force_front && $this->isAdmin ) { $theme_id = 999; } else { $cache_key = 'primary_theme[%ThemeSerial%]'; $theme_id = $this->getCache($cache_key); if ( $theme_id === false ) { $this->Conn->nextQueryCachable = true; $sql = 'SELECT ' . $this->getUnitOption('theme', 'IDField') . ' FROM ' . $this->getUnitOption('theme', 'TableName') . ' WHERE (PrimaryTheme = 1) AND (Enabled = 1)'; $theme_id = $this->Conn->GetOne($sql); if ( $theme_id !== false ) { $this->setCache($cache_key, $theme_id); } } } return $theme_id; } /** * Returns site primary currency ISO code * * @return string * @access public * @todo Move into In-Commerce */ public function GetPrimaryCurrency() { $cache_key = 'primary_currency[%CurrSerial%][%SiteDomainSerial%]:' . $this->siteDomainField('DomainId'); $currency_iso = $this->getCache($cache_key); if ( $currency_iso === false ) { if ( $this->prefixRegistred('curr') ) { $this->Conn->nextQueryCachable = true; $currency_id = $this->siteDomainField('PrimaryCurrencyId'); $sql = 'SELECT ISO FROM ' . $this->getUnitOption('curr', 'TableName') . ' WHERE ' . ($currency_id > 0 ? 'CurrencyId = ' . $currency_id : 'IsPrimary = 1'); $currency_iso = $this->Conn->GetOne($sql); } else { $currency_iso = 'USD'; } $this->setCache($cache_key, $currency_iso); } return $currency_iso; } /** * Returns site domain field. When none of site domains are found false is returned. * * @param string $field * @param bool $formatted * @param string $format * @return mixed * @todo Move into separate module */ public function siteDomainField($field, $formatted = false, $format = null) { if ( $this->isAdmin ) { // don't apply any filtering in administrative console return false; } if ( !$this->siteDomain ) { $this->siteDomain = $this->recallObject('site-domain.current', null, Array ('live_table' => true)); /* @var $site_domain kDBItem */ } if ( $this->siteDomain->isLoaded() ) { return $formatted ? $this->siteDomain->GetField($field, $format) : $this->siteDomain->GetDBField($field); } return false; } /** * Registers default classes such as kDBEventHandler, kUrlManager * * Called automatically while initializing kApplication * * @return void * @access public */ public function RegisterDefaultClasses() { $this->registerClass('kHelper', KERNEL_PATH . '/kbase.php'); $this->registerClass('kMultipleFilter', KERNEL_PATH . '/utility/filters.php'); $this->registerClass('kiCacheable', KERNEL_PATH . '/interfaces/cacheable.php'); $this->registerClass('kEventManager', KERNEL_PATH . '/event_manager.php', 'EventManager'); $this->registerClass('kHookManager', KERNEL_PATH . '/managers/hook_manager.php'); $this->registerClass('kScheduledTaskManager', KERNEL_PATH . '/managers/scheduled_task_manager.php'); $this->registerClass('kRequestManager', KERNEL_PATH . '/managers/request_manager.php'); $this->registerClass('kSubscriptionManager', KERNEL_PATH . '/managers/subscription_manager.php'); $this->registerClass('kSubscriptionItem', KERNEL_PATH . '/managers/subscription_manager.php'); $this->registerClass('kUrlManager', KERNEL_PATH . '/managers/url_manager.php'); $this->registerClass('kUrlProcessor', KERNEL_PATH . '/managers/url_processor.php'); $this->registerClass('kPlainUrlProcessor', KERNEL_PATH . '/managers/plain_url_processor.php'); $this->registerClass('kRewriteUrlProcessor', KERNEL_PATH . '/managers/rewrite_url_processor.php'); $this->registerClass('kCacheManager', KERNEL_PATH . '/managers/cache_manager.php'); $this->registerClass('PhrasesCache', KERNEL_PATH . '/languages/phrases_cache.php', 'kPhraseCache'); $this->registerClass('kTempTablesHandler', KERNEL_PATH . '/utility/temp_handler.php'); $this->registerClass('kValidator', KERNEL_PATH . '/utility/validator.php'); $this->registerClass('kOpenerStack', KERNEL_PATH . '/utility/opener_stack.php'); $this->registerClass('kLogger', KERNEL_PATH . '/utility/logger.php'); $this->registerClass('kUnitConfigReader', KERNEL_PATH . '/utility/unit_config_reader.php'); $this->registerClass('PasswordHash', KERNEL_PATH . '/utility/php_pass.php'); // Params class descendants $this->registerClass('kArray', KERNEL_PATH . '/utility/params.php'); $this->registerClass('Params', KERNEL_PATH . '/utility/params.php'); $this->registerClass('Params', KERNEL_PATH . '/utility/params.php', 'kActions'); $this->registerClass('kCache', KERNEL_PATH . '/utility/cache.php', 'kCache', 'Params'); $this->registerClass('kHTTPQuery', KERNEL_PATH . '/utility/http_query.php', 'HTTPQuery'); // session $this->registerClass('Session', KERNEL_PATH . '/session/session.php'); $this->registerClass('SessionStorage', KERNEL_PATH . '/session/session_storage.php'); $this->registerClass('InpSession', KERNEL_PATH . '/session/inp_session.php', 'Session'); $this->registerClass('InpSessionStorage', KERNEL_PATH . '/session/inp_session_storage.php', 'SessionStorage'); // template parser $this->registerClass('kTagProcessor', KERNEL_PATH . '/processors/tag_processor.php'); $this->registerClass('kMainTagProcessor', KERNEL_PATH . '/processors/main_processor.php', 'm_TagProcessor'); $this->registerClass('kDBTagProcessor', KERNEL_PATH . '/db/db_tag_processor.php'); $this->registerClass('kCatDBTagProcessor', KERNEL_PATH . '/db/cat_tag_processor.php'); $this->registerClass('NParser', KERNEL_PATH . '/nparser/nparser.php'); $this->registerClass('TemplatesCache', KERNEL_PATH . '/nparser/template_cache.php'); // database $this->registerClass('kDBConnection', KERNEL_PATH . '/db/db_connection.php'); $this->registerClass('kDBConnectionDebug', KERNEL_PATH . '/db/db_connection.php'); $this->registerClass('kDBLoadBalancer', KERNEL_PATH . '/db/db_load_balancer.php'); $this->registerClass('kDBItem', KERNEL_PATH . '/db/dbitem.php'); $this->registerClass('kCatDBItem', KERNEL_PATH . '/db/cat_dbitem.php'); $this->registerClass('kDBList', KERNEL_PATH . '/db/dblist.php'); $this->registerClass('kCatDBList', KERNEL_PATH . '/db/cat_dblist.php'); $this->registerClass('kDBEventHandler', KERNEL_PATH . '/db/db_event_handler.php'); $this->registerClass('kCatDBEventHandler', KERNEL_PATH . '/db/cat_event_handler.php'); // email sending $this->registerClass('kEmail', KERNEL_PATH . '/utility/email.php'); $this->registerClass('kEmailSendingHelper', KERNEL_PATH . '/utility/email_send.php', 'EmailSender'); $this->registerClass('kSocket', KERNEL_PATH . '/utility/socket.php', 'Socket'); // do not move to config - this helper is used before configs are read $this->registerClass('kModulesHelper', KERNEL_PATH . self::MODULE_HELPER_PATH, 'ModulesHelper'); } /** * Registers default build events * * @return void */ public function RegisterDefaultBuildEvents() { $this->EventManager->registerBuildEvent('kTempTablesHandler', 'OnTempHandlerBuild'); } /** * Returns cached category information by given cache name. All given category * information is recached, when at least one of 4 caches is missing. * * @param int $category_id * @param string $name cache name = {filenames, category_designs, category_tree} * @return string * @access public */ public function getCategoryCache($category_id, $name) { return $this->cacheManager->getCategoryCache($category_id, $name); } /** * Returns caching type (none, memory, temporary) * * @param int $caching_type * @return bool * @access public */ public function isCachingType($caching_type) { return $this->cacheManager->isCachingType($caching_type); } /** * Increments serial based on prefix and it's ID (optional) * * @param string $prefix * @param int $id ID (value of IDField) or ForeignKeyField:ID * @param bool $increment * @return string * @access public */ public function incrementCacheSerial($prefix, $id = null, $increment = true) { return $this->cacheManager->incrementCacheSerial($prefix, $id, $increment); } /** * Returns cached $key value from cache named $cache_name * * @param int $key key name from cache * @param bool $store_locally store data locally after retrieved * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getCache($key, $store_locally = true, $max_rebuild_seconds = 0) { return $this->cacheManager->getCache($key, $store_locally, $max_rebuild_seconds); } /** * Stores new $value in cache with $key name * * @param int $key key name to add to cache * @param mixed $value value of cached record * @param int $expiration when value expires (0 - doesn't expire) * @return bool * @access public */ public function setCache($key, $value, $expiration = 0) { return $this->cacheManager->setCache($key, $value, $expiration); } /** * Stores new $value in cache with $key name (only if it's not there) * * @param int $key key name to add to cache * @param mixed $value value of cached record * @param int $expiration when value expires (0 - doesn't expire) * @return bool * @access public */ public function addCache($key, $value, $expiration = 0) { return $this->cacheManager->addCache($key, $value, $expiration); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @return bool * @access public */ public function rebuildCache($name, $mode = null, $max_rebuilding_time = 0) { return $this->cacheManager->rebuildCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from cache * * @param string $key * @return void * @access public */ public function deleteCache($key) { $this->cacheManager->deleteCache($key); } /** * Reset's all memory cache at once * * @return void * @access public */ public function resetCache() { $this->cacheManager->resetCache(); } /** * Returns value from database cache * * @param string $name key name * @param int $max_rebuild_seconds * @return mixed * @access public */ public function getDBCache($name, $max_rebuild_seconds = 0) { return $this->cacheManager->getDBCache($name, $max_rebuild_seconds); } /** * Sets value to database cache * * @param string $name * @param mixed $value * @param int|bool $expiration * @return void * @access public */ public function setDBCache($name, $value, $expiration = false) { $this->cacheManager->setDBCache($name, $value, $expiration); } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @return bool * @access public */ public function rebuildDBCache($name, $mode = null, $max_rebuilding_time = 0) { return $this->cacheManager->rebuildDBCache($name, $mode, $max_rebuilding_time); } /** * Deletes key from database cache * * @param string $name * @return void * @access public */ public function deleteDBCache($name) { $this->cacheManager->deleteDBCache($name); } /** * Registers each module specific constants if any found * * @return bool * @access protected */ protected function registerModuleConstants() { if ( file_exists(KERNEL_PATH . '/constants.php') ) { kUtil::includeOnce(KERNEL_PATH . '/constants.php'); } if ( !$this->ModuleInfo ) { return false; } foreach ($this->ModuleInfo as $module_info) { $constants_file = FULL_PATH . '/' . $module_info['Path'] . 'constants.php'; if ( file_exists($constants_file) ) { kUtil::includeOnce($constants_file); } } return true; } /** * Performs redirect to hard maintenance template * * @return void * @access public */ public function redirectToMaintenance() { $maintenance_page = WRITEBALE_BASE . '/maintenance.html'; $query_string = ''; // $this->isAdmin ? '' : '?next_template=' . kUtil::escape($_SERVER['REQUEST_URI'], kUtil::ESCAPE_URL); if ( file_exists(FULL_PATH . $maintenance_page) ) { header('Location: ' . BASE_PATH . $maintenance_page . $query_string); exit; } } /** * Actually runs the parser against current template and stores parsing result * * This method gets 't' variable passed to the script, loads the template given in 't' variable and * parses it. The result is store in {@link $this->HTML} property. * * @return void * @access public */ public function Run() { // process maintenance mode redirect: begin $maintenance_mode = $this->getMaintenanceMode(); if ( $maintenance_mode == MaintenanceMode::HARD ) { $this->redirectToMaintenance(); } elseif ( $maintenance_mode == MaintenanceMode::SOFT ) { $maintenance_template = $this->isAdmin ? 'login' : $this->ConfigValue('SoftMaintenanceTemplate'); if ( $this->GetVar('t') != $maintenance_template ) { $redirect_params = Array (); if ( !$this->isAdmin ) { $redirect_params['next_template'] = $_SERVER['REQUEST_URI']; } $this->Redirect($maintenance_template, $redirect_params); } } // process maintenance mode redirect: end if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Run:'); } if ( $this->isAdminUser ) { // for permission checking in events & templates $this->LinkVar('module'); // for common configuration templates $this->LinkVar('module_key'); // for common search templates $this->LinkVar('section'); // for common configuration templates if ( $this->GetVar('m_opener') == 'p' ) { $this->LinkVar('main_prefix'); // window prefix, that opened selector $this->LinkVar('dst_field'); // field to set value choosed in selector } if ( $this->GetVar('ajax') == 'yes' && !$this->GetVar('debug_ajax') ) { // hide debug output from ajax requests automatically kUtil::safeDefine('DBG_SKIP_REPORTING', 1); // safeDefine, because debugger also defines it } } elseif ( $this->GetVar('admin') ) { $admin_session = $this->recallObject('Session.admin'); /* @var $admin_session Session */ // store Admin Console User's ID to Front-End's session for cross-session permission checks $this->StoreVar('admin_user_id', (int)$admin_session->RecallVar('user_id')); if ( $this->CheckAdminPermission('CATEGORY.MODIFY', 0, $this->getBaseCategory()) ) { // user can edit cms blocks (when viewing front-end through admin's frame) $editing_mode = $this->GetVar('editing_mode'); define('EDITING_MODE', $editing_mode ? $editing_mode : EDITING_MODE_BROWSE); } } kUtil::safeDefine('EDITING_MODE', ''); // user can't edit anything $this->Phrases->setPhraseEditing(); $this->EventManager->ProcessRequest(); $this->InitParser(); $t = $this->GetVar('render_template', $this->GetVar('t')); if ( !$this->TemplatesCache->TemplateExists($t) && !$this->isAdmin ) { $cms_handler = $this->recallObject('st_EventHandler'); /* @var $cms_handler CategoriesEventHandler */ $t = ltrim($cms_handler->GetDesignTemplate(), '/'); if ( defined('DEBUG_MODE') && $this->isDebugMode() ) { $this->Debugger->appendHTML('Design Template: ' . $t . '; CategoryID: ' . $this->GetVar('m_cat_id')); } } /*else { $cms_handler->SetCatByTemplate(); }*/ if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Parsing:'); } $this->HTML = $this->Parser->Run($t); if ( defined('DEBUG_MODE') && $this->isDebugMode() && kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application after Parsing:'); } } /** * Replaces current rendered template with given one. * * @param string|null $template Template. * * @return void */ public function QuickRun($template) { /** @var kThemesHelper $themes_helper */ $themes_helper = $this->recallObject('ThemesHelper'); // Set Web Request variables to affect link building on template itself. $this->SetVar('t', $template); $this->SetVar('m_cat_id', $themes_helper->getPageByTemplate($template)); $this->SetVar('passed', 'm'); // Replace current page content with given template. $this->InitParser(); $this->Parser->Clear(); $this->HTML = $this->Parser->Run($template); } /** * Performs template parser/cache initialization * * @param bool|string $theme_name * @return void * @access public */ public function InitParser($theme_name = false) { if ( !is_object($this->Parser) ) { $this->Parser = $this->recallObject('NParser'); $this->TemplatesCache = $this->recallObject('TemplatesCache'); } $this->TemplatesCache->forceThemeName = $theme_name; } /** * Send the parser results to browser * * Actually send everything stored in {@link $this->HTML}, to the browser by echoing it. * * @return void * @access public */ public function Done() { $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); $debug_mode = defined('DEBUG_MODE') && $this->isDebugMode(); if ( $debug_mode ) { if ( kUtil::constOn('DBG_PROFILE_MEMORY') ) { $this->Debugger->appendMemoryUsage('Application before Done:'); } $this->Session->SaveData(); // adds session data to debugger report $this->HTML = ob_get_clean() . $this->HTML . $this->Debugger->printReport(true); } else { // send "Set-Cookie" header before any output is made $this->Session->SetSession(); $this->HTML = ob_get_clean() . $this->HTML; } $this->_outputPage(); $this->cacheManager->UpdateApplicationCache(); if ( !$debug_mode ) { $this->Session->SaveData(); } $this->EventManager->runScheduledTasks(); if ( defined('DBG_CAPTURE_STATISTICS') && DBG_CAPTURE_STATISTICS && !$this->isAdmin ) { $this->_storeStatistics(); } } /** * Outputs generated page content to end-user * * @return void * @access protected */ protected function _outputPage() { $this->setContentType(); ob_start(); if ( $this->UseOutputCompression() ) { $compression_level = $this->ConfigValue('OutputCompressionLevel'); if ( !$compression_level || $compression_level < 0 || $compression_level > 9 ) { $compression_level = 7; } header('Content-Encoding: gzip'); echo gzencode($this->HTML, $compression_level); } else { // when gzip compression not used connection won't be closed early! echo $this->HTML; } // send headers to tell the browser to close the connection header('Content-Length: ' . ob_get_length()); header('Connection: close'); // flush all output ob_end_flush(); if ( ob_get_level() ) { ob_flush(); } flush(); // close current session if ( session_id() ) { session_write_close(); } } /** * Stores script execution statistics to database * * @return void * @access protected */ protected function _storeStatistics() { global $start; $script_time = microtime(true) - $start; $query_statistics = $this->Conn->getQueryStatistics(); // time & count $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'StatisticsCapture WHERE TemplateName = ' . $this->Conn->qstr($this->GetVar('t')); $data = $this->Conn->GetRow($sql); if ( $data ) { $this->_updateAverageStatistics($data, 'ScriptTime', $script_time); $this->_updateAverageStatistics($data, 'SqlTime', $query_statistics['time']); $this->_updateAverageStatistics($data, 'SqlCount', $query_statistics['count']); $data['Hits']++; $data['LastHit'] = adodb_mktime(); $this->Conn->doUpdate($data, TABLE_PREFIX . 'StatisticsCapture', 'StatisticsId = ' . $data['StatisticsId']); } else { $data['ScriptTimeMin'] = $data['ScriptTimeAvg'] = $data['ScriptTimeMax'] = $script_time; $data['SqlTimeMin'] = $data['SqlTimeAvg'] = $data['SqlTimeMax'] = $query_statistics['time']; $data['SqlCountMin'] = $data['SqlCountAvg'] = $data['SqlCountMax'] = $query_statistics['count']; $data['TemplateName'] = $this->GetVar('t'); $data['Hits'] = 1; $data['LastHit'] = adodb_mktime(); $this->Conn->doInsert($data, TABLE_PREFIX . 'StatisticsCapture'); } } /** * Calculates average time for statistics * * @param Array $data * @param string $field_prefix * @param float $current_value * @return void * @access protected */ protected function _updateAverageStatistics(&$data, $field_prefix, $current_value) { $data[$field_prefix . 'Avg'] = (($data['Hits'] * $data[$field_prefix . 'Avg']) + $current_value) / ($data['Hits'] + 1); if ( $current_value < $data[$field_prefix . 'Min'] ) { $data[$field_prefix . 'Min'] = $current_value; } if ( $current_value > $data[$field_prefix . 'Max'] ) { $data[$field_prefix . 'Max'] = $current_value; } } /** * Remembers slow query SQL and execution time into log * * @param string $slow_sql * @param int $time * @return void * @access public */ public function logSlowQuery($slow_sql, $time) { $query_crc = kUtil::crc32($slow_sql); $sql = 'SELECT * FROM ' . TABLE_PREFIX . 'SlowSqlCapture WHERE QueryCrc = ' . $query_crc; $data = $this->Conn->Query($sql, null, true); if ( $data ) { $this->_updateAverageStatistics($data, 'Time', $time); $template_names = explode(',', $data['TemplateNames']); array_push($template_names, $this->GetVar('t')); $data['TemplateNames'] = implode(',', array_unique($template_names)); $data['Hits']++; $data['LastHit'] = adodb_mktime(); $this->Conn->doUpdate($data, TABLE_PREFIX . 'SlowSqlCapture', 'CaptureId = ' . $data['CaptureId']); } else { $data['TimeMin'] = $data['TimeAvg'] = $data['TimeMax'] = $time; $data['SqlQuery'] = $slow_sql; $data['QueryCrc'] = $query_crc; $data['TemplateNames'] = $this->GetVar('t'); $data['Hits'] = 1; $data['LastHit'] = adodb_mktime(); $this->Conn->doInsert($data, TABLE_PREFIX . 'SlowSqlCapture'); } } /** * Checks if output compression options is available * * @return bool * @access protected */ protected function UseOutputCompression() { if ( kUtil::constOn('IS_INSTALL') || kUtil::constOn('DBG_ZEND_PRESENT') || kUtil::constOn('SKIP_OUT_COMPRESSION') ) { return false; } $accept_encoding = isset($_SERVER['HTTP_ACCEPT_ENCODING']) ? $_SERVER['HTTP_ACCEPT_ENCODING'] : ''; return $this->ConfigValue('UseOutputCompression') && function_exists('gzencode') && strstr($accept_encoding, 'gzip'); } // Facade /** * Returns current session id (SID) * * @return int * @access public */ public function GetSID() { $session = $this->recallObject('Session'); /* @var $session Session */ return $session->GetID(); } /** * Destroys current session * * @return void * @access public * @see UserHelper::logoutUser() */ public function DestroySession() { $session = $this->recallObject('Session'); /* @var $session Session */ $session->Destroy(); } /** * Returns variable passed to the script as GET/POST/COOKIE * * @param string $name Name of variable to retrieve * @param mixed $default default value returned in case if variable not present * @return mixed * @access public */ public function GetVar($name, $default = false) { return isset($this->HttpQuery->_Params[$name]) ? $this->HttpQuery->_Params[$name] : $default; } /** * Removes forceful escaping done to the variable upon Front-End submission. * * @param string|array $value Value. * * @return string|array * @see kHttpQuery::StripSlashes * @todo Temporary method for marking problematic places to take care of, when forceful escaping will be removed. */ public function unescapeRequestVariable($value) { return $this->HttpQuery->unescapeRequestVariable($value); } /** * Returns variable passed to the script as $type * * @param string $name Name of variable to retrieve * @param string $type Get/Post/Cookie * @param mixed $default default value returned in case if variable not present * @return mixed * @access public */ public function GetVarDirect($name, $type, $default = false) { // $type = ucfirst($type); $array = $this->HttpQuery->$type; return isset($array[$name]) ? $array[$name] : $default; } /** * Returns ALL variables passed to the script as GET/POST/COOKIE * * @return Array * @access public * @deprecated */ public function GetVars() { return $this->HttpQuery->GetParams(); } /** * Set the variable 'as it was passed to the script through GET/POST/COOKIE' * * This could be useful to set the variable when you know that * other objects would relay on variable passed from GET/POST/COOKIE * or you could use SetVar() / GetVar() pairs to pass the values between different objects.
* * @param string $var Variable name to set * @param mixed $val Variable value * @return void * @access public */ public function SetVar($var,$val) { $this->HttpQuery->Set($var, $val); } /** * Deletes kHTTPQuery variable * * @param string $var * @return void * @todo Think about method name */ public function DeleteVar($var) { $this->HttpQuery->Remove($var); } /** * Deletes Session variable * * @param string $var * @return void * @access public */ public function RemoveVar($var) { $this->Session->RemoveVar($var); } /** * Removes variable from persistent session * * @param string $var * @return void * @access public */ public function RemovePersistentVar($var) { $this->Session->RemovePersistentVar($var); } /** * Restores Session variable to it's db version * * @param string $var * @return void * @access public */ public function RestoreVar($var) { $this->Session->RestoreVar($var); } /** * Returns session variable value * * Return value of $var variable stored in Session. An optional default value could be passed as second parameter. * * @param string $var Variable name * @param mixed $default Default value to return if no $var variable found in session * @return mixed * @access public * @see Session::RecallVar() */ public function RecallVar($var,$default=false) { return $this->Session->RecallVar($var,$default); } /** * Returns variable value from persistent session * * @param string $var * @param mixed $default * @return mixed * @access public * @see Session::RecallPersistentVar() */ public function RecallPersistentVar($var, $default = false) { return $this->Session->RecallPersistentVar($var, $default); } /** * Stores variable $val in session under name $var * * Use this method to store variable in session. Later this variable could be recalled. * * @param string $var Variable name * @param mixed $val Variable value * @param bool $optional * @return void * @access public * @see kApplication::RecallVar() */ public function StoreVar($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVar($var, $val, $optional); } /** * Stores variable to persistent session * * @param string $var * @param mixed $val * @param bool $optional * @return void * @access public */ public function StorePersistentVar($var, $val, $optional = false) { $this->Session->StorePersistentVar($var, $val, $optional); } /** * Stores default value for session variable * * @param string $var * @param string $val * @param bool $optional * @return void * @access public * @see Session::RecallVar() * @see Session::StoreVar() */ public function StoreVarDefault($var, $val, $optional = false) { $session = $this->recallObject('Session'); /* @var $session Session */ $this->Session->StoreVarDefault($var, $val, $optional); } /** * Links HTTP Query variable with session variable * * If variable $var is passed in HTTP Query it is stored in session for later use. If it's not passed it's recalled from session. * This method could be used for making sure that GetVar will return query or session value for given * variable, when query variable should overwrite session (and be stored there for later use).
* This could be used for passing item's ID into popup with multiple tab - * in popup script you just need to call LinkVar('id', 'current_id') before first use of GetVar('id'). * After that you can be sure that GetVar('id') will return passed id or id passed earlier and stored in session * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @param bool $optional * @return void * @access public */ public function LinkVar($var, $ses_var = null, $default = '', $optional = false) { if ( !isset($ses_var) ) { $ses_var = $var; } if ( $this->GetVar($var) !== false ) { $this->StoreVar($ses_var, $this->GetVar($var), $optional); } else { $this->SetVar($var, $this->RecallVar($ses_var, $default)); } } /** * Returns variable from HTTP Query, or from session if not passed in HTTP Query * * The same as LinkVar, but also returns the variable value taken from HTTP Query if passed, or from session if not passed. * Returns the default value if variable does not exist in session and was not passed in HTTP Query * * @param string $var HTTP Query (GPC) variable name * @param mixed $ses_var Session variable name * @param mixed $default Default variable value * @return mixed * @access public * @see LinkVar */ public function GetLinkedVar($var, $ses_var = null, $default = '') { $this->LinkVar($var, $ses_var, $default); return $this->GetVar($var); } /** * Renders given tag and returns it's output * * @param string $prefix * @param string $tag * @param Array $params * @return mixed * @access public * @see kApplication::InitParser() */ public function ProcessParsedTag($prefix, $tag, $params) { $processor = $this->Parser->GetProcessor($prefix); /* @var $processor kDBTagProcessor */ return $processor->ProcessParsedTag($tag, $params, $prefix); } /** * Return object of IDBConnection interface * * Return object of IDBConnection interface already connected to the project database, configurable in config.php * * @return IDBConnection * @access public */ public function &GetADODBConnection() { return $this->Conn; } /** * Allows to parse given block name or include template * * @param Array $params Parameters to pass to block. Reserved parameter "name" used to specify block name. * @param bool $pass_params Forces to pass current parser params to this block/template. Use with caution, because you can accidentally pass "block_no_data" parameter. * @param bool $as_template * @return string * @access public */ public function ParseBlock($params, $pass_params = false, $as_template = false) { if ( substr($params['name'], 0, 5) == 'html:' ) { return substr($params['name'], 5); } return $this->Parser->ParseBlock($params, $pass_params, $as_template); } /** * Checks, that we have given block defined * * @param string $name * @return bool * @access public */ public function ParserBlockFound($name) { return $this->Parser->blockFound($name); } /** * Allows to include template with a given name and given parameters * * @param Array $params Parameters to pass to template. Reserved parameter "name" used to specify template name. * @return string * @access public */ public function IncludeTemplate($params) { return $this->Parser->IncludeTemplate($params, isset($params['is_silent']) ? 1 : 0); } /** * Return href for template * * @param string $t Template path * @param string $prefix index.php prefix - could be blank, 'admin' * @param Array $params * @param string $index_file * @return string */ public function HREF($t, $prefix = '', $params = Array (), $index_file = null) { return $this->UrlManager->HREF($t, $prefix, $params, $index_file); } /** * Returns theme template filename and it's corresponding page_id based on given seo template * * @param string $seo_template * @return string * @access public */ public function getPhysicalTemplate($seo_template) { return $this->UrlManager->getPhysicalTemplate($seo_template); } /** * Returns template name, that corresponds with given virtual (not physical) page id * * @param int $page_id * @return string|bool * @access public */ public function getVirtualPageTemplate($page_id) { return $this->UrlManager->getVirtualPageTemplate($page_id); } /** * Returns section template for given physical/virtual template * * @param string $template * @param int $theme_id * @return string * @access public */ public function getSectionTemplate($template, $theme_id = null) { return $this->UrlManager->getSectionTemplate($template, $theme_id); } /** * Returns variables with values that should be passed through with this link + variable list * * @param Array $params * @return Array * @access public */ public function getPassThroughVariables(&$params) { return $this->UrlManager->getPassThroughVariables($params); } /** * Builds url * * @param string $t * @param Array $params * @param string $pass * @param bool $pass_events * @param bool $env_var * @return string * @access public */ public function BuildEnv($t, $params, $pass = 'all', $pass_events = false, $env_var = true) { return $this->UrlManager->plain->build($t, $params, $pass, $pass_events, $env_var); } /** * Process QueryString only, create * events, ids, based on config * set template name and sid in * desired application variables. * * @param string $env_var environment string value * @param string $pass_name * @return Array * @access public */ public function processQueryString($env_var, $pass_name = 'passed') { return $this->UrlManager->plain->parse($env_var, $pass_name); } /** * Parses rewrite url and returns parsed variables * * @param string $url * @param string $pass_name * @return Array * @access public */ public function parseRewriteUrl($url, $pass_name = 'passed') { return $this->UrlManager->rewrite->parse($url, $pass_name); } /** * Returns base part of all urls, build on website * * @param string $prefix * @param bool $ssl * @param bool $add_port * @return string * @access public */ public function BaseURL($prefix = '', $ssl = null, $add_port = true) { if ( $ssl === null ) { // stay on same encryption level return PROTOCOL . SERVER_NAME . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/'; } if ( $ssl ) { // going from http:// to https:// $base_url = $this->isAdmin ? $this->ConfigValue('AdminSSL_URL') : false; if ( !$base_url ) { $ssl_url = $this->siteDomainField('SSLUrl'); $base_url = $ssl_url !== false ? $ssl_url : $this->ConfigValue('SSL_URL'); } return rtrim($base_url, '/') . $prefix . '/'; } // going from https:// to http:// $domain = $this->siteDomainField('DomainName'); if ( $domain === false ) { $domain = DOMAIN; } return 'http://' . $domain . ($add_port && defined('PORT') ? ':' . PORT : '') . BASE_PATH . $prefix . '/'; } /** * Redirects user to url, that's build based on given parameters * * @param string $t * @param Array $params * @param string $prefix * @param string $index_file * @return void * @access public */ public function Redirect($t = '', $params = Array(), $prefix = '', $index_file = null) { $js_redirect = getArrayValue($params, 'js_redirect'); if ( $t == '' || $t === true ) { $t = $this->GetVar('t'); } // pass prefixes and special from previous url if ( array_key_exists('js_redirect', $params) ) { unset($params['js_redirect']); } // allows to send custom responce code along with redirect header if ( array_key_exists('response_code', $params) ) { $response_code = (int)$params['response_code']; unset($params['response_code']); } else { $response_code = 302; // Found } if ( !array_key_exists('pass', $params) ) { $params['pass'] = 'all'; } if ( $this->GetVar('ajax') == 'yes' && $t == $this->GetVar('t') ) { // redirects to the same template as current $params['ajax'] = 'yes'; } $location = $this->HREF($t, $prefix, $params, $index_file); if ( $this->isDebugMode() && (kUtil::constOn('DBG_REDIRECT') || (kUtil::constOn('DBG_RAISE_ON_WARNINGS') && $this->Debugger->WarningCount)) ) { $this->Debugger->appendTrace(); echo 'Debug output above !!!
' . "\n"; if ( array_key_exists('HTTP_REFERER', $_SERVER) ) { echo 'Referer: ' . kUtil::escape($_SERVER['HTTP_REFERER'], kUtil::ESCAPE_HTML) . '
' . "\n"; } echo "Proceed to redirect: {$location}
\n"; } else { if ( $js_redirect ) { // show "redirect" template instead of redirecting, // because "Set-Cookie" header won't work, when "Location" // header is used later $this->SetVar('t', 'redirect'); $this->SetVar('redirect_to', $location); // make all additional parameters available on "redirect" template too foreach ($params as $name => $value) { $this->SetVar($name, $value); } return; } else { if ( $this->GetVar('ajax') == 'yes' && ($t != $this->GetVar('t') || !$this->isSOPSafe($location, $t)) ) { // redirection to other then current template during ajax request OR SOP violation kUtil::safeDefine('DBG_SKIP_REPORTING', 1); echo '#redirect#' . $location; } elseif ( headers_sent() != '' ) { // some output occurred -> redirect using javascript echo ''; } else { // no output before -> redirect using HTTP header // header('HTTP/1.1 302 Found'); header('Location: ' . $location, true, $response_code); } } } // session expiration is called from session initialization, // that's why $this->Session may be not defined here $session = $this->recallObject('Session'); /* @var $session Session */ if ( $this->InitDone ) { // if redirect happened in the middle of application initialization don't call event, // that presumes that application was successfully initialized $this->HandleEvent(new kEvent('adm:OnBeforeShutdown')); } $session->SaveData(); ob_end_flush(); exit; } /** * Determines if real redirect should be made within AJAX request. * * @param string $url Location. * @param string $template Template. * * @return boolean * @link http://en.wikipedia.org/wiki/Same-origin_policy */ protected function isSOPSafe($url, $template) { $parsed_url = parse_url($url); if ( $parsed_url['scheme'] . '://' != PROTOCOL ) { return false; } if ( $parsed_url['host'] != SERVER_NAME ) { return false; } if ( defined('PORT') && isset($parsed_url['port']) && $parsed_url['port'] != PORT ) { return false; } return true; } /** * Returns translation of given label * * @param string $label * @param bool $allow_editing return translation link, when translation is missing on current language * @param bool $use_admin use current Admin Console language to translate phrase * @return string * @access public */ public function Phrase($label, $allow_editing = true, $use_admin = false) { return $this->Phrases->GetPhrase($label, $allow_editing, $use_admin); } /** * Replace language tags in exclamation marks found in text * * @param string $text * @param bool $force_escape force escaping, not escaping of resulting string * @return string * @access public */ public function ReplaceLanguageTags($text, $force_escape = null) { return $this->Phrases->ReplaceLanguageTags($text, $force_escape); } /** * Checks if user is logged in, and creates * user object if so. User object can be recalled * later using "u.current" prefix_special. Also you may * get user id by getting "u.current_id" variable. * * @return void * @access protected */ protected function ValidateLogin() { $session = $this->recallObject('Session'); /* @var $session Session */ $user_id = $session->GetField('PortalUserId'); if ( !$user_id && $user_id != USER_ROOT ) { $user_id = USER_GUEST; } $this->SetVar('u.current_id', $user_id); if ( !$this->isAdmin ) { // needed for "profile edit", "registration" forms ON FRONT ONLY $this->SetVar('u_id', $user_id); } $this->StoreVar('user_id', $user_id, $user_id == USER_GUEST); // storing Guest user_id (-2) is optional $this->isAdminUser = $this->isAdmin && $this->LoggedIn(); if ( $this->GetVar('expired') == 1 ) { // this parameter is set only from admin $user = $this->recallObject('u.login-admin', null, Array ('form_name' => 'login')); /* @var $user UsersItem */ $user->SetError('UserLogin', 'session_expired', 'la_text_sess_expired'); } $this->HandleEvent(new kEvent('adm:OnLogHttpRequest')); if ( $user_id != USER_GUEST ) { // normal users + root $this->LoadPersistentVars(); } $user_timezone = $this->Session->GetField('TimeZone'); if ( $user_timezone ) { date_default_timezone_set($user_timezone); } } /** * Loads current user persistent session data * * @return void * @access public */ public function LoadPersistentVars() { $this->Session->LoadPersistentVars(); } /** * Returns configuration option value by name * * @param string $name * @return string * @access public */ public function ConfigValue($name) { return $this->cacheManager->ConfigValue($name); } /** * Changes value of individual configuration variable (+resets cache, when needed) * * @param string $name * @param string $value * @param bool $local_cache_only * @return string * @access public */ public function SetConfigValue($name, $value, $local_cache_only = false) { return $this->cacheManager->SetConfigValue($name, $value, $local_cache_only); } /** * Allows to process any type of event * * @param kEvent $event * @param Array $params * @param Array $specific_params * @return void * @access public */ public function HandleEvent($event, $params = null, $specific_params = null) { if ( isset($params) ) { $event = new kEvent($params, $specific_params); } $this->EventManager->HandleEvent($event); } /** * Notifies event subscribers, that event has occured * * @param kEvent $event * @return void */ public function notifyEventSubscribers(kEvent $event) { $this->EventManager->notifySubscribers($event); } /** * Allows to process any type of event * * @param kEvent $event * @return bool * @access public */ public function eventImplemented(kEvent $event) { return $this->EventManager->eventImplemented($event); } /** * Registers new class in the factory * * @param string $real_class Real name of class as in class declaration * @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 * @return void * @access public */ public function registerClass($real_class, $file, $pseudo_class = null) { $this->Factory->registerClass($real_class, $file, $pseudo_class); } /** * Unregisters existing class from factory * * @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 * @return void * @access public */ public function unregisterClass($real_class, $pseudo_class = null) { $this->Factory->unregisterClass($real_class, $pseudo_class); } /** * Finds the absolute path to the file where the class is defined. * * @param string $class The name of the class. * * @return string|false */ public function findClassFile($class) { return $this->Factory->findClassFile($class); } /** * Add new scheduled task * * @param string $short_name name to be used to store last maintenance run info * @param string $event_string * @param int $run_schedule run schedule like for Cron * @param int $status * @access public */ public function registerScheduledTask($short_name, $event_string, $run_schedule, $status = STATUS_ACTIVE) { $this->EventManager->registerScheduledTask($short_name, $event_string, $run_schedule, $status); } /** * Registers Hook from subprefix event to master prefix event * * Pattern: Observer * * @param string $hook_event * @param string $do_event * @param int $mode * @param bool $conditional * @access public */ public function registerHook($hook_event, $do_event, $mode = hAFTER, $conditional = false) { $this->EventManager->registerHook($hook_event, $do_event, $mode, $conditional); } /** * Registers build event for given pseudo class * * @param string $pseudo_class * @param string $event_name * @access public */ public function registerBuildEvent($pseudo_class, $event_name) { $this->EventManager->registerBuildEvent($pseudo_class, $event_name); } /** * Allows one TagProcessor tag act as other TagProcessor tag * * @param Array $tag_info * @return void * @access public */ public function registerAggregateTag($tag_info) { $aggregator = $this->recallObject('TagsAggregator', 'kArray'); /* @var $aggregator kArray */ $tag_data = Array ( $tag_info['LocalPrefix'], $tag_info['LocalTagName'], getArrayValue($tag_info, 'LocalSpecial') ); $aggregator->SetArrayValue($tag_info['AggregateTo'], $tag_info['AggregatedTagName'], $tag_data); } /** * Returns object using params specified, creates it if is required * * @param string $name * @param string $pseudo_class * @param Array $event_params * @param Array $arguments * @return kBase */ public function recallObject($name, $pseudo_class = null, array $event_params = array(), array $arguments = array()) { /*if ( !$this->hasObject($name) && $this->isDebugMode() && ($name == '_prefix_here_') ) { // first time, when object with "_prefix_here_" prefix is accessed $this->Debugger->appendTrace(); }*/ return $this->Factory->getObject($name, $pseudo_class, $event_params, $arguments); } /** * Returns tag processor for prefix specified * * @param string $prefix * @return kDBTagProcessor * @access public */ public function recallTagProcessor($prefix) { $this->InitParser(); // because kDBTagProcesor is in NParser dependencies return $this->recallObject($prefix . '_TagProcessor'); } /** * Checks if object with prefix passes was already created in factory * * @param string $name object pseudo_class, prefix * @return bool * @access public */ public function hasObject($name) { return $this->Factory->hasObject($name); } /** * Removes object from storage by given name * * @param string $name Object's name in the Storage * @return void * @access public */ public function removeObject($name) { $this->Factory->DestroyObject($name); } /** * Get's real class name for pseudo class, includes class file and creates class instance * * Pattern: Factory Method * * @param string $pseudo_class * @param Array $arguments * @return kBase * @access public */ public function makeClass($pseudo_class, array $arguments = array()) { return $this->Factory->makeClass($pseudo_class, $arguments); } /** * Checks if application is in debug mode * * @param bool $check_debugger check if kApplication debugger is initialized too, not only for defined DEBUG_MODE constant * @return bool * @author Alex * @access public */ public function isDebugMode($check_debugger = true) { $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE; if ($check_debugger) { $debug_mode = $debug_mode && is_object($this->Debugger); } return $debug_mode; } /** * Apply url rewriting used by mod_rewrite or not * * @param bool|null $ssl Force ssl link to be build * @return bool * @access public */ public function RewriteURLs($ssl = false) { // case #1,#4: // we want to create https link from http mode // we want to create https link from https mode // conditions: ($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL') // case #2,#3: // we want to create http link from https mode // we want to create http link from http mode // conditions: !$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://') $allow_rewriting = (!$ssl && (PROTOCOL == 'https://' || PROTOCOL == 'http://')) // always allow mod_rewrite for http || // or allow rewriting for redirect TO httpS or when already in httpS (($ssl || PROTOCOL == 'https://') && $this->ConfigValue('UseModRewriteWithSSL')); // but only if it's allowed in config! return kUtil::constOn('MOD_REWRITE') && $allow_rewriting; } /** * Reads unit (specified by $prefix) * option specified by $option * * @param string $prefix * @param string $option * @param mixed $default * @return string * @access public */ public function getUnitOption($prefix, $option, $default = false) { return $this->UnitConfigReader->getUnitOption($prefix, $option, $default); } /** * Set's new unit option value * * @param string $prefix * @param string $option * @param string $value * @access public */ public function setUnitOption($prefix, $option, $value) { $this->UnitConfigReader->setUnitOption($prefix,$option,$value); } /** * Read all unit with $prefix options * * @param string $prefix * @return Array * @access public */ public function getUnitOptions($prefix) { return $this->UnitConfigReader->getUnitOptions($prefix); } /** * Returns true if config exists and is allowed for reading * * @param string $prefix * @return bool */ public function prefixRegistred($prefix) { return $this->UnitConfigReader->prefixRegistred($prefix); } /** * Splits any mixing of prefix and * special into correct ones * * @param string $prefix_special * @return Array * @access public */ public function processPrefix($prefix_special) { return $this->Factory->processPrefix($prefix_special); } /** * Set's new event for $prefix_special * passed * * @param string $prefix_special * @param string $event_name * @return void * @access public */ public function setEvent($prefix_special, $event_name) { $this->EventManager->setEvent($prefix_special, $event_name); } /** * SQL Error Handler * * @param int $code * @param string $msg * @param string $sql * @return bool * @access public * @throws Exception * @deprecated */ public function handleSQLError($code, $msg, $sql) { return $this->_logger->handleSQLError($code, $msg, $sql); } /** * Returns & blocks next ResourceId available in system * * @return int * @access public */ public function NextResourceId() { $table_name = TABLE_PREFIX . 'IdGenerator'; $this->Conn->Query('LOCK TABLES ' . $table_name . ' WRITE'); $this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1'); $id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name); if ( $id === false ) { $this->Conn->Query('INSERT INTO ' . $table_name . ' (lastid) VALUES (2)'); $id = 2; } $this->Conn->Query('UNLOCK TABLES'); return $id - 1; } /** * Returns genealogical main prefix for sub-table prefix passes * OR prefix, that has been found in REQUEST and some how is parent of passed sub-table prefix * * @param string $current_prefix * @param bool $real_top if set to true will return real topmost prefix, regardless of its id is passed or not * @return string * @access public */ public function GetTopmostPrefix($current_prefix, $real_top = false) { // 1. get genealogical tree of $current_prefix $prefixes = Array ($current_prefix); while ($parent_prefix = $this->getUnitOption($current_prefix, 'ParentPrefix')) { if ( !$this->prefixRegistred($parent_prefix) ) { // stop searching, when parent prefix is not registered break; } $current_prefix = $parent_prefix; array_unshift($prefixes, $current_prefix); } if ( $real_top ) { return $current_prefix; } // 2. find what if parent is passed $passed = explode(',', $this->GetVar('all_passed')); foreach ($prefixes as $a_prefix) { if ( in_array($a_prefix, $passed) ) { return $a_prefix; } } return $current_prefix; } /** * Triggers email event of type Admin * * @param string $email_template_name * @param int $to_user_id * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access public */ public function emailAdmin($email_template_name, $to_user_id = null, $send_params = Array ()) { return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_ADMIN, $to_user_id, $send_params); } /** * Triggers email event of type User * * @param string $email_template_name * @param int $to_user_id * @param array $send_params associative array of direct send params, possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access public */ public function emailUser($email_template_name, $to_user_id = null, $send_params = Array ()) { return $this->_email($email_template_name, EmailTemplate::TEMPLATE_TYPE_FRONTEND, $to_user_id, $send_params); } /** * Triggers general email event * * @param string $email_template_name * @param int $email_template_type (0 for User, 1 for Admin) * @param int $to_user_id * @param array $send_params associative array of direct send params, * possible keys: to_email, to_name, from_email, from_name, message, message_text * @return kEvent * @access protected */ protected function _email($email_template_name, $email_template_type, $to_user_id = null, $send_params = Array ()) { $email = $this->makeClass('kEmail'); /* @var $email kEmail */ if ( !$email->findTemplate($email_template_name, $email_template_type) ) { return false; } $email->setParams($send_params); return $email->send($to_user_id); } /** * Allows to check if user in this session is logged in or not * * @return bool * @access public */ public function LoggedIn() { // no session during expiration process return is_null($this->Session) ? false : $this->Session->LoggedIn(); } /** * Check current user permissions based on it's group permissions in specified category * * @param string $name permission name * @param int $cat_id category id, current used if not specified * @param int $type permission type {1 - system, 0 - per category} * @return int * @access public */ public function CheckPermission($name, $type = 1, $cat_id = null) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->CheckPermission($name, $type, $cat_id); } /** * Check current admin permissions based on it's group permissions in specified category * * @param string $name permission name * @param int $cat_id category id, current used if not specified * @param int $type permission type {1 - system, 0 - per category} * @return int * @access public */ public function CheckAdminPermission($name, $type = 1, $cat_id = null) { $perm_helper = $this->recallObject('PermissionsHelper'); /* @var $perm_helper kPermissionsHelper */ return $perm_helper->CheckAdminPermission($name, $type, $cat_id); } /** * Set's any field of current visit * * @param string $field * @param mixed $value * @return void * @access public * @todo move to separate module */ public function setVisitField($field, $value) { if ( $this->isAdmin || !$this->ConfigValue('UseVisitorTracking') ) { // admin logins are not registered in visits list return; } $visit = $this->recallObject('visits', null, Array ('raise_warnings' => 0)); /* @var $visit kDBItem */ if ( $visit->isLoaded() ) { $visit->SetDBField($field, $value); $visit->Update(); } } /** * Allows to check if in-portal is installed * * @return bool * @access public */ public function isInstalled() { return $this->InitDone && (count($this->ModuleInfo) > 0); } /** * Allows to determine if module is installed & enabled * * @param string $module_name * @return bool * @access public */ public function isModuleEnabled($module_name) { return $this->findModule('Name', $module_name) !== false; } /** * Returns Window ID of passed prefix main prefix (in edit mode) * * @param string $prefix * @return int * @access public */ public function GetTopmostWid($prefix) { $top_prefix = $this->GetTopmostPrefix($prefix); $mode = $this->GetVar($top_prefix . '_mode'); return $mode != '' ? substr($mode, 1) : ''; } /** * Get temp table name * * @param string $table * @param mixed $wid * @return string * @access public */ public function GetTempName($table, $wid = '') { return $this->GetTempTablePrefix($wid) . $table; } /** * Builds temporary table prefix based on given window id * * @param string $wid * @return string * @access public */ public function GetTempTablePrefix($wid = '') { if ( preg_match('/prefix:(.*)/', $wid, $regs) ) { $wid = $this->GetTopmostWid($regs[1]); } return TABLE_PREFIX . 'ses_' . $this->GetSID() . ($wid ? '_' . $wid : '') . '_edit_'; } /** * Checks if given table is a temporary table * * @param string $table * @return bool * @access public */ public function IsTempTable($table) { static $cache = Array (); if ( !array_key_exists($table, $cache) ) { $cache[$table] = preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $table); } return (bool)$cache[$table]; } /** * Checks, that given prefix is in temp mode * * @param string $prefix * @param string $special * @return bool * @access public */ public function IsTempMode($prefix, $special = '') { $top_prefix = $this->GetTopmostPrefix($prefix); $var_names = Array ( $top_prefix, rtrim($top_prefix . '_' . $special, '_'), // from post rtrim($top_prefix . '.' . $special, '.'), // assembled locally ); $var_names = array_unique($var_names); $temp_mode = false; foreach ($var_names as $var_name) { $value = $this->GetVar($var_name . '_mode'); if ( $value && (substr($value, 0, 1) == 't') ) { $temp_mode = true; break; } } return $temp_mode; } /** * Return live table name based on temp table name * * @param string $temp_table * @return string */ public function GetLiveName($temp_table) { if ( preg_match('/' . TABLE_PREFIX . 'ses_' . $this->GetSID() . '(_[\d]+){0,1}_edit_(.*)/', $temp_table, $rets) ) { // cut wid from table end if any return $rets[2]; } else { return $temp_table; } } /** * Stops processing of user request and displays given message * * @param string $message * @access public */ public function ApplicationDie($message = '') { while ( ob_get_level() ) { ob_end_clean(); } if ( $this->isDebugMode() ) { $message .= $this->Debugger->printReport(true); } $this->HTML = $message; $this->_outputPage(); } /** * Returns comma-separated list of groups from given user * * @param int $user_id * @return string */ public function getUserGroups($user_id) { switch ($user_id) { case USER_ROOT: $user_groups = $this->ConfigValue('User_LoggedInGroup'); break; case USER_GUEST: $user_groups = $this->ConfigValue('User_LoggedInGroup') . ',' . $this->ConfigValue('User_GuestGroup'); break; default: $sql = 'SELECT GroupId FROM ' . TABLE_PREFIX . 'UserGroupRelations WHERE PortalUserId = ' . (int)$user_id; $res = $this->Conn->GetCol($sql); $user_groups = Array ($this->ConfigValue('User_LoggedInGroup')); if ( $res ) { $user_groups = array_merge($user_groups, $res); } $user_groups = implode(',', $user_groups); } return $user_groups; } /** * Allows to detect if page is browsed by spider (293 scheduled_tasks supported) * * @return bool * @access public */ /*public function IsSpider() { static $is_spider = null; if ( !isset($is_spider) ) { $user_agent = trim($_SERVER['HTTP_USER_AGENT']); $robots = file(FULL_PATH . '/core/robots_list.txt'); foreach ($robots as $robot_info) { $robot_info = explode("\t", $robot_info, 3); if ( $user_agent == trim($robot_info[2]) ) { $is_spider = true; break; } } } return $is_spider; }*/ /** * Allows to detect table's presence in database * * @param string $table_name * @param bool $force * @return bool * @access public */ public function TableFound($table_name, $force = false) { return $this->Conn->TableFound($table_name, $force); } /** * Returns counter value * * @param string $name counter name * @param Array $params counter parameters * @param string $query_name specify query name directly (don't generate from parameters) * @param bool $multiple_results * @return mixed * @access public */ public function getCounter($name, $params = Array (), $query_name = null, $multiple_results = false) { $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ return $count_helper->getCounter($name, $params, $query_name, $multiple_results); } /** * Resets counter, which are affected by one of specified tables * * @param string $tables comma separated tables list used in counting sqls * @return void * @access public */ public function resetCounters($tables) { if ( kUtil::constOn('IS_INSTALL') ) { return; } $count_helper = $this->recallObject('CountHelper'); /* @var $count_helper kCountHelper */ $count_helper->resetCounters($tables); } /** * Sends XML header + optionally displays xml heading * * @param string|bool $xml_version * @return string * @access public * @author Alex */ public function XMLHeader($xml_version = false) { $this->setContentType('text/xml'); return $xml_version ? '' : ''; } /** * Returns category tree * * @param int $category_id * @return Array * @access public */ public function getTreeIndex($category_id) { $tree_index = $this->getCategoryCache($category_id, 'category_tree'); if ( $tree_index ) { $ret = Array (); list ($ret['TreeLeft'], $ret['TreeRight']) = explode(';', $tree_index); return $ret; } return false; } /** * Base category of all categories * Usually replaced category, with ID = 0 in category-related operations. * * @return int * @access public */ public function getBaseCategory() { // same, what $this->findModule('Name', 'Core', 'RootCat') does // don't cache while IS_INSTALL, because of kInstallToolkit::createModuleCategory and upgrade return $this->ModuleInfo['Core']['RootCat']; } /** * Deletes all data, that was cached during unit config parsing (excluding unit config locations) * * @param Array $config_variables * @access public */ public function DeleteUnitCache($config_variables = null) { $this->cacheManager->DeleteUnitCache($config_variables); } /** * Deletes cached section tree, used during permission checking and admin console tree display * * @return void * @access public */ public function DeleteSectionCache() { $this->cacheManager->DeleteSectionCache(); } /** * Sets data from cache to object * * @param Array $data * @access public */ public function setFromCache(&$data) { $this->Factory->setFromCache($data); $this->UnitConfigReader->setFromCache($data); $this->EventManager->setFromCache($data); $this->ReplacementTemplates = $data['Application.ReplacementTemplates']; $this->RewriteListeners = $data['Application.RewriteListeners']; $this->ModuleInfo = $data['Application.ModuleInfo']; } /** * Gets object data for caching * The following caches should be reset based on admin interaction (adjusting config, enabling modules etc) * * @access public * @return Array */ public function getToCache() { return array_merge( $this->Factory->getToCache(), $this->UnitConfigReader->getToCache(), $this->EventManager->getToCache(), Array ( 'Application.ReplacementTemplates' => $this->ReplacementTemplates, 'Application.RewriteListeners' => $this->RewriteListeners, 'Application.ModuleInfo' => $this->ModuleInfo, ) ); } public function delayUnitProcessing($method, $params) { $this->cacheManager->delayUnitProcessing($method, $params); } /** * Returns current maintenance mode state * * @param bool $check_ips * @return int * @access public */ public function getMaintenanceMode($check_ips = true) { $exception_ips = defined('MAINTENANCE_MODE_IPS') ? MAINTENANCE_MODE_IPS : ''; $setting_name = $this->isAdmin ? 'MAINTENANCE_MODE_ADMIN' : 'MAINTENANCE_MODE_FRONT'; if ( defined($setting_name) && constant($setting_name) > MaintenanceMode::NONE ) { $exception_ip = $check_ips ? kUtil::ipMatch($exception_ips) : false; if ( !$exception_ip ) { return constant($setting_name); } } return MaintenanceMode::NONE; } /** * Sets content type of the page * * @param string $content_type * @param bool $include_charset * @return void * @access public */ public function setContentType($content_type = 'text/html', $include_charset = null) { static $already_set = false; if ( $already_set ) { return; } $header = 'Content-type: ' . $content_type; if ( !isset($include_charset) ) { $include_charset = $content_type = 'text/html' || $content_type == 'text/plain' || $content_type = 'text/xml'; } if ( $include_charset ) { $header .= '; charset=' . CHARSET; } $already_set = true; header($header); } /** * Posts message to event log * * @param string $message * @param int $code * @param bool $write_now Allows further customization of log record by returning kLog object * @return bool|int|kLogger * @access public */ public function log($message, $code = null, $write_now = false) { $log = $this->_logger->prepare($message, $code)->addSource($this->_logger->createTrace(null, 1)); if ( $write_now ) { return $log->write(); } return $log; } /** * Deletes log with given id from database or disk, when database isn't available * * @param int $unique_id * @param int $storage_medium * @return void * @access public * @throws InvalidArgumentException */ public function deleteLog($unique_id, $storage_medium = kLogger::LS_AUTOMATIC) { $this->_logger->delete($unique_id, $storage_medium); } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public function getClientIp() { return $this->HttpQuery->getClientIp(); } } Index: branches/5.2.x/core/kernel/utility/cache.php =================================================================== --- branches/5.2.x/core/kernel/utility/cache.php (revision 16434) +++ branches/5.2.x/core/kernel/utility/cache.php (revision 16435) @@ -1,979 +1,977 @@ siteKeyName = 'site_serial:' . crc32(SQL_TYPE . '://' . SQL_USER . ':' . SQL_PASS . '@' . SQL_SERVER . ':' . TABLE_PREFIX . ':' . SQL_DB); // get cache handler class to use - $handler_class = (isset($vars['CacheHandler']) ? $vars['CacheHandler'] : '') . 'CacheHandler'; + $handler_class = kUtil::getSystemConfig()->get('CacheHandler', '') . 'CacheHandler'; // defined cache handler doesn't exist -> use default if ( !class_exists($handler_class) ) { $handler_class = 'FakeCacheHandler'; } $handler = new $handler_class($this); if ( !$handler->isWorking() ) { // defined cache handler is not working -> use default trigger_error('Failed to initialize "' . $handler_class . '" caching handler.', E_USER_WARNING); $handler = new FakeCacheHandler($this); } elseif ( $this->Application->isDebugMode() && ($handler->getCachingType() == CACHING_TYPE_MEMORY) ) { $this->Application->Debugger->appendHTML('Memory Caching: "' . $handler_class . '"'); } $this->_handler = $handler; $this->cachingType = $handler->getCachingType(); $this->debugCache = $handler->getCachingType() == CACHING_TYPE_MEMORY && $this->Application->isDebugMode(); $this->_storeStatistics = defined('DBG_CACHE') && DBG_CACHE; if ( $this->_storeStatistics ) { // don't use FileHelper, since kFactory isn't ready yet if ( !file_exists(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage') ) { mkdir(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage'); } } } /** * Returns caching type of current storage engine * * @return int */ function getCachingType() { return $this->cachingType; } /** * Stores value to cache * * @param string $name * @param mixed $value * @param int $expiration cache record expiration time in seconds * @return bool */ function setCache($name, $value, $expiration) { // 1. stores current version of serial for given cache key $this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration); $this->storeStatistics($name, 'W'); // 2. don't replace serials within the key $saved = $this->_setCache($name, $value, $expiration); $this->storeStatistics($name, 'U'); // 3. remove rebuilding mark $this->delete($name . '_rebuilding'); return $saved; } /** * Stores value to cache * * @param string $name * @param mixed $value * @param int $expiration cache record expiration time in seconds * @return bool */ function _setCache($name, $value, $expiration) { $prepared_name = $this->prepareKeyName($name); $this->_localStorage[$prepared_name] = $value; return $this->_handler->set($prepared_name, $value, $expiration); } /** * Stores value to cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration cache record expiration time in seconds * @return bool */ function addCache($name, $value, $expiration) { // 1. stores current version of serial for given cache key $this->_setCache($name . '_serials', $this->replaceSerials($name), $expiration); // 2. remove rebuilding mark $this->delete($name . '_rebuilding'); // 3. don't replace serials within the key return $this->_addCache($name, $value, $expiration); } /** * Stores value to cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration cache record expiration time in seconds * @return bool */ function _addCache($name, $value, $expiration) { $prepared_name = $this->prepareKeyName($name); $added = $this->_handler->add($prepared_name, $value, $expiration); if ( $added ) { $this->_localStorage[$prepared_name] = $value; } return $added; } /** * Sets rebuilding mode for given cache * * @param string $name * @param int $mode * @param int $max_rebuilding_time * @param string $miss_type * @return bool */ function rebuildCache($name, $mode = null, $max_rebuilding_time = 0, $miss_type = 'M') { if ( !isset($mode) || $mode == self::REBUILD_NOW ) { $this->storeStatistics($name, $miss_type); if ( !$max_rebuilding_time ) { return true; } // prevent parallel rebuild attempt by using "add" instead of "set" method if ( !$this->_addCache($name . '_rebuilding', 1, $max_rebuilding_time) ) { $this->storeStatistics($name, 'l'); return false; } $this->storeStatistics($name, 'L'); $this->delete($name . '_rebuild'); } elseif ( $mode == self::REBUILD_LATER ) { $this->_setCache($name . '_rebuild', 1, 0); $this->delete($name . '_rebuilding'); } return true; } /** * Returns value from cache * * @param string $name * @param bool $store_locally store data locally after retrieved * @param int $max_rebuild_seconds * @return mixed */ function getCache($name, $store_locally = true, $max_rebuild_seconds = 0) { $cached_data = $this->_getCache(Array ($name . '_rebuild', $name . '_serials'), Array (true, true)); if ( $cached_data[$name . '_rebuild'] ) { // cache rebuild requested -> rebuild now $this->delete($name . '_rebuild'); if ( $this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M1]') ) { return false; } } // There are 2 key types: // - with serials, e.g. with_serial_key[%LangSerial%] // - without serials, e.g. without_serial // Evaluated serials of each cache key are stored in '{$name}_serials' cache key. // If cache is present, but serial is outdated, then cache value is assumed to be outdated. $new_serial = $this->replaceSerials($name); $old_serial = $cached_data[$name . '_serials']; if ( $name == $new_serial || $new_serial != $old_serial ) { // no serials in cache key OR cache is outdated $wait_seconds = $max_rebuild_seconds; while (true) { $cached_data = $this->_getCache(Array ($name, $name . '_rebuilding'), Array ($store_locally, false)); $cache = $cached_data[$name]; $rebuilding = $cached_data[$name . '_rebuilding']; if ( ($cache === false) && (!$rebuilding || $wait_seconds == 0) ) { // cache missing and nobody rebuilding it -> rebuild; enough waiting for cache to be ready $this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M2' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } elseif ( $cache !== false ) { // re-read serial, since it might have been changed in parallel process !!! $old_serial = $this->_getCache($name . '_serials', false); // cache present (if other user is rebuilding it, then it's outdated cache) -> return it if ( $rebuilding || $new_serial == $old_serial ) { $this->storeStatistics($name, $rebuilding ? 'h' : 'H'); return $cache; } $this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M3' . ($rebuilding ? 'R' : '!R') . ',WS=' . $wait_seconds . ']'); return false; } $wait_seconds -= self::WAIT_STEP; sleep(self::WAIT_STEP); } } $cache = $this->_getCache($name, $store_locally); if ( $cache === false ) { $this->rebuildCache($name, self::REBUILD_NOW, $max_rebuild_seconds, '[M4]'); } else { $this->storeStatistics($name, 'H'); } return $cache; } /** * Returns cached value from local cache * * @param string $prepared_name Prepared key name from kCache::prepareKeyName() function * @return mixed * @see prepareKeyName * @access public */ public function getFromLocalStorage($prepared_name) { return array_key_exists($prepared_name, $this->_localStorage) ? $this->_localStorage[$prepared_name] : false; } /** * Returns value from cache * * @param string|Array $names * @param bool|Array $store_locally store data locally after retrieved * @return mixed */ function _getCache($names, $store_locally = true) { static $request_number = 1; $res = Array (); $names = (array)$names; $store_locally = (array)$store_locally; $to_get = $prepared_names = array_map(Array (&$this, 'prepareKeyName'), $names); foreach ($prepared_names as $index => $prepared_name) { $name = $names[$index]; if ( $store_locally[$index] && array_key_exists($prepared_name, $this->_localStorage) ) { $res[$name] = $this->_localStorage[$prepared_name]; unset($to_get[$index]); } } if ( $to_get ) { $multi_res = $this->_handler->get($to_get); foreach ($to_get as $index => $prepared_name) { $name = $names[$index]; if ( array_key_exists($prepared_name, $multi_res) ) { $res[$name] =& $multi_res[$prepared_name]; } else { $res[$name] = false; } $this->_postProcessGetCache($prepared_name, $res[$name], $store_locally[$index], $request_number); } $request_number++; } return count($res) == 1 ? array_pop($res) : $res; } /** * Stores variable in local cache & collects debug info about cache * * @param string $name * @param mixed $res * @param bool $store_locally * @param int $request_number * @return void * @access protected */ protected function _postProcessGetCache($name, &$res, $store_locally = true, $request_number) { if ( $this->debugCache ) { // don't display subsequent serial cache retrievals (ones, that are part of keys) if ( is_array($res) ) { $this->Application->Debugger->appendHTML('r' . $request_number . ': Restoring key "' . $name . '". Type: ' . gettype($res) . '.'); } else { $res_display = strip_tags($res); if ( strlen($res_display) > 200 ) { $res_display = substr($res_display, 0, 50) . ' ...'; } $this->Application->Debugger->appendHTML('r' . $request_number . ': Restoring key "' . $name . '" resulted [' . $res_display . ']'); } } if ( $store_locally /*&& ($res !== false)*/ ) { $this->_localStorage[$name] = $res; } } /** * Deletes value from cache * * @param string $name * @return mixed */ function delete($name) { $name = $this->prepareKeyName($name); unset($this->_localStorage[$name]); return $this->_handler->delete($name); } /** * Reset's all memory cache at once */ function reset() { // don't check for enabled, because we maybe need to reset cache anyway if ($this->cachingType == CACHING_TYPE_TEMPORARY) { return ; } $site_key = $this->_cachePrefix(true); $this->_handler->set($site_key, $this->_handler->get($site_key) + 1); } /** * Replaces serials and adds unique site prefix to cache variable name * * @param string $name * @return string */ protected function prepareKeyName($name) { if ( $this->cachingType == CACHING_TYPE_TEMPORARY ) { return $name; } // add site-wide prefix to key return $this->_cachePrefix() . $name; } /** * Replaces serials within given string * * @param string $value * @return string * @access protected */ protected function replaceSerials($value) { if ( preg_match_all('/\[%(.*?)%\]/', $value, $regs) ) { // [%LangSerial%] - prefix-wide serial in case of any change in "lang" prefix // [%LangIDSerial:5%] - one id-wide serial in case of data, associated with given id was changed // [%CiIDSerial:ItemResourceId:5%] - foreign key-based serial in case of data, associated with given foreign key was changed $serial_names = $regs[1]; $serial_count = count($serial_names); $store_locally = Array (); for ($i = 0; $i < $serial_count; $i++) { $store_locally[] = true; } $serial_values = $this->_getCache($serial_names, $store_locally); if ( !is_array($serial_values) ) { $serial_values = Array (current($serial_names) => $serial_values); } foreach ($serial_names as $serial_name) { $value = str_replace('[%' . $serial_name . '%]', '[' . $serial_name . '=' . $serial_values[$serial_name] . ']', $value); } } return $value; } /** * Returns site-wide caching prefix * * @param bool $only_site_key_name * @return string */ function _cachePrefix($only_site_key_name = false) { if ($only_site_key_name) { return $this->siteKeyName; } if ( !isset($this->siteKeyValue) ) { $this->siteKeyValue = $this->_handler->get($this->siteKeyName); if (!$this->siteKeyValue) { $this->siteKeyValue = 1; $this->_handler->set($this->siteKeyName, $this->siteKeyValue); } } return "{$this->siteKeyName}:{$this->siteKeyValue}:"; } /** * Stores statistics about cache usage in a file (one file per cache) * * @param string $name * @param string $action_type {M - miss, L - lock, W - write, U - unlock, H - actual hit, h - outdated hit} * @return void * @access public */ public function storeStatistics($name, $action_type) { if ( !$this->_storeStatistics ) { return; } $name = str_replace(Array ('/', '\\', ':'), '_', $name); $fp = fopen(RESTRICTED . DIRECTORY_SEPARATOR . 'cache_usage' . DIRECTORY_SEPARATOR . $name, 'a'); fwrite($fp, $action_type); fclose($fp); } } abstract class kCacheHandler { /** * Remembers status of cache handler (working or not) * * @var bool * @access protected */ protected $_enabled = false; /** * Caching type that caching handler implements * * @var int * @access protected */ protected $cachingType; /** * * @var kCache * @access protected */ protected $parent; public function __construct(kCache $parent) { $this->parent = $parent; } /** * Retrieves value from cache * * @param string $names * @return mixed * @access public */ abstract public function get($names); /** * Stores value in cache * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ abstract public function set($name, $value, $expiration = 0); /** * Stores value in cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ abstract public function add($name, $value, $expiration = 0); /** * Deletes key from cach * * @param string $name * @return bool * @access public */ abstract public function delete($name); /** * Determines, that cache storage is working fine * * @return bool * @access public */ public function isWorking() { return $this->_enabled; } /** * Returns caching type of current storage engine * * @return int * @access public */ public function getCachingType() { return $this->cachingType; } } class FakeCacheHandler extends kCacheHandler { public function __construct(kCache $parent) { parent::__construct($parent); $this->_enabled = true; $this->cachingType = CACHING_TYPE_TEMPORARY; } /** * Retrieves value from cache * * @param string|Array $names * @return mixed * @access public */ public function get($names) { if ( is_array($names) ) { $res = Array (); foreach ($names as $name) { $res[$name] = $this->parent->getFromLocalStorage($name); } return $res; } return $this->parent->getFromLocalStorage($names); } /** * Stores value in cache * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function set($name, $value, $expiration = 0) { return true; } /** * Stores value in cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function add($name, $value, $expiration = 0) { return true; } /** * Deletes key from cach * * @param string $name * @return bool * @access public */ public function delete($name) { return true; } } class MemcacheCacheHandler extends kCacheHandler { /** * Memcache connection * * @var Memcache * @access protected */ protected $_handler = null; public function __construct(kCache $parent, $default_servers = '') { parent::__construct($parent); $this->cachingType = CACHING_TYPE_MEMORY; - $vars = kUtil::getConfigVars(); - $memcached_servers = isset($vars['MemcacheServers']) ? $vars['MemcacheServers'] : $default_servers; + $memcached_servers = kUtil::getSystemConfig()->get('MemcacheServers', $default_servers); if ( $memcached_servers && class_exists('Memcache') ) { $this->_enabled = true; $this->_handler = new Memcache(); $servers = explode(';', $memcached_servers); foreach ($servers as $server) { if ( preg_match('/(.*):([\d]+)$/', $server, $regs) ) { // "hostname:port" OR "unix:///path/to/socket:0" $server = $regs[1]; $port = $regs[2]; } else { $port = 11211; } $this->_handler->addServer($server, $port); } // verify, that memcache server is working if ( !$this->_handler->set('test', 1) ) { $this->_enabled = false; } } } /** * Retrieves value from cache * * @param string $name * @return mixed * @access public */ public function get($name) { return $this->_handler->get($name); } /** * Stores value in cache * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function set($name, $value, $expiration = 0) { // 0 - don't use compression return $this->_handler->set($name, $value, 0, $expiration); } /** * Stores value in cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function add($name, $value, $expiration = 0) { // 0 - don't use compression return $this->_handler->add($name, $value, 0, $expiration); } /** * Deletes key from cache * * @param string $name * @return bool * @access public */ public function delete($name) { return $this->_handler->delete($name, 0); } } class ApcCacheHandler extends kCacheHandler { public function __construct(kCache $parent) { parent::__construct($parent); $this->cachingType = CACHING_TYPE_MEMORY; $this->_enabled = function_exists('apc_fetch'); // verify, that apc is working if ( $this->_enabled && !$this->set('test', 1) ) { $this->_enabled = false; } } /** * Retrieves value from cache * * @param string $name * @return mixed * @access public */ public function get($name) { return apc_fetch($name); } /** * Stores value in cache * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function set($name, $value, $expiration = 0) { return apc_store($name, $value, $expiration); } /** * Stores value in cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function add($name, $value, $expiration = 0) { return apc_add($name, $value, $expiration); } /** * Deletes key from cache * * @param string $name * @return bool * @access public */ public function delete($name) { return apc_delete($name); } } class XCacheCacheHandler extends kCacheHandler { public function __construct(kCache $parent) { parent::__construct($parent); $this->cachingType = CACHING_TYPE_MEMORY; $this->_enabled = function_exists('xcache_get'); // verify, that xcache is working if ( $this->_enabled && !$this->set('test', 1) ) { $this->_enabled = false; } } /** * Retrieves value from cache * * @param string|Array $names * @return mixed * @access public */ public function get($names) { if ( is_array($names) ) { $res = Array (); foreach ($names as $name) { $res[$name] = $this->get($name); } return $res; } return xcache_isset($names) ? xcache_get($names) : false; } /** * Stores value in cache * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function set($name, $value, $expiration = 0) { return xcache_set($name, $value, $expiration); } /** * Stores value in cache (only if it's not there already) * * @param string $name * @param mixed $value * @param int $expiration * @return bool * @access public */ public function add($name, $value, $expiration = 0) { // not atomic operation, like in Memcached and may fail if ( xcache_isset($name) ) { return false; } return $this->set($name, $value, $expiration); } /** * Deletes key from cache * * @param string $name * @return bool * @access public */ public function delete($name) { return xcache_unset($name); } } \ No newline at end of file Index: branches/5.2.x/core/kernel/utility/http_query.php =================================================================== --- branches/5.2.x/core/kernel/utility/http_query.php (revision 16434) +++ branches/5.2.x/core/kernel/utility/http_query.php (revision 16435) @@ -1,808 +1,806 @@ Order = $order; if ( isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { // when AJAX request is made from jQuery, then create ajax variable, // so any logic based in it (like redirects) will not break down $_GET['ajax'] = 'yes'; } - $vars = kUtil::getConfigVars(); - - $this->_trustProxy = isset($vars['TrustProxy']) ? (bool)$vars['TrustProxy'] : false; + $this->_trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } /** * Discovers unit form request and returns it's QueryString option on success * * @param string $prefix_special * * @return Array|bool * @access public */ public function discoverUnit($prefix_special) { list($prefix) = explode('.', $prefix_special); $query_string = $this->getQueryString($prefix); if ($query_string) { // only units with QueryString option can be discovered $this->discoveredUnits[$prefix_special] = $query_string; return $query_string; } unset( $this->discoveredUnits[$prefix] ); return false; } /** * Returns units, passed in request * * @param bool $prefix_special_only * @return Array * @access protected */ public function getDiscoveredUnits($prefix_special_only = true) { return $prefix_special_only ? array_keys( $this->discoveredUnits ) : $this->discoveredUnits; } /** * Returns QueryMap for requested unit config. * In case if unit config is a clone, then get parent item's (from prefix) config to create clone * * @param string $prefix * @return Array * @access protected */ protected function getQueryString($prefix) { $ret = $this->Application->getUnitOption($prefix, 'QueryString', Array ()); if ( !$ret && preg_match('/(.*?)-(.*)/', $prefix, $regs) ) { // "#prefix" (new format), "prefix" (old format) return $this->_getQueryString('#' . $regs[2]); } return $ret; } /** * Returns query string (with safety check against missing prefixes) * * @param string $prefix * @return Array */ private function _getQueryString($prefix) { if ( $this->Application->prefixRegistred($prefix) ) { return $this->Application->getUnitOption($prefix, 'QueryString'); } return substr($prefix, 0, 1) == '#' ? $this->_getQueryString( substr($prefix, 1) ) : Array (); } /** * Removes specials from request * * @param Array $array * @return Array * @access protected */ protected function _removeSpecials($array) { $ret = Array (); $removed = false; foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag ) { $removed = true; list ($prefix, $special) = explode('.', $prefix_special, 2); foreach ($array as $key => $val) { $new_key = preg_match("/^" . $prefix . "[._]{1}" . $special . "(.*)/", $key, $regs) ? $prefix . $regs[1] : $key; $ret[$new_key] = is_array($val) ? $this->_removeSpecials($val) : $val; } } } return $removed ? $ret : $array; } public function process() { $this->AddAllVars(); $this->removeSpecials(); ini_set('magic_quotes_gpc', 0); $this->Application->UrlManager->LoadStructureTemplateMapping(); $this->AfterInit(); } /** * All all requested vars to * common storage place * * @return void * @access protected */ protected function AddAllVars() { for ($i = 0; $i < strlen($this->Order); $i++) { switch ($this->Order[$i]) { case 'G': $this->Get = $this->AddVars($_GET); if ( array_key_exists('sid', $_GET) ) { $this->_sidInQueryString = true; } $vars = $this->Application->processQueryString($this->Get(ENV_VAR_NAME)); if ( array_key_exists('sid', $vars) ) { // used by Session::GetPassedSIDValue $this->Get['sid'] = $vars['sid']; } $this->AddParams($vars); break; case 'P': $this->Post = $this->AddVars($_POST); $this->convertPostEvents(); $this->_processPostEnvVariables(); break; case 'C': $this->Cookie = $this->AddVars($_COOKIE); break; /*case 'E'; $this->Env = $this->AddVars($_ENV, false); //do not strip slashes! break; case 'S'; $this->Server = $this->AddVars($_SERVER, false); //do not strip slashes! break;*/ case 'F'; $this->convertFiles(); $this->Files = $this->MergeVars($_FILES); // , false); //do not strip slashes! break; } } } /** * Allow POST variables, that names were transformed by PHP ("." replaced with "_") to * override variables, that were virtually created through environment variable parsing * */ function _processPostEnvVariables() { $passed = $this->Get('passed'); if ( !$passed ) { return; } $passed = explode(',', $passed); foreach ($passed as $prefix_special) { if ( strpos($prefix_special, '.') === false ) { continue; } list ($prefix, $special) = explode('.', $prefix_special); $query_map = $this->getQueryString($prefix); $post_prefix_special = $prefix . '_' . $special; foreach ($query_map as $var_name) { if ( array_key_exists($post_prefix_special . '_' . $var_name, $this->Post) ) { $this->Set($prefix_special . '_' . $var_name, $this->Post[$post_prefix_special . '_' . $var_name]); } } } } /** * Removes requested specials from all request variables * * @return void * @access protected */ protected function removeSpecials() { $this->specialsToRemove = $this->Get('remove_specials'); if ( $this->specialsToRemove ) { foreach ($this->specialsToRemove as $prefix_special => $flag) { if ( $flag && strpos($prefix_special, '.') === false ) { unset($this->specialsToRemove[$prefix_special]); trigger_error('Incorrect usage of "remove_specials[' . $prefix_special . ']" field (no special found)', E_USER_NOTICE); } } $this->_Params = $this->_removeSpecials($this->_Params); } } /** * Finishes initialization of kHTTPQuery class * * @return void * @access protected * @todo: only uses build-in rewrite listeners, when cache is build for the first time */ protected function AfterInit() { $rewrite_url = $this->Get('_mod_rw_url_'); if ( $this->Application->RewriteURLs() || $rewrite_url ) { // maybe call onafterconfigread here $this->Application->UrlManager->initRewrite(); if ( defined('DEBUG_MODE') && $this->Application->isDebugMode() ) { $this->Application->Debugger->profileStart('url_parsing', 'Parsing MOD_REWRITE url'); $this->Application->UrlManager->rewrite->parseRewriteURL(); $description = 'Parsing MOD_REWRITE url (template: ' . $this->Get('t') . ')'; $this->Application->Debugger->profileFinish('url_parsing', $description); } else { $this->Application->UrlManager->rewrite->parseRewriteURL(); } if ( !$rewrite_url && $this->rewriteRedirectRequired() ) { // rewrite url is missing (e.g. not a script from tools folder) $url_params = $this->getRedirectParams(); // no idea about how to check, that given template require category to be passed with it, so pass anyway $url_params['pass_category'] = 1; $url_params['response_code'] = 301; // Moved Permanently trigger_error('Non mod-rewrite url "' . $_SERVER['REQUEST_URI'] . '" used', E_USER_NOTICE); $this->Application->Redirect('', $url_params); } } else { $this->Application->VerifyThemeId(); $this->Application->VerifyLanguageId(); } $virtual_template = $this->Application->getVirtualPageTemplate($this->Get('m_cat_id')); if ( ($virtual_template !== false) && preg_match('/external:(.*)/', $virtual_template) ) { trigger_error('URL of page, that has "External URL" set was accessed: "' . $_SERVER['REQUEST_URI'] . '"', E_USER_NOTICE); $this->Application->Redirect($virtual_template); } } /** * Checks, that non-rewrite url was visited and it's automatic rewrite is required * * @return bool */ function rewriteRedirectRequired() { $redirect_conditions = Array ( !$this->IsHTTPSRedirect(), // not https <-> http redirect !$this->refererIsOurSite(), // referer doesn't match ssl path or non-ssl domain (same for site domains) !defined('GW_NOTIFY'), // not in payment gateway notification script preg_match('/[\/]{0,1}index.php[\/]{0,1}/', $_SERVER['PHP_SELF']), // "index.php" was visited $this->Get('t') != 'index', // not on index page ); $perform_redirect = true; foreach ($redirect_conditions as $redirect_condition) { $perform_redirect = $perform_redirect && $redirect_condition; if (!$perform_redirect) { return false; } } return true; } /** * This is redirect from https to http or via versa * * @return bool */ function IsHTTPSRedirect() { $http_referer = array_key_exists('HTTP_REFERER', $_SERVER) ? $_SERVER['HTTP_REFERER'] : false; return ( ( PROTOCOL == 'https://' && preg_match('#http:\/\/#', $http_referer) ) || ( PROTOCOL == 'http://' && preg_match('#https:\/\/#', $http_referer) ) ); } /** * Checks, that referer is out site * * @return bool */ function refererIsOurSite() { if ( !array_key_exists('HTTP_REFERER', $_SERVER) ) { // no referer -> don't care what happens return false; } $site_helper = $this->Application->recallObject('SiteHelper'); /* @var $site_helper SiteHelper */ $found = false; $http_referer = $_SERVER['HTTP_REFERER']; preg_match('/^(.*?):\/\/(.*?)(\/|$)/', $http_referer, $regs); // 1 - protocol, 2 - domain if ($regs[1] == 'https') { $found = $site_helper->getDomainByName('SSLUrl', $http_referer) > 0; if (!$found) { // check if referer starts with our ssl url $ssl_url = $this->Application->ConfigValue('SSL_URL'); $found = $ssl_url && preg_match('/^' . preg_quote($ssl_url, '/') . '/', $http_referer); } } else { $found = $site_helper->getDomainByName('DomainName', $regs[2]) > 0; if (!$found) { $found = $regs[2] == DOMAIN; } } return $found; } function convertFiles() { if ( !$_FILES ) { return ; } $tmp = Array (); $file_keys = Array ('error', 'name', 'size', 'tmp_name', 'type'); foreach ($_FILES as $file_name => $file_info) { if ( is_array($file_info['error']) ) { $tmp[$file_name] = $this->getArrayLevel($file_info['error'], $file_name); } else { $normal_files[$file_name] = $file_info; } } if ( !$tmp ) { return ; } $files = $_FILES; $_FILES = Array (); foreach ($tmp as $prefix => $prefix_files) { $anchor =& $_FILES; foreach ($prefix_files['keys'] as $key) { $anchor =& $anchor[$key]; } foreach ($prefix_files['value'] as $field_name) { unset($inner_anchor, $copy); $work_copy = $prefix_files['keys']; foreach ($file_keys as $file_key) { $inner_anchor =& $files[$prefix][$file_key]; if ( isset($copy) ) { $work_copy = $copy; } else { $copy = $work_copy; } array_shift($work_copy); foreach ($work_copy as $prefix_file_key) { $inner_anchor =& $inner_anchor[$prefix_file_key]; } $anchor[$field_name][$file_key] = $inner_anchor[$field_name]; } } } // keys: img_temp, 0, values: LocalPath, ThumbPath } function getArrayLevel(&$level, $prefix='') { $ret['keys'] = $prefix ? Array($prefix) : Array(); $ret['value'] = Array(); foreach($level as $level_key => $level_value) { if( is_array($level_value) ) { $ret['keys'][] = $level_key; $tmp = $this->getArrayLevel($level_value); $ret['keys'] = array_merge($ret['keys'], $tmp['keys']); $ret['value'] = array_merge($ret['value'], $tmp['value']); } else { $ret['value'][] = $level_key; } } return $ret; } /** * Overwrites GET events with POST events in case if they are set and not empty * * @return void * @access protected */ protected function convertPostEvents() { $events = $this->Get('events', Array ()); /* @var $events Array */ if ( is_array($events) ) { $events = array_filter($events); foreach ($events as $prefix_special => $event_name) { $this->Set($prefix_special . '_event', $event_name); } } } function finalizeParsing($passed = Array()) { if (!$passed) { return; } foreach ($passed as $passed_prefix) { $this->discoverUnit($passed_prefix); // from mod-rewrite url parsing } $this->Set('passed', implode(',', $this->getDiscoveredUnits())); } /** * Saves variables from array specified * into common variable storage place * * @param Array $array * @param bool $strip_slashes * @return Array * @access private */ function AddVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value) { $this->Set($key, $value); } return $array; } function MergeVars($array, $strip_slashes = true) { if ( $strip_slashes ) { $array = $this->StripSlashes($array); } foreach ($array as $key => $value_array) { // $value_array is an array too $this->_Params = kUtil::array_merge_recursive($this->_Params, Array ($key => $value_array)); } return $array; } function StripSlashes($array) { static $magic_quotes = null; if (!isset($magic_quotes)) { $magic_quotes = get_magic_quotes_gpc(); } foreach ($array as $key => $value) { if (is_array($value)) { $array[$key] = $this->StripSlashes($value); } else { if ($magic_quotes) { $value = stripslashes($value); } if (!$this->Application->isAdmin) { // TODO: always escape output instead of input $value = kUtil::escape($value, kUtil::ESCAPE_HTML); } $array[$key] = $value; } } return $array; } /** * Removes forceful escaping done to the variable upon Front-End submission. * * @param string|array $value Value. * * @return string|array * @see StripSlashes */ public function unescapeRequestVariable($value) { if ( $this->Application->isAdmin ) { return $value; } // This allows to revert kUtil::escape() call for each field submitted on front-end. if ( is_array($value) ) { foreach ( $value as $param_name => $param_value ) { $value[$param_name] = $this->unescapeRequestVariable($param_value); } return $value; } return kUtil::unescape($value, kUtil::ESCAPE_HTML); } /** * Returns all $_GET array excluding system parameters, that are not allowed to be passed through generated urls * * @param bool $access_error Method is called during no_permission, require login, session expiration link preparation * @return Array */ function getRedirectParams($access_error = false) { $vars = $this->Get; $unset_vars = Array (ENV_VAR_NAME, 'rewrite', '_mod_rw_url_', 'Action'); if (!$this->_sidInQueryString) { $unset_vars[] = 'sid'; } // remove system variables foreach ($unset_vars as $var_name) { if (array_key_exists($var_name, $vars)) { unset($vars[$var_name]); } } if ($access_error) { // place 1 of 2 (also in UsersEventHandler::OnSessionExpire) $vars = $this->_removePassThroughVariables($vars); } return $vars; } /** * Removes all pass_though variables from redirect params * * @param Array $url_params * @return Array */ function _removePassThroughVariables($url_params) { $pass_through = array_key_exists('pass_through', $url_params) ? $url_params['pass_through'] : ''; if (!$pass_through) { return $url_params; } $pass_through = explode(',', $pass_through . ',pass_through'); foreach ($pass_through as $pass_through_var) { unset($url_params[$pass_through_var]); } $url_params['no_pass_through'] = 1; // this way kApplication::HREF won't add them again return $url_params; } /** * Checks, that url is empty * * @return bool * @access public */ public function isEmptyUrl() { if ( $this->Application->RewriteURLs() ) { return !$this->Get('_mod_rw_url_'); } return !count($this->Get); } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public function getClientIp() { if ( $this->_trustProxy ) { if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) { return $_SERVER['HTTP_CLIENT_IP']; } if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) { $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($client_ip as $ip_address) { $clean_ip_address = trim($ip_address); if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) { return $clean_ip_address; } } return ''; } } return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : ''; } /** * Returns headers * * @return array * @access public */ public function getHeaders() { if ( function_exists('apache_request_headers') ) { // If apache_request_headers() exists... $headers = apache_request_headers(); if ( $headers ) { return $headers; // And works... Use it } } $headers = array(); foreach ( array_keys($_SERVER) as $server_key ) { if ( substr($server_key, 0, 5) == 'HTTP_' ) { $header_name = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($server_key, 0, 5))))); $headers[$header_name] = $_SERVER[$server_key]; } } return $headers; } } Index: branches/5.2.x/core/kernel/utility/system_config.php =================================================================== --- branches/5.2.x/core/kernel/utility/system_config.php (revision 0) +++ branches/5.2.x/core/kernel/utility/system_config.php (revision 16435) @@ -0,0 +1,287 @@ +parseSections = $parse_section; + $this->strictMode = $strict; + $this->file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php'; + } + + /** + * Returns default config values. + * + * @return array + */ + protected function getDefaults() + { + $ret = array( + 'AdminDirectory' => '/admin', + 'AdminPresetsDirectory' => '/admin', + 'ApplicationClass' => 'kApplication', + 'ApplicationPath' => '/core/kernel/application.php', + 'CacheHandler' => 'Fake', + 'CmsMenuRebuildTime' => 10, + 'DomainsParsedRebuildTime' => 2, + 'EditorPath' => '/core/ckeditor/', + 'EnableSystemLog' => '0', + 'MemcacheServers' => 'localhost:11211', + 'CompressionEngine' => '', + 'RestrictedPath' => DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . '.restricted', + 'SectionsParsedRebuildTime' => 5, + 'StructureTreeRebuildTime' => 10, + 'SystemLogMaxLevel' => 5, + 'TemplateMappingRebuildTime' => 5, + 'TrustProxy' => '0', + 'UnitCacheRebuildTime' => 10, + 'WebsiteCharset' => 'utf-8', + 'WebsitePath' => rtrim(preg_replace('/'.preg_quote(rtrim(defined('REL_PATH') ? REL_PATH : '', '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'), + 'WriteablePath' => DIRECTORY_SEPARATOR . 'system', + ); + + return $this->parseSections ? array('Misc' => $ret) : $ret; + } + + /** + * Parses "/system/config.php" file and writes the result to the data variable + * + * @return array + * @throws kSystemConfigException When something goes wrong. + */ + public function parse() + { + if ( !$this->exists() ) { + if ( $this->strictMode ) { + throw new kSystemConfigException(sprintf('System config at "%s" not found', $this->file)); + } + + return array(); + } + elseif ( !is_readable($this->file) ) { + throw new kSystemConfigException(sprintf('System config at "%s" could not be opened', $this->file)); + } + + $contents = file($this->file); + + if ( $contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n" ) { + // format of "config.php" file before 5.1.0 version + array_shift($contents); + + return parse_ini_string(implode('', $contents), $this->parseSections); + } + + $_CONFIG = array(); + require($this->file); + + if ( $this->parseSections ) { + if ( isset($_CONFIG['Database']['LoadBalancing']) && $_CONFIG['Database']['LoadBalancing'] ) { + require FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'db_servers.php'; + } + + return $_CONFIG; + } + + $ret = array(); + + foreach ($_CONFIG as $section_variables) { + $ret = array_merge($ret, $section_variables); + } + + if ( !count($ret) && $this->strictMode ) { + throw new kSystemConfigException(sprintf('System config at "%s" could is empty', $this->file)); + } + + return $ret; + } + + /** + * Returns parsed variables from "config.php" file + * + * @return array + */ + public function getData() + { + if ( !$this->data ) { + $this->data = array_merge($this->getDefaults(), $this->parse()); + } + + return $this->data; + } + + /** + * Checks if given section is present in config. + * + * @param string $section + * + * @return boolean + */ + function sectionFound($section) + { + return $this->parseSections ? array_key_exists($section, $this->getData()) : false; + } + + /** + * Returns config value + * + * @param string $key Key name. + * @param string $section Section name. + * @param mixed $default Default value. + * + * @return string + */ + public function get($key, $section = null, $default = false) + { + $data = $this->getData(); + + if ( $this->parseSections ) { + return isset($data[$section][$key]) ? $data[$section][$key] : $default; + } + + return isset($data[$key]) ? $data[$key] : (isset($section) ? $section : $default); + } + + /** + * Checks, if a configuration file exists on disk. + * + * @return boolean + */ + public function exists() + { + return file_exists($this->file); + } + + /** + * Returns config status - is changed or not changed + * + * @return bool + */ + public function isChanged() + { + return $this->isChanged; + } + + /** + * Sets value to system config (yet saveConfig must be called to write it to file) + * + * @param string $key Key name. + * @param string $section Section name. + * @param mixed $value Value. + * + * @return void + */ + public function set($key, $section, $value = null) + { + $this->getData(); + $this->isChanged = true; + + if ( isset($value) ) { + // create section, when missing + if ( !array_key_exists($section, $this->data) ) { + $this->data[$section] = array(); + } + + // create key in section + $this->data[$section][$key] = $value; + + return; + } + + unset($this->data[$section][$key]); + } + + /** + * Saves config data to the file + * + * @param boolean $silent + * + * @return void + * @throws Exception + */ + public function save($silent = false) + { + if ( !is_writable($this->file) && !is_writable(dirname($this->file)) ) { + $error_msg = 'Cannot write to "' . $this->file . '" file'; + + if ( $silent ) { + trigger_error($error_msg, E_USER_WARNING); + + return; + } + + throw new Exception($error_msg); + } + + $fp = fopen($this->file, 'w'); + fwrite($fp, '<' . '?' . 'php' . "\n\n"); + + foreach ( $this->getData() as $section_name => $section_data ) { + foreach ( $section_data as $key => $value ) { + fwrite($fp, '$_CONFIG[\'' . $section_name . '\'][\'' . $key . '\'] = \'' . addslashes($value) . '\';' . "\n"); + } + + fwrite($fp, "\n"); + } + + fclose($fp); + + if ( function_exists('opcache_invalidate') ) { + opcache_invalidate($this->file); + } + + $this->isChanged = false; + } + +} + + +class kSystemConfigException extends Exception +{ + +} Property changes on: branches/5.2.x/core/kernel/utility/system_config.php ___________________________________________________________________ Added: svn:eol-style + LF Index: branches/5.2.x/core/kernel/utility/logger.php =================================================================== --- branches/5.2.x/core/kernel/utility/logger.php (revision 16434) +++ branches/5.2.x/core/kernel/utility/logger.php (revision 16435) @@ -1,1404 +1,1404 @@ _debugMode = $this->Application->isDebugMode(); - $this->setState(isset($vars['EnableSystemLog']) ? $vars['EnableSystemLog'] : self::STATE_DISABLED); - $this->_maxLogLevel = isset($vars['SystemLogMaxLevel']) ? (int)$vars['SystemLogMaxLevel'] : self::LL_NOTICE; + $this->setState($system_config->get('EnableSystemLog', self::STATE_DISABLED)); + $this->_maxLogLevel = $system_config->get('SystemLogMaxLevel', self::LL_NOTICE); foreach ($methods_to_call as $method_to_call) { call_user_func_array(Array ($this, $method_to_call[0]), $method_to_call[1]); } if ( !kUtil::constOn('DBG_ZEND_PRESENT') && !$this->Application->isDebugMode() ) { // don't report error on screen if debug mode is turned off error_reporting(0); ini_set('display_errors', 0); } register_shutdown_function(Array ($this, 'catchLastError')); } /** * Sets state of the logged (enabled/user-only/disabled) * * @param $new_state * @return void * @access public */ public function setState($new_state = null) { if ( isset($new_state) ) { $this->_state = (int)$new_state; } if ( $this->_state === self::STATE_ENABLED ) { $this->_enableErrorHandling(); } elseif ( $this->_state === self::STATE_DISABLED ) { $this->_disableErrorHandling(); } } /** * Enable error/exception handling capabilities * * @return void * @access protected */ protected function _enableErrorHandling() { $this->_disableErrorHandling(); $this->_handlers[self::LL_ERROR] = new kErrorHandlerStack($this); $this->_handlers[self::LL_CRITICAL] = new kExceptionHandlerStack($this); } /** * Disables error/exception handling capabilities * * @return void * @access protected */ protected function _disableErrorHandling() { foreach ($this->_handlers as $index => $handler) { $this->_handlers[$index]->__destruct(); unset($this->_handlers[$index]); } } /** * Initializes new log record. Use "kLogger::write" to save to db/disk * * @param string $message * @param int $code * @return kLogger * @access public */ public function prepare($message = '', $code = null) { $this->_logRecord = Array ( 'LogUniqueId' => kUtil::generateId(), 'LogMessage' => $message, 'LogLevel' => self::LL_INFO, 'LogCode' => $code, 'LogType' => self::LT_OTHER, 'LogHostname' => $_SERVER['HTTP_HOST'], 'LogRequestSource' => php_sapi_name() == 'cli' ? 2 : 1, 'LogRequestURI' => php_sapi_name() == 'cli' ? implode(' ', $GLOBALS['argv']) : $_SERVER['REQUEST_URI'], 'LogUserId' => USER_GUEST, 'IpAddress' => isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : '', 'LogSessionKey' => 0, 'LogProcessId' => getmypid(), 'LogUserData' => '', 'LogNotificationStatus' => self::LNS_DISABLED, ); if ( $this->Application->isAdmin ) { $this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_ADMIN : self::LI_ADMIN; } else { $this->_logRecord['LogInterface'] = defined('CRON') && CRON ? self::LI_CRON_FRONT : self::LI_FRONT; } if ( $this->Application->InitDone ) { $this->_logRecord['LogUserId'] = $this->Application->RecallVar('user_id'); $this->_logRecord['LogSessionKey'] = $this->Application->GetSID(); $this->_logRecord['IpAddress'] = $this->Application->getClientIp(); } return $this; } /** * Sets one or more fields of log record * * @param string|Array $field_name * @param string|null $field_value * @return kLogger * @access public * @throws UnexpectedValueException */ public function setLogField($field_name, $field_value = null) { if ( isset($field_value) ) { $this->_logRecord[$field_name] = $field_value; } elseif ( is_array($field_name) ) { $this->_logRecord = array_merge($this->_logRecord, $field_name); } else { throw new UnexpectedValueException('Invalid arguments'); } return $this; } /** * Sets user data * * @param string $data * @param bool $as_array * @return kLogger * @access public */ public function setUserData($data, $as_array = false) { if ( $as_array ) { $data = serialize((array)$data); } return $this->setLogField('LogUserData', $data); } /** * Add user data * * @param string $data * @param bool $as_array * @return kLogger * @access public */ public function addUserData($data, $as_array = false) { $new_data = $this->_logRecord['LogUserData']; if ( $as_array ) { $new_data = $new_data ? unserialize($new_data) : Array (); $new_data[] = $data; $new_data = serialize($new_data); } else { $new_data .= ($new_data ? PHP_EOL : '') . $data; } return $this->setLogField('LogUserData', $new_data); } /** * Adds event to log record * * @param kEvent $event * @return kLogger * @access public */ public function addEvent(kEvent $event) { $this->_logRecord['LogEventName'] = (string)$event; return $this; } /** * Adds log source file & file to log record * * @param string|Array $file_or_trace file path * @param int $line file line * @return kLogger * @access public */ public function addSource($file_or_trace = '', $line = 0) { if ( is_array($file_or_trace) ) { $trace_info = $file_or_trace[0]; $this->_logRecord['LogSourceFilename'] = $trace_info['file']; $this->_logRecord['LogSourceFileLine'] = $trace_info['line']; } else { $this->_logRecord['LogSourceFilename'] = $file_or_trace; $this->_logRecord['LogSourceFileLine'] = $line; } return $this; } /** * Adds session contents to log record * * @param bool $include_optional Include optional session variables * @return kLogger * @access public */ public function addSessionData($include_optional = false) { if ( $this->Application->InitDone ) { $this->_logRecord['LogSessionData'] = serialize($this->Application->Session->getSessionData($include_optional)); } return $this; } /** * Adds user request information to log record * * @return kLogger * @access public */ public function addRequestData() { $request_data = array( 'Headers' => $this->Application->HttpQuery->getHeaders(), ); $request_variables = Array('_GET' => $_GET, '_POST' => $_POST, '_COOKIE' => $_COOKIE); foreach ( $request_variables as $title => $data ) { if ( !$data ) { continue; } $request_data[$title] = $data; } $this->_logRecord['LogRequestData'] = serialize($request_data); return $this; } /** * Adds trace to log record * * @param Array $trace * @param int $skip_levels * @param Array $skip_files * @return kLogger * @access public */ public function addTrace($trace = null, $skip_levels = 1, $skip_files = null) { $trace = $this->createTrace($trace, $skip_levels, $skip_files); foreach ($trace as $trace_index => $trace_info) { if ( isset($trace_info['args']) ) { $trace[$trace_index]['args'] = $this->_implodeObjects($trace_info['args']); } } $this->_logRecord['LogBacktrace'] = serialize($this->_removeObjectsFromTrace($trace)); return $this; } /** * Remove objects from trace, since before PHP 5.2.5 there wasn't possible to remove them initially * * @param Array $trace * @return Array * @access protected */ protected function _removeObjectsFromTrace($trace) { if ( version_compare(PHP_VERSION, '5.3', '>=') ) { return $trace; } $trace_indexes = array_keys($trace); foreach ($trace_indexes as $trace_index) { unset($trace[$trace_index]['object']); } return $trace; } /** * Implodes object to prevent memory leaks * * @param Array $array * @return Array * @access protected */ protected function _implodeObjects($array) { $ret = Array (); foreach ($array as $key => $value) { if ( is_array($value) ) { $ret[$key] = $this->_implodeObjects($value); } elseif ( is_object($value) ) { if ( $value instanceof kEvent ) { $ret[$key] = 'Event: ' . (string)$value; } elseif ( $value instanceof kBase ) { $ret[$key] = (string)$value; } else { $ret[$key] = 'Class: ' . get_class($value); } } elseif ( strlen($value) > 200 ) { $ret[$key] = substr($value, 0, 50) . ' ...'; } else { $ret[$key] = $value; } } return $ret; } /** * Removes first N levels from trace * * @param Array $trace * @param int $levels * @param Array $files * @return Array * @access public */ public function createTrace($trace = null, $levels = null, $files = null) { if ( !isset($trace) ) { $trace = debug_backtrace(false); } if ( !$trace ) { // no trace information return $trace; } if ( isset($levels) && is_numeric($levels) ) { for ($i = 0; $i < $levels; $i++) { array_shift($trace); } } if ( isset($files) && is_array($files) ) { while (true) { $trace_info = $trace[0]; $file = isset($trace_info['file']) ? basename($trace_info['file']) : ''; if ( !in_array($file, $files) ) { break; } array_shift($trace); } } return $trace; } /** * Adds PHP error to log record * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @return kLogger * @access public */ public function addError($errno, $errstr, $errfile = null, $errline = null) { $errstr = self::expandMessage($errstr, !$this->_debugMode); $this->_logRecord['LogLevel'] = $this->_getLogLevelByErrorNo($errno); if ( $this->isLogType(self::LT_DATABASE, $errstr) ) { list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr); $this->_logRecord['LogType'] = self::LT_DATABASE; $this->_logRecord['LogUserData'] = $sql; $trace = $this->createTrace(null, 4, $this->_ignoreInTrace); $this->addSource($trace); $this->addTrace($trace, 0); } else { $this->_logRecord['LogType'] = self::LT_PHP; $this->addSource((string)$errfile, $errline); $this->addTrace(null, 4); } $this->_logRecord['LogCode'] = $errno; $this->_logRecord['LogMessage'] = $errstr; return $this; } /** * Adds PHP exception to log record * * @param Exception $exception * @return kLogger * @access public */ public function addException($exception) { $errstr = self::expandMessage($exception->getMessage(), !$this->_debugMode); $this->_logRecord['LogLevel'] = self::LL_CRITICAL; $exception_trace = $exception->getTrace(); array_unshift($exception_trace, array( 'function' => '', 'file' => $exception->getFile() !== null ? $exception->getFile() : 'n/a', 'line' => $exception->getLine() !== null ? $exception->getLine() : 'n/a', 'args' => array(), )); if ( $this->isLogType(self::LT_DATABASE, $errstr) ) { list ($errno, $errstr, $sql) = self::parseDatabaseError($errstr); $this->_logRecord['LogType'] = self::LT_DATABASE; $this->_logRecord['LogUserData'] = $sql; $trace = $this->createTrace($exception_trace, null, $this->_ignoreInTrace); $this->addSource($trace); $this->addTrace($trace, 0); } else { $this->_logRecord['LogType'] = self::LT_PHP; $errno = $exception->getCode(); $this->addSource((string)$exception->getFile(), $exception->getLine()); $this->addTrace($exception_trace, 0); } $this->_logRecord['LogCode'] = $errno; $this->_logRecord['LogMessage'] = $errstr; return $this; } /** * Allows to map PHP error numbers to syslog log level * * @param int $errno * @return int * @access protected */ protected function _getLogLevelByErrorNo($errno) { $error_number_mapping = Array ( self::LL_ERROR => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), self::LL_WARNING => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING), self::LL_NOTICE => Array (E_NOTICE, E_USER_NOTICE, E_STRICT), ); if ( version_compare(PHP_VERSION, '5.3.0', '>=') ) { $error_number_mapping[self::LL_NOTICE][] = E_DEPRECATED; $error_number_mapping[self::LL_NOTICE][] = E_USER_DEPRECATED; } foreach ($error_number_mapping as $log_level => $error_numbers) { if ( in_array($errno, $error_numbers) ) { return $log_level; } } return self::LL_ERROR; } /** * Changes log level of a log record * * @param int $log_level * @return kLogger * @access public */ public function setLogLevel($log_level) { $this->_logRecord['LogLevel'] = $log_level; return $this; } /** * Writes prepared log to database or disk, when database isn't available * * @param int $storage_medium * @return bool|int * @access public * @throws InvalidArgumentException */ public function write($storage_medium = self::LS_AUTOMATIC) { if ( !$this->_logRecord || $this->_logRecord['LogLevel'] > $this->_maxLogLevel || $this->_state == self::STATE_DISABLED ) { // nothing to save OR less detailed logging requested OR disabled return false; } $this->_logRecord['LogMemoryUsed'] = memory_get_usage(); $this->_logRecord['LogTimestamp'] = adodb_mktime(); $this->_logRecord['LogDate'] = adodb_date('Y-m-d H:i:s'); if ( $storage_medium == self::LS_AUTOMATIC ) { $storage_medium = $this->Conn->connectionOpened() ? self::LS_DATABASE : self::LS_DISK; } if ( $storage_medium == self::LS_DATABASE ) { $result = $this->Conn->doInsert($this->_logRecord, TABLE_PREFIX . 'SystemLog'); } elseif ( $storage_medium == self::LS_DISK ) { $result = $this->_saveToFile(RESTRICTED . '/system.log'); } else { throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"'); } $unique_id = $this->_logRecord['LogUniqueId']; if ( $this->_logRecord['LogNotificationStatus'] == self::LNS_SENT ) { $this->_sendNotification($unique_id); } $this->_logRecord = Array (); return $result ? $unique_id : false; } /** * Catches last error happened before script ended * * @return void * @access public */ public function catchLastError() { $this->write(); $last_error = error_get_last(); if ( !is_null($last_error) && isset($this->_handlers[self::LL_ERROR]) ) { $handler = $this->_handlers[self::LL_ERROR]; /* @var $handler kErrorHandlerStack */ $handler->handle($last_error['type'], $last_error['message'], $last_error['file'], $last_error['line']); } } /** * Deletes log with given id from database or disk, when database isn't available * * @param int $unique_id * @param int $storage_medium * @return void * @access public * @throws InvalidArgumentException */ public function delete($unique_id, $storage_medium = self::LS_AUTOMATIC) { if ( $storage_medium == self::LS_AUTOMATIC ) { $storage_medium = $this->Conn->connectionOpened() ? self::LS_DATABASE : self::LS_DISK; } if ( $storage_medium == self::LS_DATABASE ) { $sql = 'DELETE FROM ' . TABLE_PREFIX . 'SystemLog WHERE LogUniqueId = ' . $unique_id; $this->Conn->Query($sql); } elseif ( $storage_medium == self::LS_DISK ) { // TODO: no way to delete a line from a file } else { throw new InvalidArgumentException('Unknown storage medium "' . $storage_medium . '"'); } } /** * Send notification (delayed or instant) about log record to e-mail from configuration * * @param bool $instant * @return kLogger * @access public */ public function notify($instant = false) { $this->_logRecord['LogNotificationStatus'] = $instant ? self::LNS_SENT : self::LNS_PENDING; return $this; } /** * Sends notification e-mail about message with given $unique_id * * @param int $unique_id * @return void * @access protected */ protected function _sendNotification($unique_id) { $notification_email = $this->Application->ConfigValue('SystemLogNotificationEmail'); if ( !$notification_email ) { trigger_error('System Log notification E-mail not specified', E_USER_NOTICE); return; } $send_params = Array ( 'to_name' => $notification_email, 'to_email' => $notification_email, ); // initialize list outside of e-mail event with right settings $this->Application->recallObject('system-log.email', 'system-log_List', Array ('unique_id' => $unique_id)); $this->Application->emailAdmin('SYSTEM.LOG.NOTIFY', null, $send_params); $this->Application->removeObject('system-log.email'); } /** * Adds error/exception handler * * @param string|Array $handler * @param bool $is_exception * @return void * @access public */ public function addErrorHandler($handler, $is_exception = false) { $this->_handlers[$is_exception ? self::LL_CRITICAL : self::LL_ERROR]->add($handler); } /** * SQL Error Handler * * When not debug mode, then fatal database query won't break anything. * * @param int $code * @param string $msg * @param string $sql * @return bool * @access public * @throws RuntimeException */ public function handleSQLError($code, $msg, $sql) { $error_msg = self::shortenMessage(self::DB_ERROR_PREFIX . ' #' . $code . ' - ' . $msg . '. SQL: ' . trim($sql)); if ( isset($this->Application->Debugger) ) { if ( kUtil::constOn('DBG_SQL_FAILURE') && !defined('IS_INSTALL') ) { throw new RuntimeException($error_msg); } else { $this->Application->Debugger->appendTrace(); } } // next line also trigger attached error handlers trigger_error($error_msg, E_USER_WARNING); return true; } /** * Packs information about error into a single line * * @param string $errno * @param bool $strip_tags * @return string * @access public */ public function toString($errno = null, $strip_tags = false) { if ( !isset($errno) ) { $errno = $this->_logRecord['LogCode']; } $errstr = $this->_logRecord['LogMessage']; $errfile = $this->_logRecord['LogSourceFilename']; $errline = $this->_logRecord['LogSourceFileLine']; if ( PHP_SAPI === 'cli' ) { $result = sprintf(' [%s] ' . PHP_EOL . ' %s', $errno, $errstr); if ( $this->_logRecord['LogBacktrace'] ) { $result .= $this->printBacktrace(unserialize($this->_logRecord['LogBacktrace'])); } } else { $result = '' . $errno . ': ' . "{$errstr} in {$errfile} on line {$errline}"; } return $strip_tags ? strip_tags($result) : $result; } /** * Prints backtrace result * * @param array $trace Trace. * * @return string */ protected function printBacktrace(array $trace) { if ( !$trace ) { return ''; } $ret = PHP_EOL . PHP_EOL . PHP_EOL . 'Exception trace:' . PHP_EOL; foreach ( $trace as $trace_info ) { $class = isset($trace_info['class']) ? $trace_info['class'] : ''; $type = isset($trace_info['type']) ? $trace_info['type'] : ''; $function = $trace_info['function']; $args = isset($trace_info['args']) && $trace_info['args'] ? '...' : ''; $file = isset($trace_info['file']) ? $trace_info['file'] : 'n/a'; $line = isset($trace_info['line']) ? $trace_info['line'] : 'n/a'; $ret .= sprintf(' %s%s%s(%s) at %s:%s' . PHP_EOL, $class, $type, $function, $args, $file, $line); } return $ret; } /** * Saves log to file (e.g. when not possible to save into database) * * @param $filename * @return bool * @access protected */ protected function _saveToFile($filename) { $time = adodb_date('Y-m-d H:i:s'); $log_file = new SplFileObject($filename, 'a'); return $log_file->fwrite('[' . $time . '] #' . $this->toString(null, true) . PHP_EOL) > 0; } /** * Checks if log type of current log record matches given one * * @param int $log_type * @param string $log_message * @return bool * @access public */ public function isLogType($log_type, $log_message = null) { if ( $this->_logRecord['LogType'] == $log_type ) { return true; } if ( $log_type == self::LT_DATABASE ) { if ( !isset($log_message) ) { $log_message = $this->_logRecord['LogMessage']; } return strpos($log_message, self::DB_ERROR_PREFIX) !== false; } return false; } /** * Shortens message * * @param string $message * @return string * @access public */ public static function shortenMessage($message) { $max_len = ini_get('log_errors_max_len'); if ( strlen($message) > $max_len ) { $long_key = kUtil::generateId(); self::$_longMessages[$long_key] = $message; return mb_substr($message, 0, $max_len - strlen($long_key) - 2) . ' #' . $long_key; } return $message; } /** * Expands shortened message * * @param string $message * @param bool $clear_cache Allow debugger to expand message after it's been expanded by kLogger * @return string * @access public */ public static function expandMessage($message, $clear_cache = true) { if ( preg_match('/(.*)#([\d]+)$/', $message, $regs) ) { $long_key = $regs[2]; if ( isset(self::$_longMessages[$long_key]) ) { $message = self::$_longMessages[$long_key]; if ( $clear_cache ) { unset(self::$_longMessages[$long_key]); } } } return $message; } /** * Parses database error message into error number, error message and sql that caused that error * * @static * @param string $message * @return Array * @access public */ public static function parseDatabaseError($message) { $regexp = '/' . preg_quote(self::DB_ERROR_PREFIX) . ' #(.*?) - (.*?)\. SQL: (.*?)$/s'; if ( preg_match($regexp, $message, $regs) ) { // errno, errstr, sql return Array ($regs[1], $regs[2], $regs[3]); } return Array (0, $message, ''); } } /** * Base class for error or exception handling */ abstract class kHandlerStack extends kBase { /** * List of added handlers * * @var Array * @access protected */ protected $_handlers = Array (); /** * Reference to event log, which created this object * * @var kLogger * @access protected */ protected $_logger; /** * Remembers if handler is activated * * @var bool * @access protected */ protected $_enabled = false; public function __construct(kLogger $logger) { parent::__construct(); $this->_logger = $logger; if ( !kUtil::constOn('DBG_ZEND_PRESENT') ) { $this->attach(); $this->_enabled = true; } } /** * Detaches from error handling routines on class destruction * * @return void * @access public */ public function __destruct() { if ( !$this->_enabled ) { return; } $this->detach(); $this->_enabled = false; } /** * Attach to error handling routines * * @abstract * @return void * @access protected */ abstract protected function attach(); /** * Detach from error handling routines * * @abstract * @return void * @access protected */ abstract protected function detach(); /** * Adds new handler to the stack * * @param callable $handler * @return void * @access public */ public function add($handler) { $this->_handlers[] = $handler; } protected function _handleFatalError($errno) { $debug_mode = defined('DEBUG_MODE') && DEBUG_MODE; $skip_reporting = defined('DBG_SKIP_REPORTING') && DBG_SKIP_REPORTING; if ( !$this->_handlers || ($debug_mode && $skip_reporting) ) { // when debugger absent OR it's present, but we actually can't see it's error report (e.g. during ajax request) if ( $this->_isFatalError($errno) ) { $this->_displayFatalError($errno); } if ( !$this->_handlers ) { return true; } } return null; } /** * Determines if given error is a fatal * * @abstract * @param Exception|int $errno * @return bool */ abstract protected function _isFatalError($errno); /** * Displays div with given error message * * @param string $errno * @return void * @access protected */ protected function _displayFatalError($errno) { $errno = $this->_getFatalErrorTitle($errno); $margin = $this->Application->isAdmin ? '8px' : 'auto'; $error_msg = $this->_logger->toString($errno, PHP_SAPI === 'cli'); if ( PHP_SAPI === 'cli' ) { echo $error_msg; } else { echo '
' . $error_msg . '
'; } exit; } /** * Returns title to show for a fatal * * @abstract * @param Exception|int $errno * @return string */ abstract protected function _getFatalErrorTitle($errno); } /** * Class, that handles errors */ class kErrorHandlerStack extends kHandlerStack { /** * Attach to error handling routines * * @return void * @access protected */ protected function attach() { // set as error handler $error_handler = set_error_handler(Array ($this, 'handle')); if ( $error_handler ) { // wrap around previous error handler, if any was set $this->_handlers[] = $error_handler; } } /** * Detach from error handling routines * * @return void * @access protected */ protected function detach() { restore_error_handler(); } /** * Determines if given error is a fatal * * @param int $errno * @return bool * @access protected */ protected function _isFatalError($errno) { $fatal_errors = Array (E_USER_ERROR, E_RECOVERABLE_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE); return in_array($errno, $fatal_errors); } /** * Returns title to show for a fatal * * @param int $errno * @return string * @access protected */ protected function _getFatalErrorTitle($errno) { return 'Fatal Error'; } /** * Default error handler * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param Array $errcontext * @return bool * @access public */ public function handle($errno, $errstr, $errfile = null, $errline = null, $errcontext = Array ()) { $log = $this->_logger->prepare()->addError($errno, $errstr, $errfile, $errline); if ( $this->_handleFatalError($errno) ) { $log->write(); return true; } $log->write(); $res = false; foreach ($this->_handlers as $handler) { $res = call_user_func($handler, $errno, $errstr, $errfile, $errline, $errcontext); } return $res; } } /** * Class, that handles exceptions */ class kExceptionHandlerStack extends kHandlerStack { /** * Attach to error handling routines * * @return void * @access protected */ protected function attach() { // set as exception handler $exception_handler = set_exception_handler(Array ($this, 'handle')); if ( $exception_handler ) { // wrap around previous exception handler, if any was set $this->_handlers[] = $exception_handler; } } /** * Detach from error handling routines * * @return void * @access protected */ protected function detach() { restore_exception_handler(); } /** * Determines if given error is a fatal * * @param Exception $errno * @return bool */ protected function _isFatalError($errno) { return true; } /** * Returns title to show for a fatal * * @param Exception $errno * @return string */ protected function _getFatalErrorTitle($errno) { return get_class($errno); } /** * Handles exception * * @param Exception $exception * @return bool * @access public */ public function handle($exception) { $log = $this->_logger->prepare()->addException($exception); if ( $exception instanceof kRedirectException ) { /* @var $exception kRedirectException */ $exception->run(); } if ( $this->_handleFatalError($exception) ) { $log->write(); return true; } $log->write(); $res = false; foreach ($this->_handlers as $handler) { $res = call_user_func($handler, $exception); } return $res; } } Index: branches/5.2.x/core/kernel/utility/debugger.php =================================================================== --- branches/5.2.x/core/kernel/utility/debugger.php (revision 16434) +++ branches/5.2.x/core/kernel/utility/debugger.php (revision 16435) @@ -1,2105 +1,2104 @@ = 1099511627776) { $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); $suffix = "TB"; } elseif ($bytes >= 1073741824) { $return = round($bytes / 1024 / 1024 / 1024, 2); $suffix = "GB"; } elseif ($bytes >= 1048576) { $return = round($bytes / 1024 / 1024, 2); $suffix = "MB"; } elseif ($bytes >= 1024) { $return = round($bytes / 1024, 2); $suffix = "KB"; } else { $return = $bytes; $suffix = "Byte"; } $return .= ' '.$suffix; return $return; } /** * Checks, that user IP address is within allowed range * * @param string $ip_list semi-column (by default) separated ip address list * @param string $separator ip address separator (default ";") * * @return bool */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $client_ip = self::getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Returns the client IP address. * * @return string The client IP address * @access public */ public static function getClientIp() { if ( self::$trustProxy ) { if ( array_key_exists('HTTP_CLIENT_IP', $_SERVER) ) { return $_SERVER['HTTP_CLIENT_IP']; } if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) ) { $client_ip = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']); foreach ($client_ip as $ip_address) { $clean_ip_address = trim($ip_address); if ( false !== filter_var($clean_ip_address, FILTER_VALIDATE_IP) ) { return $clean_ip_address; } } return ''; } } return $_SERVER['REMOTE_ADDR']; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } if ($network == $ip) { // comparing two ip addresses directly return true; } $d = strpos($network, '-'); if ($d !== false) { // ip address range specified $from = ip2long(trim(substr($network, 0, $d))); $to = ip2long(trim(substr($network, $d + 1))); $ip = ip2long($ip); return ($ip >= $from && $ip <= $to); } elseif (strpos($network, '/') !== false) { // single subnet specified $ip_arr = explode('/', $network); if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) { $ip_arr[0] .= '.0'; // Alternate form 194.1.4/24 } $network_long = ip2long($ip_arr[0]); $x = ip2long($ip_arr[1]); $mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1])); $ip_long = ip2long($ip); return ($ip_long & $mask) == ($network_long & $mask); } return false; } } /** * Main debugger class, that can be used with any In-Portal (or not) project */ class Debugger { const ROW_TYPE_ERROR = 'error'; const ROW_TYPE_WARNING = 'warning'; const ROW_TYPE_NOTICE = 'notice'; const ROW_TYPE_SQL = 'sql'; const ROW_TYPE_OTHER = 'other'; /** * Holds reference to global KernelApplication instance * * @var kApplication * @access private */ private $Application = null; /** * Stores last fatal error hash or 0, when no fatal error happened * * @var integer */ private $_fatalErrorHash = 0; private $_filterTypes = Array ('error', 'sql', 'other'); /** * Counts warnings on the page * * @var int * @access public */ public $WarningCount = 0; /** * Allows to track compile errors, like "stack-overflow" * * @var bool * @access private */ private $_compileError = false; /** * Debugger data for building report * * @var Array * @access private */ private $Data = Array (); /** * Holds information about each profiler record (start/end/description) * * @var Array * @access private */ private $ProfilerData = Array (); /** * Holds information about total execution time per profiler key (e.g. total sql time) * * @var Array * @access private */ private $ProfilerTotals = Array (); /** * Counts how much each of total types were called (e.g. total error count) * * @var Array * @access private */ private $ProfilerTotalCount = Array (); /** * Holds information about all profile points registered * * @var Array * @access private */ private $ProfilePoints = Array (); /** * Prevent recursion when processing debug_backtrace() function results * * @var Array * @access private */ private $RecursionStack = Array (); /** * Cross browser debugger report scrollbar width detection * * @var int * @access private */ private $scrollbarWidth = 0; /** * Remembers how much memory & time was spent on including files * * @var Array * @access public * @see kUtil::includeOnce */ public $IncludesData = Array (); /** * Remembers maximal include deep level * * @var int * @access public * @see kUtil::includeOnce */ public $IncludeLevel = 0; /** * Prevents report generation more then once * * @var bool * @access private */ private $_inReportPrinting = false; /** * Transparent spacer image used in case of none spacer image defined via SPACER_URL constant. * Used while drawing progress bars (memory usage, time usage, etc.) * * @var string * @access private */ private $dummyImage = ''; /** * Temporary files created by debugger will be stored here * * @var string * @access private */ private $tempFolder = ''; /** * Debug rows will be separated using this string before writing to debug file * * @var string * @access private */ private $rowSeparator = '@@'; /** * Base URL for debugger includes * * @var string * @access private */ private $baseURL = ''; /** * Sub-folder, where In-Portal is installed * * @var string * @access private */ private $basePath = ''; /** * Holds last recorded timestamp (for appendTimestamp) * * @var int * @access private */ private $LastMoment; /** * Determines, that current request is AJAX request * * @var bool * @access private */ private $_isAjax = false; /** * Creates instance of debugger */ public function __construct() { global $start, $dbg_options; // check if user haven't defined DEBUG_MODE contant directly if ( defined('DEBUG_MODE') && DEBUG_MODE ) { die('error: constant DEBUG_MODE defined directly, please use $dbg_options array instead'); } if ( class_exists('kUtil') ) { - $vars = kUtil::getConfigVars(); - DebuggerUtil::$trustProxy = isset($vars['TrustProxy']) ? (bool)$vars['TrustProxy'] : false; + DebuggerUtil::$trustProxy = kUtil::getSystemConfig()->get('TrustProxy'); } // check IP before enabling debug mode $ip_match = DebuggerUtil::ipMatch(isset($dbg_options['DBG_IP']) ? $dbg_options['DBG_IP'] : ''); if ( !$ip_match || (isset($_COOKIE['debug_off']) && $_COOKIE['debug_off']) ) { define('DEBUG_MODE', 0); return; } // debug is allowed for user, continue initialization $this->InitDebugger(); $this->profileStart('kernel4_startup', 'Startup and Initialization of kernel4', $start); $this->profileStart('script_runtime', 'Script runtime', $start); $this->LastMoment = $start; error_reporting(E_ALL & ~E_STRICT); // show errors on screen in case if not in Zend Studio debugging ini_set('display_errors', DebuggerUtil::constOn('DBG_ZEND_PRESENT') ? 0 : 1); // vertical scrollbar width differs in Firefox and other browsers $this->scrollbarWidth = $this->isGecko() ? 22 : 25; $this->appendRequest(); } /** * Set's default values to constants debugger uses * */ function InitDebugger() { global $dbg_options; unset($dbg_options['DBG_IP']); // Detect fact, that this session being debugged by Zend Studio foreach ($_COOKIE as $cookie_name => $cookie_value) { if (substr($cookie_name, 0, 6) == 'debug_') { DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 1); break; } } DebuggerUtil::safeDefine('DBG_ZEND_PRESENT', 0); // set this constant value to 0 (zero) to debug debugger using Zend Studio // set default values for debugger constants $dbg_constMap = Array ( 'DBG_USE_HIGHLIGHT' => 1, // highlight output same as php code using "highlight_string" function 'DBG_WINDOW_WIDTH' => 700, // set width of debugger window (in pixels) for better viewing large amount of debug data 'DBG_USE_SHUTDOWN_FUNC' => DBG_ZEND_PRESENT ? 0 : 1, // use shutdown function to include debugger code into output 'DBG_HANDLE_ERRORS' => DBG_ZEND_PRESENT ? 0 : 1, // handle all allowed by php (see php manual) errors instead of default handler 'DBG_DOMVIEWER' => '/temp/domviewer.html', // path to DOMViewer on website 'DOC_ROOT' => str_replace('\\', '/', realpath($_SERVER['DOCUMENT_ROOT']) ), // windows hack 'DBG_LOCAL_BASE_PATH' => 'w:', // replace DOC_ROOT in filenames (in errors) using this path 'DBG_EDITOR_URL' => 'file://%F:%L', 'DBG_SHORTCUT' => 'F12', // Defines debugger activation shortcut (any symbols or Ctrl/Alt/Shift are allowed, e.g. Ctrl+Alt+F12) ); // debugger is initialized before kHTTPQuery, so do jQuery headers check here too if (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') { $this->_isAjax = true; } elseif (array_key_exists('ajax', $_GET) && $_GET['ajax'] == 'yes') { $this->_isAjax = true; } // user defined options override debugger defaults $dbg_constMap = array_merge($dbg_constMap, $dbg_options); if ($this->_isAjax && array_key_exists('DBG_SKIP_AJAX', $dbg_constMap) && $dbg_constMap['DBG_SKIP_AJAX']) { $dbg_constMap['DBG_SKIP_REPORTING'] = 1; } // allows to validate unit configs via request variable if ( !array_key_exists('DBG_VALIDATE_CONFIGS', $dbg_constMap) ) { $dbg_constMap['DBG_VALIDATE_CONFIGS'] = array_key_exists('validate_configs', $_GET) ? (int)$_GET['validate_configs'] : 0; } // when validation configs, don't show sqls for better validation error displaying if ($dbg_constMap['DBG_VALIDATE_CONFIGS']) { $dbg_constMap['DBG_SQL_PROFILE'] = 0; } // when showing explain make shure, that debugger window is large enough if (array_key_exists('DBG_SQL_EXPLAIN', $dbg_constMap) && $dbg_constMap['DBG_SQL_EXPLAIN']) { $dbg_constMap['DBG_WINDOW_WIDTH'] = 1000; } foreach ($dbg_constMap as $dbg_constName => $dbg_constValue) { DebuggerUtil::safeDefine($dbg_constName, $dbg_constValue); } } /** * Performs debugger initialization * * @return void */ private function InitReport() { if ( !class_exists('kApplication') ) { return; } $application =& kApplication::Instance(); // string used to separate debugger records while in file (used in debugger dump filename too) $this->rowSeparator = '@' . (/*is_object($application->Factory) &&*/ $application->InitDone ? $application->GetSID() : 0) . '@'; // $this->rowSeparator = '@' . rand(0, 100000) . '@'; // include debugger files from this url $reg_exp = '/^' . preg_quote(FULL_PATH, '/') . '/'; $kernel_path = preg_replace($reg_exp, '', KERNEL_PATH, 1); $this->baseURL = PROTOCOL . SERVER_NAME . (defined('PORT') ? ':' . PORT : '') . rtrim(BASE_PATH, '/') . $kernel_path . '/utility/debugger'; // store debugger cookies at this path $this->basePath = rtrim(BASE_PATH, '/'); // save debug output in this folder $this->tempFolder = defined('RESTRICTED') ? RESTRICTED : WRITEABLE . '/cache'; } /** * Appends all passed variable values (without variable names) to debug output * * @return void * @access public */ public function dumpVars() { $dump_mode = 'var_dump'; $dumpVars = func_get_args(); if ( $dumpVars[count($dumpVars) - 1] === 'STRICT' ) { $dump_mode = 'strict_var_dump'; array_pop($dumpVars); } foreach ($dumpVars as $varValue) { $this->Data[] = Array ('value' => $varValue, 'debug_type' => $dump_mode); } } /** * Transforms collected data at given index into human-readable HTML to place in debugger report * * @param int $dataIndex * @return string * @access private */ private function prepareHTML($dataIndex) { static $errors_displayed = 0; $Data =& $this->Data[$dataIndex]; if ( $Data['debug_type'] == 'html' ) { return $Data['html']; } switch ($Data['debug_type']) { case 'error': $errors_displayed++; $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '' . $this->getErrorNameByCode($Data['no']) . ' (#' . $errors_displayed . '): ' . $Data['str']; $ret .= ' in ' . $fileLink . ' on line ' . $Data['line'] . ''; return $ret; break; case 'exception': $fileLink = $this->getFileLink($Data['file'], $Data['line']); $ret = '' . $Data['exception_class'] . ': ' . $Data['str']; $ret .= ' in ' . $fileLink . ' on line ' . $Data['line'] . ''; return $ret; break; case 'var_dump': return $this->highlightString($this->print_r($Data['value'], true)); break; case 'strict_var_dump': return $this->highlightString(var_export($Data['value'], true)); break; case 'trace': ini_set('memory_limit', '500M'); $trace =& $Data['trace']; $i = 0; $traceCount = count($trace); $ret = ''; while ( $i < $traceCount ) { $traceRec =& $trace[$i]; $argsID = 'trace_args_' . $dataIndex . '_' . $i; $has_args = isset($traceRec['args']); if ( isset($traceRec['file']) ) { $func_name = isset($traceRec['class']) ? $traceRec['class'] . $traceRec['type'] . $traceRec['function'] : $traceRec['function']; $args_link = $has_args ? 'Function' : 'Function'; $ret .= $args_link . ': ' . $this->getFileLink($traceRec['file'], $traceRec['line'], $func_name); $ret .= ' in ' . basename($traceRec['file']) . ' on line ' . $traceRec['line'] . '
'; } else { $ret .= 'no file information available'; } if ( $has_args ) { // if parameter value is longer then 200 symbols, then leave only first 50 $args = $this->highlightString($this->print_r($traceRec['args'], true)); $ret .= ''; } $i++; } return $ret; break; case 'profiler': $profileKey = $Data['profile_key']; $Data =& $this->ProfilerData[$profileKey]; $runtime = ($Data['ends'] - $Data['begins']); // in seconds $totals_key = getArrayValue($Data, 'totalsKey'); if ( $totals_key ) { $total_before = $Data['totalsBefore']; $total = $this->ProfilerTotals[$totals_key]; $div_width = Array (); $total_width = ($this->getWindowWidth() - 10); $div_width['before'] = round(($total_before / $total) * $total_width); $div_width['current'] = round(($runtime / $total) * $total_width); $div_width['left'] = round((($total - $total_before - $runtime) / $total) * $total_width); $subtitle = array_key_exists('subtitle', $Data) ? ' (' . $Data['subtitle'] . ')' : ''; $ret = 'Name' . $subtitle . ': ' . $Data['description'] . '
'; $additional = isset($Data['additional']) ? $Data['additional'] : Array (); if ( isset($Data['file']) ) { array_unshift($additional, Array ('name' => 'File', 'value' => $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line']))); } array_unshift($additional, Array ('name' => 'Runtime', 'value' => $runtime . 's')); $ret .= '
'; //FF 3.5 needs this! foreach ($additional as $mixed_param) { $ret .= '[' . $mixed_param['name'] . ': ' . $mixed_param['value'] . '] '; } /*if ( isset($Data['file']) ) { $ret .= '[Runtime: ' . $runtime . 's] [File: ' . $this->getFileLink($Data['file'], $Data['line'], basename($Data['file']) . ':' . $Data['line']) . ']
'; } else { $ret .= 'Runtime: ' . $runtime . 's
'; }*/ $ret .= '
'; $ret .= '
'; $ret .= '
'; $ret .= '
'; return $ret; } else { return 'Name: ' . $Data['description'] . '
Runtime: ' . $runtime . 's'; } break; default: return 'incorrect debug data'; break; } } /** * Returns row type for debugger row. * * @param integer $data_index Index of the row. * * @return string */ protected function getRowType($data_index) { $data = $this->Data[$data_index]; switch ($data['debug_type']) { case 'html': if ( strpos($data['html'], 'SQL Total time') !== false ) { return self::ROW_TYPE_SQL; } break; case 'error': $error_map = array( 'Fatal Error' => self::ROW_TYPE_ERROR, 'Warning' => self::ROW_TYPE_WARNING, 'Notice' => self::ROW_TYPE_NOTICE, ); return $error_map[$this->getErrorNameByCode($data['no'])]; break; case 'exception': return self::ROW_TYPE_ERROR; break; case 'profiler': if ( preg_match('/^sql_/', $data['profile_key']) ) { return self::ROW_TYPE_SQL; } break; } return self::ROW_TYPE_OTHER; } /** * Returns debugger report window width excluding scrollbar * * @return int * @access private */ private function getWindowWidth() { return DBG_WINDOW_WIDTH - $this->scrollbarWidth - 8; } /** * Tells debugger to skip objects that are heavy in plan of memory usage while printing debug_backtrace results * * @param Object $object * @return bool * @access private */ private function IsBigObject(&$object) { $skip_classes = Array( defined('APPLICATION_CLASS') ? APPLICATION_CLASS : 'kApplication', 'kFactory', 'kUnitConfigReader', 'NParser', ); foreach ($skip_classes as $class_name) { if ( strtolower(get_class($object)) == strtolower($class_name) ) { return true; } } return false; } /** * Advanced version of print_r (for debugger only). Don't print objects recursively. * * @param mixed $p_array Value to be printed. * @param boolean $return_output Return output or print it out. * @param integer $tab_count Offset in tabs. * * @return string */ private function print_r(&$p_array, $return_output = false, $tab_count = -1) { static $first_line = true; // not an array at all if ( !is_array($p_array) ) { switch ( gettype($p_array) ) { case 'NULL': return 'NULL' . "\n"; break; case 'object': return $this->processObject($p_array, $tab_count); break; case 'resource': return (string)$p_array . "\n"; break; default: // number or string if ( strlen($p_array) > 200 ) { $p_array = substr($p_array, 0, 50) . ' ...'; } return $p_array . "\n"; break; } } $output = ''; if ( count($p_array) > 50 ) { $array = array_slice($p_array, 0, 50); $array[] = '...'; } else { $array = $p_array; } $tab_count++; $output .= "Array\n" . str_repeat(' ', $tab_count) . "(\n"; $tab_count++; $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; $array_keys = array_keys($array); foreach ($array_keys as $key) { switch ( gettype($array[$key]) ) { case 'array': $output .= $tabsign . '[' . $key . '] = ' . $this->print_r($array[$key], true, $tab_count); break; case 'boolean': $output .= $tabsign . '[' . $key . '] = ' . ($array[$key] ? 'true' : 'false') . "\n"; break; case 'integer': case 'double': case 'string': if ( strlen($array[$key]) > 200 ) { $array[$key] = substr($array[$key], 0, 50) . ' ...'; } $output .= $tabsign . '[' . $key . '] = ' . $array[$key] . "\n"; break; case 'NULL': $output .= $tabsign . '[' . $key . "] = NULL\n"; break; case 'object': $output .= $tabsign . '[' . $key . "] = "; $output .= "Object (" . get_class($array[$key]) . ") = \n" . str_repeat(' ', $tab_count + 1) . "(\n"; $output .= $this->processObject($array[$key], $tab_count + 2); $output .= str_repeat(' ', $tab_count + 1) . ")\n"; break; default: $output .= $tabsign . '[' . $key . '] unknown = ' . gettype($array[$key]) . "\n"; break; } } $tab_count--; $output .= str_repeat(' ', $tab_count) . ")\n"; if ( $first_line ) { $first_line = false; $output .= "\n"; } $tab_count--; if ( $return_output ) { return $output; } else { echo $output; } return true; } /** * Returns string representation of given object (more like print_r, but with recursion prevention check) * * @param Object $object * @param int $tab_count * @return string * @access private */ private function processObject(&$object, $tab_count) { $object_class = get_class($object); if ( !in_array($object_class, $this->RecursionStack) ) { if ( $this->IsBigObject($object) ) { return 'SKIPPED (class: ' . $object_class . ")\n"; } $attribute_names = get_class_vars($object_class); if ( !$attribute_names ) { return "NO_ATTRIBUTES\n"; } else { $output = ''; array_push($this->RecursionStack, $object_class); $tabsign = $tab_count ? str_repeat(' ', $tab_count) : ''; foreach ($attribute_names as $attribute_name => $attribute_value) { if ( is_object($object->$attribute_name) ) { // it is object $output .= $tabsign . '[' . $attribute_name . '] = ' . $this->processObject($object->$attribute_name, $tab_count + 1); } else { $output .= $tabsign . '[' . $attribute_name . '] = ' . $this->print_r($object->$attribute_name, true, $tab_count); } } array_pop($this->RecursionStack); return $output; } } else { // object [in recursion stack] return '*** RECURSION *** (class: ' . $object_class . ")\n"; } } /** * Format SQL Query using predefined formatting * and highlighting techniques * * @param string $sql * @return string * @access public */ public function formatSQL($sql) { $sql = trim(preg_replace('/(\n|\t| )+/is', ' ', $sql)); // whitespace in the beginning of the regex is to avoid splitting inside words, for example "FROM int_ConfigurationValues" into "FROM intConfiguration\n\tValues" $formatted_sql = preg_replace('/\s(CREATE TABLE|DROP TABLE|SELECT|UPDATE|SET|REPLACE|INSERT|DELETE|VALUES|FROM|LEFT JOIN|INNER JOIN|LIMIT|WHERE|HAVING|GROUP BY|ORDER BY)\s/is', "\n\t$1 ", ' ' . $sql); $formatted_sql = $this->highlightString($formatted_sql); if ( defined('DBG_SQL_EXPLAIN') && DBG_SQL_EXPLAIN ) { if ( substr($sql, 0, 6) == 'SELECT' ) { $formatted_sql .= '
' . 'Explain:

'; $explain_result = $this->Application->Conn->Query('EXPLAIN ' . $sql, null, true); $explain_table = ''; foreach ($explain_result as $explain_row) { if ( !$explain_table ) { // first row -> draw header $explain_table .= '' . implode('', array_keys($explain_row)) . ''; } $explain_table .= '' . implode('', $explain_row) . ''; } $formatted_sql .= '' . $explain_table . '
'; } } return $formatted_sql; } /** * Highlights given string using "highlight_string" method * * @param string $string * @return string * @access public */ public function highlightString($string) { if ( !(defined('DBG_USE_HIGHLIGHT') && DBG_USE_HIGHLIGHT) || $this->_compileError ) { return nl2br($string); } $string = str_replace(Array ('\\', '/'), Array ('_no_match_string_', '_n_m_s_'), $string); $this->_compileError = true; // next line is possible cause of compile error $string = highlight_string('', true); $this->_compileError = false; $string = str_replace(Array ('_no_match_string_', '_n_m_s_'), Array ('\\', '/'), $string); if ( strlen($string) >= 65536 ) { // preg_replace will fail, when string is longer, then 65KB return str_replace(Array ('<?php ', '?>'), '', $string); } return preg_replace('/<\?(.*)php (.*)\?>/Us', '\\2', $string); } /** * Determine by php type of browser used to show debugger * * @return bool * @access private */ private function isGecko() { // we need isset because we may run scripts from shell with no user_agent at all return isset($_SERVER['HTTP_USER_AGENT']) && strpos(strtolower($_SERVER['HTTP_USER_AGENT']), 'firefox') !== false; } /** * Returns link for editing php file (from error) in external editor * * @param string $file filename with path from root folder * @param int $line_number line number in file where error is found * @param string $title text to show on file edit link * @return string * @access public */ public function getFileLink($file, $line_number = 1, $title = '') { if ( !$title ) { $title = str_replace('/', '\\', $this->getLocalFile($file)); } $url = str_replace('%F', $this->getLocalFile($file), DBG_EDITOR_URL); $url = str_replace('%L', $line_number, $url); return '' . $title . ''; } /** * Converts filepath on server to filepath in mapped DocumentRoot on developer pc * * @param string $remoteFile * @return string * @access private */ private function getLocalFile($remoteFile) { return preg_replace('/^' . preg_quote(DOC_ROOT, '/') . '/', DBG_LOCAL_BASE_PATH, $remoteFile, 1); } /** * Appends call trace till this method call * * @param int $levels_to_shift * @return void * @access public */ public function appendTrace($levels_to_shift = 1) { $levels_shifted = 0; $trace = debug_backtrace(); while ( $levels_shifted < $levels_to_shift ) { array_shift($trace); $levels_shifted++; } $this->Data[] = Array ('trace' => $trace, 'debug_type' => 'trace'); } /** * Appends call trace till this method call * * @param Exception $exception * @return void * @access private */ private function appendExceptionTrace(&$exception) { $trace = $exception->getTrace(); $this->Data[] = Array('trace' => $trace, 'debug_type' => 'trace'); } /** * Adds memory usage statistics * * @param string $msg * @param int $used * @return void * @access public */ public function appendMemoryUsage($msg, $used = null) { if ( !isset($used) ) { $used = round(memory_get_usage() / 1024); } $this->appendHTML('Memory usage ' . $msg . ' ' . $used . 'Kb'); } /** * Appends HTML code without transformations * * @param string $html * @return void * @access public */ public function appendHTML($html) { $this->Data[] = Array ('html' => $html, 'debug_type' => 'html'); } /** * Returns instance of FirePHP class * * @return FirePHP * @link http://www.firephp.org/HQ/Use.htm */ function firePHP() { require_once('FirePHPCore/FirePHP.class.php'); return FirePHP::getInstance(true); } /** * Change debugger info that was already generated before. * Returns true if html was set. * * @param int $index * @param string $html * @param string $type = {'append','prepend','replace'} * @return bool * @access public */ public function setHTMLByIndex($index, $html, $type = 'append') { if ( !isset($this->Data[$index]) || $this->Data[$index]['debug_type'] != 'html' ) { return false; } switch ( $type ) { case 'append': $this->Data[$index]['html'] .= '
' . $html; break; case 'prepend': $this->Data[$index]['html'] = $this->Data[$index]['html'] . '
' . $html; break; case 'replace': $this->Data[$index]['html'] = $html; break; } return true; } /** * Move $debugLineCount lines of input from debug output * end to beginning. * * @param int $debugLineCount * @return void * @access private */ private function moveToBegin($debugLineCount) { $lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount); $this->Data = array_merge($lines, $this->Data); } /** * Moves all debugger report lines after $debugLineCount into $new_row position * * @param int $new_row * @param int $debugLineCount * @return void * @access private */ private function moveAfterRow($new_row, $debugLineCount) { $lines = array_splice($this->Data, count($this->Data) - $debugLineCount, $debugLineCount); $rows_before = array_splice($this->Data, 0, $new_row, $lines); $this->Data = array_merge($rows_before, $this->Data); } /** * Appends HTTP REQUEST information to debugger report * * @return void * @access private */ private function appendRequest() { if ( isset($_SERVER['SCRIPT_FILENAME']) ) { $script = $_SERVER['SCRIPT_FILENAME']; } else { $script = $_SERVER['DOCUMENT_ROOT'] . $_SERVER['PHP_SELF']; } $this->appendHTML('ScriptName: ' . $this->getFileLink($script, 1, basename($script)) . ' (' . dirname($script) . ')'); if ( $this->_isAjax ) { $this->appendHTML('RequestURI: ' . $_SERVER['REQUEST_URI'] . ' (QS Length:' . strlen($_SERVER['QUERY_STRING']) . ')'); } $tools_html = '
' . $this->_getDomViewerHTML() . ' ' . $this->_getToolsHTML() . '
'; $this->appendHTML($tools_html); ob_start(); ?> $_GET, 'PO' => $_POST, 'CO' => $_COOKIE); foreach ($super_globals as $prefix => $data) { foreach ($data as $key => $value) { if ( !is_array($value) && trim($value) == '' ) { $value = 'no value'; } else { $value = htmlspecialchars($this->print_r($value, true), ENT_QUOTES, 'UTF-8'); } echo ''; } } ?>
SrcNameValue
' . $prefix . '' . $key . '' . $value . '
appendHTML(ob_get_contents()); ob_end_clean(); } /** * Appends php session content to debugger output * * @return void * @access private */ private function appendSession() { if ( isset($_SESSION) && $_SESSION ) { $this->appendHTML('PHP Session: [' . ini_get('session.name') . ']'); $this->dumpVars($_SESSION); $this->moveToBegin(2); } } /** * Starts profiling of a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileStart($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $this->ProfilerData[$key] = Array ('begins' => $timeStamp, 'ends' => 5000, 'debuggerRowID' => count($this->Data)); if ( isset($description) ) { $this->ProfilerData[$key]['description'] = $description; } if ( substr($key, 0, 4) == 'sql_' ) { // append place from what was called $trace_results = debug_backtrace(); $trace_count = count($trace_results); $i = 0; while ( $i < $trace_count ) { if ( !isset($trace_results[$i]['file']) ) { $i++; continue; } $trace_file = basename($trace_results[$i]['file']); if ( $trace_file != 'db_connection.php' && $trace_file != 'db_load_balancer.php' && $trace_file != 'adodb.inc.php' ) { break; } $i++; } $this->ProfilerData[$key]['file'] = $trace_results[$i]['file']; $this->ProfilerData[$key]['line'] = $trace_results[$i]['line']; if ( isset($trace_results[$i + 1]['object']) && isset($trace_results[$i + 1]['object']->Prefix) ) { $object =& $trace_results[$i + 1]['object']; /* @var $object kBase */ $prefix_special = rtrim($object->Prefix . '.' . $object->Special, '.'); $this->ProfilerData[$key]['prefix_special'] = $prefix_special; } unset($trace_results); } $this->Data[] = Array ('profile_key' => $key, 'debug_type' => 'profiler'); } /** * Ends profiling for a given $key * * @param string $key * @param string $description * @param int $timeStamp * @return void * @access public */ public function profileFinish($key, $description = null, $timeStamp = null) { if ( !isset($timeStamp) ) { $timeStamp = microtime(true); } $this->ProfilerData[$key]['ends'] = $timeStamp; if ( isset($description) ) { $this->ProfilerData[$key]['description'] = $description; } if ( substr($key, 0, 4) == 'sql_' ) { $func_arguments = func_get_args(); $rows_affected = $func_arguments[3]; $additional = Array (); if ( $rows_affected > 0 ) { $additional[] = Array ('name' => 'Affected Rows', 'value' => $rows_affected); if ( isset($func_arguments[4]) ) { if ( strlen($func_arguments[4]) > 200 ) { $func_arguments[4] = substr($func_arguments[4], 0, 50) . ' ...'; } $additional[] = Array ('name' => 'Result', 'value' => $func_arguments[4]); } } $additional[] = Array ('name' => 'Query Number', 'value' => $func_arguments[5]); if ( $func_arguments[6] ) { $this->profilerAddTotal('cachable_queries', $key); $this->ProfilerData[$key]['subtitle'] = 'cachable'; } if ( (string)$func_arguments[7] !== '' ) { $additional[] = Array ('name' => 'Server #', 'value' => $func_arguments[7]); } if ( array_key_exists('prefix_special', $this->ProfilerData[$key]) ) { $additional[] = Array ('name' => 'PrefixSpecial', 'value' => $this->ProfilerData[$key]['prefix_special']); } $this->ProfilerData[$key]['additional'] =& $additional; } } /** * Collects total execution time from profiler record * * @param string $total_key * @param string $key * @param int $value * @return void * @access public */ public function profilerAddTotal($total_key, $key = null, $value = null) { if ( !isset($this->ProfilerTotals[$total_key]) ) { $this->ProfilerTotals[$total_key] = 0; $this->ProfilerTotalCount[$total_key] = 0; } if ( !isset($value) ) { $value = $this->ProfilerData[$key]['ends'] - $this->ProfilerData[$key]['begins']; } if ( isset($key) ) { $this->ProfilerData[$key]['totalsKey'] = $total_key; $this->ProfilerData[$key]['totalsBefore'] = $this->ProfilerTotals[$total_key]; } $this->ProfilerTotals[$total_key] += $value; $this->ProfilerTotalCount[$total_key]++; } /** * Traces relative code execution speed between this method calls * * @param string $message * @return void * @access public */ public function appendTimestamp($message) { global $start; $time = microtime(true); $from_last = $time - $this->LastMoment; $from_start = $time - $start; $this->appendHTML(sprintf("%s %.5f from last %.5f from start", $message, $from_last, $from_start)); $this->LastMoment = $time; } /** * Returns unique ID for each method call * * @return int * @access public */ public function generateID() { list($usec, $sec) = explode(' ', microtime()); $id_part_1 = substr($usec, 4, 4); $id_part_2 = mt_rand(1, 9); $id_part_3 = substr($sec, 6, 4); $digit_one = substr($id_part_1, 0, 1); if ( $digit_one == 0 ) { $digit_one = mt_rand(1, 9); $id_part_1 = preg_replace('/^0/', '', $id_part_1); $id_part_1 = $digit_one . $id_part_1; } return $id_part_1 . $id_part_2 . $id_part_3; } /** * Returns error name based on it's code * * @param int $error_code * @return string * @access private */ private function getErrorNameByCode($error_code) { $error_map = Array ( 'Fatal Error' => Array (E_RECOVERABLE_ERROR, E_USER_ERROR, E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_PARSE), 'Warning' => Array (E_WARNING, E_USER_WARNING, E_CORE_WARNING, E_COMPILE_WARNING), 'Notice' => Array (E_NOTICE, E_USER_NOTICE, E_STRICT), ); if ( defined('E_DEPRECATED') ) { // since PHP 5.3 $error_map['Notice'][] = E_DEPRECATED; $error_map['Notice'][] = E_USER_DEPRECATED; } foreach ($error_map as $error_name => $error_codes) { if ( in_array($error_code, $error_codes) ) { return $error_name; } } return ''; } /** * Returns profile total key (check against missing key too) * * @param string $key * @return int * @access private */ private function getProfilerTotal($key) { if ( isset($this->ProfilerTotalCount[$key]) ) { return (int)$this->ProfilerTotalCount[$key]; } return 0; } /** * Counts how much calls were made to a place, where this method is called (basic version of profiler) * * @param string $title * @param int $level * @return void * @access public */ public function ProfilePoint($title, $level = 1) { $trace_results = debug_backtrace(); $level = min($level, count($trace_results) - 1); do { $point = $trace_results[$level]; $location = $point['file'] . ':' . $point['line']; $level++; $has_more = isset($trace_results[$level]); } while ( $has_more && $point['function'] == $trace_results[$level]['function'] ); if ( !isset($this->ProfilePoints[$title]) ) { $this->ProfilePoints[$title] = Array (); } if ( !isset($this->ProfilePoints[$title][$location]) ) { $this->ProfilePoints[$title][$location] = 0; } $this->ProfilePoints[$title][$location]++; } /** * Generates report * * @param boolean $return_result Returns or output report contents. * @param boolean $clean_output_buffer Clean output buffers before displaying anything. * @param boolean $is_shutdown_func Called from shutdown function. * * @return string */ public function printReport($return_result = false, $clean_output_buffer = true, $is_shutdown_func = false) { if ( $this->_inReportPrinting ) { // don't print same report twice (in case if shutdown function used + compression + fatal error) return ''; } $this->_inReportPrinting = true; $last_error = error_get_last(); if ( !is_null($last_error) && $is_shutdown_func ) { $this->saveError( $last_error['type'], $last_error['message'], $last_error['file'], $last_error['line'], null, $is_shutdown_func ); } $this->profileFinish('script_runtime'); $this->_breakOutOfBuffering(!$return_result); $debugger_start = memory_get_usage(); if ( defined('SPACER_URL') ) { $this->dummyImage = SPACER_URL; } $this->InitReport(); // set parameters required by AJAX // defined here, because user can define this constant while script is running, not event before debugger is started DebuggerUtil::safeDefine('DBG_RAISE_ON_WARNINGS', 0); DebuggerUtil::safeDefine('DBG_TOOLBAR_BUTTONS', 1); $this->appendSession(); // show php session if any // ensure, that 1st line of debug output always is this one: $top_line = '
[Reload Frame] [Hide Debugger] [Clear Debugger][Current Time: ' . date('H:i:s') . '] [File Size: #DBG_FILESIZE#]
' . $this->getFilterDropdown() . '
'; $this->appendHTML($top_line); $this->moveToBegin(1); if ( count($this->ProfilePoints) > 0 ) { foreach ($this->ProfilePoints as $point => $locations) { arsort($this->ProfilePoints[$point]); } $this->appendHTML($this->highlightString($this->print_r($this->ProfilePoints, true))); } if ( DebuggerUtil::constOn('DBG_SQL_PROFILE') && isset($this->ProfilerTotals['sql']) ) { // sql query profiling was enabled -> show totals if ( array_key_exists('cachable_queries', $this->ProfilerTotalCount) ) { $append = ' Cachable queries: ' . $this->ProfilerTotalCount['cachable_queries']; } else { $append = ''; } $this->appendHTML('SQL Total time: ' . $this->ProfilerTotals['sql'] . ' Number of queries: ' . $this->ProfilerTotalCount['sql'] . $append); } if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') && isset($this->ProfilerTotals['includes']) ) { // included file profiling was enabled -> show totals $this->appendHTML('Included Files Total time: ' . $this->ProfilerTotals['includes'] . ' Number of includes: ' . $this->ProfilerTotalCount['includes']); } if ( DebuggerUtil::constOn('DBG_PROFILE_MEMORY') ) { // detailed memory usage reporting by objects was enabled -> show totals $this->appendHTML('Memory used by Objects: ' . round($this->ProfilerTotals['objects'] / 1024, 2) . 'Kb'); } if ( DebuggerUtil::constOn('DBG_INCLUDED_FILES') ) { $files = get_included_files(); $this->appendHTML('Included files:'); foreach ($files as $file) { $this->appendHTML($this->getFileLink($this->getLocalFile($file)) . ' (' . round(filesize($file) / 1024, 2) . 'Kb)'); } } if ( DebuggerUtil::constOn('DBG_PROFILE_INCLUDES') ) { $totals = $totals_configs = Array ('mem' => 0, 'time' => 0); $this->appendHTML('Included files statistics:' . (DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ? ' (sorted by memory usage)' : '')); if ( is_array($this->IncludesData['mem']) ) { if ( DebuggerUtil::constOn('DBG_SORT_INCLUDES_MEM') ) { array_multisort($this->IncludesData['mem'], SORT_DESC, $this->IncludesData['file'], $this->IncludesData['time'], $this->IncludesData['level']); } foreach ($this->IncludesData['file'] as $key => $file_name) { $this->appendHTML(str_repeat(' -> ', ($this->IncludesData['level'][$key] >= 0 ? $this->IncludesData['level'][$key] : 0)) . $file_name . ' Mem: ' . sprintf("%.4f Kb", $this->IncludesData['mem'][$key] / 1024) . ' Time: ' . sprintf("%.4f", $this->IncludesData['time'][$key])); if ( $this->IncludesData['level'][$key] == 0 ) { $totals['mem'] += $this->IncludesData['mem'][$key]; $totals['time'] += $this->IncludesData['time'][$key]; } elseif ( $this->IncludesData['level'][$key] == -1 ) { $totals_configs['mem'] += $this->IncludesData['mem'][$key]; $totals_configs['time'] += $this->IncludesData['time'][$key]; } } $this->appendHTML('Sub-Total classes: ' . ' Mem: ' . sprintf("%.4f Kb", $totals['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals['time'])); $this->appendHTML('Sub-Total configs: ' . ' Mem: ' . sprintf("%.4f Kb", $totals_configs['mem'] / 1024) . ' Time: ' . sprintf("%.4f", $totals_configs['time'])); $this->appendHTML('Grand Total: ' . ' Mem: ' . sprintf("%.4f Kb", ($totals['mem'] + $totals_configs['mem']) / 1024) . ' Time: ' . sprintf("%.4f", $totals['time'] + $totals_configs['time'])); } } $skip_reporting = DebuggerUtil::constOn('DBG_SKIP_REPORTING') || DebuggerUtil::constOn('DBG_ZEND_PRESENT'); if ( ($this->_isAjax && !DebuggerUtil::constOn('DBG_SKIP_AJAX')) || !$skip_reporting ) { $debug_file = $this->tempFolder . '/debug_' . $this->rowSeparator . '.txt'; if ( file_exists($debug_file) ) { unlink($debug_file); } $i = 0; $fp = fopen($debug_file, 'a'); $lineCount = count($this->Data); while ( $i < $lineCount ) { $html = $this->prepareHTML($i); $row_type = $this->getRowType($i); fwrite($fp, json_encode(Array ('html' => $html, 'row_type' => $row_type)) . $this->rowSeparator); $i++; } fclose($fp); } if ( $skip_reporting ) { // let debugger write report and then don't output anything return ''; } $application =& kApplication::Instance(); $dbg_path = str_replace(FULL_PATH, '', $this->tempFolder); $debugger_params = Array ( 'FilterTypes' => $this->_filterTypes, 'RowSeparator' => $this->rowSeparator, 'ErrorsCount' => (int)$this->getProfilerTotal('error_handling'), 'IsFatalError' => $this->_fatalErrorHappened(), 'SQLCount' => (int)$this->getProfilerTotal('sql'), 'SQLTime' => isset($this->ProfilerTotals['sql']) ? sprintf('%.5f', $this->ProfilerTotals['sql']) : 0, 'ScriptTime' => sprintf('%.5f', $this->ProfilerData['script_runtime']['ends'] - $this->ProfilerData['script_runtime']['begins']), 'ScriptMemory' => DebuggerUtil::formatSize($this->getMemoryUsed($debugger_start)), 'Shortcut' => DBG_SHORTCUT, ); ob_start(); // the getShortReport($this->getMemoryUsed($debugger_start)); return $ret; } else { if ( !DebuggerUtil::constOn('DBG_HIDE_FULL_REPORT') ) { $this->_breakOutOfBuffering(); } elseif ( $clean_output_buffer ) { ob_clean(); } echo $this->getShortReport($this->getMemoryUsed($debugger_start)); } return ''; } function getFilterDropdown() { $filter_options = ''; foreach ( $this->_filterTypes as $filter_type ) { $filter_options .= ''; } return 'Show: '; } function getMemoryUsed($debugger_start) { if ( !isset($this->ProfilerTotals['error_handling']) ) { $memory_used = $debugger_start; $this->ProfilerTotalCount['error_handling'] = 0; } else { $memory_used = $debugger_start - $this->ProfilerTotals['error_handling']; } return $memory_used; } /** * Format's memory usage report by debugger * * @param int $memory_used * @return string * @access private */ private function getShortReport($memory_used) { if ( DebuggerUtil::constOn('DBG_TOOLBAR_BUTTONS') ) { // evenrything is in toolbar - don't duplicate return ''; } else { // toolbar not visible, then show sql & error count too $info = Array ( 'Script Runtime' => 'PROFILE:script_runtime', 'SQL\'s Runtime' => 'PROFILE_T:sql', '-' => 'SEP:-', 'Notice / Warning' => 'PROFILE_TC:error_handling', 'SQLs Count' => 'PROFILE_TC:sql', ); } $ret = ''; // 'Application:' . DebuggerUtil::formatSize($memory_used) . ' (' . $memory_used . ')'; foreach ($info as $title => $value_key) { list ($record_type, $record_data) = explode(':', $value_key, 2); switch ( $record_type ) { case 'PROFILE': // profiler totals value $Data =& $this->ProfilerData[$record_data]; $profile_time = ($Data['ends'] - $Data['begins']); // in seconds $ret .= '' . $title . ':' . sprintf('%.5f', $profile_time) . ' s'; break; case 'PROFILE_TC': // profile totals record count $record_cell = ''; if ( $record_data == 'error_handling' && $this->ProfilerTotalCount[$record_data] > 0 ) { $record_cell = ''; } $ret .= '' . $record_cell . $title . ':' . $record_cell . '' . $this->ProfilerTotalCount[$record_data] . ''; break; case 'PROFILE_T': // profile total $record_cell = ''; $total = array_key_exists($record_data, $this->ProfilerTotals) ? $this->ProfilerTotals[$record_data] : 0; $ret .= '' . $record_cell . $title . ':' . $record_cell . '' . sprintf('%.5f', $total) . ' s'; break; case 'SEP': $ret .= ''; break; } } return '
' . $ret . '
'; } /** * Detects if there was a fatal error at some point * * @return boolean */ private function _fatalErrorHappened() { return $this->_fatalErrorHash !== 0; } /** * Creates error hash * * @param string $errfile File, where error happened. * @param integer $errline Line in file, where error happened. * * @return integer */ private function _getErrorHash($errfile, $errline) { return crc32($errfile . ':' . $errline); } /** * User-defined error handler * * @param integer $errno Error code. * @param string $errstr Error message. * @param string $errfile Error file. * @param integer $errline Error line. * @param array $errcontext Error context. * @param boolean $is_shutdown_func Called from shutdown function. * * @return boolean * @throws Exception When unknown error code given. */ public function saveError( $errno, $errstr, $errfile = null, $errline = null, array $errcontext = null, $is_shutdown_func = false ) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $errorType = $this->getErrorNameByCode($errno); if (!$errorType) { throw new Exception('Unknown error type [' . $errno . ']'); return false; } elseif ( substr($errorType, 0, 5) == 'Fatal' ) { $this->_fatalErrorHash = $this->_getErrorHash($errfile, $errline); $this->appendTrace(4); } $this->expandError($errstr, $errfile, $errline); $this->Data[] = Array ( 'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'context' => $errcontext, 'debug_type' => 'error' ); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); if ($errorType == 'Warning') { $this->WarningCount++; } if ( $this->_fatalErrorHappened() && $this->_getErrorHash($errfile, $errline) === $this->_fatalErrorHash ) { // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); if ( !$is_shutdown_func ) { exit; } } return true; } /** * Adds exception details into debugger but don't cause fatal error * * @param Exception $exception * @return void * @access public */ public function appendException($exception) { $this->ProfilerData['error_handling']['begins'] = memory_get_usage(); $this->appendExceptionTrace($exception); $errno = $exception->getCode(); $errstr = $exception->getMessage(); $errfile = $exception->getFile(); $errline = $exception->getLine(); $this->expandError($errstr, $errfile, $errline); $this->Data[] = Array ( 'no' => $errno, 'str' => $errstr, 'file' => $errfile, 'line' => $errline, 'exception_class' => get_class($exception), 'debug_type' => 'exception' ); $this->ProfilerData['error_handling']['ends'] = memory_get_usage(); $this->profilerAddTotal('error_handling', 'error_handling'); } /** * User-defined exception handler * * @param Exception $exception * @return void * @access public */ public function saveException($exception) { $this->appendException($exception); $this->_fatalErrorHash = $this->_getErrorHash($exception->getFile(), $exception->getLine()); // Append debugger report to data in buffer & clean buffer afterwards. echo $this->_breakOutOfBuffering(false) . $this->printReport(true); } /** * Transforms short error messages into long ones * * @param string $errstr * @param string $errfile * @param int $errline * @return void * @access private */ private function expandError(&$errstr, &$errfile, &$errline) { $errstr = kLogger::expandMessage($errstr); list ($errno, $errstr, $sql) = kLogger::parseDatabaseError($errstr); if ( $errno != 0 ) { $errstr = '' . $errstr . ' (' . $errno . ')
SQL: ' . $this->formatSQL($sql); } if ( strpos($errfile, 'eval()\'d code') !== false ) { $errstr = '[EVAL, line ' . $errline . ']: ' . $errstr; $tmpStr = $errfile; $pos = strpos($tmpStr, '('); $errfile = substr($tmpStr, 0, $pos); $pos++; $errline = substr($tmpStr, $pos, strpos($tmpStr, ')', $pos) - $pos); } } /** * Break buffering in case if fatal error is happened in the middle * * @param bool $flush * @return string * @access private */ private function _breakOutOfBuffering($flush = true) { $buffer_content = Array (); while ( ob_get_level() ) { $buffer_content[] = ob_get_clean(); } $ret = implode('', array_reverse($buffer_content)); if ( $flush ) { echo $ret; flush(); } return $ret; } /** * Saves given message to "vb_debug.txt" file in DocumentRoot * * @param string $msg * @return void * @access public */ public function saveToFile($msg) { $fp = fopen($_SERVER['DOCUMENT_ROOT'] . '/vb_debug.txt', 'a'); fwrite($fp, $msg . "\n"); fclose($fp); } /** * Prints given constant values in a table * * @param mixed $constants * @return void * @access public */ public function printConstants($constants) { if ( !is_array($constants) ) { $constants = explode(',', $constants); } $constant_tpl = '%s%s'; $ret = ''; foreach ($constants as $constant_name) { $ret .= sprintf($constant_tpl, $constant_name, constant($constant_name)); } $ret .= '
'; $this->appendHTML($ret); } /** * Attaches debugger to Application * * @return void * @access public */ public function AttachToApplication() { if ( !DebuggerUtil::constOn('DBG_HANDLE_ERRORS') ) { return; } if ( class_exists('kApplication') ) { $this->Application =& kApplication::Instance(); $this->Application->Debugger = $this; } // kLogger will auto-detect these automatically // error/exception handlers registered before debugger will be removed! set_error_handler( Array ($this, 'saveError') ); set_exception_handler( Array ($this, 'saveException') ); } /** * Returns HTML for tools section * * @return string * @access private */ private function _getToolsHTML() { $html = '
System Tools:
'; return $html; } /** * Returns HTML for dom viewer section * * @return string * @access private */ private function _getDomViewerHTML() { $html = '
DomViewer:
'; return $html; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !DebuggerUtil::constOn('DBG_ZEND_PRESENT') ) { $debugger = new Debugger(); } if ( DebuggerUtil::constOn('DBG_USE_SHUTDOWN_FUNC') ) { register_shutdown_function(array(&$debugger, 'printReport'), false, true, true); } } Index: branches/5.2.x/core/kernel/globals.php =================================================================== --- branches/5.2.x/core/kernel/globals.php (revision 16434) +++ branches/5.2.x/core/kernel/globals.php (revision 16435) @@ -1,1125 +1,1076 @@ $array2_value ) { if ( isset($array1[$array2_key]) ) { $array1[$array2_key] = self::array_merge_recursive($array1[$array2_key], $array2_value); } else { $array1[$array2_key] = $array2_value; } } return $array1; } /** * Prepend a reference to an element to the beginning of an array. * Renumbers numeric keys, so $value is always inserted to $array[0] * * @param $array array * @param $value mixed * @return int * @access public */ public static function array_unshift_ref(&$array, &$value) { $return = array_unshift($array,''); $array[0] =& $value; return $return; } /** * Rename key in associative array, maintaining keys order * * @param Array $array Associative Array * @param mixed $old Old key name * @param mixed $new New key name * @access public */ public static function array_rename_key(&$array, $old, $new) { $new_array = Array (); foreach ($array as $key => $val) { $new_array[ $key == $old ? $new : $key] = $val; } $array = $new_array; } /** * Same as print_r, but outputs result on screen or in debugger report (when in debug mode) * * @param Array $data * @param string $label * @param bool $on_screen * @access public */ public static function print_r($data, $label = '', $on_screen = false) { $is_debug = false; if ( class_exists('kApplication') && !$on_screen ) { $application =& kApplication::Instance(); $is_debug = $application->isDebugMode(); } if ( $is_debug && isset($application) ) { if ( $label ) { $application->Debugger->appendHTML('' . $label . ''); } $application->Debugger->dumpVars($data); } else { if ( $label ) { echo '' . $label . '
'; } echo '
', print_r($data, true), '
'; } } /** * Define constant if it was not already defined before * * @param string $const_name * @param string $const_value * @access public */ public static function safeDefine($const_name, $const_value) { if ( !defined($const_name) ) { define($const_name, $const_value); } } /** - * Parses "/system/config.php" file and returns the result + * Instantiate kSystemConfig class once and store locally * - * @param bool $parse_section - * @return Array - * @access public - */ - public static function parseConfig($parse_section = false) - { - $file = FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'config.php'; - - if ( !file_exists($file) ) { - return Array (); - } - - if ( file_exists($file) && !is_readable($file) ) { - die('Could Not Open Ini File'); - } - - $contents = file($file); - - if ( $contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n" ) { - // format of "config.php" file before 5.1.0 version - array_shift($contents); - - return parse_ini_string(implode('', $contents), $parse_section); - } - - $_CONFIG = Array (); - require($file); - - if ( $parse_section ) { - if ( isset($_CONFIG['Database']['LoadBalancing']) && $_CONFIG['Database']['LoadBalancing'] ) { - require FULL_PATH . DIRECTORY_SEPARATOR . 'system' . DIRECTORY_SEPARATOR . 'db_servers.php'; - } - - return $_CONFIG; - } - - $ret = Array (); - - foreach ($_CONFIG as $section => $section_variables) { - $ret = array_merge($ret, $section_variables); - } - - return $ret; - } - - /** - * Returns parsed variables from "config.php" file - * - * @return Array * @access public */ - public static function getConfigVars() + public static function getSystemConfig() { - static $vars = NULL; + static $system_config; - if ( !isset($vars) ) { - $vars = self::parseConfig(); + if ( !isset($system_config) ) { + $system_config = new kSystemConfig(); } - return $vars; + return $system_config; } /** * Same as "include_once", but also profiles file includes in debug mode and DBG_PROFILE_INCLUDES constant is set * * @param string $file * @access public */ public static function includeOnce($file) { global $debugger; if ( defined('DEBUG_MODE') && DEBUG_MODE && isset($debugger) && defined('DBG_PROFILE_INCLUDES') && DBG_PROFILE_INCLUDES ) { if ( in_array($file, get_included_files()) ) { return ; } global $debugger; /*$debugger->IncludeLevel++; $before_mem = memory_get_usage();*/ $debugger->ProfileStart('inc_'.crc32($file), $file); include_once($file); $debugger->ProfileFinish('inc_'.crc32($file)); $debugger->profilerAddTotal('includes', 'inc_'.crc32($file)); /*$used_mem = memory_get_usage() - $before_mem; $debugger->IncludeLevel--; $debugger->IncludesData['file'][] = str_replace(FULL_PATH, '', $file); $debugger->IncludesData['mem'][] = $used_mem; $debugger->IncludesData['time'][] = $used_time; $debugger->IncludesData['level'][] = $debugger->IncludeLevel;*/ } else { include_once($file); } } /** * Checks if given string is a serialized array * * @param string $string * @return bool * @access public */ public static function IsSerialized($string) { if ( is_array($string) ) { return false; } return preg_match('/a:([\d]+):{/', $string); } /** * Generates password of given length * * @param int $length * @return string * @access public */ public static function generatePassword($length = 10) { $pass_length = $length; $p1 = Array ('b','c','d','f','g','h','j','k','l','m','n','p','q','r','s','t','v','w','x','y','z'); $p2 = Array ('a','e','i','o','u'); $p3 = Array ('1','2','3','4','5','6','7','8','9'); $p4 = Array ('(','&',')',';','%'); // if you need real strong stuff // how much elements in the array // can be done with a array count but counting once here is faster $s1 = 21;// this is the count of $p1 $s2 = 5; // this is the count of $p2 $s3 = 9; // this is the count of $p3 $s4 = 5; // this is the count of $p4 // possible readable combinations $c1 = '121'; // will be like 'bab' $c2 = '212'; // will be like 'aba' $c3 = '12'; // will be like 'ab' $c4 = '3'; // will be just a number '1 to 9' if you dont like number delete the 3 //$c5 = '4'; // uncomment to active the strong stuff $comb = '4'; // the amount of combinations you made above (and did not comment out) for ($p = 0; $p < $pass_length;) { mt_srand((double)microtime() * 1000000); $strpart = mt_rand(1, $comb); // checking if the stringpart is not the same as the previous one if ($strpart != $previous) { $pass_structure .= ${'c' . $strpart}; // shortcutting the loop a bit $p = $p + mb_strlen(${'c' . $strpart}); } $previous = $strpart; } // generating the password from the structure defined in $pass_structure for ($g = 0; $g < mb_strlen($pass_structure); $g++) { mt_srand((double)microtime() * 1000000); $sel = mb_substr($pass_structure, $g, 1); $pass .= ${'p' . $sel}[ mt_rand(0,-1+${'s'.$sel}) ]; } return $pass; } /** * submits $url with $post as POST * * @param string $url * @param mixed $data * @param Array $headers * @param string $request_type * @param Array $curl_options * @return string * @access public * @deprecated */ public static function curl_post($url, $data, $headers = NULL, $request_type = 'POST', $curl_options = NULL) { $application =& kApplication::Instance(); $curl_helper = $application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ if ($request_type == 'POST') { $curl_helper->SetRequestMethod(kCurlHelper::REQUEST_METHOD_POST); } $curl_helper->SetRequestData($data); if (!is_null($headers)) { // not an associative array, so don't use kCurlHelper::SetHeaders method $curl_helper->setOptions( Array (CURLOPT_HTTPHEADER => $headers) ); } if (is_array($curl_options)) { $curl_helper->setOptions($curl_options); } $curl_helper->followLocation = false; $ret = $curl_helper->Send($url); $GLOBALS['curl_errorno'] = $curl_helper->lastErrorCode; $GLOBALS['curl_error'] = $curl_helper->lastErrorMsg; return $ret; } /** * Checks if constant is defined and has positive value * * @param string $const_name * @return bool * @access public */ public static function constOn($const_name) { return defined($const_name) && constant($const_name); } /** * Converts KG to Pounds * * @param float $kg * @param bool $pounds_only * @return float * @access public */ public static function Kg2Pounds($kg, $pounds_only = false) { $major = floor( round($kg / self::POUND_TO_KG, 3) ); $minor = abs(round(($kg - $major * self::POUND_TO_KG) / self::POUND_TO_KG * 16, 2)); if ($pounds_only) { $major += round($minor * 0.0625, 2); $minor = 0; } return array($major, $minor); } /** * Converts Pounds to KG * * @param float $pounds * @param float $ounces * @return float * @access public */ public static function Pounds2Kg($pounds, $ounces = 0.00) { return round(($pounds + ($ounces / 16)) * self::POUND_TO_KG, 5); } /** * Formats file/memory size in nice way * * @param int $bytes * @return string * @access public */ public static function formatSize($bytes) { if ($bytes >= 1099511627776) { $return = round($bytes / 1024 / 1024 / 1024 / 1024, 2); $suffix = "TB"; } elseif ($bytes >= 1073741824) { $return = round($bytes / 1024 / 1024 / 1024, 2); $suffix = "GB"; } elseif ($bytes >= 1048576) { $return = round($bytes / 1024 / 1024, 2); $suffix = "MB"; } elseif ($bytes >= 1024) { $return = round($bytes / 1024, 2); $suffix = "KB"; } else { $return = $bytes; $suffix = "Byte"; } $return .= ' '.$suffix; return $return; } /** * Enter description here... * * @param resource $filePointer the file resource to write to * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @access public */ public static function fputcsv($filePointer, $data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { fwrite($filePointer, self::getcsvline($data, $delimiter, $enclosure, $recordSeparator)); } /** * Enter description here... * * @param Array $data the data to write out * @param string $delimiter the field separator * @param string $enclosure symbol to enclose field data to * @param string $recordSeparator symbols to separate records with * @return string * @access public */ public static function getcsvline($data, $delimiter = ',', $enclosure = '"', $recordSeparator = "\r\n") { ob_start(); $fp = fopen('php://output', 'w'); fputcsv($fp, $data, $delimiter, $enclosure); fclose($fp); $ret = ob_get_clean(); if ( $recordSeparator != "\n" ) { return substr($ret, 0, -1) . $recordSeparator; } return $ret; } /** * Allows to replace #section# within any string with current section * * @param string $string * @return string * @access public */ public static function replaceModuleSection($string) { $application =& kApplication::Instance(); $module_section = $application->RecallVar('section'); if ($module_section) { // substitute section instead of #section# parameter in title preset name $module_section = explode(':', $module_section); $section = preg_replace('/(configuration|configure)_(.*)/i', '\\2', $module_section[count($module_section) == 2 ? 1 : 0]); $string = str_replace('#section#', mb_strtolower($section), $string); } return $string; } /** * Checks, that user IP address is within allowed range * * @param string $ip_list semi-column (by default) separated ip address list * @param string $separator ip address separator (default ";") * * @return bool * @access public */ public static function ipMatch($ip_list, $separator = ';') { if ( php_sapi_name() == 'cli' ) { return false; } $ip_match = false; $ip_addresses = $ip_list ? explode($separator, $ip_list) : Array (); $application =& kApplication::Instance(); $client_ip = $application->getClientIp(); foreach ($ip_addresses as $ip_address) { if ( self::netMatch($ip_address, $client_ip) ) { $ip_match = true; break; } } return $ip_match; } /** * Checks, that given ip belongs to given subnet * * @param string $network * @param string $ip * @return bool * @access public */ public static function netMatch($network, $ip) { $network = trim($network); $ip = trim($ip); if ( preg_replace('/[\d\.\/-]/', '', $network) != '' ) { $network = gethostbyname($network); } if ($network == $ip) { // comparing two ip addresses directly return true; } $d = strpos($network, '-'); if ($d !== false) { // ip address range specified $from = ip2long(trim(substr($network, 0, $d))); $to = ip2long(trim(substr($network, $d + 1))); $ip = ip2long($ip); return ($ip >= $from && $ip <= $to); } elseif (strpos($network, '/') !== false) { // single subnet specified $ip_arr = explode('/', $network); if (!preg_match("@\d*\.\d*\.\d*\.\d*@", $ip_arr[0], $matches)) { $ip_arr[0] .= '.0'; // Alternate form 194.1.4/24 } $network_long = ip2long($ip_arr[0]); $x = ip2long($ip_arr[1]); $mask = long2ip($x) == $ip_arr[1] ? $x : (0xffffffff << (32 - $ip_arr[1])); $ip_long = ip2long($ip); return ($ip_long & $mask) == ($network_long & $mask); } return false; } /** * Returns mime type corresponding to given file * @param string $file * @return string * @access public */ public static function mimeContentType($file) { $ret = self::vendorMimeContentType($file); if ( $ret ) { // vendor-specific mime types override any automatic detection return $ret; } if ( function_exists('finfo_open') && function_exists('finfo_file') ) { $mime_magic_resource = finfo_open(FILEINFO_MIME_TYPE); if ( $mime_magic_resource ) { $ret = finfo_file($mime_magic_resource, $file); finfo_close($mime_magic_resource); } } elseif ( function_exists('mime_content_type') ) { $ret = mime_content_type($file); } return $ret ? $ret : self::mimeContentTypeByExtension($file); } /** * Determines vendor-specific mime type from a given file * * @param string $file * @return bool * @access public * @static */ public static function vendorMimeContentType($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = Array ( 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12' ); return isset($mapping[$file_extension]) ? $mapping[$file_extension] : false; } /** * Detects mime type of the file purely based on it's extension * * @param string $file * @return string * @access public */ public static function mimeContentTypeByExtension($file) { $file_extension = mb_strtolower(pathinfo(self::removeTempExtension($file), PATHINFO_EXTENSION)); $mapping = '(xls:application/excel)(hqx:application/macbinhex40)(doc,dot,wrd:application/msword)(pdf:application/pdf) (pgp:application/pgp)(ps,eps,ai:application/postscript)(ppt:application/powerpoint)(rtf:application/rtf) (tgz,gtar:application/x-gtar)(gz:application/x-gzip)(php,php3:application/x-httpd-php)(js:application/x-javascript) (ppd,psd:application/x-photoshop)(swf,swc,rf:application/x-shockwave-flash)(tar:application/x-tar)(zip:application/zip) (mid,midi,kar:audio/midi)(mp2,mp3,mpga:audio/mpeg)(ra:audio/x-realaudio)(wav:audio/wav)(bmp:image/bitmap)(bmp:image/bitmap) (gif:image/gif)(iff:image/iff)(jb2:image/jb2)(jpg,jpe,jpeg:image/jpeg)(jpx:image/jpx)(png:image/png)(tif,tiff:image/tiff) (wbmp:image/vnd.wap.wbmp)(xbm:image/xbm)(css:text/css)(txt:text/plain)(htm,html:text/html)(xml:text/xml) (mpg,mpe,mpeg:video/mpeg)(qt,mov:video/quicktime)(avi:video/x-ms-video)(eml:message/rfc822) (sxw:application/vnd.sun.xml.writer)(sxc:application/vnd.sun.xml.calc)(sxi:application/vnd.sun.xml.impress) (sxd:application/vnd.sun.xml.draw)(sxm:application/vnd.sun.xml.math) (odt:application/vnd.oasis.opendocument.text)(oth:application/vnd.oasis.opendocument.text-web) (odm:application/vnd.oasis.opendocument.text-master)(odg:application/vnd.oasis.opendocument.graphics) (odp:application/vnd.oasis.opendocument.presentation)(ods:application/vnd.oasis.opendocument.spreadsheet) (odc:application/vnd.oasis.opendocument.chart)(odf:application/vnd.oasis.opendocument.formula) (odi:application/vnd.oasis.opendocument.image)'; if ( preg_match('/[\(,]' . $file_extension . '[,]{0,1}.*?:(.*?)\)/s', $mapping, $regs) ) { return $regs[1]; } return 'application/octet-stream'; } /** * Strips ".tmp" suffix (added by flash uploader) from a filename * * @param string $file * @return string * @access public * @static */ public static function removeTempExtension($file) { return preg_replace('/(_[\d]+)?\.tmp$/', '', $file); } /** * Return param value and removes it from params array * * @param string $name * @param Array $params * @param bool $default * @return string */ public static function popParam($name, &$params, $default = false) { if ( isset($params[$name]) ) { $value = $params[$name]; unset($params[$name]); return $value; } return $default; } /** * Generate subpath from hashed value * * @param string $name * @param int $levels * @return string */ public static function getHashPathForLevel($name, $levels = 2) { if ( $levels == 0 ) { return ''; } else { $path = ''; $hash = md5($name); for ($i = 0; $i < $levels; $i++) { $path .= substr($hash, $i, 1) . '/'; } return $path; } } /** * Calculates the crc32 polynomial of a string (always positive number) * * @param string $str * @return int */ public static function crc32($str) { return sprintf('%u', crc32($str)); } /** * Returns instance of DateTime class with date set based on timestamp * * @static * @param int $timestamp * @return DateTime * @access public */ public static function dateFromTimestamp($timestamp) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { $date = new DateTime('@' . $timestamp); $date->setTimezone(new DateTimeZone(date_default_timezone_get())); } else { $date = new DateTime(); $date->setTimestamp($timestamp); } return $date; } /** * Returns timestamp from given DateTime class instance * * @static * @param DateTime $date_time * @return int|string * @access public */ public static function timestampFromDate(DateTime $date_time) { if ( version_compare(PHP_VERSION, '5.3.0', '<') ) { return $date_time->format('U'); } return $date_time->getTimestamp(); } /** * Generates random numeric id * * @static * @return string * @access public */ public static function generateId() { list($usec, $sec) = explode(' ', microtime()); $id_part_1 = substr($usec, 4, 4); $id_part_2 = mt_rand(1, 9); $id_part_3 = substr($sec, 6, 4); $digit_one = substr($id_part_1, 0, 1); if ( $digit_one == 0 ) { $digit_one = mt_rand(1, 9); $id_part_1 = preg_replace('/^0/', '', $id_part_1); $id_part_1 = $digit_one . $id_part_1; } return $id_part_1 . $id_part_2 . $id_part_3; } /** * Changes script resource limits. Omitted argument results in limit removal. * * @static * @param string|int $memory_limit * @param int $time_limit * @return void * @access public */ public static function setResourceLimit($memory_limit = null, $time_limit = null) { set_time_limit(isset($time_limit) ? $time_limit : 0); ini_set('memory_limit', isset($memory_limit) ? $memory_limit : -1); } /** * Escapes a string. * * @param string $text Text to escape. * @param string $strategy Escape strategy. * * @return string * @throws InvalidArgumentException When unknown escape strategy is given. */ public static function escape($text, $strategy = null) { if ( !isset($strategy) ) { $strategy = self::$escapeStrategy; } if ( strpos($strategy, '+') !== false ) { $previous_strategy = ''; $strategies = explode('+', $strategy); foreach ($strategies as $current_strategy) { // apply default strategy if ( $current_strategy == '' ) { $current_strategy = self::$escapeStrategy; } // don't double-escape if ( $current_strategy != $previous_strategy ) { $text = self::escape($text, $current_strategy); $previous_strategy = $current_strategy; } } return $text; } if ( $strategy == self::ESCAPE_HTML ) { return htmlspecialchars($text, ENT_QUOTES, CHARSET); } if ( $strategy == self::ESCAPE_JS ) { // TODO: consider using "addcslashes", because "addslashes" isn't really for JavaScript escaping (according to docs) $text = addslashes($text); $text = str_replace(array("\r", "\n"), array('\r', '\n'), $text); $text = str_replace('', "", $text); return $text; } if ( $strategy == self::ESCAPE_URL ) { return rawurlencode($text); } if ( $strategy == self::ESCAPE_RAW ) { return $text; } throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } /** * Unescapes a string. * * @param string $text Text to unescape. * @param string $strategy Escape strategy. * * @return string * @throws InvalidArgumentException When unknown escape strategy is given. */ public static function unescape($text, $strategy = null) { if ( !isset($strategy) ) { $strategy = self::$escapeStrategy; } if ( strpos($strategy, '+') !== false ) { $previous_strategy = ''; $strategies = explode('+', $strategy); foreach ($strategies as $current_strategy) { // apply default strategy if ( $current_strategy == '' ) { $current_strategy = self::$escapeStrategy; } // don't double-unescape if ( $current_strategy != $previous_strategy ) { $text = self::unescape($text, $current_strategy); $previous_strategy = $current_strategy; } } return $text; } if ( $strategy == self::ESCAPE_HTML ) { return htmlspecialchars_decode($text, ENT_QUOTES); } if ( $strategy == self::ESCAPE_JS ) { // TODO: consider using "stripcslashes", because "stripslashes" isn't really for JavaScript unescaping (according to docs) $text = str_replace("", '', $text); $text = str_replace(array('\r', '\n'), array("\r", "\n"), $text); $text = stripslashes($text); return $text; } if ( $strategy == self::ESCAPE_URL ) { return rawurldecode($text); } if ( $strategy == self::ESCAPE_RAW ) { return $text; } throw new InvalidArgumentException(sprintf('Unknown escape strategy "%s"', $strategy)); } } /** * Returns array value if key exists * Accepts infinite number of parameters * * @param Array $array searchable array * @param int $key array key * @return string */ function getArrayValue(&$array, $key) { $ret = isset($array[$key]) ? $array[$key] : false; if ( $ret && func_num_args() > 2 ) { for ($i = 2; $i < func_num_args(); $i++) { $cur_key = func_get_arg($i); $ret = getArrayValue($ret, $cur_key); if ( $ret === false ) { break; } } } return $ret; } if ( !function_exists('parse_ini_string') ) { /** * Equivalent for "parse_ini_string" function available since PHP 5.3.0 * * @param string $ini * @param bool $process_sections * @param int $scanner_mode * @return Array */ function parse_ini_string($ini, $process_sections = false, $scanner_mode = NULL) { # Generate a temporary file. $tempname = tempnam('/tmp', 'ini'); $fp = fopen($tempname, 'w'); fwrite($fp, $ini); $ini = parse_ini_file($tempname, !empty($process_sections)); fclose($fp); @unlink($tempname); return $ini; } } if ( !function_exists('memory_get_usage') ) { // PHP 4.x and compiled without --enable-memory-limit option function memory_get_usage() { return -1; } } if ( !function_exists('imagecreatefrombmp') ) { // just in case if GD will add this function in future function imagecreatefrombmp($filename) { //Ouverture du fichier en mode binaire if (! $f1 = fopen($filename,"rb")) return FALSE; //1 : Chargement des ent�tes FICHIER $FILE = unpack("vfile_type/Vfile_size/Vreserved/Vbitmap_offset", fread($f1,14)); if ($FILE['file_type'] != 19778) return FALSE; //2 : Chargement des ent�tes BMP $BMP = unpack('Vheader_size/Vwidth/Vheight/vplanes/vbits_per_pixel'. '/Vcompression/Vsize_bitmap/Vhoriz_resolution'. '/Vvert_resolution/Vcolors_used/Vcolors_important', fread($f1,40)); $BMP['colors'] = pow(2,$BMP['bits_per_pixel']); if ($BMP['size_bitmap'] == 0) $BMP['size_bitmap'] = $FILE['file_size'] - $FILE['bitmap_offset']; $BMP['bytes_per_pixel'] = $BMP['bits_per_pixel']/8; $BMP['bytes_per_pixel2'] = ceil($BMP['bytes_per_pixel']); $BMP['decal'] = ($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] -= floor($BMP['width']*$BMP['bytes_per_pixel']/4); $BMP['decal'] = 4-(4*$BMP['decal']); if ($BMP['decal'] == 4) $BMP['decal'] = 0; //3 : Chargement des couleurs de la palette $PALETTE = array(); if ($BMP['colors'] < 16777216) { $PALETTE = unpack('V'.$BMP['colors'], fread($f1,$BMP['colors']*4)); } //4 : Cr�ation de l'image $IMG = fread($f1,$BMP['size_bitmap']); $VIDE = chr(0); $res = imagecreatetruecolor($BMP['width'],$BMP['height']); $P = 0; $Y = $BMP['height']-1; while ($Y >= 0) { $X=0; while ($X < $BMP['width']) { if ($BMP['bits_per_pixel'] == 24) $COLOR = unpack("V",substr($IMG,$P,3).$VIDE); elseif ($BMP['bits_per_pixel'] == 16) { $COLOR = unpack("n",substr($IMG,$P,2)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 8) { $COLOR = unpack("n",$VIDE.substr($IMG,$P,1)); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 4) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*2)%2 == 0) $COLOR[1] = ($COLOR[1] >> 4) ; else $COLOR[1] = ($COLOR[1] & 0x0F); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } elseif ($BMP['bits_per_pixel'] == 1) { $COLOR = unpack("n",$VIDE.substr($IMG,floor($P),1)); if (($P*8)%8 == 0) $COLOR[1] = $COLOR[1] >>7; elseif (($P*8)%8 == 1) $COLOR[1] = ($COLOR[1] & 0x40)>>6; elseif (($P*8)%8 == 2) $COLOR[1] = ($COLOR[1] & 0x20)>>5; elseif (($P*8)%8 == 3) $COLOR[1] = ($COLOR[1] & 0x10)>>4; elseif (($P*8)%8 == 4) $COLOR[1] = ($COLOR[1] & 0x8)>>3; elseif (($P*8)%8 == 5) $COLOR[1] = ($COLOR[1] & 0x4)>>2; elseif (($P*8)%8 == 6) $COLOR[1] = ($COLOR[1] & 0x2)>>1; elseif (($P*8)%8 == 7) $COLOR[1] = ($COLOR[1] & 0x1); $COLOR[1] = $PALETTE[$COLOR[1]+1]; } else return FALSE; imagesetpixel($res,$X,$Y,$COLOR[1]); $X++; $P += $BMP['bytes_per_pixel']; } $Y--; $P+=$BMP['decal']; } //Fermeture du fichier fclose($f1); return $res; } } Index: branches/5.2.x/core/units/helpers/minifiers/minify_helper.php =================================================================== --- branches/5.2.x/core/units/helpers/minifiers/minify_helper.php (revision 16434) +++ branches/5.2.x/core/units/helpers/minifiers/minify_helper.php (revision 16435) @@ -1,315 +1,315 @@ debugMode = $this->Application->isDebugMode(false); $this->resourceFolder = WRITEABLE . '/cache'; } /** * When used as non-block tag, then compress given files and return url to result * * @param Array $params * @return string * @access public */ public function CompressScriptTag($params) { // put to queue if ( array_key_exists('to', $params) ) { $files = $this->Application->GetVar($params['to'], ''); $this->Application->SetVar($params['to'], $files . '|' . $params['files']); return ''; } if ( array_key_exists('from', $params) ) { // get from queue $files = $this->Application->GetVar($params['from']); } else { // get from tag $files = $params['files']; } $files = $this->_getTemplatePaths( array_map('trim', explode('|', $files)) ); if ( !$files ) { trigger_error('No files specified for compression.', E_USER_NOTICE); return ''; } $extension = pathinfo($files[0], PATHINFO_EXTENSION); $save_as = isset($params['save_as']) ? $params['save_as'] : false; $dst_file = $this->resourceFolder . DIRECTORY_SEPARATOR . ($this->debugMode ? 'd' : 'c') . '_'; /** @var FileHelper $file_helper */ $file_helper = $this->Application->recallObject('FileHelper'); if ( $save_as ) { $dst_file .= $save_as . ( strpos($save_as, '.') === false ? '.' . $extension : '' ); } else { $dst_file .= $this->_getHash($file_helper->makeRelative($files)) . '.' . $extension; } $was_compressed = file_exists($dst_file); if ( !$was_compressed || ($this->debugMode && filemtime($dst_file) < $this->_getMaxFileDate($files)) ) { $string = ''; $path_length = strlen(FULL_PATH) + 1; foreach ($files as $file) { if ( !file_exists($file) ) { continue; } // add filename before for easier debugging if ( $this->debugMode ) { $string .= '/* === File: ' . substr($file, $path_length) . ' === */' . "\n"; $string .= '/* ' . str_repeat('=', strlen(substr($file, $path_length)) + 14) . ' */' . "\n\n"; } // add file content $string .= file_get_contents($file) . "\n\n"; } // replace templates base if ( isset($params['templates_base']) ) { $templates_base = $params['templates_base']; } else { $templates_base = $this->Application->ProcessParsedTag('m', 'TemplatesBase', Array ()); } $templates_base = preg_replace('/^' . preg_quote($this->Application->BaseURL(), '/') . '/', BASE_PATH . '/', $templates_base); $string = str_replace('@templates_base@', rtrim($templates_base, '/'), $string); if ( !$this->debugMode ) { // don't compress merged js/css file in debug mode to allow js/css debugging $this->compressString($string, $extension); } // save compressed file file_put_contents($dst_file, $string); } return $file_helper->pathToUrl($dst_file) . '?ts=' . adodb_date('Y-m-d_H:i:s', filemtime($dst_file)); } /** * Returns maximal modification date across given files * * @param Array $files * @return int * @access protected */ protected function _getMaxFileDate($files) { $ret = 0; foreach ($files as $file) { if ( file_exists($file) ) { $ret = max($ret, filemtime($file)); } } return $ret; } /** * Returns hash string based on given files * * @param Array $files * @return int * @access protected */ protected function _getHash($files) { $hash = $files; if ($this->Application->isAdmin) { array_unshift($hash, 'A:1'); } else { array_unshift($hash, 'A:0;T:' . $this->Application->GetVar('m_theme')); } return kUtil::crc32(implode('|', $hash)); } /** * Deletes compression info file * * @todo also delete all listed there files * @access public */ public function delete() { $iterator = new DirectoryIterator($this->resourceFolder); /* @var $file_info DirectoryIterator */ foreach ($iterator as $file_info) { if ( !$file_info->isDir() && preg_match('/^(c|d)_.*.(css|js)$/', $file_info->getFilename()) ) { unlink( $file_info->getPathname() ); } } } /** * Compress $string based on $extension * * @param string $string * @param string $extension * @return void * @access protected */ public function compressString(&$string, $extension) { - $vars = kUtil::getConfigVars(); + $compression_engine = kUtil::getSystemConfig()->get('CompressionEngine'); - if ( !array_key_exists('CompressionEngine', $vars) ) { + if ( !$compression_engine ) { // compression method not specified - use none return; } - switch ( $vars['CompressionEngine'] ) { + switch ( $compression_engine ) { case 'yui': $this->compressViaJava($string, $extension); break; case 'php': $this->compressViaPHP($string, $extension); break; } } /** * Compresses string using YUI compressor (uses Java) * * @param string $string * @param string $extension * @return void * @access protected */ protected function compressViaJava(&$string, $extension) { $tmp_file = tempnam('/tmp', 'to_compress_'); file_put_contents($tmp_file, $string); $command = 'java -jar ' . dirname(__FILE__) . DIRECTORY_SEPARATOR . 'yuicompressor-2.4.2.jar --type ' . $extension . ' --charset utf-8 ' . $tmp_file; $string = shell_exec($command); unlink($tmp_file); } /** * Compresses string using PHP compressor * * @param string $string * @param string $extension * @return void * @access protected */ protected function compressViaPHP(&$string, $extension) { $minifier = $this->Application->makeClass($extension == 'js' ? 'JsMinifyHelper' : 'CssMinifyHelper'); /* @var $minifier JsMinifyHelper */ $string = $minifier->minify($string); } /** * Get full paths on disk for each of given templates * * @param Array $templates * @return Array * @access protected */ protected function _getTemplatePaths($templates) { $ret = Array (); $reg_exp = '/^' . preg_quote($this->Application->BaseURL(), '/') . '(.*)/'; foreach ($templates as $template) { if ( !$template ) { continue; } if ( preg_match($reg_exp, $template, $regs) ) { // full url (from current domain) to a file $ret[] = FULL_PATH . '/' . $regs[1]; } elseif ( strpos($template, '{module_path}') !== false ) { $ret = array_merge($ret, $this->_moduleInclude($template)); } else { $ret[] = $this->Application->TemplatesCache->GetRealFilename($template); } } return $ret; } /** * * @param string $template * @return Array * @access protected */ protected function _moduleInclude($template) { $ret = $included = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_data) { if ( $module_name == 'In-Portal' ) { continue; } $module_prefix = $this->Application->isAdmin ? mb_strtolower($module_name) . '/' : $module_data['TemplatePath']; if ( in_array($module_prefix, $included) ) { continue; } $ret[] = $this->Application->TemplatesCache->GetRealFilename(str_replace('{module_path}', $module_prefix, $template)); $included[] = $module_prefix; } return $ret; } } Index: branches/5.2.x/core/units/helpers/modules_helper.php =================================================================== --- branches/5.2.x/core/units/helpers/modules_helper.php (revision 16434) +++ branches/5.2.x/core/units/helpers/modules_helper.php (revision 16435) @@ -1,496 +1,497 @@ Application->isAdmin) { // no license checks on front-end return implode(' AND ', $where_clause); } $modules = $this->_GetModules(); if ($modules) { foreach ($modules as $module_index => $module) { $modules[$module_index] = $this->Conn->qstr($module); } $where_clause[] = 'Name IN ('.implode(',', $modules).')'; } return implode(' AND ', $where_clause); } function _EnableCookieSID() { $session = $this->Application->recallObject('Session'); /* @var $session Session */ return $session->CookiesEnabled; } function _IsSpider($UserAgent) { global $robots; $lines = file(FULL_PATH.'/robots_list.txt'); if (!is_array($robots)) { $robots = Array(); for($i = 0; $i < count($lines); $i++) { $l = $lines[$i]; $p = explode("\t", $l, 3); $robots[] = $p[2]; } } return in_array($UserAgent, $robots); } function _MatchIp($ip1, $ip2) { $matched = TRUE; $ip = explode('.', $ip1); $MatchIp = explode('.', $ip2); for ($i = 0; $i < count($ip); $i++) { if($i == count($MatchIp)) break; if (trim($ip[$i]) != trim($MatchIp[$i]) || trim($ip[$i]) == '*') { $matched = FALSE; break; } } return $matched; } function _IpAccess($IpAddress, $AllowList, $DenyList) { $allowed = explode(',', $AllowList); $denied = explode(',', $DenyList); $MatchAllowed = FALSE; for ($x = 0; $x < count($allowed); $x++) { $ip = explode('.', $allowed[$x]); $MatchAllowed = $this->_MatchIp($IpAddress, $allowed[$x]); if ($MatchAllowed) break; } $MatchDenied = FALSE; for ($x = 0; $x < count($denied); $x++) { $ip = explode('.', $denied[$x]); $MatchDenied = $this->_MatchIp($IpAddress, $denied[$x]); if ($MatchDenied) break; } $Result = (($MatchAllowed && !$MatchDenied) || (!$MatchAllowed && !$MatchDenied) || ($MatchAllowed && $MatchDenied)); return $Result; } /** * Leaves only domain part from hostname (e.g. extract "intechnic.lv" from "test.intechnic.lv") * Used for admin login license check * * @param string $d * @return string */ function _StripDomainHost($d) { $IsIp = false; $dotcount = substr_count($d, '.'); if ($dotcount == 3) { $IsIp = true; for ($x = 0; $x < strlen($d); $x++) { if (!is_numeric(substr($d, $x, 1)) && substr($d, $x, 1) != '.') { $IsIp = false; break; } } } if ($dotcount > 1 && !$IsIp) { $p = explode('.', $d); $ret = $p[count($p) - 2].'.'.$p[count($p) - 1]; } else { $ret = $d; } return $ret; } /** * When logging into admin then check only last 2 parts of host name VS domain in license * * @param string $user_domain * @param string $license_domain * @return int */ function _CheckDomain($user_domain, $license_domain) { if ($this->Application->isAdmin) { $user_domain = $this->_StripDomainHost($user_domain); return preg_match('/(.*)'.preg_quote($user_domain, '/').'$/', $license_domain); } else { return preg_match('/(.*)'.preg_quote($license_domain, '/').'$/', $user_domain); } } /** * Returns modules list, that are in license * * @return Array */ function _GetModules() { static $modules = null; if (isset($modules)) { return $modules; } $modules = Array(); - $vars = kUtil::getConfigVars(); - $license = array_key_exists('License', $vars) ? base64_decode($vars['License']) : false; + $system_config = kUtil::getSystemConfig(); + $license = $system_config->get('License') ? base64_decode($system_config->get('License')) : false; + if ($license) { list ( , , $i_Keys) = $this->_ParseLicense($license); - $domain = $this->_GetDomain($vars); + $domain = $this->_GetDomain($system_config); if (!$this->_IsLocalSite($domain)) { for ($x = 0; $x < count($i_Keys); $x++) { $key = $i_Keys[$x]; if ($this->_CheckDomain($domain, $key['domain'])) { // used hostname is subdomain or matches domain from license $modules = explode(',', $key['mod']); } } } else { // all already installed modules are licensed for localhost $modules = array_keys($this->Application->ModuleInfo); } } // all modules starting from "in-" doesn't require license $base_modules = Array ('Core', 'In-Portal', 'Custom'); - $modules = array_merge($modules, $base_modules, $this->_getFreeModules($vars)); + $modules = array_merge($modules, $base_modules, $this->_getFreeModules($system_config)); $modules = array_unique( array_map('strtolower', $modules) ); return $modules; } /** * Get all modules, that don't require licensing * - * @param Array $vars + * @param kSystemConfig $system_config * @return Array * @access protected */ - protected function _getFreeModules($vars) + protected function _getFreeModules(kSystemConfig $system_config) { return array_map('strtolower', $this->getModules()); } /** * Allows to determine if module is licensed * * @param string $name * @return bool */ function _ModuleLicensed($name) { $modules = $this->_GetModules(); return in_array($name, $modules); } /** * Returns domain from licences (and direct in case of install script) * - * @param Array $vars + * @param kSystemConfig $system_config * @return string */ - function _GetDomain($vars) + function _GetDomain(kSystemConfig $system_config) { - return isset($vars['Domain']) ? $vars['Domain'] : SERVER_NAME; + return $system_config->get('Domain', SERVER_NAME); } function _keyED($txt, $encrypt_key) { $encrypt_key = md5($encrypt_key); $ctr = 0; $tmp = ''; for ($i = 0; $i < strlen($txt); $i++) { if ($ctr == strlen($encrypt_key)) $ctr = 0; $tmp .= substr($txt, $i, 1) ^ substr($encrypt_key, $ctr, 1); $ctr++; } return $tmp; } function _decrypt($txt, $key) { $txt = $this->_keyED($txt,$key); $tmp = ''; for ($i = 0; $i < strlen($txt); $i++) { $md5 = substr($txt, $i, 1); $i++; $tmp .= (substr($txt, $i, 1) ^ $md5); } return $tmp; } function LoadFromRemote() { return ''; } function DLid() { die($GLOBALS['lid']."\n"); } function _LoadLicense($LoadRemote = false) { $f = FULL_PATH.'/intechnic.php'; if ($this->_falseIsLocalSite($f)) $ret = true; if (file_exists($f)) { $contents = file($f); $data = base64_decode($contents[1]); } else { if ($LoadRemote) return $LoadFromRemote; } return $data; } function _VerifyKey($domain, $k) { $key = md5($domain); $lkey = substr($key, 0, strlen($key) / 2); $rkey = substr($key, strlen($key) / 2); $r = $rkey.$lkey; if ($k == $r) return true; return false; } function _ParseLicense($txt) { // global $i_User, $i_Pswd, $i_Keys; if (!$this->_falseIsLocalSite($txt)) { $nah = false; } $data = $this->_decrypt($txt, 'beagle'); $i_User = $i_Pswd = ''; $i_Keys = Array(); $lines = explode("\n", $data); for ($x = 0; $x < count($lines); $x++) { $l = $lines[$x]; $p = explode('=', $l, 2); switch($p[0]) { case 'Username': $i_User = $p[1]; break; case 'UserPass': $i_Pswd = $p[1]; break; default: if (substr($p[0], 0, 3) == 'key') { $parts = explode('|', $p[1]); if ($this->_VerifyKey($parts[0], $parts[1])) { unset($K); $k['domain'] = $parts[0]; $k['key'] = $parts[1]; $k['desc'] = $parts[2]; $k['mod'] = $parts[3]; $i_Keys[] = $k; } } break; } } return Array ($i_User, $i_Pswd, $i_Keys); } function _GetObscureValue($i) { if ($i == 'x') return 0254; $z = ''; if ($i == 'z') return 0x7F.'.'; if ($i == 'c') return '--code--'; if ($i >= 5 && $i < 7) return $this->_GetObscureValue($z)*$this->_GetObscureValue('e'); if ($i > 30) return Array(0x6c,0x6f,0x63,0x61,0x6c,0x68,0x6f,0x73,0x74); if ($i > 20) return 99; if ($i > 10) return '.'.($this->_GetObscureValue(6.5)+1); if ($i == 'a') return 0xa; return 0; } function _Chr($val) { $x = $this->_GetObscureValue(25); $f = chr($x).chr($x+5).chr($x+15); return $f($val); } function _IsLocalSite($domain) { $ee = $this->_GetObscureValue(35); $yy = ''; foreach ($ee as $e) $yy .= $this->_Chr($e); $localb = FALSE; if(substr($domain,0,3)==$this->_GetObscureValue('x')) { $b = substr($domain,0,6); $p = explode(".",$domain); $subnet = $p[1]; if($p[1]>15 && $p[1]<32) $localb=TRUE; } $zz = $this->_GetObscureValue('z').$this->_GetObscureValue(5).'.'.(int)$this->_GetObscureValue(7).$this->_GetObscureValue(12); $ff = $this->_GetObscureValue('z')+65; $hh = $ff-0x18; if($domain==$yy || $domain==$zz || substr($domain,0,7)==$ff.$this->_Chr(46).$hh || substr($domain,0,3)==$this->_GetObscureValue('a').$this->_Chr(46) || $localb || strpos($domain,".")==0) { return TRUE; } return FALSE; } function _falseIsLocalSite($domain) { $localb = FALSE; if(substr($domain,0,3)=="172") { $b = substr($domain,0,6); $p = explode(".",$domain); $subnet = $p[1]; if($p[1]>15 && $p[1]<32) $localb=TRUE; } if($domain=="localhost" || $domain=="127.0.0.1" || substr($domain,0,7)=="192.168" || substr($domain,0,3)=="10." || $localb || strpos($domain,".")==0) { return TRUE; } return FALSE; } function verifyLicense($license_hash) { $license_hash = base64_decode($license_hash); list ($license_user, $license_password, ) = $this->_ParseLicense($license_hash); return strlen($license_user) && strlen($license_password); } function moduleInstalled($module_name) { static $modules = null; if ( is_null($modules) ) { $sql = 'SELECT LOWER(Name) FROM ' . $this->Application->getUnitOption('mod', 'TableName'); $modules = $this->Conn->GetCol($sql); } if ( $module_name == 'kernel' ) { $module_name = 'in-portal'; } return in_array(strtolower($module_name), $modules); } /** * Returns list of matching modules * * @param int $module_type * @return Array * @access public */ public function getModules($module_type = self::ANY) { $modules = Array (); try { $iterator = new DirectoryIterator(MODULES_PATH); /* @var $file_info DirectoryIterator */ } catch (UnexpectedValueException $e) { return $modules; } foreach ($iterator as $file_info) { $file_path = $file_info->getPathname(); if ( $file_info->isDir() && !$file_info->isDot() && $this->isInPortalModule($file_path) ) { $install_order = trim( file_get_contents($file_path . '/install/install_order.txt') ); $modules[$install_order] = $file_info->getFilename(); } } // allows to control module install order ksort($modules, SORT_NUMERIC); if ( $module_type == self::ANY ) { return $modules; } foreach ($modules as $install_order => $module_name) { $installed = $this->moduleInstalled($module_name); if ( ($module_type == self::INSTALLED && !$installed) || ($module_type == self::NOT_INSTALLED && $installed) ) { unset($modules[$install_order]); } } return $modules; } /** * Checks, that given folder is In-Portal module's root folder * * @param string $folder_path * @return bool * @access public */ public static function isInPortalModule($folder_path) { return file_exists($folder_path . '/install.php') && file_exists($folder_path . '/install/install_schema.sql'); } } Index: branches/5.2.x/core/install/step_templates/select_license.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/select_license.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/select_license.tpl (revision 16435) @@ -1,42 +1,42 @@ toolkit->getSystemConfig('Intechnic', 'License'); + $license_found = $this->toolkit->systemConfig->get('License', 'Intechnic'); $license_source = $this->GetVar('license_source'); if ( ($license_source === false) && $license_found ) { $license_source = 3; } if ( !$license_found && (!$license_source || $license_source == 3) ) { // when disabled option is selected -> select 1st available - open source $license_source = 4; } ?> > > type="radio" name="license_source" id="license_source_3" value="3"> Index: branches/5.2.x/core/install/step_templates/db_reconfig.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/db_reconfig.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/db_reconfig.tpl (revision 16435) @@ -1,67 +1,67 @@ Database Server Type*: Database Hostname *: - + Database Name *: - + Database User Name *: - + Database User Password: - + Database Collation *: Prefix for Table Names: - + \ No newline at end of file Index: branches/5.2.x/core/install/step_templates/sys_config.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/sys_config.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/sys_config.tpl (revision 16435) @@ -1,77 +1,77 @@ Array ('type' => 'text', 'title' => 'Web Path to Installation', 'section' => 'Misc', 'required' => 1, 'default' => ''), - 'WriteablePath' => Array ('type' => 'text', 'title' => 'Path to Writable folder', 'section' => 'Misc', 'required' => 1, 'default' => '/system'), - 'RestrictedPath' => Array ('type' => 'text', 'title' => 'Path to Restricted folder', 'section' => 'Misc', 'required' => 1, 'default' => '/system/.restricted'), - 'AdminDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin folder', 'section' => 'Misc', 'default' => '/admin'), - 'AdminPresetsDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin Interface Presets folder', 'section' => 'Misc', 'default' => '/admin'), - 'ApplicationClass' => Array ('type' => 'text', 'title' => 'Name of Base Application Class', 'section' => 'Misc', 'default' => 'kApplication'), - 'ApplicationPath' => Array ('type' => 'text', 'title' => 'Path to Base Application Class file', 'section' => 'Misc', 'default' => '/core/kernel/application.php'), - 'CacheHandler' => Array ('type' => 'select', 'title' => 'Output Caching Engine', 'section' => 'Misc', 'default' => 'Fake'), - 'MemcacheServers' => Array ('type' => 'text', 'title' => 'Location of Memcache Servers', 'section' => 'Misc', 'default' => 'localhost:11211'), - 'CompressionEngine' => Array ('type' => 'select', 'title' => 'CSS/JS Compression Engine', 'section' => 'Misc', 'default' => ''), - 'WebsiteCharset' => Array ('type' => 'text', 'title' => 'Website Charset', 'section' => 'Misc', 'required' => 1, 'default' => 'utf-8'), - 'EnableSystemLog' => Array ('type' => 'radio', 'title' => 'Enable "System Log"', 'section' => 'Misc', 'required' => 1, 'default' => '0'), - 'SystemLogMaxLevel' => Array ('type' => 'select', 'title' => 'Highest "Log Level", that will be saved in "System Log"', 'section' => 'Misc', 'required' => 1, 'default' => '5'), - 'TrustProxy' => Array ('type' => 'radio', 'title' => 'Trust Proxy', 'section' => 'Misc', 'required' => 1, 'default' => '0'), + 'WebsitePath' => Array ('type' => 'text', 'title' => 'Web Path to Installation', 'section' => 'Misc', 'required' => 1), + 'WriteablePath' => Array ('type' => 'text', 'title' => 'Path to Writable folder', 'section' => 'Misc', 'required' => 1), + 'RestrictedPath' => Array ('type' => 'text', 'title' => 'Path to Restricted folder', 'section' => 'Misc', 'required' => 1), + 'AdminDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin folder', 'section' => 'Misc'), + 'AdminPresetsDirectory' => Array ('type' => 'text', 'title' => 'Path to Admin Interface Presets folder', 'section' => 'Misc'), + 'ApplicationClass' => Array ('type' => 'text', 'title' => 'Name of Base Application Class', 'section' => 'Misc'), + 'ApplicationPath' => Array ('type' => 'text', 'title' => 'Path to Base Application Class file', 'section' => 'Misc'), + 'CacheHandler' => Array ('type' => 'select', 'title' => 'Output Caching Engine', 'section' => 'Misc'), + 'MemcacheServers' => Array ('type' => 'text', 'title' => 'Location of Memcache Servers', 'section' => 'Misc'), + 'CompressionEngine' => Array ('type' => 'select', 'title' => 'CSS/JS Compression Engine', 'section' => 'Misc'), + 'WebsiteCharset' => Array ('type' => 'text', 'title' => 'Website Charset', 'section' => 'Misc', 'required' => 1), + 'EnableSystemLog' => Array ('type' => 'radio', 'title' => 'Enable "System Log"', 'section' => 'Misc', 'required' => 1), + 'SystemLogMaxLevel' => Array ('type' => 'select', 'title' => 'Highest "Log Level", that will be saved in "System Log"', 'section' => 'Misc', 'required' => 1), + 'TrustProxy' => Array ('type' => 'radio', 'title' => 'Trust Proxy', 'section' => 'Misc', 'required' => 1), ); $settings['CacheHandler']['options'] = $this->toolkit->getWorkingCacheHandlers(); $settings['CompressionEngine']['options'] = $this->toolkit->getWorkingCompressionEngines(); $settings['EnableSystemLog']['options'] = Array (1 => 'Enabled', 2 => 'User-only', 0 => 'Disabled'); $settings['SystemLogMaxLevel']['options'] = Array ( 0 => 'emergency', 1 => 'alert', 2 => 'critical', 3 => 'error', 4 => 'warning', 5 => 'notice', 6 => 'info', 7 => 'debug' ); $settings['TrustProxy']['options'] = Array (1 => 'Yes', 0 => 'No'); $row_class = 'table-color2'; foreach ($settings as $config_var => $output_params) { $row_class = $row_class == 'table-color1' ? 'table-color2' : 'table-color1'; ?> *' : ''); ?>: toolkit->getSystemConfig($output_params['section'], $config_var, $output_params['default']); + $config_value = $this->toolkit->systemConfig->get($config_var, $output_params['section'], ''); switch ( $output_params['type'] ) { case 'text': echo ''; break; case 'select': echo ''; break; case 'radio': foreach($output_params['options'] as $option_key => $option_value) { $selected = $option_key == $config_value ? ' checked' : ''; echo '' . $option_value; } break; } ?> Index: branches/5.2.x/core/install/step_templates/security.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/security.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/security.tpl (revision 16435) @@ -1,109 +1,109 @@ %1$s '; $error_tpl = ' %s %s '; $output = ''; $write_check = true; - $check_paths = Array ('/', '/index.php', $this->toolkit->defaultWritablePath . '/config.php', ADMIN_DIRECTORY . '/index.php'); + $check_paths = Array ('/', '/index.php', $this->toolkit->systemConfig->get('WriteablePath', 'Misc') . '/config.php', ADMIN_DIRECTORY . '/index.php'); foreach ($check_paths as $check_path) { $path_secure = true; $path_check_status = $this->toolkit->checkWritePermissions(FULL_PATH . $check_path); if (is_bool($path_check_status) && $path_check_status) { $write_check = $path_secure = false; } $status_text = $path_secure ? '[Secure]' : '[Vulnerable]'; $output .= sprintf($error_tpl, $check_path . (!$path_secure? ' (755 required)' : ''), $status_text); } $skip_check = $write_check ? '' : ''; $output = sprintf($heading_tpl, 'Write Permissions Check' . $skip_check, 'text') . $output; if (!$write_check) { $output .= ' For security reasons it\'s REQUIRED to set 755 permissions on the above files to prevent from attacks on your website!

'; } // script execute check if (file_exists(WRITEABLE . '/install_check.php')) { unlink(WRITEABLE . '/install_check.php'); } $fp = fopen(WRITEABLE . '/install_check.php', 'w'); fwrite($fp, "Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $result = $curl_helper->Send($this->Application->BaseURL(WRITEBALE_BASE) . 'install_check.php'); unlink(WRITEABLE . '/install_check.php'); $execute_check = ($result !== 'OK'); $output .= sprintf($heading_tpl, 'Ability to Execute PHP in Writable Folders', 'text'); $status_text = $execute_check ? '[Secure]' : '[Vulnerable]'; $output .= sprintf($error_tpl, 'Result of creating and executing PHP file(s) in "/system" (or "/system/images") folder', $status_text); if (!$execute_check) { $output .= ' For security reasons it\'s highly recommended disable the access (execution) to PHP files within "/system" folder and it\'s subfolders. You can do this by:


Note that "AllowOverride LIMIT" option should be enabled by your hosting provider. '; } $output .= sprintf($heading_tpl, 'Webserver PHP Configuration', 'text'); $directive_check = true; $ini_vars = Array ('register_globals' => false, 'open_basedir' => true, 'allow_url_fopen' => false); foreach ($ini_vars as $var_name => $var_value) { $current_value = ini_get($var_name); if (!is_numeric($current_value)) { $formatted_value = $current_value ? 'On' : 'Off'; } else { $formatted_value = "'" . $current_value . "'"; } if (($var_value && !$current_value) || (!$var_value && $current_value)) { $directive_check = false; $message_text = 'set to ' . $formatted_value . ''; $status_text = '[Vulnerable]'; } else { $message_text = 'set to ' . $formatted_value . ''; $status_text = '[Secure]'; } $output .= sprintf($error_tpl, 'Directive: ' . $var_name . ' ' . $message_text, $status_text); } /*if (!$directive_check) { // show additional warning about directives }*/ echo $output; ?> \ No newline at end of file Index: branches/5.2.x/core/install/step_templates/db_config.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/db_config.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/db_config.tpl (revision 16435) @@ -1,77 +1,77 @@ Database Server Type*: Database Hostname *: - + Database Name *: - + Database User Name *: - + Database User Password: - + Database Collation *: Prefix for Table Names: - + GetVar('preset') != 'already_installed') { ?> Use existing In-Portal installation in this Database: \ No newline at end of file Index: branches/5.2.x/core/install/step_templates/check_paths.tpl =================================================================== --- branches/5.2.x/core/install/step_templates/check_paths.tpl (revision 16434) +++ branches/5.2.x/core/install/step_templates/check_paths.tpl (revision 16435) @@ -1,26 +1,26 @@ %s %s toolkit->getSystemConfig('Misc', 'WriteablePath'); + $writeable_base = $this->toolkit->systemConfig->get('WriteablePath', 'Misc'); foreach ($this->writeableFolders as $folder_path) { $file_path = str_replace('$1', $writeable_base, $folder_path); if (file_exists(FULL_PATH . $file_path)) { if (!is_writable(FULL_PATH . $file_path)) { $folder_status = '[FAILED]'; $error_message = ' (777 required)'; } else { $folder_status = '[OK]'; $error_message = ''; } echo sprintf($folder_tpl, $file_path . $error_message, $folder_status); } } ?> \ No newline at end of file Index: branches/5.2.x/core/install/install_toolkit.php =================================================================== --- branches/5.2.x/core/install/install_toolkit.php (revision 16434) +++ branches/5.2.x/core/install/install_toolkit.php (revision 16435) @@ -1,1188 +1,1026 @@ defaultWritablePath = DIRECTORY_SEPARATOR . 'system'; + $this->systemConfig = new kSystemConfig(true, false); if ( class_exists('kApplication') ) { // auto-setup in case of separate module install $this->Application =& kApplication::Instance(); $this->Application->Init(); // needed for standalone module install $this->Conn =& $this->Application->GetADODBConnection(); } - - $this->INIFile = FULL_PATH . $this->defaultWritablePath . DIRECTORY_SEPARATOR . 'config.php'; - - $this->systemConfig = $this->ParseConfig(true); } /** * Sets installator * * @param kInstallator $instance */ function setInstallator(&$instance) { $this->_installator =& $instance; } /** * Checks prerequisities before module install or upgrade * * @param string $module_path * @param string $versions * @param string $mode upgrade mode = {install, standalone, upgrade} * @return bool */ function CheckPrerequisites($module_path, $versions, $mode) { if ( !$versions ) { return Array (); } $prerequisite_object =& $this->getPrerequisiteObject($module_path); /* @var $prerequisite_object InPortalPrerequisites */ // some errors possible return is_object($prerequisite_object) ? $prerequisite_object->CheckPrerequisites($versions, $mode) : Array (); } /** * Call prerequisites method * * @param string $module_path * @param string $method * @return array */ function CallPrerequisitesMethod($module_path, $method) { $prerequisite_object =& $this->getPrerequisiteObject($module_path); /* @var $prerequisite_object InPortalPrerequisites */ return is_object($prerequisite_object) ? $prerequisite_object->$method() : false; } /** * Returns prerequisite object to be used for checks * * @param string $module_path * @return kHelper * @access protected */ protected function &getPrerequisiteObject($module_path) { static $prerequisite_classes = Array (); $prerequisites_file = sprintf(PREREQUISITE_FILE, $module_path); if ( !file_exists($prerequisites_file) ) { $false = false; return $false; } if ( !isset($prerequisite_classes[$module_path]) ) { // save class name, because 2nd time // (in after call $prerequisite_class variable will not be present) include_once $prerequisites_file; $prerequisite_classes[$module_path] = $prerequisite_class; } $prerequisite_object = new $prerequisite_classes[$module_path](); /* @var $prerequisite_object InPortalPrerequisites */ if ( method_exists($prerequisite_object, 'setToolkit') ) { $prerequisite_object->setToolkit($this); } return $prerequisite_object; } /** * Processes one license, received from server * * @param string $file_data */ function processLicense($file_data) { $modules_helper = $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ $file_data = explode('Code==:', $file_data); $file_data[0] = str_replace('In-Portal License File - do not edit!' . "\n", '', $file_data[0]); $file_data = array_map('trim', $file_data); if ($modules_helper->verifyLicense($file_data[0])) { - $this->setSystemConfig('Intechnic', 'License', $file_data[0]); + $this->systemConfig->set('License', 'Intechnic', $file_data[0]); if (array_key_exists(1, $file_data)) { - $this->setSystemConfig('Intechnic', 'LicenseCode', $file_data[1]); + $this->systemConfig->set('LicenseCode', 'Intechnic', $file_data[1]); } else { - $this->setSystemConfig('Intechnic', 'LicenseCode'); + $this->systemConfig->set('LicenseCode', 'Intechnic'); } - $this->SaveConfig(); + $this->systemConfig->save(); } else { // invalid license received from licensing server $this->_installator->errorMessage = 'Invalid License File'; } } /** * Saves given configuration values to database * * @param Array $config */ function saveConfigValues($config) { foreach ($config as $config_var => $value) { $sql = 'UPDATE ' . TABLE_PREFIX . 'SystemSettings SET VariableValue = ' . $this->Conn->qstr($value) . ' WHERE VariableName = ' . $this->Conn->qstr($config_var); $this->Conn->Query($sql); } } /** * Sets module version to passed * * @param string $module_name * @param string|bool $module_path * @param string|bool $version */ function SetModuleVersion($module_name, $module_path = false, $version = false) { if ($version === false) { if (!$module_path) { throw new Exception('Module path must be given to "SetModuleVersion" method to auto-detect version'); return ; } $version = $this->GetMaxModuleVersion($module_path); } // get table prefix from config, because application may not be available here - $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); + $table_prefix = $this->systemConfig->get('TablePrefix', 'Database'); if ($module_name == 'kernel') { $module_name = 'in-portal'; } // don't use "adodb_mktime" here, because it's not yet included $sql = 'UPDATE ' . $table_prefix . 'Modules SET Version = "' . $version . '", BuildDate = ' . time() . ' WHERE LOWER(Name) = "' . strtolower($module_name) . '"'; $this->Conn->Query($sql); } /** * Sets module root category to passed * * @param string $module_name * @param int $category_id */ function SetModuleRootCategory($module_name, $category_id = 0) { // get table prefix from config, because application may not be available here - $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); + $table_prefix = $this->systemConfig->get('TablePrefix', 'Database'); if ($module_name == 'kernel') { $module_name = 'in-portal'; } $sql = 'UPDATE ' . $table_prefix . 'Modules SET RootCat = ' . $category_id . ' WHERE LOWER(Name) = "' . strtolower($module_name) . '"'; $this->Conn->Query($sql); } /** * Returns maximal version of given module by scanning it's upgrade scripts * * @param string $module_path * @return string */ function GetMaxModuleVersion($module_path) { $module_path = rtrim(mb_strtolower($module_path), '/'); $upgrades_file = sprintf(UPGRADES_FILE, $module_path . '/', 'sql'); if (!file_exists($upgrades_file)) { // no upgrade file return '5.0.0'; } $sqls = file_get_contents($upgrades_file); $versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); if (!$versions_found) { // upgrades file doesn't contain version definitions return '5.0.0'; } return end($regs[1]); } /** * Runs SQLs from file * * @param string $filename * @param mixed $replace_from * @param mixed $replace_to */ function RunSQL($filename, $replace_from = null, $replace_to = null) { if (!file_exists(FULL_PATH.$filename)) { return ; } $sqls = file_get_contents(FULL_PATH.$filename); if (!$this->RunSQLText($sqls, $replace_from, $replace_to)) { if (is_object($this->_installator)) { $this->_installator->Done(); } else { if (isset($this->Application)) { $this->Application->Done(); } exit; } } } /** * Runs SQLs from string * * @param string $sqls * @param mixed $replace_from * @param mixed $replace_to * @param int $start_from * @return bool */ function RunSQLText(&$sqls, $replace_from = null, $replace_to = null, $start_from = 0) { - $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); + $table_prefix = $this->systemConfig->get('TablePrefix', 'Database'); // add prefix to all tables if (strlen($table_prefix) > 0) { $replacements = Array ('INSERT INTO ', 'UPDATE ', 'ALTER TABLE ', 'DELETE FROM ', 'REPLACE INTO '); + foreach ($replacements as $replacement) { $sqls = str_replace($replacement, $replacement . $table_prefix, $sqls); } } $sqls = str_replace('CREATE TABLE ', 'CREATE TABLE IF NOT EXISTS ' . $table_prefix, $sqls); $sqls = str_replace('DROP TABLE ', 'DROP TABLE IF EXISTS ' . $table_prefix, $sqls); $sqls = str_replace('<%TABLE_PREFIX%>', $table_prefix, $sqls); $primary_language = is_object($this->Application) ? $this->Application->GetDefaultLanguageId() : 1; $sqls = str_replace('<%PRIMARY_LANGUAGE%>', $primary_language, $sqls); if (isset($replace_from) && isset($replace_to)) { // replace something additionally, e.g. module root category $sqls = str_replace($replace_from, $replace_to, $sqls); } $sqls = str_replace("\r\n", "\n", $sqls); // convert to linux line endings $no_comment_sqls = preg_replace("/#\s([^;]*?)\n/is", '', $sqls); // remove all comments "#" on new lines if ($no_comment_sqls === null) { // "ini.pcre.backtrack-limit" reached and error happened $sqls = explode(";\n", $sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); // remove all comments "#" on new lines (takes about 2 seconds for 53000 sqls) $sqls = preg_replace("/#\s([^;]*?)/", '', $sqls); } else { $sqls = explode(";\n", $no_comment_sqls . "\n"); // ensures that last sql won't have ";" in it $sqls = array_map('trim', $sqls); } $sql_count = count($sqls); - $db_collation = $this->getSystemConfig('Database', 'DBCollation'); + $db_collation = $this->systemConfig->get('DBCollation', 'Database'); for ($i = $start_from; $i < $sql_count; $i++) { $sql = $sqls[$i]; if (!$sql || (substr($sql, 0, 1) == '#')) { continue; // usually last line } if (substr($sql, 0, 13) == 'CREATE TABLE ' && $db_collation) { // it is CREATE TABLE statement -> add collation $sql .= ' COLLATE \'' . $db_collation . '\''; } $this->Conn->Query($sql); if ($this->Conn->getErrorCode() != 0) { if (is_object($this->_installator)) { $this->_installator->errorMessage = 'Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg().'

Last Database Query:
'; $this->_installator->LastQueryNum = $i + 1; } return false; } } return true; } /** * Performs clean language import from given xml file * * @param string $lang_file * @param bool $upgrade * @todo Import for "core/install/english.lang" (322KB) takes 18 seconds to work on Windows */ function ImportLanguage($lang_file, $upgrade = false) { $lang_file = FULL_PATH.$lang_file.'.lang'; if (!file_exists($lang_file)) { return ; } $language_import_helper = $this->Application->recallObject('LanguageImportHelper'); /* @var $language_import_helper LanguageImportHelper */ $language_import_helper->performImport($lang_file, '|0|1|2|', '', $upgrade ? LANG_SKIP_EXISTING : LANG_OVERWRITE_EXISTING); } /** * Converts module version in format X.Y.Z[-BN/-RCM] to signle integer * * @param string $version * @return int */ function ConvertModuleVersion($version) { if (preg_match('/(.*)-(B|RC)([\d]+)/', $version, $regs)) { // -B or RC- $parts = explode('.', $regs[1]); $parts[] = $regs[2] == 'B' ? 1 : 2; // B reliases goes before RC releases $parts[] = $regs[3]; } else { // releases without B/RC marks go after any B/RC releases $parts = explode('.', $version . '.3.100'); } $bin = ''; foreach ($parts as $part_index => $part) { if ($part_index == 3) { // version type only can be 1/2/3 (11 in binary form), so don't use padding at all $pad_count = 2; } else { $pad_count = 8; } $bin .= str_pad(decbin($part), $pad_count, '0', STR_PAD_LEFT); } return bindec($bin); } /** * Returns themes, found in system * * @param bool $rebuild * @return int */ function getThemes($rebuild = false) { if ($rebuild) { $this->rebuildThemes(); } $id_field = $this->Application->getUnitOption('theme', 'IDField'); $table_name = $this->Application->getUnitOption('theme', 'TableName'); $sql = 'SELECT Name, ' . $id_field . ' FROM ' . $table_name . ' ORDER BY Name ASC'; return $this->Conn->GetCol($sql, $id_field); } - function ParseConfig($parse_section = false) - { - if (!file_exists($this->INIFile)) { - return Array (); - } - - if (file_exists($this->INIFile) && !is_readable($this->INIFile)) { - die('Could Not Open Ini File'); - } - - $contents = file($this->INIFile); - - if ($contents && $contents[0] == '<' . '?' . 'php die() ?' . ">\n") { - // format of "config.php" file before 5.1.0 version - array_shift($contents); - - return $this->parseIniString(implode('', $contents), $parse_section); - } - - $_CONFIG = Array (); - require($this->INIFile); - - if ($parse_section) { - return $_CONFIG; - } - - $ret = Array (); - - foreach ($_CONFIG as $section => $section_variables) { - $ret = array_merge($ret, $section_variables); - } - - return $ret; - } - - /** - * Equivalent for "parse_ini_string" function available since PHP 5.3.0 - * - * @param string $ini - * @param bool $process_sections - * @param int $scanner_mode - * @return Array - */ - function parseIniString($ini, $process_sections = false, $scanner_mode = null) - { - # Generate a temporary file. - $tempname = tempnam('/tmp', 'ini'); - $fp = fopen($tempname, 'w'); - fwrite($fp, $ini); - $ini = parse_ini_file($tempname, !empty($process_sections)); - fclose($fp); - @unlink($tempname); - - return $ini; - } - - function SaveConfig($silent = false) - { - if (!is_writable($this->INIFile) && !is_writable(dirname($this->INIFile))) { - $error_msg = 'Cannot write to "' . $this->INIFile . '" file'; - - if ($silent) { - trigger_error($error_msg, E_USER_WARNING); - } - else { - throw new Exception($error_msg); - } - - return ; - } - - $fp = fopen($this->INIFile, 'w'); - fwrite($fp, '<' . '?' . 'php' . "\n\n"); - - foreach ($this->systemConfig as $section_name => $section_data) { - foreach ($section_data as $key => $value) { - fwrite($fp, '$_CONFIG[\'' . $section_name . '\'][\'' . $key . '\'] = \'' . addslashes($value) . '\';' . "\n"); - } - - fwrite($fp, "\n"); - } - - fclose($fp); - - if ( function_exists('opcache_invalidate') ) { - opcache_invalidate($this->INIFile); - } - - $this->systemConfigChanged = false; - } - - /** - * Sets value to system config (yet SaveConfig must be called to write it to file) - * - * @param string $section - * @param string $key - * @param string $value - */ - function setSystemConfig($section, $key, $value = null) - { - $this->systemConfigChanged = true; - - if (isset($value)) { - if (!array_key_exists($section, $this->systemConfig)) { - // create section, when missing - $this->systemConfig[$section] = Array (); - } - - // create key in section - $this->systemConfig[$section][$key] = $value; - return ; - } - - unset($this->systemConfig[$section][$key]); - } - - /** - * Returns information from system config - * - * @param string $section - * @param string $key - * @param mixed $default - * @return string|bool - */ - function getSystemConfig($section, $key, $default = false) - { - if ( !array_key_exists($section, $this->systemConfig) ) { - return $default; - } - - if ( !array_key_exists($key, $this->systemConfig[$section]) ) { - return $default; - } - - return isset($this->systemConfig[$section][$key]) ? $this->systemConfig[$section][$key] : $default; - } - /** * Checks if system config is present and is not empty * * @return bool */ function systemConfigFound() { - return file_exists($this->INIFile) && $this->systemConfig; + return $this->systemConfig->exists(); } /** * Checks if given section is present in config * * @param string $section * @return bool */ function sectionFound($section) { - return array_key_exists($section, $this->systemConfig); + return $this->systemConfig->sectionFound($section); } /** * Returns formatted module name based on it's root folder * * @param string $module_folder * @return string */ function getModuleName($module_folder) { return implode('-', array_map('ucfirst', explode('-', $module_folder))); } /** * Returns information about module (based on "install/module_info.xml" file) * * @param string $module_name * @return Array */ function getModuleInfo($module_name) { if ( $module_name == 'core' ) { $info_file = FULL_PATH . '/' . $module_name . '/install/module_info.xml'; } else { $info_file = MODULES_PATH . '/' . $module_name . '/install/module_info.xml'; } if ( !file_exists($info_file) ) { return Array (); } $ret = Array (); $module_info = simplexml_load_file($info_file); if ( $module_info === false ) { // non-valid xml file return Array (); } foreach ($module_info as $node) { /* @var $node SimpleXMLElement */ $ret[strtolower($node->getName())] = trim($node); } return $ret; } /** * Returns nice module string to be used on install/upgrade screens * * @param string $module_name * @param string $version_string * @return string */ function getModuleString($module_name, $version_string) { // image (if exists) ( ) $ret = Array (); $module_info = $this->getModuleInfo($module_name); if (array_key_exists('name', $module_info) && $module_info['name']) { $module_name = $module_info['name']; } else { $module_name = $this->getModuleName($module_name); } if (array_key_exists('image', $module_info) && $module_info['image']) { $image_src = $module_info['image']; if (!preg_match('/^(http|https):\/\//', $image_src)) { // local image -> make absolute url $image_src = $this->Application->BaseURL() . $image_src; } $ret[] = '' . htmlspecialchars($module_name, ENT_QUOTES, 'UTF-8') . ''; } if (array_key_exists('description', $module_info) && $module_info['description']) { $ret[] = $module_info['description']; } else { $ret[] = $module_name; } $ret[] = '(' . $module_name . ' ' . $version_string . ')'; return implode(' ', $ret); } /** * Creates module root category in "Home" category using given data and returns it * * @param string $name * @param string $description * @param string $category_template * @param string $category_icon * @return kDBItem */ function &createModuleCategory($name, $description, $category_template = null, $category_icon = null) { static $fields = null; if ( !isset($fields) ) { $ml_formatter = $this->Application->recallObject('kMultiLanguage'); /* @var $ml_formatter kMultiLanguage */ $fields['name'] = $ml_formatter->LangFieldName('Name'); $fields['description'] = $ml_formatter->LangFieldName('Description'); } $category = $this->Application->recallObject('c', null, Array ('skip_autoload' => true)); /* @var $category kDBItem */ $category_fields = Array ( $fields['name'] => $name, 'Filename' => $name, 'AutomaticFilename' => 1, $fields['description'] => $description, 'Status' => STATUS_ACTIVE, 'Priority' => -9999, // prevents empty link to module category on spearate module install 'NamedParentPath' => 'Content/' . $name, ); $category_fields['ParentId'] = $this->Application->getBaseCategory(); if ( isset($category_template) ) { $category_fields['Template'] = $category_template; $category_fields['CachedTemplate'] = $category_template; } if ( isset($category_icon) ) { $category_fields['UseMenuIconUrl'] = 1; $category_fields['MenuIconUrl'] = $category_icon; } $category->Clear(); $category->SetDBFieldsFromHash($category_fields); $category->Create(); $priority_helper = $this->Application->recallObject('PriorityHelper'); /* @var $priority_helper kPriorityHelper */ $event = new kEvent('c:OnListBuild'); // ensure, that newly created category has proper value in Priority field $priority_helper->recalculatePriorities($event, 'ParentId = ' . $category_fields['ParentId']); // update Priority field in object, becase "CategoriesItem::Update" method will be called // from "kInstallToolkit::setModuleItemTemplate" and otherwise will set 0 to Priority field $sql = 'SELECT Priority FROM ' . $category->TableName . ' WHERE ' . $category->IDField . ' = ' . $category->GetID(); $category->SetDBField('Priority', $this->Conn->GetOne($sql)); return $category; } /** * Sets category item template into custom field for given prefix * * @param kDBItem $category * @param string $prefix * @param string $item_template */ function setModuleItemTemplate(&$category, $prefix, $item_template) { $this->Application->removeObject('c-cdata'); // recreate all fields, because custom fields are added during install script $category->Configure(); $category->SetDBField('cust_' . $prefix .'_ItemTemplate', $item_template); $category->Update(); } /** * Link custom field records with search config records + create custom field columns * * @param string $module_folder * @param string $prefix * @param int $item_type */ function linkCustomFields($module_folder, $prefix, $item_type) { $module_folder = strtolower($module_folder); $module_name = $module_folder; if ( $module_folder == 'kernel' ) { $module_name = 'in-portal'; $module_folder = 'core'; } $db =& $this->Application->GetADODBConnection(); $sql = 'SELECT FieldName, CustomFieldId FROM ' . TABLE_PREFIX . 'CustomFields WHERE Type = ' . $item_type . ' AND IsSystem = 0'; // config is not read here yet :( $this->Application->getUnitOption('p', 'ItemType'); $custom_fields = $db->GetCol($sql, 'CustomFieldId'); foreach ($custom_fields as $cf_id => $cf_name) { $sql = 'UPDATE ' . TABLE_PREFIX . 'SearchConfig SET CustomFieldId = ' . $cf_id . ' WHERE (TableName = "CustomFields") AND (LOWER(ModuleName) = "' . $module_name . '") AND (FieldName = ' . $db->qstr($cf_name) . ')'; $db->Query($sql); } // because of configs was read only from installed before modules (in-portal), then reread configs $this->Application->UnitConfigReader->scanModules(MODULES_PATH . DIRECTORY_SEPARATOR . $module_folder); // create correct columns in CustomData table $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->createFields($prefix . '-cdata', true); } /** * Deletes cache, useful after separate module install and installator last step * * @param bool $refresh_permissions * @return void */ function deleteCache($refresh_permissions = false) { $this->Application->HandleEvent(new kEvent('adm:OnResetMemcache')); // not in DB = 100% invalidate $this->Application->HandleEvent(new kEvent('adm:OnResetConfigsCache')); $this->Application->HandleEvent(new kEvent('adm:OnResetSections')); $this->Application->HandleEvent(new kEvent('c:OnResetCMSMenuCache')); $this->Conn->Query('DELETE FROM ' . TABLE_PREFIX . 'CachedUrls'); if ( $refresh_permissions ) { $rebuild_mode = $this->Application->ConfigValue('CategoryPermissionRebuildMode'); if ( $rebuild_mode == CategoryPermissionRebuild::SILENT ) { // refresh permission without progress bar $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); } elseif ( $rebuild_mode == CategoryPermissionRebuild::AUTOMATIC ) { // refresh permissions with ajax progress bar (when available) $this->Application->setDBCache('ForcePermCacheUpdate', 1); } } } /** * Deletes all temp tables (from active sessions too) * */ function deleteEditTables() { - $table_prefix = $this->getSystemConfig('Database', 'TablePrefix'); + $table_prefix = $this->systemConfig->get('TablePrefix', 'Database'); $tables = $this->Conn->GetCol('SHOW TABLES'); $mask_edit_table = '/' . $table_prefix . 'ses_(.*)_edit_(.*)/'; $mask_search_table = '/' . $table_prefix . 'ses_(.*?)_(.*)/'; foreach ($tables as $table) { if ( preg_match($mask_edit_table, $table, $rets) || preg_match($mask_search_table, $table, $rets) ) { $this->Conn->Query('DROP TABLE IF EXISTS ' . $table); } } } /** * Perform redirect after separate module install * * @param string $module_folder * @param bool $refresh_permissions */ function finalizeModuleInstall($module_folder, $refresh_permissions = false) { $this->SetModuleVersion(basename($module_folder), $module_folder); if (!$this->Application->GetVar('redirect')) { return ; } $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ // use direct query, since module isn't yet in kApplication::ModuleInfo array $sql = 'SELECT Name FROM ' . TABLE_PREFIX . 'Modules WHERE Path = ' . $this->Conn->qstr(rtrim($module_folder, '/') . '/'); $module_name = $this->Conn->GetOne($sql); $themes_helper->synchronizeModule($module_name); $ml_helper = $this->Application->recallObject('kMultiLanguageHelper'); /* @var $ml_helper kMultiLanguageHelper */ $ml_helper->massCreateFields(); $this->deleteCache($refresh_permissions); $url_params = Array ( 'pass' => 'm', 'admin' => 1, 'RefreshTree' => 1, 'index_file' => 'index.php', ); $this->Application->Redirect('modules/modules_list', $url_params); } /** * Performs rebuild of themes * */ function rebuildThemes() { $this->Application->HandleEvent(new kEvent('adm:OnRebuildThemes')); } /** * Checks that file is writable by group or others * * @param string $file * @return boolean */ function checkWritePermissions($file) { if (DIRECTORY_SEPARATOR == '\\') { // windows doen't allow to check permissions (always returns null) return null; } $permissions = fileperms($file); return $permissions & 0x0010 || $permissions & 0x0002; } /** * Upgrades primary skin to the latest version * * @param Array $module_info * @return string|bool */ function upgradeSkin($module_info) { $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'css'); $data = file_get_contents($upgrades_file); // get all versions with their positions in file $versions = Array (); preg_match_all('/(' . VERSION_MARK . ')/s', $data, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); $from_version_int = $this->ConvertModuleVersion($module_info['FromVersion']); foreach ($matches as $index => $match) { $version_int = $this->ConvertModuleVersion($match[2][0]); if ( $version_int < $from_version_int ) { // only process versions, that were released after currently used version continue; } $start_pos = $match[0][1] + strlen($match[0][0]); $end_pos = array_key_exists($index + 1, $matches) ? $matches[$index + 1][0][1] : mb_strlen($data); $patch_data = str_replace("\r\n", "\n", substr($data, $start_pos, $end_pos - $start_pos)); $versions[] = Array ( 'Version' => $match[2][0], // fixes trimmed leading spaces by modern text editor 'Data' => ltrim( str_replace("\n\n", "\n \n", $patch_data) ), ); } if ( !$versions ) { // not skin changes -> quit return true; } $primary_skin = $this->Application->recallObject('skin.primary', null, Array ('skip_autoload' => true)); /* @var $primary_skin kDBItem */ $primary_skin->Load(1, 'IsPrimary'); if ( !$primary_skin->isLoaded() ) { // we always got primary skin, but just in case return false; } $temp_handler = $this->Application->recallObject('skin_TempHandler', 'kTempTablesHandler'); /* @var $temp_handler kTempTablesHandler */ // clone current skin $cloned_ids = $temp_handler->CloneItems('skin', '', Array ($primary_skin->GetID())); if ( !$cloned_ids ) { // can't clone return false; } $skin = $this->Application->recallObject('skin.tmp', null, Array ('skip_autoload' => true)); /* @var $skin kDBItem */ $skin->Load($cloned_ids[0]); // save css to temp file (for patching) $skin_file = tempnam('/tmp', 'skin_css_'); $fp = fopen($skin_file, 'w'); fwrite($fp, str_replace("\r\n", "\n", $skin->GetDBField('CSS'))); fclose($fp); $output = Array (); $patch_file = tempnam('/tmp', 'skin_patch_'); foreach ($versions as $version_info) { // for each left version get it's patch and apply to temp file $fp = fopen($patch_file, 'w'); fwrite($fp, $version_info['Data']); fclose($fp); $output[ $version_info['Version'] ] = shell_exec('patch ' . $skin_file . ' ' . $patch_file . ' 2>&1') . "\n"; } // place temp file content into cloned skin $skin->SetDBField('Name', 'Upgraded to ' . $module_info['ToVersion']); $skin->SetDBField('CSS', file_get_contents($skin_file)); $skin->Update(); unlink($skin_file); unlink($patch_file); $has_errors = false; foreach ($output as $version => $version_output) { $version_errors = trim(preg_replace("/(^|\n)(patching file .*?|Hunk #.*?\.)(\n|$)/m", '', $version_output)); if ( $version_errors ) { $has_errors = true; $output[$version] = trim(preg_replace("/(^|\n)(patching file .*?)(\n|$)/m", '', $output[$version])); } else { unset($output[$version]); } } if ( !$has_errors ) { // copy patched css back to primary skin $primary_skin->SetDBField('CSS', $skin->GetDBField('CSS')); $primary_skin->Update(); // delete temporary skin record $temp_handler->DeleteItems('skin', '', Array ($skin->GetID())); return true; } // put clean skin from new version $skin->SetDBField('CSS', file_get_contents(FULL_PATH . '/core/admin_templates/incs/style_template.css')); $skin->Update(); // return output in case of errors return $output; } /** * Returns cache handlers, that are working * * @param string $current * @return Array */ public function getWorkingCacheHandlers($current = null) { if ( !isset($current) ) { - $current = $this->getSystemConfig('Misc', 'CacheHandler'); + $current = $this->systemConfig->get('CacheHandler', 'Misc'); } $cache_handler = $this->Application->makeClass('kCache'); $cache_handlers = Array ( 'Fake' => 'None', 'Memcache' => 'Memcached', 'XCache' => 'XCache', 'Apc' => 'Alternative PHP Cache' ); foreach ($cache_handlers AS $class_prefix => $title) { $handler_class = $class_prefix . 'CacheHandler'; if ( !class_exists($handler_class) ) { unset($cache_handlers[$class_prefix]); } else { $handler = new $handler_class($cache_handler, 'localhost:11211'); /* @var $handler FakeCacheHandler */ if ( !$handler->isWorking() ) { if ( $current == $class_prefix ) { $cache_handlers[$class_prefix] .= ' (offline)'; } else { unset($cache_handlers[$class_prefix]); } } } } return $cache_handlers; } /** * Returns compression engines, that are working * * @param string $current * @return Array */ public function getWorkingCompressionEngines($current = null) { if ( !isset($current) ) { - $current = $this->getSystemConfig('Misc', 'CompressionEngine'); + $current = $this->systemConfig->get('CompressionEngine', 'Misc'); } $output = shell_exec('java -version 2>&1'); $compression_engines = Array ('' => 'None', 'yui' => 'YUICompressor (Java)', 'php' => 'PHP-based'); if ( stripos($output, 'java version') === false ) { if ( $current == 'yui' ) { $compression_engines['yui'] .= ' (offline)'; } else { unset($compression_engines['yui']); } } return $compression_engines; } - } \ No newline at end of file + } Index: branches/5.2.x/core/install.php =================================================================== --- branches/5.2.x/core/install.php (revision 16434) +++ branches/5.2.x/core/install.php (revision 16435) @@ -1,1783 +1,1765 @@ Init(); $install_engine->Run(); $install_engine->Done(); class kInstallator { /** * Reference to kApplication class object * * @var kApplication */ var $Application = null; /** * Connection to database * * @var IDBConnection */ var $Conn = null; /** * XML file containing steps information * * @var string */ var $StepDBFile = ''; /** * Step name, that currently being processed * * @var string */ var $currentStep = ''; /** * Steps list (preset) to use for current installation * * @var string */ var $stepsPreset = ''; /** * Installation steps to be done * * @var Array */ var $steps = Array ( 'fresh_install' => Array ('sys_requirements', 'check_paths', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'sys_config', 'select_theme', 'security', 'finish'), 'clean_reinstall' => Array ('install_setup', 'sys_requirements', 'check_paths', 'clean_db', 'db_config', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'sys_config', 'select_theme', 'security', 'finish'), 'already_installed' => Array ('check_paths', 'install_setup'), 'upgrade' => Array ('check_paths', 'install_setup', 'sys_config', 'upgrade_modules', 'skin_upgrade', 'security', 'finish'), 'update_license' => Array ('check_paths', 'install_setup', 'select_license', /*'download_license',*/ 'select_domain', 'security', 'finish'), 'update_config' => Array ('check_paths', 'install_setup', 'sys_config', 'security', 'finish'), 'db_reconfig' => Array ('check_paths', 'install_setup', 'db_reconfig', 'security', 'finish'), 'sys_requirements' => Array ('check_paths', 'install_setup', 'sys_requirements', 'security', 'finish') ); /** * Steps, that doesn't required admin to be logged-in to proceed * * @var Array */ var $skipLoginSteps = Array ('sys_requirements', 'check_paths', 'select_license', /*'download_license',*/ 'select_domain', 'root_password', 'choose_modules', 'post_config', 'select_theme', 'security', 'finish', -1); /** * Steps, on which kApplication should not be initialized, because of missing correct db table structure * * @var Array */ var $skipApplicationSteps = Array ('sys_requirements', 'check_paths', 'clean_db', 'db_config', 'db_reconfig' /*, 'install_setup'*/); // remove install_setup when application will work separately from install /** * Folders that should be writeable to continue installation. $1 - main writeable folder from config.php ("/system" by default) * * @var Array */ var $writeableFolders = Array ( '$1', '$1/.restricted', '$1/images', '$1/images/pending', '$1/images/emoticons', // for "In-Bulletin" '$1/user_files', '$1/cache', ); /** * Contains last error message text * * @var string */ var $errorMessage = ''; /** * Base path for includes in templates * * @var string */ var $baseURL = ''; /** * Holds number of last executed query in the SQL * * @var int */ var $LastQueryNum = 0; /** * Dependencies, that should be used in upgrade process * * @var Array */ var $upgradeDepencies = Array (); /** * Log of upgrade - list of upgraded modules and their versions * * @var Array */ var $upgradeLog = Array (); /** * Common tools required for installation process * * @var kInstallToolkit */ var $toolkit = null; function Init() { include_once(FULL_PATH . REL_PATH . '/kernel/kbase.php'); // required by kDBConnection class include_once(FULL_PATH . REL_PATH . '/kernel/utility/multibyte.php'); // emulating multi-byte php extension + include_once(FULL_PATH . REL_PATH . '/kernel/utility/system_config.php'); require_once(FULL_PATH . REL_PATH . '/install/install_toolkit.php'); // toolkit required for module installations to installator + $this->toolkit = new kInstallToolkit(); $this->toolkit->setInstallator($this); - $this->StepDBFile = FULL_PATH.'/'.REL_PATH.'/install/steps_db.xml'; - - $base_path = rtrim(preg_replace('/'.preg_quote(rtrim(REL_PATH, '/'), '/').'$/', '', str_replace('\\', '/', dirname($_SERVER['PHP_SELF']))), '/'); - $this->baseURL = 'http://'.$_SERVER['HTTP_HOST'].$base_path.'/core/install/'; - + $this->baseURL = 'http://' . $_SERVER['HTTP_HOST'] . $this->toolkit->systemConfig->get('WebsitePath', 'Misc') . '/core/install/'; set_error_handler( Array(&$this, 'ErrorHandler') ); - if (file_exists($this->toolkit->INIFile)) { + if ( $this->toolkit->systemConfigFound() ) { // if config.php found, then check his write permission too - $this->writeableFolders[] = $this->toolkit->defaultWritablePath . '/config.php'; - } - - if ( !$this->toolkit->getSystemConfig('Misc', 'WriteablePath') ) { - $this->toolkit->setSystemConfig('Misc', 'WriteablePath', $this->toolkit->defaultWritablePath); - } - - if ( !$this->toolkit->getSystemConfig('Misc', 'RestrictedPath') ) { - $this->toolkit->setSystemConfig('Misc', 'RestrictedPath', $this->toolkit->getSystemConfig('Misc', 'WriteablePath') . DIRECTORY_SEPARATOR . '.restricted'); - } - - if ( !$this->toolkit->getSystemConfig('Misc', 'WebsitePath') ) { - $this->toolkit->setSystemConfig('Misc', 'WebsitePath', $base_path); - } - - if ( $this->toolkit->systemConfigChanged ) { - // immediately save, because this paths will be used in kApplication class later - $this->toolkit->SaveConfig(true); + $this->writeableFolders[] = $this->toolkit->systemConfig->get('WriteablePath', 'Misc') . '/config.php'; } $this->currentStep = $this->GetVar('step'); // can't check login on steps where no application present anyways :) $this->skipLoginSteps = array_unique(array_merge($this->skipLoginSteps, $this->skipApplicationSteps)); $this->SelectPreset(); if (!$this->currentStep) { $this->SetFirstStep(); // sets first step of current preset } $this->InitStep(); } function SetFirstStep() { reset($this->steps[$this->stepsPreset]); $this->currentStep = current($this->steps[$this->stepsPreset]); } /** * Selects preset to proceed based on various criteria * */ function SelectPreset() { $preset = $this->GetVar('preset'); if ($this->toolkit->systemConfigFound()) { // only at installation first step $status = $this->CheckDatabase(false); if ($status && $this->AlreadyInstalled()) { // if already installed, then all future actions need login to work $this->skipLoginSteps = Array ('check_paths', -1); if (!$preset) { $preset = 'already_installed'; $this->currentStep = ''; } } } if ($preset === false) { $preset = 'fresh_install'; // default preset } $this->stepsPreset = $preset; } /** * Returns variable from request * * @param string $name * @param mixed $default * @return string|bool * @access private */ private function GetVar($name, $default = false) { if ( array_key_exists($name, $_COOKIE) ) { return $_COOKIE[$name]; } if ( array_key_exists($name, $_POST) ) { return $_POST[$name]; } return array_key_exists($name, $_GET) ? $_GET[$name] : $default; } /** * Sets new value for request variable * * @param string $name * @param mixed $value * @return void * @access private */ private function SetVar($name, $value) { $_POST[$name] = $value; } /** * Performs needed intialization of data, that step requires * */ function InitStep() { $require_login = !in_array($this->currentStep, $this->skipLoginSteps); $this->InitApplication($require_login); if ($require_login) { // step require login to proceed if (!$this->Application->LoggedIn()) { $this->stepsPreset = 'already_installed'; $this->currentStep = 'install_setup'; // manually set 2nd step, because 'check_paths' step doesn't contain login form // $this->SetFirstStep(); } } switch ($this->currentStep) { case 'sys_requirements': $required_checks = Array ( 'php_version', 'composer', 'curl', 'simplexml', 'freetype', 'gd_version', 'jpeg', 'mysql', 'json', 'date.timezone', 'output_buffering', ); $check_results = $this->toolkit->CallPrerequisitesMethod('core/', 'CheckSystemRequirements'); $required_checks = array_diff($required_checks, array_keys( array_filter($check_results) )); if ( $required_checks ) { // php-based checks failed - show error $this->errorMessage = '
Installation can not continue until all required environment parameters are set correctly'; } elseif ( $this->GetVar('js_enabled') === false ) { // can't check JS without form submit - set some fake error, so user stays on this step $this->errorMessage = ' '; } elseif ( !$this->GetVar('js_enabled') || !$this->GetVar('cookies_enabled') ) { // js/cookies disabled $this->errorMessage = '
Installation can not continue until all required environment parameters are set correctly'; } break; case 'check_paths': - $writeable_base = $this->toolkit->getSystemConfig('Misc', 'WriteablePath'); + $writeable_base = $this->toolkit->systemConfig->get('WriteablePath', 'Misc'); foreach ($this->writeableFolders as $folder_path) { $file_path = FULL_PATH . str_replace('$1', $writeable_base, $folder_path); if (file_exists($file_path) && !is_writable($file_path)) { $this->errorMessage = '
Installation can not continue until all required permissions are set correctly'; break; } } break; case 'clean_db': // don't use Application, because all tables will be erased and it will crash $sql = 'SELECT Path FROM ' . TABLE_PREFIX . 'Modules'; $modules = $this->Conn->GetCol($sql); foreach ($modules as $module_folder) { $remove_file = '/' . $module_folder . 'install/remove_schema.sql'; if (file_exists(FULL_PATH . $remove_file)) { $this->toolkit->RunSQL($remove_file); } } $this->toolkit->deleteEditTables(); $this->currentStep = $this->GetNextStep(); break; case 'db_config': case 'db_reconfig': $fields = Array ( 'DBType', 'DBHost', 'DBName', 'DBUser', 'DBUserPassword', 'DBCollation', 'TablePrefix' ); // set fields foreach ($fields as $field_name) { $submit_value = $this->GetVar($field_name); if ($submit_value !== false) { - $this->toolkit->setSystemConfig('Database', $field_name, $submit_value); + $this->toolkit->systemConfig->set($field_name, 'Database', $submit_value); } /*else { - $this->toolkit->setSystemConfig('Database', $field_name, ''); + $this->toolkit->systemConfig->set($field_name, 'Database', ''); }*/ } break; case 'download_license': $license_source = $this->GetVar('license_source'); if ($license_source !== false && $license_source != 1) { // previous step was "Select License" and not "Download from Intechnic" option was selected $this->currentStep = $this->GetNextStep(); } break; case 'choose_modules': // if no modules found, then proceed to next step $modules = $this->ScanModules(); if (!$modules) { $this->currentStep = $this->GetNextStep(); } break; case 'select_theme': // put available theme list in database $this->toolkit->rebuildThemes(); break; case 'upgrade_modules': // get installed modules from db and compare their versions to upgrade script $modules = $this->GetUpgradableModules(); if (!$modules) { $this->currentStep = $this->GetNextStep(); } break; case 'skin_upgrade': if ($this->Application->RecallVar('SkinUpgradeLog') === false) { // no errors during skin upgrade -> skip this step $this->currentStep = $this->GetNextStep(); } break; case 'install_setup': if ( $this->Application->TableFound(TABLE_PREFIX . 'UserSession', true) ) { // update to 5.2.0 -> rename session table before using it // don't rename any other table here, since their names could be used in upgrade script $this->Conn->Query('RENAME TABLE ' . TABLE_PREFIX . 'UserSession TO ' . TABLE_PREFIX . 'UserSessions'); $this->Conn->Query('RENAME TABLE ' . TABLE_PREFIX . 'SessionData TO ' . TABLE_PREFIX . 'UserSessionData'); } $next_preset = $this->Application->GetVar('next_preset'); if ($next_preset !== false) { $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $username = $this->Application->GetVar('login'); $password = $this->Application->GetVar('password'); if ($username == 'root') { // verify "root" user using configuration settings $login_result = $user_helper->loginUser($username, $password); if ($login_result != LoginResult::OK) { $error_phrase = $login_result == LoginResult::NO_PERMISSION ? 'la_no_permissions' : 'la_invalid_password'; $this->errorMessage = $this->Application->Phrase($error_phrase) . '. If you don\'t know your username or password, contact Intechnic Support'; } } else { // non "root" user -> verify using licensing server $url_params = Array ( 'login' => md5($username), 'password' => md5($password), 'action' => 'check', - 'license_code' => base64_encode( $this->toolkit->getSystemConfig('Intechnic', 'LicenseCode') ), + 'license_code' => base64_encode( $this->toolkit->systemConfig->get('LicenseCode', 'Intechnic') ), 'version' => '4.3.0',//$this->toolkit->GetMaxModuleVersion('core/'), 'domain' => base64_encode($_SERVER['HTTP_HOST']), ); $curl_helper = $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $curl_helper->SetRequestData($url_params); $file_data = $curl_helper->Send(GET_LICENSE_URL); if ( !$curl_helper->isGoodResponseCode() ) { $this->errorMessage = 'In-Portal servers temporarily unavailable. Please contact In-Portal support personnel directly.'; } elseif (substr($file_data, 0, 5) == 'Error') { $this->errorMessage = substr($file_data, 6) . ' If you don\'t know your username or password, contact Intechnic Support'; } if ($this->errorMessage == '') { $user_helper->loginUserById(USER_ROOT); } } if ($this->errorMessage == '') { // processed with redirect to selected step preset if (!isset($this->steps[$next_preset])) { $this->errorMessage = 'Preset "'.$next_preset.'" not yet implemented'; } else { $this->stepsPreset = $next_preset; } } } else { // if preset was not choosen, then raise error $this->errorMessage = 'Please select action to perform'; } break; case 'security': // perform write check if ($this->Application->GetVar('skip_security_check')) { // administrator intensionally skips security checks break; } $write_check = true; - $check_paths = Array ('/', '/index.php', $this->toolkit->defaultWritablePath . '/config.php', ADMIN_DIRECTORY . '/index.php'); + $check_paths = Array ('/', '/index.php', $this->toolkit->systemConfig->get('WriteablePath', 'Misc') . '/config.php', ADMIN_DIRECTORY . '/index.php'); + foreach ($check_paths as $check_path) { $path_check_status = $this->toolkit->checkWritePermissions(FULL_PATH . $check_path); if (is_bool($path_check_status) && $path_check_status) { $write_check = false; break; } } // script execute check if (file_exists(WRITEABLE . '/install_check.php')) { unlink(WRITEABLE . '/install_check.php'); } $fp = fopen(WRITEABLE . '/install_check.php', 'w'); fwrite($fp, "Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ $output = $curl_helper->Send($this->Application->BaseURL(WRITEBALE_BASE) . 'install_check.php'); unlink(WRITEABLE . '/install_check.php'); $execute_check = ($output !== 'OK'); $directive_check = true; $ini_vars = Array ('register_globals' => false, 'open_basedir' => true, 'allow_url_fopen' => false); foreach ($ini_vars as $var_name => $var_value) { $current_value = ini_get($var_name); if (($var_value && !$current_value) || (!$var_value && $current_value)) { $directive_check = false; break; } } if (!$write_check || !$execute_check || !$directive_check) { $this->errorMessage = true; } /*else { $this->currentStep = $this->GetNextStep(); }*/ break; } $this->PerformValidation(); // returns validation status (just in case) } /** * Validates data entered by user * * @return bool */ function PerformValidation() { if ($this->GetVar('step') != $this->currentStep) { // just redirect from previous step, don't validate return true; } $status = true; switch ($this->currentStep) { case 'db_config': case 'db_reconfig': // 1. check if required fields are filled $section_name = 'Database'; $required_fields = Array ('DBType', 'DBHost', 'DBName', 'DBUser', 'DBCollation'); foreach ($required_fields as $required_field) { - if (!$this->toolkit->getSystemConfig($section_name, $required_field)) { + if (!$this->toolkit->systemConfig->get($required_field, $section_name)) { $status = false; $this->errorMessage = 'Please fill all required fields'; break; } } if ( !$status ) { break; } // 2. check permissions, that use have in this database $status = $this->CheckDatabase(($this->currentStep == 'db_config') && !$this->GetVar('UseExistingSetup')); break; case 'select_license': $license_source = $this->GetVar('license_source'); if ($license_source == 2) { // license from file -> file must be uploaded $upload_error = $_FILES['license_file']['error']; if ($upload_error != UPLOAD_ERR_OK) { $this->errorMessage = 'Missing License File'; } } elseif (!is_numeric($license_source)) { $this->errorMessage = 'Please select license'; } $status = $this->errorMessage == ''; break; case 'root_password': // check, that password & verify password match $password = $this->Application->GetVar('root_password'); $password_verify = $this->Application->GetVar('root_password_verify'); if ($password != $password_verify) { $this->errorMessage = 'Passwords does not match'; } elseif (mb_strlen($password) < 4) { $this->errorMessage = 'Root Password must be at least 4 characters'; } $status = $this->errorMessage == ''; break; case 'choose_modules': break; case 'upgrade_modules': $modules = $this->Application->GetVar('modules'); if (!$modules) { $modules = Array (); $this->errorMessage = 'Please select module(-s) to ' . ($this->currentStep == 'choose_modules' ? 'install' : 'upgrade'); } // check interface module $upgrade_data = $this->GetUpgradableModules(); if (array_key_exists('core', $upgrade_data) && !in_array('core', $modules)) { // core can be upgraded, but isn't selected $this->errorMessage = 'Please select "Core" as interface module'; } $status = $this->errorMessage == ''; break; } return $status; } /** * Perform installation step actions * */ function Run() { if ($this->errorMessage) { // was error during data validation stage return ; } switch ($this->currentStep) { case 'db_config': case 'db_reconfig': // store db configuration $sql = 'SHOW COLLATION - LIKE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\''; + LIKE \''.$this->toolkit->systemConfig->get('DBCollation', 'Database').'\''; $collation_info = $this->Conn->Query($sql); if ($collation_info) { - $this->toolkit->setSystemConfig('Database', 'DBCharset', $collation_info[0]['Charset']); + $this->toolkit->systemConfig->set('DBCharset', 'Database', $collation_info[0]['Charset']); // database is already connected, that's why set collation on the fly - $this->Conn->Query('SET NAMES \''.$this->toolkit->getSystemConfig('Database', 'DBCharset').'\' COLLATE \''.$this->toolkit->getSystemConfig('Database', 'DBCollation').'\''); + $this->Conn->Query('SET NAMES \''.$this->toolkit->systemConfig->get('DBCharset', 'Database').'\' COLLATE \''.$this->toolkit->systemConfig->get('DBCollation', 'Database').'\''); } - $this->toolkit->SaveConfig(); + $this->toolkit->systemConfig->save(); if ($this->currentStep == 'db_config') { if ($this->GetVar('UseExistingSetup')) { // abort clean install and redirect to already_installed $this->stepsPreset = 'already_installed'; break; } // import base data into new database, not for db_reconfig $this->toolkit->RunSQL('/core/install/install_schema.sql'); $this->toolkit->RunSQL('/core/install/install_data.sql'); // create category using sql, because Application is not available here - $table_name = $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'IdGenerator'; + $table_name = $this->toolkit->systemConfig->get('TablePrefix', 'Database') . 'IdGenerator'; $this->Conn->Query('UPDATE ' . $table_name . ' SET lastid = lastid + 1'); $resource_id = $this->Conn->GetOne('SELECT lastid FROM ' . $table_name); if ($resource_id === false) { $this->Conn->Query('INSERT INTO '.$table_name.' (lastid) VALUES (2)'); $resource_id = 2; } // can't use USER_ROOT constant, since Application isn't available here $fields_hash = Array ( 'l1_Name' => 'Content', 'l1_MenuTitle' => 'Content', 'Filename' => 'Content', 'AutomaticFilename' => 0, 'CreatedById' => -1, 'CreatedOn' => time(), 'ResourceId' => $resource_id - 1, 'l1_Description' => 'Content', 'Status' => 4, ); - $this->Conn->doInsert($fields_hash, $this->toolkit->getSystemConfig('Database', 'TablePrefix') . 'Categories'); + $this->Conn->doInsert($fields_hash, $this->toolkit->systemConfig->get('TablePrefix', 'Database') . 'Categories'); $this->toolkit->SetModuleRootCategory('Core', $this->Conn->getInsertID()); // set module "Core" version after install (based on upgrade scripts) $this->toolkit->SetModuleVersion('Core', 'core/'); // for now we set "In-Portal" module version to "Core" module version (during clean install) $this->toolkit->SetModuleVersion('In-Portal', 'core/'); } break; case 'select_license': // reset memory cache, when application is first available (on fresh install and clean reinstall steps) $this->Application->HandleEvent(new kEvent('adm:OnResetMemcache')); $license_source = $this->GetVar('license_source'); switch ($license_source) { case 1: // Download from Intechnic break; case 2: // Upload License File $file_data = array_map('trim', file($_FILES['license_file']['tmp_name'])); if ((count($file_data) == 3) && $file_data[1]) { $modules_helper = $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ if ($modules_helper->verifyLicense($file_data[1])) { - $this->toolkit->setSystemConfig('Intechnic', 'License', $file_data[1]); - $this->toolkit->setSystemConfig('Intechnic', 'LicenseCode', $file_data[2]); - $this->toolkit->SaveConfig(); + $this->toolkit->systemConfig->set('License', 'Intechnic', $file_data[1]); + $this->toolkit->systemConfig->set('LicenseCode', 'Intechnic', $file_data[2]); + $this->toolkit->systemConfig->save(); } else { $this->errorMessage = 'Invalid License File'; } } else { $this->errorMessage = 'Invalid License File'; } break; case 3: // Use Existing License - $license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License'); + $license_hash = $this->toolkit->systemConfig->get('License', 'Intechnic'); if ($license_hash) { $modules_helper = $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ if (!$modules_helper->verifyLicense($license_hash)) { $this->errorMessage = 'Invalid or corrupt license detected'; } } else { // happens, when browser's "Back" button is used $this->errorMessage = 'Missing License File'; } break; case 4: // Skip License (Local Domain Installation) if ($this->toolkit->sectionFound('Intechnic')) { // remove any previous license information - $this->toolkit->setSystemConfig('Intechnic', 'License'); - $this->toolkit->setSystemConfig('Intechnic', 'LicenseCode'); - $this->toolkit->SaveConfig(); + $this->toolkit->systemConfig->set('License', 'Intechnic'); + $this->toolkit->systemConfig->set('LicenseCode', 'Intechnic'); + $this->toolkit->systemConfig->save(); } break; } break; case 'download_license': $license_login = $this->GetVar('login'); $license_password = $this->GetVar('password'); $license_id = $this->GetVar('licenses'); $curl_helper = $this->Application->recallObject('CurlHelper'); /* @var $curl_helper kCurlHelper */ if (strlen($license_login) && strlen($license_password) && !$license_id) { // Here we determine weather login is ok & check available licenses $url_params = Array ( 'login' => md5($license_login), 'password' => md5($license_password), 'version' => $this->toolkit->GetMaxModuleVersion('core/'), 'domain' => base64_encode($_SERVER['HTTP_HOST']), ); $curl_helper->SetRequestData($url_params); $file_data = $curl_helper->Send(GET_LICENSE_URL); if (!$file_data) { // error connecting to licensing server $this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!'; } else { if (substr($file_data, 0, 5) == 'Error') { // after processing data server returned error $this->errorMessage = substr($file_data, 6); } else { // license received if (substr($file_data, 0, 3) == 'SEL') { // we have more, then one license -> let user choose $this->SetVar('license_selection', base64_encode( substr($file_data, 4) )); // we received html with radio buttons with names "licenses" $this->errorMessage = 'Please select which license to use'; } else { // we have one license $this->toolkit->processLicense($file_data); } } } } else if (!$license_id) { // licenses were not queried AND user/password missing $this->errorMessage = 'Incorrect Username or Password. If you don\'t know your username or password, contact Intechnic Support'; } else { // Here we download license $url_params = Array ( 'license_id' => md5($license_id), 'dlog' => md5($license_login), 'dpass' => md5($license_password), 'version' => $this->toolkit->GetMaxModuleVersion('core/'), 'domain' => base64_encode($_SERVER['HTTP_HOST']), ); $curl_helper->SetRequestData($url_params); $file_data = $curl_helper->Send(GET_LICENSE_URL); if (!$file_data) { // error connecting to licensing server $this->errorMessage = 'Unable to connect to the Intechnic server! Please try again later!'; } else { if (substr($file_data, 0, 5) == 'Error') { // after processing data server returned error $this->errorMessage = substr($file_data, 6); } else { $this->toolkit->processLicense($file_data); } } } break; case 'select_domain': $modules_helper = $this->Application->recallObject('ModulesHelper'); /* @var $modules_helper kModulesHelper */ // get domain name as entered by user on the form $domain = $this->GetVar('domain') == 1 ? $_SERVER['HTTP_HOST'] : str_replace(' ', '', $this->GetVar('other')); - $license_hash = $this->toolkit->getSystemConfig('Intechnic', 'License'); + $license_hash = $this->toolkit->systemConfig->get('License', 'Intechnic'); if ($license_hash) { // when license present, then extract domain from it $license_hash = base64_decode($license_hash); list ( , , $license_keys) = $modules_helper->_ParseLicense($license_hash); $license_domain = $license_keys[0]['domain']; } else { // when license missing, then use current domain or domain entered by user $license_domain = $domain; } if ($domain != '') { if (strstr($domain, $license_domain) || $modules_helper->_IsLocalSite($domain)) { - $this->toolkit->setSystemConfig('Misc', 'Domain', $domain); - $this->toolkit->SaveConfig(); + $this->toolkit->systemConfig->set('Domain', 'Misc', $domain); + $this->toolkit->systemConfig->save(); } else { $this->errorMessage = 'Domain name entered does not match domain name in the license!'; } } else { $this->errorMessage = 'Please enter valid domain!'; } break; case 'sys_config': $config_data = $this->GetVar('system_config'); foreach ($config_data as $section => $section_vars) { foreach ($section_vars as $var_name => $var_value) { - $this->toolkit->setSystemConfig($section, $var_name, $var_value); + $this->toolkit->systemConfig->set($var_name, $section, $var_value); } } - $this->toolkit->SaveConfig(); + $this->toolkit->systemConfig->save(); break; case 'root_password': // update root password in database $password_formatter = $this->Application->recallObject('kPasswordFormatter'); /* @var $password_formatter kPasswordFormatter */ $config_values = Array ( 'RootPass' => $password_formatter->hashPassword($this->Application->GetVar('root_password')), - 'Backup_Path' => FULL_PATH . $this->toolkit->getSystemConfig('Misc', 'WriteablePath') . DIRECTORY_SEPARATOR . 'backupdata', - 'DefaultEmailSender' => 'portal@' . $this->toolkit->getSystemConfig('Misc', 'Domain') + 'Backup_Path' => FULL_PATH . $this->toolkit->systemConfig->get('WriteablePath', 'Misc') . DIRECTORY_SEPARATOR . 'backupdata', + 'DefaultEmailSender' => 'portal@' . $this->toolkit->systemConfig->get('Domain', 'Misc') ); $site_timezone = date_default_timezone_get(); if ($site_timezone) { $config_values['Config_Site_Time'] = $site_timezone; } $this->toolkit->saveConfigValues($config_values); $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ // login as "root", when no errors on password screen $user_helper->loginUser('root', $this->Application->GetVar('root_password')); // import base language for core (english) $this->toolkit->ImportLanguage('/core/install/english'); // make sure imported language is set as active in session, created during installation $this->Application->Session->SetField('Language', 1); // set imported language as primary $lang = $this->Application->recallObject('lang.-item', null, Array('skip_autoload' => true)); /* @var $lang LanguagesItem */ $lang->Load(1); // fresh install => ID=1 $lang->setPrimary(true); // for Front-End break; case 'choose_modules': // run module install scripts $modules = $this->Application->GetVar('modules'); if ($modules) { foreach ($modules as $module) { $install_file = MODULES_PATH.'/'.$module.'/install.php'; if (file_exists($install_file)) { include_once($install_file); } } } // update category cache $updater = $this->Application->makeClass('kPermCacheUpdater'); /* @var $updater kPermCacheUpdater */ $updater->OneStepRun(); break; case 'post_config': $this->toolkit->saveConfigValues( $this->GetVar('config') ); break; case 'select_theme': // 1. mark theme, that user is selected $theme_id = $this->GetVar('theme'); $theme_table = $this->Application->getUnitOption('theme', 'TableName'); $theme_idfield = $this->Application->getUnitOption('theme', 'IDField'); $sql = 'UPDATE ' . $theme_table . ' SET Enabled = 1, PrimaryTheme = 1 WHERE ' . $theme_idfield . ' = ' . $theme_id; $this->Conn->Query($sql); $this->toolkit->rebuildThemes(); // rescan theme to create structure after theme is enabled !!! // install theme dependent demo data if ($this->Application->GetVar('install_demo_data')) { $sql = 'SELECT Name FROM ' . $theme_table . ' WHERE ' . $theme_idfield . ' = ' . $theme_id; $theme_name = $this->Conn->GetOne($sql); - $site_path = $this->toolkit->getSystemConfig('Misc', 'WebsitePath') . '/'; + $site_path = $this->toolkit->systemConfig->get('WebsitePath','Misc') . '/'; $file_helper = $this->Application->recallObject('FileHelper'); /* @var $file_helper FileHelper */ foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ($module_name == 'In-Portal') { continue; } $template_path = '/themes' . '/' . $theme_name . '/' . $module_info['TemplatePath']; $this->toolkit->RunSQL( $template_path . '_install/install_data.sql', Array('{ThemeId}', '{SitePath}'), Array($theme_id, $site_path) ); if ( file_exists(FULL_PATH . $template_path . '_install/images') ) { // copy theme demo images into writable path accessible by FCKEditor $file_helper->copyFolderRecursive(FULL_PATH . $template_path . '_install/images' . DIRECTORY_SEPARATOR, WRITEABLE . '/user_files/Images'); } } } break; case 'upgrade_modules': // get installed modules from db and compare their versions to upgrade script $modules = $this->Application->GetVar('modules'); if ($modules) { $upgrade_data = $this->GetUpgradableModules(); $start_from_query = $this->Application->GetVar('start_from_query'); $this->upgradeDepencies = $this->getUpgradeDependencies($modules, $upgrade_data); if ($start_from_query !== false) { $this->upgradeLog = unserialize( $this->Application->RecallVar('UpgradeLog') ); } else { $start_from_query = 0; $this->upgradeLog = Array ('ModuleVersions' => Array ()); // remember each module version, before upgrade scripts are executed foreach ($modules as $module_name) { $module_info = $upgrade_data[$module_name]; $this->upgradeLog['ModuleVersions'][$module_name] = $module_info['FromVersion']; } $this->Application->RemoveVar('UpgradeLog'); } // 1. perform "php before", "sql", "php after" upgrades foreach ($modules as $module_name) { $module_info = $upgrade_data[$module_name]; /*echo '

Upgrading "' . $module_info['Name'] . '" to "' . $module_info['ToVersion'] . '"

' . "\n"; flush();*/ if (!$this->RunUpgrade($module_info['Name'], $module_info['ToVersion'], $upgrade_data, $start_from_query)) { $this->Application->StoreVar('UpgradeLog', serialize($this->upgradeLog)); $this->Done(); } // restore upgradable module version (makes sense after sql error processing) $upgrade_data[$module_name]['FromVersion'] = $this->upgradeLog['ModuleVersions'][$module_name]; } // 2. import language pack, perform "languagepack" upgrade for all upgraded versions foreach ($modules as $module_name) { $module_info = $upgrade_data[$module_name]; $sqls =& $this->getUpgradeQueriesFromVersion($module_info['Path'], $module_info['FromVersion']); preg_match_all('/' . VERSION_MARK . '/s', $sqls, $regs); // import module language pack $this->toolkit->ImportLanguage('/' . $module_info['Path'] . 'install/english', true); // perform advanced language pack upgrade foreach ($regs[1] as $version) { $this->RunUpgradeScript($module_info['Path'], $version, 'languagepack'); } } // 3. update all theme language packs $themes_helper = $this->Application->recallObject('ThemesHelper'); /* @var $themes_helper kThemesHelper */ $themes_helper->synchronizeModule(false); // 4. upgrade admin skin if (in_array('core', $modules)) { $skin_upgrade_log = $this->toolkit->upgradeSkin($upgrade_data['core']); if ($skin_upgrade_log === true) { $this->Application->RemoveVar('SkinUpgradeLog'); } else { $this->Application->StoreVar('SkinUpgradeLog', serialize($skin_upgrade_log)); } // for now we set "In-Portal" module version to "Core" module version (during upgrade) $this->toolkit->SetModuleVersion('In-Portal', false, $upgrade_data['core']['ToVersion']); } } break; case 'finish': // delete cache $this->toolkit->deleteCache(); $this->toolkit->rebuildThemes(); // compile admin skin, so it will be available in 3 frames at once $skin_helper = $this->Application->recallObject('SkinHelper'); /* @var $skin_helper SkinHelper */ $skin = $this->Application->recallObject('skin', null, Array ('skip_autoload' => true)); /* @var $skin kDBItem */ $skin->Load(1, 'IsPrimary'); $skin_helper->compile($skin); // set installation finished mark if ($this->Application->ConfigValue('InstallFinished') === false) { $fields_hash = Array ( 'VariableName' => 'InstallFinished', 'VariableValue' => 1, ); $this->Conn->doInsert($fields_hash, TABLE_PREFIX.'SystemSettings'); } break; } if ($this->errorMessage) { // was error during run stage return ; } $this->currentStep = $this->GetNextStep(); $this->InitStep(); // init next step (that will be shown now) $this->InitApplication(); if ($this->currentStep == -1) { // step after last step -> redirect to admin $user_helper = $this->Application->recallObject('UserHelper'); /* @var $user_helper UserHelper */ $user_helper->logoutUser(); $this->Application->Redirect($user_helper->event->redirect, $user_helper->event->getRedirectParams(), '', 'index.php'); } } function getUpgradeDependencies($modules, &$upgrade_data) { $dependencies = Array (); foreach ($modules as $module_name) { $module_info = $upgrade_data[$module_name]; $upgrade_object =& $this->getUpgradeObject($module_info['Path']); if (!is_object($upgrade_object)) { continue; } foreach ($upgrade_object->dependencies as $dependent_version => $version_dependencies) { if (!$version_dependencies) { // module is independent -> skip continue; } list ($parent_name, $parent_version) = each($version_dependencies); if (!array_key_exists($parent_name, $dependencies)) { // parent module $dependencies[$parent_name] = Array (); } if (!array_key_exists($parent_version, $dependencies[$parent_name])) { // parent module versions, that are required by other module versions $dependencies[$parent_name][$parent_version] = Array (); } $dependencies[$parent_name][$parent_version][] = Array ($module_info['Name'] => $dependent_version); } } return $dependencies; } /** * Returns database queries, that should be executed to perform upgrade from given to lastest version of given module path * * @param string $module_path * @param string $from_version * @return string */ function &getUpgradeQueriesFromVersion($module_path, $from_version) { $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'sql'); $sqls = file_get_contents($upgrades_file); $version_mark = preg_replace('/(\(.*?\))/', $from_version, VERSION_MARK); // get only sqls from next (relative to current) version to end of file $start_pos = strpos($sqls, $version_mark); $sqls = substr($sqls, $start_pos); return $sqls; } function RunUpgrade($module_name, $to_version, &$upgrade_data, &$start_from_query) { $module_info = $upgrade_data[ strtolower($module_name) ]; $sqls =& $this->getUpgradeQueriesFromVersion($module_info['Path'], $module_info['FromVersion']); preg_match_all('/(' . VERSION_MARK . ')/s', $sqls, $matches, PREG_SET_ORDER + PREG_OFFSET_CAPTURE); foreach ($matches as $index => $match) { // upgrade version $version = $match[2][0]; if ($this->toolkit->ConvertModuleVersion($version) > $this->toolkit->ConvertModuleVersion($to_version)) { // only upgrade to $to_version, not further break; } if (!in_array($module_name . ':' . $version, $this->upgradeLog)) { if ($this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Upgrading "' . $module_name . '" to "' . $version . '" version: BEGIN.'); } /*echo 'Upgrading "' . $module_name . '" to "' . $version . '".
' . "\n"; flush();*/ // don't upgrade same version twice $start_pos = $match[0][1] + strlen($match[0][0]); $end_pos = array_key_exists($index + 1, $matches) ? $matches[$index + 1][0][1] : strlen($sqls); $version_sqls = substr($sqls, $start_pos, $end_pos - $start_pos); if ($start_from_query == 0) { $this->RunUpgradeScript($module_info['Path'], $version, 'before'); } if (!$this->toolkit->RunSQLText($version_sqls, null, null, $start_from_query)) { $this->errorMessage .= ''; $this->errorMessage .= '
Module "' . $module_name . '" upgrade to "' . $version . '" failed.'; $this->errorMessage .= '
Click Continue button below to skip this query and go further
'; return false; } else { // reset query counter, when all queries were processed $start_from_query = 0; } $this->RunUpgradeScript($module_info['Path'], $version, 'after'); if ($this->Application->isDebugMode()) { $this->Application->Debugger->appendHTML('Upgrading "' . $module_name . '" to "' . $version . '" version: END.'); } // remember, that we've already upgraded given version $this->upgradeLog[] = $module_name . ':' . $version; } if (array_key_exists($module_name, $this->upgradeDepencies) && array_key_exists($version, $this->upgradeDepencies[$module_name])) { foreach ($this->upgradeDepencies[$module_name][$version] as $dependency_info) { list ($dependent_module, $dependent_version) = each($dependency_info); if (!$this->RunUpgrade($dependent_module, $dependent_version, $upgrade_data, $start_from_query)) { return false; } } } // only mark module as updated, when all it's dependent modules are upgraded $this->toolkit->SetModuleVersion($module_name, false, $version); } return true; } /** * Run upgrade PHP scripts for module with specified path * * @param string $module_path * @param Array $version * @param string $mode upgrade mode = {before,after,languagepack} */ function RunUpgradeScript($module_path, $version, $mode) { $upgrade_object =& $this->getUpgradeObject($module_path); if (!is_object($upgrade_object)) { return ; } $upgrade_method = 'Upgrade_' . str_replace(Array ('.', '-'), '_', $version); if (method_exists($upgrade_object, $upgrade_method)) { $upgrade_object->$upgrade_method($mode); } } /** * Returns upgrade class for given module path * * @param string $module_path * @return kUpgradeHelper */ function &getUpgradeObject($module_path) { static $upgrade_classes = Array (); $upgrades_file = sprintf(UPGRADES_FILE, $module_path, 'php'); if (!file_exists($upgrades_file)) { $false = false; return $false; } if (!isset($upgrade_classes[$module_path])) { require_once(FULL_PATH . REL_PATH . '/install/upgrade_helper.php'); // save class name, because 2nd time (in after call) // $upgrade_class variable will not be present include_once $upgrades_file; $upgrade_classes[$module_path] = $upgrade_class; } $upgrade_object = new $upgrade_classes[$module_path](); /* @var $upgrade_object CoreUpgrades */ $upgrade_object->setToolkit($this->toolkit); return $upgrade_object; } /** * Initialize kApplication * * @param bool $force initialize in any case */ function InitApplication($force = false) { if (($force || !in_array($this->currentStep, $this->skipApplicationSteps)) && !isset($this->Application)) { // step is allowed for application usage & it was not initialized in previous step global $start, $debugger, $dbg_options; include_once(FULL_PATH.'/core/kernel/startup.php'); $this->Application =& kApplication::Instance(); $this->toolkit->Application =& kApplication::Instance(); $this->includeModuleConstants(); $this->Application->Init(); $this->Conn =& $this->Application->GetADODBConnection(); $this->toolkit->Conn =& $this->Application->GetADODBConnection(); } } /** * When no modules installed, then pre-include all modules contants, since they are used in unit configs * */ function includeModuleConstants() { $modules = $this->ScanModules(); foreach ($modules as $module_path) { $constants_file = MODULES_PATH . '/' . $module_path . '/constants.php'; if ( file_exists($constants_file) ) { kUtil::includeOnce($constants_file); } } } /** * Show next step screen * * @param string $error_message * @return void */ function Done($error_message = null) { if ( isset($error_message) ) { $this->errorMessage = $error_message; } include_once (FULL_PATH . '/' . REL_PATH . '/install/incs/install.tpl'); if ( isset($this->Application) ) { $this->Application->Done(); } exit; } function ConnectToDatabase() { include_once FULL_PATH . '/core/kernel/db/i_db_connection.php'; include_once FULL_PATH . '/core/kernel/db/db_connection.php'; $required_keys = Array ('DBType', 'DBUser', 'DBName'); foreach ($required_keys as $required_key) { - if (!$this->toolkit->getSystemConfig('Database', $required_key)) { + if (!$this->toolkit->systemConfig->get($required_key, 'Database')) { // one of required db connection settings missing -> abort connection return false; } } - $this->Conn = new kDBConnection($this->toolkit->getSystemConfig('Database', 'DBType'), Array(&$this, 'DBErrorHandler')); - $this->Conn->setup( $this->toolkit->systemConfig ); + $this->Conn = new kDBConnection($this->toolkit->systemConfig->get('DBType', 'Database'), Array(&$this, 'DBErrorHandler')); + $this->Conn->setup($this->toolkit->systemConfig->getData()); // setup toolkit too $this->toolkit->Conn =& $this->Conn; return !$this->Conn->hasError(); } /** * Checks if core is already installed * * @return bool */ function AlreadyInstalled() { - $table_prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix'); + $table_prefix = $this->toolkit->systemConfig->get('TablePrefix', 'Database'); $settings_table = $this->TableExists('ConfigurationValues') ? 'ConfigurationValues' : 'SystemSettings'; $sql = 'SELECT VariableValue FROM ' . $table_prefix . $settings_table . ' WHERE VariableName = "InstallFinished"'; return $this->TableExists($settings_table) && $this->Conn->GetOne($sql); } function CheckDatabase($check_installed = true) { // perform various check type to database specified // 1. user is allowed to connect to database // 2. user has all types of permissions in database // 3. database environment settings met minimum requirements - if (mb_strlen($this->toolkit->getSystemConfig('Database', 'TablePrefix')) > 7) { + if (mb_strlen($this->toolkit->systemConfig->get('TablePrefix', 'Database')) > 7) { $this->errorMessage = 'Table prefix should not be longer than 7 characters'; return false; } // connect to database $status = $this->ConnectToDatabase(); if ($status) { // if connected, then check if all sql statements work $sql_tests[] = 'DROP TABLE IF EXISTS test_table'; $sql_tests[] = 'CREATE TABLE test_table(test_col mediumint(6))'; $sql_tests[] = 'LOCK TABLES test_table WRITE'; $sql_tests[] = 'INSERT INTO test_table(test_col) VALUES (5)'; $sql_tests[] = 'UPDATE test_table SET test_col = 12'; $sql_tests[] = 'UNLOCK TABLES'; $sql_tests[] = 'ALTER TABLE test_table ADD COLUMN new_col varchar(10)'; $sql_tests[] = 'SELECT * FROM test_table'; $sql_tests[] = 'DELETE FROM test_table'; $sql_tests[] = 'DROP TABLE IF EXISTS test_table'; foreach ($sql_tests as $sql_test) { $this->Conn->Query($sql_test); if ($this->Conn->getErrorCode() != 0) { $status = false; break; } } if ($status) { // if statements work & connection made, then check table existance if ($check_installed && $this->AlreadyInstalled()) { $this->errorMessage = 'An In-Portal Database already exists at this location'; return false; } $requirements_error = Array (); $db_check_results = $this->toolkit->CallPrerequisitesMethod('core/', 'CheckDBRequirements'); if ( !$db_check_results['version'] ) { $requirements_error[] = '- MySQL Version is below 5.0'; } if ( !$db_check_results['packet_size'] ) { $requirements_error[] = '- MySQL Packet Size is below 1 MB'; } if ( $requirements_error ) { $this->errorMessage = 'Connection successful, but following system requirements were not met:
' . implode('
', $requirements_error); return false; } } else { // user has insufficient permissions in database specified $this->errorMessage = 'Permission Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg(); return false; } } else { // was error while connecting if (!$this->Conn) return false; $this->errorMessage = 'Connection Error: ('.$this->Conn->getErrorCode().') '.$this->Conn->getErrorMsg(); return false; } return true; } /** * Checks if all passed tables exists * * @param string $tables comma separated tables list * @return bool */ function TableExists($tables) { - $prefix = $this->toolkit->getSystemConfig('Database', 'TablePrefix'); + $prefix = $this->toolkit->systemConfig->get('TablePrefix', 'Database'); $all_found = true; $tables = explode(',', $tables); foreach ($tables as $table_name) { $sql = 'SHOW TABLES LIKE "'.$prefix.$table_name.'"'; if (count($this->Conn->Query($sql)) == 0) { $all_found = false; break; } } return $all_found; } /** * Returns modules list found in modules folder * * @return Array */ function ScanModules() { static $modules = null; if ( !isset($modules) ) { // use direct include, because it's called before kApplication::Init, that creates class factory kUtil::includeOnce( KERNEL_PATH . kApplication::MODULE_HELPER_PATH ); $modules_helper = new kModulesHelper(); $modules = $modules_helper->getModules(); } return $modules; } /** * Virtually place module under "modules" folder or it won't be recognized during upgrade to 5.1.0 version * * @param string $name * @param string $path * @param string $version * @return string */ function getModulePath($name, $path, $version) { if ($name == 'Core') { // don't transform path for Core module return $path; } if (!preg_match('/^modules\//', $path)) { // upgrade from 5.0.x/1.0.x to 5.1.x/1.1.x return 'modules/' . $path; } return $path; } /** * Returns list of modules, that can be upgraded * */ function GetUpgradableModules() { $ret = Array (); foreach ($this->Application->ModuleInfo as $module_name => $module_info) { if ($module_name == 'In-Portal') { // don't show In-Portal, because it shares upgrade scripts with Core module continue; } $module_info['Path'] = $this->getModulePath($module_name, $module_info['Path'], $module_info['Version']); $upgrades_file = sprintf(UPGRADES_FILE, $module_info['Path'], 'sql'); if (!file_exists($upgrades_file)) { // no upgrade file continue; } $sqls = file_get_contents($upgrades_file); $versions_found = preg_match_all('/'.VERSION_MARK.'/s', $sqls, $regs); if (!$versions_found) { // upgrades file doesn't contain version definitions continue; } $to_version = end($regs[1]); $this_version = $this->toolkit->ConvertModuleVersion($module_info['Version']); if ($this->toolkit->ConvertModuleVersion($to_version) > $this_version) { // destination version is greather then current foreach ($regs[1] as $version) { if ($this->toolkit->ConvertModuleVersion($version) > $this_version) { $from_version = $version; break; } } $version_info = Array ( 'FromVersion' => $from_version, 'ToVersion' => $to_version, ); $ret[ strtolower($module_name) ] = array_merge($module_info, $version_info); } } return $ret; } /** * Returns content to show for current step * * @return string */ function GetStepBody() { $step_template = FULL_PATH.'/core/install/step_templates/'.$this->currentStep.'.tpl'; if (file_exists($step_template)) { ob_start(); include_once ($step_template); return ob_get_clean(); } return '{step template "'.$this->currentStep.'" missing}'; } /** * Parses step information file, cache result for current step ONLY & return it * * @return Array */ function &_getStepInfo() { static $info = Array('help_title' => null, 'step_title' => null, 'help_body' => null, 'queried' => false); if (!$info['queried']) { $fdata = file_get_contents($this->StepDBFile); $parser = xml_parser_create(); xml_parse_into_struct($parser, $fdata, $values, $index); xml_parser_free($parser); foreach ($index['STEP'] as $section_index) { $step_data =& $values[$section_index]; if ($step_data['attributes']['NAME'] == $this->currentStep) { $info['step_title'] = $step_data['attributes']['TITLE']; if (isset($step_data['attributes']['HELP_TITLE'])) { $info['help_title'] = $step_data['attributes']['HELP_TITLE']; } else { // if help title not set, then use step title $info['help_title'] = $step_data['attributes']['TITLE']; } $info['help_body'] = trim($step_data['value']); break; } } $info['queried'] = true; } return $info; } /** * Returns particular information abou current step * * @param string $info_type * @return string */ function GetStepInfo($info_type) { $step_info =& $this->_getStepInfo(); if (isset($step_info[$info_type])) { return $step_info[$info_type]; } return '{step "'.$this->currentStep.'"; param "'.$info_type.'" missing}'; } /** * Returns passed steps titles * * @param Array $steps * @return Array * @see kInstaller:PrintSteps */ function _getStepTitles($steps) { $fdata = file_get_contents($this->StepDBFile); $parser = xml_parser_create(); xml_parse_into_struct($parser, $fdata, $values, $index); xml_parser_free($parser); $ret = Array (); foreach ($index['STEP'] as $section_index) { $step_data =& $values[$section_index]; if (in_array($step_data['attributes']['NAME'], $steps)) { $ret[ $step_data['attributes']['NAME'] ] = $step_data['attributes']['TITLE']; } } return $ret; } /** * Returns current step number in active steps_preset. * Value can't be cached, because same step can have different number in different presets * * @return int */ function GetStepNumber() { return array_search($this->currentStep, $this->steps[$this->stepsPreset]) + 1; } /** * Returns step name to process next * * @return string */ function GetNextStep() { $next_index = $this->GetStepNumber(); if ($next_index > count($this->steps[$this->stepsPreset]) - 1) { return -1; } return $this->steps[$this->stepsPreset][$next_index]; } /** * Returns step name, that was processed before this step * * @return string */ function GetPreviousStep() { $next_index = $this->GetStepNumber() - 1; if ($next_index < 0) { $next_index = 0; } return $this->steps[$this->stepsPreset][$next_index]; } /** * Prints all steps from active steps preset and highlights current step * * @param string $active_tpl * @param string $passive_tpl * @return string */ function PrintSteps($active_tpl, $passive_tpl) { $ret = ''; $step_titles = $this->_getStepTitles($this->steps[$this->stepsPreset]); foreach ($this->steps[$this->stepsPreset] as $step_name) { $template = $step_name == $this->currentStep ? $active_tpl : $passive_tpl; $ret .= sprintf($template, $step_titles[$step_name]); } return $ret; } /** * Installation error handler for sql errors * * @param int $code * @param string $msg * @param string $sql * @return bool * @access private */ function DBErrorHandler($code, $msg, $sql) { $this->errorMessage = 'Query:
'.htmlspecialchars($sql, ENT_QUOTES, 'UTF-8').'
execution result is error:
['.$code.'] '.$msg; return true; } /** * Installation error handler * * @param int $errno * @param string $errstr * @param string $errfile * @param int $errline * @param Array|string $errcontext */ function ErrorHandler($errno, $errstr, $errfile = '', $errline = 0, $errcontext = '') { if ($errno == E_USER_ERROR) { // only react on user fatal errors $this->Done($errstr); } } /** * Checks, that given button should be visible on current installation step * * @param string $name * @return bool */ function buttonVisible($name) { $button_visibility = Array ( 'continue' => $this->GetNextStep() != -1 || ($this->stepsPreset == 'already_installed'), 'refresh' => in_array($this->currentStep, Array ('sys_requirements', 'check_paths', 'security')), 'back' => in_array($this->currentStep, Array (/*'select_license',*/ 'download_license', 'select_domain')), ); if ($name == 'any') { foreach ($button_visibility as $button_name => $button_visible) { if ($button_visible) { return true; } } return false; } return array_key_exists($name, $button_visibility) ? $button_visibility[$name] : true; } } Property changes on: branches/5.2.x/CREDITS ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/CREDITS:r15962 Property changes on: branches/5.2.x/README ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/README:r15962 Property changes on: branches/5.2.x/index.php ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/index.php:r15962 Property changes on: branches/5.2.x/LICENSES ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/LICENSES:r15962 Property changes on: branches/5.2.x/INSTALL ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/INSTALL:r15962 Property changes on: branches/5.2.x/COPYRIGHT ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/COPYRIGHT:r15962 Property changes on: branches/5.2.x/.htaccess ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x/.htaccess:r15962 Property changes on: branches/5.2.x ___________________________________________________________________ Modified: svn:mergeinfo Merged /in-portal/branches/5.3.x:r15962