/sparkplug/sparkplug.php
PHP | 2320 lines | 1142 code | 300 blank | 878 comment | 103 complexity | a887e62d6aa40a4c8bdcd1d56e6260d1 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1
- <?php
- /*
- Copyright 2009-2012 Sam Weiss
- All Rights Reserved.
- This file is part of Spark/Plug.
- Spark/Plug is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- if (!defined('spark/plug'))
- {
- header('HTTP/1.1 403 Forbidden');
- exit('<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"><html><head><title>403 Forbidden</title></head><body><h1>Forbidden</h1><p>You don\'t have permission to access the requested resource on this server.</p></body></html>');
- }
- define('spark_plug_version', '1.1.0');
- /**
- * The SparkException class is our custom exception class.
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- class SparkException extends Exception
- {
- private $_vars;
-
- /**
- * Construct a new exception.
- *
- * @param string $message
- * @param int $code
- * @return SparkException object
- */
- public function __construct($message, $code = 0, $vars = NULL)
- {
- parent::__construct($message, $code);
- $this->_vars = empty($vars) ? array() : $vars;
- }
- /**
- * Return vars associated with this exception.
- *
- * @return array
- */
- public function vars()
- {
- return $this->_vars;
- }
- /**
- * Return named var associated with this exception.
- *
- * @param string $name
- * @return mixed
- */
- public function getVar($name, $default = NULL)
- {
- return isset($this->_vars[$name]) ? $this->_vars[$name] : $default;
- }
- }
- /**
- * The SparkHTTPException class is used to automatically convert HTTP status codes to exceptions.
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- class SparkHTTPException extends SparkException
- {
- private $_httpStatusCode;
- private $_httpStatusText;
-
- /**
- * Convert HTTP status code to equivalent exception.
- *
- * @param string $message
- * @param int $code
- * @return SparkException object
- */
- public function __construct($httpStatusCode, $httpStatusText, $message = '', $vars = '')
- {
- parent::__construct($message, 0, $vars);
- $this->_httpStatusCode = $httpStatusCode;
- $this->_httpStatusText = $httpStatusText;
- }
- /**
- * Return HTTP status code associated with this exception.
- *
- * @return int
- */
- public function getHTTPStatusCode()
- {
- return $this->_httpStatusCode;
- }
-
- /**
- * Return HTTP status text associated with this exception.
- *
- * @return string
- */
- public function getHTTPStatusText()
- {
- return $this->_httpStatusText;
- }
-
- /**
- * Return HTTP status messages associated with this exception.
- * Suitable for use in HTTP header.
- *
- * @return string
- */
- public function getHTTPStatus()
- {
- return $this->_httpStatusCode . ' ' . $this->_httpStatusText;
- }
- }
- class SparkHTTPException_BadRequest extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(400, 'Bad Request', $message, $vars);
- }
- }
- class SparkHTTPException_Unauthorized extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(401, 'Unauthorized', $message, $vars);
- }
- }
- class SparkHTTPException_Forbidden extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- isset($message) || $message = 'Permission denied.';
- parent::__construct(403, 'Forbidden', $message, $vars);
- }
- }
- class SparkHTTPException_NotFound extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- isset($message) || $message = 'The page you requested was not found.';
- parent::__construct(404, 'Not Found', $message, $vars);
- }
- }
- class SparkHTTPException_MethodNotAllowed extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(405, 'Method Not Allowed', $message, $vars);
- }
- }
- class SparkHTTPException_Conflict extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(409, 'Conflict', $message, $vars);
- }
- }
- class SparkHTTPException_Gone extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(410, 'Gone', $message, $vars);
- }
- }
- class SparkHTTPException_LengthRequired extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(411, 'Length Required', $message, $vars);
- }
- }
- class SparkHTTPException_RequestEntityTooLarge extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(413, 'Request Entity Too Large', $message, $vars);
- }
- }
- class SparkHTTPException_RequestURITooLong extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(414, 'Request-URI Too Long', $message, $vars);
- }
- }
- class SparkHTTPException_UnsupportedMediaType extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(415, 'Unsupported Media Type', $message, $vars);
- }
- }
- class SparkHTTPException_InternalServerError extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- isset($message) || $message = 'Your request could not be processed.';
- parent::__construct(500, 'Internal Server Error', $message, $vars);
- }
- }
- class SparkHTTPException_NotImplemented extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(501, 'Not Implemented', $message, $vars);
- }
- }
- class SparkHTTPException_ServiceUnavailable extends SparkHTTPException
- {
- public function __construct($message = NULL, $vars = NULL)
- {
- parent::__construct(503, 'Service Unavailable', $message, $vars);
- }
- }
- /**
- * The SparkPHPException class is used to automatically convert PHP errors to exceptions.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class SparkPHPException
- {
- /**
- * Convert PHP error to equivalent exception.
- *
- * @param int $code
- * @param string $message
- * @param string $file
- * @param int $line
- * @throw ErrorException
- */
- public static function errorHandler($code, $message, $file, $line)
- {
- throw new ErrorException($message, 0, $code, $file, $line);
- }
- }
- /**
- * The SparkUtil class contains some utility functions commonly needed by web apps.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class SparkUtil
- {
- const kRequestMethod_HEAD = 1;
- const kRequestMethod_GET = 2;
- const kRequestMethod_POST = 3;
- const kRequestMethod_PUT = 4;
- const kRequestMethod_DELETE = 5;
- const kRequestMethod_OPTIONS = 6;
- private static $_http_methods = array
- (
- 'head' => self::kRequestMethod_HEAD,
- 'get' => self::kRequestMethod_GET,
- 'post' => self::kRequestMethod_POST,
- 'put' => self::kRequestMethod_PUT,
- 'delete' => self::kRequestMethod_DELETE,
- 'options' => self::kRequestMethod_OPTIONS,
- );
-
- /**
- * Return document root.
- *
- * @return string
- */
- final public static function doc_root()
- {
- return $_SERVER['DOCUMENT_ROOT'];
- }
- /**
- * Return whether this page was accessed securely.
- *
- * @return boolean
- */
- final public static function is_https()
- {
- return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
- }
- /**
- * Return scheme string.
- *
- * @return string
- */
- final public static function scheme()
- {
- return self::is_https() ? 'https://' : 'http://';
- }
- /**
- * Return host string.
- *
- * @return string
- */
- final public static function host()
- {
- return $_SERVER['HTTP_HOST'];
- }
- /**
- * Return server name string.
- *
- * @return string
- */
- final public static function server_name()
- {
- return $_SERVER['SERVER_NAME'];
- }
- /**
- * Return server ip address string.
- *
- * @return string
- */
- final public static function server_addr()
- {
- return $_SERVER['SERVER_ADDR'];
- }
- /**
- * Return script name.
- *
- * @return string
- */
- final public static function script_name()
- {
- return $_SERVER['SCRIPT_NAME'];
- }
- /**
- * Return original request URI.
- *
- * @return string
- */
- final public static function request_uri()
- {
- return $_SERVER['REQUEST_URI'];
- }
- /**
- * Return original request URI minus any query parameters.
- *
- * @return string
- */
- final public static function request_uri_base()
- {
- return self::remove_query_from_url(self::request_uri());
- }
- /**
- * Return original request URL.
- *
- * @return string
- */
- final public static function self_url()
- {
- if (($host = self::host()) === '')
- {
- $host = self::server_name();
- }
- return self::scheme() . $host . self::request_uri();
- }
- /**
- * Return original request method.
- *
- * @return string
- */
- final public static function request_method()
- {
- return !empty($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : '';
- }
- /**
- * Return original query string.
- *
- * @return string
- */
- final public static function query_string()
- {
- return !empty($_SERVER['QUERY_STRING']) ? $_SERVER['QUERY_STRING'] : '';
- }
- /**
- * Return user's browser agent string.
- *
- * @return string
- */
- final public static function user_agent()
- {
- return isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '';
- }
- /**
- * Return user's IP address.
- *
- * @return string
- */
- final public static function remote_ip()
- {
- return $_SERVER['REMOTE_ADDR'];
- }
-
- /**
- * Return referring url.
- *
- * @return string
- */
- final public static function referrer_url()
- {
- return isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '';
- }
-
- /**
- * Return referring url.
- *
- * @return string
- */
- final public static function get_http_method($method = NULL)
- {
- if (!$method)
- {
- $method = self::request_method();
- }
- if ($method = @self::$_http_methods[strtolower($method)])
- {
- return $method;
- }
-
- return NULL;
- }
-
- /**
- * Return boolean indicating whether the current request was made via ajax.
- *
- * @return boolean
- */
- final public static function is_ajax_request()
- {
- return (strtolower(@$_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest');
- }
-
- /**
- * Return array of request headers.
- *
- * @return array
- */
- final public static function request_headers()
- {
- if (!function_exists('apache_request_headers'))
- {
- function apache_request_headers()
- {
- $headers = array();
- foreach($_SERVER as $key => $value)
- {
- if (substr_compare($key, 'HTTP_', 0, 5) === 0)
- {
- $key = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key,5)))));
- $headers[$key] = $value;
- }
- }
- return $headers;
- }
- }
-
- return apache_request_headers();
- }
-
- /**
- * Check if two IP addresses match or are similar.
- *
- * @param string $ip1
- * @param string $ip2
- * @return boolean
- */
- final public static function match_ip($ip1, $ip2, $matchOctets = 4)
- {
- // full IP match?
-
- if ($ip1 == $ip2)
- {
- return true;
- }
-
- // entire IP address did not match (probably a pesky proxy server)
- // break the address into octets
-
- $octets1 = explode('.' , $ip1, 4);
- $octets2 = explode('.' , $ip2, 4);
-
- for ($match = 0; $match < $matchOctets; ++$match)
- {
- if ($octets1[$match] != $octets2[$match])
- {
- return false;
- }
- }
-
- return true;
- }
- /**
- * Check for integer value, and force to type integer if true.
- *
- * @param any &$val
- * @return boolean
- */
- final public static function valid_int(&$val)
- {
- if (is_int($val))
- {
- return true;
- }
- if (preg_match('/^[-+]?\d+$/', $val))
- {
- $val = (int)$val;
- return true;
- }
- return false;
- }
- /**
- * Check if URL is valid.
- *
- * httpurl = https?://{hostport}(/{hpath}(\?{search})?)?
- * hostport = host(:{port})?
- * host = {hostname}|{hostnumber}
- * hostname = ({domainlabel}\.)*{toplabel}
- * domainlabel = {alphadigit}(({alphadigit}|\-)*{alphadigit})?
- * toplabel = {alpha}(({alphadigit}|\-)*{alphadigit})?
- * hostnumber = {digits}\.{digits}\.{digits}\.{digits}
- * port = {digits}
- * hpath = {hsegment}(/{hsegment})*
- * hsegment = ({uchar}|[;:@&=])*
- * search = ({uchar}|[;:@&=])*
- * uchar = {unreserved}|{escape}
- * unreserved = {alphadigit}|{safe}|{extra}
- * escape = %{hex}{hex}
- * safe = [\$\-\_\.\+]
- * extra = [\!\*\'\(\)\,]
- * hex = [0-9a-f]
- * alpha = [a-z]
- * digit = [0-9]
- * alphadigit = [a-z0-9]
- *
- * hsegment = (?:[a-z0-9\$\-\_\.\+\!\*\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*
- *
- * return preg_match('#^https?://(?:(?:(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)*[a-z](?:[a-z0-9\-]*[a-z0-9])?)|(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}))(?::[0-9]{1,5})?(?:/(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*(?:/(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)*(?:\?(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)?)?$#i', $item);
- *
- *
- * @param string $url
- * @return boolean
- */
- final public static function valid_url($url, $requireScheme = true)
- {
- return preg_match('#^(?:https?://)' . ($requireScheme ? '' : '?') . '(?:(?:(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?\.)*[a-z](?:[a-z0-9\-]*[a-z0-9])?)|(?:(?:[0-9]{1,3}\.){3}[0-9]{1,3}))(?::[0-9]{1,5})?(?:/(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)*(?:\?(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)?$#i', $url) ? true : false;
- }
- /**
- * Check if URL path is valid.
- *
- * @param string $url
- * @return boolean
- */
- final public static function valid_url_path($url)
- {
- return preg_match('#^(?:/(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)*(?:\?(?:[a-z0-9\$\-\_\.\+\!\*\\\'\(\)\,\;\:\@\&\=\~]|(?:%[0-9a-f]{2}))*)?$#i', $url) ? true : false;
- }
- /**
- * Check if email address is valid according to HTML5 spec (http://dev.w3.org/html5/spec/states-of-the-type-attribute.html#valid-e-mail-address).
- *
- * @param string $email
- * @return boolean
- */
- final public static function valid_email($email)
- {
- return preg_match('/^([[:alnum:]!#$%&\'*+-\/=?^_`{|}.])+@[[:alnum:]-]+(\.[[:alnum:]-]+)*$/', $email) ? true : false;
- }
- /**
- * Extract scheme and host from url.
- *
- * @param string $url
- * @return string
- */
- final public static function extract_scheme_host_from_url($url)
- {
- return preg_replace('#^(https?://[^/]+)(.*)$#', '$1', $url);
- }
- /**
- * Extract host from url.
- *
- * @param string $url
- * @return string
- */
- final public static function extract_host_from_url($url)
- {
- return preg_replace('#^https?://([^/]+)(.*)$#', '$1', $url);
- }
- /**
- * Remove query string from url.
- *
- * @param string $url
- * @return string
- */
- final public static function remove_query_from_url($url)
- {
- return preg_replace('/\?.*$/', '', $url);
- }
- /**
- * Create a nice big random string.
- *
- * @return string
- */
- final public static function make_nonce($length = 32)
- {
- $nonce = '';
- do
- {
- $nonce .= sha1(uniqid(mt_rand(), true));
- } while (strlen($nonce) < $length);
- return substr($nonce, 0, $length);
- }
- /**
- * Create a version 4 random UUID.
- *
- * @return string
- */
- final public static function make_uuid($canonical = true)
- {
- $format = $canonical ? '%04x%04x-%04x-%04x-%04x-%04x%04x%04x' : '%04x%04x%04x%04x%04x%04x%04x%04x';
- return sprintf
- (
- $format,
- mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
- mt_rand(0, 0x0fff) | 0x4000,
- mt_rand(0, 0x3fff) | 0x8000,
- mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
- );
- }
- /**
- * Encode a UUID into smaller ASCII string.
- *
- * @param string $uuid
- * @return string
- */
- final public static function encode_uuid($uuid)
- {
- return str_replace(array('/','+'), array('_','-'), substr(base64_encode(pack('H*', str_replace('-', '', $uuid))), 0, 22));
- }
-
- /**
- * Decode an encoded UUID.
- *
- * @param string $uuid
- * @return string
- */
- final public static function decode_uuid($uuid)
- {
- $decoded = bin2hex(base64_decode(str_replace(array('_','-'), array('/','+'), $uuid) . '=='));
- return substr($decoded, 0, 8) . '-' . substr($decoded, 8, 4) . '-' . substr($decoded, 12, 4) . '-' . substr($decoded, 16, 4) . '-' . substr($decoded, 20);
- }
-
- /**
- * Clean (erase) all output buffers and turn off output buffering.
- */
- final public static function ob_end_clean_all()
- {
- while(@ob_end_clean())
- ;
- }
-
- /**
- * Determine whether a value contains a valid serialized string, and optionally
- * return the unserialized result.
- *
- * @param any $value value to check
- * @param and &$decoded unserialized result (only valid if method returns true)
- * @return bool
- */
- final public static function is_serialized($value, &$decoded = NULL)
- {
- if (!is_string($value))
- {
- return false;
- }
-
- if ($value === 'b:0;')
- {
- $decoded = false;
- return true;
- }
-
- return (($decoded = @unserialize($value)) !== false);
- }
-
- /**
- * Determine whether a value contains a valid JSON-encoded string, and optionally
- * return the decoded result.
- *
- * @param any $value value to check
- * @param and &$decoded decoded result (only valid if method returns true)
- * @return bool
- */
- final public static function is_json($value, &$decoded = NULL)
- {
- if (!is_string($value))
- {
- return false;
- }
-
- return (($decoded = @json_decode($value)) !== NULL) || (json_last_error() === JSON_ERROR_NONE);
- }
-
- }
- /**
- * The SparkInflector class is used to convert strings to/from under_score and CamelCase.
- * This class may be used by core classes, application classes and plugs.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class SparkInflector
- {
- /**
- * Convert an indentifier to readable text ("example_identifier" -> "Example Identifier").
- *
- * @param string $s
- * @return string
- */
- final public static function humanize($s)
- {
- return ucfirst(str_replace('_', ' ', $s));
- }
- /**
- * Convert readable text to identifier ("Example Identifier" -> "example_identifier").
- *
- * @param string $s
- * @return string
- */
- final public static function dehumanize($s)
- {
- return strtolower(str_replace(' ', '_', $s));
- }
- /**
- * Convert an indentifier to CamelCase ("example_identifier" -> "ExampleIdentifier").
- *
- * @param string $s
- * @return string
- */
- final public static function camelize($s)
- {
- return str_replace(' ', '', ucwords(str_replace('_', ' ', $s)));
- }
- /**
- * Convert an indentifier from CamelCase to underscores ("ExampleIdentifier" -> "example_identifier").
- *
- * @param string $s
- * @return string
- */
- final public static function decamelize($s)
- {
- return trim(strtolower(preg_replace(array('/([A-Z])([a-z])/', '/([a-z])([A-Z])/'), array('_$1$2', '$1_$2'), $s)), '_');
- }
- }
- /**
- * The SparkConfig class is used to store and retrieve configuration data.
- * This class may be used by core classes, application classes and plugs.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class SparkConfig
- {
- private $_config; // key-value dictionary of configuration settings
-
- /**
- * Construct a new Config object.
- *
- * @param array $config: key-value dictionary of configuration values
- */
- public function __construct(&$config)
- {
- $this->_config =& $config;
- }
- /**
- * Retrieve a previously stored configuration item.
- * If the item is not found, return the provided default value (if any).
- *
- * @param string $key Name of configuration parameter to retrieve
- * @param string $default Default value to return if configuration parameter not found
- * @return any Value of configuration parameter if found, or default if not found
- */
- final public function get($key, $default = NULL)
- {
- return self::paramGet($this->_config, $key, $default);
- }
- /**
- * Store a new configuration item.
- *
- * @param string $key Name of configuration parameter to store
- * @param string $val Value to store
- */
- final public function set($key, $val)
- {
- $this->_config[$key] = $val;
- }
- /**
- * Retrieve a value from a parameter array.
- * If the item is not found, return the provided default value (if any).
- *
- * @param string $key Name of parameter to retrieve
- * @param string $default Default value to return if parameter not found
- * @return any Value of parameter if found, or default if not found
- */
- final public static function paramGet($arr, $key, $default = NULL)
- {
- return isset($arr[$key]) ? $arr[$key] : $default;
- }
- /**
- * Retrieve a (normalized) PHP configuration setting.
- *
- * @param string $key Name of configuration parameter to retrieve
- * @param string $default Default value to return if configuration parameter not found
- * @return string Value of configuration parameter if found, or default if not found
- */
- final public static function php_ini_get($key, $default = NULL)
- {
- switch (strtolower($val = ini_get($key)))
- {
- case '':
- return $default;
-
- case '0':
- case 'off':
- case 'false':
- case 'no':
- case 'none':
- return false;
-
- case '1':
- case 'on':
- case 'true':
- case 'yes':
- return true;
-
- default:
- return $val;
- }
- }
- }
- /**
- * The SparkObserver class is used to register observable events and their observers.
- * Once events and observers have been registered, events may be fired and all observers
- * registered for those events will be notified.
- * This class may be used by core classes, application classes and plugs.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class SparkObserver
- {
- private $_events; // list of observable events
-
- /**
- * Construct a new SparkObserver object.
- *
- */
- public function __construct()
- {
- $this->_events = array();
- }
- /**
- * Register an observer for one or more events.
- *
- * An observer can be one of:
- *
- * a function name
- * a lambda function
- * an array: (class name, static method name)
- * an array: (object, method name)
- *
- * One or more events can be registered for each observer.
- * Events can be specified as either an array of event names or as a
- * comma-delimited string containing one or more event names.
- *
- * @param string $observer Called when registered events fires
- * @param string $events Event (or events) to observe
- */
- final public function observe($observer, $events)
- {
- if (!is_array($events))
- {
- $events = explode(',', str_replace(' ', '', $events));
- }
-
- // add the observer to the event list for each event it is observing
-
- foreach($events as $event)
- {
- $this->_events[$event][] = $observer;
- }
- }
- /**
- * Notify observers that an event has fired.
- * Observers will be passed the event name plus any additional args passed to
- * this method.
- *
- * @param mixed $events Event (or events) to fire
- * @param mixed $params1...$paramN variable argument list to pass to observer
- */
- final public function notify($events)
- {
- if (!is_array($events))
- {
- $events = explode(',', str_replace(' ', '', $events));
- }
-
- // A "composite event" is of the form "general:more_specific:most_specific",
- // a series of component events separated by colons in which each subsequent
- // component is more specific than the preceding component. For composite
- // events, we send multiple notifications, so listeners may listen on on as
- // specific a component as desired. For example, given composite event:
- // "site_changed:content:page_saved:edited"
- // we would send the event notification to listeners of the folowing four events:
- // "site_changed"
- // "site_changed:content"
- // "site_changed:content:page_saved"
- // "site_changed:content:page_saved:edited"
- //
- // Note that the specific event (in this case, "site_changed:content:page_saved:edited")
- // is always passed as the first argument to the listener's callback function.
-
- foreach ($events as $event)
- {
- $first = true;
- foreach (explode(':', $event) as $component)
- {
- if (!empty($component))
- {
- if ($first)
- {
- $notification = $component;
- $first = false;
- }
- else
- {
- $notification .= ':' . $component;
- }
- if (isset($this->_events[$notification]))
- {
- foreach ($this->_events[$notification] as $observer)
- {
- $args = func_get_args();
- $args[0] = $event;
- call_user_func_array($observer, $args);
- }
- }
- }
- }
- }
- }
- }
- /**
- * The Spark class is Spark/Plug's object factory. It loads plugs and constructs
- * object hierarchies on the fly when instantiating a new object.
- * This class may be used by core classes, application classes and plugs.
- * This is one of the few *final* core classes in Spark/Plug (not extensible by plugs).
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- final class Spark
- {
- private $_spDir; // Spark/Plug directory
- private $_observer; // global observer
- private $_plugList; // list if installed plugs indexed by class name
- private $_plugMap; // maps class names to list of plugs that implement/extend that class name
- private $_plugCacheDir; // directory containing cached plugs
- private $_plugDirStack; // directories containing plugs
- // --------------------------------------------------------------------------
- /**
- * Construct a new Spark object, bootstrap the application, and "activate" plugs.
- * Only plugs that extend (derive directly from) the SparkPlug class need be specified
- * at this time. Additional plugs may be activated at any time by invoking the findPlugs()
- * method of this class.
- *
- * @param array $classes Array of names of plugs to "activate" (make available to the running application)
- * @param string|array $searchPaths Optional path(s) to search for plugs
- * @param string $cacheDir Optional path to plug cache directory
- */
- final public function __construct($classes = array(), $searchPaths = NULL, $cacheDir = NULL)
- {
- $this->_observer = new SparkObserver();
- $this->_plugList = array();
- $this->_plugMap = array();
- $this->_plugCacheDir = NULL;
- $this->_spDir = dirname(__FILE__);
- if (!empty($cacheDir))
- {
- $this->setPlugCacheDir($cacheDir);
- $this->_observer->observe(array($this, 'flushPlugCache'), 'Spark:cache:request_flush');
- }
-
- if (empty($searchPaths))
- {
- $searchPaths = array(NULL);
- }
- foreach ($searchPaths as $dir)
- {
- if (empty($dir))
- {
- $this->_plugDirStack[] = "{$this->_spDir}/plugs";
- }
- else
- {
- if ($dir[0] !== '/')
- {
- $dir = "{$this->_spDir}/{$dir}";
- }
- $this->_plugDirStack[] = $dir;
- }
- }
- $this->findPlugs($classes);
- // load the SparkPlug class and all its plugs
- $this->loadClass('SparkPlug');
- }
- // --------------------------------------------------------------------------
- /**
- * Return the global observer object
- *
- * @return object Observer object
- */
- final public function observer()
- {
- return $this->_observer;
- }
- // --------------------------------------------------------------------------
- /**
- * Initialize the Spark object and load plugs that extend core classes.
- * This method is invoked at the bottom of this file.
- *
- */
- final public function init()
- {
- // load all the core classes and their respective plugs
-
- $this->loadClass('SparkModel');
- $this->loadClass('SparkView');
- $this->loadClass('SparkController');
- $this->loadClass('SparkApplication');
- }
- // --------------------------------------------------------------------------
- /**
- * Returns the path to the plug cache directory.
- *
- * @return string Path to plug cache directory
- */
- // --------------------------------------------------------------------------
- public function getPlugCacheDir()
- {
- return $this->_plugCacheDir;
- }
- // --------------------------------------------------------------------------
- /**
- * Call this method to change the plug cache directory for subsequent plug cache operations.
- * Used to dynamically change where code is cached based on caller's criteria (eg. host name).
- * Should be called as early as possible. Note that some classes may have already been cached
- * by the time this method is called.
- *
- * @param string $cacheDir Path to new plug cache directory
- *
- * @return string Previous cache directory path
- */
- // --------------------------------------------------------------------------
- public function setPlugCacheDir($cacheDir)
- {
- $oldDir = $this->_plugCacheDir;
- $this->_plugCacheDir = $cacheDir;
- if ($this->_plugCacheDir[0] !== '/')
- {
- $this->_plugCacheDir = "{$this->_spDir}/{$this->_plugCacheDir}";
- }
- return $oldDir;
- }
- // --------------------------------------------------------------------------
- /**
- * Call this method to flush the plug code cache.
- * Not usually called directly, as it can be invoked via the "Spark:cache:request_flush"
- * notification.
- */
- // --------------------------------------------------------------------------
- public function flushPlugCache()
- {
- if (!empty($this->_plugCacheDir))
- {
- $iter = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->_plugCacheDir), RecursiveIteratorIterator::CHILD_FIRST);
- foreach ($iter as $file)
- {
- $filePath = $file->getPathname();
- if ($file->isDir())
- {
- $fileName = $file->getFilename();
- if ($fileName !== '.' && $fileName !== '..')
- {
- rmdir($filePath);
- }
- }
- else
- {
- unlink($filePath);
- }
- }
- $this->_observer->notify('Spark:cache:flush');
- }
- }
- // --------------------------------------------------------------------------
- /**
- * Call this method to make additional plugs available to be loaded.
- * Subsequent invocations of manufacture() will consider these additional plugs when
- * determining which plugs to load. If $searchPaths parameter is provided, it will
- * be used instead of the main plug directory for the scope of this invocation only.
- * If $callback is provided, it will be called for each plug and passed the plug as a
- * parameter.
- *
- * @param array $classes Array of plug names to make available
- * @param string|array $searchPaths Optional path(s) to search for plugs
- * @param string $callback Callback (optional)
- */
- final public function findPlugs($classes, $searchPaths = NULL, $callback = NULL)
- {
- if (!empty($classes))
- {
- if (empty($searchPaths))
- {
- $searchPaths = $this->_plugDirStack;
- }
- elseif (!is_array($searchPaths))
- {
- $searchPaths = array($searchPaths);
- }
-
- foreach ($classes as $class)
- {
- // allow nested plug directories
-
- if ($subPath = (strpos($class, '/') === false) ? '' : '/'.dirName($class))
- {
- $class = basename($class);
- }
- foreach ($searchPaths as $plugDir)
- {
- $dirName = $plugDir . $subPath . '/' . SparkInflector::decamelize($class);
- if (file_exists($dirName))
- {
- break;
- }
- }
- // load plug's manifest
- unset($plug);
- require($dirName . '/manifest.php');
-
- // add plug to plug map
- if (isset($plug))
- {
- $plugs = $plug['plugs'];
- unset($plug['plugs']);
-
- foreach ($plugs as $nextPlug)
- {
- if ($callback)
- {
- $nextPlug = array_merge($plug, $nextPlug); // make sure callback has all the plug info
- // Note: We do not use call_user_func() here because it does not allow pass-by-reference
- if (is_string($callback))
- {
- if (!$callback($nextPlug))
- {
- continue;
- }
- }
- elseif (is_array($callback))
- {
- if (!$callback[0]->$callback[1]($nextPlug))
- {
- continue;
- }
- }
- }
- $name = $nextPlug['name'];
-
- // if an explicit path is provided, use it, otherwise deduce a path from the plug's name
-
- if ($path = @$nextPlug['path'])
- {
- // explicit path can be absolute, relative to plugs directory, or relative to plug's own directory
-
- if ($path[0] != '/')
- {
- if (!strncasecmp($path, 'plugs/', 6))
- {
- $path = $plugDir . '/' . substr($path, 6);
- }
- else
- {
- $path = $dirName . '/' . $path;
- }
- }
- }
- else
- {
- $path = $dirName . '/' . SparkInflector::decamelize($name) . '.php';
- }
-
- // plug can be one of two types:
- //
- // 1. an extension plug that augments an existing class,
- // or
- // 2. a base plug that simply provides some new service
-
- if ($extends = @$nextPlug['extends'])
- {
- // extension plugs can declare a preferred load order to help avoid conflicts
-
- $order = isset($nextPlug['order']) ? max(1, min(100, $nextPlug['order'])) : 50;
- }
- else
- {
- $extends = $name;
- $order = 0;
- }
- $plugInfo = array('name'=>$name, 'extends'=>$extends, 'order'=>$order, 'file'=>$path);
- if (!empty($nextPlug['requires']))
- {
- if (is_string($requires = $nextPlug['requires']))
- {
- $requires = array_map('trim', explode(',', $requires));
- }
- $plugInfo['requires'] = $requires;
- }
- if (($order === 0) && isset($nextPlug['base_class']))
- {
- $plugInfo['base_class'] = $nextPlug['base_class'];
- }
- $this->_plugList[$name] =& $plugInfo;
- $this->_plugMap[$extends][$order][] =& $plugInfo;
- unset($plugInfo);
- }
- unset($plug);
- }
- }
-
- // sort plugs according to load order
-
- foreach(array_keys($this->_plugMap) as $key)
- {
- ksort($this->_plugMap[$key]);
- }
- }
- }
- // --------------------------------------------------------------------------
- /**
- * Call this method to make an additional plug available to be loaded.
- * Subsequent invocations of manufacture() will consider this plug when
- * determining which plugs to load. Unlike findPlugs(), this method can
- * accept a callback to load the plug's code, rather than a file.
- *
- * @param array $plug:
- * (
- * string 'name' => name of plug to add,
- * string 'requires' => name(s) of class(es) (if any) required by this plug,
- * string 'extends' => name of class (if any) being extended by this plug,
- * int 'order' => load order for this plug
- * string 'file' => location of plug's code file, OR
- * mixed 'callback' => callback to create the plug,
- */
- final public function addPlug($plug)
- {
- if (!empty($plug['requires']))
- {
- if (is_string($requires = $plug['requires']))
- {
- $requires = array_map('trim', explode(',', $requires));
- }
- $plug['requires'] = $requires;
- }
- $plug['order'] = !empty($plug['extends']) ? max(1, min(100, !empty($plug['order']) ? $plug['order'] : 100)) : 0;
- if (empty($plug['extends']))
- {
- $plug['extends'] = $plug['name'];
- }
- $this->_plugList[$plug['name']] =& $plug;
- $this->_plugMap[$plug['extends']][$plug['order']][] =& $plug;
- ksort($this->_plugMap[$plug['extends']]);
- }
-
- // --------------------------------------------------------------------------
- /**
- * Call this method to retrieve information about an installed plug.
- *
- * @param string $name: => name of plug
- * @return array of plug info
- */
- final public function getPlug($name)
- {
- return @$this->_plugList[$name];
- }
-
- // --------------------------------------------------------------------------
- /**
- * Call this method to retrieve information about all installed plugs that extend a specified plug.
- *
- * @param string $name: => name of extended plug
- * @return array of plug infos
- */
- final public function getExtensions($name)
- {
- if (empty($this->_plugMap[$name]))
- {
- return NULL;
- }
-
- $extensions = array();
- foreach ($this->_plugMap[$name] as $plugList)
- {
- $extensions = array_merge($extensions, $plugList);
- }
-
- return $extensions;
- }
-
- // --------------------------------------------------------------------------
- /**
- * Dynamically build a class hierarchy and instantiate a new object.
- * This method is used to create any object that you want to be extensible.
- *
- * For example, any object derived from SparkPlug may call:
- *
- * $myObject = $this->factory->manufacture('MyClass');
- *
- * and $myObject will be created and will include the functionality provided by
- * all plugs that extend MyClass.
- *
- * However, if instead you call:
- *
- * $myObject = new MyClass;
- *
- * you will still create a MyClass object, but it will not include any plug functionality.
- *
- * @param string $class Name of class to instantiate
- * @return object
- */
- final public function manufacture($class)
- {
- $this->_observer->notify('Spark:manufacture:' . $class, $class);
- // load the class and all its extension plugs
-
- $class = $this->loadClass($class);
- // get parameters for constructor (remove the class name from start of args)
- $params = func_get_args();
- array_shift($params);
-
- // return call_user_func_array(array($class, '__construct'), $params);
- // can't call a constructor via call_user_func_array() because it is not a static method
- switch ($c = count($params))
- {
- case 0:
- return new $class();
- case 1:
- return new $class($params[0]);
- case 2:
- return new $class($params[0], $params[1]);
- case 3:
- return new $class($params[0], $params[1], $params[2]);
- case 4:
- return new $class($params[0], $params[1], $params[2], $params[3]);
- case 5:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4]);
- case 6:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4], $params[5]);
- case 7:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6]);
- case 8:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7]);
- case 9:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8]);
- case 10:
- return new $class($params[0], $params[1], $params[2], $params[3], $params[4], $params[5], $params[6], $params[7], $params[8], $params[9]);
- default:
- throw new SparkException('too many arguments (' . $c . ') passed to constructor (max 10)');
- }
- }
- // --------------------------------------------------------------------------
- /**
- * Call this method to load a class and all its extension plugs.
- * Optionally cache the class to the plug cache directory.
- *
- * @param string $class Name of class to load
- * @param boolean $abstract True if this is an abstract base class
- * @return string Name of instantiated class (alternate implementation may change class name)
- */
- final public function loadClass($class, $abstract = false)
- {
- // no need to load a class more than once
- if (class_exists($class, false))
- {
- return $class;
- }
-
- // If caching is enabled, check the cache directory for this class.
- if (!empty($this->_plugCacheDir))
- {
- $cacheFile = $this->_plugCacheDir . '/' . $class . '.php';
- if ((@include $cacheFile) !== false)
- {
- return $class;
- }
- }
-
- // Extensible classes are always declared with an underscore prefix on the name,
- // which allows us to use the class as a base class for plugs, while leaving the
- // ultimate class name (without the underscore) free for dynamic creation once
- // all the plugs have been loaded.
-
- $baseClass = '_' . $class;
- // if the base class does not exist, it is a plug itself and needs to be loaded
-
- if (isset($this->_plugMap[$class])) // any plugs want to extend this class?
- {
- foreach ($this->_plugMap[$class] as $plugs)
- {
- foreach ($plugs as $plug)
- {
- $plugName = $plug['name']; // plug's name
- $plugOrder = $plug['order']; // plug's load order
- $plugCode = false; // plug's code
-
- // take care of dependencies
-
- foreach ((array)@$plug['requires'] as $dependency)
- {
- $this->loadClass($dependency);
- }
- // To allow easier integration with external class libraries, the manifest may
- // specify alternate class names for base classes.
-
- if (($plugOrder == 0) && isset($plug['base_class']))
- {
- $baseClass = $plug['base_class'];
- }
-
- // load the plug's code
-
- if ($plugFile = @$plug['file'])
- {
- $plugCode = file_get_contents($plugFile);
- }
- elseif ($callback = @$plug['callback'])
- {
- // Note: We do not use call_user_func() here because it does not allow pass-by-reference
-
- if (is_string($callback))
- {
- $plugCode = $callback($plug);
- }
- elseif (is_array($callback))
- {
- $plugCode = $callback[0]->$callback[1]($plug);
- }
- }
-
- if ($plugCode !== false)
- {
- $plugCode = preg_replace("/\s*<\?(php)?/", '', $plugCode, 1);
- // Here's the tricky bit... We dynamically rewrite the plug's class definition to build our
- // class hierarchy. (Self-modifying code - oh my!)
- // So, each plug is loaded in turn and serves as the base class for the next plug to be loaded.
- // Performance of this mechanism actually turns out to be quite respectable. The major downside
- // is that opcode cachers will not be able to cache the bytecode of plugs loaded in this manner,
- // (which is generally true of eval'd code). However, this drawback may be mitigated by enabling
- // plug caching, which writes the entire class inheritance chain to a single class file.
-
- if ($plugOrder != 0) // not a base class?
- {
- $firstClass = isset($plug['first_class']) ? $plug['first_class'] : $plugName;
- $plugCode = preg_replace("/class\s+{$firstClass}\s+extends\s+(?:{$class}|{$baseClass})/", "class {$firstClass} extends {$baseClass}", $plugCode, 1, $count);
-
- if ($count !== 1) // something fishy here, best abort
- {
- throw new SparkException('plug err: Could not load plug "' . ($plugFile ? $plugFile : $plugName) . '"');
- }
- }
-
- // create cache file if caching enabled
-
- if (!isset($cacheFileHandle) && !empty($this->_plugCacheDir))
- {
- if (($cacheFileHandle = @fopen($cacheFile, 'w')) !== false)
- {
- if (fwrite($cacheFileHandle, "<?php\n") === false)
- {
- throw new SparkException('plug err: Could not write plug cache file "' . $cacheFile . '"');
- }
- }
- }
-
- if (!empty($cacheFileHandle))
- {
- if (fwrite($cacheFileHandle, $plugCode) === false)
- {
- throw new SparkException('plug err: Could not write plug cache file "' . $cacheFile . '"');
- }
- }
- elseif ($this->loadPlug($plugCode) === false)
- {
- throw new SparkException('plug err: Could not load plug "' . ($plugFile ? $plugFile : $plugName) . '"');
- }
-
- // plug successfully loaded and will serve as the base class for the next to load (if any)
-
- if ($plugOrder != 0) // not a base class?
- {
- $baseClass = $plugName;
- }
- }
- }
- }
- }
-
- // Finally, dynamically generate the requested class as a simple wrapper class.
- // This makes the class name a legitimate object in the PHP namespace so it can be referred to by
- // other functions, etc.
-
- $plugCode = "\nif (!class_exists('{$class}', false)) { " . ($abstract ? 'abstract ' : '') . "class {$class} extends {$baseClass} {} }\n";
- if (!empty($cacheFileHandle))
- {
- if (fwrite($cacheFileHandle, $plugCode) === false)
- {
- throw new SparkException('plug err: Could not write plug cache file "' . $cacheFile . '"');
- }
- fclose($cacheFileHandle);
- include $cacheFile;
- }
- else
- {
- if (!class_exists($baseClass, false))
- {
- throw new SparkException('loader err: Could not load class "' . $baseClass . '"');
- }
- $this->loadPlug($plugCode);
- }
- return $class;
- }
-
- // --------------------------------------------------------------------------
- /**
- * Private method to isolate the loading of the plug's code.
- *
- * @param string $plugCode Plug code to load into PHP interpreter
- * @return mixed Result of loading the plug's code
- */
- final private function loadPlug($plugCode)
- {
- return eval($plugCode);
- }
- }
- /**
- * The _SparkPlug (SparkPlug) class is Spark/Plug's universal base object class. Any
- * class that wants access to the application object and/or the Spark factory object
- * should derive from this class or one of its descendants. This class is not meant to
- * be instantiated directly. When specifying this class as a base class in your class
- * definition, be sure to extend SparkPlug (not _SparkPlug). Otherwise, your derived class
- * will not inherit the capabilities of any plugs that extend this class.
- * This class may be used by core classes, application classes and plugs.
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- abstract class _SparkPlug
- {
- public $app; // convenience reference to global application object
- public $config; // convenience reference to application configuration store
- public $factory; // convenience reference back global object factory
- public $observer; // convenience reference to global observer
- // --------------------------------------------------------------------------
- /**
- * Construct a new SparkPlug object. Make key application objects available to all
- * derived classes.
- */
- public function __construct()
- {
- $this->app = SparkApplication::instance();
- $this->config = $this->app->config();
- $this->factory = $this->app->factory();
- $this->observer = $this->factory->observer();
- }
- /**
- * Create a new model object. Calls through to the application's newModel() method.
- *
- * @param string $name Name of model class to instatiate
- * @return object Model object
- */
- public function newModel($name, $params = NULL)
- {
- return $this->app->newModel($name, $params);
- }
- /**
- * Create a full url to the specified (static) path. Calls through to the application's urlToStatic() method.
- *
- * @param string $path Path to which to create full URL
- * @param bool $withHost Whether to include host in URL
- * @param bool $withScheme Whether to include scheme in URL
- * @param bool $secure Whether to generate a secure URL (true), a non-secure URL (false), or default (NULL)
- * @return string URL
- */
- public function urlToStatic($path, $withHost = false, $withScheme = true, $secure = NULL)
- {
- return $this->app->urlToStatic($path, $withHost, $withScheme, $secure);
- }
- /**
- * Create a full url to the specified (dynamic) path. Calls through to the application's urlTo() method.
- *
- * @param string $path Path to which to create full URL
- * @param bool $withHost Whether to include host in URL
- * @param bool $withScheme Whether to include scheme in URL
- * @param bool $secure Whether to generate a secure URL (true), a non-secure URL (false), or default (NULL)
- * @return string URL
- */
- public function urlTo($path, $withHost = false, $withScheme = true, $secure = NULL)
- {
- return $this->app->urlTo($path, $withHost, $withScheme, $secure);
- }
- /**
- * Create a full url to the specified (static) secure path. Calls through to the application's urlToStaticSecure() method.
- *
- * @param string $path Path to which to create secure URL
- * @return string URL
- */
- public function urlToStaticSecure($path)
- {
- return $this->app->urlToStaticSecure($path);
- }
- /**
- * Create a full url to the specified (dynamic) secure path. Calls through to the application's urlToSecure() method.
- *
- * @param string $path Path to which to create secure URL
- * @return string URL
- */
- public function urlToSecure($path)
- {
- return $this->app->urlToSecure($path);
- }
- /**
- * Create a redirect to the specified (static) path. Calls through to the application's urlToStatic() method.
- *
- * @param string $path Path to which to create full URL to redirect to
- * @param bool $secure Whether to redirect to a secure page (true), a non-secure page (false), or default (NULL)
- */
- public function redirectStatic($path, $secure = NULL)
- {
- $this->redirectAbsolute($this->urlToStatic($path, true, true, $secure));
- }
- /**
- * Create a redirect to the specified path. Calls through to the application's urlTo() method.
- *
- * @param string $path Path to which to create full URL to redirect to
- * @param bool $secure Whether to redirect to a secure page (true), a non-secure page (false), or default (NULL)
- */
- public function redirect($path, $secure = NULL)
- {
- $this->redirectAbsolute($this->urlTo($path, true, true, $secure));
- }
- /**
- * Create a redirect to the specified absolute url.
- *
- * @param string $url Absolute URL to redirect to
- */
- public function redirectAbsolute($url)
- {
- $this->observer->notify('SparkPlug:redirect:before', $url);
- header('Location: ' . $url);
- exit;
- }
- }
- // -----------------------------------------------------------------------------
- /**
- * Now we have defined the core classes that allow us to create the global factory object.
- * We must instantiate the global factory object now because doing so will dynamically create
- * the SparkPlug class definition, which the following class declarations depend on.
- * The file that includes this file my declare a global array of plugs to load into the application.
- * We pass that array to the constructor.
- */
- $spark = new Spark(isset($plugs) ? $plugs : NULL, isset($plug_search_paths) ? $plug_search_paths : NULL, isset($plug_cache_dir) ? $plug_cache_dir : NULL);
- unset($plugs);
- unset($plug_search_paths);
- unset($plug_cache_dir);
- // -----------------------------------------------------------------------------
- /**
- * The _SparkModel (SparkModel) class is Spark/Plug's model base class.
- * While Spark/Plug is a model-view-controller framework, use of models is optional.
- * If your application uses models, they should be derived from this class or one of its
- * descendants. When specifying this class as a base class in your class definition, be sure
- * to extend SparkModel (not _SparkModel). Otherwise, your derived class will not inherit the
- * capabilities of any plugs that extend this class.
- *
- * To create a new model, invoke the newModel() method on any object derived from SparkPlug:
- *
- * $model = $this->newModel('MyModel');
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- abstract class _SparkModel extends SparkPlug
- {
- /**
- * Construct a new SparkModel object.
- * Not much to see here. Move along...
- */
- public function __construct()
- {
- parent::__construct();
- }
- //---------------------------------------------------------------------------
- /**
- * Convenience function to return the current date/time UTC in a database-friendly format.
- *
- * @return string Current date/time UTC
- */
- public static function now($iso_8601 = false)
- {
- return $iso_8601 ? (gmdate('Y-m-d') . 'T' . gmdate('H:i:s') . 'Z') : gmdate('Y-m-d H:i:s');
- }
- //---------------------------------------------------------------------------
- /**
- * Convenience function to return an "empty" date in a database-friendly format.
- *
- * @return string Empty date
- */
- public static function never()
- {
- return '0000-00-00';
- }
- //---------------------------------------------------------------------------
- /**
- * Convenience method to load a helper file. Calls through to app's helper method.
- *
- * @param string $helperList Comma-delimited list of names of helpers to load
- */
- protected function helper($helperList)
- {
- return $this->app->helper($helperList);
- }
- }
- /**
- * The _SparkView (SparkView) class is Spark/Plug's view base object class.
- * This class is not meant to be instantiated directly. When specifying this
- * class as a base class in your class definition, be sure to extend SparkView (not _SparkView).
- * Otherwise, your derived class will not inherit the capabilities of any plugs that extend this class.
- * This class will not generally be used by Spark/Plug applications directly, as Spark/Plug automatically
- * creates a single view class on initialization, which is reused for all view rendering.
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- abstract class _SparkView extends SparkPlug
- {
- private $_viewDirStack; // list of directories where view files can be found
- private $_viewStack; // keeps track of nested view contexts
- private $_viewDefault; // name of default view (rendered if none is explicitly specified)
- private $_viewNameExtension; // optional view filename extension
- private $_helperDirStack; // list of directories where helper files can be found
- private $_helperNameExtension; // optional helper filename extension
- private $_loadedHelpers; // track helpers so we only load them once
-
- /**
- * Construct a new SparkView object.
- * Not much to see here. Move along...
- */
- public function __construct($viewDir, $viewNameExtension = '', $helperDir = NULL, $helperNameExtension = '')
- {
- parent::__construct();
- $this->_viewDirStack = (array)$viewDir;
- $this->_viewStack = array();
- $this->_viewDefault = '';
- $this->_viewNameExtension = $viewNameExtension;
- $this->_helperDirStack = (array)$helperDir;
- $this->_helperNameExtension = $helperNameExtension;
- $this->_loadedHelpers = NULL;
- }
- // --------------------------------------------------------------------------
- /**
- * Set name of default view (rendered if none specified).
- *
- * @param string $viewDefault Name of default view
- */
- final public function setDefault($viewDefault)
- {
- $this->_viewDefault = $viewDefault;
- }
- // --------------------------------------------------------------------------
- /**
- * Push a view directory onto the stack.
- *
- * @param string $dir Path to view directory
- */
- final public function pushViewDir($dir)
- {
- $this->_viewDirStack[] = $dir;
- }
- // --------------------------------------------------------------------------
- /**
- * Pop the view directory stack.
- *
- */
- final public function popViewDir()
- {
- if (count($this->_viewDirStack) > 1)
- {
- return array_pop($this->_viewDirStack);
- }
- return $this->_viewDirStack[0];
- }
- // --------------------------------------------------------------------------
- /**
- * Push a helper directory onto the stack.
- *
- * @param string $dir Path to helper directory
- */
- final public function pushHelperDir($dir)
- {
- $this->_helperDirStack[] = $dir;
- }
- // --------------------------------------------------------------------------
- /**
- * Pop the helper directory stack.
- *
- */
- final public function popHelperDir()
- {
- if (count($this->_helperDirStack) > 1)
- {
- return array_pop($this->_helperDirStack);
- }
- return $this->_helperDirStack[0];
- }
- // --------------------------------------------------------------------------
- /**
- * Render a view file.
- * Accepts 0, 1, 2 or 3 parameters. Parameters may appear in any order and are as follows:
- *
- * @param string $viewList Comma-delimited list of names of views to render (first found will be rendered)
- * @param array $vars Array of variables to make available to view (extract into view as locals)
- * @param bool $returnBuffer true if rendered result should be returned instead of buffered for display
- * @return string Result of render (if $returnBuffer is true)
- */
- final public function render()
- {
- $viewList = NULL;
- $vars = array();
- $returnBuffer = false;
-
- // decode the argument list - all are optional and they can appear in any order
-
- foreach (func_get_args() as $arg)
- {
- if (is_string($arg))
- {
- $viewList = array_map('trim', explode(',', $arg));
- }
- elseif (is_array($arg))
- {
- $vars = $arg;
- }
- elseif (is_bool($arg))
- {
- $returnBuffer = $arg;
- }
- }
-
- $nested = !empty($this->_viewStack);
-
- // use the default view if no view specified
-
- if (!$nested && empty($viewList))
- {
- $viewList = array($this->_viewDefault);
- }
-
- // start buffering if necessary
-
- if ($returnBuffer || !$nested)
- {
- ob_start();
- }
- // nested views inherit the variables of the containing view(s)
- if ($nested)
- {
- $vars = array_merge(end($this->_viewStack), $vars);
- }
-
- // push our context on a stack, for use by views nested within this one
-
- $this->_viewStack[] = $vars;
-
- // load the view
-
- $foundIt = false;
- $viewNames = array_map(array('SparkInflector', 'decamelize'), $viewList);
- try
- {
- for ($iViewDir = count($this->_viewDirStack) - 1; $iViewDir >= 0; --$iViewDir)
- {
- foreach ($viewList as $iView => $view)
- {
- $viewFileName = $viewNames[$iView] . $this->_viewNameExtension . '.php';
- if (file_exists($viewPath = "{$this->_viewDirStack[$iViewDir]}/{$viewFileName}"))
- {
- // send pre_render notification, allowing recipient to alter the contents of the vars array
-
- $this->observer->notify('SparkView:render:before:' . $view, $view, (object)array('vars'=>&$vars));
-
- $this->loadView($viewPath, $vars);
- $foundIt = true;
-
- // send post_render notification
-
- $this->observer->notify('SparkView:render:after:' . $view, $view);
-
- break 2;
- }
- }
- }
- if (!$foundIt)
- {
- throw new SparkException('render err: Could not load view "' . $viewFileName . '"');
- }
- }
- catch (Exception $e)
- {
- array_pop($this->_viewStack);
- throw $e;
- }
- array_pop($this->_viewStack);
-
- // return the rendered result if this is the outer-most view (or if caller requested it)
-
- if ($returnBuffer || !$nested)
- {
- return ob_get_clean();
- }
- }
- // --------------------------------------------------------------------------
- /**
- * Load a view helper.
- *
- * @param string $helperList Array or comma-delimited string listing of names of helpers to load
- */
- final public function helper($helperList)
- {
- $foundIt = true;
- if (is_string($helperList))
- {
- $helperList = array_map('trim', explode(',', $helperList));
- }
- $helperList = array_map(array('SparkInflector', 'decamelize'), $helperList);
- foreach ($helperList as $helper)
- {
- if (!empty($this->_loadedHelpers[$helper]))
- {
- continue;
- }
- for ($iHelperDir = count($this->_helperDirStack) - 1; $iHelperDir >= 0; --$iHelperDir)
- {
- $helperFileName = $helper . $this->_helperNameExtension . '.php';
-
- if (file_exists($helperPath = "{$this->_helperDirStack[$iHelperDir]}/{$helperFileName}"))
- {
- require($helperPath);
- $this->_loadedHelpers[$helper] = true;
- continue 2;
- }
- }
- $foundIt = false;
- break;
- }
- if (!$foundIt)
- {
- throw new SparkException('loader err: Could not load helper "' . $helperFileName . '"');
- }
- }
- // --------------------------------------------------------------------------
- /**
- * Escape item for inclusion in HTML output.
- *
- * @param string $item Text to escape
- * @param boolean $encodeAll Whether to encode all characters to entity equivalents
- * @param string $charset Character set of text
- * @return string Escaped item
- */
- final public static function escape_html($item, $encodeAll = false, $charset = 'UTF-8')
- {
- return $encodeAll
- ? htmlentities($item, ENT_QUOTES, $charset)
- : htmlspecialchars($item, ENT_QUOTES, $charset);
- }
- // deprecated alias for escape_html
-
- final public static function escape($item, $encodeAll = false, $charset = 'UTF-8')
- {
- return self::escape_html($item, $encodeAll, $charset);
- }
- // --------------------------------------------------------------------------
- /**
- * Reverse HTML escaping.
- *
- * @param string $item Text to unescape
- * @param boolean $decodeAll Whether to decode all entities or only special entities
- * @param string $charset Character set of text
- * @return string Unescaped item
- */
- final public static function unescape_html($item, $decodeAll = false, $charset = 'UTF-8')
- {
- return $decodeAll
- ? html_entity_decode($item, ENT_QUOTES, $charset)
- : htmlspecialchars_decode($item, ENT_QUOTES);
- }
- // --------------------------------------------------------------------------
- /**
- * Escape item for inclusion in XML output.
- *
- * @param string $item Text to escape
- * @param string $charset Character set of text
- * @return string Escaped item
- */
- final public static function escape_xml($item, $charset = 'UTF-8')
- {
- return htmlspecialchars($item, ENT_NOQUOTES, $charset);
- }
- // --------------------------------------------------------------------------
- /**
- * Reverse XML escaping.
- *
- * @param string $item Text to unescape
- * @param string $charset Character set of text
- * @return string Unescaped item
- */
- final public static function unescape_xml($item, $charset = 'UTF-8')
- {
- return htmlspecialchars_decode($item, ENT_QUOTES);
- }
- // --------------------------------------------------------------------------
- /**
- * Escape item for inclusion in query string or form-encoded output.
- *
- * @param string $item Text to escape
- * @param string $charset Character set of text (ignored)
- * @return string Escaped item
- */
- final public static function escape_uri($item, $charset = 'UTF-8')
- {
- return urlencode($item);
- }
- // --------------------------------------------------------------------------
- /**
- * Reverse URL escaping.
- *
- * @param string $item Text to escape
- * @param string $charset Character set of text (ignored)
- * @return string Unescaped item
- */
- final public static function unescape_uri($item, $charset = 'UTF-8')
- {
- return urldecode($item);
- }
- // --------------------------------------------------------------------------
- /**
- * Load a view file.
- *
- * Note the funky use of func_get_args() to retrieve the filename. This is due to
- * the fact that the extract operation can potentially overwrite the $file param
- * with a local var named 'file'.
- *
- * @param string $file Path to view file
- * @param array $vars Array of variables to make available to view (extract into view as locals)
- */
- final private static function array_first($array) { return $array[0]; }
- final private function loadView($file, $vars)
- {
- extract($vars, EXTR_OVERWRITE);
- unset($vars);
- require(self::array_first(func_get_args()));
- }
- }
- /**
- * The _SparkController (SparkController) class is Spark/Plug's controller base class.
- * Your application's controllers should be derived from this class or one of its
- * descendants. When specifying this class as a base class in your class definition, be sure
- * to extend SparkController (not _SparkController). Otherwise, your derived class will not
- * inherit the capabilities of any plugs that extend this class.
- *
- * Your application will not generally create controller objecct directly, as Spark/Plug
- * automatically instantiates a controller object based on the current URL.
- *
- * @package Spark/Plug
- */
- // -----------------------------------------------------------------------------
- abstract class _SparkController extends SparkPlug
- {
- /**
- * Construct a new SparkController object.
- * Not much to see here. Move along...
- *
- * @param object $app Reference to the application that instantiated us.
- */
- public function __construct($app)
- {
- parent::_