Location: PHPKode > projects > Utopia CMS > utopia/core/kernel.php
<?php
/*
	+------------------------------------------------------------------------------+
	| Utopia : Web Portal System                                                   |
	+------------------------------------------------------------------------------+
	| Copyright (c) 2003-2004 Olivier BICHLER                                      |
	+------------------------------------------------------------------------------+
	| This program is free software; you can redistribute it and/or modify it      |
	| under the terms of the GNU General Public License as published by the Free   |
	| Software Foundation; either version 2 of the License, or (at your option)    |
	| any later version.                                                           |
	|                                                                              |
	| This program is distributed in the hope that it will be useful, but WITHOUT  |
	| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or        |
	| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for     |
	| more details.                                                                |
	|                                                                              |
	| You should have received a copy of the GNU General Public License along with |
	| this program; if not, write to the Free Software Foundation, Inc., 675 Mass  |
	| Ave, Cambridge, MA 02139, USA.                                               |
	+------------------------------------------------------------------------------+
	| Author : Olivier BICHLER <hide@address.com>                       |
	+------------------------------------------------------------------------------+
*/

/**
 * Noyau d'Utopia
 *
 * Dans ce fichier sont déclaré les objets de base constituant le noyau d'Utopia. Ces objets sont nécessaires aux modules et librairies du portail.
 * Certains traitements sont également effectués dans ce fichier :
 * - Suppression des variables PHP inutiles ($_ENV et $_REQUEST).
 * - Emulation de l'option PHP magic_quotes_gpc à OFF.
 * - Emulation de l'option PHP magic_quotes_runtime à OFF.
 * - Emulation de l'option PHP register_globals à OFF.
 * @package Utopia
 * @subpackage Kernel
*/

defined('INC') or exit;

if (version_compare(phpversion(), '6.0.0-dev', '<')) {
	// Emulate magic_quotes_gpc to off.
	if (get_magic_quotes_gpc()) {
		$in = array(&$_GET, &$_POST, &$_COOKIE);

		while (list($k, $v) = each($in)) {
			foreach ($v as $key => $val) {
				if (is_array($val))
					$in[] = &$in[$k][$key];
				else
					$in[$k][$key] = stripslashes($val);
			}
		}

		unset($in);
	}

	if (get_magic_quotes_runtime())
		set_magic_quotes_runtime(0);

	// Emulate register_globals to off.
	if (ini_get('register_globals')) {
		foreach (array_keys($GLOBALS) as $var) {
			if ($var != 'GLOBALS'
			 && $var != '_SERVER'
			 && $var != '_GET'
			 && $var != '_POST'
			 && $var != '_COOKIE'
			 && $var != '_FILES')
				unset($GLOBALS[$var], $$var);
		}
	}
}

// Unset unused variables (make free memory).
unset($_ENV, $_REQUEST); // Use getenv() and $_GET, $_POST, $_COOKIE instead.
// Utiliser $_REQUEST ne permet pas d'identifier la provenance des données et peut rendre le site particulièrement vulnérable a des attaques du type Cross-Site Request Forgeries

ignore_user_abort(TRUE);

// Extensions
// require_once('kconfig-xml.php');		// Import/export configuration from/to XML format.
// require_once('klang-xml.php');		// Import/export language from/to XML format.
// require_once('klang-gettext.php');	// Use PHP gettext extension for language.

/**
 * Objet de base du noyau qui permet le traitement des messages (notices, erreurs, debug...) et leur journalisation.
 *
 * @package Utopia
 * @subpackage Kernel
*/
class KError {
	/**
	 * Chemin absolu vers le répertoire contenant le kernel.
	 * @var string
	*/
	public $corePath;

	/**
	 * Chemin vers le journal (fichier log).
	 * @var string
	*/
	public $logFile;

	/**
	 * Constructeur initialisant {@link KError::$logFile} et {@link KError::$corePath}.
	 * @param string $logFile Chemin vers le journal (fichier log)
	*/
	function __construct($logFile) {
		$corePath = dirname(__FILE__);

		// dirname isn't enough, because paths like "C:\www\dtd/log.dtd" aren't supported by the XML parser for example.
		if (DIRECTORY_SEPARATOR !== '/')
			$corePath = str_replace(DIRECTORY_SEPARATOR, '/', $corePath);

		if (substr($corePath, -1) !== '/')
			$corePath.= '/';

		$this -> corePath = $corePath;
		$this -> logFile = realpath(dirname($logFile)).DIRECTORY_SEPARATOR.basename($logFile);
		// realpath est nécessaire quand addToLog ou error est appelé depuis un destructeur.
	}

