Major refactoring, implemented namespaces

pull/11/head
Mike Cao 13 years ago
parent 8af17f2215
commit e2f1dc99bb

@ -5,6 +5,15 @@
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
include __DIR__.'/core/Loader.php';
include __DIR__.'/core/Dispatcher.php';
/**
* The Flight class represents the framework itself. It is responsible
* loading an HTTP request, running the assigned services, and generating
* an HTTP response.
*/
class Flight {
/**
* Stored variables.
@ -14,39 +23,18 @@ class Flight {
protected static $vars = array();
/**
* Registered classes.
*
* @var array
*/
protected static $classes = array();
/**
* Mapped methods.
*
* @var array
*/
protected static $methods = array();
/**
* Method filters.
*
* @var array
*/
protected static $filters = array();
/**
* Class instances.
* Class loader.
*
* @var array
* @var object
*/
protected static $instances = array();
protected static $loader;
/**
* Autoload directories.
* Event dispatcher.
*
* @var array
* @var object
*/
protected static $dirs = array();
protected static $dispatcher;
// Don't allow object instantiation
private function __construct() {}
@ -60,28 +48,15 @@ class Flight {
* @param array $args Method parameters
*/
public static function __callStatic($name, $params) {
// Check if call is mapped to a method
if (isset(self::$methods[$name]) || method_exists(__CLASS__, '_'.$name)) {
$method = self::$methods[$name] ?: array(__CLASS__, '_'.$name);
// Run pre-filters
if (!empty(self::$filters['before'][$name])) {
self::filter(self::$filters['before'][$name], $params);
}
$callback = self::$dispatcher->get($name);
// Run requested method
$output = self::execute($method, $params);
// Run post-filters
if (!empty(self::$filters['after'][$name])) {
self::filter(self::$filters['after'][$name], $output);
}
return $output;
if (is_callable($callback)) {
return self::$dispatcher->run($name, $params);
}
// Otherwise try to autoload class
return self::load($name, (!empty($params)) ? (bool)$params[0] : true);
$shared = (!empty($params)) ? (bool)$params[0] : true;
return self::$loader->load($name, $shared);
}
/**
@ -95,7 +70,7 @@ class Flight {
throw new Exception('Cannot override an existing framework method.');
}
self::$methods[$name] = $callback;
self::$dispatcher->set($name, $callback);
}
/**
@ -111,40 +86,7 @@ class Flight {
throw new Exception('Cannot override an existing framework method.');
}
unset(self::$instances[$class]);
self::$classes[$name] = array($class, $params, $callback);
}
/**
* Loads a registered class.
*
* @param string $name Method name
* @param bool $shared Shared instance
*/
public static function load($name, $shared = true) {
if (isset(self::$classes[$name])) {
list($class, $params, $callback) = self::$classes[$name];
$do_callback = ($callback && (!$shared || !isset(self::$instances[$class])));
$obj = ($shared) ?
self::getInstance($class, $params) :
self::getClass($class, $params);
if ($do_callback) {
$ref = array(&$obj);
self::execute($callback, $ref);
}
return $obj;
}
$class = ucfirst($name);
return ($shared) ?
self::getInstance($class) :
self::getClass($class);
self::$loader->register($name, $class, $params, $callback);
}
/**
@ -154,7 +96,7 @@ class Flight {
* @param callback $callback Callback function
*/
public static function before($name, $callback) {
self::$filters['before'][$name][] = $callback;
self::$dispatcher->hook($name, 'before', $callback);
}
/**
@ -164,130 +106,18 @@ class Flight {
* @param callback $callback Callback function
*/
public static function after($name, $callback) {
self::$filters['after'][$name][] = $callback;
}
/**
* Executes a callback function.
*
* @param callback $callback Callback function
* @param array $params Function parameters
* @return mixed Function results
*/
public static function execute($callback, array &$params = array()) {
if (is_callable($callback)) {
return is_array($callback) ?
self::invokeMethod($callback, $params) :
self::callFunction($callback, $params);
}
}
/**
* Executes a chain of method filters.
*
* @param array $filters Chain of filters
* @param reference $data Method parameters or method output
*/
public static function filter($filters, &$data) {
$params = array(&$data);
foreach ($filters as $callback) {
$continue = self::execute($callback, $params);
if ($continue === false) break;
}
}
/**
* Calls a function.
*
* @param string $func Name of function to call
* @param array $params Function parameters
*/
public static function callFunction($func, array &$params = array()) {
switch (count($params)) {
case 0:
return $func();
case 1:
return $func($params[0]);
case 2:
return $func($params[0], $params[1]);
case 3:
return $func($params[0], $params[1], $params[2]);
case 4:
return $func($params[0], $params[1], $params[2], $params[3]);
case 5:
return $func($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array($func, $params);
}
self::$dispatcher->hook($name, 'after', $callback);
}
/**
* Invokes a method.
* Adds a path for class autoloading.
*
* @param mixed $func Class method
* @param array $params Class method parameters
* @param string $dir Directory path
*/
public static function invokeMethod($func, array &$params = array()) {
list($class, $method) = $func;
switch (count($params)) {
case 0:
return $class::$method();
case 1:
return $class::$method($params[0]);
case 2:
return $class::$method($params[0], $params[1]);
case 3:
return $class::$method($params[0], $params[1], $params[2]);
case 4:
return $class::$method($params[0], $params[1], $params[2], $params[3]);
case 5:
return $class::$method($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array($func, $params);
}
public static function path($dir) {
self::$loader->addDirectory($dir);
}
/**
* Gets a single instance of a class.
*
* @param string $class Class name
* @param array $params Class initialization parameters
*/
public static function getInstance($class, array $params = array()) {
if (!isset(self::$instances[$class])) {
self::$instances[$class] = self::getClass($class, $params);
}
return self::$instances[$class];
}
/**
* Gets a class object.
*
* @param string $class Class name
* @param array $params Class initialization parameters
*/
public static function getClass($class, array $params = array()) {
switch (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]);
default:
$refClass = new ReflectionClass($class);
return $refClass->newInstanceArgs($params);
}
}
/**
* Gets a variable.
*
@ -317,12 +147,12 @@ class Flight {
}
/**
* Checks if a variable exists.
* Checks if a variable has been set.
*
* @param string $key Key
* @return bool Variable status
*/
public static function exists($key) {
public static function has($key) {
return isset(self::$vars[$key]);
}
@ -347,9 +177,6 @@ class Flight {
static $initialized = false;
if (!$initialized) {
// Register autoloader
spl_autoload_register(array(__CLASS__, 'autoload'));
// Handle errors internally
set_error_handler(array(__CLASS__, 'handleError'));
@ -369,6 +196,29 @@ class Flight {
$_COOKIE = array_map($func, $_COOKIE);
}
// Load core components
self::$loader = new \flight\core\Loader();
self::$dispatcher = new \flight\core\Dispatcher();
// Register default components
self::$loader->addDirectory(dirname(__DIR__));
self::$loader->register('request', '\flight\net\Request');
self::$loader->register('response', '\flight\net\Response');
self::$loader->register('router', '\flight\net\Router');
self::$loader->register('view', '\flight\template\View', array(), function($view){
$view->path = Flight::get('flight.views.path') ?: './views';
});
// Register framework methods
$methods = array(
'start','stop','route','halt','error','notFound',
'render','redirect','etag','lastModified','json'
);
foreach ($methods as $name) {
self::$dispatcher->set($name, array(__CLASS__, '_'.$name));
}
// Enable output buffering
ob_start();
@ -376,27 +226,6 @@ class Flight {
}
}
/**
* Autoloads classes.
*
* @param string $class Class name
*/
public static function autoload($class) {
$file = str_replace('\\', '/', str_replace('_', '/', $class)).'.php';
$base = (strpos($file, '/') === false) ? __DIR__ : (self::get('flight.lib.path') ?: '.');
if (file_exists($base.'/'.$file)) {
require $base.'/'.$file;
}
else {
$loaders = spl_autoload_functions();
$last = array_pop($loaders);
if (is_array($last) && $last[0] == __CLASS__ && $last[1] == __FUNCTION__) {
throw new Exception('Unable to load file: '.$base.'/'.$file);
}
}
}
/**
* Custom error handler.
*/
@ -414,7 +243,6 @@ class Flight {
static::error($e);
}
catch (Exception $ex) {
error_log($ex->getMessage());
exit(
'<h1>500 Internal Server Error</h1>'.
'<h3>'.$ex->getMessage().'</h3>'.
@ -424,26 +252,17 @@ class Flight {
}
/**
* Routes a URL to a callback function.
*
* @param string $pattern URL pattern to match
* @param callback $callback Callback function
*/
public static function _route($pattern, $callback) {
self::router()->map($pattern, $callback);
}
/**
* Start the framework.
* Starts the framework.
*/
public static function _start() {
// Route the request
$result = self::router()->route(self::request());
$callback = self::router()->route(self::request());
if ($result !== false) {
list($callback, $params) = $result;
self::execute($callback, array_values($params));
if ($callback !== false) {
self::$dispatcher->execute(
$callback,
array_values(self::request()->params)
);
}
else {
self::notFound();
@ -467,6 +286,16 @@ class Flight {
->send();
}
/**
* Routes a URL to a callback function.
*
* @param string $pattern URL pattern to match
* @param callback $callback Callback function
*/
public static function _route($pattern, $callback) {
self::router()->map($pattern, $callback);
}
/**
* Stops processing and returns a given response.
*
@ -487,7 +316,6 @@ class Flight {
* @param object $ex Exception
*/
public static function _error(Exception $e) {
error_log($e->getMessage());
self::response(false)
->status(500)
->write(
@ -584,6 +412,6 @@ class Flight {
}
}
// Initialize framework on include
// Initialize the framework on include
Flight::init();
?>

@ -0,0 +1,171 @@
<?php
/**
* Flight: An extensible micro-framework.
*
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\core;
/**
* The Dispatcher class is responsible for dispatching events. Events
* are simply aliases for class methods or functions. The Dispatcher
* allows you to hook other function to an event that can modify the
* input parameters and/or the output.
*/
class Dispatcher {
/**
* Mapped events.
*
* @var array
*/
protected $events = array();
/**
* Method filters.
*
* @var array
*/
protected $filters = array();
public function __construct() {}
/**
* Dispatches an event.
*
* @param string $name Event name
* @param array $params Callback parameters
*/
public function run($name, $params) {
$output = '';
// Run pre-filters
if (!empty($this->filters[$name]['before'])) {
$this->filter($this->filters[$name]['before'], $params, $output);
}
// Run requested method
$output = $this->execute($this->get($name), $params);
// Run post-filters
if (!empty($this->filters[$name]['after'])) {
$this->filter($this->filters[$name]['after'], $params, $output);
}
return $output;
}
/**
* Assigns a callback to an event.
*
* @param string $name Event name
* @param callback $callback Callback function
*/
public function set($name, $callback) {
$this->events[$name] = $callback;
}
/**
* Gets an assigned callback.
*
* @param string $name Event name
* @param callback $callback Callback function
*/
public function get($name) {
return $this->events[$name];
}
/**
* Hooks a callback to an event.
*
* @param string $event Event name
* @param string $type Filter type
* @param callback $callback Callback function
*/
public function hook($name, $type, $callback) {
$this->filters[$name][$type][] = $callback;
}
/**
* Executes a chain of method filters.
*
* @param array $filters Chain of filters
* @param reference $data Method parameters or method output
*/
public function filter($filters, &$params, &$output) {
$args = array(&$params, &$output);
foreach ($filters as $callback) {
$continue = $this->execute($callback, $args);
if ($continue === false) break;
}
}
/**
* Executes a callback function.
*
* @param callback $callback Callback function
* @param array $params Function parameters
* @return mixed Function results
*/
public static function execute($callback, array &$params = array()) {
if (is_callable($callback)) {
return is_array($callback) ?
self::invokeMethod($callback, $params) :
self::callFunction($callback, $params);
}
}
/**
* Calls a function.
*
* @param string $func Name of function to call
* @param array $params Function parameters
*/
public static function callFunction($func, array &$params = array()) {
switch (count($params)) {
case 0:
return $func();
case 1:
return $func($params[0]);
case 2:
return $func($params[0], $params[1]);
case 3:
return $func($params[0], $params[1], $params[2]);
case 4:
return $func($params[0], $params[1], $params[2], $params[3]);
case 5:
return $func($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array($func, $params);
}
}
/**
* Invokes a method.
*
* @param mixed $func Class method
* @param array $params Class method parameters
*/
public static function invokeMethod($func, array &$params = array()) {
list($class, $method) = $func;
switch (count($params)) {
case 0:
return $class::$method();
case 1:
return $class::$method($params[0]);
case 2:
return $class::$method($params[0], $params[1]);
case 3:
return $class::$method($params[0], $params[1], $params[2]);
case 4:
return $class::$method($params[0], $params[1], $params[2], $params[3]);
case 5:
return $class::$method($params[0], $params[1], $params[2], $params[3], $params[4]);
default:
return call_user_func_array($func, $params);
}
}
}
?>

@ -0,0 +1,173 @@
<?php
/**
* Flight: An extensible micro-framework.
*
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\core;
/**
* The Loader class is resonsible for loading objects. It maintains
* a list of reusable class instances and can generate a new class
* instances with custom initialization parameters. It also performs
* class autoloading.
*/
class Loader {
/**
* Registered classes.
*
* @var array
*/
protected $classes = array();
/**
* Class instances.
*
* @var array
*/
protected $instances = array();
/**
* Autoload directories.
*
* @var array
*/
protected $dirs = array();
/**
* Constructor. Enables autoloading.
*/
public function __construct() {
spl_autoload_register(array(__CLASS__, 'autoload'));
}
/**
* Registers a class.
*
* @param string $name Registry name
* @param string $class Class name
* @param array $params Class initialization parameters
* @param callback $callback Function to call after object instantiation
*/
public function register($name, $class, array $params = array(), $callback = null) {
unset($this->instances[$class]);
$this->classes[$name] = array($class, $params, $callback);
}
/**
* Unregisters a class.
*
* @param string $name Registry name
*/
public function unregister($name) {
unset($this->classes[$name]);
}
/**
* Loads a registered class.
*
* @param string $name Method name
* @param bool $shared Shared instance
*/
public function load($name, $shared = true) {
if (isset($this->classes[$name])) {
list($class, $params, $callback) = $this->classes[$name];
$do_callback = ($callback && (!$shared || !isset($this->instances[$class])));
$obj = ($shared) ?
$this->getInstance($class, $params) :
$this->newInstance($class, $params);
if ($do_callback) {
$ref = array(&$obj);
call_user_func_array($callback, $ref);
}
return $obj;
}
$class = ucfirst($name);
return ($shared) ?
$this->getInstance($class) :
$this->newInstance($class);
}
/**
* Gets a single instance of a class.
*
* @param string $class Class name
* @param array $params Class initialization parameters
*/
public function getInstance($class, array $params = array()) {
if (!isset($this->instances[$class])) {
$this->instances[$class] = $this->newInstance($class, $params);
}
return $this->instances[$class];
}
/**
* Gets a new instance of a class.
*
* @param string $class Class name
* @param array $params Class initialization parameters
*/
public function newInstance($class, array $params = array()) {
switch (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]);
default:
$refClass = new ReflectionClass($class);
return $refClass->newInstanceArgs($params);
}
}
/**
* Adds a directory for autoloading classes.
*
* @param string $dir Directory path
*/
public function addDirectory($dir) {
$this->dirs[] = $dir;
}
/**
* Autoloads classes.
*
* @param string $class Class name
*/
public function autoload($class) {
$class_file = str_replace('\\', '/', str_replace('_', '/', $class)).'.php';
$dirs = array_merge($this->dirs, array(__DIR__, '.'));
foreach ($dirs as $dir) {
$file = $dir.'/'.$class_file;
if (file_exists($file)) {
require $file;
return;
}
}
// Allow other autoloaders to run before raising an error
$loader = array_pop(spl_autoload_functions());
if (is_array($loader) && $loader[0] == __CLASS__ && $loader[1] == __FUNCTION__) {
throw new Exception('Unable to load file: '.$class_file);
}
}
}
?>

@ -5,6 +5,33 @@
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\net;
/**
* The Request class represents an HTTP request. Data from
* all the super globals $_GET, $_POST, $_COOKIE, and $_FILES
* are stored and accessible via the Request object.
*
* The default request properties are:
* url - The URL being requested
* base - The parent subdirectory of the URL
* method - The request method (GET, POST, PUT, DELETE)
* referrer - The referrer URL
* ip - IP address of the client
* ajax - Whether the request is an AJAX request
* scheme - The server protocol (http, https)
* user_agent - Browser information
* body - Raw data from the request body
* type - The content type
* length - The content length
* query - Query string parameters
* data - Post parameters
* cookies - Cookie parameters
* files - Uploaded files
* params - Matched URL parameters from the router
* matched - Matched URL patter from the router
*/
class Request {
/**
* Constructor.
@ -26,14 +53,16 @@ class Request {
'body' => file_get_contents('php://input'),
'type' => $_SERVER['CONTENT_TYPE'],
'length' => $_SERVER['CONTENT_LENGTH'],
'query' => array(),
'data' => $_POST,
'cookies' => $_COOKIE,
'files' => $_FILES
'query' => (object)$_GET,
'data' => (object)$_POST,
'cookies' => (object)$_COOKIE,
'files' => (object)$_FILES,
'params' => array(),
'matched' => null
);
}
self::init($config);
$this->init($config);
}
/**
@ -43,7 +72,7 @@ class Request {
*/
public function init($properties) {
foreach ($properties as $name => $value) {
$this->{$name} = $value;
$this->$name = $value;
}
if ($this->base != '/' && strpos($this->url, $this->base) === 0) {
@ -54,14 +83,19 @@ class Request {
$this->url = '/';
}
else {
$this->query = self::parseQuery($this->url);
$query = self::parseQuery($this->url);
$this->query = (object)$query;
$_GET = $query;
}
}
/**
* Parse query parameters from a URL.
*
* @param string $url URL string
* @return array Query parameters
*/
public function parseQuery($url) {
public static function parseQuery($url) {
$params = array();
$args = parse_url($url);

@ -5,6 +5,14 @@
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\net;
/**
* The Response class represents an HTTP response. The object
* contains the response headers, HTTP status code, and response
* body.
*/
class Response {
protected $headers = array();
protected $status = 200;
@ -68,7 +76,7 @@ class Response {
}
}
else {
throw new Exception('Invalid status code.');
throw new \Exception('Invalid status code.');
}
return $this;

@ -5,6 +5,14 @@
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\net;
/**
* The Router class is responsible for routing an HTTP request to
* an assigned callback function. The Router tries to match the
* requested URL against a series of URL patterns.
*/
class Router {
/**
* Mapped routes.
@ -13,6 +21,22 @@ class Router {
*/
protected $routes = array();
/**
* Gets mapped routes.
*
* @return array Array of routes
*/
public function getRoutes() {
return $this->routes;
}
/**
* Resets the router.
*/
public function clear() {
$this->routes = array();
}
/**
* Maps a URL pattern to a callback function.
*
@ -36,10 +60,9 @@ class Router {
* Tries to match a requst to a route. Also parses named parameters in the url.
*
* @param string $pattern URL pattern
* @param string $url Request URL
* @param array $params Named URL parameters
* @param object $request Request object
*/
public function match($pattern, $url, array &$params = array()) {
public function match($pattern, $request) {
$ids = array();
// Build the regex for matching
@ -60,10 +83,13 @@ class Router {
)).'\/?(?:\?.*)?$/i';
// Attempt to match route and named parameters
if (preg_match($regex, $url, $matches)) {
if (preg_match($regex, $request->url, $matches)) {
if (!empty($ids)) {
$params = array_intersect_key($matches, $ids);
$request->params = array_intersect_key($matches, $ids);
}
$request->matched = $pattern;
return true;
}
@ -74,35 +100,21 @@ class Router {
* Routes the current request.
*
* @param object $request Request object
* @return callable Matched callback function
*/
public function route(&$request) {
$params = array();
public function route(Request $request) {
$request->matched = null;
$request->params = array();
$routes = ($this->routes[$request->method] ?: array()) + ($this->routes['*'] ?: array());
foreach ($routes as $pattern => $callback) {
if ($pattern === '*' || $request->url === $pattern || self::match($pattern, $request->url, $params)) {
$request->matched = $pattern;
return array($callback, $params);
if ($pattern === '*' || $request->url === $pattern || self::match($pattern, $request)) {
return $callback;
}
}
return false;
}
/**
* Gets mapped routes.
*
* @return array Array of routes
*/
public function getRoutes() {
return $this->routes;
}
/**
* Resets the router.
*/
public function clear() {
$this->routes = array();
}
}
?>

@ -5,18 +5,36 @@
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license http://www.opensource.org/licenses/mit-license.php
*/
namespace flight\template;
/**
* The View class represents output to be displayed. It provides
* methods for managing view data and inserts the data into
* view templates upon rendering.
*/
class View {
/**
* Locaton of view templates.
*
* @var string
*/
public $path;
public $template;
public $data = array();
/**
* View variables.
*
* @var array
*/
protected $data = array();
/**
* Constructor.
*
* @param string $path Path to templates directory
*/
public function __construct($path = null) {
$this->path = $path ?: (Flight::get('flight.views.path') ?: './views');
public function __construct($path = '.') {
$this->path = $path;
}
/**
@ -53,7 +71,7 @@ class View {
* @param string $key Key
*/
public function has($key) {
return !empty($this->data[$key]);
return !isset($this->data[$key]);
}
/**
@ -77,7 +95,7 @@ class View {
* @param array $data Template data
*/
public function render($file, $data = null) {
$this->template = (substr($file, -4) == '.php') ? $file : $file.'.php';
$template = $this->getTemplate($file);
if (is_array($data)) {
$this->data = array_merge($this->data, $data);
@ -85,13 +103,11 @@ class View {
extract($this->data);
$file = $this->path.'/'.$this->template;
if (!file_exists($file)) {
throw new Exception("Template file not found: $file.");
if (!file_exists($template)) {
throw new \Exception("Template file not found: $template.");
}
include $file;
include $template;
}
/**
@ -111,16 +127,6 @@ class View {
return $output;
}
/**
* Displays escaped output.
*
* @param string $str String to escape
* @return string Escaped string
*/
public function e($str) {
echo htmlentities($str);
}
/**
* Checks if a template file exists.
*
@ -128,27 +134,27 @@ class View {
* @return bool Template file exists
*/
public function exists($file) {
return file_exists($this->path.'/'.((substr($file, -4) == '.php') ? $file : $file.'.php'));
return file_exists($this->getTemplate($file));
}
/**
* Loads and executes view helper functions.
* Gets the full path to a template file.
*
* @param string $name Function name
* @param array $params Function parameters
* @param string $file Template file
* @return string Template file location
*/
public function __call($name, $params) {
return Flight::invokeMethod(array('Flight', $name), $params);
public function getTemplate($file) {
return $this->path.'/'.((substr($file, -4) == '.php') ? $file : $file.'.php');
}
/**
* Loads view helper classes.
* Displays escaped output.
*
* @param string $name Class name
* @return object Class instance
* @param string $str String to escape
* @return string Escaped string
*/
public function __get($name) {
return Flight::load($name);
public function e($str) {
echo htmlentities($str);
}
}
?>
Loading…
Cancel
Save