diff --git a/README.md b/README.md index 28fbb5b..595f81d 100644 --- a/README.md +++ b/README.md @@ -178,7 +178,7 @@ The register method also allows you to pass along parameters to your class const // Get an instance of your class // This will create an object with the defined parameters // - // new Database('localhost', 'test', 'user', 'pass'); + // new Database('localhost', 'mydb', 'user', 'pass'); // $db = Flight::db(); @@ -192,11 +192,11 @@ If you pass in an additional callback parameter, it will be executed immediately 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: - // Shared instance of User - $shared = Flight::user(); + // Shared instance of Database class + $shared = Flight::db(); - // New instance of User - $new = Flight::user(false); + // New instance of Database 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. @@ -213,13 +213,6 @@ You can override this behavior by using the `map` method: include 'errors/404.html'; }); -Flight also has custom error handling which you can override: - - Flight::map('error', function($e){ - // Log error somewhere - log_error($e); - }); - 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: @@ -286,7 +279,7 @@ This should display: Hello Fred! Have a nice day! -If you have defined multiple filters, you can break the chain by returning `false`: +If you have defined multiple filters, you can break the chain by returning `false` in any of your filter functions: Flight::before('start', function(&$params, &$output){ echo 'one'; @@ -372,9 +365,9 @@ It is common for websites to have a single layout template file with interchangi Your view will then have saved variables called `header_content` and `body_content`. You can then render your layout by doing: - Flight::render('layout', array('title' => 'Home'); + Flight::render('layout', array('title' => 'Home')); -If the template file looks like this: +If the template files looks like this: header.php: @@ -460,7 +453,7 @@ When a URL can't be found, Flight calls the `notFound` method. The default behav send an HTTP `404 Not Found` response with a simple message. You can override this behavior for your own needs: Flight::map('notFound', function(){ - // Display custom error page + // Handle not found }); # Redirects @@ -469,6 +462,38 @@ You can redirect the current request by using the `redirect` method and passing Flight::redirect('/new/location'); +# Requests + +Flight encapsulates the HTTP request into a single object, which can be accessed by doing: + + $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 + body - Raw data from the request body + type - The content type + length - The content length + query - Query string parameters + data - Post parameters + cookies - Cookie parameters + files - Uploaded files + +To access query string parameters, you can do: + + $id = Flight::request()->query['id']; + +You can also get the data using object notation: + + $id = Flight::request()->query->id; + # HTTP Caching Flight provides built-in support for HTTP level caching. If the caching condition is met, @@ -512,3 +537,55 @@ 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: Flight::stop(); + +# Framework Methods + +## Core Methods + +The following are Flight's core methods: + + 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. + +## Extensible Methods + +The following methods are extensible, meaning you can filter and override them: + + 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 400 response. + + Flight::etag($id, [$type]) - Performs ETag HTTP caching. + + Flight::lastModified($time) - Performs last modified HTTP caching. + + Flight::json($data) - Sends a JSON response. + +All custom methods added with `map` and `register` can also be filtered. diff --git a/flight/Flight.php b/flight/Flight.php index 36d2283..0aa0e66 100644 --- a/flight/Flight.php +++ b/flight/Flight.php @@ -181,7 +181,7 @@ class Flight { set_error_handler(array(__CLASS__, 'handleError')); // Handle exceptions internally - set_exception_handler(array(__CLASS__, 'handleException')); + set_exception_handler(array(__CLASS__, 'error')); // Turn off notices error_reporting (E_ALL ^ E_NOTICE); @@ -241,22 +241,6 @@ class Flight { } } - /** - * Custom exception handler. - */ - public static function handleException(Exception $e) { - try { - static::error($e); - } - catch (Exception $ex) { - exit( - '

500 Internal Server Error

'. - '

'.$ex->getMessage().'