	public static function detectIp() {
		// Gets the default ip sent by the user
		$directIp = NULL;

		if (!empty($_SERVER['REMOTE_ADDR']))
			$directIp = $_SERVER['REMOTE_ADDR'];

		// Gets the proxy ip sent by the user
		// Important : l'IP donnée par supposé proxy n'est pas fiable du tout.
		// Il ne s'agit que d'un header ajouté à la requête !
		$proxyIp = NULL;

		if (!empty($_SERVER['HTTP_X_FORWARDED_FOR']))
			$proxyIp = $_SERVER['HTTP_X_FORWARDED_FOR'];
		elseif (!empty($_SERVER['HTTP_X_FORWARDED']))
			$proxyIp = $_SERVER['HTTP_X_FORWARDED'];
		elseif (!empty($_SERVER['HTTP_FORWARDED_FOR']))
			$proxyIp = $_SERVER['HTTP_FORWARDED_FOR'];
		elseif (!empty($_SERVER['HTTP_FORWARDED']))
			$proxyIp = $_SERVER['HTTP_FORWARDED'];
		elseif (!empty($_SERVER['HTTP_CLIENT_IP']))
			$proxyIp = $_SERVER['HTTP_CLIENT_IP'];
		elseif (!empty($_SERVER['HTTP_X_COMING_FROM']))
			$proxyIp = $_SERVER['HTTP_X_COMING_FROM'];
		elseif (!empty($_SERVER['HTTP_COMING_FROM']))
			$proxyIp = $_SERVER['HTTP_COMING_FROM'];
		// HTTP_VIA = IP of the proxy (= REMOTE_ADDR)

		if ($proxyIp === NULL)
			// IP without proxy
			return $directIp;
		elseif (preg_match('/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})/', $proxyIp, $match)) {
			// IP behind a proxy
			$patternIp = array(
				// Bad IPs
				'/^0\..*/', // Don't exists
				'/^127\.0\.0\.1/', // Local IP

				// Intranet reserved IPs
				'/^10\..*/',
				'/^172\.1[6-9]\..*/',
				'/^172\.2[0-9]\..*/',
				'/^172\.3[0-1]\..*/',
				'/^192\.168\..*/',

				// Reserved IPs (D and E class)
				'/^22[4-9]\..*/',
				'/^2[3-5][0-9]\..*/'
			);

			return preg_replace($patternIp, $directIp, trim($match[1]));
		}
		else
			// Can't define IP: there is a proxy but we don't have information about the true IP
			return NULL;
	}

	/**
	 * Ajoute une entrée dans le journal.
	 * @param string $entryType Type de message (peut prendre les valeurs "notice", "error", "client-error", "fatal-error", "debug", "access")
	 * @param array $debugBackTrace Trace généré par la fonction PHP debug_backtrace()
	 * @param string $stringId ID de la chaîne du message
	 * @param string $stringSection Section où se trouve la chaîne ayant l'ID {@link $stringId}
	*/
	public function addToLog($entryType, $debugBackTrace, $stringId, $stringSection = 'Kernel') {
		$log = $this -> detectIp();

		if (!empty($_SERVER['REMOTE_ADDR']) && $log != $_SERVER['REMOTE_ADDR'])
			$log.= ' '.$_SERVER['REMOTE_ADDR'];

		$log.= ' - ['.gmdate('r').'] '.$entryType.': '.$stringId.' ('.$stringSection.')'.PHP_EOL;

		if (is_array($debugBackTrace)) {
			if (isset($debugBackTrace[0]['file']))
				$log.= $debugBackTrace[0]['file'];

			if (isset($debugBackTrace[0]['line']))
				$log.= ' ('.$debugBackTrace[0]['line'].') ';

			if (isset($debugBackTrace[0]['class']))
				$log.= $debugBackTrace[0]['class'].'::';

			if (isset($debugBackTrace[0]['function'])) {
				$log.= $debugBackTrace[0]['function'].'()';

				// Security risk : sometimes password are in arguments... (connect() function of LibUser !)
//					if (!empty($debugBackTrace[0]['args']))
//						$log.= PHP_EOL.print_r($debugBackTrace[0]['args'], TRUE);
			}

			$log.= PHP_EOL;
		}
		elseif ($debugBackTrace !== NULL)
			$log.= $debugBackTrace.PHP_EOL;

		return file_put_contents($this -> logFile, $log, FILE_APPEND);
	}

	/**
	 * Génère une erreur dans le journal et poursuit l'exécution.
	 * @param array $debugBackTrace Trace généré par la fonction PHP debug_backtrace()
	 * @param string $stringId ID de la chaîne du message
	 * @param string $stringSection Section où se trouve la chaîne ayant l'ID {@link $stringId}
	*/
	public function error($debugBackTrace, $stringId, $stringSection = 'Kernel') {
		$this -> addToLog('error', $debugBackTrace, $stringId, $stringSection);
	}

	/**
	 * Génère une erreur fatale dans le journal et arrête l'exécution.
	 * @param array $debugBackTrace Trace généré par la fonction PHP debug_backtrace()
	 * @param string $stringId ID de la chaîne du message
	 * @param string $stringSection Section où se trouve la chaîne ayant l'ID {@link $stringId}
	*/
	public function fatalError($debugBackTrace, $stringId, $stringSection = 'Kernel') {
		$this -> addToLog('fatal-error', $debugBackTrace, $stringId, $stringSection);

		// On essaie de rendre l'erreur présentable avant de l'envoyer.
		@ob_end_clean();	// Pourrait générer une E_NOTICE si aucune buffurisation n'a été mise en route
		@header('Content-Type: text/plain');	// Pourrait générer une E_NOTICE si les headers ont déjà été envoyés
		@header('Content-Encoding: identity');

		exit("Fatal internal error, script execution prematurely ended.\nPlease check CMS log file for more details.");
	}
}

