/index.php
PHP | 2874 lines | 1506 code | 370 blank | 998 comment | 357 complexity | f3d42b366d37edb6d208f40583a74f9c MD5 | raw file
Large files files are truncated, but you can click here to view the full file
- <?php
- /**
- * Atomik Framework
- * Copyright (c) 2008-2009 Maxime Bouroumeau-Fuseau
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- *
- * @package Atomik
- * @author Maxime Bouroumeau-Fuseau
- * @copyright 2008-2009 (c) Maxime Bouroumeau-Fuseau
- * @license http://www.opensource.org/licenses/mit-license.php
- * @link http://www.atomikframework.com
- */
-
- define('ATOMIK_VERSION', '2.2.2');
- !defined('ATOMIK_APP_ROOT') && define('ATOMIK_APP_ROOT', './app');
-
- /* -------------------------------------------------------------------------------------------
- * APPLICATION CONFIGURATION
- * ------------------------------------------------------------------------------------------ */
-
- Atomik::reset(array(
-
- 'app' => array(
-
- /* @var string */
- 'default_action' => 'index',
-
- /* The name of the layout
- * Add multiple layouts using an array (will be rendered in reverse order)
- * @var array|bool|string */
- 'layout' => false,
-
- /* @var bool */
- 'disable_layout' => false,
-
- /* An array where keys are route names and their value is an associative
- * array of default values
- * @see Atomik::route()
- * @var array */
- 'routes' => array(),
-
- /* @var bool */
- 'force_uri_extension' => false,
-
- /* List of escaping profiles where keys are profile names and their
- * value an array of callbacks
- * @see Atomik::escape()
- * @var array */
- 'escaping' => array(
- 'default' => array('htmlspecialchars', 'nl2br')
- ),
-
- /* @see Atomik::filter()
- * @var array */
- 'filters' => array(
-
- /* @var array */
- 'rules' => array(),
-
- /* @var array */
- 'callbacks' => array(),
-
- /* @var string */
- 'default_message' => 'The %s field failed to validate',
-
- /* @var string */
- 'required_message' => 'The %s field must be filled'
- ),
-
- /* @see Atomik::render()
- * @var array */
- 'views' => array(
-
- /* @var string */
- 'file_extension' => '.phtml',
-
- /* Alternative rendering engine
- * @see Atomik::_render()
- * @var callback */
- 'engine' => false,
-
- /* @var string */
- 'default_context' => 'html',
-
- /* The GET parameter to retrieve the current context
- * @var string */
- 'context_param' => 'format',
-
- /* List of contexts where keys are the context name.
- * Contexts can specify:
- * - prefix (string): the view filename's extension prefix
- * - layout (bool): whether the layout should be rendered
- * - content_type (string): the HTTP response content type
- * @var array */
- 'contexts' => array(
- 'html' => array(
- 'prefix' => '',
- 'layout' => true,
- 'content_type' => 'text/html'
- ),
- 'ajax' => array(
- 'prefix' => '',
- 'layout' => false,
- 'content_type' => 'text/html'
- ),
- 'xml' => array(
- 'prefix' => 'xml',
- 'layout' => false,
- 'content_type' => 'text/xml'
- ),
- 'json' => array(
- 'prefix' => 'json',
- 'layout' => false,
- 'content_type' => 'application/json'
- )
- )
- ),
-
- /* A parameter in the route that will allow to specify the http method
- * (override the request's method). False to disable
- * @var string */
- 'http_method_param' => '_method',
-
- /* @var array */
- 'allowed_http_methods' => array('GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'HEAD', 'OPTIONS', 'CONNECT')
-
- )
- ));
-
- /* -------------------------------------------------------------------------------------------
- * CORE CONFIGURATION
- * ------------------------------------------------------------------------------------------ */
-
- Atomik::set(array(
-
- /* @var array */
- 'plugins' => array(),
-
- /* @var array */
- 'atomik' => array(
-
- /* Atomik's filename
- * @var string */
- 'scriptname' => __FILE__,
-
- /* Base url, set to null for auto detection
- * @var string */
- 'base_url' => null,
-
- /* Whether url rewriting is activated on the server
- * @var bool */
- 'url_rewriting' => false,
-
- /* @var bool */
- 'debug' => false,
-
- /* The GET parameter used to retreive the action
- * @var string */
- 'trigger' => 'action',
-
- /* Whether to register the class autoloader
- * @var bool */
- 'class_autoload' => true,
-
- /* @var bool */
- 'start_session' => true,
-
- /* Plugin's assets path template.
- * %s will be replaced by the plugin's name
- * @see Atomik::pluginAsset()
- * @var string */
- 'plugin_assets_tpl' => 'app/plugins/%s/assets/',
-
- /* @var array */
- 'log' => array(
-
- /* @var bool */
- 'register_default' => false,
-
- /* From which level to start logging messages
- * @var int */
- 'level' => LOG_WARNING,
-
- /* Message template for the default logger
- * @see Atomik::logToFile()
- * @var string */
- 'message_template' => '[%date%] [%level%] %message%'
- ),
-
- /* @var array */
- 'dirs' => array(
- 'app' => ATOMIK_APP_ROOT,
- 'plugins' => array(ATOMIK_APP_ROOT . '/modules', ATOMIK_APP_ROOT . '/plugins/'),
- 'actions' => ATOMIK_APP_ROOT . '/actions/',
- 'views' => ATOMIK_APP_ROOT . '/views/',
- 'layouts' => array(ATOMIK_APP_ROOT . '/layouts', ATOMIK_APP_ROOT . '/views'),
- 'helpers' => ATOMIK_APP_ROOT . '/helpers/',
- 'includes' => array(ATOMIK_APP_ROOT . '/includes/', ATOMIK_APP_ROOT . '/libraries/'),
- 'overrides' => ATOMIK_APP_ROOT . '/overrides/'
- ),
-
- /* @var array */
- 'files' => array(
- 'index' => 'index.php',
- 'config' => ATOMIK_APP_ROOT . '/config', // without extension
- 'bootstrap' => ATOMIK_APP_ROOT . '/bootstrap.php',
- 'pre_dispatch' => ATOMIK_APP_ROOT . '/pre_dispatch.php',
- 'post_dispatch' => ATOMIK_APP_ROOT . '/post_dispatch.php',
- '404' => ATOMIK_APP_ROOT . '/404.php',
- 'error' => ATOMIK_APP_ROOT . '/error.php',
- 'log' => ATOMIK_APP_ROOT . '/log.txt'
- ),
-
- /* @var bool */
- 'catch_errors' => false,
-
- /* @var bool */
- 'display_errors' => true,
-
- /* @var array */
- 'error_report_attrs' => array(
- 'atomik-error' => 'style="padding: 10px"',
- 'atomik-error-title' => 'style="font-size: 1.3em; font-weight: bold; color: #FF0000"',
- 'atomik-error-lines' => 'style="width: 100%; margin-bottom: 20px; background-color: #fff;'
- . 'border: 1px solid #000; font-size: 0.8em"',
- 'atomik-error-line' => '',
- 'atomik-error-line-error' => 'style="background-color: #ffe8e7"',
- 'atomik-error-line-number' => 'style="background-color: #eeeeee"',
- 'atomik-error-line-text' => '',
- 'atomik-error-stack' => ''
- )
-
- ),
-
- /* @var int */
- 'start_time' => time() + microtime()
- ));
-
- /* -------------------------------------------------------------------------------------------
- * CORE
- * ------------------------------------------------------------------------------------------ */
-
- // creates the A function (shortcut to Atomik::get)
- if (!function_exists('A')) {
- /**
- * Shortcut function to Atomik::get()
- * Useful when dealing with selectors
- *
- * @see Atomik::get()
- * @return mixed
- */
- function A()
- {
- $args = func_get_args();
- return call_user_func_array(array('Atomik', 'get'), $args);
- }
- }
-
- // starts Atomik unless ATOMIK_AUTORUN is set to false
- if (!defined('ATOMIK_AUTORUN') || ATOMIK_AUTORUN === true) {
- Atomik::run();
- }
-
- /**
- * Exception class for Atomik
- *
- * @package Atomik
- */
- class Atomik_Exception extends Exception {}
-
- /**
- * Atomik Framework Main class
- *
- * @package Atomik
- */
- final class Atomik
- {
- /**
- * Global store
- *
- * This property is used to stored all data accessed using get(), set()...
- *
- * @var array
- */
- public static $store = array();
-
- /**
- * Global store to reset to
- *
- * @var array
- */
- private static $reset = array();
-
- /**
- * Loaded plugins
- *
- * When a plugin is loaded, its name is saved in this array to
- * avoid loading it twice.
- *
- * @var array
- */
- private static $plugins = array();
-
- /**
- * Registered events
- *
- * The array keys are event names and their value is an array with
- * the event callbacks
- *
- * @var array
- */
- private static $events = array();
-
- /**
- * Selectors namespaces
- *
- * The array keys are the namespace name and the associated value is
- * the callback to call when the namespace is used
- *
- * @var array
- */
- private static $namespaces = array('flash' => array('Atomik', '_getFlashMessages'));
-
- /**
- * Execution contexts
- *
- * Each call to Atomik::execute() creates a context.
- *
- * @var array
- */
- private static $execContexts = array();
-
- /**
- * Pluggable applications
- *
- * @var array
- */
- private static $pluggableApplications = array();
-
- /**
- * Registered methods
- *
- * @var array
- */
- private static $methods = array();
-
- /**
- * Already loaded helpers
- *
- * @var array
- */
- private static $loadedHelpers = array();
-
- /**
- * Starts Atomik
- *
- * If dispatch is false, you will have to manually dispatch the request and exit.
- *
- * @param string $uri
- * @param bool $dispatch Whether to dispatch
- */
- public static function run($uri = null, $dispatch = true)
- {
- // wrap the whole app inside a try/catch block to catch all errors
- try {
- @chdir(dirname(A('atomik/scriptname')));
-
- // loads the config file
- if (file_exists($filename = self::get('atomik/files/config') . '.php')) {
- // PHP
- if (is_array($config = include($filename))) {
- self::set($config);
- }
-
- } else if (file_exists($filename = self::get('atomik/files/config') . '.ini')) {
- // INI
- if (($data = parse_ini_file($filename, true)) === false) {
- throw new Atomik_Exception('INI configuration malformed');
- }
- self::set(self::_dimensionizeArray($data, '.'), null, false);
-
- } else if (file_exists($filename = self::get('atomik/files/config') . '.json')) {
- // JSON
- if (($config = json_decode(file_get_contents($filename), true)) === null) {
- throw new Atomik_Exception('JSON configuration malformed');
- }
- self::set($config);
- }
-
- // adds includes dirs to php include path
- $includePaths = self::path(self::get('atomik/dirs/includes', array()), true);
- $includePaths[] = get_include_path();
- set_include_path(implode(PATH_SEPARATOR, $includePaths));
-
- // registers the error handler
- if (self::get('atomik/catch_errors', true) == true) {
- set_error_handler('Atomik::_errorHandler');
- }
-
- // sets the error reporting to all errors if debug mode is on
- if (self::get('atomik/debug', false) == true) {
- error_reporting(E_ALL | E_STRICT);
- }
-
- // default logger
- if (self::get('atomik/log/register_default', false) == true) {
- self::listenEvent('Atomik::Log', 'Atomik::logToFile');
- }
-
- // starts the session
- if (self::get('atomik/start_session', true) == true) {
- session_start();
- self::$store['session'] = &$_SESSION;
- }
-
- // registers the class autoload handler
- if (self::get('atomik/class_autoload', true) == true) {
- if (!function_exists('spl_autoload_register')) {
- throw new Atomik_Exception('Missing spl_autoload_register function');
- }
- spl_autoload_register('Atomik::autoload');
- }
-
- // cleans the plugins array
- $plugins = array();
- foreach (self::get('plugins', array()) as $key => $value) {
- if (!is_string($key)) {
- $key = $value;
- $value = array();
- }
- $plugins[ucfirst($key)] = (array) $value;
- }
- self::set('plugins', $plugins, false);
-
- // loads plugins
- // this method allows plugins that are being loaded to modify the plugins array
- $disabledPlugins = array();
- while (count($pluginsToLoad = array_diff(array_keys(self::get('plugins')), self::getLoadedPlugins(), $disabledPlugins)) > 0) {
- foreach ($pluginsToLoad as $plugin) {
- if (self::loadPlugin($plugin) === false) {
- $disabledPlugins[] = $plugin;
- }
- }
- }
-
- // loads bootstrap file
- if (file_exists($filename = self::get('atomik/files/bootstrap'))) {
- require($filename);
- }
-
- // core is starting
- self::fireEvent('Atomik::Start', array(&$cancel));
- if ($cancel) {
- self::end(true);
- }
- self::log('Starting', LOG_DEBUG);
-
- // checks if url rewriting is used
- if (!self::has('atomik/url_rewriting')) {
- self::set('atomik/url_rewriting', isset($_SERVER['REDIRECT_URL']) || isset($_SERVER['REDIRECT_URI']));
- }
-
- // dispatches
- if ($dispatch) {
- if (!self::dispatch($uri)) {
- self::trigger404();
- }
- // end
- self::end(true);
- }
-
- } catch (Exception $e) {
- self::log('Exception caught: ' . $e->getMessage(), LOG_ERR);
-
- // checks if we really want to catch errors
- if (!self::get('atomik/catch_errors', true)) {
- throw $e;
- }
-
- self::fireEvent('Atomik::Error', array($e));
- header('Location: ', false, 500);
- self::renderException($e);
- self::end(false);
- }
- }
-
- /**
- * Dispatches the request
- *
- * It takes an URI, applies routes, executes the action and renders the view.
- * If $uri is null, the value of the GET parameter specified as the trigger
- * will be used.
- *
- * @param string $uri
- * @param bool $allowPluggableApplication Whether to allow plugin application to be loaded
- */
- public static function dispatch($uri = null, $allowPluggableApplication = true)
- {
- self::fireEvent('Atomik::Dispatch::Start', array(&$uri, &$allowPluggableApplication, &$cancel));
- if ($cancel) {
- return true;
- }
-
- // checks if it's needed to auto discover the uri
- if ($uri === null) {
-
- // retreives the requested uri
- $trigger = self::get('atomik/trigger', 'action');
- if (isset($_GET[$trigger]) && !empty($_GET[$trigger])) {
- $uri = trim($_GET[$trigger], '/');
- }
-
- // retreives the base url
- if (self::get('atomik/base_url', null) === null) {
- if (self::get('atomik/url_rewriting') && (isset($_SERVER['REDIRECT_URL']) || isset($_SERVER['REDIRECT_URI']))) {
- // finds the base url from the redirected url
- $redirectUrl = isset($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['REDIRECT_URI'];
- self::set('atomik/base_url', substr($redirectUrl, 0, -strlen($_GET[$trigger])));
- } else {
- // finds the base url from the script name
- self::set('atomik/base_url', rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\') . '/');
- }
- }
-
- } else {
- // sets the user defined request
- // retreives the base url
- if (self::get('atomik/base_url', null) === null) {
- // finds the base url from the script name
- self::set('atomik/base_url', rtrim(dirname($_SERVER['SCRIPT_NAME']), '/\\') . '/');
- }
- }
-
- // default uri
- if (empty($uri)) {
- $uri = self::get('app/default_action', 'index');
- }
-
- // routes the request
- if (($request = self::route($uri, $_GET)) === false) {
- return false;
- }
-
- // checking if no dot are in the action name to avoid any hack attempt and if no
- // underscore is use as first character in a segment
- if (strpos($request['action'], '..') !== false || substr($request['action'], 0, 1) == '_'
- || strpos($request['action'], '/_') !== false) {
- return false;
- }
-
- self::set('request_uri', $uri);
- self::set('request', $request);
- if (!self::has('full_request_uri')) {
- self::set('full_request_uri', $uri);
- }
-
- self::fireEvent('Atomik::Dispatch::Uri', array(&$uri, &$request, &$cancel));
- if ($cancel) {
- return true;
- }
-
- // checks if the uri triggers a pluggable application
- if ($allowPluggableApplication) {
- foreach (self::$pluggableApplications as $plugin => $pluggAppConfig) {
- if (!self::uriMatch($pluggAppConfig['route'], $uri)) {
- continue;
- }
-
- // rewrite uri
- $baseAction = trim($pluggAppConfig['route'], '/*');
- $uri = substr(trim($uri, '/'), strlen($baseAction));
- if ($uri == self::get('app/default_action')) {
- $uri = '';
- }
- self::set('atomik/base_action', $baseAction);
-
- // dispatches the pluggable application
- return self::dispatchPluggableApplication($plugin, $uri, $pluggAppConfig['config']);
- }
- }
-
- // fetches the http method
- $httpMethod = $_SERVER['REQUEST_METHOD'];
- if (($param = self::get('app/http_method_param', false)) !== false) {
- // checks if the route parameter to override the method is defined
- $httpMethod = self::get($param, $httpMethod, $request);
- }
- if (!in_array($httpMethod, self::get('app/allowed_http_methods'))) {
- // specified method not allowed
- return false;
- }
- self::set('app/http_method', strtoupper($httpMethod));
-
- // fetches the view context
- $viewContext = self::get(self::get('app/views/context_param', 'format'),
- self::get('app/views/default_context', 'html'), $request);
- self::set('app/view_context', $viewContext);
-
- // retreives view context params and prepare the response
- if (($viewContextParams = self::get('app/views/contexts/' . $viewContext, false)) !== false) {
- if ($viewContextParams['layout'] !== true) {
- self::set('app/layout', $viewContextParams['layout']);
- }
- header('Content-type: ' . self::get('content-type', 'text/html', $viewContextParams));
- }
-
- // configuration is ok, ready to dispatch
- self::fireEvent('Atomik::Dispatch::Before', array(&$cancel));
- if ($cancel) {
- return true;
- }
-
- self::log('Dispatching action ' . $request['action'], LOG_DEBUG);
-
- // pre dispatch action
- if (file_exists($filename = self::get('atomik/files/pre_dispatch'))) {
- require($filename);
- }
-
- // executes the action
- ob_start();
- if (($content = self::execute(self::get('request/action'), $viewContext, false)) === false) {
- return false;
- }
- $content = ob_get_clean() . $content;
-
- // renders the layouts if enable
- if (($layouts = self::get('app/layout', false)) !== false) {
- if (!empty($layouts) && !self::get('app/disable_layout', false)) {
- foreach (array_reverse((array) $layouts) as $layout) {
- $content = self::renderLayout($layout, $content);
- }
- }
- }
-
- // echoes the content
- self::fireEvent('Atomik::Output::Before', array(&$content));
- echo $content;
- self::fireEvent('Atomik::Output::After', array($content));
-
- // dispatch done
- self::fireEvent('Atomik::Dispatch::After');
-
- // post dispatch action
- if (file_exists($filename = self::get('atomik/files/post_dispatch'))) {
- require($filename);
- }
-
- return true;
- }
-
- /**
- * Checks if an uri matches the pattern. The pattern can contain the * wildcard at the
- * end to specify that it matches the target and all its child segments.
- *
- * @param string $pattern
- * @param string $uri Default is the current request uri
- * @return bool
- */
- public static function uriMatch($pattern, $uri = null)
- {
- if ($uri === null) {
- $uri = self::get('request_uri');
- }
- $uri = trim($uri, '/');
- $pattern = trim($pattern, '/');
-
- if (substr($pattern, -1) == '*') {
- $pattern = rtrim($pattern, '/*');
- return strlen($uri) >= strlen($pattern) && substr($uri, 0, strlen($pattern)) == $pattern;
- } else {
- return $uri == $pattern;
- }
- }
-
- /**
- * Parses an uri to extract parameters
- *
- * Routes defines how to extract parameters from an uri. They can
- * have additional default parameters.
- * There are two kind of routes:
- *
- * - segments:
- * the uri is divided into path segments. Each segment can be
- * either static or a parameter (indicated by :).
- * eg: /archives/:year/:month
- *
- * - regexp:
- * uses a regexp against the uri. Must be enclosed using # instead of
- * slashes parameters must be specified as named subpattern.
- * eg: #^archives/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})$#
- *
- * If no route matches, the default route (ie :action) will automatically be used.
- * Unless you're using regexps, any additional segments will be added as parameters
- * eg: /archives/2009/01/id/1, will also have the id=1 parameter
- *
- * You can also name your routes using the @name parameter (which won't be included
- * in the returned params). Named route can then be use with Atomik::url()
- *
- * @param string $uri
- * @param array $params Additional parameters which are not in the uri
- * @param array $routes Uses app/routes if null
- * @return array|boolean Route parameters or false if it fails
- */
- public static function route($uri, $params = array(), $routes = null)
- {
- if ($routes === null) {
- $routes = self::get('app/routes');
- }
-
- self::fireEvent('Atomik::Router::Start', array(&$uri, &$routes, &$params));
-
- // extracts uri information
- $components = parse_url($uri);
- $uri = trim($components['path'], '/');
- $uriSegments = explode('/', $uri);
- $uriExtension = false;
- if (isset($components['query'])) {
- parse_str($components['query'], $query);
- $params = array_merge($query, $params);
- }
-
- // extract the file extension from the uri
- $lastSegment = array_pop($uriSegments);
- if (($dot = strrpos($lastSegment, '.')) !== false) {
- $uriExtension = substr($lastSegment, $dot + 1);
- $lastSegment = substr($lastSegment, 0, $dot);
- }
- $uriSegments[] = $lastSegment;
-
- // checks if the extension must be present
- if (self::get('app/force_uri_extension', false) && $uriExtension === false) {
- return false;
- }
-
- // searches for a route matching the uri
- $found = false;
- $request = array();
- foreach ($routes as $route => $default) {
- if (!is_string($route)) {
- $route = $default;
- $default = array();
- }
-
- // removes the route name from the default params
- if (isset($default['@name'])) {
- unset($default['@name']);
- }
-
- // regexp
- if ($route{0} == '#') {
- if (!preg_match($route, $uri, $matches)) {
- continue;
- }
- unset($matches[0]);
- $found = true;
- $request = array_merge($default, $matches);
- break;
- }
-
- $valid = true;
- $segments = explode('/', trim($route, '/'));
- $request = $default;
- $extension = false;
-
- // extract the file extension from the route
- $lastSegment = array_pop($segments);
- if (($dot = strrpos($lastSegment, '.')) !== false) {
- $extension = substr($lastSegment, $dot + 1);
- $lastSegment = substr($lastSegment, 0, $dot);
- }
- $segments[] = $lastSegment;
-
- // checks the extension
- if ($extension !== false) {
- if ($extension{0} == ':') {
- // extension is a parameter
- if ($uriExtension !== false) {
- $request[substr($extension, 1)] = $uriExtension;
- } else if (!isset($request[substr($extension, 1)])) {
- // no uri extension and no default value
- continue;
- }
- } else if ($extension != $uriExtension) {
- continue;
- }
- }
-
- for ($i = 0, $count = count($segments); $i < $count; $i++) {
- if (substr($segments[$i], 0, 1) == ':') {
- // segment is a parameter
- if (isset($uriSegments[$i])) {
- // this segment is defined in the uri
- $request[substr($segments[$i], 1)] = $uriSegments[$i];
- $segments[$i] = $uriSegments[$i];
- } else if (!array_key_exists(substr($segments[$i], 1), $default)) {
- // not defined in the uri and no default value
- $valid = false;
- break;
- }
- } else {
- // fixed segment
- if (!isset($uriSegments[$i]) || $uriSegments[$i] != $segments[$i]) {
- $valid = false;
- break;
- }
- }
- }
-
- // checks if route is valid and if the action param is set
- if ($valid && isset($request['action'])) {
- $found = true;
- // if there's remaining segments in the uri, adding them as params
- if (($count = count($uriSegments)) > ($start = count($segments))) {
- for ($i = $start; $i < $count; $i += 2) {
- if (isset($uriSegments[$i + 1])) {
- $request[$uriSegments[$i]] = $uriSegments[$i + 1];
- }
- }
- }
- break;
- }
- }
-
- if (!$found) {
- // route not found, creating default route
- $request = array(
- 'action' => implode('/', $uriSegments),
- self::get('app/views/context_param', 'format') => $uriExtension === false ?
- self::get('app/views/default_context', 'html') : $uriExtension
- );
- }
-
- $request = array_merge($params, $request);
- self::fireEvent('Atomik::Router::End', array($uri, &$request));
-
- return $request;
- }
-
- /**
- * Executes an action
- *
- * Searches for a file called after the action (with the php extension) inside
- * directories set under atomik/dirs/actions. If no file is found, it will search
- * for a view and render it. If neither of them are found, it will throw an exception.
- *
- * @see Atomik::render()
- * @param string $action The action name. The HTTP method can be prefixed after a dot
- * @param bool|string $viewContext The view context. Set to false to not render the view and return the variables or to true for the request's context
- * @param bool $triggerError Whether to throw an exception if an error occurs
- * @return mixed The output of the view or an array of variables or false if an error occured
- */
- public static function execute($action, $viewContext = true, $triggerError = true)
- {
- $view = $action;
- $vars = array();
- $render = $viewContext !== false;
-
- if (is_bool($viewContext)) {
- // using the request's context
- $viewContext = self::get('app/view_context');
- }
- // appends the context's prefix to the view name
- $prefix = self::get('app/views/contexts/' . $viewContext . '/prefix', $viewContext);
- if (!empty($prefix)) {
- $view .= '.' . $prefix;
- }
-
- // creates the execution context
- $context = array('action' => &$action, 'view' => &$view, 'render' => &$render);
- self::$execContexts[] =& $context;
-
- self::fireEvent('Atomik::Execute::Start', array(&$action, &$context, &$triggerError));
- if ($action === false) {
- return false;
- }
-
- // checks if the method is specified in $action
- if (($dot = strrpos($action, '.')) !== false) {
- // it is, extract it
- $methodAction = $action;
- $method = substr($action, $dot + 1);
- $action = substr($action, 0, $dot);
- } else {
- // use the current request's http method
- $method = strtolower(self::get('app/http_method'));
- $methodAction = $action . '.' . $method;
- }
-
- // filenames
- $actionFilename = self::actionFilename($action);
- $methodActionFilename = self::actionFilename($methodAction);
- $viewFilename = self::viewFilename($view);
-
- // checks if at least one of the action files or the view file is defined
- if ($actionFilename === false && $methodActionFilename === false && $viewFilename === false) {
- if ($triggerError) {
- throw new Atomik_Exception('Action ' . $action . ' does not exist');
- }
- return false;
- }
-
- if ($viewFilename === false) {
- // no view files, disabling view
- $view = false;
- }
-
- self::fireEvent('Atomik::Execute::Before', array(&$action, &$context, &$actionFilename, &$methodActionFilename, &$triggerError));
-
- // class name if using a class
- $className = str_replace(' ', '_', ucwords(str_replace('/', ' ', $action)));
-
- // executes the global action
- if ($actionFilename !== false) {
- // executes the action in its own scope and fetches defined variables
- $vars = self::executeFile($actionFilename, array(), $className . 'Action');
- }
-
- // executes the method specific action
- if ($methodActionFilename !== false) {
- // executes the action in its own scope and fetches defined variables
- $vars = self::executeFile($methodActionFilename, $vars, $className . ucfirst($method) . 'Action');
- }
-
- self::fireEvent('Atomik::Execute::After', array($action, &$context, &$vars, &$triggerError));
-
- // deletes the execution context
- array_pop(self::$execContexts);
-
- // returns $vars if the view should not be rendered
- if ($render === false) {
- return $vars;
- }
-
- // no view
- if ($view === false) {
- return '';
- }
-
- // renders the view associated to the action
- return self::render($view, $vars, $triggerError);
- }
-
- /**
- * Executes the action file inside a clean scope and returns defined variables
- *
- * @see Atomik::_execute()
- * @param string $actionFilename
- * @param array $vars Variables that will be available in the scope
- * @param string $className If a class name is specified, it will try to execute its execute() static method
- * @return array
- */
- public static function executeFile($actionFilename, $vars = array(), $className = null)
- {
- self::fireEvent('Atomik::Executefile', array(&$actionFilename, &$vars));
-
- $atomik = new self();
- $vars = $atomik->_execute($actionFilename, $vars, $className);
- $atomik = null;
-
- return $vars;
- }
-
- /**
- * Executes a file inside a clean scope and returns defined variables
- *
- * @internal
- * @param string $__filename Filename
- * @param array $__vars An array containing key/value pairs that will be transformed to variables accessible inside the file
- * @return string View output
- */
- private function _execute($__filename, $__vars = array(), $__className = null)
- {
- extract($__vars);
- require($__filename);
- $vars = array();
-
- // checks if a class is used
- if ($__className !== null && class_exists($__className, false) && method_exists($__className, 'execute')) {
- // call the class execute() static method
- if (($vars = call_user_func(array($__className, 'execute'))) === null) {
- $vars = array();
- }
- $vars = array_merge(get_class_vars($__className), $vars);
- }
-
- // retreives "public" variables (not prefixed with an underscore)
- $definedVars = get_defined_vars();
- foreach ($definedVars as $name => $value) {
- if (substr($name, 0, 1) != '_') {
- $vars[$name] = $value;
- }
- }
-
- return $vars;
- }
-
- /**
- * Prevents the view of the actionfrom which it's called to be rendered
- */
- public static function noRender()
- {
- if (count(self::$execContexts)) {
- self::$execContexts[count(self::$execContexts) - 1]['view'] = false;
- }
- }
-
- /**
- * Modifies the view associted to the action from which it's called
- *
- * @param string $view View name
- */
- public static function setView($view)
- {
- if (count(self::$execContexts)) {
- self::$execContexts[count(self::$execContexts) - 1]['view'] = $view;
- }
- }
-
- /**
- * Renders a view
- *
- * Searches for a file called after the view inside
- * directories configured in atomik/dirs/views. If no file is found, an
- * exception is thrown unless $triggerError is false.
- *
- * @param string $view The view name
- * @param array $vars An array containing key/value pairs that will be transformed to variables accessible inside the view
- * @param bool $triggerError Whether to throw an exception if an error occurs
- * @param array $dirs Directories where view files are stored
- * @return string|bool
- */
- public static function render($view, $vars = array(), $triggerError = true, $dirs = null)
- {
- if ($dirs === null) {
- $dirs = self::get('atomik/dirs/views');
- }
-
- self::fireEvent('Atomik::Render::Start', array(&$view, &$vars, &$dirs, &$triggerError));
-
- // view filename
- if (($filename = self::viewFilename($view, $dirs)) === false) {
- if ($triggerError) {
- throw new Atomik_Exception('View ' . $view . ' not found');
- }
- return false;
- }
-
- self::fireEvent('Atomik::Render::Before', array(&$view, &$vars, &$filename, $triggerError));
-
- $output = self::renderFile($filename, $vars);
-
- self::fireEvent('Atomik::Render::After', array($view, &$output, $vars, $filename, $triggerError));
-
- return $output;
- }
-
- /**
- * Renders a file using a filename which will not be resolved.
- *
- * @param string $filename Filename
- * @param array $vars An array containing key/value pairs that will be transformed to variables accessible inside the file
- * @return string The output of the rendered file
- */
- public static function renderFile($filename, $vars = array())
- {
- self::fireEvent('Atomik::Renderfile::Before', array(&$filename, &$vars));
-
- if (($callback = self::get('app/views/engine', false)) !== false) {
- if (!is_callable($callback)) {
- throw new Atomik_Exception('The specified rendering engine callback cannot be called');
- }
- $output = $callback($filename, $vars);
-
- } else {
- $atomik = new self();
- $output = $atomik->_render($filename, $vars);
- $atomik = null;
- }
-
- self::fireEvent('Atomik::Renderfile::After', array($filename, &$output, $vars));
-
- return $output;
- }
-
- /**
- * Renders a layout
- *
- * @param string $layout Layout name
- * @param string $content The content that will be available in the layout in the $contentForLayout variable
- * @param array $vars An array containing key/value pairs that will be transformed to variables accessible inside the layout
- * @param bool $triggerError Whether to throw an exception if an error occurs
- * @param array $dirs Directories where to search for layouts
- * @return string
- */
- public static function renderLayout($layout, $content, $vars = array(), $triggerError = true, $dirs = null)
- {
- if ($dirs === null) {
- $dirs = self::get('atomik/dirs/layouts');
- }
-
- self::fireEvent('Atomik::Renderlayout', array(&$layout, &$content, &$vars, &$triggerError, &$dirs));
- $vars['contentForLayout'] = $content;
-
- return self::render($layout, $vars, $triggerError, $dirs);
- }
-
- /**
- * Renders a file (internal/default rendering engine)
- *
- * @internal
- * @param string $__filename Filename
- * @param array $__vars An array containing key/value pairs that will be transformed to variables accessible inside the file
- * @return string View output
- */
- private function _render($__filename, $__vars = array())
- {
- extract($__vars);
- ob_start();
- include($__filename);
- return ob_get_clean();
- }
-
- /**
- * Loads an helper file
- *
- * @param string $helperName
- * @param array $dirs Directories where to search for helpers
- */
- public static function loadHelper($helperName, $dirs = null)
- {
- if (isset(self::$loadedHelpers[$helperName])) {
- return;
- }
-
- if ($dirs === null) {
- $dirs = self::get('atomik/dirs/helpers');
- }
-
- self::fireEvent('Atomik::Loadhelper::Before', array(&$helperName, &$dirs));
-
- if (($filename = self::path($helperName . '.php', $dirs)) === false) {
- throw new Atomik_Exception('Helper ' . $helperName . ' not found');
- }
-
- include $filename;
-
- if (!function_exists($helperName)) {
- // searching for an helper defined as a class
- $camelizedHelperName = str_replace(' ', '', ucwords(str_replace('_', ' ', $helperName)));
- $className = $camelizedHelperName . 'Helper';
-
- if (!class_exists($className, false)) {
- // neither a function nor a class has been found
- throw new Exception('Helper ' . $helperName . ' file found but no function or class matching the helper name');
- }
- // helper defined as a class
- self::$loadedHelpers[$helperName] = array(new $className(), $camelizedHelperName);
-
- } else {
- // helper defined as a function
- self::$loadedHelpers[$helperName] = $helperName;
- }
-
- self::fireEvent('Atomik::Loadhelper::After', array($helperName, $dirs));
- }
-
- /**
- * Executes an helper
- *
- * @param string $helperName
- * @param array $args Arguments for the helper
- * @param array $dirs Directories where to search for helpers
- * @return mixed
- */
- public static function helper($helperName, $args = array(), $dirs = null)
- {
- self::loadHelper($helperName, $dirs);
- return call_user_func_array(self::$loadedHelpers[$helperName], $args);
- }
-
- /**
- * PHP magic method to handle calls to helper in views
- *
- * @param string $helperName
- * @param array $args
- * @return mixed
- */
- public function __call($helperName, $args)
- {
- if (method_exists('Atomik', $helperName)) {
- return call_user_func_array(array('Atomik', $helperName), $args);
- }
- if (isset(self::$methods[$helperName])) {
- return call_user_func_array(self::$methods[$helperName], $args);
- }
- return self::helper($helperName, $args);
- }
-
- /**
- * Disables the layout
- *
- * @param bool $disable Whether to disable the layout
- */
- public static function disableLayout($disable = true)
- {
- self::set('app/disable_layout', $disable);
- }
-
- /**
- * Fires the Atomik::End event and exits the application
- *
- * @param bool $success Whether the application exit on success or because an error occured
- * @param bool $writeSession Whether to call session_write_close() before exiting
- */
- public static function end($success = false, $writeSession = true)
- {
- self::fireEvent('Atomik::End', array($success, &$writeSession));
-
- if ($writeSession) {
- session_write_close();
- }
-
- self::log('Ending', LOG_DEBUG);
- exit;
- }
-
-
- /* -------------------------------------------------------------------------------------------
- * Accessors
- * ------------------------------------------------------------------------------------------ */
-
- /**
- * Sets a key/value pair in the store
- *
- * If the first argument is an array, values are merged recursively.
- * The array is first dimensionized
- * You can set values from sub arrays by using a path-like key.
- * For example, to set the value inside the array $array[key1][key2]
- * use the key 'key1/key2'
- * Can be used on any array by specifying the third argument
- *
- * @see Atomik::_dimensionizeArray()
- * @param array|string $key Can be an array to set many key/value
- * @param mixed $value
- * @param bool $dimensionize Whether to use Atomik::_dimensionizeArray() on $key
- * @param array $array The array on which the operation is applied
- * @param array $add Whether to add values or replace them
- */
- public static function set($key, $value = null, $dimensionize = true, &$array = null, $add = false)
- {
- // if $data is null, uses the global store
- if ($array === null) {
- $array = &self::$store;
- }
-
- // setting a key directly
- if (is_string($key)) {
- $parentArrayKey = strpos($key, '/') !== false ? dirname($key) : null;
- $key = basename($key);
-
- $parentArray = &self::getRef($parentArrayKey, $array);
- if ($parentArray === null) {
- $dimensionizedParentArray = self::_dimensionizeArray(array($parentArrayKey => null));
- $array = self::_mergeRecursive($array, $dimensionizedParentArray);
- $parentArray = &self::getRef($parentArrayKey, $array);
- }
-
- if ($add !== false) {
- if (!isset($parentArray[$key]) || $parentArray[$key] === null) {
- if (!is_array($value)) {
- $parentArray[$key] = $value;
- return;
- }
- $parentArray[$key] = array();
- } else if (!is_array($parentArray[$key])) {
- $parentArray[$key] = array($parentArray[$key]);
- }
-
- $value = is_array($value) ? $value : array($value);
- if ($add == 'prepend') {
- $parentArray[$key] = array_merge_recursive($value, $parentArray[$key]);
- } else {
- $parentArray[$key] = array_merge_recursive($parentArray[$key], $value);
- }
- } else {
- $parentArray[$key] = $value;
- }
-
- return;
- }
-
- if (!is_array($key)) {
- throw new Atomik_Exception('The first parameter of Atomik::set() must be a string or an array, ' . gettype($key) . ' given');
- }
-
- if ($dimensionize) {
- $key = self::_dimensionizeArray($key);
- }
-
- // merges the store and the array
- if ($add) {
- $array = array_merge_recursive($array, $key);
- } else {
- $array = self::_mergeRecursive($array, $key);
- }
- }
-
- /**
- * Adds a value to the array pointed by the key
- *
- * If the first argument is an arr…
Large files files are truncated, but you can click here to view the full file