'. - '
'.$ex->getTraceAsString().'
' - ); - } - } - /** * Starts the framework. */ @@ -295,26 +279,16 @@ class Flight { ->send(); } - /** - * Routes a URL to a callback function. - * - * @param string $pattern URL pattern to match - * @param callback $callback Callback function - */ - public static function _route($pattern, $callback) { - self::router()->map($pattern, $callback); - } - /** * Stops processing and returns a given response. * * @param int $code HTTP status code - * @param int $msg Response text + * @param int $message Response message */ - public static function _halt($code = 200, $text = '') { + public static function _halt($code = 200, $message = '') { self::response(false) ->status($code) - ->write($text) + ->write($message) ->cache(false) ->send(); } @@ -322,20 +296,25 @@ class Flight { /** * Sends an HTTP 500 response for any errors. * - * @param object $ex Exception + * @param object $e Exception */ public static function _error(Exception $e) { - if (self::get('flight.log_errors')) { - error_log($e->getMessage()); + $msg = '

500 Internal Server Error

'. + '

'.$e->getMessage().'

'. + '
'.$e->getTraceAsString().'
'; + + try { + if (self::get('flight.log_errors')) { + error_log($e->getMessage()); + } + self::response(false) + ->status(500) + ->write($msg) + ->send(); + } + catch (Exception $ex) { + exit($msg); } - self::response(false) - ->status(500) - ->write( - '

500 Internal Server Error

'. - '

'.$e->getMessage().'

'. - '
'.$e->getTraceAsString().'
' - ) - ->send(); } /** @@ -352,6 +331,16 @@ class Flight { ->send(); } + /** + * Routes a URL to a callback function. + * + * @param string $pattern URL pattern to match + * @param callback $callback Callback function + */ + public static function _route($pattern, $callback) { + self::router()->map($pattern, $callback); + } + /** * Redirects the current request to another URL. * @@ -381,6 +370,19 @@ class Flight { } } + /** + * Sends a JSON response. + * + * @param mixed $data Data to JSON encode + */ + public static function _json($data) { + self::response(false) + ->status(200) + ->header('Content-Type', 'application/json') + ->write(json_encode($data)) + ->send(); + } + /** * Handles ETag HTTP caching. * @@ -409,19 +411,6 @@ class Flight { self::halt(304); } } - - /** - * Sends a JSON response. - * - * @param mixed $data Data to JSON encode - */ - public static function _json($data) { - self::response(false) - ->status(200) - ->header('Content-Type', 'application/json') - ->write(json_encode($data)) - ->send(); - } } // Initialize the framework on include diff --git a/flight/net/Request.php b/flight/net/Request.php index 10f2cc0..6177495 100644 --- a/flight/net/Request.php +++ b/flight/net/Request.php @@ -8,6 +8,8 @@ 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 @@ -51,10 +53,10 @@ class Request { 'body' => file_get_contents('php://input'), 'type' => $_SERVER['CONTENT_TYPE'], 'length' => $_SERVER['CONTENT_LENGTH'], - 'query' => $_GET, - 'data' => $_POST, - 'cookies' => $_COOKIE, - 'files' => $_FILES + 'query' => new Collection($_GET), + 'data' => new Collection($_POST), + 'cookies' => new Collection($_COOKIE), + 'files' => new Collection($_FILES) ); } @@ -79,8 +81,9 @@ class Request { $this->url = '/'; } else { - $this->query = self::parseQuery($this->url); - $_GET = $this->query; + $_GET = self::parseQuery($this->url); + + $this->query->setData($_GET); } } diff --git a/flight/util/Collection.php b/flight/util/Collection.php new file mode 100644 index 0000000..0c40a75 --- /dev/null +++ b/flight/util/Collection.php @@ -0,0 +1,206 @@ + + * @license http://www.opensource.org/licenses/mit-license.php + */ + +namespace flight\util; + +/** + * The Collection class allows you to access a set of data + * using both array and object notation. + */ +class Collection implements \ArrayAccess, \Iterator, \Countable { + /** + * 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 $this->data[$key]; + } + + /** + * 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() { + $count = iterator_count($this); + $this->rewind(); + return $count; + } + + /** + * 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; + } + + /** + * Removes all items from the collection. + */ + public function clear() { + $this->data = array(); + } +} +?>