/**
 * Objet de base du noyau qui permet la gestion de la configuration du système
 *
 * @package Utopia
 * @subpackage Kernel
*/
class KConfig extends KError {
	/**
	 * Chemin vers le répertoire contenant les fichiers de configuration.
	 * @var string
	*/
	public $configPath;

	/**
	 * Tableau contenant tous les paramètres de configuration chargés en mémoire.
	 * @var array
	*/
	protected $params = array();

	/**
	 * Constructeur initialisant {@link KError::$logFile} et {@link KConfig::$configPath}.
	 * @param string $logFile Chemin vers le journal (fichier log)
	 * @param string $configPath Chemin vers le répertoire contenant les fichiers de configuration
	*/
	function __construct($logFile, $configPath) {
		parent::__construct($logFile);
		$this -> configPath = $configPath;
	}

	/**
	 * Ajoute une section à la configuration.
	 * @param string $section Nom de la section à ajouter
	*/
	public function addConfigSection($section) {
		$this -> params[$section] = array();
	}

	/**
	 * Supprime une section de la configuration.
	 * @param string $section Nom de la section à supprimer
	*/
	public function deleteConfigSection($section) {
		unset($this -> params[$section]);
	}

	/**
	 * Liste de toutes les sections de configuration chargées.
	 * @return array Tableau contenant tous les noms de sections
	*/
	public function getConfigSections() {
		return array_keys($this -> params);
	}

	/**
	 * Renvoie la valeur d'un paramètre de configuration.
	 * @param string $name Nom du paramètre
	 * @param string $section Nom de la section où se trouve le paramètre
	 * @return integer|float|string|boolean Valeur du paramètre
	*/
	public function getParam($name, $section = 'Kernel') {
		if (isset($this -> params[$section][$name]))
			return $this -> params[$section][$name];
		else {
			if (isset($this -> params[$section]))
				$this -> error(debug_backtrace(), 'UndefinedConfigParamXInSectionY,'.$name.','.$section);
			else
				$this -> fatalError(debug_backtrace(), 'SectionXFromConfigNotLoaded,'.$section);

			return NULL;
		}
	}

	/**
	 * Liste de tous les paramètres contenus dans une section de configuration.
	 * @param string $section Nom de la section
	 * @return array Tableau associatif contenant tous les couples "Nom du paramètre => Valeur"
	*/
	public function getSectionParams($section) {
		if (isset($this -> params[$section]))
			return $this -> params[$section];
		else {
			$this -> error(debug_backtrace(), 'SectionXFromConfigNotLoaded,'.$section);
			return NULL;
		}
	}

	/**
	 * Vérifie si une section de configuration a été chargée.
	 * @param string $section Nom de la section
	 * @return boolean Renvoie TRUE si la section de configuration a été chargée, FALSE sinon
	*/
	public function isConfigSectionLoaded($section) {
		return isset($this -> params[$section]);
	}

	/**
	 * Charge un fichier de configuration.
	 * @param string $fileName Nom du fichier de configuration (il doit se trouver dans le répertoire {@link KConfig::$configPath})
	 * @param null|array $sectionsToLoad Liste des sections à charger (NULL = charger toutes les sections)
	 * @param boolean $ignoreOverLoading Si une section de configuration a déjà été chargée auparavant, la mise à TRUE de cet argument écrasera les paramètres de configuration de la section déjà chargés en mémoire au profit des nouveaux paramètres
	 * @return boolean Renvoie TRUE si le chargement du fichier de configuration a été correctement chargé, FALSE sinon
	*/
	public function loadConfig($fileName, $sectionsToLoad = NULL, $ignoreOverLoading = FALSE) {
		$config = include($this -> configPath.$fileName);

		if (!$config)
			$this -> fatalError(debug_backtrace(), 'ConfigFileXDoesNotExist,'.$this -> configPath.$fileName);

		if (is_array($config)) {
			if ($sectionsToLoad !== NULL) {
				foreach (array_keys($config) as $sectionId) {
					if (!in_array($sectionId, $sectionsToLoad))
						unset($config[$sectionId]);
				}
			}

			foreach ($config as $sectionId => $params) {
				if (!isset($this -> params[$sectionId])) {
					$this -> params[$sectionId] = $params;

					if ($this instanceof Kernel && $sectionId === 'Kernel')
						$initialize = TRUE;
				}
				elseif ($ignoreOverLoading)
					$this -> params[$sectionId] = array_merge($this -> params[$sectionId], $params);
				else
					$this -> error(debug_backtrace(), 'ConfigSectionXAlreadyLoaded,'.$sectionId);
			}

			if (isset($initialize))
				$this -> initialize();

			return TRUE;
		}
		else
			$this -> error(debug_backtrace(), 'ConfigFileXNotValid,'.$this -> configPath.$fileName);

		return FALSE;
	}

