kolibri-discuss team mailing list archive
-
kolibri-discuss team
-
Mailing list archive
-
Message #00038
[Merge] lp:~frode-danielsen/kolibri/config+autoload into lp:kolibri
Frode Danielsen has proposed merging lp:~frode-danielsen/kolibri/config+autoload into lp:kolibri.
Requested reviews:
Kolibri Dev (kolibri-dev)
--
https://code.launchpad.net/~frode-danielsen/kolibri/config+autoload/+merge/5563
Your team Kolibri Discuss is subscribed to branch lp:kolibri.
=== removed file 'examples/wishlist/conf/config.php'
--- examples/wishlist/conf/config.php 2009-04-07 23:17:37 +0000
+++ examples/wishlist/conf/config.php 1970-01-01 00:00:00 +0000
@@ -1,72 +0,0 @@
-<?php
-/*
- * General configuration. Put whatever application-specific configuration you like here. Each setting can be
- * get by calling Config::get('key'), where key is the setting you want to return, i.e. 'mail'.
- */
-$config = array(
- 'webRoot' => 'http://localhost', // Must be absolute URI including scheme. No trailing slash!
- 'staticRoot' => '/static', // URI of static resources (can be another host as http://static.example.com)
- 'locale' => 'en_US.utf8',
- 'logging' => array(
- 'enabled' => false, // When logging is disabled, errors are outputted directly. When enabled...
- 'logFile' => '', // ...they can be logged to a file
- 'logEmail' => '' // ...and/or an email address (make sure you config 'mail' for this to work)
- ),
- /*
- * Database configuration. 'type' is mandatory, while implementations define other settings.
- */
- 'db' => array(
- 'type' => 'Sqlite', // We also support PostgreSql, but not for this demo app
- 'database' => APP_PATH . '/db/wishlist.db' // Filename for SQLite (writable by Apache)
- ),
- /*
- * Configure your e-mail details for the MailService library used to send e-mails.
- */
- 'mail' => array(
- 'from' => '',
- 'from_name' => '',
- 'smtp_auth' => false,
- 'smtp_host' => '',
- 'smtp_username' => '',
- 'smtp_password' => ''
- )
-);
-
-/*
- * Defines which action mappers to use for the specified URIs. The first match is used - the entire
- * array is not necessarily iterated.
- */
-$actionMappers = array(
- '*' => 'DefaultActionMapper'
-);
-
-/*
- * $interceptorMappings defines which interceptors are to be invoked at a given URI. Wildcard mapping
- * is supported. You specify interceptors by their name configured in
- * <code>interceptors.php</code>, which can either refer to single interceptors or interceptor
- * stacks. The interceptors must be wrapped in an array, and you may reference both single interceptors
- * and interceptor stacks in the same mapping.
- *
- * You can exclude certain interceptors from being invoked at specific URIs by prefixing the
- * interceptor name with a ! (exclamation mark). For instance, you may have an authentication
- * interceptor mapped to /admin/* (everything within /admin), but want to leave /admin/login open
- * to the public (after all, users must be allowed to log in). This can be done by these mappings:
- *
- * '/admin/*' => array('auth'),
- * '/admin/login' => array('!auth')
- *
- * Order is of significance when mapping interceptors. If you were to define the excluding mapping
- * in this example before the regular inclusive mapping, it would not work as advertised above.
- * The wild-card mapping within /admin would then override the specific /admin/login, and the
- * exclude-mapping would not have any effect.
- */
-$interceptorMappings = array(
- '*' => array('defaultStack')
-);
-
-/*
- * You may override any interceptor specific settings you like. For example, to specify a custom model for the
- * user object, you could do the following (see kolibri/conf/interceptors.php for more).
- */
-//$interceptors['auth']['AuthInterceptor']['userModel'] = 'MyUser';
-?>
=== added file 'examples/wishlist/conf/development.ini'
--- examples/wishlist/conf/development.ini 1970-01-01 00:00:00 +0000
+++ examples/wishlist/conf/development.ini 2009-04-14 21:48:11 +0000
@@ -0,0 +1,1 @@
+; Empty for this simple example application.
\ No newline at end of file
=== added file 'examples/wishlist/conf/production.ini'
--- examples/wishlist/conf/production.ini 1970-01-01 00:00:00 +0000
+++ examples/wishlist/conf/production.ini 2009-04-14 21:48:11 +0000
@@ -0,0 +1,41 @@
+; Simple configuration of the wishlist application. This is the only configuration file
+; loaded in the production environment, but it will also be loaded as a base configuration
+; in development and test environments. Simply create files called development.ini
+; and test.ini to override any configuration settings for those environments. A good idea
+; is to use separate databases for production and development. A separate database is required
+; in the test environment (if you use databases in general).
+
+[app]
+webRoot = "http://localhost" ; Must be absolute URI including scheme. No trailing slash!
+staticRoot = "/static" ; URI of static resources (ie. another host: http://static.example.com)
+locale = "en_US.utf8"
+
+; Section defining where to log exceptions and errors to.
+
+[logging]
+level = Off ; When log level is Off, errors are output directly. When set to On...
+file = ; ...they can be logged to a file (must be writable by Apache)
+email = ; ...and/or to an email address (make sure to configure 'mail' as well)
+
+; Database configuration. 'type' is mandatory, while implementations can define other settings.
+
+[database]
+type = "Sqlite" ; We also support PostgreSql, but not for this demo app
+name = APP_PATH "/db/wishlist.db" ; Filename for SQLite (writable by Apache)
+
+; Configure your e-mail details for the MailService library used to send e-mails.
+
+[mail]
+from.email =
+from.name =
+smtp.auth = Off
+smtp.host =
+smtp.username =
+smtp.password =
+
+; Section defining which interceptors are to be invoked at a given URI. Wildcard mapping
+; is supported. See production.ini in src/conf/ for more details on how this works and
+; info about other parts of interceptors you can configure.
+
+[interceptors]
+* = "defaultStack"
=== modified file 'examples/wishlist/views/error.php'
--- examples/wishlist/views/error.php 2009-04-03 16:04:34 +0000
+++ examples/wishlist/views/error.php 2009-04-14 21:48:11 +0000
@@ -23,9 +23,10 @@
<div id="error">
<?php
/*
- * If logging is disable, we display detailed error.
+ * If logging is disabled we display detailed error, but only if we're not in production mode.
*/
-if (!isset($config['logging']['enabled']) || !$config['logging']['enabled']):
+if (Config::getMode() != Config::PRODUCTION
+ && !isset($config['logging']['level']) || $config['logging']['level'] == false):
?>
<h1><?php echo get_class($exception) ?></h1>
<p id="message">
=== modified file 'src/actions/ValidationAware.php'
--- src/actions/ValidationAware.php 2009-04-12 02:11:21 +0000
+++ src/actions/ValidationAware.php 2009-04-14 21:28:43 +0000
@@ -14,6 +14,6 @@
* Called when validation failed. A Result object must be returned, which will be rendered
* instead of continuing request processing.
*/
- public function validationFailed () {}
+ public function validationFailed ();
}
?>
=== modified file 'src/conf/autoload.php'
--- src/conf/autoload.php 2009-04-12 01:09:55 +0000
+++ src/conf/autoload.php 2009-04-12 02:36:42 +0000
@@ -1,10 +1,10 @@
<?php
/*
- * Defines classes that may be loaded upon its first use by __autoload(). They are not automatically
- * loaded unless they are actually used. See <code>UtilsInterceptor</code> for automatic loading of utils.
+ * Defines classes that may be loaded upon its first use by Autoloader::load(). They are not
+ * automatically loaded unless they are actually used. See <code>UtilsInterceptor</code> for
+ * automatic loading of utils.
*/
$autoloadClasses = array(
- 'DefaultActionMapper' => '/core/DefaultActionMapper.php',
'ActionSupport' => '/actions/ActionSupport.php',
'AuthAware' => '/actions/AuthAware.php',
'MessageAware' => '/actions/MessageAware.php',
@@ -47,6 +47,7 @@
'SmartyResult' => '/results/SmartyResult.php',
'TextResult' => '/results/TextResult.php',
'XsltResult' => '/results/XsltResult.php',
+ 'Utils' => '/core/Utils.php',
'Validator' => '/validation/Validator.php',
'ValidationHelper' => '/validation/ValidationHelper.php');
?>
=== removed file 'src/conf/config.php'
--- src/conf/config.php 2009-04-07 23:17:37 +0000
+++ src/conf/config.php 1970-01-01 00:00:00 +0000
@@ -1,74 +0,0 @@
-<?php
-/*
- * Sample general configuration file. Copy this into your conf/ directory within your application. You may put
- * whatever application-specific configuration you like here. Each setting can bee get by calling
- * Config::get('key'), where key is the setting you want to return, i.e. 'mail'.
- */
-$config = array(
- 'webRoot' => 'http://localhost', // Must be absolute URI including scheme. No trailing slash!
- 'staticRoot' => '/static', // URI of static resources (can be another host as http://static.example.com)
- 'locale' => 'en_US.utf8',
- 'logging' => array(
- 'enabled' => false
- ),
- /*
- * Database configuration. 'type' is mandatory, while implementations can define other settings.
- */
- 'db' => array(
- 'type' => 'PostgreSql', // Or Sqlite
- 'database' => '', // Database name for PostgreSql, file for SQLite (writable by Apache)
- 'host' => 'localhost', // Not relevant for Sqlite
- 'username' => '', // --"--
- 'password' => '' // --"--
- ),
- /*
- * Configure your e-mail details for the MailService library used to send e-mails.
- */
- 'mail' => array(
- 'from' => '',
- 'from_name' => '',
- 'smtp_auth' => false,
- 'smtp_host' => '',
- 'smtp_username' => '',
- 'smtp_password' => ''
- )
-);
-
-/*
- * Defines which action mappers to use for the specified URIs. The first match is used - the entire
- * array is not necessarily iterated.
- */
-$actionMappers = array(
- '*' => 'DefaultActionMapper'
-);
-
-/*
- * $interceptorMappings defines which interceptors are to be invoked at a given URI. Wildcard mapping
- * is supported. You specify interceptors by their name configured in
- * <code>interceptors.php</code>, which can either refer to single interceptors or interceptor
- * stacks. The interceptors must be wrapped in an array, and you may reference both single interceptors
- * and interceptor stacks in the same mapping.
- *
- * You can exclude certain interceptors from being invoked at specific URIs by prefixing the
- * interceptor name with a ! (exclamation mark). For instance, you may have an authentication
- * interceptor mapped to /admin/* (everything within /admin), but want to leave /admin/login open
- * to the public (after all, users must be allowed to log in). This can be done by these mappings:
- *
- * '/admin/*' => array('auth'),
- * '/admin/login' => array('!auth')
- *
- * Order is of significance when mapping interceptors. If you were to define the excluding mapping
- * in this example before the regular inclusive mapping, it would not work as advertised above.
- * The wild-card mapping within /admin would then override the specific /admin/login, and the
- * exclude-mapping would not have any effect.
- */
-$interceptorMappings = array(
- '*' => array('defaultStack')
-);
-
-/*
- * You may override any interceptor specific settings you like. For example, to specify a custom model for the
- * user object, you could do the following (see kolibri/conf/interceptors.php for more).
- */
-//$interceptors['auth']['AuthInterceptor']['userModel'] = 'MyUser';
-?>
=== added file 'src/conf/development.ini'
--- src/conf/development.ini 1970-01-01 00:00:00 +0000
+++ src/conf/development.ini 2009-04-14 21:28:43 +0000
@@ -0,0 +1,5 @@
+; Application configuration file for development mode, the default environment mode
+; for Kolibri. You only need to override configuration values from production.ini,
+; if any, here. Ie. if you use the same database name, username and password only
+; on different hosts between production and development mode, all you need to override
+; is the 'host' value in the [database] section.
\ No newline at end of file
=== modified file 'src/conf/interceptors.php'
--- src/conf/interceptors.php 2009-04-08 15:34:28 +0000
+++ src/conf/interceptors.php 2009-04-14 21:28:43 +0000
@@ -7,17 +7,9 @@
$interceptors = array(
'message' => 'MessageInterceptor',
'validation' => 'ValidationInterceptor',
- 'error' => array(
- 'ErrorInterceptor' => array('result' => 'PhpResult', 'view' => '/error')
- ),
+ 'error' => 'ErrorInterceptor',
'session' => 'SessionInterceptor',
- 'auth' => array(
- 'AuthInterceptor' => array(
- 'userModel' => 'AuthUser',
- 'userKey' => 'user',
- 'loginUri' => '/login'
- )
- ),
+ 'auth' => 'AuthInterceptor',
'model' => 'ModelInterceptor',
'params' => 'ParametersInterceptor',
'upload' => 'UploadInterceptor',
@@ -26,6 +18,22 @@
);
/*
+ * Defines the default settings for interceptors. Can be overriden in applications in the
+ * [interceptors.settings] section of a ini file.
+ */
+$interceptorSettings = array(
+ 'error' => array(
+ 'result' => 'PhpResult',
+ 'view' => '/error'
+ ),
+ 'auth' => array(
+ 'userModel' => 'AuthUser',
+ 'userKey' => 'user',
+ 'loginUri' => '/login'
+ )
+);
+
+/*
* Defines stacks of interceptors. This makes it easy to group together several interceptors, which just as
* interceptor names, can be referenced directly in <code>main.php</code> or your own
* <code>config.php</code>.
=== added file 'src/conf/production.ini'
--- src/conf/production.ini 1970-01-01 00:00:00 +0000
+++ src/conf/production.ini 2009-04-14 21:48:11 +0000
@@ -0,0 +1,84 @@
+; Sample general configuration file for the production environment. Copy this into your
+; application's conf/ directory. You may put whatever application-specific configuration
+; you like here. Each setting can be fetched by calling Config::get('key'), where key is
+; the setting you want to return, ie. 'mail'.
+
+[app]
+webRoot = ; Must be absolute URI including scheme. No trailing slash!
+staticRoot = "/static" ; URI of static resources (ie. another host: http://static.example.com)
+locale = "en_US.utf8"
+
+; Section defining where to log exceptions and errors to. Logging is automatically enabled
+; in the production environment, and disabled in development and testing (where they are
+; displayed instead).
+
+[logging]
+level = Off ; Currently only On or Off
+file = ; Absolute path, needs to be writable by Apache
+email = ; When defined, be sure to configure MailService below as well
+
+; Database configuration. 'type' is mandatory, while implementations can define other settings.
+
+[database]
+type = "PostgreSql" ; Or 'Sqlite'
+name = ; Database name for PostgreSql, file for SQLite (writable by Apache)
+host = "localhost" ; Not relevant for Sqlite
+username = ; --"--
+password = ; --"--
+
+; Configure your e-mail details for the MailService library used to send e-mails.
+
+[mail]
+from.email =
+from.name =
+smtp.auth = Off
+smtp.host =
+smtp.username =
+smtp.password =
+
+; Section defining which interceptors are to be invoked at a given URI. Wildcard mapping
+; is supported. You specify interceptors by their name configured in interceptors.php, which
+; can either refer to single interceptors or interceptor stacks. The interceptors must be
+; wrapped in an array, and you may reference both single interceptors and interceptor stacks
+; in the same mapping.
+;
+; You can exclude certain interceptors from being invoked at specific URIs by prefixing the
+; interceptor name with a ! (exclamation mark). For instance, you may have an authentication
+; interceptor mapped to /admin/* (everything within /admin), but want to leave /admin/login open
+; to the public (after all, users must be allowed to log in). This can be done by these mappings:
+;
+; /admin/* = "auth"
+; /admin/login = "!auth"
+;
+; Order is of significance when mapping interceptors, and should be from least to most specific
+; URI. If you were to define the excluding mapping in this example before the regular inclusive
+; mapping, it would not work as advertised above. The wild-card mapping within /admin
+; would then override the specific /admin/login, and the exclude-mapping would not have any
+; effect.
+
+[interceptors]
+* = "defaultStack"
+
+; You may override any interceptor specific settings you like. The example below specifies
+; the model class, session key and login URI for the authentication interceptor
+; (see src/conf/interceptors.php in for more settings).
+
+[interceptors.settings]
+; auth.userModel = "AuthUser"
+; auth.userKey = "user"
+; auth.loginUri = "/login"
+
+; You can define your own stacks of interceptors when you use a specific combination
+; of interceptors often. The name of a stack is used in exactly the same way as
+; the name of a single interceptor in the URI mapping above. There are two predefined
+; stacks, shown below.
+
+[interceptors.stacks]
+; defaultStack = "session, message, error, transaction, model, validation"
+; authStack = "session, message, error, transaction, auth, model, validation"
+
+; Section defining which action mappers to use for the specified URIs. The first match is
+; used - all definitions are not necessarily searched for the most specific match.
+
+; [actionmappers]
+; * = "DefaultActionMapper"
=== added file 'src/conf/test.ini'
--- src/conf/test.ini 1970-01-01 00:00:00 +0000
+++ src/conf/test.ini 2009-04-14 21:28:43 +0000
@@ -0,0 +1,4 @@
+; Application configuration file for test mode, the environment mode Kolibri uses
+; when you run your application tests. This configuration inherits values from
+; development.ini in the same way development.ini inherits from production.ini.
+; Usually all you need to configure here is a separate test database.
\ No newline at end of file
=== added file 'src/core/Autoloader.php'
--- src/core/Autoloader.php 1970-01-01 00:00:00 +0000
+++ src/core/Autoloader.php 2009-04-08 17:13:50 +0000
@@ -0,0 +1,56 @@
+<?php
+/**
+ * Loads core framework classes and application models with their DAO classes as required.
+ */
+class Autoloader {
+ /**
+ * Mapping of class names to files with their definitions.
+ * @var array
+ */
+ private static $classes = array();
+
+ /**
+ * Cache table with names of classes that have been loaded.
+ * Only used for classes not in the autoload class mapping.
+ * @var array
+ */
+ private static $loaded = array();
+
+ /**
+ * Initializes the Autoloader with a mapping of class names to files,
+ * and registers the autoload function with the PHP runtime.
+ */
+ public static function initialize ($config) {
+ self::$classes = $config;
+
+ spl_autoload_register(array('Autoloader', 'load'));
+ }
+
+ /**
+ * Kolibris autoload function. Handles autoloading of core framework classes
+ * through a lookup mapping defined in conf/autoload.php. It also handles autoloading
+ * of application model and DAO classes.
+ */
+ public static function load ($className) {
+ // We optimize for core classes and check if that's what needs loading first.
+ if (isset(self::$classes[$className])) {
+ require(ROOT . self::$classes[$className]);
+ }
+ else if (!isset(self::$loaded[$className])) {
+ // DAO class names in Kolibri consists of the model name with 'Dao' appended.
+ if (substr($className, -3) == 'Dao') {
+ require(MODELS_PATH . "/dao/{$className}.php");
+ }
+ // If it's not a DAO class, see if it's a model class
+ else if (file_exists(MODELS_PATH . "/{$className}.php")) {
+ require(MODELS_PATH . "/{$className}.php");
+ }
+ // If it's not a core, model or DAO class we simply use the include_path
+ else {
+ require($className . '.php');
+ }
+ self::$loaded[$className] = true;
+ }
+ }
+}
+?>
\ No newline at end of file
=== modified file 'src/core/Config.php'
--- src/core/Config.php 2008-12-08 18:45:30 +0000
+++ src/core/Config.php 2009-04-14 21:28:43 +0000
@@ -1,11 +1,33 @@
<?php
+require(ROOT . '/core/Autoloader.php');
+require(ROOT . '/core/ConfigHelper.php');
+
+// Define constants for application specific directories
+define('ACTIONS_PATH', APP_PATH . '/actions');
+define('MODELS_PATH', APP_PATH . '/models');
+define('VIEW_PATH', APP_PATH . '/views');
+
/**
* This class represents the configuration of the Kolibri framework.
*
- * All configuration variables are easily availible through the static methods of this class.
+ * All configuration variables are easily available through the static methods of this class.
*/
class Config {
/**
+ * Constants for the different environment modes an application can be in.
+ * Development is default, but can be changed through KOLIBRI_MODE environment variable.
+ */
+ const PRODUCTION = 'production';
+ const DEVELOPMENT = 'development';
+ const TEST = 'test';
+
+ /**
+ * Current environment mode.
+ * @var string
+ */
+ private $mode;
+
+ /**
* General configuration settings.
* @var array
*/
@@ -18,23 +40,29 @@
private $interceptorClasses;
/**
- * Interceptors mapped to actions [action path => interceptors]
- * @var array
- * */
+ * Settings for interceptors.
+ * @var array
+ */
+ private $interceptorSettings;
+
+ /**
+ * Prepared list of URIs to unique set of interceptors.
+ * @var array
+ */
private $interceptorMappings;
-
- /**
- * Defines the action mappers responding to specific URIs.
- * @var array
- */
- private $actionMappers;
-
- /**
- * Defines the validation configuration (validator classes and validation messages).
- * @var array
- */
- private $validation;
-
+
+ /**
+ * Defines the validation classes.
+ * @var array
+ */
+ private $validationClasses;
+
+ /**
+ * Defines validation error messages.
+ * @var array
+ */
+ private $validationMessages;
+
/**
* Singleton instance of this class.
* @var Config
@@ -45,48 +73,86 @@
* Private constructor which initializes the configuration. It is defined private as all
* interaction with this class goes through static methods.
*/
- private function __construct () {
+ private function __construct ($mode) {
+ $this->mode = $mode;
+
+ require(ROOT . '/conf/autoload.php');
require(ROOT . '/conf/interceptors.php');
require(ROOT . '/conf/validation.php');
- require(APP_PATH . '/conf/config.php');
-
- $this->config = $config;
- $this->actionMappers = $actionMappers;
- $this->interceptorClasses = $interceptors;
- $this->interceptorMappings = $interceptorMappings;
- $this->validation = array('classes' => $validators, 'messages' => $validationMessages);
-
- /*
- * Loop through interceptor stacks. For each stack, add the stack to the regular interceptor
- * list with the correct interceptors attached. This makes it possible to use a stack just as
- * a single interceptor.
- */
- foreach ($interceptorStacks as $name => $stack) {
- foreach ($stack as $interceptor) {
- /*
- * $interceptor must be the name of an existing interceptor. This gives us access
- * to the actual interceptor class within the stack.
- */
- $this->interceptorClasses[$name][] = $this->interceptorClasses[$interceptor];
- }
- }
-
+
+ // Initialize the Kolibri class autoloader with classname-to-file mappings from conf/autoload.php
+ Autoloader::initialize($autoloadClasses);
+
+ // Load relevant app configuration depending on current environment mode
+ $this->config = ConfigHelper::loadApp($this->mode);
+
+ /*
+ * Extract all interceptor configurations from the loaded application
+ * configuration. These configurations are irrelevant as normal configuration
+ * values. They are instead merged with the default configuration and compiled
+ * internally for use with the Dispatcher.
+ */
+ if (isset($this->config['interceptors.stacks'])) {
+ $appInterceptorStacks = $this->config['interceptors.stacks'];
+ unset($this->config['interceptors.stacks']);
+ }
+ else $appInterceptorStacks = array();
+
+ if (isset($this->config['interceptors.settings'])) {
+ $appInterceptorSettings = $this->config['interceptors.settings'];
+ unset($this->config['interceptors.settings']);
+ }
+ else $appInterceptorSettings = array();
+
+ if (isset($this->config['interceptors'])) {
+ $appInterceptorMappings = $this->config['interceptors'];
+ unset($this->config['interceptors']);
+ }
+ else $appInterceptorMappings = array();
+
+ // Compile single index of interceptor classes, default stacks and application stacks
+ $this->interceptorClasses =
+ ConfigHelper::prepareInterceptors($interceptors, $interceptorStacks, $appInterceptorStacks);
+ // Merge default interceptor settings with any application specific settings
+ $this->interceptorSettings =
+ ConfigHelper::prepareInterceptorSettings($interceptors, $interceptorSettings,
+ $appInterceptorSettings);
+ /*
+ * Optimize interceptor mappings by flattening stacks and filtering down to a unique
+ * list of interceptors for each URI.
+ */
+ $this->interceptorMappings =
+ ConfigHelper::prepareInterceptorMappings($this->interceptorClasses, $appInterceptorMappings);
+
+ // Store validation configuration from conf/validation.php
+ $this->validationClasses = $validators;
+ $this->validationMessages = $validationMessages;
+ }
+
+ /**
+ * Initializes PHP settings based on the current configuration. This is done separately
+ * from the constructor to support initalization after loading a stored instance
+ * of the configuration (ie. serialized).
+ */
+ private function init () {
$incPath = ROOT . '/lib';
- if (isset($this->config['include_path'])) {
- $incPath .= PATH_SEPARATOR . implode(PATH_SEPARATOR, $this->config['include_path']);
+ if (isset($this->config['includePath'])) {
+ $incPath .= PATH_SEPARATOR . implode(PATH_SEPARATOR, (array) $this->config['includePath']);
}
ini_set('include_path', ini_get('include_path') . PATH_SEPARATOR . $incPath);
-
+
/*
* Sets the current locale for date formatting et cetera
* XXX: We leave LC_NUMERIC at default, as locales with comma as decimal seperator will
* cause SQL queries with floating point values to fail. We should find a better solution...
*/
- $envLocale = setlocale(LC_NUMERIC, 0);
- setlocale(LC_ALL, $this->config['locale']);
- setlocale(LC_NUMERIC, $envLocale);
+ if (isset($this->config['locale'])) {
+ $envLocale = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_ALL, $this->config['locale']);
+ setlocale(LC_NUMERIC, $envLocale);
+ }
}
-
+
/**
* Returns an instance of this class. An existing instance is returned if one exists, else a new
* instance is created.
@@ -95,32 +161,24 @@
*/
public static function getInstance () {
if (!isset(self::$instance)) {
- self::$instance = new Config();
+ $mode = ConfigHelper::getMode();
+ // XXX: Unserialize cached config when appropriate depending on mode here
+ self::$instance = new Config($mode);
+ self::$instance->init();
}
return self::$instance;
}
/**
- * Loads a configuration file. The new configuration settings will take precidence if there any
- * conflicts with existing configuration settings.
+ * Returns the environment mode Kolibri is currently running in.
*
- * @param string $file Configuration file to load (a PHP file).
- * @throws Exception If no config file was specified, or it doesn't exist.
+ * @return string Either Config::DEVELOPMENT, Config::TEST or Config::PRODUCTION.
*/
- public static function load ($file) {
- if (!empty($file) && is_file($file)) {
- require($file);
-
- if (is_array($config)) {
- $instance = Config::getInstance();
- $instance->config = array_merge($instance->config, $config);
- }
- }
- else {
- throw new Exception('No config-file specified');
- }
+ public static function getMode () {
+ $instance = Config::getInstance();
+ return $instance->mode;
}
-
+
/**
* Returns the value of the configuration setting with the specified key, or <code>NULL</code> if
* not found. If no key is supplied, all settings are returned.
@@ -139,57 +197,50 @@
/**
* Returns the names of the action mappers defined for this application.
*
- * @return array Associative array with URIs mapped to action mappers.
+ * @return mixed Associative array with URIs mapped to action mappers, or <code>NULL</code> when
+ * none is defined.
*/
public static function getActionMappers () {
$instance = Config::getInstance();
- return $instance->actionMappers;
+ return (isset($instance->config['actionmappers']) ? $instance->config['actionmappers'] : null);
}
/**
* Returns the interceptor mappings defined for this application.
*
- * @return array Associative array with action paths mapped to interceptor names.
+ * @return array Associative array with action paths mapped to interceptor names.
*/
public static function getInterceptorMappings () {
$instance = Config::getInstance();
- return $instance->interceptorMappings;
+ return (array) $instance->interceptorMappings;
}
/**
- * Returns an array with the class of an interceptor or classes of an interceptor stack.
+ * Returns any settings defined for a specific interceptor, or all interceptor
+ * settings.
*
- * @param string $key Key of interceptor or interceptor stack to get classes for.
- * @return array Array of class names.
+ * @param string $name Optional name of interceptor to return settings for.
+ * @return array Associative array with settings for all interceptors or the one
+ * interceptor asked for.
*/
- public static function getInterceptorClasses ($key) {
+ public static function getInterceptorSettings ($name = null) {
$instance = Config::getInstance();
-
- if (!empty($instance->interceptorClasses[$key])) {
- $class = $instance->interceptorClasses[$key];
-
- /*
- * We must check if index 0 is not set in addition to the regular array-check, as $class can
- * be an interceptor definition with parameters (an array with a class name as the key and an
- * array of interceptor parameters as the value). We want to wrap those in an array as well,
- * hence the extra check.
- */
- if (!is_array($class) || !isset($class[0])) {
- return array($class);
- }
- return $class;
+ if ($name !== null) {
+ return (isset($instance->interceptorSettings[$name]) ?
+ $instance->interceptorSettings[$name] : null);
}
- return array();
+ return (array) $instance->interceptorSettings;
}
/**
* Returns an array of validation configuration.
+ * XXX: Separate fetching of validation classes and messages.
*
* @return array Array of validation configuration.
*/
public static function getValidationConfig () {
$instance = Config::getInstance();
- return $instance->validation;
+ return array('classes' => $instance->validationClasses, 'messages' => $instance->validationMessages);
}
}
?>
=== added file 'src/core/ConfigHelper.php'
--- src/core/ConfigHelper.php 1970-01-01 00:00:00 +0000
+++ src/core/ConfigHelper.php 2009-04-14 21:48:11 +0000
@@ -0,0 +1,230 @@
+<?php
+/**
+ * Helper class for reading environment variables, loading configuration files and parsing
+ * configuration values.
+ */
+class ConfigHelper {
+ /**
+ * Determines and returns the current environment mode. Checks for an environment variable
+ * named KOLIBRI_MODE and uses it's value if it's one of the three supported environment
+ * modes. In any other case the default environment mode is development.
+ *
+ * @throws Exception If KOLIBRI_MODE contains an unsupported environment mode.
+ */
+ public static function getMode () {
+ if (($envMode = getenv('KOLIBRI_MODE')) !== false) {
+ if ($envMode == Config::PRODUCTION
+ || $envMode == Config::DEVELOPMENT
+ || $envMode == Config::TEST) {
+ return $envMode;
+ }
+ else {
+ throw new Exception("Invalid environment mode in \$KOLIBRI_MODE: '$envMode'");
+ }
+ }
+
+ return Config::DEVELOPMENT;
+ }
+
+ /**
+ * Loads all application configuration for the current environment mode.
+ * Configuration files are loaded for each environment in a hierarchy:
+ * Production -> Development -> Test
+ * The production configuration will always be loaded, but overridden where
+ * neccessary in development and test environments.
+ *
+ * @param string $runtimeMode Current environment mode, usually as determined through
+ * ConfigHelper::getMode().
+ * @return array Associative array with all configuration settings for the current
+ * environment mode.
+ * @throws Exception If application runs in test mode but no database is configured
+ * to replace the development database configuration.
+ */
+ public static function loadApp ($runtimeMode) {
+ Utils::import('arrays');
+
+ // Set up the cascading stack of configuration files depending on current mode
+ $configStack = array(Config::PRODUCTION);
+ if ($runtimeMode != Config::PRODUCTION) {
+ $configStack[] = Config::DEVELOPMENT;
+
+ if ($runtimeMode == Config::TEST) {
+ $configStack[] = Config::TEST;
+ }
+ }
+
+ $config = array();
+ foreach ($configStack as $configMode) {
+ $modeConfig = self::loadMode($configMode);
+
+ /*
+ * We want to prevent accidental use of the development or production database
+ * in the test environment where all data is volatile.
+ */
+ if ($configMode == Config::TEST) {
+ if (isset($config['database']) && !isset($modeConfig['database'])) {
+ throw new Exception('No test database configured to override development database.');
+ }
+ }
+
+ // Merge config for a specific mode with previously loaded configuration recursively
+ $config = array_merge_recursive_distinct($config, $modeConfig);
+ }
+
+ /*
+ * The 'app' section in configuration files are automatically flattened to global
+ * configuration values for convenience.
+ */
+ if (isset($config['app'])) {
+ foreach ($config['app'] as $key => $value) {
+ $config[$key] = $value;
+ }
+ unset($config['app']);
+ }
+
+ return $config;
+ }
+
+ /**
+ * Loads the application's configuration file for a specific environment mode.
+ *
+ * @param string $mode Either Config::PRODUCTION, Config::DEVELOPMENT or Config::TEST.
+ * @throws Exception If the configuration file for the mode does not exist, or
+ * there was an error parsing the file (syntax error).
+ */
+ public static function loadMode ($mode) {
+ $file = APP_PATH . "/conf/{$mode}.ini";
+ if (!file_exists($file)) {
+ throw new Exception("Application configuration file missing for {$mode} environment: $file");
+ }
+
+ $config = @parse_ini_file($file, true);
+ if ($config === false) {
+ // Raise the PHP warning from syntax errors in configuration file to an Exception
+ $error = error_get_last();
+ throw new Exception($error['message']);
+ }
+
+ return $config;
+ }
+
+ /**
+ * Merges default interceptor stacks with the application's stacks. Then loops through
+ * all interceptor stacks, adding each stack to the regular interceptor list with the
+ * correct interceptors attached. This makes it possible to use a stack just as a single
+ * interceptor in interceptor mappings.
+ *
+ * @param array $classes Mapping of interceptor names to interceptor classes.
+ * @param array $defaultStacks Default interceptor stacks defined in Kolibri.
+ * @param array $applicationStacks Interceptor stacks defined in the application's
+ * configuration files.
+ * @return array Mapping of interceptors that also include stacks.
+ * @throws Exception If a stack includes the name of a non-existing interceptor.
+ */
+ public static function prepareInterceptors (array $classes, array $defaultStacks,
+ array $applicationStacks) {
+ // Parse stacks defined in application ini files
+ foreach ($applicationStacks as $name => $stack) {
+ $applicationStacks[$name] = preg_split('/,\s*/', $stack);
+ }
+
+ $stacks = array_merge($defaultStacks, $applicationStacks);
+ foreach ($stacks as $name => $stack) {
+ /*
+ * Reset stack in case another with the same name exists already,
+ * we don't support merging of individual interceptors for stacks.
+ */
+ $classes[$name] = array();
+ foreach ($stack as $interceptor) {
+ /*
+ * $interceptor must be the name of an existing interceptor. This gives us access
+ * to the actual interceptor class within the stack.
+ */
+ if (isset($classes[$interceptor])) {
+ $classes[$name][$interceptor] = $classes[$interceptor];
+ }
+ else {
+ throw new Exception("Non-existing interceptor '$interceptor' used "
+ . "in stack '$name'");
+ }
+ }
+ }
+
+ return $classes;
+ }
+
+ /**
+ * Parses and validates application specific interceptor settings, and merges them with
+ * the default interceptor settings. Each application specific setting must have a name
+ * including both the name of the interceptor and the property it defines a value for,
+ * separated by a period. Ie. a setting for which model class to use for an authenticated
+ * user would look like this:
+ * auth.userModel = MyUser
+ *
+ * @param array $classes Mapping of interceptor names to interceptor classes.
+ * @param array $defaultSettings Kolibri's default settings for interceptors.
+ * @param array $applicationSettings Application specific settings for interceptors.
+ * @param array $settings Associative array with setting names and their values, grouped
+ * under the name of the interceptor they relate to.
+ * @throws Exception If an application specific setting's name is invalid or incomplete.
+ */
+ public static function prepareInterceptorSettings (array $classes, array $defaultSettings,
+ array $applicationSettings) {
+ Utils::import('arrays');
+
+ $settings = array();
+ foreach ($applicationSettings as $setting => $value) {
+ if (substr_count($setting, '.') == 1) {
+ list($interceptor, $setting) = explode('.', $setting);
+ if (isset($classes[$interceptor])) {
+ $settings[$interceptor][$setting] = $value;
+ }
+ else {
+ throw new Exception('Settings defined for non-existing interceptor '
+ . "'$interceptor' ($file)");
+ }
+ }
+ else {
+ throw new Exception("Invalid key '$setting' in interceptor settings ($file)");
+ }
+ }
+
+ return array_merge_recursive_distinct($defaultSettings, $settings);
+ }
+
+ /**
+ * Prepares interceptor mappings by flattening interceptor stacks and translating
+ * interceptor names to class names.
+ *
+ * @param array $interceptors Complete list of interceptors and interceptor stacks.
+ * @param array $applicationMappings Mapping of application URIs to interceptors.
+ * @return array Map of application URIs => interceptors where stacks are flattened and
+ * interceptor names converted to their class names.
+ */
+ public static function prepareInterceptorMappings (array $interceptors, array $applicationMappings) {
+ foreach ($applicationMappings as $uri => $names) {
+ $classes = array();
+ foreach (preg_split('/,\s*/', $names) as $name) {
+ if ($name{0} == '!') {
+ $prefix = '!';
+ $name = substr($name, 1);
+ }
+ else {
+ $prefix = '';
+ }
+
+ if (is_array($interceptors[$name])) {
+ $classes = array_merge($classes, $interceptors[$name]);
+ }
+ else {
+ $classes[$name] = $prefix . $interceptors[$name];
+ }
+ }
+
+ $applicationMappings[$uri] = $classes;
+ }
+
+ return $applicationMappings;
+ }
+}
+?>
\ No newline at end of file
=== modified file 'src/core/Dispatcher.php'
--- src/core/Dispatcher.php 2008-10-20 16:41:10 +0000
+++ src/core/Dispatcher.php 2009-04-14 21:28:43 +0000
@@ -1,4 +1,6 @@
<?php
+require(ROOT . '/core/InterceptorFactory.php');
+
/**
* This class dispatches the request to the interceptors and action target of the request.
*
@@ -55,26 +57,16 @@
if (preg_match($uriMatch, $request->getUri()) == 1) {
/*
- * Current URI matches the mapping. We loop through each interceptor mapped, find their
- * actual classes and sets or unsets their use depending on the current URI mapping.
+ * Current URI matches the mapping. We loop through each interceptor for the
+ * matched URI and add or remove from the final stack depending on it's prefix.
*/
- foreach ($interceptors as $name) {
- // Check whether the current interceptor is to be used or negated
- if (!($use = (substr($name, 0, 1) != '!'))) {
- $name = substr($name, 1); // Strip ! from the name
+ foreach ($interceptors as $name => $class) {
+ // Only interceptor classes without a ! prefix should be in the stack
+ if (substr($class, 0, 1) != '!') {
+ $stack[$name] = $class;
}
-
- // Gets the actual classes the name represents
- $classes = Config::getInterceptorClasses($name);
-
- // Loop through interceptor classes and set or unset its use
- foreach ($classes as $class) {
- if ($use) {
- $stack[] = $class;
- }
- else {
- unset($stack[array_search($class, $stack)]);
- }
+ else {
+ unset($stack[$name]);
}
}
}
=== modified file 'src/core/InterceptorFactory.php'
--- src/core/InterceptorFactory.php 2008-10-20 16:41:10 +0000
+++ src/core/InterceptorFactory.php 2009-04-14 21:28:43 +0000
@@ -13,21 +13,16 @@
/**
* Instantiates, initilizes and returns interceptors set to be used.
*
- * @param array $interceptors Interceptor classes to instantiate.
- * @return array An array with instantiated interceptors.
+ * @param array $interceptors Interceptor classes to instantiate.
+ * @param array $settings Associative array with settings for interceptors.
+ * @return array An array with instantiated interceptors.
*/
- public static function createInterceptors ($interceptors) {
+ public static function createInterceptors (array $interceptors) {
$stack = array(); // To hold instantiated interceptors
-
- foreach ($interceptors as $class) {
- if (is_array($class)) {
- // $class contains parameters to be passed to the constructor
- $actualClass = key($class);
- $instance = new $actualClass(current($class));
- }
- else {
- $instance = new $class();
- }
+
+ foreach ($interceptors as $name => $class) {
+ $settings = Config::getInterceptorSettings($name);
+ $instance = new $class($settings);
$instance->init();
$stack[] = $instance;
=== modified file 'src/core/RequestProcessor.php'
--- src/core/RequestProcessor.php 2008-10-20 16:41:10 +0000
+++ src/core/RequestProcessor.php 2009-04-12 02:36:42 +0000
@@ -1,4 +1,7 @@
<?php
+require(ROOT . '/core/Request.php');
+require(ROOT . '/core/Dispatcher.php');
+
/**
* This is the main class of the TURBO framework, which is responsible for initializing the request
* processing flow.
@@ -65,20 +68,30 @@
*/
private function findActionMapper () {
$actionMappers = Config::getActionMappers();
-
- foreach ($actionMappers as $uri => $mapper) { // Loop through URIs/mappers
+
+ if ($actionMappers === null) {
+ return 'DefaultActionMapper';
+ }
+
+ $requestUri = $this->request->getUri();
+ foreach ($actionMappers as $uri => $mapper) {
// Replace star wildcard mappings with regex "any characters" mapping, to use regex
$uri = '#^' . str_replace('*', '.*?', $uri) . '$#';
- if (preg_match($uri, $this->request->getUri()) == 1) {
+ if (preg_match($uri, $requestUri) == 1) {
if ($mapper != 'DefaultActionMapper') {
- // Mapper is application-specific, include it (DefaultActionMapper is autoloadable)
- require(APP_PATH . "/mappers/$mapper.php");
- }
-
+ // Mapper is application-specific, rely on include_path for loading it
+ require("$mapper.php");
+ }
+ else {
+ require('/core/DefaultActionMapper.php');
+ }
+
return $mapper;
}
}
+
+ throw new Exception("No ActionMapper configured for requested URI: $requestUri");
}
/**
=== added file 'src/core/Utils.php'
--- src/core/Utils.php 1970-01-01 00:00:00 +0000
+++ src/core/Utils.php 2009-04-08 00:04:08 +0000
@@ -0,0 +1,14 @@
+<?php
+class Utils {
+ public static function import ($module) {
+ $path = ROOT . "/utils/{$module}.php";
+
+ if (file_exists($path)) {
+ require_once($path);
+ }
+ else {
+ throw new Exception("Utility module '$module' does not exist.");
+ }
+ }
+}
+?>
\ No newline at end of file
=== modified file 'src/database/DatabaseFactory.php'
--- src/database/DatabaseFactory.php 2008-12-05 01:34:28 +0000
+++ src/database/DatabaseFactory.php 2009-04-12 02:36:42 +0000
@@ -27,9 +27,10 @@
* @throws Exception If the implementation the configuration specifies is not found.
* @return DatabaseConnection
*/
- public static function getConnection ($confName = 'db') {
- if (!isset(self::$connections[$confName])) {
- $dbConf = Config::get($confName);
+ public static function getConnection ($confName = null) {
+ $confKey = ($confName === null ? 'database' : "database.{$confName}");
+ if (!isset(self::$connections[$confKey])) {
+ $dbConf = Config::get($confKey);
if (!empty($dbConf)) {
$dbConnClass = $dbConf['type'] . 'Connection';
@@ -46,14 +47,14 @@
}
}
- self::$connections[$confName] = new $dbConnClass($dbConf);
+ self::$connections[$confKey] = new $dbConnClass($dbConf);
}
else {
- throw new DatabaseException('No database configured');
+ throw new DatabaseException("No database configuration section named '$confKey'");
}
}
- return self::$connections[$confName];
+ return self::$connections[$confKey];
}
}
?>
=== modified file 'src/database/PostgreSqlConnection.php'
--- src/database/PostgreSqlConnection.php 2009-04-08 22:39:04 +0000
+++ src/database/PostgreSqlConnection.php 2009-04-12 02:36:42 +0000
@@ -39,7 +39,7 @@
$this->host = isset($conf['host']) ? $conf['host'] : null;
$this->username = $conf['username'];
$this->password = $conf['password'];
- $this->database = $conf['database'];
+ $this->database = $conf['name'];
$this->autocommit = isset($conf['autocommit']) ? $conf['autocommit'] : false;
}
=== modified file 'src/database/SqliteConnection.php'
--- src/database/SqliteConnection.php 2009-04-09 16:33:12 +0000
+++ src/database/SqliteConnection.php 2009-04-12 02:36:42 +0000
@@ -30,7 +30,7 @@
* @param array $conf Database configuration.
*/
public function __construct ($conf) {
- $this->database = $conf['database'];
+ $this->database = $conf['name'];
$this->autocommit = isset($conf['autocommit']) ? $conf['autocommit'] : false;
}
=== modified file 'src/interceptors/AbstractInterceptor.php'
--- src/interceptors/AbstractInterceptor.php 2008-10-20 16:41:10 +0000
+++ src/interceptors/AbstractInterceptor.php 2009-04-14 21:28:43 +0000
@@ -11,9 +11,9 @@
* the instance. This requires that the concrete interceptor implementation defines the properties as
* protected or public.
*
- * @param array $conf Configuration for this interceptor, if any.
+ * @param array $conf Configuration for this interceptor, if any.
*/
- public function __construct ($conf = null) {
+ public function __construct (array $conf = null) {
if ($conf !== null) {
foreach ($conf as $param => $value) {
$this->$param = $value;
=== modified file 'src/interceptors/AuthInterceptor.php'
--- src/interceptors/AuthInterceptor.php 2008-10-20 16:41:10 +0000
+++ src/interceptors/AuthInterceptor.php 2009-04-14 21:28:43 +0000
@@ -30,7 +30,7 @@
* Initialize this interceptor by making sure the user model has been included.
*/
public function init () {
- import($this->userModel);
+ Autoloader::load($this->userModel);
}
/**
=== modified file 'src/interceptors/UtilsInterceptor.php'
--- src/interceptors/UtilsInterceptor.php 2008-10-20 16:41:10 +0000
+++ src/interceptors/UtilsInterceptor.php 2009-04-12 02:36:42 +0000
@@ -12,7 +12,7 @@
$utils = Config::get('loadUtils');
if (is_array($utils)) {
foreach ($utils as $util) {
- import($util, 'util');
+ Utils::import($util);
}
}
=== modified file 'src/kolibri.php'
--- src/kolibri.php 2008-12-09 23:12:48 +0000
+++ src/kolibri.php 2009-04-08 17:13:50 +0000
@@ -1,84 +1,40 @@
<?php
/**
- * The entryway to the Kolibri framework. Essential core files are included, config initialized and
- * processing of the request is started.
- */
-
-/*
- * Defines the root directory of the framework. By default this in a directory named kolibri within the
- * document root, but this can be changed at will.
- */
-define('ROOT', dirname(__FILE__) . '/kolibri');
-
-/*
- * Application specific directories. Modify to your setup if different from the default.
- */
-define('APP_PATH', dirname(__FILE__));
-define('ACTIONS_PATH', APP_PATH . '/actions');
-define('MODELS_PATH', APP_PATH . '/models');
-define('VIEW_PATH', APP_PATH . '/views');
+ * The HTTP gateway to the Kolibri framework. Essential core files are included, config initialized and
+ * processing of the HTTP request is started.
+ * The constants defined here, ROOT and APP_PATH, can be changed from their default values by
+ * using SetEnv in the .htaccess file or in an appropriate directive in httpd.conf.
+ */
+
+/*
+ * Defines the root directory of the Kolibri framework. By default this is a directory named
+ * 'kolibri' within the document root.
+ */
+if ($rootDir = getenv('KOLIBRI_ROOT')) {
+ define('ROOT', $rootDir);
+}
+else {
+ define('ROOT', dirname(__FILE__) . '/kolibri');
+}
+
+/*
+ * Defines the root directory for the application's actions, models, views etc. By default this is
+ * the same directory as this kolibri.php file.
+ */
+if ($appDir = getenv('KOLIBRI_APP')) {
+ define('APP_PATH', $appDir);
+}
+else {
+ define('APP_PATH', dirname(__FILE__));
+}
// Require essential files. Others are loaded as needed.
require(ROOT . '/core/Config.php');
require(ROOT . '/core/RequestProcessor.php');
-require(ROOT . '/core/Dispatcher.php');
-require(ROOT . '/core/InterceptorFactory.php');
-require(ROOT . '/core/Request.php');
// Init configuration
Config::getInstance();
$kolibri = new RequestProcessor();
$kolibri->process();
-
-/**
- * Loads framework classes and application models as required.
- */
-function __autoload ($name) {
- static $autoload;
-
- if (!isset($autoload)) {
- require(ROOT . '/conf/autoload.php');
- $autoload = $autoloadClasses;
- }
-
- if (isset($autoload[$name])) {
- require(ROOT . $autoload[$name]);
- }
- else {
- // Class not specified in autoload.php, but may be available in include_path (i.e. /lib)
- if (!import($name)) {
- require($name . '.php');
- }
- }
-}
-
-/**
- * Loads model, DAO or util files. Used internally by __autoload(), or by the user for importing utils like
- * this:
- *
- * import('urls', 'util');
- */
-function import ($file, $type = 'model') {
- static $files;
-
- switch ($type) {
- case 'model': $path = MODELS_PATH; break;
- case 'dao': $path = MODELS_PATH . '/dao'; break;
- case 'util': $path = ROOT . '/utils'; break;
- default: throw new Exception("Could not import $file. Type $type is unknown.");
- }
-
- $filePath = $path . "/$file.php";
- if (!isset($files[$filePath])) {
- if (!file_exists($filePath)) {
- return false;
- }
-
- $files[$filePath] = true;
- require($filePath);
- }
-
- return true;
-}
?>
=== modified file 'src/lib/ErrorHandler.php'
--- src/lib/ErrorHandler.php 2009-04-03 16:04:34 +0000
+++ src/lib/ErrorHandler.php 2009-04-14 21:28:43 +0000
@@ -56,19 +56,19 @@
$data = array('exception' => $exception, 'action' => $this->action);
$logging = Config::get('logging');
- if (!empty($logging) && isset($logging['enabled']) && $logging['enabled']) {
- if (!empty($logging['logFile']) && is_writable($logging['logFile'])) {
+ if (!empty($logging) && isset($logging['level']) && $logging['level'] == true) {
+ if (!empty($logging['file']) && is_writable($logging['file'])) {
error_log($exception->getMessage() . " ({$exception->getFile()}:{$exception->getLine()})",
- 3, $logging['logFile']);
+ 3, $logging['file']);
}
else {
error_log($exception->getMessage() . " ({$exception->getFile()}:{$exception->getLine()})");
}
- if (!empty($logging['logEmail'])) {
+ if (!empty($logging['email'])) {
// Send email to admin
$email = $this->generateMail($exception);
- $email->addRecipient($logging['logEmail']);
+ $email->addRecipient($logging['email']);
$mailer = new MailService();
$mailer->send($email);
}
=== modified file 'src/lib/MailService.php'
--- src/lib/MailService.php 2008-12-08 20:34:09 +0000
+++ src/lib/MailService.php 2009-04-12 02:36:42 +0000
@@ -30,16 +30,16 @@
$this->IsSmtp();
$this->PluginDir = ROOT . '/lib/phpmailer/';
- $this->Host = $conf['smtp_host'];
- $this->SMTPAuth = $conf['smtp_auth'];
+ $this->Host = $conf['smtp.host'];
+ $this->SMTPAuth = $conf['smtp.auth'];
- if (isset($conf['smtp_port'])) {
- $this->Port = $conf['smtp_port'];
+ if (isset($conf['smtp.port'])) {
+ $this->Port = $conf['smtp.port'];
}
if ($this->SMTPAuth) {
- $this->Username = $conf['smtp_username'];
- $this->Password = $conf['smtp_password'];
+ $this->Username = $conf['smtp.username'];
+ $this->Password = $conf['smtp.password'];
}
$this->CharSet = 'utf-8';
@@ -82,8 +82,8 @@
public function send ($mail) {
if (empty($mail->from)) {
$conf = Config::get('mail');
- $mail->from = $conf['from'];
- $mail->fromName = $conf['from_name'];
+ $mail->from = $conf['from.email'];
+ $mail->fromName = $conf['from.name'];
}
$this->From = $mail->from;
=== modified file 'src/models/DataAccessProxy.php'
--- src/models/DataAccessProxy.php 2009-01-30 03:44:24 +0000
+++ src/models/DataAccessProxy.php 2009-04-08 00:04:08 +0000
@@ -38,7 +38,7 @@
public function __construct ($modelProxy, $modelClass) {
$this->modelProxy = $modelProxy;
$this->daoClass = $modelClass . 'Dao';
- import($this->daoClass, 'dao');
+ Autoloader::load($this->daoClass);
$reflection = new ReflectionClass($modelClass);
$this->modelPk = $reflection->getConstant('PK');
@@ -48,7 +48,7 @@
* Imports the DAO we are proxying upon wakeup (i.e. if a proxied model is put in the session).
*/
public function __wakeup () {
- import($this->daoClass, 'dao');
+ Autoloader::load($this->daoClass);
}
/**
=== modified file 'src/utils/arrays.php'
--- src/utils/arrays.php 2009-01-30 23:18:57 +0000
+++ src/utils/arrays.php 2009-04-12 02:36:42 +0000
@@ -20,6 +20,38 @@
}
/**
+ * A blend of PHP's built-in array_merge() and array_merge_recursive(). This function merges
+ * recursively for array values, but like array_merge() it will overwrite a value in the
+ * first array with the corresponding value from the second array for equal keys.
+ *
+ * @param array $first Base array in which values will be merged into.
+ * @param array $second Array to merge values from.
+ * @param array ⦠Optional extra arrays to merge values from.
+ * @return array Array with merged values from all input arrays.
+ */
+function array_merge_recursive_distinct (array $first, array $second/*, array â¦*/) {
+ $merged = $first;
+
+ if (is_array($second)) {
+ foreach ($second as $key => $value) {
+ if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
+ $merged[$key] = array_merge_recursive_distinct($merged[$key], $value);
+ }
+ else {
+ $merged[$key] = $value;
+ }
+ }
+ }
+
+ if (func_num_args() > 2) {
+ $params = array_merge(array($merged), array_slice(func_get_args(), 2));
+ return call_user_func_array('array_merge_recursive_distinct', $params);
+ }
+
+ return $merged;
+}
+
+/**
* BETA
* Converts a multi-dimensional array into a one-dimensional array. Items which are objects are
* converted to arrays consisting of their properties and considered just another dimension. An
=== modified file 'src/views/error.php'
--- src/views/error.php 2009-04-03 16:04:34 +0000
+++ src/views/error.php 2009-04-14 21:28:43 +0000
@@ -23,9 +23,10 @@
<div id="error">
<?php
/*
- * If logging is disable, we display detailed error.
+ * If logging is disabled we display detailed error, but only if we're not in production mode.
*/
-if (!isset($config['logging']['enabled']) || !$config['logging']['enabled']):
+if (Config::getMode() != Config::PRODUCTION
+ && !isset($config['logging']['level']) || $config['logging']['level'] == false):
?>
<h1><?php echo get_class($exception) ?></h1>
<p id="message">
Follow ups