/webroot/vork
[return to app]1
<?php error_reporting(E_ALL | E_STRICT);
define('DEBUG_MODE', false); //this is the only line that needs to be adjusted in this file
/**
* Vork Framework www.Vork.us
* @version 3.00
* @author Eric David Benari, Vork Chief Architect
*/
class configInit {
/**
* Location of the packages folder (relative to webroot)
*/
public static function packagesPath() {
return configDefaults::basepath() . 'packages' . self::DS;
}
/**
* Directory separator, in both Linux and Windows you can just leave this as-is
*/
const DS = '/';
/**
* Creates the ability to access constants within inheriting classes just like properties
* If there is a naming conflict properties take precedence, but this should never be
* an issue when following standard Zend Framework/PEAR naming conventions where properties use
* camel-caps and constants are all-capitals delimited by undersores.
*
* get::$config->myProperty
* get::$config->MY_CONSTANT
*
* @param string $key
* @return mixed
*/
public function __get($key) {
$constantName = get_class($this) . '::' . $key;
if (defined($constantName)) {
return constant($constantName);
} else {
trigger_error('Undefined property: ' . $constantName, E_USER_NOTICE);
}
}
}
if (DEBUG_MODE) {
require configDefaults::basepath() . '.debug';
}
/**
* Default configuration - these settings will only be used if they are not defined in the config file
*/
class configDefaults extends configInit {
/**
* Path to the config file, change only if not using the default file name/location
* Note: if no DB connections or other configuration settings are needed the .config file can
* be deleted and this setting will be ignored.
*/
const CONFIG_PATH = '.config';
/**
* Optional, only need to set these if you wish to execute a file that is global to the
* application before and/or after the page loads. Filename must match the class name and be in the MVC root.
*/
const APP_CONSTRUCT = null, APP_DESTRUCT = null;
/**
* Optional, only need to set these in your config class if you move the folders from the default locations
*
* @var string
*/
public $pathToMvc, $modelsFolder, $viewsFolder, $controllersFolder,
$layoutsFolder, $elementsFolder, $componentsFolder, $helpersFolder;
/**
* Optional, only need to set this in your config class if the webroot folder is not installed in the root of
* your web site directory
*
* @var string
*/
public $basepath = '/';
/**
* Name of controller that is used to handle errors
*
* @var string
*/
public $errorHandler = 'error';
/**
* By default no extension is used for MVC files, if you need to use a file extension then
* it needs to be set it in your config class in order for the files to be found
* and you need to include the prepended dot
*
* @var string
*/
public $fileExtension = '';
/**
* These are the default helperes that will be loaded into every view (and available to every element)
*
* Default is 'html' and 'form'; you may wish to add more by overriding this property this
* property within your config and adding the name of the additional helpers to the parent array.
*
* Note: only do this if most of your application needs the helper, otherwise you can load
* helpers in the __construct of a controller or within any method (action) of a controller
*
* @var array
*/
public $helpers = array('html', 'form');
/**
* This sets the objects that will be available to all your models, the default is db and
* that should suffice for most applications. To add more create the object (class) in your config
* and then override this property adding the name of your object to the parent array and add a static
* property with the same name as the object to the config class (this will contain the object later.)
*/
public static $db, $modelObjects = array('db');
/**
* Internal cache of basepath
* @var string
*/
protected static $_basepath;
/**
* Set the base working path
* @return string
*/
public static function basepath() {
if (self::$_basepath === null) {
if (isset($_SERVER['DOCUMENT_ROOT']) && $_SERVER['DOCUMENT_ROOT']) {
$_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], self::DS);
self::$_basepath = (substr($_SERVER['DOCUMENT_ROOT'], -7) == 'webroot'
? substr($_SERVER['DOCUMENT_ROOT'], 0, -8) : $_SERVER['DOCUMENT_ROOT']) .
self::DS;
} else { //cli
self::$_basepath = substr($_SERVER['SCRIPT_FILENAME'], 0, -12); //remove webroot/vork
}
}
return self::$_basepath;
}
/**
* Establishes MVC folder locations automatically and sets default title/meta-data (if defaults exist)
*/
public function __construct() {
if (!get::$title) {
get::$title = (isset($this->title) && $this->title ? $this->title : $this->SITE_NAME);
}
if (!get::$meta && isset($this->meta) && is_array($this->meta)) {
get::$meta = $this->meta;
}
if ($this->pathToMvc === null) {
$this->pathToMvc = configDefaults::basepath() . 'mvc' . self::DS;
if (isset($_SERVER['SCRIPT_NAME']) && $_SERVER['SCRIPT_NAME'] == '/webroot/vork') {
$this->pathToMvc = $_SERVER['DOCUMENT_ROOT'] . self::DS . 'mvc' . self::DS;
}
} else {
if (substr($this->pathToMvc, -1) != self::DS) { //append trailing slash if needed
$this->pathToMvc .= self::DS;
}
}
$mvcFolders = array('models', 'views', 'controllers',
'layouts', 'elements', 'components', 'helpers');
foreach ($mvcFolders as $mvcFolder) {
if ($this->{$mvcFolder . 'Folder'} === null) {
$this->{$mvcFolder . 'Folder'} = $mvcFolder;
}
}
}
}
/**
* The internal MVC logic - essentially the brain of the framework
*/
class mvc {
/**
* Override these within your application to change controller, action, layout or view
* You can also modify the params array as needed.
* mvc::$controller = 'cart';
* mvc::$action = 'logout';
* mvc::$layout = 'mobile';
* mvc::$view = 'confirmation';
*
* Changes to the above will only take affect after the current action returns, to make the affect
* immediate (to redirect before completing the current action) simply add a return; statement right after.
*
* These properties have defaults set:
* If no controller is defined in the URL then the index controller is used
* If no action is defined in the URL then the index action is used
* If no layout is defined in the controller then the default layout is used
*
* Views will always default to a file with the same name as the action and will first look within a
* folder in the views directory that has the same name as the controller. If using the index action or
* in the case that a controller is not found (usually for a page of semi-static content) and the folder
* or the file within it is not found it will then look for a file in the root of the views
* directory with the name of the controller.
*
* Acceptable locations are:
* /mvc/views/controllerName/actionName
* /mvc/views/controllerName
*
* To use a view from a different controller you can simply override the view setting within
* your application and include the path:
* mvc::$view = 'someOtherController' . get::$config->DS . 'newView';
*/
public static $controller = 'index', $action = 'index', $layout = 'default', $view, $params = array();
/**
* This is a safety to prevent redirecting in infinite loops (eg. a redirects to b, then b redirects
* back to a.) The default maximum redirects is 10 within one request, if you need more you could
* increase this but keep in mind that any app that needs to redirect over 10 times per request
* likely has some serious flaw in the architecture.
*/
protected $_maxActionRedirects = 10, $_redirectHistory = array();
/**
* Buffer used to contain view params
* @var array
*/
protected static $_viewParams = array();
/**
* Indicator as to the current processing-state of Vork internals
*/
protected static $_state = '__construct';
/**
* Set the $_state property
*/
public static function setState($state) {
if (DEBUG_MODE) {
debug::log('Vork internal state: ' . $state);
}
self::$_state = $state;
}
/**
* Read-only public access to the $_state property
* @return string
*/
public static function state() {
return self::$_state;
}
/**
* Replaces characters that are not valid within the name of a PHP object
*
* @param string $objName
* @return string
*/
public static function getValidObjectName($objName) {
$objName = preg_replace('/\W/', '_', $objName);
if (is_numeric($objName[0])) { //cannot start with a number
$objName = '_' . $objName;
}
return $objName;
}
/**
* Load config, open a DB connection & cache (if they are defined,) parse the URL to determine
controller/action
* and then load the page
*/
public function __construct() {
if (is_file(configDefaults::basepath() . configDefaults::CONFIG_PATH)) {
require configDefaults::basepath() . configDefaults::CONFIG_PATH;
get::$config = new config;
} else {
get::$config = new configDefaults;
}
if (DEBUG_MODE) {
debug::log('Config loaded');
}
if (get::$config->CACHE_CONNECT) {
get::dbConnection('cache');
if (DEBUG_MODE) {
debug::log('Cache loaded');
}
}
$this->_parseUrl();
if (get::$config->APP_CONSTRUCT && is_file(get::$config->pathToMvc . get::$config->APP_CONSTRUCT)) {
require get::$config->pathToMvc . get::$config->APP_CONSTRUCT;
get::$config->{get::$config->APP_CONSTRUCT} = new get::$config->APP_CONSTRUCT;
if (DEBUG_MODE) {
debug::log('APP_CONSTRUCT loaded');
}
}
$this->_loadAction();
if (get::$config->APP_DESTRUCT && is_file(get::$config->pathToMvc . get::$config->APP_DESTRUCT)) {
require get::$config->pathToMvc . get::$config->APP_DESTRUCT;
get::$config->{get::$config->APP_DESTRUCT} = new get::$config->APP_DESTRUCT;
if (DEBUG_MODE) {
debug::log('APP_DESTRUCT loaded');
}
}
}
/**
* Loads an element. Elements can access the view params plus the params sent to them as an argument as if
* they are local variables. If there is a naming conflict the variable in the argument takes precedence.
*
* @param string $outputType
* @param string $elementName
* @param array $elementParams
* @return mixed
*/
protected static function _loadElement($outputType, $elementName, $elementParams) {
$elementFile = get::mvcFilePath('elements', $elementName);
if (is_file($elementFile)) {
if (is_array(self::$_viewParams)) {
extract(self::$_viewParams);
}
if (is_array($elementParams)) {
extract($elementParams);
}
if (DEBUG_MODE) {
$logMsg = 'element ' . $elementName . ' loaded';
if ($elementParams) {
$logMsg .= ' with ' . json_encode($elementParams);
}
}
if ($outputType == 'return') {
ob_start();
require $elementFile;
$element = ob_get_contents();
ob_end_clean();
if (DEBUG_MODE) {
debug::log($logMsg);
}
return $element;
} else {
$return = require $elementFile;
if (DEBUG_MODE) {
debug::log($logMsg);
}
return $return;
}
} else {
trigger_error('File for element ' . $elementName . ' could not be found', E_USER_WARNING);
}
}
/**
* Returns the output of an element as a string
*
* @param string $elementName
* @param array $elementParams
* @return string
*/
public static function getElement($elementName, $elementParams = array()) {
return self::_loadElement('return', $elementName, $elementParams);
}
/**
* Loads an element, any output goes directly to stdout (echos to the screen)
*
* @param string $elementName
* @param array $elementParams
*/
public static function loadElement($elementName, $elementParams = array()) {
return self::_loadElement('echo', $elementName, $elementParams);
}
/**
* Loads the default helpers defined in the config at every page instance
*/
protected function _loadDefaultHelpers() {
foreach (get::$config->helpers as $helper) {
if (!isset(self::$_viewParams[$helper])) {
$helperFile = get::mvcFilePath('helpers', $helper);
if (is_file($helperFile)) {
if (!class_exists($helper . 'Helper')) {
require $helperFile;
}
$helperClassName = $helper . 'Helper';
get::$loadedObjects['helper'][$helper][null] = self::$_viewParams[$helper] = new
$helperClassName;
} else {
trigger_error('File for ' . $helperFile . ' helper could not be found', E_USER_WARNING);
}
}
}
if (DEBUG_MODE) {
debug::log('Default helpers loaded: ' . implode(', ', get::$config->helpers));
}
}
/**
* Loads a view, if a layout exists then the view will be loaded into the layout
*
* @param string $controllerName
* @param string $action
* @return mixed
*/
protected function _loadView($controllerName, $action) {
if (!load::$loadView || self::$view === false) {
return;
}
self::setState('view');
if (self::$view == null) { //try: /mvc/views/controllerName/actionName
$viewFile = get::mvcFilePath('views', $controllerName . get::$config->DS . $action);
if (!is_file($viewFile) && $action == 'index') { //try: /mvc/views/controllerName
$viewFile = get::mvcFilePath('views', $controllerName);
}
} else {
$viewFile = get::mvcFilePath('views', $controllerName . get::$config->DS . self::$view);
if (!is_file($viewFile)) { //local path failed, try absolute path
$viewFile = get::mvcFilePath('views', self::$view);
}
}
if (!is_file($viewFile) && $controllerName != get::$config->errorHandler) {
return $this->_loadAction(get::$config->errorHandler, 'missingView');
}
if (is_array(self::$_viewParams)) {
extract(self::$_viewParams);
}
if (is::ajax() && get::$config->ajaxEnabled
&& (!is_array(get::$config->ajaxEnabled) //ajax=true
|| (isset(get::$config->ajaxEnabled[$controllerName])
&& (get::$config->ajaxEnabled[$controllerName] === true //ajax[controllerName]=true
|| get::$config->ajaxEnabled[$controllerName] == $action //ajax[controllerName]=action
|| (is_array(get::$config->ajaxEnabled[$controllerName])
//ajax[controllerName]=array(action)
&& in_array($action, get::$config->ajaxEnabled[$controllerName])))))) {
self::$layout = false; //AJAX request
}
$layoutFile = get::mvcFilePath('layouts', self::$layout);
if (self::$layout && is_file($layoutFile)) {
ob_start();
require $viewFile;
$view = ob_get_contents();
ob_end_clean();
if (DEBUG_MODE) {
debug::log('View loaded');
}
self::setState('layout');
require $layoutFile;
if (DEBUG_MODE) {
debug::log('Layout loaded');
}
} else {
require $viewFile;
if (DEBUG_MODE) {
debug::log('View loaded');
}
}
}
/**
* Redirects controllers and/or action
*
* @param string $originalController
* @param string $originalAction
*/
protected function _redirectAction($originalController, $originalAction) {
if (count($this->_redirectHistory) < $this->_maxActionRedirects) {
$this->_redirectHistory[] = array('oldController' => $originalController,
'newController' => self::$controller,
'oldAction' => $originalAction,
'newAction' => self::$action);
$this->_loadAction();
} else {
self::$params['redirectHistory'] = $this->_redirectHistory;
$this->_loadAction(get::$config->errorHandler, 'redirectEndlessLooping');
}
}
/**
* Loads an action
*
* @param string $controllerName Optional, if omitted then self::$controller is used
* @param string $action Optional, if omitted then self::$action is used
* @return null
*/
protected function _loadAction($controllerName = null, $action = null) {
if (is_null($controllerName)) {
$controllerName = self::$controller;
}
if (is_null($action)) {
$action = self::getValidObjectName(self::$action);
}
if (strpos($controllerName, '..') !== false || strpos($controllerName, ':') !== false) {
return $this->_loadAction(get::$config->errorHandler, 'invalidControllerName');
}
if (!is_file(get::mvcFilePath('controllers', $controllerName))) { //no controller, attempt to load view
directly
$this->_loadDefaultHelpers();
return $this->_loadView($controllerName, $action);
}
$originalController = $controllerName;
$controllerClassName = $controllerName . 'Controller';
if (!class_exists($controllerClassName)) {
require get::mvcFilePath('controllers', $controllerName);
}
if (!class_exists($controllerClassName) && $controllerName != get::$config->errorHandler) {
return $this->_loadAction(get::$config->errorHandler, 'controllerNotDefined');
}
$originalAction = self::$action;
$controller = new $controllerClassName;
if ($controllerName != self::$controller && $controllerName != get::$config->errorHandler) {
return $this->_redirectAction($originalController, $action); //controller was changed in
__construct()
}
if ($originalAction != self::$action) { //action was changed in __construct()
$action = self::$action;
}
$optionalAction = (defined($controllerName . 'Controller::optionalAction')
&& constant($controllerName . 'Controller::optionalAction'));
if (!$optionalAction) { //updates self::$action in instances where _loadAction() was called with $action
arg
self::$action = $action;
}
if (!method_exists($controller, $action) && $controllerName != get::$config->errorHandler) {
if ($optionalAction && self::$action != 'index') {
array_unshift(self::$params, self::$action);
self::$action = 'index';
return $this->_loadAction($controllerName, 'index');
} else {
return $this->_loadAction(get::$config->errorHandler, 'missingAction');
}
}
$this->_loadDefaultHelpers();
self::setState('controller::action');
$viewParams = $controller->$action();
if (is_array($viewParams)) {
self::$_viewParams = array_merge(self::$_viewParams, $viewParams);
}
if (DEBUG_MODE) {
debug::log('Controller:action loaded');
}
if ($controllerName != get::$config->errorHandler) {
if (self::$controller != $originalController || self::$action != $action) {
return $this->_redirectAction($originalController, $action);
}
}
$this->_loadView($controllerName, $action);
}
/**
* Parse URL string to extract controller name, action name and params
*/
protected function _parseUrl() {
$basepathLen = strlen(get::$config->basepath);
if (isset($_SERVER['REDIRECT_URL'])) {
$_SERVER['REQUEST_URI'] = $_SERVER['REDIRECT_URL'];
}
if (isset($_SERVER['REQUEST_URI'])) {
$path = substr($_SERVER['REQUEST_URI'], $basepathLen);
$positionOfGet = strpos($path, '?');
if ($positionOfGet !== false) {
$path = substr($path, 0, $positionOfGet);
}
$pathParts = explode('/', $path);
$fileName = (strrpos($_SERVER['SCRIPT_FILENAME'], get::$config->DS) + 1);
if (isset($pathParts[0]) && $pathParts[0] == substr($_SERVER['SCRIPT_FILENAME'], $fileName)) {
$firstSegment = array_shift($pathParts); //remove the current filename from the path parts;
if ($firstSegment == 'vork' && is::superuser()) {
require configDefaults::basepath() . '.installer';
}
}
foreach ($pathParts as $pathSegment) {
$pathSegment = trim($pathSegment);
if ($pathSegment != '') {
$pathSegments[] = $pathSegment;
}
}
} else if (isset($_SERVER['argv'])) { //cli-mode
$pathSegments = $_SERVER['argv'];
array_shift($pathSegments);
}
if (isset($pathSegments) && $pathSegments) {
self::$controller = self::getValidObjectName(array_shift($pathSegments));
if (isset($pathSegments[0])) {
self::$action = array_shift($pathSegments);
if (!empty($pathSegments)) {
self::$params = $pathSegments;
}
}
}
if (DEBUG_MODE) {
debug::log('URL parsed');
}
}
}
/**
* Blackhole object used when catching a database exception to avoid fatal errors being thrown before reaching
* the error-handler controller & view.
* Note: static database methods will still fatal-error in pre-5.3 versions of PHP as __callStatic was not yet
supported
*/
class blackhole implements Iterator, ArrayAccess {
public static function __callStatic($method, array $args) {
return new blackhole;
}
public function __call($method, array $args) {
return $this;
}
public function __get($name) {
return $this;
}
public function __set($name, $val) {}
public function __isset($name) {
return false;
}
public function __unset($name) {}
public function __toString() {
return '';
}
public function rewind() {}
public function current() {}
public function key() {}
public function next() {}
public function valid() {
return false;
}
public function offsetExists($offset) {
return false;
}
public function offsetGet($offset) {}
public function offsetSet($offset, $value) {}
public function offsetUnset($offset) {}
}
/**
* Public interface to determine Boolean properties
*/
class is {
/**
* If instance is requested via AJAX
* @return Boolean
*/
public static function ajax() {
return (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] ==
'XMLHttpRequest');
}
/**
* If instance is requested via HTTPS
* @return Boolean
*/
public static function ssl() {
return (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
}
/**
* Internal cache for the is::bot() and is::mobile() methods
* @var Boolean or null
*/
protected static $_bot, $_mobile;
/**
* Identifies a spider/bot/crawler
* @return Boolean
*/
public static function bot() {
if (is_null(self::$_bot)) {
self::$_bot = false;
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$spiders = array('search', 'spider', 'crawler', 'archiver', 'indexer', 'webbot', 'robot',
'nagios', 'Googlebot', 'MSNbot', 'Jeeves', 'Slurp', 'Scooter', 'InfoSeek',
'Lycos',
'seoprofiler', 'Feedfetcher', 'facebookexternalhit');
self::$_bot = (preg_match('/' . implode('|', $spiders) . '/i', $_SERVER['HTTP_USER_AGENT']));
}
}
return self::$_bot;
}
/**
* Identifies a mobile user
* @return Boolean
*/
public static function mobile() {
if (is_null(self::$_mobile)) {
self::$_mobile = false;
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$mobile = array('Android', 'AvantGo', 'BlackBerry', 'DoCoMo', 'Fennec', 'iPod', 'iPhone', 'J2ME',
'MIDP', 'NetFront', 'Nokia', 'Opera Mini', 'PalmOS', 'PalmSource', 'portalmmm',
'Plucker', 'ReqwirelessWeb', 'SonyEricsson', 'Symbian', 'UP\.Browser', 'webOS',
'Windows CE', 'Xiino');
self::$_mobile = (preg_match('/' . implode('|', $mobile) . '/i', $_SERVER['HTTP_USER_AGENT']));
}
}
return self::$_mobile;
}
/**
* If instance is requested from Flash
* @return Boolean
*/
public static function flash() {
return (isset($_SERVER['HTTP_USER_AGENT'])
&& preg_match('/^(Shockwave|Adobe) Flash/i', $_SERVER['HTTP_USER_AGENT']));
}
/**
* Wrapper for get::$config->isSuperuser() to unify syntax
* @return Boolean
*/
public static function superuser() {
return get::$config->isSuperuser();
}
}
/**
* Public interface to retrieve rendered elements and other objects
*/
class get {
/**
* Opens up public access to config constants and variables and the cache object
* @var object
*/
public static $config, $cache;
/**
* Used to store the page title and meta data for layout usage
*
* You can set properties for "title" and "meta" in the .config to act as default values (setting in the view
or
* controller will always override the defaults)
* public $title = 'My Default Title';
* public $meta = array('keyowords' => 'apples, bananas, kiwis', 'description' => 'My site is about...');
*
* @var string $title
* @var array $meta valid keys are description and keywords
*/
public static $title, $meta = array();
/**
* Index of objects loaded, used to maintain uniqueness of singletons
* @var array
*/
public static $loadedObjects = array();
/**
* Index of names of database-objects that have a connection open
* @var array Keys are DB names, vals are always Bool true
*/
protected static $_dbConnected = array();
/**
* Returns fully qualified path to an MVC file
*
* @param string $type
* @param string $mvcName
* @return string
*/
public static function mvcFilePath($type, $mvcName) {
return self::$config->pathToMvc . self::$config->{$type . 'Folder'}
. self::$config->DS . $mvcName . self::$config->fileExtension;
}
/**
* Gets the current URL
*
* @param array Optional, keys:
* get - Boolean Default: false - include GET URL if it exists
* abs - Boolean Default: false - true=absolute URL (aka. FQDN), false=just the path for relative
links
* ssl - Boolean Default: null - true=https, false=http, unset/null=auto-selects the current
protocol
* a true or false value implies abs=true
* @return string
*/
public static function url(array $args = array()) {
$ssl = null;
$get = false;
$abs = false;
extract($args);
if (!isset($_SERVER['HTTP_HOST']) && PHP_SAPI == 'cli') {
$_SERVER['HTTP_HOST'] = trim(`hostname`);
$argv = $_SERVER['argv'];
array_shift($argv);
$_SERVER['REDIRECT_URL'] = '/' . implode('/', $argv);
$get = false; // command-line has no GET
}
$url = (isset($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['SCRIPT_NAME']);
if (substr($url, -1) == '/') { //strip trailing slash for URL consistency
$url = substr($url, 0, -1);
}
if (is_null($ssl) && $abs == true) {
$ssl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
}
if ($abs || !is_null($ssl)) {
$url = (!$ssl ? 'http://' : 'https://') . $_SERVER['HTTP_HOST'] . $url;
}
if ($get && isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING']) {
$url .= '?' . $_SERVER['QUERY_STRING'];
}
return ($url ? $url : '/');
}
/**
* Initializes a database connection
* @param string $db
*/
public static function dbConnection($db) {
if (!isset(self::$_dbConnected[$db]) && method_exists(self::$config, 'dbConnect')) {
try {
self::$config->dbConnect($db);
self::$_dbConnected[$db] = true;
} catch (Exception $e) {
mvc::$params = $e;
mvc::$controller = get::$config->errorHandler;
mvc::$action = 'DatasourceError';
config::$$db = new blackhole;
}
}
}
/**
* Loads an object as a singleton
*
* @param string $objectType
* @param string $objectName
* @param mixed $args Optional, default is null - this is sent as an argument to the constructor and used as
part
* of the criteria to determine a unique singleton object - differing args return new
objects
* @return object
*/
protected static function _loadObject($objectType, $objectName, $args = null) {
if ($objectName == null) {
$objectName = mvc::$controller;
}
$argsSerialized = serialize($args);
if (isset(self::$loadedObjects[$objectType][$objectName][$argsSerialized])) {
return self::$loadedObjects[$objectType][$objectName][$argsSerialized];
}
$objectFile = self::mvcFilePath($objectType . 's', $objectName);
if (is_file($objectFile)) {
if (strpos($objectName, '/')) { //objects within folders
$segments = explode('/', $objectName);
$objectName = array_shift($segments);
foreach ($segments as $segment) {
$objectName .= ucfirst($segment);
}
}
$objectClassName = mvc::getValidObjectName($objectName) . ucfirst($objectType);
if (!class_exists($objectClassName)) {
require $objectFile;
}
if (class_exists($objectClassName)) {
try {
$objectObject = new $objectClassName($args);
} catch (Exception $e) {
trigger_error($e->getMessage(), E_USER_WARNING);
return new blackhole;
}
if ($objectType == 'model' && config::MODEL_AUTOCONNECT) {
foreach (config::$modelObjects as $modelObject) {
self::dbConnection($modelObject);
$objectObject->$modelObject = config::$$modelObject;
}
}
self::$loadedObjects[$objectType][$objectName][$argsSerialized] = $objectObject;
if (DEBUG_MODE) {
debug::log($objectType . ' ' . $objectName . ' loaded');
}
return $objectObject;
} else {
$errorMsg = 'Class for ' . $objectType . ' ' . $objectName . ' could not be found';
}
} else {
$errorMsg = 'File for ' . $objectType . ' ' . $objectName . ' could not be found';
}
trigger_error($errorMsg, E_USER_WARNING);
}
/**
* Returns a model object
*
* @param string $model
* @param mixed $args Optional - arguments to be passed to the model-object constructor
* @return object
*/
public static function model($model = null, $args = null) {
return self::_loadObject('model', $model, $args);
}
/**
* Returns a component object
*
* @param string $component
* @param mixed $args Optional - arguments to be passed to the component-object constructor
* @return object
*/
public static function component($component = null, $args = null) {
return self::_loadObject('component', $component, $args);
}
/**
* Returns a helper object
*
* @param string $helper
* @param mixed $args Optional - arguments to be passed to the helper-object constructor
* @return object
*/
public static function helper($helper, $args = null) {
if (is_array($helper)) {
array_walk($helper, array('self', __METHOD__));
return;
}
if (!in_array($helper, self::$config->helpers)) {
self::$config->helpers[] = $helper;
}
return self::_loadObject('helper', $helper, $args);
}
/**
* Renders an element and returns the results as a string
*
* @param string $element
* @param array $params
*/
public static function element($element, $params = array()) {
$elementId = $element . $paramsString = serialize($params);
if (!isset(self::$loadedObjects['element'][$elementId])) {
self::$loadedObjects['element'][$elementId] = mvc::getElement($element, $params);
}
return self::$loadedObjects['element'][$elementId];
}
/**
* Overloads the php function htmlentities and changes the default charset to UTF-8 and the default value for
the
* fourth parameter $doubleEncode to false. Also adds ability to pass a null value to get the default
$quoteStyle
* and $charset (removes need to repeatedly define ENT_COMPAT, 'UTF-8', just to access the $doubleEncode
argument)
*
* If you are using a PHP version prior to 5.2.3 the $doubleEncode parameter is not available so you will
need
* to comment out the last parameter in the return clause (including the preceding comma)
*
* @param string $string
* @param int $quoteStyle Uses ENT_COMPAT if null or omitted
* @param string $charset Uses UTF-8 if null or omitted
* @param boolean $doubleEncode
* @return string
*/
public static function htmlentities($string, $quoteStyle = ENT_COMPAT, $charset = 'UTF-8', $doubleEncode =
false) {
return htmlentities($string, (!is_null($quoteStyle) ? $quoteStyle : ENT_COMPAT),
(!is_null($charset) ? $charset : 'UTF-8'), $doubleEncode);
}
/**
* Initialize the character maps needed for the xhtmlentities() method and verifies the argument values
* passed to it are valid.
*
* @param int $quoteStyle
* @param string $charset Only valid options are UTF-8 and ISO-8859-1 (Latin-1)
* @param boolean $doubleEncode
*/
protected static function initXhtmlentities($quoteStyle, $charset, $doubleEncode) {
$chars = get_html_translation_table(HTML_ENTITIES, $quoteStyle);
if (isset($chars)) {
unset($chars['<'], $chars['>'], $chars['&']);
$charMaps[$quoteStyle]['ISO-8859-1'][true] = $chars;
$charMaps[$quoteStyle]['ISO-8859-1'][false] = array_combine(array_values($chars), $chars);
$charMaps[$quoteStyle]['UTF-8'][true] = array_combine(array_map('utf8_encode', array_keys($chars)),
$chars);
$charMaps[$quoteStyle]['UTF-8'][false] = array_merge($charMaps[$quoteStyle]['ISO-8859-1'][false],
$charMaps[$quoteStyle]['UTF-8'][true]);
self::$loadedObjects['xhtmlEntities'] = $charMaps;
}
if (!isset($charMaps[$quoteStyle][$charset][$doubleEncode])) {
if (!isset($chars)) {
$invalidArgument = 'quoteStyle = ' . $quoteStyle;
} else if (!isset($charMaps[$quoteStyle][$charset])) {
$invalidArgument = 'charset = ' . $charset;
} else {
$invalidArgument = 'doubleEncode = ' . (string) $doubleEncode;
}
trigger_error('Undefined argument sent to xhtmlentities() method: ' . $invalidArgument,
E_USER_NOTICE);
}
}
/**
* Converts special characters in a string to XHTML-valid ASCII encoding the same as htmlentities except
* this method allows the use of HTML tags and ASCII-code within your string. Signature is the same as
htmlentities
* except that the only character sets available (third argument) are UTF-8 (default) and ISO-8859-1
(Latin-1).
*
* @param string $string
* @param int $quoteStyle Constants available are ENT_NOQUOTES (default), ENT_QUOTES, ENT_COMPAT
* @param string $charset Only valid options are UTF-8 (default) and ISO-8859-1 (Latin-1)
* @param boolean $doubleEncode Default is false
* @return string
*/
public static function xhtmlentities($string, $quoteStyle = ENT_NOQUOTES, $charset = 'UTF-8',
$doubleEncode = false) {
$quoteStyles = array(ENT_NOQUOTES, ENT_QUOTES, ENT_COMPAT);
$quoteStyle = (!in_array($quoteStyle, $quoteStyles) ? current($quoteStyles) : $quoteStyle);
$charset = ($charset != 'ISO-8859-1' ? 'UTF-8' : $charset);
$doubleEncode = (Boolean) $doubleEncode;
if (!isset(self::$loadedObjects['xhtmlEntities'][$quoteStyle][$charset][$doubleEncode])) {
self::initXhtmlentities($quoteStyle, $charset, $doubleEncode);
}
$string = strtr($string, self::$loadedObjects['xhtmlEntities'][$quoteStyle][$charset][$doubleEncode]);
return preg_replace('/\&(?!([a-zA-Z]{3}[a-zA-Z0-9]{0,3}|#\d{1,8}|#x[a-zA-Z0-9]{2,8});)/', '&',
$string);
}
}
/**
* Abstract class to extend model functionality (usage is optional, model functionality is not dependent on this)
*/
abstract class model {
/**
* Enables true lazy-loading of database connections when config::MODEL_AUTOCONNECT is set to false and each
* model extends this class
*
* @param string $var
* @return object
*/
public function __GET($var) {
get::dbConnection($var);
return $this->$var = config::$$var;
}
}
class cache {
/**
* Public access to the cache object
* @return obj
*/
public function __construct() {
return get::$cache;
}
/**
* Static wrapper for the caching methods
*
* @param str $method
* @param array $args
* @return mixed
*/
public static function __callStatic($method, array $args) {
return call_user_func_array(array(get::$cache, $method), $args);
}
/**
* Automates caching of a function or method result
*
* @param str $id
* @param mixed $method
* @param array $args
* @param int $expires
* @return mixed
*/
public static function method($id, $method, array $args = array(), $flag = null, $expires = null) {
$return = get::$cache->get($id);
if ($return === false) {
$return = call_user_func_array($method, $args);
get::$cache->set($id, $return, $flag, $expires);
}
return $return;
}
/**
* The next bunch of methods are workarounds for lack of __callStatic(), they can be safely removed in PHP
5.3+
* @deprecated
*/
public static function get() {
$args = func_get_args();
return call_user_func_array(array(get::$cache, __FUNCTION__), $args);
}
public static function set() {
$args = func_get_args();
return call_user_func_array(array(get::$cache, __FUNCTION__), $args);
}
public static function delete() {
$args = func_get_args();
return call_user_func_array(array(get::$cache, __FUNCTION__), $args);
}
public static function flush() {
$args = func_get_args();
return call_user_func_array(array(get::$cache, __FUNCTION__), $args);
}
}
/**
* Public interface to load elements and cause redirects
*/
class load {
/**
* Flag set to false to supress loading a view; used when the user is redirected
* @var boolean
*/
public static $loadView = true;
/**
* Sends a redirects header and disables view rendering
* This redirects via a browser command, this is not the same as changing controllers which is handled within
MVC
*
* @param string $url Optional, if undefined this will refresh the page (mostly useful for dumping post
values)
* @param boolean $exit Optional, defaults to true - if false then the instance will be allowed to continue
after
*/
public static function redirect($url = null, $exit = true) {
self::$loadView = false;
header('Location: ' . ($url ? $url : get::url(array('get' => true))));
if ($exit) {
exit(0);
}
}
/**
* Loads the 404 Not Found error page
*
* During controller execution this takes effect only upon completion of the current controller-action
* During view/layout execution this uses a browser-redirect
*
* For immediate redirection without further code-execution, "return" immediately via: return
load::error404();
*/
public static function error404() {
mvc::$controller = get::$config->errorHandler;
mvc::$action = 'notFound';
$state = mvc::state();
if ($state == 'layout' || $state == 'view') {
self::redirect('/' . mvc::$controller . '/' . mvc::$action);
}
}
/**
* Loads an element to STDOUT
*
* @param string $element
* @param array $params
*/
public static function element($element, $params = array()) {
return mvc::loadElement($element, $params);
}
/**
* Loads JavaScript library; any output goes directly to stdout (echos to the screen)
* Simplified-access wrapper to htmlHelper::jsLoad()
*
* @param mixed $args Same args as htmlHelper::jsLoad()
*/
public static function js($args) {
echo get::helper('html')->jsLoad($args);
}
}
if (!defined('DONT_AUTOSTART_VORK')) {
new mvc;
}