	/**
	 * Sauvegarde la configuration chargée en mémoire dans un fichier.
	 * @param string $fileName Nom du fichier de configuration (il doit se trouver dans le répertoire {@link KConfig::$configPath})
	 * @param null|array $sectionsToSave Liste des sections à sauvegarder (NULL = sauvegarder toutes les sections)
	 * @return integer Longueur du fichier écrit, en octets
	*/
	public function saveConfig($fileName, $sectionsToSave = NULL) {
		$config = array();

		if ($sectionsToSave === NULL)
			$sectionsToSave = array_keys($this -> params);

		foreach ($sectionsToSave as $sectionId) {
			if (isset($this -> params[$sectionId])) {
				$config[$sectionId] = $this -> params[$sectionId];
				ksort($config[$sectionId], SORT_STRING);
			}
			else
				$this -> error(debug_backtrace(), 'SectionXFromConfigNotLoaded,'.$sectionId);
		}

		ksort($config, SORT_STRING);

		$res = file_put_contents($this -> configPath.$fileName, "<?php\n// last-modified: ".gmdate('Y-m-d\TH:i:s\Z')."\n\nreturn ".var_export($config, TRUE).";\n?>");

		if ($res !== FALSE)
			$this -> addToLog('notice', NULL, 'ConfigXSaved,'.$fileName);
		else
			$this -> error(debug_backtrace(), 'UnableToSaveConfigX,'.$fileName);

		return $res;
	}

	/**
	 * Ajoute ou modifie un paramètre de configuration.
	 * @param string $name Nom du paramètre
	 * @param integer|float|string|boolean $value Nouvelle valeur du paramètre
	 * @param string $section Nom de la section où se trouve le paramètre
	 * @return boolean Renvoie TRUE si réussite et FALSE en cas d'échec
	*/
	public function setParam($name, $value, $section = 'Kernel') {
		if (isset($this -> params[$section])) {
			if (is_scalar($value)) {
				$this -> params[$section][$name] = $value;
				return TRUE;
			}
			elseif ($value === NULL) { // NULL is not considered as a scalar.
				unset($this -> params[$section][$name]);
				return TRUE;
			}
			else
				$this -> error(debug_backtrace(), 'ConfigParamXMustBeAScalar,'.$name);
		}
		else
			$this -> error(debug_backtrace(), 'SectionXFromConfigNotLoaded,'.$section);

		return FALSE;
	}
}

/**
 * Objet de base du noyau qui permet la gestion de la langue
 *
 * @package Utopia
 * @subpackage Kernel
*/
class KLang extends KConfig {
	/**
	 * Chemin vers le répertoire contenant les fichiers de langue.
	 * @var string
	*/
	public $langPath;

	/**
	 * Tableau contenant toutes les chaînes de caractère chargées en mémoire.
	 * @var array
	*/
	protected $strings = array();

	/**
	 * Constructeur initialisant {@link KError::$logFile}, {@link KConfig::$configPath} et {@link KLang::$langPath}.
	 * @param string $logFile Chemin vers le journal (fichier log)
	 * @param string $configPath Chemin vers le répertoire contenant les fichiers de configuration
	 * @param string $langPath Chemin vers le répertoire contenant les fichiers de langue
	*/
	function __construct($logFile, $configPath, $langPath) {
		parent::__construct($logFile, $configPath);
		$this -> langPath = $langPath;
	}

	/**
	 * Ajoute une section de langue.
	 * @param string $section Nom de la section à ajouter
	*/
	public function addLangSection($section) {
		$this -> strings[$section] = array();
	}

	/**
	 * Supprime une section de langue.
	 * @param string $section Nom de la section à supprimer
	*/
	public function deleteLangSection($section) {
		unset($this -> strings[$section]);
	}

	/**
	 * Liste de toutes les sections de langue chargées.
	 * @return array Tableau contenant tous les noms de sections
	*/
	public function getLangSections() {
		return array_keys($this -> strings);
	}

	/**
	 * Renvoie la chaîne de caractère correspondante à une ID
	 * @param string $id ID de la chaîne, suivi éventuellement de bouts de chaîne à insérer dans celle-ci, séparés par des virgules. Exemple : "ThereAreXLineFoundInFileY,5,core.php" où la valeur de l'ID "ThereAreXLineFoundInFileY" sera (dans le fichier de langue) "Il y a %1$d lignes trouvées dans le fichier %2$s". Le format utilisé est exactement le même que pour la fonction PHP printf(), qui est utilisée en interne dans cette fonction.
	 * @param string $section Nom de la section où se trouve la chaîne
	 * @return string Chaîne de caractère
	*/
	public function getString($id, $section = 'Kernel') {
		if (isset($this -> strings[$section][$id]))
			return $this -> strings[$section][$id];
		else {
			$args = preg_split('/(?<!\\\\),/', $id);

			if (isset($this -> strings[$section][$args[0]])) {
				$args[0] = $this -> strings[$section][$args[0]];
				return stripcslashes(call_user_func_array('sprintf', $args));
			}
			elseif (isset($this -> strings[$section]))
				$this -> error(debug_backtrace(), 'UndefinedLangStringXInSectionY,'.addcslashes($args[0], '\\,').','.$section);
			else
				$this -> fatalError(debug_backtrace(), 'SectionXFromLangNotLoaded,'.$section);
		}

		return $id;
	}

