Commit version 24.12.13800
This commit is contained in:
4
libs/flight1/.gitignore
vendored
Normal file
4
libs/flight1/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
.idea
|
||||
vendor/
|
||||
composer.phar
|
||||
composer.lock
|
577
libs/flight1/Engine.php
Normal file
577
libs/flight1/Engine.php
Normal file
@ -0,0 +1,577 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight;
|
||||
|
||||
use flight\core\Loader;
|
||||
use flight\core\Dispatcher;
|
||||
|
||||
/**
|
||||
* The Engine class contains the core functionality of the framework.
|
||||
* It is responsible for loading an HTTP request, running the assigned services,
|
||||
* and generating an HTTP response.
|
||||
*
|
||||
* Core methods
|
||||
* @method void start() Starts engine
|
||||
* @method void stop() Stops framework and outputs current response
|
||||
* @method void halt(int $code = 200, string $message = '') Stops processing and returns a given response.
|
||||
*
|
||||
*
|
||||
* Routing
|
||||
* @method void route(string $pattern, callable $callback, bool $pass_route = false) Routes a URL to a callback function.
|
||||
* @method \flight\net\Router router() Gets router
|
||||
*
|
||||
* Views
|
||||
* @method void render(string $file, array $data = null, string $key = null) Renders template
|
||||
* @method \flight\template\View view() Gets current view
|
||||
*
|
||||
* Request-response
|
||||
* @method \flight\net\Request request() Gets current request
|
||||
* @method \flight\net\Response response() Gets current response
|
||||
* @method void error(\Exception $e) Sends an HTTP 500 response for any errors.
|
||||
* @method void notFound() Sends an HTTP 404 response when a URL is not found.
|
||||
* @method void redirect(string $url, int $code = 303) Redirects the current request to another URL.
|
||||
* @method void json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) Sends a JSON response.
|
||||
* @method void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) Sends a JSONP response.
|
||||
*
|
||||
* HTTP caching
|
||||
* @method void etag($id, string $type = 'strong') Handles ETag HTTP caching.
|
||||
* @method void lastModified(int $time) Handles last modified HTTP caching.
|
||||
*/
|
||||
class Engine {
|
||||
/**
|
||||
* Stored variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vars;
|
||||
|
||||
/**
|
||||
* Class loader.
|
||||
*
|
||||
* @var Loader
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Event dispatcher.
|
||||
*
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->vars = array();
|
||||
|
||||
$this->loader = new Loader();
|
||||
$this->dispatcher = new Dispatcher();
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles calls to class methods.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param array $params Method parameters
|
||||
* @return mixed Callback results
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __call($name, $params) {
|
||||
$callback = $this->dispatcher->get($name);
|
||||
|
||||
if (is_callable($callback)) {
|
||||
return $this->dispatcher->run($name, $params);
|
||||
}
|
||||
|
||||
if (!$this->loader->get($name)) {
|
||||
throw new \Exception("{$name} must be a mapped method.");
|
||||
}
|
||||
|
||||
$shared = (!empty($params)) ? (bool)$params[0] : true;
|
||||
|
||||
return $this->loader->load($name, $shared);
|
||||
}
|
||||
|
||||
/*** Core Methods ***/
|
||||
|
||||
/**
|
||||
* Initializes the framework.
|
||||
*/
|
||||
public function init() {
|
||||
static $initialized = false;
|
||||
$self = $this;
|
||||
|
||||
if ($initialized) {
|
||||
$this->vars = array();
|
||||
$this->loader->reset();
|
||||
$this->dispatcher->reset();
|
||||
}
|
||||
|
||||
// Register default components
|
||||
$this->loader->register('request', '\flight\net\Request');
|
||||
$this->loader->register('response', '\flight\net\Response');
|
||||
$this->loader->register('router', '\flight\net\Router');
|
||||
$this->loader->register('view', '\flight\template\View', array(), function($view) use ($self) {
|
||||
$view->path = $self->get('flight.views.path');
|
||||
$view->extension = $self->get('flight.views.extension');
|
||||
});
|
||||
|
||||
// Register framework methods
|
||||
$methods = array(
|
||||
'start','stop','route','halt','error','notFound',
|
||||
'render','redirect','etag','lastModified','json','jsonp'
|
||||
);
|
||||
foreach ($methods as $name) {
|
||||
$this->dispatcher->set($name, array($this, '_'.$name));
|
||||
}
|
||||
|
||||
// Default configuration settings
|
||||
$this->set('flight.base_url', null);
|
||||
$this->set('flight.case_sensitive', false);
|
||||
$this->set('flight.handle_errors', true);
|
||||
$this->set('flight.log_errors', false);
|
||||
$this->set('flight.views.path', './views');
|
||||
$this->set('flight.views.extension', '.php');
|
||||
$this->set('flight.content_length', true);
|
||||
|
||||
// Startup configuration
|
||||
$this->before('start', function() use ($self) {
|
||||
// Enable error handling
|
||||
if ($self->get('flight.handle_errors')) {
|
||||
set_error_handler(array($self, 'handleError'));
|
||||
set_exception_handler(array($self, 'handleException'));
|
||||
}
|
||||
|
||||
// Set case-sensitivity
|
||||
$self->router()->case_sensitive = $self->get('flight.case_sensitive');
|
||||
// Set Content-Length
|
||||
$self->response()->content_length = $self->get('flight.content_length');
|
||||
});
|
||||
|
||||
$initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error handler. Converts errors into exceptions.
|
||||
*
|
||||
* @param int $errno Error number
|
||||
* @param int $errstr Error string
|
||||
* @param int $errfile Error file name
|
||||
* @param int $errline Error file line number
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function handleError($errno, $errstr, $errfile, $errline) {
|
||||
if ($errno & error_reporting()) {
|
||||
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom exception handler. Logs exceptions.
|
||||
*
|
||||
* @param \Exception $e Thrown exception
|
||||
*/
|
||||
public function handleException($e) {
|
||||
if ($this->get('flight.log_errors')) {
|
||||
error_log($e->getMessage());
|
||||
}
|
||||
|
||||
$this->error($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a callback to a framework method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
* @throws \Exception If trying to map over a framework method
|
||||
*/
|
||||
public function map($name, $callback) {
|
||||
if (method_exists($this, $name)) {
|
||||
throw new \Exception('Cannot override an existing framework method.');
|
||||
}
|
||||
|
||||
$this->dispatcher->set($name, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class to a framework method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param string $class Class name
|
||||
* @param array $params Class initialization parameters
|
||||
* @param callback $callback Function to call after object instantiation
|
||||
* @throws \Exception If trying to map over a framework method
|
||||
*/
|
||||
public function register($name, $class, array $params = array(), $callback = null) {
|
||||
if (method_exists($this, $name)) {
|
||||
throw new \Exception('Cannot override an existing framework method.');
|
||||
}
|
||||
|
||||
$this->loader->register($name, $class, $params, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pre-filter to a method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function before($name, $callback) {
|
||||
$this->dispatcher->hook($name, 'before', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a post-filter to a method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function after($name, $callback) {
|
||||
$this->dispatcher->hook($name, 'after', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a variable.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key = null) {
|
||||
if ($key === null) return $this->vars;
|
||||
|
||||
return isset($this->vars[$key]) ? $this->vars[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable.
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @param string $value Value
|
||||
*/
|
||||
public function set($key, $value = null) {
|
||||
if (is_array($key) || is_object($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
$this->vars[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a variable has been set.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return bool Variable status
|
||||
*/
|
||||
public function has($key) {
|
||||
return isset($this->vars[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a variable. If no key is passed in, clear all variables.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function clear($key = null) {
|
||||
if (is_null($key)) {
|
||||
$this->vars = array();
|
||||
}
|
||||
else {
|
||||
unset($this->vars[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path for class autoloading.
|
||||
*
|
||||
* @param string $dir Directory path
|
||||
*/
|
||||
public function path($dir) {
|
||||
$this->loader->addDirectory($dir);
|
||||
}
|
||||
|
||||
/*** Extensible Methods ***/
|
||||
|
||||
/**
|
||||
* Starts the framework.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _start() {
|
||||
$dispatched = false;
|
||||
$self = $this;
|
||||
$request = $this->request();
|
||||
$response = $this->response();
|
||||
$router = $this->router();
|
||||
|
||||
// Allow filters to run
|
||||
$this->after('start', function() use ($self) {
|
||||
$self->stop();
|
||||
});
|
||||
|
||||
// Flush any existing output
|
||||
if (ob_get_length() > 0) {
|
||||
$response->write(ob_get_clean());
|
||||
}
|
||||
|
||||
// Enable output buffering
|
||||
ob_start();
|
||||
|
||||
// Route the request
|
||||
while ($route = $router->route($request)) {
|
||||
$params = array_values($route->params);
|
||||
|
||||
// Add route info to the parameter list
|
||||
if ($route->pass) {
|
||||
$params[] = $route;
|
||||
}
|
||||
|
||||
// Call route handler
|
||||
$continue = $this->dispatcher->execute(
|
||||
$route->callback,
|
||||
$params
|
||||
);
|
||||
|
||||
$dispatched = true;
|
||||
|
||||
if (!$continue) break;
|
||||
|
||||
$router->next();
|
||||
|
||||
$dispatched = false;
|
||||
}
|
||||
|
||||
if (!$dispatched) {
|
||||
$this->notFound();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the framework and outputs the current response.
|
||||
*
|
||||
* @param int $code HTTP status code
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _stop($code = null) {
|
||||
$response = $this->response();
|
||||
|
||||
if (!$response->sent()) {
|
||||
if ($code !== null) {
|
||||
$response->status($code);
|
||||
}
|
||||
|
||||
$response->write(ob_get_clean());
|
||||
|
||||
$response->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes a URL to a callback function.
|
||||
*
|
||||
* @param string $pattern URL pattern to match
|
||||
* @param callback $callback Callback function
|
||||
* @param boolean $pass_route Pass the matching route object to the callback
|
||||
*/
|
||||
public function _route($pattern, $callback, $pass_route = false) {
|
||||
$this->router()->map($pattern, $callback, $pass_route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops processing and returns a given response.
|
||||
*
|
||||
* @param int $code HTTP status code
|
||||
* @param string $message Response message
|
||||
*/
|
||||
public function _halt($code = 200, $message = '') {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status($code)
|
||||
->write($message)
|
||||
->send();
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP 500 response for any errors.
|
||||
*
|
||||
* @param \Exception|\Throwable $e Thrown exception
|
||||
*/
|
||||
public function _error($e) {
|
||||
$msg = sprintf('<h1>500 Internal Server Error</h1>'.
|
||||
'<h3>%s (%s)</h3>'.
|
||||
'<pre>%s</pre>',
|
||||
$e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e->getTraceAsString()
|
||||
);
|
||||
|
||||
try {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status(500)
|
||||
->write($msg)
|
||||
->send();
|
||||
}
|
||||
catch (\Throwable $t) { // PHP 7.0+
|
||||
exit($msg);
|
||||
} catch(\Exception $e) { // PHP < 7
|
||||
exit($msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP 404 response when a URL is not found.
|
||||
*/
|
||||
public function _notFound() {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status(404)
|
||||
->write(
|
||||
'<h1>404 Not Found</h1>'.
|
||||
'<h3>The page you have requested could not be found.</h3>'.
|
||||
str_repeat(' ', 512)
|
||||
)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the current request to another URL.
|
||||
*
|
||||
* @param string $url URL
|
||||
* @param int $code HTTP status code
|
||||
*/
|
||||
public function _redirect($url, $code = 303) {
|
||||
$base = $this->get('flight.base_url');
|
||||
|
||||
if ($base === null) {
|
||||
$base = $this->request()->base;
|
||||
}
|
||||
|
||||
// Append base url to redirect url
|
||||
if ($base != '/' && strpos($url, '://') === false) {
|
||||
$url = $base . preg_replace('#/+#', '/', '/' . $url);
|
||||
}
|
||||
|
||||
$this->response()
|
||||
->clear()
|
||||
->status($code)
|
||||
->header('Location', $url)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @param string $key View variable name
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _render($file, $data = null, $key = null) {
|
||||
if ($key !== null) {
|
||||
$this->view()->set($key, $this->view()->fetch($file, $data));
|
||||
}
|
||||
else {
|
||||
$this->view()->render($file, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a JSON response.
|
||||
*
|
||||
* @param mixed $data JSON data
|
||||
* @param int $code HTTP status code
|
||||
* @param bool $encode Whether to perform JSON encoding
|
||||
* @param string $charset Charset
|
||||
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _json(
|
||||
$data,
|
||||
$code = 200,
|
||||
$encode = true,
|
||||
$charset = 'utf-8',
|
||||
$option = 0
|
||||
) {
|
||||
$json = ($encode) ? json_encode($data, $option) : $data;
|
||||
|
||||
$this->response()
|
||||
->status($code)
|
||||
->header('Content-Type', 'application/json; charset='.$charset)
|
||||
->write($json)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a JSONP response.
|
||||
*
|
||||
* @param mixed $data JSON data
|
||||
* @param string $param Query parameter that specifies the callback name.
|
||||
* @param int $code HTTP status code
|
||||
* @param bool $encode Whether to perform JSON encoding
|
||||
* @param string $charset Charset
|
||||
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _jsonp(
|
||||
$data,
|
||||
$param = 'jsonp',
|
||||
$code = 200,
|
||||
$encode = true,
|
||||
$charset = 'utf-8',
|
||||
$option = 0
|
||||
) {
|
||||
$json = ($encode) ? json_encode($data, $option) : $data;
|
||||
|
||||
$callback = $this->request()->query[$param];
|
||||
|
||||
$this->response()
|
||||
->status($code)
|
||||
->header('Content-Type', 'application/javascript; charset='.$charset)
|
||||
->write($callback.'('.$json.');')
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ETag HTTP caching.
|
||||
*
|
||||
* @param string $id ETag identifier
|
||||
* @param string $type ETag type
|
||||
*/
|
||||
public function _etag($id, $type = 'strong') {
|
||||
$id = (($type === 'weak') ? 'W/' : '').$id;
|
||||
|
||||
$this->response()->header('ETag', $id);
|
||||
|
||||
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
|
||||
$_SERVER['HTTP_IF_NONE_MATCH'] === $id) {
|
||||
$this->halt(304);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles last modified HTTP caching.
|
||||
*
|
||||
* @param int $time Unix timestamp
|
||||
*/
|
||||
public function _lastModified($time) {
|
||||
$this->response()->header('Last-Modified', gmdate('D, d M Y H:i:s \G\M\T', $time));
|
||||
|
||||
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
||||
strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $time) {
|
||||
$this->halt(304);
|
||||
}
|
||||
}
|
||||
}
|
96
libs/flight1/Flight.php
Normal file
96
libs/flight1/Flight.php
Normal file
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Flight class is a static representation of the framework.
|
||||
*
|
||||
* Core.
|
||||
* @method static void start() Starts the framework.
|
||||
* @method static void path($path) Adds a path for autoloading classes.
|
||||
* @method static void stop() Stops the framework and sends a response.
|
||||
* @method static void halt($code = 200, $message = '') Stop the framework with an optional status code and message.
|
||||
*
|
||||
* Routing.
|
||||
* @method static void route($pattern, $callback) Maps a URL pattern to a callback.
|
||||
* @method static \flight\net\Router router() Returns Router instance.
|
||||
*
|
||||
* Extending & Overriding.
|
||||
* @method static void map($name, $callback) Creates a custom framework method.
|
||||
* @method static void register($name, $class, array $params = array(), $callback = null) Registers a class to a framework method.
|
||||
*
|
||||
* Filtering.
|
||||
* @method static void before($name, $callback) Adds a filter before a framework method.
|
||||
* @method static void after($name, $callback) Adds a filter after a framework method.
|
||||
*
|
||||
* Variables.
|
||||
* @method static void set($key, $value) Sets a variable.
|
||||
* @method static mixed get($key) Gets a variable.
|
||||
* @method static bool has($key) Checks if a variable is set.
|
||||
* @method static void clear($key = null) Clears a variable.
|
||||
*
|
||||
* Views.
|
||||
* @method static void render($file, array $data = null, $key = null) Renders a template file.
|
||||
* @method static \flight\template\View view() Returns View instance.
|
||||
*
|
||||
* Request & Response.
|
||||
* @method static \flight\net\Request request() Returns Request instance.
|
||||
* @method static \flight\net\Response response() Returns Response instance.
|
||||
* @method static void redirect($url, $code = 303) Redirects to another URL.
|
||||
* @method static void json($data, $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSON response.
|
||||
* @method static void jsonp($data, $param = 'jsonp', $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSONP response.
|
||||
* @method static void error($exception) Sends an HTTP 500 response.
|
||||
* @method static void notFound() Sends an HTTP 404 response.
|
||||
*
|
||||
* HTTP Caching.
|
||||
* @method static void etag($id, $type = 'strong') Performs ETag HTTP caching.
|
||||
* @method static void lastModified($time) Performs last modified HTTP caching.
|
||||
*/
|
||||
class Flight {
|
||||
/**
|
||||
* Framework engine.
|
||||
*
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private static $engine;
|
||||
|
||||
// Don't allow object instantiation
|
||||
private function __construct() {}
|
||||
private function __destruct() {}
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Handles calls to static methods.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param array $params Method parameters
|
||||
* @return mixed Callback results
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function __callStatic($name, $params) {
|
||||
$app = Flight::app();
|
||||
|
||||
return \flight\core\Dispatcher::invokeMethod(array($app, $name), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \flight\Engine Application instance
|
||||
*/
|
||||
public static function app() {
|
||||
static $initialized = false;
|
||||
|
||||
if (!$initialized) {
|
||||
require_once __DIR__.'/autoload.php';
|
||||
|
||||
self::$engine = new \flight\Engine();
|
||||
|
||||
$initialized = true;
|
||||
}
|
||||
|
||||
return self::$engine;
|
||||
}
|
||||
}
|
21
libs/flight1/LICENSE
Normal file
21
libs/flight1/LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011 Mike Cao <mike@mikecao.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
918
libs/flight1/README.md
Normal file
918
libs/flight1/README.md
Normal file
@ -0,0 +1,918 @@
|
||||
# What is Flight?
|
||||
|
||||
Flight is a fast, simple, extensible framework for PHP. Flight enables you to
|
||||
quickly and easily build RESTful web applications.
|
||||
|
||||
```php
|
||||
require 'flight/Flight.php';
|
||||
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
|
||||
Flight::start();
|
||||
```
|
||||
|
||||
[Learn more](http://flightphp.com/learn)
|
||||
|
||||
# Requirements
|
||||
|
||||
Flight requires `PHP 5.3` or greater.
|
||||
|
||||
# License
|
||||
|
||||
Flight is released under the [MIT](http://flightphp.com/license) license.
|
||||
|
||||
# Installation
|
||||
|
||||
1\. Download the files.
|
||||
|
||||
If you're using [Composer](https://getcomposer.org/), you can run the following command:
|
||||
|
||||
```
|
||||
composer require mikecao/flight
|
||||
```
|
||||
|
||||
OR you can [download](https://github.com/mikecao/flight/archive/master.zip) them directly
|
||||
and extract them to your web directory.
|
||||
|
||||
2\. Configure your webserver.
|
||||
|
||||
For *Apache*, edit your `.htaccess` file with the following:
|
||||
|
||||
```
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
```
|
||||
|
||||
**Note**: If you need to use flight in a subdirectory add the line `RewriteBase /subdir/` just after `RewriteEngine On`.
|
||||
|
||||
For *Nginx*, add the following to your server declaration:
|
||||
|
||||
```
|
||||
server {
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php;
|
||||
}
|
||||
}
|
||||
```
|
||||
3\. Create your `index.php` file.
|
||||
|
||||
First include the framework.
|
||||
|
||||
```php
|
||||
require 'flight/Flight.php';
|
||||
```
|
||||
|
||||
If you're using Composer, run the autoloader instead.
|
||||
|
||||
```php
|
||||
require 'vendor/autoload.php';
|
||||
```
|
||||
|
||||
Then define a route and assign a function to handle the request.
|
||||
|
||||
```php
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
```
|
||||
|
||||
Finally, start the framework.
|
||||
|
||||
```php
|
||||
Flight::start();
|
||||
```
|
||||
|
||||
# Routing
|
||||
|
||||
Routing in Flight is done by matching a URL pattern with a callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
```
|
||||
|
||||
The callback can be any object that is callable. So you can use a regular function:
|
||||
|
||||
```php
|
||||
function hello(){
|
||||
echo 'hello world!';
|
||||
}
|
||||
|
||||
Flight::route('/', 'hello');
|
||||
```
|
||||
|
||||
Or a class method:
|
||||
|
||||
```php
|
||||
class Greeting {
|
||||
public static function hello() {
|
||||
echo 'hello world!';
|
||||
}
|
||||
}
|
||||
|
||||
Flight::route('/', array('Greeting', 'hello'));
|
||||
```
|
||||
|
||||
Or an object method:
|
||||
|
||||
```php
|
||||
class Greeting
|
||||
{
|
||||
public function __construct() {
|
||||
$this->name = 'John Doe';
|
||||
}
|
||||
|
||||
public function hello() {
|
||||
echo "Hello, {$this->name}!";
|
||||
}
|
||||
}
|
||||
|
||||
$greeting = new Greeting();
|
||||
|
||||
Flight::route('/', array($greeting, 'hello'));
|
||||
```
|
||||
|
||||
Routes are matched in the order they are defined. The first route to match a
|
||||
request will be invoked.
|
||||
|
||||
## Method Routing
|
||||
|
||||
By default, route patterns are matched against all request methods. You can respond
|
||||
to specific methods by placing an identifier before the URL.
|
||||
|
||||
```php
|
||||
Flight::route('GET /', function(){
|
||||
echo 'I received a GET request.';
|
||||
});
|
||||
|
||||
Flight::route('POST /', function(){
|
||||
echo 'I received a POST request.';
|
||||
});
|
||||
```
|
||||
|
||||
You can also map multiple methods to a single callback by using a `|` delimiter:
|
||||
|
||||
```php
|
||||
Flight::route('GET|POST /', function(){
|
||||
echo 'I received either a GET or a POST request.';
|
||||
});
|
||||
```
|
||||
|
||||
## Regular Expressions
|
||||
|
||||
You can use regular expressions in your routes:
|
||||
|
||||
```php
|
||||
Flight::route('/user/[0-9]+', function(){
|
||||
// This will match /user/1234
|
||||
});
|
||||
```
|
||||
|
||||
## Named Parameters
|
||||
|
||||
You can specify named parameters in your routes which will be passed along to
|
||||
your callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/@name/@id', function($name, $id){
|
||||
echo "hello, $name ($id)!";
|
||||
});
|
||||
```
|
||||
|
||||
You can also include regular expressions with your named parameters by using
|
||||
the `:` delimiter:
|
||||
|
||||
```php
|
||||
Flight::route('/@name/@id:[0-9]{3}', function($name, $id){
|
||||
// This will match /bob/123
|
||||
// But will not match /bob/12345
|
||||
});
|
||||
```
|
||||
|
||||
## Optional Parameters
|
||||
|
||||
You can specify named parameters that are optional for matching by wrapping
|
||||
segments in parentheses.
|
||||
|
||||
```php
|
||||
Flight::route('/blog(/@year(/@month(/@day)))', function($year, $month, $day){
|
||||
// This will match the following URLS:
|
||||
// /blog/2012/12/10
|
||||
// /blog/2012/12
|
||||
// /blog/2012
|
||||
// /blog
|
||||
});
|
||||
```
|
||||
|
||||
Any optional parameters that are not matched will be passed in as NULL.
|
||||
|
||||
## Wildcards
|
||||
|
||||
Matching is only done on individual URL segments. If you want to match multiple
|
||||
segments you can use the `*` wildcard.
|
||||
|
||||
```php
|
||||
Flight::route('/blog/*', function(){
|
||||
// This will match /blog/2000/02/01
|
||||
});
|
||||
```
|
||||
|
||||
To route all requests to a single callback, you can do:
|
||||
|
||||
```php
|
||||
Flight::route('*', function(){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
## Passing
|
||||
|
||||
You can pass execution on to the next matching route by returning `true` from
|
||||
your callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/user/@name', function($name){
|
||||
// Check some condition
|
||||
if ($name != "Bob") {
|
||||
// Continue to next route
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Flight::route('/user/*', function(){
|
||||
// This will get called
|
||||
});
|
||||
```
|
||||
|
||||
## Route Info
|
||||
|
||||
If you want to inspect the matching route information, you can request for the route
|
||||
object to be passed to your callback by passing in `true` as the third parameter in
|
||||
the route method. The route object will always be the last parameter passed to your
|
||||
callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/', function($route){
|
||||
// Array of HTTP methods matched against
|
||||
$route->methods;
|
||||
|
||||
// Array of named parameters
|
||||
$route->params;
|
||||
|
||||
// Matching regular expression
|
||||
$route->regex;
|
||||
|
||||
// Contains the contents of any '*' used in the URL pattern
|
||||
$route->splat;
|
||||
}, true);
|
||||
```
|
||||
|
||||
# Extending
|
||||
|
||||
Flight is designed to be an extensible framework. The framework comes with a set
|
||||
of default methods and components, but it allows you to map your own methods,
|
||||
register your own classes, or even override existing classes and methods.
|
||||
|
||||
## Mapping Methods
|
||||
|
||||
To map your own custom method, you use the `map` function:
|
||||
|
||||
```php
|
||||
// Map your method
|
||||
Flight::map('hello', function($name){
|
||||
echo "hello $name!";
|
||||
});
|
||||
|
||||
// Call your custom method
|
||||
Flight::hello('Bob');
|
||||
```
|
||||
|
||||
## Registering Classes
|
||||
|
||||
To register your own class, you use the `register` function:
|
||||
|
||||
```php
|
||||
// Register your class
|
||||
Flight::register('user', 'User');
|
||||
|
||||
// Get an instance of your class
|
||||
$user = Flight::user();
|
||||
```
|
||||
|
||||
The register method also allows you to pass along parameters to your class
|
||||
constructor. So when you load your custom class, it will come pre-initialized.
|
||||
You can define the constructor parameters by passing in an additional array.
|
||||
Here's an example of loading a database connection:
|
||||
|
||||
```php
|
||||
// Register class with constructor parameters
|
||||
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'));
|
||||
|
||||
// Get an instance of your class
|
||||
// This will create an object with the defined parameters
|
||||
//
|
||||
// new PDO('mysql:host=localhost;dbname=test','user','pass');
|
||||
//
|
||||
$db = Flight::db();
|
||||
```
|
||||
|
||||
If you pass in an additional callback parameter, it will be executed immediately
|
||||
after class construction. This allows you to perform any set up procedures for your
|
||||
new object. The callback function takes one parameter, an instance of the new object.
|
||||
|
||||
```php
|
||||
// The callback will be passed the object that was constructed
|
||||
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'), function($db){
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
});
|
||||
```
|
||||
|
||||
By default, every time you load your class you will get a shared instance.
|
||||
To get a new instance of a class, simply pass in `false` as a parameter:
|
||||
|
||||
```php
|
||||
// Shared instance of the class
|
||||
$shared = Flight::db();
|
||||
|
||||
// New instance of the class
|
||||
$new = Flight::db(false);
|
||||
```
|
||||
|
||||
Keep in mind that mapped methods have precedence over registered classes. If you
|
||||
declare both using the same name, only the mapped method will be invoked.
|
||||
|
||||
# Overriding
|
||||
|
||||
Flight allows you to override its default functionality to suit your own needs,
|
||||
without having to modify any code.
|
||||
|
||||
For example, when Flight cannot match a URL to a route, it invokes the `notFound`
|
||||
method which sends a generic `HTTP 404` response. You can override this behavior
|
||||
by using the `map` method:
|
||||
|
||||
```php
|
||||
Flight::map('notFound', function(){
|
||||
// Display custom 404 page
|
||||
include 'errors/404.html';
|
||||
});
|
||||
```
|
||||
|
||||
Flight also allows you to replace core components of the framework.
|
||||
For example you can replace the default Router class with your own custom class:
|
||||
|
||||
```php
|
||||
// Register your custom class
|
||||
Flight::register('router', 'MyRouter');
|
||||
|
||||
// When Flight loads the Router instance, it will load your class
|
||||
$myrouter = Flight::router();
|
||||
```
|
||||
|
||||
Framework methods like `map` and `register` however cannot be overridden. You will
|
||||
get an error if you try to do so.
|
||||
|
||||
# Filtering
|
||||
|
||||
Flight allows you to filter methods before and after they are called. There are no
|
||||
predefined hooks you need to memorize. You can filter any of the default framework
|
||||
methods as well as any custom methods that you've mapped.
|
||||
|
||||
A filter function looks like this:
|
||||
|
||||
```php
|
||||
function(&$params, &$output) {
|
||||
// Filter code
|
||||
}
|
||||
```
|
||||
|
||||
Using the passed in variables you can manipulate the input parameters and/or the output.
|
||||
|
||||
You can have a filter run before a method by doing:
|
||||
|
||||
```php
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
You can have a filter run after a method by doing:
|
||||
|
||||
```php
|
||||
Flight::after('start', function(&$params, &$output){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
You can add as many filters as you want to any method. They will be called in the
|
||||
order that they are declared.
|
||||
|
||||
Here's an example of the filtering process:
|
||||
|
||||
```php
|
||||
// Map a custom method
|
||||
Flight::map('hello', function($name){
|
||||
return "Hello, $name!";
|
||||
});
|
||||
|
||||
// Add a before filter
|
||||
Flight::before('hello', function(&$params, &$output){
|
||||
// Manipulate the parameter
|
||||
$params[0] = 'Fred';
|
||||
});
|
||||
|
||||
// Add an after filter
|
||||
Flight::after('hello', function(&$params, &$output){
|
||||
// Manipulate the output
|
||||
$output .= " Have a nice day!";
|
||||
});
|
||||
|
||||
// Invoke the custom method
|
||||
echo Flight::hello('Bob');
|
||||
```
|
||||
|
||||
This should display:
|
||||
|
||||
Hello Fred! Have a nice day!
|
||||
|
||||
If you have defined multiple filters, you can break the chain by returning `false`
|
||||
in any of your filter functions:
|
||||
|
||||
```php
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'one';
|
||||
});
|
||||
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'two';
|
||||
|
||||
// This will end the chain
|
||||
return false;
|
||||
});
|
||||
|
||||
// This will not get called
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'three';
|
||||
});
|
||||
```
|
||||
|
||||
Note, core methods such as `map` and `register` cannot be filtered because they
|
||||
are called directly and not invoked dynamically.
|
||||
|
||||
# Variables
|
||||
|
||||
Flight allows you to save variables so that they can be used anywhere in your application.
|
||||
|
||||
```php
|
||||
// Save your variable
|
||||
Flight::set('id', 123);
|
||||
|
||||
// Elsewhere in your application
|
||||
$id = Flight::get('id');
|
||||
```
|
||||
To see if a variable has been set you can do:
|
||||
|
||||
```php
|
||||
if (Flight::has('id')) {
|
||||
// Do something
|
||||
}
|
||||
```
|
||||
|
||||
You can clear a variable by doing:
|
||||
|
||||
```php
|
||||
// Clears the id variable
|
||||
Flight::clear('id');
|
||||
|
||||
// Clears all variables
|
||||
Flight::clear();
|
||||
```
|
||||
|
||||
Flight also uses variables for configuration purposes.
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
# Views
|
||||
|
||||
Flight provides some basic templating functionality by default. To display a view
|
||||
template call the `render` method with the name of the template file and optional
|
||||
template data:
|
||||
|
||||
```php
|
||||
Flight::render('hello.php', array('name' => 'Bob'));
|
||||
```
|
||||
|
||||
The template data you pass in is automatically injected into the template and can
|
||||
be reference like a local variable. Template files are simply PHP files. If the
|
||||
content of the `hello.php` template file is:
|
||||
|
||||
```php
|
||||
Hello, '<?php echo $name; ?>'!
|
||||
```
|
||||
|
||||
The output would be:
|
||||
|
||||
Hello, Bob!
|
||||
|
||||
You can also manually set view variables by using the set method:
|
||||
|
||||
```php
|
||||
Flight::view()->set('name', 'Bob');
|
||||
```
|
||||
|
||||
The variable `name` is now available across all your views. So you can simply do:
|
||||
|
||||
```php
|
||||
Flight::render('hello');
|
||||
```
|
||||
|
||||
Note that when specifying the name of the template in the render method, you can
|
||||
leave out the `.php` extension.
|
||||
|
||||
By default Flight will look for a `views` directory for template files. You can
|
||||
set an alternate path for your templates by setting the following config:
|
||||
|
||||
```php
|
||||
Flight::set('flight.views.path', '/path/to/views');
|
||||
```
|
||||
|
||||
## Layouts
|
||||
|
||||
It is common for websites to have a single layout template file with interchanging
|
||||
content. To render content to be used in a layout, you can pass in an optional
|
||||
parameter to the `render` method.
|
||||
|
||||
```php
|
||||
Flight::render('header', array('heading' => 'Hello'), 'header_content');
|
||||
Flight::render('body', array('body' => 'World'), 'body_content');
|
||||
```
|
||||
|
||||
Your view will then have saved variables called `header_content` and `body_content`.
|
||||
You can then render your layout by doing:
|
||||
|
||||
```php
|
||||
Flight::render('layout', array('title' => 'Home Page'));
|
||||
```
|
||||
|
||||
If the template files looks like this:
|
||||
|
||||
`header.php`:
|
||||
|
||||
```php
|
||||
<h1><?php echo $heading; ?></h1>
|
||||
```
|
||||
|
||||
`body.php`:
|
||||
|
||||
```php
|
||||
<div><?php echo $body; ?></div>
|
||||
```
|
||||
|
||||
`layout.php`:
|
||||
|
||||
```php
|
||||
<html>
|
||||
<head>
|
||||
<title><?php echo $title; ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<?php echo $header_content; ?>
|
||||
<?php echo $body_content; ?>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The output would be:
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Home Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello</h1>
|
||||
<div>World</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Custom Views
|
||||
|
||||
Flight allows you to swap out the default view engine simply by registering your
|
||||
own view class. Here's how you would use the [Smarty](http://www.smarty.net/)
|
||||
template engine for your views:
|
||||
|
||||
```php
|
||||
// Load Smarty library
|
||||
require './Smarty/libs/Smarty.class.php';
|
||||
|
||||
// Register Smarty as the view class
|
||||
// Also pass a callback function to configure Smarty on load
|
||||
Flight::register('view', 'Smarty', array(), function($smarty){
|
||||
$smarty->template_dir = './templates/';
|
||||
$smarty->compile_dir = './templates_c/';
|
||||
$smarty->config_dir = './config/';
|
||||
$smarty->cache_dir = './cache/';
|
||||
});
|
||||
|
||||
// Assign template data
|
||||
Flight::view()->assign('name', 'Bob');
|
||||
|
||||
// Display the template
|
||||
Flight::view()->display('hello.tpl');
|
||||
```
|
||||
|
||||
For completeness, you should also override Flight's default render method:
|
||||
|
||||
```php
|
||||
Flight::map('render', function($template, $data){
|
||||
Flight::view()->assign($data);
|
||||
Flight::view()->display($template);
|
||||
});
|
||||
```
|
||||
# Error Handling
|
||||
|
||||
## Errors and Exceptions
|
||||
|
||||
All errors and exceptions are caught by Flight and passed to the `error` method.
|
||||
The default behavior is to send a generic `HTTP 500 Internal Server Error`
|
||||
response with some error information.
|
||||
|
||||
You can override this behavior for your own needs:
|
||||
|
||||
```php
|
||||
Flight::map('error', function(Exception $ex){
|
||||
// Handle error
|
||||
echo $ex->getTraceAsString();
|
||||
});
|
||||
```
|
||||
|
||||
By default errors are not logged to the web server. You can enable this by
|
||||
changing the config:
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
## Not Found
|
||||
|
||||
When a URL can't be found, Flight calls the `notFound` method. The default
|
||||
behavior is to send an `HTTP 404 Not Found` response with a simple message.
|
||||
|
||||
You can override this behavior for your own needs:
|
||||
|
||||
```php
|
||||
Flight::map('notFound', function(){
|
||||
// Handle not found
|
||||
});
|
||||
```
|
||||
|
||||
# Redirects
|
||||
|
||||
You can redirect the current request by using the `redirect` method and passing
|
||||
in a new URL:
|
||||
|
||||
```php
|
||||
Flight::redirect('/new/location');
|
||||
```
|
||||
|
||||
By default Flight sends a HTTP 303 status code. You can optionally set a
|
||||
custom code:
|
||||
|
||||
```php
|
||||
Flight::redirect('/new/location', 401);
|
||||
```
|
||||
|
||||
# Requests
|
||||
|
||||
Flight encapsulates the HTTP request into a single object, which can be
|
||||
accessed by doing:
|
||||
|
||||
```php
|
||||
$request = Flight::request();
|
||||
```
|
||||
|
||||
The request object provides the following properties:
|
||||
|
||||
```
|
||||
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
|
||||
type - The content type
|
||||
length - The content length
|
||||
query - Query string parameters
|
||||
data - Post data or JSON data
|
||||
cookies - Cookie data
|
||||
files - Uploaded files
|
||||
secure - Whether the connection is secure
|
||||
accept - HTTP accept parameters
|
||||
proxy_ip - Proxy IP address of the client
|
||||
host - The request host name
|
||||
```
|
||||
|
||||
You can access the `query`, `data`, `cookies`, and `files` properties
|
||||
as arrays or objects.
|
||||
|
||||
So, to get a query string parameter, you can do:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->query['id'];
|
||||
```
|
||||
|
||||
Or you can do:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->query->id;
|
||||
```
|
||||
|
||||
## RAW Request Body
|
||||
|
||||
To get the raw HTTP request body, for example when dealing with PUT requests, you can do:
|
||||
|
||||
```php
|
||||
$body = Flight::request()->getBody();
|
||||
```
|
||||
|
||||
## JSON Input
|
||||
|
||||
If you send a request with the type `application/json` and the data `{"id": 123}` it will be available
|
||||
from the `data` property:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->data->id;
|
||||
```
|
||||
|
||||
# HTTP Caching
|
||||
|
||||
Flight provides built-in support for HTTP level caching. If the caching condition
|
||||
is met, Flight will return an HTTP `304 Not Modified` response. The next time the
|
||||
client requests the same resource, they will be prompted to use their locally
|
||||
cached version.
|
||||
|
||||
## Last-Modified
|
||||
|
||||
You can use the `lastModified` method and pass in a UNIX timestamp to set the date
|
||||
and time a page was last modified. The client will continue to use their cache until
|
||||
the last modified value is changed.
|
||||
|
||||
```php
|
||||
Flight::route('/news', function(){
|
||||
Flight::lastModified(1234567890);
|
||||
echo 'This content will be cached.';
|
||||
});
|
||||
```
|
||||
|
||||
## ETag
|
||||
|
||||
`ETag` caching is similar to `Last-Modified`, except you can specify any id you
|
||||
want for the resource:
|
||||
|
||||
```php
|
||||
Flight::route('/news', function(){
|
||||
Flight::etag('my-unique-id');
|
||||
echo 'This content will be cached.';
|
||||
});
|
||||
```
|
||||
|
||||
Keep in mind that calling either `lastModified` or `etag` will both set and check the
|
||||
cache value. If the cache value is the same between requests, Flight will immediately
|
||||
send an `HTTP 304` response and stop processing.
|
||||
|
||||
# Stopping
|
||||
|
||||
You can stop the framework at any point by calling the `halt` method:
|
||||
|
||||
```php
|
||||
Flight::halt();
|
||||
```
|
||||
|
||||
You can also specify an optional `HTTP` status code and message:
|
||||
|
||||
```php
|
||||
Flight::halt(200, 'Be right back...');
|
||||
```
|
||||
|
||||
Calling `halt` will discard any response content up to that point. If you want to stop
|
||||
the framework and output the current response, use the `stop` method:
|
||||
|
||||
```php
|
||||
Flight::stop();
|
||||
```
|
||||
|
||||
# JSON
|
||||
|
||||
Flight provides support for sending JSON and JSONP responses. To send a JSON response you
|
||||
pass some data to be JSON encoded:
|
||||
|
||||
```php
|
||||
Flight::json(array('id' => 123));
|
||||
```
|
||||
|
||||
For JSONP requests you, can optionally pass in the query parameter name you are
|
||||
using to define your callback function:
|
||||
|
||||
```php
|
||||
Flight::jsonp(array('id' => 123), 'q');
|
||||
```
|
||||
|
||||
So, when making a GET request using `?q=my_func`, you should receive the output:
|
||||
|
||||
```
|
||||
my_func({"id":123});
|
||||
```
|
||||
|
||||
If you don't pass in a query parameter name it will default to `jsonp`.
|
||||
|
||||
|
||||
# Configuration
|
||||
|
||||
You can customize certain behaviors of Flight by setting configuration values
|
||||
through the `set` method.
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
The following is a list of all the available configuration settings:
|
||||
|
||||
flight.base_url - Override the base url of the request. (default: null)
|
||||
flight.case_sensitive - Case sensitive matching for URLs. (default: false)
|
||||
flight.handle_errors - Allow Flight to handle all errors internally. (default: true)
|
||||
flight.log_errors - Log errors to the web server's error log file. (default: false)
|
||||
flight.views.path - Directory containing view template files. (default: ./views)
|
||||
flight.views.extension - View template file extension. (default: .php)
|
||||
|
||||
# Framework Methods
|
||||
|
||||
Flight is designed to be easy to use and understand. The following is the complete
|
||||
set of methods for the framework. It consists of core methods, which are regular
|
||||
static methods, and extensible methods, which are mapped methods that can be filtered
|
||||
or overridden.
|
||||
|
||||
## Core Methods
|
||||
|
||||
```php
|
||||
Flight::map($name, $callback) // Creates a custom framework method.
|
||||
Flight::register($name, $class, [$params], [$callback]) // Registers a class to a framework method.
|
||||
Flight::before($name, $callback) // Adds a filter before a framework method.
|
||||
Flight::after($name, $callback) // Adds a filter after a framework method.
|
||||
Flight::path($path) // Adds a path for autoloading classes.
|
||||
Flight::get($key) // Gets a variable.
|
||||
Flight::set($key, $value) // Sets a variable.
|
||||
Flight::has($key) // Checks if a variable is set.
|
||||
Flight::clear([$key]) // Clears a variable.
|
||||
Flight::init() // Initializes the framework to its default settings.
|
||||
Flight::app() // Gets the application object instance
|
||||
```
|
||||
|
||||
## Extensible Methods
|
||||
|
||||
```php
|
||||
Flight::start() // Starts the framework.
|
||||
Flight::stop() // Stops the framework and sends a response.
|
||||
Flight::halt([$code], [$message]) // Stop the framework with an optional status code and message.
|
||||
Flight::route($pattern, $callback) // Maps a URL pattern to a callback.
|
||||
Flight::redirect($url, [$code]) // Redirects to another URL.
|
||||
Flight::render($file, [$data], [$key]) // Renders a template file.
|
||||
Flight::error($exception) // Sends an HTTP 500 response.
|
||||
Flight::notFound() // Sends an HTTP 404 response.
|
||||
Flight::etag($id, [$type]) // Performs ETag HTTP caching.
|
||||
Flight::lastModified($time) // Performs last modified HTTP caching.
|
||||
Flight::json($data, [$code], [$encode], [$charset], [$option]) // Sends a JSON response.
|
||||
Flight::jsonp($data, [$param], [$code], [$encode], [$charset], [$option]) // Sends a JSONP response.
|
||||
```
|
||||
|
||||
Any custom methods added with `map` and `register` can also be filtered.
|
||||
|
||||
|
||||
# Framework Instance
|
||||
|
||||
Instead of running Flight as a global static class, you can optionally run it
|
||||
as an object instance.
|
||||
|
||||
```php
|
||||
require 'flight/autoload.php';
|
||||
|
||||
use flight\Engine;
|
||||
|
||||
$app = new Engine();
|
||||
|
||||
$app->route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
|
||||
$app->start();
|
||||
```
|
||||
|
||||
So instead of calling the static method, you would call the instance method with
|
||||
the same name on the Engine object.
|
1
libs/flight1/VERSION
Normal file
1
libs/flight1/VERSION
Normal file
@ -0,0 +1 @@
|
||||
1.3.9
|
11
libs/flight1/autoload.php
Normal file
11
libs/flight1/autoload.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2013, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once __DIR__.'/core/Loader.php';
|
||||
|
||||
\flight\core\Loader::autoload(true, dirname(__DIR__));
|
232
libs/flight1/core/Dispatcher.php
Normal file
232
libs/flight1/core/Dispatcher.php
Normal file
@ -0,0 +1,232 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
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 functions 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();
|
||||
|
||||
/**
|
||||
* Dispatches an event.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @param array $params Callback parameters
|
||||
* @return string Output of callback
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run($name, array $params = array()) {
|
||||
$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
|
||||
* @return callback $callback Callback function
|
||||
*/
|
||||
public function get($name) {
|
||||
return isset($this->events[$name]) ? $this->events[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an event has been set.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @return bool Event status
|
||||
*/
|
||||
public function has($name) {
|
||||
return isset($this->events[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an event. If no name is given,
|
||||
* all events are removed.
|
||||
*
|
||||
* @param string $name Event name
|
||||
*/
|
||||
public function clear($name = null) {
|
||||
if ($name !== null) {
|
||||
unset($this->events[$name]);
|
||||
unset($this->filters[$name]);
|
||||
}
|
||||
else {
|
||||
$this->events = array();
|
||||
$this->filters = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks a callback to an event.
|
||||
*
|
||||
* @param string $name 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 array $params Method parameters
|
||||
* @param mixed $output Method output
|
||||
* @throws \Exception
|
||||
*/
|
||||
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
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function execute($callback, array &$params = array()) {
|
||||
if (is_callable($callback)) {
|
||||
return is_array($callback) ?
|
||||
self::invokeMethod($callback, $params) :
|
||||
self::callFunction($callback, $params);
|
||||
}
|
||||
else {
|
||||
throw new \Exception('Invalid callback specified.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a function.
|
||||
*
|
||||
* @param string $func Name of function to call
|
||||
* @param array $params Function parameters
|
||||
* @return mixed Function results
|
||||
*/
|
||||
public static function callFunction($func, array &$params = array()) {
|
||||
// Call static method
|
||||
if (is_string($func) && strpos($func, '::') !== false) {
|
||||
return call_user_func_array($func, $params);
|
||||
}
|
||||
|
||||
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
|
||||
* @return mixed Function results
|
||||
*/
|
||||
public static function invokeMethod($func, array &$params = array()) {
|
||||
list($class, $method) = $func;
|
||||
|
||||
$instance = is_object($class);
|
||||
|
||||
switch (count($params)) {
|
||||
case 0:
|
||||
return ($instance) ?
|
||||
$class->$method() :
|
||||
$class::$method();
|
||||
case 1:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0]) :
|
||||
$class::$method($params[0]);
|
||||
case 2:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1]) :
|
||||
$class::$method($params[0], $params[1]);
|
||||
case 3:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2]) :
|
||||
$class::$method($params[0], $params[1], $params[2]);
|
||||
case 4:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2], $params[3]) :
|
||||
$class::$method($params[0], $params[1], $params[2], $params[3]);
|
||||
case 5:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) :
|
||||
$class::$method($params[0], $params[1], $params[2], $params[3], $params[4]);
|
||||
default:
|
||||
return call_user_func_array($func, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the object to the initial state.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->events = array();
|
||||
$this->filters = array();
|
||||
}
|
||||
}
|
217
libs/flight1/core/Loader.php
Normal file
217
libs/flight1/core/Loader.php
Normal file
@ -0,0 +1,217 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\core;
|
||||
|
||||
/**
|
||||
* The Loader class is responsible 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 static $dirs = array();
|
||||
|
||||
/**
|
||||
* Registers a class.
|
||||
*
|
||||
* @param string $name Registry name
|
||||
* @param string|callable $class Class name or function to instantiate class
|
||||
* @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[$name]);
|
||||
|
||||
$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
|
||||
* @return object Class instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function load($name, $shared = true) {
|
||||
$obj = null;
|
||||
|
||||
if (isset($this->classes[$name])) {
|
||||
list($class, $params, $callback) = $this->classes[$name];
|
||||
|
||||
$exists = isset($this->instances[$name]);
|
||||
|
||||
if ($shared) {
|
||||
$obj = ($exists) ?
|
||||
$this->getInstance($name) :
|
||||
$this->newInstance($class, $params);
|
||||
|
||||
if (!$exists) {
|
||||
$this->instances[$name] = $obj;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$obj = $this->newInstance($class, $params);
|
||||
}
|
||||
|
||||
if ($callback && (!$shared || !$exists)) {
|
||||
$ref = array(&$obj);
|
||||
call_user_func_array($callback, $ref);
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single instance of a class.
|
||||
*
|
||||
* @param string $name Instance name
|
||||
* @return object Class instance
|
||||
*/
|
||||
public function getInstance($name) {
|
||||
return isset($this->instances[$name]) ? $this->instances[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new instance of a class.
|
||||
*
|
||||
* @param string|callable $class Class name or callback function to instantiate class
|
||||
* @param array $params Class initialization parameters
|
||||
* @return object Class instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function newInstance($class, array $params = array()) {
|
||||
if (is_callable($class)) {
|
||||
return call_user_func_array($class, $params);
|
||||
}
|
||||
|
||||
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:
|
||||
try {
|
||||
$refClass = new \ReflectionClass($class);
|
||||
return $refClass->newInstanceArgs($params);
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new \Exception("Cannot instantiate {$class}", 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Registry name
|
||||
* @return mixed Class information or null if not registered
|
||||
*/
|
||||
public function get($name) {
|
||||
return isset($this->classes[$name]) ? $this->classes[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the object to the initial state.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->classes = array();
|
||||
$this->instances = array();
|
||||
}
|
||||
|
||||
/*** Autoloading Functions ***/
|
||||
|
||||
/**
|
||||
* Starts/stops autoloader.
|
||||
*
|
||||
* @param bool $enabled Enable/disable autoloading
|
||||
* @param array $dirs Autoload directories
|
||||
*/
|
||||
public static function autoload($enabled = true, $dirs = array()) {
|
||||
if ($enabled) {
|
||||
spl_autoload_register(array(__CLASS__, 'loadClass'));
|
||||
}
|
||||
else {
|
||||
spl_autoload_unregister(array(__CLASS__, 'loadClass'));
|
||||
}
|
||||
|
||||
if (!empty($dirs)) {
|
||||
self::addDirectory($dirs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads classes.
|
||||
*
|
||||
* @param string $class Class name
|
||||
*/
|
||||
public static function loadClass($class) {
|
||||
$class_file = str_replace(array('\\', '_'), '/', $class).'.php';
|
||||
/* Observium hook */
|
||||
$class_file = str_replace('flight/', 'flight1/', $class_file);
|
||||
|
||||
foreach (self::$dirs as $dir) {
|
||||
$file = $dir.'/'.$class_file;
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a directory for autoloading classes.
|
||||
*
|
||||
* @param mixed $dir Directory path
|
||||
*/
|
||||
public static function addDirectory($dir) {
|
||||
if (is_array($dir) || is_object($dir)) {
|
||||
foreach ($dir as $value) {
|
||||
self::addDirectory($value);
|
||||
}
|
||||
}
|
||||
else if (is_string($dir)) {
|
||||
if (!in_array($dir, self::$dirs)) self::$dirs[] = $dir;
|
||||
}
|
||||
}
|
||||
}
|
310
libs/flight1/net/Request.php
Normal file
310
libs/flight1/net/Request.php
Normal file
@ -0,0 +1,310 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
use flight\util\Collection;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* type - The content type
|
||||
* length - The content length
|
||||
* query - Query string parameters
|
||||
* data - Post parameters
|
||||
* cookies - Cookie parameters
|
||||
* files - Uploaded files
|
||||
* secure - Connection is secure
|
||||
* accept - HTTP accept parameters
|
||||
* proxy_ip - Proxy IP address of the client
|
||||
*/
|
||||
class Request {
|
||||
/**
|
||||
* @var string URL being requested
|
||||
*/
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* @var string Parent subdirectory of the URL
|
||||
*/
|
||||
public $base;
|
||||
|
||||
/**
|
||||
* @var string Request method (GET, POST, PUT, DELETE)
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* @var string Referrer URL
|
||||
*/
|
||||
public $referrer;
|
||||
|
||||
/**
|
||||
* @var string IP address of the client
|
||||
*/
|
||||
public $ip;
|
||||
|
||||
/**
|
||||
* @var bool Whether the request is an AJAX request
|
||||
*/
|
||||
public $ajax;
|
||||
|
||||
/**
|
||||
* @var string Server protocol (http, https)
|
||||
*/
|
||||
public $scheme;
|
||||
|
||||
/**
|
||||
* @var string Browser information
|
||||
*/
|
||||
public $user_agent;
|
||||
|
||||
/**
|
||||
* @var string Content type
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var int Content length
|
||||
*/
|
||||
public $length;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Query string parameters
|
||||
*/
|
||||
public $query;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Post parameters
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Cookie parameters
|
||||
*/
|
||||
public $cookies;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Uploaded files
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* @var bool Whether the connection is secure
|
||||
*/
|
||||
public $secure;
|
||||
|
||||
/**
|
||||
* @var string HTTP accept parameters
|
||||
*/
|
||||
public $accept;
|
||||
|
||||
/**
|
||||
* @var string Proxy IP address of the client
|
||||
*/
|
||||
public $proxy_ip;
|
||||
|
||||
/**
|
||||
* @var string HTTP host name
|
||||
*/
|
||||
public $host;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config Request configuration
|
||||
*/
|
||||
public function __construct($config = array()) {
|
||||
// Default properties
|
||||
if (empty($config)) {
|
||||
$config = array(
|
||||
'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')),
|
||||
'base' => str_replace(array('\\',' '), array('/','%20'), dirname(self::getVar('SCRIPT_NAME'))),
|
||||
'method' => self::getMethod(),
|
||||
'referrer' => self::getVar('HTTP_REFERER'),
|
||||
'ip' => self::getVar('REMOTE_ADDR'),
|
||||
'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest',
|
||||
'scheme' => self::getScheme(),
|
||||
'user_agent' => self::getVar('HTTP_USER_AGENT'),
|
||||
'type' => self::getVar('CONTENT_TYPE'),
|
||||
'length' => self::getVar('CONTENT_LENGTH', 0),
|
||||
'query' => new Collection($_GET),
|
||||
'data' => new Collection($_POST),
|
||||
'cookies' => new Collection($_COOKIE),
|
||||
'files' => new Collection($_FILES),
|
||||
'secure' => self::getScheme() == 'https',
|
||||
'accept' => self::getVar('HTTP_ACCEPT'),
|
||||
'proxy_ip' => self::getProxyIpAddress(),
|
||||
'host' => self::getVar('HTTP_HOST'),
|
||||
);
|
||||
}
|
||||
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize request properties.
|
||||
*
|
||||
* @param array $properties Array of request properties
|
||||
*/
|
||||
public function init($properties = array()) {
|
||||
// Set all the defined properties
|
||||
foreach ($properties as $name => $value) {
|
||||
$this->$name = $value;
|
||||
}
|
||||
|
||||
// Get the requested URL without the base directory
|
||||
if ($this->base != '/' && strlen($this->base) > 0 && strpos($this->url, $this->base) === 0) {
|
||||
$this->url = substr($this->url, strlen($this->base));
|
||||
}
|
||||
|
||||
// Default url
|
||||
if (empty($this->url)) {
|
||||
$this->url = '/';
|
||||
}
|
||||
// Merge URL query parameters with $_GET
|
||||
else {
|
||||
$_GET += self::parseQuery($this->url);
|
||||
|
||||
$this->query->setData($_GET);
|
||||
}
|
||||
|
||||
// Check for JSON input
|
||||
if (strpos($this->type, 'application/json') === 0) {
|
||||
$body = $this->getBody();
|
||||
if ($body != '') {
|
||||
$data = json_decode($body, true);
|
||||
if ($data != null) {
|
||||
$this->data->setData($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body of the request.
|
||||
*
|
||||
* @return string Raw HTTP request body
|
||||
*/
|
||||
public static function getBody() {
|
||||
static $body;
|
||||
|
||||
if (!is_null($body)) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
$method = self::getMethod();
|
||||
|
||||
if ($method == 'POST' || $method == 'PUT' || $method == 'DELETE' || $method == 'PATCH') {
|
||||
$body = file_get_contents('php://input');
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getMethod() {
|
||||
$method = self::getVar('REQUEST_METHOD', 'GET');
|
||||
|
||||
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
||||
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||
}
|
||||
elseif (isset($_REQUEST['_method'])) {
|
||||
$method = $_REQUEST['_method'];
|
||||
}
|
||||
|
||||
return strtoupper($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real remote IP address.
|
||||
*
|
||||
* @return string IP address
|
||||
*/
|
||||
public static function getProxyIpAddress() {
|
||||
static $forwarded = array(
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED',
|
||||
'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED'
|
||||
);
|
||||
|
||||
$flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE;
|
||||
|
||||
foreach ($forwarded as $key) {
|
||||
if (array_key_exists($key, $_SERVER)) {
|
||||
sscanf($_SERVER[$key], '%[^,]', $ip);
|
||||
if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a variable from $_SERVER using $default if not provided.
|
||||
*
|
||||
* @param string $var Variable name
|
||||
* @param string $default Default value to substitute
|
||||
* @return string Server variable value
|
||||
*/
|
||||
public static function getVar($var, $default = '') {
|
||||
return isset($_SERVER[$var]) ? $_SERVER[$var] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse query parameters from a URL.
|
||||
*
|
||||
* @param string $url URL string
|
||||
* @return array Query parameters
|
||||
*/
|
||||
public static function parseQuery($url) {
|
||||
$params = array();
|
||||
|
||||
$args = parse_url($url);
|
||||
if (isset($args['query'])) {
|
||||
parse_str($args['query'], $params);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
public static function getScheme() {
|
||||
if (
|
||||
(isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on')
|
||||
||
|
||||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https')
|
||||
||
|
||||
(isset($_SERVER['HTTP_FRONT_END_HTTPS']) && $_SERVER['HTTP_FRONT_END_HTTPS'] === 'on')
|
||||
||
|
||||
(isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https')
|
||||
) {
|
||||
return 'https';
|
||||
}
|
||||
return 'http';
|
||||
}
|
||||
}
|
308
libs/flight1/net/Response.php
Normal file
308
libs/flight1/net/Response.php
Normal file
@ -0,0 +1,308 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
/**
|
||||
* The Response class represents an HTTP response. The object
|
||||
* contains the response headers, HTTP status code, and response
|
||||
* body.
|
||||
*/
|
||||
class Response {
|
||||
/**
|
||||
* @var int HTTP status
|
||||
*/
|
||||
protected $status = 200;
|
||||
|
||||
/**
|
||||
* @var array HTTP headers
|
||||
*/
|
||||
protected $headers = array();
|
||||
|
||||
/**
|
||||
* @var string HTTP response body
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* @var bool HTTP response sent
|
||||
*/
|
||||
protected $sent = false;
|
||||
|
||||
/**
|
||||
* header Content-Length
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $content_length = true;
|
||||
|
||||
/**
|
||||
* @var array HTTP status codes
|
||||
*/
|
||||
public static $codes = array(
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing',
|
||||
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
203 => 'Non-Authoritative Information',
|
||||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
207 => 'Multi-Status',
|
||||
208 => 'Already Reported',
|
||||
|
||||
226 => 'IM Used',
|
||||
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
306 => '(Unused)',
|
||||
307 => 'Temporary Redirect',
|
||||
308 => 'Permanent Redirect',
|
||||
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Timeout',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Payload Too Large',
|
||||
414 => 'URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
|
||||
422 => 'Unprocessable Entity',
|
||||
423 => 'Locked',
|
||||
424 => 'Failed Dependency',
|
||||
|
||||
426 => 'Upgrade Required',
|
||||
|
||||
428 => 'Precondition Required',
|
||||
429 => 'Too Many Requests',
|
||||
|
||||
431 => 'Request Header Fields Too Large',
|
||||
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported',
|
||||
506 => 'Variant Also Negotiates',
|
||||
507 => 'Insufficient Storage',
|
||||
508 => 'Loop Detected',
|
||||
|
||||
510 => 'Not Extended',
|
||||
511 => 'Network Authentication Required'
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the HTTP status of the response.
|
||||
*
|
||||
* @param int $code HTTP status code.
|
||||
* @return object|int Self reference
|
||||
* @throws \Exception If invalid status code
|
||||
*/
|
||||
public function status($code = null) {
|
||||
if ($code === null) {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
if (array_key_exists($code, self::$codes)) {
|
||||
$this->status = $code;
|
||||
}
|
||||
else {
|
||||
throw new \Exception('Invalid status code.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header to the response.
|
||||
*
|
||||
* @param string|array $name Header name or array of names and values
|
||||
* @param string $value Header value
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function header($name, $value = null) {
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $k => $v) {
|
||||
$this->headers[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->headers[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the headers from the response
|
||||
* @return array
|
||||
*/
|
||||
public function headers() {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes content to the response body.
|
||||
*
|
||||
* @param string $str Response content
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function write($str) {
|
||||
$this->body .= $str;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the response.
|
||||
*
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function clear() {
|
||||
$this->status = 200;
|
||||
$this->headers = array();
|
||||
$this->body = '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets caching headers for the response.
|
||||
*
|
||||
* @param int|string $expires Expiration time
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function cache($expires) {
|
||||
if ($expires === false) {
|
||||
$this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
|
||||
$this->headers['Cache-Control'] = array(
|
||||
'no-store, no-cache, must-revalidate',
|
||||
'post-check=0, pre-check=0',
|
||||
'max-age=0'
|
||||
);
|
||||
$this->headers['Pragma'] = 'no-cache';
|
||||
}
|
||||
else {
|
||||
$expires = is_int($expires) ? $expires : strtotime($expires);
|
||||
$this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT';
|
||||
$this->headers['Cache-Control'] = 'max-age='.($expires - time());
|
||||
if (isset($this->headers['Pragma']) && $this->headers['Pragma'] == 'no-cache'){
|
||||
unset($this->headers['Pragma']);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers.
|
||||
*
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function sendHeaders() {
|
||||
// Send status code header
|
||||
if (strpos(php_sapi_name(), 'cgi') !== false) {
|
||||
header(
|
||||
sprintf(
|
||||
'Status: %d %s',
|
||||
$this->status,
|
||||
self::$codes[$this->status]
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
else {
|
||||
header(
|
||||
sprintf(
|
||||
'%s %d %s',
|
||||
(isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'),
|
||||
$this->status,
|
||||
self::$codes[$this->status]),
|
||||
true,
|
||||
$this->status
|
||||
);
|
||||
}
|
||||
|
||||
// Send other headers
|
||||
foreach ($this->headers as $field => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
header($field.': '.$v, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
header($field.': '.$value);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->content_length) {
|
||||
// Send content length
|
||||
$length = $this->getContentLength();
|
||||
|
||||
if ($length > 0) {
|
||||
header('Content-Length: '.$length);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content length.
|
||||
*
|
||||
* @return string Content length
|
||||
*/
|
||||
public function getContentLength() {
|
||||
return extension_loaded('mbstring') ?
|
||||
mb_strlen($this->body, 'latin1') :
|
||||
strlen($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether response was sent.
|
||||
*/
|
||||
public function sent() {
|
||||
return $this->sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a HTTP response.
|
||||
*/
|
||||
public function send() {
|
||||
if (ob_get_length() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
if (!headers_sent()) {
|
||||
$this->sendHeaders();
|
||||
}
|
||||
|
||||
echo $this->body;
|
||||
|
||||
$this->sent = true;
|
||||
}
|
||||
}
|
||||
|
144
libs/flight1/net/Route.php
Normal file
144
libs/flight1/net/Route.php
Normal file
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
/**
|
||||
* The Route 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 Route {
|
||||
/**
|
||||
* @var string URL pattern
|
||||
*/
|
||||
public $pattern;
|
||||
|
||||
/**
|
||||
* @var mixed Callback function
|
||||
*/
|
||||
public $callback;
|
||||
|
||||
/**
|
||||
* @var array HTTP methods
|
||||
*/
|
||||
public $methods = array();
|
||||
|
||||
/**
|
||||
* @var array Route parameters
|
||||
*/
|
||||
public $params = array();
|
||||
|
||||
/**
|
||||
* @var string Matching regular expression
|
||||
*/
|
||||
public $regex;
|
||||
|
||||
/**
|
||||
* @var string URL splat content
|
||||
*/
|
||||
public $splat = '';
|
||||
|
||||
/**
|
||||
* @var boolean Pass self in callback parameters
|
||||
*/
|
||||
public $pass = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $pattern URL pattern
|
||||
* @param mixed $callback Callback function
|
||||
* @param array $methods HTTP methods
|
||||
* @param boolean $pass Pass self in callback parameters
|
||||
*/
|
||||
public function __construct($pattern, $callback, $methods, $pass) {
|
||||
$this->pattern = $pattern;
|
||||
$this->callback = $callback;
|
||||
$this->methods = $methods;
|
||||
$this->pass = $pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL matches the route pattern. Also parses named parameters in the URL.
|
||||
*
|
||||
* @param string $url Requested URL
|
||||
* @param boolean $case_sensitive Case sensitive matching
|
||||
* @return boolean Match status
|
||||
*/
|
||||
public function matchUrl($url, $case_sensitive = false) {
|
||||
// Wildcard or exact match
|
||||
if ($this->pattern === '*' || $this->pattern === $url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ids = array();
|
||||
$last_char = substr($this->pattern, -1);
|
||||
|
||||
// Get splat
|
||||
if ($last_char === '*') {
|
||||
$n = 0;
|
||||
$len = strlen($url);
|
||||
$count = substr_count($this->pattern, '/');
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($url[$i] == '/') $n++;
|
||||
if ($n == $count) break;
|
||||
}
|
||||
|
||||
$this->splat = (string)substr($url, $i+1);
|
||||
}
|
||||
|
||||
// Build the regex for matching
|
||||
$regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern);
|
||||
|
||||
$regex = preg_replace_callback(
|
||||
'#@([\w]+)(:([^/\(\)]*))?#',
|
||||
function($matches) use (&$ids) {
|
||||
$ids[$matches[1]] = null;
|
||||
if (isset($matches[3])) {
|
||||
return '(?P<'.$matches[1].'>'.$matches[3].')';
|
||||
}
|
||||
return '(?P<'.$matches[1].'>[^/\?]+)';
|
||||
},
|
||||
$regex
|
||||
);
|
||||
|
||||
// Fix trailing slash
|
||||
if ($last_char === '/') {
|
||||
$regex .= '?';
|
||||
}
|
||||
// Allow trailing slash
|
||||
else {
|
||||
$regex .= '/?';
|
||||
}
|
||||
|
||||
// Attempt to match route and named parameters
|
||||
if (preg_match('#^'.$regex.'(?:\?.*)?$#'.(($case_sensitive) ? '' : 'i'), $url, $matches)) {
|
||||
foreach ($ids as $k => $v) {
|
||||
$this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
|
||||
}
|
||||
|
||||
$this->regex = $regex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an HTTP method matches the route methods.
|
||||
*
|
||||
* @param string $method HTTP method
|
||||
* @return bool Match status
|
||||
*/
|
||||
public function matchMethod($method) {
|
||||
return count(array_intersect(array($method, '*'), $this->methods)) > 0;
|
||||
}
|
||||
}
|
117
libs/flight1/net/Router.php
Normal file
117
libs/flight1/net/Router.php
Normal file
@ -0,0 +1,117 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
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.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = array();
|
||||
|
||||
/**
|
||||
* Pointer to current route.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = 0;
|
||||
|
||||
/**
|
||||
* Case sensitive matching.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $case_sensitive = false;
|
||||
|
||||
/**
|
||||
* Gets mapped routes.
|
||||
*
|
||||
* @return array Array of routes
|
||||
*/
|
||||
public function getRoutes() {
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all routes in the router.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->routes = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a URL pattern to a callback function.
|
||||
*
|
||||
* @param string $pattern URL pattern to match
|
||||
* @param callback $callback Callback function
|
||||
* @param boolean $pass_route Pass the matching route object to the callback
|
||||
*/
|
||||
public function map($pattern, $callback, $pass_route = false) {
|
||||
$url = $pattern;
|
||||
$methods = array('*');
|
||||
|
||||
if (strpos($pattern, ' ') !== false) {
|
||||
list($method, $url) = explode(' ', trim($pattern), 2);
|
||||
$url = trim($url);
|
||||
$methods = explode('|', $method);
|
||||
}
|
||||
|
||||
$this->routes[] = new Route($url, $callback, $methods, $pass_route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes the current request.
|
||||
*
|
||||
* @param Request $request Request object
|
||||
* @return Route|bool Matching route or false if no match
|
||||
*/
|
||||
public function route(Request $request) {
|
||||
$url_decoded = urldecode( $request->url );
|
||||
while ($route = $this->current()) {
|
||||
if ($route !== false && $route->matchMethod($request->method) && $route->matchUrl($url_decoded, $this->case_sensitive)) {
|
||||
return $route;
|
||||
}
|
||||
$this->next();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current route.
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function current() {
|
||||
return isset($this->routes[$this->index]) ? $this->routes[$this->index] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next route.
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function next() {
|
||||
$this->index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to the first route.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->index = 0;
|
||||
}
|
||||
}
|
||||
|
184
libs/flight1/template/View.php
Normal file
184
libs/flight1/template/View.php
Normal file
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
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 {
|
||||
/**
|
||||
* Location of view templates.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $extension = '.php';
|
||||
|
||||
/**
|
||||
* View variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Template file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path Path to templates directory
|
||||
*/
|
||||
public function __construct($path = '.') {
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a template variable.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function get($key) {
|
||||
return isset($this->vars[$key]) ? $this->vars[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a template variable.
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @param string $value Value
|
||||
*/
|
||||
public function set($key, $value = null) {
|
||||
if (is_array($key) || is_object($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
$this->vars[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a template variable is set.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return boolean If key exists
|
||||
*/
|
||||
public function has($key) {
|
||||
return isset($this->vars[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a template variable. If no key is passed in, clear all variables.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function clear($key = null) {
|
||||
if (is_null($key)) {
|
||||
$this->vars = array();
|
||||
}
|
||||
else {
|
||||
unset($this->vars[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @throws \Exception If template not found
|
||||
*/
|
||||
public function render($file, $data = null) {
|
||||
$this->template = $this->getTemplate($file);
|
||||
|
||||
if (!file_exists($this->template)) {
|
||||
throw new \Exception("Template file not found: {$this->template}.");
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
$this->vars = array_merge($this->vars, $data);
|
||||
}
|
||||
|
||||
extract($this->vars);
|
||||
|
||||
include $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output of a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @return string Output of template
|
||||
*/
|
||||
public function fetch($file, $data = null) {
|
||||
ob_start();
|
||||
|
||||
$this->render($file, $data);
|
||||
$output = ob_get_clean();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a template file exists.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @return bool Template file exists
|
||||
*/
|
||||
public function exists($file) {
|
||||
return file_exists($this->getTemplate($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full path to a template file.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @return string Template file location
|
||||
*/
|
||||
public function getTemplate($file) {
|
||||
$ext = $this->extension;
|
||||
|
||||
if (!empty($ext) && (substr($file, -1 * strlen($ext)) != $ext)) {
|
||||
$file .= $ext;
|
||||
}
|
||||
|
||||
if ((substr($file, 0, 1) == '/')) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return $this->path.'/'.$file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays escaped output.
|
||||
*
|
||||
* @param string $str String to escape
|
||||
* @return string Escaped string
|
||||
*/
|
||||
public function e($str) {
|
||||
echo htmlentities($str);
|
||||
}
|
||||
}
|
||||
|
216
libs/flight1/util/Collection.php
Normal file
216
libs/flight1/util/Collection.php
Normal file
@ -0,0 +1,216 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\util;
|
||||
|
||||
if (!interface_exists('JsonSerializable')) {
|
||||
require_once dirname(__FILE__) . '/LegacyJsonSerializable.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* The Collection class allows you to access a set of data
|
||||
* using both array and object notation.
|
||||
*/
|
||||
class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializable {
|
||||
/**
|
||||
* Collection data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $data;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $data Initial data
|
||||
*/
|
||||
public function __construct(array $data = array()) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function __get($key) {
|
||||
return isset($this->data[$key]) ? $this->data[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an item.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @param mixed $value Value
|
||||
*/
|
||||
public function __set($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item exists.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return bool Item status
|
||||
*/
|
||||
public function __isset($key) {
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function __unset($key) {
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
return isset($this->data[$offset]) ? $this->data[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @param mixed $value Value
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
if (is_null($offset)) {
|
||||
$this->data[] = $value;
|
||||
}
|
||||
else {
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item exists at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return bool Item status
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the collection.
|
||||
*/
|
||||
public function rewind() {
|
||||
reset($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current collection item.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function current() {
|
||||
return current($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current collection key.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next collection value.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current collection key is valid.
|
||||
*
|
||||
* @return bool Key status
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$key = key($this->data);
|
||||
return ($key !== NULL && $key !== FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the collection.
|
||||
*
|
||||
* @return int Collection size
|
||||
*/
|
||||
public function count() {
|
||||
return sizeof($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item keys.
|
||||
*
|
||||
* @return array Collection keys
|
||||
*/
|
||||
public function keys() {
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection data.
|
||||
*
|
||||
* @return array Collection data
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collection data.
|
||||
*
|
||||
* @param array $data New collection data
|
||||
*/
|
||||
public function setData(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection data which can be serialized to JSON
|
||||
*
|
||||
* @return array Collection data which can be serialized by <b>json_encode</b>
|
||||
*/
|
||||
public function jsonSerialize() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items from the collection.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->data = array();
|
||||
}
|
||||
}
|
11
libs/flight1/util/LegacyJsonSerializable.php
Normal file
11
libs/flight1/util/LegacyJsonSerializable.php
Normal file
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
interface JsonSerializable {
|
||||
public function jsonSerialize();
|
||||
}
|
Reference in New Issue
Block a user