Open-Source PHP Framework - Designed for rapid development of performance-oriented scalable applications

/webroot/vork

[return to app]
1 <?php error_reporting(E_ALL E_STRICT);
2
3
define('DEBUG_MODE'false); //this is the only line that needs to be adjusted in this file
4
5 /**
6  * Vork Framework www.Vork.us
7  * @version 3.00
8  * @author Eric David Benari, Vork Chief Architect
9  */
10
class configInit {
11     
/**
12      * Location of the packages folder (relative to webroot)
13      */
14     
public static function packagesPath() {
15          return 
configDefaults::basepath() . 'packages' self::DS;
16     }
17
18     
/**
19      * Directory separator, in both Linux and Windows you can just leave this as-is
20      */
21     
const DS '/';
22
23     
/**
24      * Creates the ability to access constants within inheriting classes just like properties
25      * If there is a naming conflict properties take precedence, but this should never be
26      * an issue when following standard Zend Framework/PEAR naming conventions where properties use
27      * camel-caps and constants are all-capitals delimited by undersores.
28      *
29      * get::$config->myProperty
30      * get::$config->MY_CONSTANT
31      *
32      * @param string $key
33      * @return mixed
34      */
35     
public function __get($key) {
36         
$constantName get_class($this) . '::' $key;
37         if (
defined($constantName)) {
38             return 
constant($constantName);
39         } else {
40             
trigger_error('Undefined property: ' $constantNameE_USER_NOTICE);
41         }
42     }
43 }
44
45 if (
DEBUG_MODE) {
46     require 
configDefaults::basepath() . '.debug';
47 }
48
49
/**
50  * Default configuration - these settings will only be used if they are not defined in the config file
51  */
52
class configDefaults extends configInit {
53     
/**
54      * Path to the config file, change only if not using the default file name/location
55      * Note: if no DB connections or other configuration settings are needed the .config file can
56      * be deleted and this setting will be ignored.
57      */
58     
const CONFIG_PATH '.config';
59
60     
/**
61      * Optional, only need to set these if you wish to execute a file that is global to the
62      * application before and/or after the page loads. Filename must match the class name and be in the MVC root.
63      */
64     
const APP_CONSTRUCT nullAPP_DESTRUCT null;
65
66     
/**
67      * Optional, only need to set these in your config class if you move the folders from the default locations
68      *
69      * @var string
70      */
71     
public $pathToMvc$modelsFolder$viewsFolder$controllersFolder,
72            
$layoutsFolder$elementsFolder$componentsFolder$helpersFolder;
73
74     
/**
75      * Optional, only need to set this in your config class if the webroot folder is not installed in the root of
76      * your web site directory
77      *
78      * @var string
79      */
80     
public $basepath '/';
81
82     
/**
83      * Name of controller that is used to handle errors
84      *
85      * @var string
86      */
87     
public $errorHandler 'error';
88
89     
/**
90      * By default no extension is used for MVC files, if you need to use a file extension then
91      * it needs to be set it in your config class in order for the files to be found
92      * and you need to include the prepended dot
93      *
94      * @var string
95      */
96     
public $fileExtension '';
97
98     
/**
99      * These are the default helperes that will be loaded into every view (and available to every element)
100      *
101      * Default is 'html' and 'form'; you may wish to add more by overriding this property this
102      * property within your config and adding the name of the additional helpers to the parent array.
103      *
104      * Note: only do this if most of your application needs the helper, otherwise you can load
105      * helpers in the __construct of a controller or within any method (action) of a controller
106      *
107      * @var array
108      */
109     
public $helpers = array('html''form');
110
111     
/**
112      * This sets the objects that will be available to all your models, the default is db and
113      * that should suffice for most applications. To add more create the object (class) in your config
114      * and then override this property adding the name of your object to the parent array and add a static
115      * property with the same name as the object to the config class (this will contain the object later.)
116      */
117     
public static $db$modelObjects = array('db');
118
119     
/**
120      * Internal cache of basepath
121      * @var string
122      */
123     
protected static $_basepath;
124
125     
/**
126      * Set the base working path
127      * @return string
128      */
129     
public static function basepath() {
130         if (
self::$_basepath === null) {
131             if (isset(
$_SERVER['DOCUMENT_ROOT']) && $_SERVER['DOCUMENT_ROOT']) {
132                 
$_SERVER['DOCUMENT_ROOT'] = rtrim($_SERVER['DOCUMENT_ROOT'], self::DS);
133                 
self::$_basepath = (substr($_SERVER['DOCUMENT_ROOT'], -7) == 'webroot'
134                                     
substr($_SERVER['DOCUMENT_ROOT'], 0, -8) : $_SERVER['DOCUMENT_ROOT']) .
 
self::DS;
135             } else { 
//cli
136                 
self::$_basepath substr($_SERVER['SCRIPT_FILENAME'], 0, -12); //remove webroot/vork
137             
}
138         }
139         return 
self::$_basepath;
140     }
141
142     
/**
143      * Establishes MVC folder locations automatically and sets default title/meta-data (if defaults exist)
144      */
145     
public function __construct() {
146         if (!
get::$title) {
147             
get::$title = (isset($this->title) && $this->title $this->title $this->SITE_NAME);
148         }
149         if (!
get::$meta && isset($this->meta) && is_array($this->meta)) {
150             
get::$meta $this->meta;
151         }
152
153         if (
$this->pathToMvc === null) {
154             
$this->pathToMvc configDefaults::basepath() . 'mvc' self::DS;
155             if (isset(
$_SERVER['SCRIPT_NAME']) && $_SERVER['SCRIPT_NAME'] == '/webroot/vork') {
156                 
$this->pathToMvc $_SERVER['DOCUMENT_ROOT'] . self::DS 'mvc' self::DS;
157             }
158         } else {
159             if (
substr($this->pathToMvc, -1) != self::DS) { //append trailing slash if needed
160                 
$this->pathToMvc .= self::DS;
161             }
162         }
163         
$mvcFolders = array('models''views''controllers',
164                             
'layouts''elements''components''helpers');
165         foreach (
$mvcFolders as $mvcFolder) {
166             if (
$this->{$mvcFolder 'Folder'} === null) {
167                 
$this->{$mvcFolder 'Folder'} = $mvcFolder;
168             }
169         }
170     }
171 }
172
173
/**
174  * The internal MVC logic - essentially the brain of the framework
175  */
176
class mvc {
177     
/**
178      * Override these within your application to change controller, action, layout or view
179      * You can also modify the params array as needed.
180      * mvc::$controller = 'cart';
181      * mvc::$action = 'logout';
182      * mvc::$layout = 'mobile';
183      * mvc::$view = 'confirmation';
184      *
185      * Changes to the above will only take affect after the current action returns, to make the affect
186      * immediate (to redirect before completing the current action) simply add a return; statement right after.
187      *
188      * These properties have defaults set:
189      * If no controller is defined in the URL then the index controller is used
190      * If no action is defined in the URL then the index action is used
191      * If no layout is defined in the controller then the default layout is used
192      *
193      * Views will always default to a file with the same name as the action and will first look within a
194      * folder in the views directory that has the same name as the controller. If using the index action or
195      * in the case that a controller is not found (usually for a page of semi-static content) and the folder
196      * or the file within it is not found it will then look for a file in the root of the views
197      * directory with the name of the controller.
198      *
199      * Acceptable locations are:
200      * /mvc/views/controllerName/actionName
201      * /mvc/views/controllerName
202      *
203      * To use a view from a different controller you can simply override the view setting within
204      * your application and include the path:
205      * mvc::$view = 'someOtherController' . get::$config->DS . 'newView';
206      */
207     
public static $controller 'index'$action 'index'$layout 'default'$view$params = array();
208
209     
/**
210      * This is a safety to prevent redirecting in infinite loops (eg. a redirects to b, then b redirects
211      * back to a.) The default maximum redirects is 10 within one request, if you need more you could
212      * increase this but keep in mind that any app that needs to redirect over 10 times per request
213      * likely has some serious flaw in the architecture.
214      */
215     
protected $_maxActionRedirects 10$_redirectHistory = array();
216
217     
/**
218      * Buffer used to contain view params
219      * @var array
220      */
221     
protected static $_viewParams = array();
222
223     
/**
224      * Indicator as to the current processing-state of Vork internals
225      */
226     
protected static $_state '__construct';
227
228     
/**
229      * Set the $_state property
230      */
231     
public static function setState($state) {
232         if (
DEBUG_MODE) {
233             
debug::log('Vork internal state: ' $state);
234         }
235         
self::$_state $state;
236     }
237
238     
/**
239      * Read-only public access to the $_state property
240      * @return string
241      */
242     
public static function state() {
243         return 
self::$_state;
244     }
245
246     
/**
247      * Replaces characters that are not valid within the name of a PHP object
248      *
249      * @param string $objName
250      * @return string
251      */
252     
public static function getValidObjectName($objName) {
253         
$objName preg_replace('/\W/''_'$objName);
254         if (
is_numeric($objName[0])) { //cannot start with a number
255             
$objName '_' $objName;
256         }
257         return 
$objName;
258     }
259
260     
/**
261      * Load config, open a DB connection & cache (if they are defined,) parse the URL to determine
 
controller/action
262      
* and then load the page
263      
*/
264     public function 
__construct() {
265         if (
is_file(configDefaults::basepath() . configDefaults::CONFIG_PATH)) {
266             require 
configDefaults::basepath() . configDefaults::CONFIG_PATH;
267             
get::$config = new config;
268         } else {
269             
get::$config = new configDefaults;
270         }
271         if (
DEBUG_MODE) {
272             
debug::log('Config loaded');
273         }
274
275         if (
get::$config->CACHE_CONNECT) {
276             
get::dbConnection('cache');
277             if (
DEBUG_MODE) {
278                 
debug::log('Cache loaded');
279             }
280         }
281
282         
$this->_parseUrl();
283
284         if (
get::$config->APP_CONSTRUCT && is_file(get::$config->pathToMvc get::$config->APP_CONSTRUCT)) {
285             require 
get::$config->pathToMvc get::$config->APP_CONSTRUCT;
286             
get::$config->{get::$config->APP_CONSTRUCT} = new get::$config->APP_CONSTRUCT;
287             if (
DEBUG_MODE) {
288                 
debug::log('APP_CONSTRUCT loaded');
289             }
290         }
291
292         
$this->_loadAction();
293
294         if (
get::$config->APP_DESTRUCT && is_file(get::$config->pathToMvc get::$config->APP_DESTRUCT)) {
295             require 
get::$config->pathToMvc get::$config->APP_DESTRUCT;
296             
get::$config->{get::$config->APP_DESTRUCT} = new get::$config->APP_DESTRUCT;
297             if (
DEBUG_MODE) {
298                 
debug::log('APP_DESTRUCT loaded');
299             }
300         }
301     }
302
303     
/**
304      * Loads an element. Elements can access the view params plus the params sent to them as an argument as if
305      * they are local variables. If there is a naming conflict the variable in the argument takes precedence.
306      *
307      * @param string $outputType
308      * @param string $elementName
309      * @param array $elementParams
310      * @return mixed
311      */
312     
protected static function _loadElement($outputType$elementName$elementParams) {
313         
$elementFile get::mvcFilePath('elements'$elementName);
314         if (
is_file($elementFile)) {
315             if (
is_array(self::$_viewParams)) {
316                 
extract(self::$_viewParams);
317             }
318             if (
is_array($elementParams)) {
319                 
extract($elementParams);
320             }
321             if (
DEBUG_MODE) {
322                 
$logMsg 'element ' $elementName ' loaded';
323                 if (
$elementParams) {
324                     
$logMsg .= ' with ' json_encode($elementParams);
325                 }
326             }
327             if (
$outputType == 'return') {
328                 
ob_start();
329                 require 
$elementFile;
330                 
$element ob_get_contents();
331                 
ob_end_clean();
332                 if (
DEBUG_MODE) {
333                     
debug::log($logMsg);
334                 }
335                 return 
$element;
336             } else {
337                 
$return = require $elementFile;
338                 if (
DEBUG_MODE) {
339                     
debug::log($logMsg);
340                 }
341                 return 
$return;
342             }
343         } else {
344             
trigger_error('File for element ' $elementName ' could not be found'E_USER_WARNING);
345         }
346     }
347
348     
/**
349      * Returns the output of an element as a string
350      *
351      * @param string $elementName
352      * @param array $elementParams
353      * @return string
354      */
355     
public static function getElement($elementName$elementParams = array()) {
356         return 
self::_loadElement('return'$elementName$elementParams);
357     }
358
359     
/**
360      * Loads an element, any output goes directly to stdout (echos to the screen)
361      *
362      * @param string $elementName
363      * @param array $elementParams
364      */
365     
public static function loadElement($elementName$elementParams = array()) {
366         return 
self::_loadElement('echo'$elementName$elementParams);
367     }
368
369     
/**
370      * Loads the default helpers defined in the config at every page instance
371      */
372     
protected function _loadDefaultHelpers() {
373         foreach (
get::$config->helpers as $helper) {
374             if (!isset(
self::$_viewParams[$helper])) {
375                 
$helperFile get::mvcFilePath('helpers'$helper);
376                 if (
is_file($helperFile)) {
377                     if (!
class_exists($helper 'Helper')) {
378                         require 
$helperFile;
379                     }
380                     
$helperClassName $helper 'Helper';
381                     
get::$loadedObjects['helper'][$helper][null] = self::$_viewParams[$helper] = new
 
$helperClassName;
382                 } else {
383                     
trigger_error('File for ' $helperFile ' helper could not be found'E_USER_WARNING);
384                 }
385             }
386         }
387         if (
DEBUG_MODE) {
388             
debug::log('Default helpers loaded: ' implode(', 'get::$config->helpers));
389         }
390     }
391
392     
/**
393      * Loads a view, if a layout exists then the view will be loaded into the layout
394      *
395      * @param string $controllerName
396      * @param string $action
397      * @return mixed
398      */
399     
protected function _loadView($controllerName$action) {
400         if (!
load::$loadView || self::$view === false) {
401             return;
402         }
403         
self::setState('view');
404         if (
self::$view == null) { //try: /mvc/views/controllerName/actionName
405             
$viewFile get::mvcFilePath('views'$controllerName get::$config->DS $action);
406             if (!
is_file($viewFile) && $action == 'index') { //try: /mvc/views/controllerName
407                 
$viewFile get::mvcFilePath('views'$controllerName);
408             }
409         } else {
410             
$viewFile get::mvcFilePath('views'$controllerName get::$config->DS self::$view);
411             if (!
is_file($viewFile)) { //local path failed, try absolute path
412                 
$viewFile get::mvcFilePath('views'self::$view);
413             }
414         }
415
416         if (!
is_file($viewFile) && $controllerName != get::$config->errorHandler) {
417             return 
$this->_loadAction(get::$config->errorHandler'missingView');
418         }
419
420         if (
is_array(self::$_viewParams)) {
421             
extract(self::$_viewParams);
422         }
423
424         if (
is::ajax() && get::$config->ajaxEnabled
425             
&& (!is_array(get::$config->ajaxEnabled//ajax=true
426                 
|| (isset(get::$config->ajaxEnabled[$controllerName])
427                     && (
get::$config->ajaxEnabled[$controllerName] === true //ajax[controllerName]=true
428                         
|| get::$config->ajaxEnabled[$controllerName] == $action //ajax[controllerName]=action
429                         
|| (is_array(get::$config->ajaxEnabled[$controllerName])
 //ajax[controllerName]=array(action)
430                             
&& in_array($actionget::$config->ajaxEnabled[$controllerName])))))) {
431             
self::$layout false//AJAX request
432         
}
433
434         
$layoutFile get::mvcFilePath('layouts'self::$layout);
435         if (
self::$layout && is_file($layoutFile)) {
436             
ob_start();
437             require 
$viewFile;
438             
$view ob_get_contents();
439             
ob_end_clean();
440             if (
DEBUG_MODE) {
441                 
debug::log('View loaded');
442             }
443             
self::setState('layout');
444             require 
$layoutFile;
445             if (
DEBUG_MODE) {
446                 
debug::log('Layout loaded');
447             }
448         } else {
449             require 
$viewFile;
450             if (
DEBUG_MODE) {
451                 
debug::log('View loaded');
452             }
453         }
454     }
455
456     
/**
457      * Redirects controllers and/or action
458      *
459      * @param string $originalController
460      * @param string $originalAction
461      */
462     
protected function _redirectAction($originalController$originalAction) {
463         if (
count($this->_redirectHistory) < $this->_maxActionRedirects) {
464             
$this->_redirectHistory[] = array('oldController' => $originalController,
465                                               
'newController' => self::$controller,
466                                               
'oldAction' => $originalAction,
467                                               
'newAction' => self::$action);
468             
$this->_loadAction();
469         } else {
470             
self::$params['redirectHistory'] = $this->_redirectHistory;
471             
$this->_loadAction(get::$config->errorHandler'redirectEndlessLooping');
472         }
473     }
474
475     
/**
476      * Loads an action
477      *
478      * @param string $controllerName Optional, if omitted then self::$controller is used
479      * @param string $action Optional, if omitted then self::$action is used
480      * @return null
481      */
482     
protected function _loadAction($controllerName null$action null) {
483         if (
is_null($controllerName)) {
484             
$controllerName self::$controller;
485         }
486         if (
is_null($action)) {
487             
$action self::getValidObjectName(self::$action);
488         }
489         if (
strpos($controllerName'..') !== false || strpos($controllerName':') !== false) {
490             return 
$this->_loadAction(get::$config->errorHandler'invalidControllerName');
491         }
492         if (!
is_file(get::mvcFilePath('controllers'$controllerName))) { //no controller, attempt to load view
 directly
493             
$this->_loadDefaultHelpers();
494             return 
$this->_loadView($controllerName$action);
495         }
496         
$originalController $controllerName;
497         
$controllerClassName $controllerName 'Controller';
498         if (!
class_exists($controllerClassName)) {
499             require 
get::mvcFilePath('controllers'$controllerName);
500         }
501         if (!
class_exists($controllerClassName) && $controllerName != get::$config->errorHandler) {
502             return 
$this->_loadAction(get::$config->errorHandler'controllerNotDefined');
503         }
504
505         
$originalAction self::$action;
506         
$controller = new $controllerClassName;
507         if (
$controllerName != self::$controller && $controllerName != get::$config->errorHandler) {
508             return 
$this->_redirectAction($originalController$action); //controller was changed in
 __construct()
509         
}
510         if (
$originalAction != self::$action) { //action was changed in __construct()
511             
$action self::$action;
512         }
513         
$optionalAction = (defined($controllerName 'Controller::optionalAction')
514                            && 
constant($controllerName 'Controller::optionalAction'));
515         if (!
$optionalAction) { //updates self::$action in instances where _loadAction() was called with $action
 arg
516             
self::$action $action;
517         }
518         if (!
method_exists($controller$action) && $controllerName != get::$config->errorHandler) {
519             if (
$optionalAction && self::$action != 'index') {
520                 
array_unshift(self::$paramsself::$action);
521                 
self::$action 'index';
522                 return 
$this->_loadAction($controllerName'index');
523             } else {
524                 return 
$this->_loadAction(get::$config->errorHandler'missingAction');
525             }
526         }
527
528         
$this->_loadDefaultHelpers();
529         
self::setState('controller::action');
530         
$viewParams $controller->$action();
531         if (
is_array($viewParams)) {
532             
self::$_viewParams array_merge(self::$_viewParams$viewParams);
533         }
534         if (
DEBUG_MODE) {
535             
debug::log('Controller:action loaded');
536         }
537
538         if (
$controllerName != get::$config->errorHandler) {
539             if (
self::$controller != $originalController || self::$action != $action) {
540                 return 
$this->_redirectAction($originalController$action);
541             }
542         }
543         
$this->_loadView($controllerName$action);
544     }
545
546     
/**
547      * Parse URL string to extract controller name, action name and params
548      */
549     
protected function _parseUrl() {
550         
$basepathLen strlen(get::$config->basepath);
551         if (isset(
$_SERVER['REDIRECT_URL'])) {
552             
$_SERVER['REQUEST_URI'] = $_SERVER['REDIRECT_URL'];
553         }
554         if (isset(
$_SERVER['REQUEST_URI'])) {
555             
$path substr($_SERVER['REQUEST_URI'], $basepathLen);
556             
$positionOfGet strpos($path'?');
557             if (
$positionOfGet !== false) {
558                 
$path substr($path0$positionOfGet);
559             }
560             
$pathParts explode('/'$path);
561             
$fileName = (strrpos($_SERVER['SCRIPT_FILENAME'], get::$config->DS) + 1);
562             if (isset(
$pathParts[0]) && $pathParts[0] == substr($_SERVER['SCRIPT_FILENAME'], $fileName)) {
563                 
$firstSegment array_shift($pathParts); //remove the current filename from the path parts;
564                 
if ($firstSegment == 'vork' && is::superuser()) {
565                     require 
configDefaults::basepath() . '.installer';
566                 }
567             }
568             foreach (
$pathParts as $pathSegment) {
569                 
$pathSegment trim($pathSegment);
570                 if (
$pathSegment != '') {
571                     
$pathSegments[] = $pathSegment;
572                 }
573             }
574         } else if (isset(
$_SERVER['argv'])) { //cli-mode
575             
$pathSegments $_SERVER['argv'];
576             
array_shift($pathSegments);
577         }
578
579         if (isset(
$pathSegments) && $pathSegments) {
580             
self::$controller self::getValidObjectName(array_shift($pathSegments));
581
582             if (isset(
$pathSegments[0])) {
583             
self::$action array_shift($pathSegments);
584                 if (!empty(
$pathSegments)) {
585                     
self::$params $pathSegments;
586                 }
587             }
588         }
589         if (
DEBUG_MODE) {
590             
debug::log('URL parsed');
591         }
592     }
593 }
594
595
/**
596  * Blackhole object used when catching a database exception to avoid fatal errors being thrown before reaching
597  * the error-handler controller & view.
598  * Note: static database methods will still fatal-error in pre-5.3 versions of PHP as __callStatic was not yet
 
supported
599  
*/
600 class 
blackhole implements IteratorArrayAccess {
601     public static function 
__callStatic($method, array $args) {
602         return new 
blackhole;
603     }
604     public function 
__call($method, array $args) {
605         return 
$this;
606     }
607     public function 
__get($name) {
608         return 
$this;
609     }
610     public function 
__set($name$val) {}
611     public function 
__isset($name) {
612         return 
false;
613     }
614     public function 
__unset($name) {}
615     public function 
__toString() {
616         return 
'';
617     }
618
619     public function 
rewind() {}
620     public function 
current() {}
621     public function 
key() {}
622     public function 
next() {}
623     public function 
valid() {
624         return 
false;
625     }
626
627     public function 
offsetExists($offset) {
628         return 
false;
629     }
630     public function 
offsetGet($offset) {}
631     public function 
offsetSet($offset$value) {}
632     public function 
offsetUnset($offset) {}
633 }
634
635
/**
636  * Public interface to determine Boolean properties
637  */
638
class is {
639     
/**
640      * If instance is requested via AJAX
641      * @return Boolean
642      */
643     
public static function ajax() {
644         return (isset(
$_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] ==
 
'XMLHttpRequest');
645     }
646
647     
/**
648      * If instance is requested via HTTPS
649      * @return Boolean
650      */
651     
public static function ssl() {
652         return (isset(
$_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
653     }
654
655     
/**
656      * Internal cache for the is::bot() and is::mobile() methods
657      * @var Boolean or null
658      */
659     
protected static $_bot$_mobile;
660
661     
/**
662      * Identifies a spider/bot/crawler
663      * @return Boolean
664      */
665     
public static function bot() {
666         if (
is_null(self::$_bot)) {
667             
self::$_bot false;
668             if (isset(
$_SERVER['HTTP_USER_AGENT'])) {
669                 
$spiders = array('search''spider''crawler''archiver''indexer''webbot''robot',
670                                  
'nagios''Googlebot''MSNbot''Jeeves''Slurp''Scooter''InfoSeek',
 
'Lycos',
671                                  
'seoprofiler''Feedfetcher''facebookexternalhit');
672                 
self::$_bot = (preg_match('/' implode('|'$spiders) . '/i'$_SERVER['HTTP_USER_AGENT']));
673             }
674         }
675         return 
self::$_bot;
676     }
677
678     
/**
679      * Identifies a mobile user
680      * @return Boolean
681      */
682     
public static function mobile() {
683         if (
is_null(self::$_mobile)) {
684             
self::$_mobile false;
685             if (isset(
$_SERVER['HTTP_USER_AGENT'])) {
686                 
$mobile = array('Android''AvantGo''BlackBerry''DoCoMo''Fennec''iPod''iPhone''J2ME',
687                                 
'MIDP''NetFront''Nokia''Opera Mini''PalmOS''PalmSource''portalmmm',
688                                 
'Plucker''ReqwirelessWeb''SonyEricsson''Symbian''UP\.Browser''webOS',
689                                 
'Windows CE''Xiino');
690                 
self::$_mobile = (preg_match('/' implode('|'$mobile) . '/i'$_SERVER['HTTP_USER_AGENT']));
691             }
692         }
693         return 
self::$_mobile;
694     }
695
696     
/**
697      * If instance is requested from Flash
698      * @return Boolean
699      */
700     
public static function flash() {
701         return (isset(
$_SERVER['HTTP_USER_AGENT'])
702                 && 
preg_match('/^(Shockwave|Adobe) Flash/i'$_SERVER['HTTP_USER_AGENT']));
703     }
704
705     
/**
706      * Wrapper for get::$config->isSuperuser() to unify syntax
707      * @return Boolean
708      */
709     
public static function superuser() {
710         return 
get::$config->isSuperuser();
711     }
712 }
713
714
/**
715  * Public interface to retrieve rendered elements and other objects
716  */
717
class get {
718     
/**
719      * Opens up public access to config constants and variables and the cache object
720      * @var object
721      */
722     
public static $config$cache;
723
724     
/**
725      * Used to store the page title and meta data for layout usage
726      *
727      * You can set properties for "title" and "meta" in the .config to act as default values (setting in the view
 
or
728      * 
controller will always override the defaults)
729      * public 
$title 'My Default Title';
730      * public 
$meta = array('keyowords' => 'apples, bananas, kiwis''description' => 'My site is about...');
731      *
732      * @var 
string $title
733      
* @var array $meta valid keys are description and keywords
734      
*/
735     public static 
$title$meta = array();
736
737     
/**
738      * Index of objects loaded, used to maintain uniqueness of singletons
739      * @var array
740      */
741     
public static $loadedObjects = array();
742
743     
/**
744      * Index of names of database-objects that have a connection open
745      * @var array Keys are DB names, vals are always Bool true
746      */
747     
protected static $_dbConnected = array();
748
749     
/**
750      * Returns fully qualified path to an MVC file
751      *
752      * @param string $type
753      * @param string $mvcName
754      * @return string
755      */
756     
public static function mvcFilePath($type$mvcName) {
757         return 
self::$config->pathToMvc self::$config->{$type 'Folder'}
758              . 
self::$config->DS $mvcName self::$config->fileExtension;
759     }
760
761     
/**
762      * Gets the current URL
763      *
764      * @param array Optional, keys:
765      *              get - Boolean Default: false - include GET URL if it exists
766      *              abs - Boolean Default: false - true=absolute URL (aka. FQDN), false=just the path for relative
 
links
767      
*              ssl Boolean Default: null  true=httpsfalse=http, unset/null=auto-selects the current
 
protocol
768      
*                                             a true or false value implies abs=true
769      
* @return string
770      
*/
771     public static function 
url(array $args = array()) {
772         
$ssl null;
773         
$get false;
774         
$abs false;
775         
extract($args);
776
777         if (!isset(
$_SERVER['HTTP_HOST']) && PHP_SAPI == 'cli') {
778             
$_SERVER['HTTP_HOST'] = trim(`hostname`);
779             
$argv $_SERVER['argv'];
780             
array_shift($argv);
781             
$_SERVER['REDIRECT_URL'] = '/' implode('/'$argv);
782             
$get false// command-line has no GET
783         
}
784
785         
$url = (isset($_SERVER['REDIRECT_URL']) ? $_SERVER['REDIRECT_URL'] : $_SERVER['SCRIPT_NAME']);
786         if (
substr($url, -1) == '/') { //strip trailing slash for URL consistency
787             
$url substr($url0, -1);
788         }
789
790         if (
is_null($ssl) && $abs == true) {
791             
$ssl = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on');
792         }
793         if (
$abs || !is_null($ssl)) {
794             
$url = (!$ssl 'http://' 'https://') . $_SERVER['HTTP_HOST'] . $url;
795         }
796
797         if (
$get && isset($_SERVER['QUERY_STRING']) && $_SERVER['QUERY_STRING']) {
798             
$url .= '?' $_SERVER['QUERY_STRING'];
799         }
800         return (
$url $url '/');
801     }
802
803     
/**
804      * Initializes a database connection
805      * @param string $db
806      */
807     
public static function dbConnection($db) {
808         if (!isset(
self::$_dbConnected[$db]) && method_exists(self::$config'dbConnect')) {
809             try {
810                 
self::$config->dbConnect($db);
811                 
self::$_dbConnected[$db] = true;
812             } catch (
Exception $e) {
813                 
mvc::$params $e;
814                 
mvc::$controller get::$config->errorHandler;
815                 
mvc::$action 'DatasourceError';
816                 
config::$$db = new blackhole;
817             }
818         }
819     }
820
821     
/**
822      * Loads an object as a singleton
823      *
824      * @param string $objectType
825      * @param string $objectName
826      * @param mixed $args Optional, default is null - this is sent as an argument to the constructor and used as
 
part
827      
*                    of the criteria to determine a unique singleton object differing args return new
 
objects
828      
* @return object
829      
*/
830     protected static function 
_loadObject($objectType$objectName$args null) {
831         if (
$objectName == null) {
832             
$objectName mvc::$controller;
833         }
834         
$argsSerialized serialize($args);
835         if (isset(
self::$loadedObjects[$objectType][$objectName][$argsSerialized])) {
836             return 
self::$loadedObjects[$objectType][$objectName][$argsSerialized];
837         }
838         
$objectFile self::mvcFilePath($objectType 's'$objectName);
839         if (
is_file($objectFile)) {
840             if (
strpos($objectName'/')) { //objects within folders
841                 
$segments explode('/'$objectName);
842                 
$objectName array_shift($segments);
843                 foreach (
$segments as $segment) {
844                     
$objectName .= ucfirst($segment);
845                 }
846             }
847             
$objectClassName mvc::getValidObjectName($objectName) . ucfirst($objectType);
848             if (!
class_exists($objectClassName)) {
849                 require 
$objectFile;
850             }
851             if (
class_exists($objectClassName)) {
852                 try {
853                 
$objectObject = new $objectClassName($args);
854                 } catch (
Exception $e) {
855                     
trigger_error($e->getMessage(), E_USER_WARNING);
856                     return new 
blackhole;
857                 }
858                 if (
$objectType == 'model' && config::MODEL_AUTOCONNECT) {
859                     foreach (
config::$modelObjects as $modelObject) {
860                         
self::dbConnection($modelObject);
861                         
$objectObject->$modelObject config::$$modelObject;
862                     }
863                 }
864                 
self::$loadedObjects[$objectType][$objectName][$argsSerialized] = $objectObject;
865                 if (
DEBUG_MODE) {
866                     
debug::log($objectType ' ' $objectName ' loaded');
867                 }
868                 return 
$objectObject;
869             } else {
870                 
$errorMsg 'Class for ' $objectType ' ' $objectName ' could not be found';
871             }
872         } else {
873             
$errorMsg 'File for ' $objectType ' ' $objectName ' could not be found';
874         }
875         
trigger_error($errorMsgE_USER_WARNING);
876     }
877
878     
/**
879      * Returns a model object
880      *
881      * @param string $model
882      * @param mixed $args Optional - arguments to be passed to the model-object constructor
883      * @return object
884      */
885     
public static function model($model null$args null) {
886         return 
self::_loadObject('model'$model$args);
887     }
888
889     
/**
890      * Returns a component object
891      *
892      * @param string $component
893      * @param mixed $args Optional - arguments to be passed to the component-object constructor
894      * @return object
895      */
896     
public static function component($component null$args null) {
897         return 
self::_loadObject('component'$component$args);
898     }
899
900     
/**
901      * Returns a helper object
902      *
903      * @param string $helper
904      * @param mixed $args Optional - arguments to be passed to the helper-object constructor
905      * @return object
906      */
907     
public static function helper($helper$args null) {
908         if (
is_array($helper)) {
909             
array_walk($helper, array('self'__METHOD__));
910             return;
911         }
912         if (!
in_array($helperself::$config->helpers)) {
913             
self::$config->helpers[] = $helper;
914         }
915         return 
self::_loadObject('helper'$helper$args);
916     }
917
918     
/**
919      * Renders an element and returns the results as a string
920      *
921      * @param string $element
922      * @param array $params
923      */
924     
public static function element($element$params = array()) {
925         
$elementId $element $paramsString serialize($params);
926         if (!isset(
self::$loadedObjects['element'][$elementId])) {
927             
self::$loadedObjects['element'][$elementId] = mvc::getElement($element$params);
928         }
929         return 
self::$loadedObjects['element'][$elementId];
930     }
931
932     
/**
933      * Overloads the php function htmlentities and changes the default charset to UTF-8 and the default value for
 
the
934      
fourth parameter $doubleEncode to falseAlso adds ability to pass a null value to get the default
 
$quoteStyle
935      
* and $charset (removes need to repeatedly define ENT_COMPAT'UTF-8'just to access the $doubleEncode
 
argument)
936      *
937      * If 
you are using a PHP version prior to 5.2.3 the $doubleEncode parameter is not available so you will
 
need
938      
to comment out the last parameter in the return clause (including the preceding comma)
939      *
940      * @
param string $string
941      
* @param int $quoteStyle Uses ENT_COMPAT if null or omitted
942      
* @param string $charset Uses UTF-if null or omitted
943      
* @param boolean $doubleEncode
944      
* @return string
945      
*/
946     public static function 
htmlentities($string$quoteStyle ENT_COMPAT$charset 'UTF-8'$doubleEncode =
 
false) {
947         return 
htmlentities($string, (!is_null($quoteStyle) ? $quoteStyle ENT_COMPAT),
948                             (!
is_null($charset) ? $charset 'UTF-8'), $doubleEncode);
949     }
950
951     
/**
952      * Initialize the character maps needed for the xhtmlentities() method and verifies the argument values
953      * passed to it are valid.
954      *
955      * @param int $quoteStyle
956      * @param string $charset Only valid options are UTF-8 and ISO-8859-1 (Latin-1)
957      * @param boolean $doubleEncode
958      */
959     
protected static function initXhtmlentities($quoteStyle$charset$doubleEncode) {
960         
$chars get_html_translation_table(HTML_ENTITIES$quoteStyle);
961         if (isset(
$chars)) {
962             unset(
$chars['<'], $chars['>'], $chars['&']);
963             
$charMaps[$quoteStyle]['ISO-8859-1'][true] = $chars;
964             
$charMaps[$quoteStyle]['ISO-8859-1'][false] = array_combine(array_values($chars), $chars);
965             
$charMaps[$quoteStyle]['UTF-8'][true] = array_combine(array_map('utf8_encode'array_keys($chars)),
 
$chars);
966             
$charMaps[$quoteStyle]['UTF-8'][false] = array_merge($charMaps[$quoteStyle]['ISO-8859-1'][false],
967                                                                  
$charMaps[$quoteStyle]['UTF-8'][true]);
968             
self::$loadedObjects['xhtmlEntities'] = $charMaps;
969         }
970         if (!isset(
$charMaps[$quoteStyle][$charset][$doubleEncode])) {
971             if (!isset(
$chars)) {
972                 
$invalidArgument 'quoteStyle = ' $quoteStyle;
973             } else if (!isset(
$charMaps[$quoteStyle][$charset])) {
974                 
$invalidArgument 'charset = ' $charset;
975             } else {
976                 
$invalidArgument 'doubleEncode = ' . (string) $doubleEncode;
977             }
978             
trigger_error('Undefined argument sent to xhtmlentities() method: ' $invalidArgument,
 
E_USER_NOTICE);
979         }
980     }
981
982     
/**
983      * Converts special characters in a string to XHTML-valid ASCII encoding the same as htmlentities except
984      * this method allows the use of HTML tags and ASCII-code within your string. Signature is the same as
 
htmlentities
985      
except that the only character sets available (third argumentare UTF-(default) and ISO-8859-1
 
(Latin-1).
986      *
987      * @
param string $string
988      
* @param int $quoteStyle Constants available are ENT_NOQUOTES (default), ENT_QUOTESENT_COMPAT
989      
* @param string $charset Only valid options are UTF-(default) and ISO-8859-(Latin-1)
990      * @
param boolean $doubleEncode Default is false
991      
* @return string
992      
*/
993     public static function 
xhtmlentities($string$quoteStyle ENT_NOQUOTES$charset 'UTF-8',
994                                          
$doubleEncode false) {
995         
$quoteStyles = array(ENT_NOQUOTESENT_QUOTESENT_COMPAT);
996         
$quoteStyle = (!in_array($quoteStyle$quoteStyles) ? current($quoteStyles) : $quoteStyle);
997         
$charset = ($charset != 'ISO-8859-1' 'UTF-8' $charset);
998         
$doubleEncode = (Boolean) $doubleEncode;
999         if (!isset(
self::$loadedObjects['xhtmlEntities'][$quoteStyle][$charset][$doubleEncode])) {
1000             
self::initXhtmlentities($quoteStyle$charset$doubleEncode);
1001         }
1002         
$string strtr($stringself::$loadedObjects['xhtmlEntities'][$quoteStyle][$charset][$doubleEncode]);
1003         return 
preg_replace('/\&(?!([a-zA-Z]{3}[a-zA-Z0-9]{0,3}|#\d{1,8}|#x[a-zA-Z0-9]{2,8});)/''&amp;',
 
$string);
1004     }
1005 }
1006
1007
/**
1008  * Abstract class to extend model functionality (usage is optional, model functionality is not dependent on this)
1009  */
1010
abstract class model {
1011     
/**
1012      * Enables true lazy-loading of database connections when config::MODEL_AUTOCONNECT is set to false and each
1013      * model extends this class
1014      *
1015      * @param string $var
1016      * @return object
1017      */
1018     
public function __GET($var) {
1019         
get::dbConnection($var);
1020         return 
$this->$var config::$$var;
1021     }
1022 }
1023
1024 class 
cache {
1025     
/**
1026      * Public access to the cache object
1027      * @return obj
1028      */
1029     
public function __construct() {
1030         return 
get::$cache;
1031     }
1032
1033     
/**
1034      * Static wrapper for the caching methods
1035      *
1036      * @param str $method
1037      * @param array $args
1038      * @return mixed
1039      */
1040     
public static function __callStatic($method, array $args) {
1041         return 
call_user_func_array(array(get::$cache$method), $args);
1042     }
1043
1044     
/**
1045      * Automates caching of a function or method result
1046      *
1047      * @param str $id
1048      * @param mixed $method
1049      * @param array $args
1050      * @param int $expires
1051      * @return mixed
1052      */
1053     
public static function method($id$method, array $args = array(), $flag null$expires null) {
1054         
$return get::$cache->get($id);
1055         if (
$return === false) {
1056             
$return call_user_func_array($method$args);
1057             
get::$cache->set($id$return$flag$expires);
1058         }
1059         return 
$return;
1060     }
1061
1062     
/**
1063      * The next bunch of methods are workarounds for lack of __callStatic(), they can be safely removed in PHP
 
5.3+
1064      * @
deprecated
1065      
*/
1066     public static function 
get() {
1067         
$args func_get_args();
1068         return 
call_user_func_array(array(get::$cache__FUNCTION__), $args);
1069     }
1070     public static function 
set() {
1071         
$args func_get_args();
1072         return 
call_user_func_array(array(get::$cache__FUNCTION__), $args);
1073     }
1074     public static function 
delete() {
1075         
$args func_get_args();
1076         return 
call_user_func_array(array(get::$cache__FUNCTION__), $args);
1077     }
1078     public static function 
flush() {
1079         
$args func_get_args();
1080         return 
call_user_func_array(array(get::$cache__FUNCTION__), $args);
1081     }
1082 }
1083
1084
/**
1085  * Public interface to load elements and cause redirects
1086  */
1087
class load {
1088     
/**
1089      * Flag set to false to supress loading a view; used when the user is redirected
1090      * @var boolean
1091      */
1092     
public static $loadView true;
1093
1094     
/**
1095      * Sends a redirects header and disables view rendering
1096      * This redirects via a browser command, this is not the same as changing controllers which is handled within
 
MVC
1097      
*
1098      * @
param string $url Optional, if undefined this will refresh the page (mostly useful for dumping post
 
values)
1099      * @
param boolean $exit Optionaldefaults to true - if false then the instance will be allowed to continue
 
after
1100      
*/
1101     public static function 
redirect($url null$exit true) {
1102         
self::$loadView false;
1103         
header('Location: ' . ($url $url get::url(array('get' => true))));
1104         if (
$exit) {
1105             exit(
0);
1106         }
1107     }
1108
1109     
/**
1110      * Loads the 404 Not Found error page
1111      *
1112      * During controller execution this takes effect only upon completion of the current controller-action
1113      * During view/layout execution this uses a browser-redirect
1114      *
1115      * For immediate redirection without further code-execution, "return" immediately via: return
 
load::error404();
1116      */
1117     public static function 
error404() {
1118         
mvc::$controller get::$config->errorHandler;
1119         
mvc::$action 'notFound';
1120         
$state mvc::state();
1121         if (
$state == 'layout' || $state == 'view') {
1122             
self::redirect('/' mvc::$controller '/' mvc::$action);
1123         }
1124     }
1125
1126     
/**
1127      * Loads an element to STDOUT
1128      *
1129      * @param string $element
1130      * @param array $params
1131      */
1132     
public static function element($element$params = array()) {
1133         return 
mvc::loadElement($element$params);
1134     }
1135
1136     
/**
1137      * Loads JavaScript library; any output goes directly to stdout (echos to the screen)
1138      * Simplified-access wrapper to htmlHelper::jsLoad()
1139      *
1140      * @param mixed $args Same args as htmlHelper::jsLoad()
1141      */
1142     
public static function js($args) {
1143         echo 
get::helper('html')->jsLoad($args);
1144     }
1145 }
1146
1147 if (!
defined('DONT_AUTOSTART_VORK')) {
1148     new 
mvc;
1149 }