	/**
	 * Liste de tous les chaînes contenues dans une section de langue.
	 * @param string $section Nom de la section
	 * @return array Tableau associatif contenant tous les couples "ID => Chaîne"
	*/
	public function getSectionStrings($section) {
		if (isset($this -> strings[$section]))
			return $this -> strings[$section];
		else {
			$this -> error(debug_backtrace(), 'SectionXFromLangNotLoaded,'.$section);
			return NULL;
		}
	}

	/**
	 * Vérifie si une section de langue a été chargée.
	 * @param string $section Nom de la section
	 * @return boolean Renvoie TRUE si la section de langue a été chargée, FALSE sinon
	*/
	public function isLangSectionLoaded($section) {
		return isset($this -> strings[$section]);
	}

	/**
	 * Charge un fichier de langue.
	 * @param string $fileName Nom du fichier de langue (il doit se trouver dans le répertoire {@link KLang::$langPath})
	 * @param null|array $sectionsToLoad Liste des sections à charger (NULL = charger toutes les sections)
	 * @param boolean $ignoreOverLoading Si une section de langue a déjà été chargée auparavant, la mise à TRUE de cet argument écrasera les chaînes de langue de la section déjà chargés en mémoire au profit des nouvelles chaînes
	 * @return boolean Renvoie TRUE si le chargement du fichier de langue a été correctement chargé, FALSE sinon
	*/
	public function loadLang($fileName, $sectionsToLoad = NULL, $ignoreOverLoading = FALSE) {
		$lang = include($this -> langPath.$fileName);

		if (!$lang)
			$this -> fatalError(debug_backtrace(), 'LangFileXDoesNotExist,'.$this -> langPath.$fileName);

		if (is_array($lang)) {
			if ($sectionsToLoad !== NULL) {
				foreach (array_keys($lang) as $sectionId) {
					if (!in_array($sectionId, $sectionsToLoad))
						unset($lang[$sectionId]);
				}
			}

			foreach ($lang as $sectionId => $strings) {
				if (!isset($this -> strings[$sectionId]))
					$this -> strings[$sectionId] = $strings;
				elseif ($ignoreOverLoading)
					$this -> strings[$sectionId] = array_merge($this -> strings[$sectionId], $strings);
				else
					$this -> error(debug_backtrace(), 'LangSectionXAlreadyLoaded,'.$sectionId);
			}

			return TRUE;
		}
		else
			$this -> error(debug_backtrace(), 'LangFileXNotValid,'.$this -> langPath.$fileName);

		return FALSE;
	}

	/**
	 * Sauvegarde la langue chargée en mémoire dans un fichier.
	 * @param string $fileName Nom du fichier de langue (il doit se trouver dans le répertoire {@link KLang::$langPath})
	 * @param null|array $sectionsToSave Liste des sections à sauvegarder (NULL = sauvegarder toutes les sections)
	 * @return integer Longueur du fichier écrit, en octets
	*/
	public function saveLang($fileName, $sectionsToSave = NULL) {
		$lang = array();

		if ($sectionsToSave === NULL)
			$sectionsToSave = array_keys($this -> strings);

		foreach ($sectionsToSave as $sectionId) {
			if (isset($this -> strings[$sectionId])) {
				$lang[$sectionId] = $this -> strings[$sectionId];
				ksort($lang[$sectionId], SORT_STRING);
			}
			else
				$this -> error(debug_backtrace(), 'SectionXFromLangNotLoaded,'.$sectionId);
		}

		ksort($lang, SORT_STRING);

		$res = file_put_contents($this -> langPath.$fileName, "<?php\n// last-modified: ".gmdate('Y-m-d\TH:i:s\Z')."\n\nreturn ".var_export($lang, TRUE).";\n?>");

		if ($res !== FALSE)
			$this -> addToLog('notice', NULL, 'LangXSaved,'.$fileName);
		else
			$this -> error(debug_backtrace(), 'UnableToSaveLangX,'.$fileName);

		return $res;
	}

	/**
	 * Ajoute ou modifie une chaîne de langue
	 * @param string $id ID de la chaîne de caractère
	 * @param string $value Chaîne de caractère
	 * @param string $section Nom de la section où se trouve la chaîne
	 * @return boolean Renvoie TRUE si réussite et FALSE en cas d'échec
	*/
	public function setString($id, $value, $section = 'Kernel') {
		if (isset($this -> strings[$section])) {
			if (is_string($value)) {
				$this -> strings[$section][$id] = $value;
				return TRUE;
			}
			elseif ($value === NULL) { // NULL is not considered as a string.
				unset($this -> strings[$section][$id]);
				return TRUE;
			}
			else
				$this -> error(debug_backtrace(), 'LangStringXMustBeAString,'.$id);
		}
		else
			$this -> error(debug_backtrace(), 'SectionXFromLangNotLoaded,'.$section);

		return FALSE;
	}
}

/**
 * Le noyau d'Utopia, qui gère le chargement des librairies et modules, initialise la configuration et comprend des fonctions de traitement des dates et des nombres localisés.
 *
 * @package Utopia
 * @subpackage Kernel
*/
class Kernel extends KLang {
	const Version = 1.006;
	const WebHome = 'https://sourceforge.net/projects/utopiacms/';
	const WebSupport = 'https://sourceforge.net/forum/?group_id=218754';
	const WebUpdate = 'https://sourceforge.net/project/platformdownload.php?group_id=218754';

	public $libraries = array();
	public $modules = array();

	/**
	 * Constructeur initialisant {@link KError::$logFile}, {@link KConfig::$configPath} et {@link KLang::$langPath}.
	 * @param string $logFile Chemin vers le journal (fichier log)
	 * @param string $configPath Chemin vers le répertoire contenant les fichiers de configuration
	 * @param string $langPath Chemin vers le répertoire contenant les fichiers de langue
	*/
	function __construct($logFile, $configPath, $langPath) {
		parent::__construct($logFile, $configPath, $langPath);

//		if (file_exists($this -> corePath.'_PERSISTENT'))
//			$GLOBALS['_PERSISTENT'] = unserialize(file_get_contents($this -> corePath.'_PERSISTENT'));
//		else
//			$GLOBALS['_PERSISTENT'] = array();
	}

	/**
	 * Charge une librairie.
	 * @param string $libName Nom de la librairie (correspond au nom du fichier sans l'extension ".php")
	 * @param null|string $path Chemin vers le répertoire contenant la librairie (NULL = chemin par défaut contenu dans la configuration)
	 * @return boolean Renvoie TRUE si réussite et FALSE en cas d'échec
	*/
	public function loadLib($libName, $path = NULL) {
		if (!in_array($libName, $this -> libraries)) {
			if ($path === NULL)
				$path = $this -> corePath.'lib/';

			$fileName = $path.$libName.'.php';

			if (include($fileName))
				$this -> libraries[] = $libName;
			else {
				$this -> error(debug_backtrace(), 'LibraryXDoesNotExist,'.$libName);
				return FALSE;
			}
		}

		return TRUE;
	}

	/**
	 * Charge un module.
	 * @param string $modName Nom du module (correspond au nom du répertoire du module, qui doit contenir un fichier "index.php" qui est le fichier principal du module)
	 * @param null|string $path Chemin vers le répertoire contenant le module (NULL = chemin par défaut contenu dans la configuration)
	 * @return boolean Renvoie TRUE si réussite et FALSE en cas d'échec
	*/
	public function loadMod($modName, $path = NULL) {
		if (!in_array($modName, $this -> modules)) {
			if ($path === NULL)
				$path = $this -> corePath.'mod/';

			$fileName = $path.$modName.'/index.php';

			if (include($fileName))
				$this -> modules[] = $modName;
			else {
				$this -> error(debug_backtrace(), 'ModuleXDoesNotExist,'.$modName);
				return FALSE;
			}
		}

		return TRUE;
	}

	/**
	 * Formate un nombre pour l'affichage
	 *
	 * Cette fonction remplace la fonction PHP number_format() qui dépend de la configuration locale du système.
	 * @param integer|float $number Nombre. Identique au premier argument de strftime()
	 * @param null|integer $decimals Nombre de décimales à afficher. Identique au deuxième argument de strftime()
	 * @return string Chaîne de caractère correspondant au nombre formaté
	*/
	public function numberFormat($number, $decimals = NULL) {
		return number_format($number, $decimals, $this -> getString('NumberDecimalSep', 'LocaleConfig'), $this -> getString('NumberThousandsSep', 'LocaleConfig'));
	}

	/**
	 * Formate une date pour l'affichage
	 *
	 * Cette fonction remplace la fonction PHP strftime() qui dépend de la configuration locale du système.
	 * @param string $format Format de la date (le même que celui utilisé par la fonction PHP strftime()). Identique au premier argument de strftime()
	 * @param null|integer $timestamp Le timestamp UNIX (ce timestamp est un entier long, contenant le nombre de secondes entre le début de l'époque UNIX (1er Janvier 1970) et le temps spécifié). Identique au deuxième argument de strftime()
	 * @return null|integer Décalage horaire, en secondes
	*/
	public function dateFormat($format, $timestamp = NULL, $timeZone = NULL) {
		if ($timestamp === NULL)
			$timestamp = time() - date('Z');

		if ($timeZone === NULL)
			$timeZone = $this -> getParam('DefaultTimeZone');

		$timestamp+= $timeZone;

		// Changing to locale (do not change the order !)
		$days = explode(',', $this -> getString('Days', 'LocaleConfig'));
		$months = explode(',', $this -> getString('Months', 'LocaleConfig'));
		$amPm = explode(',', $this -> getString('AmPmRepresentation', 'LocaleConfig'));
		$date = explode(',', date('w,n,G,W,Y,d', $timestamp)); // Avoid multiple call to date() !

		if ($date[0] == 0)
			$date[0] = 7;

		$tZ = sprintf('%02d:%02d', (int) abs($timeZone / 3600), (int) (($timeZone % 3600) / 60));

		if ($timeZone > 0)
			$tZ = '+'.$tZ;
		else
			$tZ = '-'.$tZ;

		$strReplace = array(
			'%c' => $this -> getString('DatetimeFormat', 'LocaleConfig'),
			'%x' => $this -> getString('DateFormat', 'LocaleConfig'),
			'%X' => $this -> getString('TimeFormat', 'LocaleConfig'),
			'%a' => $days[$date[0] - 1]{0}.$days[$date[0] - 1]{1}.$days[$date[0] - 1]{2},
			'%A' => $days[$date[0] - 1],
			'%b' => $months[$date[1] - 1]{0}.$months[$date[1] - 1]{1}.$months[$date[1] - 1]{2},
			'%B' => $months[$date[1] - 1],
			'%p' => $amPm[(int) ($date[2] / 13)],
			'%Z' => $tZ,
		);

		$format = str_replace(array_keys($strReplace), array_values($strReplace), $format);

		if (!strftime('%C', $timestamp)) {
			// See http://www.opengroup.org/onlinepubs/009695399/functions/strftime.html.
			$Gparam = $date[4];

			if ($date[1] == 1 && ($date[3] == 52 || $date[3] == 53))
				$Gparam--;
			elseif ($date[1] == 12 && $date[3] == 1)
				$Gparam++;

			$Gparam = (string) $Gparam;

			$strReplace = array(
				// Support for Issue 6 of The Open Group Base Specifications (except %n and %t, which are nothing to do with date...).
				'%C' => (int) ($date[4] / 100),
				'%D' => '%m/%d/%y',
				'%e' => sprintf('% 2d', $date[5]),
				'%h' => $months[$date[1] - 1]{0}.$months[$date[1] - 1]{1}.$months[$date[1] - 1]{2}, // = %b but %b are already replaced.
				'%r' => '%I:%M:%S%p',
				'%R' => '%H:%M',
				'%T' => '%H:%M:%S',
				'%z' => str_replace(':', '', $tZ),

				// Other params not supported on Windows
				'%F' => '%Y-%m-%d',
				'%g' => $Gparam[2].$Gparam[3],
				'%G' => $Gparam,
				'%u' => $date[0],
				'%V' => $date[3],
			);

			$format = str_replace(array_keys($strReplace), array_values($strReplace), $format);
		}

		return strftime($format, $timestamp);
	}

	/**
	 * Renvoie le timestamp d'une date formatté au format RFC 3339
	 *
	 * @param string $date Date au format RFC 3339
	 * @return integer Timestamp UNIX
	*/
	public function getTimestamp($date) {
		// RFC 3339 - Date and Time on the Internet: Timestamps (based on ISO 8601)
		if (preg_match('/^([0-9]{4})\-([0-9]{2})\-([0-9]{2})((T| )([0-9]{2})\:([0-9]{2})\:([0-9]{2})(\.[0-9]+)?(Z|(\+|\-)([0-9]{2})\:([0-9]{2}))?)?$/D', $date, $regs)) {
			$time = 0;

			if (!checkdate($regs[2], $regs[3], $regs[1]))
				return FALSE;

			if (empty($regs[4]))
				$time+= mktime(0, 0, 0, $regs[2], $regs[3], $regs[1]);
			else {
				if ($regs[6] >= 0 && $regs[6] < 24 && $regs[7] >= 0 && $regs[7] < 60 && $regs[8] >= 0 && $regs[8] < 60)
					$time+= mktime($regs[6], $regs[7], $regs[8], $regs[2], $regs[3], $regs[1]);
				else
					return FALSE;
			}

			if (!empty($regs[9]))
				$time+= (float) $regs[9];

			if (!empty($regs[10]) && $regs[10] != 'Z') {
				if ($regs[12] >= 0 && $regs[12] < 24 && $regs[13] >= 0 && $regs[13] < 60)
					$time-= (int) ($regs[11].($regs[12] * 3600 + $regs[13] * 60));
				else
					return FALSE;
			}

			return $time;
		}
		else
			return FALSE;
	}

	/**
	 * Initialise le noyau
	 *
	 * Cette fonction est directement appelée lors du chargement de la configuration du noyau et effectue les actions suivantes :
	 * - Initialise la configuration PHP.
	 * - Définit le paramètre de configuration "Site.URL" s'il n'est pas précisé (détection de l'URL absolue du site).
	 * - Initialise le générateur de nombres aléatoires.
	 * - Charge les librairies par défaut.
	 * - Initialise la session PHP.
	*/
	protected function initialize() {
		// 1. PHP config initialization (if AllowModifyPHPConfig = TRUE).
		if ($this -> getParam('AllowModifyPHPConfig') && $this -> isConfigSectionLoaded('PHP')) {
			if (function_exists('ini_set')) {
				$params = $this -> getSectionParams('PHP');

				foreach ($params as $param => $value)
					ini_set($param, $value);

				// Configuration automatique du "error_log" si non spécifié.
				// Si "error_log" est défini mais n'est pas un chemin valide, le script s'arrête net (page blanche), sans autre explication de la part de PHP.
				if (!isset($params['error_log']))
					ini_set('error_log', dirname($this -> logFile).DIRECTORY_SEPARATOR.'php.log');

				unset($params);
			}
			else
				$this -> error(debug_backtrace(), 'UnableToInitializePhpConfig');
		}

		// 2. Try to get the absolute URL of the site (if not specified).
		if ($this -> getParam('Site.URL') == '') {
			if (!empty($_SERVER['HTTP_SCHEME']))
				$scheme = $_SERVER['HTTP_SCHEME'];
			elseif (!empty($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) != 'off')
				$scheme = 'https';
			else
				// Simple supposition, mais c'est ce qu'il y a de plus probable.
				$scheme = 'http';

			$siteURL = $scheme.'://';

			if (!empty($_SERVER['HTTP_HOST'])) {
				$siteURL.= $_SERVER['HTTP_HOST'];

				if (isset($_SERVER['SERVER_PORT']) && !($scheme == 'http' && $_SERVER['SERVER_PORT'] == 80) && !($scheme == 'https' && $_SERVER['SERVER_PORT'] == 443))
					$siteURL.= ':'.$_SERVER['SERVER_PORT'];

				unset($scheme);
			}
			else
				// Si on a pas le HTTP_HOST, on essai avec une URL "semi" relative (commençant par /).
				$siteURL = '';

			// REQUEST_URI contient également le query, qui peut contenir "/" et demanderait donc un traitement supplémentaire avant d'être passé en argument de dirname().
			if (isset($_SERVER['SCRIPT_NAME']))
				$siteURL.= dirname($_SERVER['SCRIPT_NAME']);
			elseif (isset($_SERVER['PHP_SELF']))
				$siteURL.= dirname($_SERVER['PHP_SELF']);

			if (substr($siteURL, -1) != '/')
				$siteURL.= '/';

			$this -> setParam('Site.URL', $siteURL); unset($siteURL);
		}

		// 3. Random initialization
		// Better let PHP seed... or use the code below if you are paranoid
		// but don't forget to uncomment the lines that sometimes save the config in index.php
/*
		$a = $this -> getParam('RandSeed');
		$a = (($a & 1) << 32)
			| ((($a >> 1) & 1) << 31)
			| ((($a >> 2) & 1) << 30)
			| ((($a >> 3) & 1) << 29)
			| ((($a >> 4) & 1) << 28)
			| ((($a >> 5) & 1) << 27)
			| ((($a >> 6) & 1) << 26)
			| ((($a >> 7) &	1) << 25)
			| ((($a >> 8) & 1) << 24)
			| ((($a >> 9) & 1) << 23)
			| ((($a >> 10) & 1) << 22)
			| ((($a >> 11) & 1) << 21)
			| ((($a >> 12) & 1) << 20)
			| ((($a >> 13) & 1) << 19)
			| ((($a >> 14) & 1) << 18)
			| ((($a >> 15) & 1) << 17)
			| ((($a >> 16) & 1) << 16)
			| ((($a >> 17) & 1) << 15)
			| ((($a >> 18) & 1) << 14)
			| ((($a >> 19) & 1) << 13)
			| ((($a >> 20) & 1) << 12)
			| ((($a >> 21) & 1) << 11)
			| ((($a >> 22) & 1) << 10)
			| ((($a >> 23) & 1) << 9)
			| ((($a >> 24) & 1) << 8)
			| ((($a >> 25) & 1) << 7)
			| ((($a >> 26) & 1) << 6)
			| ((($a >> 27) & 1) << 5)
			| ((($a >> 28) & 1) << 4)
			| ((($a >> 29) & 1) << 3)
			| ((($a >> 30) & 1) << 2)
			| ((($a >> 31) & 1) << 1)
			| (($a >> 32) & 1);
		// 12 bits [time()] + approx. 20 bits [microtime()] = 32 bits
		$b = ((time() & 4095) << 20) | (int) ((float) microtime() * 1048576);
		$s = $a ^ $b;

		$this -> setParam('RandSeed', $s);
		mt_srand($s); unset($a, $b, $s);
*/
		// 4. Loading default libraries.
		$autoLoad = explode(',', $this -> getParam('Library.AutoLoad'));

		foreach ($autoLoad as $lib)
			$this -> loadLib($lib);

		if (!headers_sent($file, $line)) {
			// 5. Session initialization.
			session_start();
			$sessionName = session_name();

			if (!isset($_SESSION['Kernel']['initiated'])) {
				// [Security] Less permissive session management mechanism. See http://www.acros.si/papers/session_fixation.pdf ("STEP 1: Session setup", page 4).
				session_regenerate_id();
				$_SESSION['Kernel']['initiated'] = TRUE;
			}

			// [Security] For more security, switch "session.use_only_cookies" to "on".
			if (isset($_COOKIE[$sessionName]) || ini_get('session.use_only_cookies')) {
				define('SID_URL', '');
				define('SID_XML_URL', '');
			}
			else {
				$sessionId = rawurlencode(session_id());
				define('SID_URL', '&'.$sessionName.'='.$sessionId);
				define('SID_XML_URL', '&amp;'.$sessionName.'='.$sessionId);
			}
		}
		else
			$this -> fatalError(debug_backtrace(), 'HeadersAlreadySentInFileXAtLineY,'.$file.','.$line);
	}

//	function __destruct() {
//		if (!empty($GLOBALS['_PERSISTENT']))
//			file_put_contents($this -> corePath.'_PERSISTENT', serialize($GLOBALS['_PERSISTENT']), LOCK_EX);
//	}
}
?>
Return current item: Utopia CMS