PHP ^7.4|^8.0 compatibility

pull/439/head
Masroor Ehsan 4 years ago
parent 6e9768503a
commit 04e471bf46

@ -12,7 +12,8 @@
} }
], ],
"require": { "require": {
"php": ">=5.3.0" "php": "^7.4|^8.0",
"ext-json": "*"
}, },
"autoload": { "autoload": {
"files": [ "flight/autoload.php", "flight/Flight.php" ] "files": [ "flight/autoload.php", "flight/Flight.php" ]

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -8,8 +10,15 @@
namespace flight; namespace flight;
use flight\core\Loader; use ErrorException;
use Exception;
use flight\core\Dispatcher; use flight\core\Dispatcher;
use flight\core\Loader;
use flight\net\Request;
use flight\net\Response;
use flight\net\Router;
use flight\template\View;
use Throwable;
/** /**
* The Engine class contains the core functionality of the framework. * The Engine class contains the core functionality of the framework.
@ -17,23 +26,21 @@ use flight\core\Dispatcher;
* and generating an HTTP response. * and generating an HTTP response.
* *
* Core methods * Core methods
*
* @method void start() Starts engine * @method void start() Starts engine
* @method void stop() Stops framework and outputs current response * @method void stop() Stops framework and outputs current response
* @method void halt(int $code = 200, string $message = '') Stops processing and returns a given 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 void route(string $pattern, callable $callback, bool $pass_route = false) Routes a URL to a callback function.
* @method \flight\net\Router router() Gets router * @method Router router() Gets router
* *
* Views * Views
* @method void render(string $file, array $data = null, string $key = null) Renders template * @method void render(string $file, array $data = null, string $key = null) Renders template
* @method \flight\template\View view() Gets current view * @method View view() Gets current view
* *
* Request-response * Request-response
* @method \flight\net\Request request() Gets current request * @method Request request() Gets current request
* @method \flight\net\Response response() Gets current response * @method Response response() Gets current response
* @method void error(\Exception $e) Sends an HTTP 500 response for any errors. * @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 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 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 json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) Sends a JSON response.
@ -43,33 +50,29 @@ use flight\core\Dispatcher;
* @method void etag($id, string $type = 'strong') Handles ETag HTTP caching. * @method void etag($id, string $type = 'strong') Handles ETag HTTP caching.
* @method void lastModified(int $time) Handles last modified HTTP caching. * @method void lastModified(int $time) Handles last modified HTTP caching.
*/ */
class Engine { class Engine
{
/** /**
* Stored variables. * Stored variables.
*
* @var array
*/ */
protected $vars; protected array $vars;
/** /**
* Class loader. * Class loader.
*
* @var Loader
*/ */
protected $loader; protected Loader $loader;
/** /**
* Event dispatcher. * Event dispatcher.
*
* @var Dispatcher
*/ */
protected $dispatcher; protected Dispatcher $dispatcher;
/** /**
* Constructor. * Constructor.
*/ */
public function __construct() { public function __construct()
$this->vars = array(); {
$this->vars = [];
$this->loader = new Loader(); $this->loader = new Loader();
$this->dispatcher = new Dispatcher(); $this->dispatcher = new Dispatcher();
@ -80,38 +83,42 @@ class Engine {
/** /**
* Handles calls to class methods. * Handles calls to class methods.
* *
* @param string $name Method name * @param string $name Method name
* @param array $params Method parameters * @param array $params Method parameters
*
*@throws Exception
*
* @return mixed Callback results * @return mixed Callback results
* @throws \Exception
*/ */
public function __call($name, $params) { public function __call(string $name, array $params)
{
$callback = $this->dispatcher->get($name); $callback = $this->dispatcher->get($name);
if (is_callable($callback)) { if (\is_callable($callback)) {
return $this->dispatcher->run($name, $params); return $this->dispatcher->run($name, $params);
} }
if (!$this->loader->get($name)) { if (!$this->loader->get($name)) {
throw new \Exception("{$name} must be a mapped method."); throw new Exception("{$name} must be a mapped method.");
} }
$shared = (!empty($params)) ? (bool)$params[0] : true; $shared = (!empty($params)) ? (bool) $params[0] : true;
return $this->loader->load($name, $shared); return $this->loader->load($name, $shared);
} }
/*** Core Methods ***/ // Core Methods
/** /**
* Initializes the framework. * Initializes the framework.
*/ */
public function init() { public function init(): void
{
static $initialized = false; static $initialized = false;
$self = $this; $self = $this;
if ($initialized) { if ($initialized) {
$this->vars = array(); $this->vars = [];
$this->loader->reset(); $this->loader->reset();
$this->dispatcher->reset(); $this->dispatcher->reset();
} }
@ -120,18 +127,18 @@ class Engine {
$this->loader->register('request', '\flight\net\Request'); $this->loader->register('request', '\flight\net\Request');
$this->loader->register('response', '\flight\net\Response'); $this->loader->register('response', '\flight\net\Response');
$this->loader->register('router', '\flight\net\Router'); $this->loader->register('router', '\flight\net\Router');
$this->loader->register('view', '\flight\template\View', array(), function($view) use ($self) { $this->loader->register('view', '\flight\template\View', [], function ($view) use ($self) {
$view->path = $self->get('flight.views.path'); $view->path = $self->get('flight.views.path');
$view->extension = $self->get('flight.views.extension'); $view->extension = $self->get('flight.views.extension');
}); });
// Register framework methods // Register framework methods
$methods = array( $methods = [
'start','stop','route','halt','error','notFound', 'start', 'stop', 'route', 'halt', 'error', 'notFound',
'render','redirect','etag','lastModified','json','jsonp' 'render', 'redirect', 'etag', 'lastModified', 'json', 'jsonp',
); ];
foreach ($methods as $name) { foreach ($methods as $name) {
$this->dispatcher->set($name, array($this, '_'.$name)); $this->dispatcher->set($name, [$this, '_' . $name]);
} }
// Default configuration settings // Default configuration settings
@ -144,11 +151,11 @@ class Engine {
$this->set('flight.content_length', true); $this->set('flight.content_length', true);
// Startup configuration // Startup configuration
$this->before('start', function() use ($self) { $this->before('start', function () use ($self) {
// Enable error handling // Enable error handling
if ($self->get('flight.handle_errors')) { if ($self->get('flight.handle_errors')) {
set_error_handler(array($self, 'handleError')); set_error_handler([$self, 'handleError']);
set_exception_handler(array($self, 'handleException')); set_exception_handler([$self, 'handleException']);
} }
// Set case-sensitivity // Set case-sensitivity
@ -163,24 +170,27 @@ class Engine {
/** /**
* Custom error handler. Converts errors into exceptions. * Custom error handler. Converts errors into exceptions.
* *
* @param int $errno Error number * @param int $errno Error number
* @param int $errstr Error string * @param int $errstr Error string
* @param int $errfile Error file name * @param int $errfile Error file name
* @param int $errline Error file line number * @param int $errline Error file line number
* @throws \ErrorException *
* @throws ErrorException
*/ */
public function handleError($errno, $errstr, $errfile, $errline) { public function handleError(int $errno, int $errstr, int $errfile, int $errline)
{
if ($errno & error_reporting()) { if ($errno & error_reporting()) {
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline); throw new ErrorException($errstr, $errno, 0, $errfile, $errline);
} }
} }
/** /**
* Custom exception handler. Logs exceptions. * Custom exception handler. Logs exceptions.
* *
* @param \Exception $e Thrown exception * @param Exception $e Thrown exception
*/ */
public function handleException($e) { public function handleException($e): void
{
if ($this->get('flight.log_errors')) { if ($this->get('flight.log_errors')) {
error_log($e->getMessage()); error_log($e->getMessage());
} }
@ -191,13 +201,15 @@ class Engine {
/** /**
* Maps a callback to a framework method. * Maps a callback to a framework method.
* *
* @param string $name Method name * @param string $name Method name
* @param callback $callback Callback function * @param callback $callback Callback function
* @throws \Exception If trying to map over a framework method *
* @throws Exception If trying to map over a framework method
*/ */
public function map($name, $callback) { public function map(string $name, callable $callback): void
{
if (method_exists($this, $name)) { if (method_exists($this, $name)) {
throw new \Exception('Cannot override an existing framework method.'); throw new Exception('Cannot override an existing framework method.');
} }
$this->dispatcher->set($name, $callback); $this->dispatcher->set($name, $callback);
@ -206,15 +218,17 @@ class Engine {
/** /**
* Registers a class to a framework method. * Registers a class to a framework method.
* *
* @param string $name Method name * @param string $name Method name
* @param string $class Class name * @param string $class Class name
* @param array $params Class initialization parameters * @param array $params Class initialization parameters
* @param callback $callback Function to call after object instantiation * @param callable|null $callback $callback Function to call after object instantiation
* @throws \Exception If trying to map over a framework method *
* @throws Exception If trying to map over a framework method
*/ */
public function register($name, $class, array $params = array(), $callback = null) { public function register(string $name, string $class, array $params = [], ?callable $callback = null): void
{
if (method_exists($this, $name)) { if (method_exists($this, $name)) {
throw new \Exception('Cannot override an existing framework method.'); throw new Exception('Cannot override an existing framework method.');
} }
$this->loader->register($name, $class, $params, $callback); $this->loader->register($name, $class, $params, $callback);
@ -223,48 +237,54 @@ class Engine {
/** /**
* Adds a pre-filter to a method. * Adds a pre-filter to a method.
* *
* @param string $name Method name * @param string $name Method name
* @param callback $callback Callback function * @param callback $callback Callback function
*/ */
public function before($name, $callback) { public function before(string $name, callable $callback): void
{
$this->dispatcher->hook($name, 'before', $callback); $this->dispatcher->hook($name, 'before', $callback);
} }
/** /**
* Adds a post-filter to a method. * Adds a post-filter to a method.
* *
* @param string $name Method name * @param string $name Method name
* @param callback $callback Callback function * @param callback $callback Callback function
*/ */
public function after($name, $callback) { public function after(string $name, callable $callback): void
{
$this->dispatcher->hook($name, 'after', $callback); $this->dispatcher->hook($name, 'after', $callback);
} }
/** /**
* Gets a variable. * Gets a variable.
* *
* @param string $key Key * @param string|null $key Key
* @return mixed *
* @return array|mixed|null
*/ */
public function get($key = null) { public function get(?string $key = null)
if ($key === null) return $this->vars; {
if (null === $key) {
return $this->vars;
}
return isset($this->vars[$key]) ? $this->vars[$key] : null; return $this->vars[$key] ?? null;
} }
/** /**
* Sets a variable. * Sets a variable.
* *
* @param mixed $key Key * @param mixed $key Key
* @param string $value Value * @param mixed|null $value Value
*/ */
public function set($key, $value = null) { public function set($key, $value = null): void
if (is_array($key) || is_object($key)) { {
if (\is_array($key) || \is_object($key)) {
foreach ($key as $k => $v) { foreach ($key as $k => $v) {
$this->vars[$k] = $v; $this->vars[$k] = $v;
} }
} } else {
else {
$this->vars[$key] = $value; $this->vars[$key] = $value;
} }
} }
@ -273,22 +293,24 @@ class Engine {
* Checks if a variable has been set. * Checks if a variable has been set.
* *
* @param string $key Key * @param string $key Key
*
* @return bool Variable status * @return bool Variable status
*/ */
public function has($key) { public function has(string $key): bool
{
return isset($this->vars[$key]); return isset($this->vars[$key]);
} }
/** /**
* Unsets a variable. If no key is passed in, clear all variables. * Unsets a variable. If no key is passed in, clear all variables.
* *
* @param string $key Key * @param string|null $key Key
*/ */
public function clear($key = null) { public function clear(?string $key = null): void
if (is_null($key)) { {
$this->vars = array(); if (null === $key) {
} $this->vars = [];
else { } else {
unset($this->vars[$key]); unset($this->vars[$key]);
} }
} }
@ -298,17 +320,20 @@ class Engine {
* *
* @param string $dir Directory path * @param string $dir Directory path
*/ */
public function path($dir) { public function path(string $dir): void
{
$this->loader->addDirectory($dir); $this->loader->addDirectory($dir);
} }
/*** Extensible Methods ***/ // Extensible Methods
/** /**
* Starts the framework. * Starts the framework.
* @throws \Exception *
* @throws Exception
*/ */
public function _start() { public function _start(): void
{
$dispatched = false; $dispatched = false;
$self = $this; $self = $this;
$request = $this->request(); $request = $this->request();
@ -316,7 +341,7 @@ class Engine {
$router = $this->router(); $router = $this->router();
// Allow filters to run // Allow filters to run
$this->after('start', function() use ($self) { $this->after('start', function () use ($self) {
$self->stop(); $self->stop();
}); });
@ -345,7 +370,9 @@ class Engine {
$dispatched = true; $dispatched = true;
if (!$continue) break; if (!$continue) {
break;
}
$router->next(); $router->next();
@ -360,14 +387,16 @@ class Engine {
/** /**
* Stops the framework and outputs the current response. * Stops the framework and outputs the current response.
* *
* @param int $code HTTP status code * @param int|null $code HTTP status code
* @throws \Exception *
* @throws Exception
*/ */
public function _stop($code = null) { public function _stop(?int $code = null): void
{
$response = $this->response(); $response = $this->response();
if (!$response->sent()) { if (!$response->sent()) {
if ($code !== null) { if (null !== $code) {
$response->status($code); $response->status($code);
} }
@ -380,21 +409,23 @@ class Engine {
/** /**
* Routes a URL to a callback function. * Routes a URL to a callback function.
* *
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callback $callback Callback function * @param callback $callback Callback function
* @param boolean $pass_route Pass the matching route object to the callback * @param bool $pass_route Pass the matching route object to the callback
*/ */
public function _route($pattern, $callback, $pass_route = false) { public function _route(string $pattern, callable $callback, bool $pass_route = false): void
{
$this->router()->map($pattern, $callback, $pass_route); $this->router()->map($pattern, $callback, $pass_route);
} }
/** /**
* Stops processing and returns a given response. * Stops processing and returns a given response.
* *
* @param int $code HTTP status code * @param int $code HTTP status code
* @param string $message Response message * @param string $message Response message
*/ */
public function _halt($code = 200, $message = '') { public function _halt(int $code = 200, string $message = ''): void
{
$this->response() $this->response()
->clear() ->clear()
->status($code) ->status($code)
@ -406,11 +437,12 @@ class Engine {
/** /**
* Sends an HTTP 500 response for any errors. * Sends an HTTP 500 response for any errors.
* *
* @param \Exception|\Throwable $e Thrown exception * @param Exception|Throwable $e Thrown exception
*/ */
public function _error($e) { public function _error($e): void
$msg = sprintf('<h1>500 Internal Server Error</h1>'. {
'<h3>%s (%s)</h3>'. $msg = sprintf('<h1>500 Internal Server Error</h1>' .
'<h3>%s (%s)</h3>' .
'<pre>%s</pre>', '<pre>%s</pre>',
$e->getMessage(), $e->getMessage(),
$e->getCode(), $e->getCode(),
@ -423,10 +455,9 @@ class Engine {
->status(500) ->status(500)
->write($msg) ->write($msg)
->send(); ->send();
} } catch (Throwable $t) { // PHP 7.0+
catch (\Throwable $t) { // PHP 7.0+
exit($msg); exit($msg);
} catch(\Exception $e) { // PHP < 7 } catch (Exception $e) { // PHP < 7
exit($msg); exit($msg);
} }
} }
@ -434,13 +465,14 @@ class Engine {
/** /**
* Sends an HTTP 404 response when a URL is not found. * Sends an HTTP 404 response when a URL is not found.
*/ */
public function _notFound() { public function _notFound(): void
{
$this->response() $this->response()
->clear() ->clear()
->status(404) ->status(404)
->write( ->write(
'<h1>404 Not Found</h1>'. '<h1>404 Not Found</h1>' .
'<h3>The page you have requested could not be found.</h3>'. '<h3>The page you have requested could not be found.</h3>' .
str_repeat(' ', 512) str_repeat(' ', 512)
) )
->send(); ->send();
@ -449,18 +481,19 @@ class Engine {
/** /**
* Redirects the current request to another URL. * Redirects the current request to another URL.
* *
* @param string $url URL * @param string $url URL
* @param int $code HTTP status code * @param int $code HTTP status code
*/ */
public function _redirect($url, $code = 303) { public function _redirect(string $url, int $code = 303): void
{
$base = $this->get('flight.base_url'); $base = $this->get('flight.base_url');
if ($base === null) { if (null === $base) {
$base = $this->request()->base; $base = $this->request()->base;
} }
// Append base url to redirect url // Append base url to redirect url
if ($base != '/' && strpos($url, '://') === false) { if ('/' != $base && false === strpos($url, '://')) {
$url = $base . preg_replace('#/+#', '/', '/' . $url); $url = $base . preg_replace('#/+#', '/', '/' . $url);
} }
@ -474,16 +507,17 @@ class Engine {
/** /**
* Renders a template. * Renders a template.
* *
* @param string $file Template file * @param string $file Template file
* @param array $data Template data * @param array|null $data Template data
* @param string $key View variable name * @param string|null $key View variable name
* @throws \Exception *
* @throws Exception
*/ */
public function _render($file, $data = null, $key = null) { public function _render(string $file, ?array $data = null, ?string $key = null): void
if ($key !== null) { {
if (null !== $key) {
$this->view()->set($key, $this->view()->fetch($file, $data)); $this->view()->set($key, $this->view()->fetch($file, $data));
} } else {
else {
$this->view()->render($file, $data); $this->view()->render($file, $data);
} }
} }
@ -491,25 +525,26 @@ class Engine {
/** /**
* Sends a JSON response. * Sends a JSON response.
* *
* @param mixed $data JSON data * @param mixed $data JSON data
* @param int $code HTTP status code * @param int $code HTTP status code
* @param bool $encode Whether to perform JSON encoding * @param bool $encode Whether to perform JSON encoding
* @param string $charset Charset * @param string $charset Charset
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT * @param int $option Bitmask Json constant such as JSON_HEX_QUOT
* @throws \Exception *
* @throws Exception
*/ */
public function _json( public function _json(
$data, $data,
$code = 200, int $code = 200,
$encode = true, bool $encode = true,
$charset = 'utf-8', string $charset = 'utf-8',
$option = 0 int $option = 0
) { ): void {
$json = ($encode) ? json_encode($data, $option) : $data; $json = ($encode) ? json_encode($data, $option) : $data;
$this->response() $this->response()
->status($code) ->status($code)
->header('Content-Type', 'application/json; charset='.$charset) ->header('Content-Type', 'application/json; charset=' . $charset)
->write($json) ->write($json)
->send(); ->send();
} }
@ -517,41 +552,43 @@ class Engine {
/** /**
* Sends a JSONP response. * Sends a JSONP response.
* *
* @param mixed $data JSON data * @param mixed $data JSON data
* @param string $param Query parameter that specifies the callback name. * @param string $param Query parameter that specifies the callback name.
* @param int $code HTTP status code * @param int $code HTTP status code
* @param bool $encode Whether to perform JSON encoding * @param bool $encode Whether to perform JSON encoding
* @param string $charset Charset * @param string $charset Charset
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT * @param int $option Bitmask Json constant such as JSON_HEX_QUOT
* @throws \Exception *
* @throws Exception
*/ */
public function _jsonp( public function _jsonp(
$data, $data,
$param = 'jsonp', string $param = 'jsonp',
$code = 200, int $code = 200,
$encode = true, bool $encode = true,
$charset = 'utf-8', string $charset = 'utf-8',
$option = 0 int $option = 0
) { ): void {
$json = ($encode) ? json_encode($data, $option) : $data; $json = ($encode) ? json_encode($data, $option) : $data;
$callback = $this->request()->query[$param]; $callback = $this->request()->query[$param];
$this->response() $this->response()
->status($code) ->status($code)
->header('Content-Type', 'application/javascript; charset='.$charset) ->header('Content-Type', 'application/javascript; charset=' . $charset)
->write($callback.'('.$json.');') ->write($callback . '(' . $json . ');')
->send(); ->send();
} }
/** /**
* Handles ETag HTTP caching. * Handles ETag HTTP caching.
* *
* @param string $id ETag identifier * @param string $id ETag identifier
* @param string $type ETag type * @param string $type ETag type
*/ */
public function _etag($id, $type = 'strong') { public function _etag(string $id, string $type = 'strong'): void
$id = (($type === 'weak') ? 'W/' : '').$id; {
$id = (('weak' === $type) ? 'W/' : '') . $id;
$this->response()->header('ETag', $id); $this->response()->header('ETag', $id);
@ -566,7 +603,8 @@ class Engine {
* *
* @param int $time Unix timestamp * @param int $time Unix timestamp
*/ */
public function _lastModified($time) { public function _lastModified(int $time): void
{
$this->response()->header('Last-Modified', gmdate('D, d M Y H:i:s \G\M\T', $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']) && if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -6,10 +8,18 @@
* @license MIT, http://flightphp.com/license * @license MIT, http://flightphp.com/license
*/ */
use flight\core\Dispatcher;
use flight\Engine;
use flight\net\Request;
use flight\net\Response;
use flight\net\Router;
use flight\template\View;
/** /**
* The Flight class is a static representation of the framework. * The Flight class is a static representation of the framework.
* *
* Core. * Core.
*
* @method static void start() Starts the framework. * @method static void start() Starts the framework.
* @method static void path($path) Adds a path for autoloading classes. * @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 stop() Stops the framework and sends a response.
@ -17,7 +27,7 @@
* *
* Routing. * Routing.
* @method static void route($pattern, $callback) Maps a URL pattern to a callback. * @method static void route($pattern, $callback) Maps a URL pattern to a callback.
* @method static \flight\net\Router router() Returns Router instance. * @method static Router router() Returns Router instance.
* *
* Extending & Overriding. * Extending & Overriding.
* @method static void map($name, $callback) Creates a custom framework method. * @method static void map($name, $callback) Creates a custom framework method.
@ -35,11 +45,11 @@
* *
* Views. * Views.
* @method static void render($file, array $data = null, $key = null) Renders a template file. * @method static void render($file, array $data = null, $key = null) Renders a template file.
* @method static \flight\template\View view() Returns View instance. * @method static View view() Returns View instance.
* *
* Request & Response. * Request & Response.
* @method static \flight\net\Request request() Returns Request instance. * @method static Request request() Returns Request instance.
* @method static \flight\net\Response response() Returns Response instance. * @method static Response response() Returns Response instance.
* @method static void redirect($url, $code = 303) Redirects to another URL. * @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 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 jsonp($data, $param = 'jsonp', $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSONP response.
@ -50,43 +60,54 @@
* @method static void etag($id, $type = 'strong') Performs ETag HTTP caching. * @method static void etag($id, $type = 'strong') Performs ETag HTTP caching.
* @method static void lastModified($time) Performs last modified HTTP caching. * @method static void lastModified($time) Performs last modified HTTP caching.
*/ */
class Flight { class Flight
{
/** /**
* Framework engine. * Framework engine.
*
* @var \flight\Engine
*/ */
private static $engine; private static Engine $engine;
// Don't allow object instantiation // Don't allow object instantiation
private function __construct() {} private function __construct()
private function __destruct() {} {
private function __clone() {} }
private function __destruct()
{
}
private function __clone()
{
}
/** /**
* Handles calls to static methods. * Handles calls to static methods.
* *
* @param string $name Method name * @param string $name Method name
* @param array $params Method parameters * @param array $params Method parameters
*
* @throws Exception
*
* @return mixed Callback results * @return mixed Callback results
* @throws \Exception
*/ */
public static function __callStatic($name, $params) { public static function __callStatic(string $name, array $params)
$app = Flight::app(); {
$app = self::app();
return \flight\core\Dispatcher::invokeMethod(array($app, $name), $params); return Dispatcher::invokeMethod([$app, $name], $params);
} }
/** /**
* @return \flight\Engine Application instance * @return Engine Application instance
*/ */
public static function app() { public static function app(): Engine
{
static $initialized = false; static $initialized = false;
if (!$initialized) { if (!$initialized) {
require_once __DIR__.'/autoload.php'; require_once __DIR__ . '/autoload.php';
self::$engine = new \flight\Engine(); self::$engine = new Engine();
$initialized = true; $initialized = true;
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -6,6 +8,8 @@
* @license MIT, http://flightphp.com/license * @license MIT, http://flightphp.com/license
*/ */
require_once __DIR__.'/core/Loader.php'; use flight\core\Loader;
require_once __DIR__ . '/core/Loader.php';
\flight\core\Loader::autoload(true, dirname(__DIR__)); Loader::autoload(true, [dirname(__DIR__)]);

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -8,36 +10,38 @@
namespace flight\core; namespace flight\core;
use Exception;
/** /**
* The Dispatcher class is responsible for dispatching events. Events * The Dispatcher class is responsible for dispatching events. Events
* are simply aliases for class methods or functions. The Dispatcher * are simply aliases for class methods or functions. The Dispatcher
* allows you to hook other functions to an event that can modify the * allows you to hook other functions to an event that can modify the
* input parameters and/or the output. * input parameters and/or the output.
*/ */
class Dispatcher { class Dispatcher
{
/** /**
* Mapped events. * Mapped events.
*
* @var array
*/ */
protected $events = array(); protected array $events = [];
/** /**
* Method filters. * Method filters.
*
* @var array
*/ */
protected $filters = array(); protected array $filters = [];
/** /**
* Dispatches an event. * Dispatches an event.
* *
* @param string $name Event name * @param string $name Event name
* @param array $params Callback parameters * @param array $params Callback parameters
*
*@throws Exception
*
* @return string Output of callback * @return string Output of callback
* @throws \Exception
*/ */
public function run($name, array $params = array()) { public function run(string $name, array $params = []): ?string
{
$output = ''; $output = '';
// Run pre-filters // Run pre-filters
@ -59,10 +63,11 @@ class Dispatcher {
/** /**
* Assigns a callback to an event. * Assigns a callback to an event.
* *
* @param string $name Event name * @param string $name Event name
* @param callback $callback Callback function * @param callback $callback Callback function
*/ */
public function set($name, $callback) { public function set(string $name, callable $callback): void
{
$this->events[$name] = $callback; $this->events[$name] = $callback;
} }
@ -70,19 +75,23 @@ class Dispatcher {
* Gets an assigned callback. * Gets an assigned callback.
* *
* @param string $name Event name * @param string $name Event name
*
* @return callback $callback Callback function * @return callback $callback Callback function
*/ */
public function get($name) { public function get(string $name): ?callable
return isset($this->events[$name]) ? $this->events[$name] : null; {
return $this->events[$name] ?? null;
} }
/** /**
* Checks if an event has been set. * Checks if an event has been set.
* *
* @param string $name Event name * @param string $name Event name
*
* @return bool Event status * @return bool Event status
*/ */
public function has($name) { public function has(string $name): bool
{
return isset($this->events[$name]); return isset($this->events[$name]);
} }
@ -90,27 +99,28 @@ class Dispatcher {
* Clears an event. If no name is given, * Clears an event. If no name is given,
* all events are removed. * all events are removed.
* *
* @param string $name Event name * @param string|null $name Event name
*/ */
public function clear($name = null) { public function clear(?string $name = null): void
if ($name !== null) { {
if (null !== $name) {
unset($this->events[$name]); unset($this->events[$name]);
unset($this->filters[$name]); unset($this->filters[$name]);
} } else {
else { $this->events = [];
$this->events = array(); $this->filters = [];
$this->filters = array();
} }
} }
/** /**
* Hooks a callback to an event. * Hooks a callback to an event.
* *
* @param string $name Event name * @param string $name Event name
* @param string $type Filter type * @param string $type Filter type
* @param callback $callback Callback function * @param callback $callback Callback function
*/ */
public function hook($name, $type, $callback) { public function hook(string $name, string $type, callable $callback)
{
$this->filters[$name][$type][] = $callback; $this->filters[$name][$type][] = $callback;
} }
@ -118,15 +128,19 @@ class Dispatcher {
* Executes a chain of method filters. * Executes a chain of method filters.
* *
* @param array $filters Chain of filters * @param array $filters Chain of filters
* @param array $params Method parameters * @param array $params Method parameters
* @param mixed $output Method output * @param mixed $output Method output
* @throws \Exception *
* @throws Exception
*/ */
public function filter($filters, &$params, &$output) { public function filter(array $filters, array &$params, &$output): void
$args = array(&$params, &$output); {
$args = [&$params, &$output];
foreach ($filters as $callback) { foreach ($filters as $callback) {
$continue = $this->execute($callback, $args); $continue = $this->execute($callback, $args);
if ($continue === false) break; if (false === $continue) {
break;
}
} }
} }
@ -134,35 +148,39 @@ class Dispatcher {
* Executes a callback function. * Executes a callback function.
* *
* @param callback $callback Callback function * @param callback $callback Callback function
* @param array $params Function parameters * @param array $params Function parameters
*
*@throws Exception
*
* @return mixed Function results * @return mixed Function results
* @throws \Exception
*/ */
public static function execute($callback, array &$params = array()) { public static function execute(callable $callback, array &$params = [])
if (is_callable($callback)) { {
return is_array($callback) ? if (\is_callable($callback)) {
return \is_array($callback) ?
self::invokeMethod($callback, $params) : self::invokeMethod($callback, $params) :
self::callFunction($callback, $params); self::callFunction($callback, $params);
} }
else {
throw new \Exception('Invalid callback specified.'); throw new Exception('Invalid callback specified.');
}
} }
/** /**
* Calls a function. * Calls a function.
* *
* @param string $func Name of function to call * @param callable|string $func Name of function to call
* @param array $params Function parameters * @param array $params Function parameters
*
* @return mixed Function results * @return mixed Function results
*/ */
public static function callFunction($func, array &$params = array()) { public static function callFunction($func, array &$params = [])
{
// Call static method // Call static method
if (is_string($func) && strpos($func, '::') !== false) { if (\is_string($func) && false !== strpos($func, '::')) {
return call_user_func_array($func, $params); return \call_user_func_array($func, $params);
} }
switch (count($params)) { switch (\count($params)) {
case 0: case 0:
return $func(); return $func();
case 1: case 1:
@ -176,23 +194,25 @@ class Dispatcher {
case 5: case 5:
return $func($params[0], $params[1], $params[2], $params[3], $params[4]); return $func($params[0], $params[1], $params[2], $params[3], $params[4]);
default: default:
return call_user_func_array($func, $params); return \call_user_func_array($func, $params);
} }
} }
/** /**
* Invokes a method. * Invokes a method.
* *
* @param mixed $func Class method * @param mixed $func Class method
* @param array $params Class method parameters * @param array $params Class method parameters
*
* @return mixed Function results * @return mixed Function results
*/ */
public static function invokeMethod($func, array &$params = array()) { public static function invokeMethod($func, array &$params = [])
list($class, $method) = $func; {
[$class, $method] = $func;
$instance = is_object($class); $instance = \is_object($class);
switch (count($params)) { switch (\count($params)) {
case 0: case 0:
return ($instance) ? return ($instance) ?
$class->$method() : $class->$method() :
@ -218,15 +238,16 @@ class Dispatcher {
$class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) : $class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) :
$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: default:
return call_user_func_array($func, $params); return \call_user_func_array($func, $params);
} }
} }
/** /**
* Resets the object to the initial state. * Resets the object to the initial state.
*/ */
public function reset() { public function reset(): void
$this->events = array(); {
$this->filters = array(); $this->events = [];
$this->filters = [];
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -8,46 +10,46 @@
namespace flight\core; namespace flight\core;
use Exception;
use ReflectionClass;
use ReflectionException;
/** /**
* The Loader class is responsible for loading objects. It maintains * The Loader class is responsible for loading objects. It maintains
* a list of reusable class instances and can generate a new class * a list of reusable class instances and can generate a new class
* instances with custom initialization parameters. It also performs * instances with custom initialization parameters. It also performs
* class autoloading. * class autoloading.
*/ */
class Loader { class Loader
{
/** /**
* Registered classes. * Registered classes.
*
* @var array
*/ */
protected $classes = array(); protected array $classes = [];
/** /**
* Class instances. * Class instances.
*
* @var array
*/ */
protected $instances = array(); protected array $instances = [];
/** /**
* Autoload directories. * Autoload directories.
*
* @var array
*/ */
protected static $dirs = array(); protected static array $dirs = [];
/** /**
* Registers a class. * Registers a class.
* *
* @param string $name Registry name * @param string $name Registry name
* @param string|callable $class Class name or function to instantiate class * @param callable|string $class Class name or function to instantiate class
* @param array $params Class initialization parameters * @param array $params Class initialization parameters
* @param callback $callback Function to call after object instantiation * @param callable|null $callback $callback Function to call after object instantiation
*/ */
public function register($name, $class, array $params = array(), $callback = null) { public function register(string $name, $class, array $params = [], ?callable $callback = null): void
{
unset($this->instances[$name]); unset($this->instances[$name]);
$this->classes[$name] = array($class, $params, $callback); $this->classes[$name] = [$class, $params, $callback];
} }
/** /**
@ -55,23 +57,27 @@ class Loader {
* *
* @param string $name Registry name * @param string $name Registry name
*/ */
public function unregister($name) { public function unregister(string $name): void
{
unset($this->classes[$name]); unset($this->classes[$name]);
} }
/** /**
* Loads a registered class. * Loads a registered class.
* *
* @param string $name Method name * @param string $name Method name
* @param bool $shared Shared instance * @param bool $shared Shared instance
*
* @throws Exception
*
* @return object Class instance * @return object Class instance
* @throws \Exception
*/ */
public function load($name, $shared = true) { public function load(string $name, bool $shared = true): ?object
{
$obj = null; $obj = null;
if (isset($this->classes[$name])) { if (isset($this->classes[$name])) {
list($class, $params, $callback) = $this->classes[$name]; [$class, $params, $callback] = $this->classes[$name];
$exists = isset($this->instances[$name]); $exists = isset($this->instances[$name]);
@ -83,14 +89,13 @@ class Loader {
if (!$exists) { if (!$exists) {
$this->instances[$name] = $obj; $this->instances[$name] = $obj;
} }
} } else {
else {
$obj = $this->newInstance($class, $params); $obj = $this->newInstance($class, $params);
} }
if ($callback && (!$shared || !$exists)) { if ($callback && (!$shared || !$exists)) {
$ref = array(&$obj); $ref = [&$obj];
call_user_func_array($callback, $ref); \call_user_func_array($callback, $ref);
} }
} }
@ -101,26 +106,31 @@ class Loader {
* Gets a single instance of a class. * Gets a single instance of a class.
* *
* @param string $name Instance name * @param string $name Instance name
*
* @return object Class instance * @return object Class instance
*/ */
public function getInstance($name) { public function getInstance(string $name): ?object
return isset($this->instances[$name]) ? $this->instances[$name] : null; {
return $this->instances[$name] ?? null;
} }
/** /**
* Gets a new instance of a class. * Gets a new instance of a class.
* *
* @param string|callable $class Class name or callback function to instantiate class * @param callable|string $class Class name or callback function to instantiate class
* @param array $params Class initialization parameters * @param array $params Class initialization parameters
*
* @throws Exception
*
* @return object Class instance * @return object Class instance
* @throws \Exception
*/ */
public function newInstance($class, array $params = array()) { public function newInstance($class, array $params = []): object
if (is_callable($class)) { {
return call_user_func_array($class, $params); if (\is_callable($class)) {
return \call_user_func_array($class, $params);
} }
switch (count($params)) { switch (\count($params)) {
case 0: case 0:
return new $class(); return new $class();
case 1: case 1:
@ -135,44 +145,48 @@ class Loader {
return new $class($params[0], $params[1], $params[2], $params[3], $params[4]); return new $class($params[0], $params[1], $params[2], $params[3], $params[4]);
default: default:
try { try {
$refClass = new \ReflectionClass($class); $refClass = new ReflectionClass($class);
return $refClass->newInstanceArgs($params); return $refClass->newInstanceArgs($params);
} catch (\ReflectionException $e) { } catch (ReflectionException $e) {
throw new \Exception("Cannot instantiate {$class}", 0, $e); throw new Exception("Cannot instantiate {$class}", 0, $e);
} }
} }
} }
/** /**
* @param string $name Registry name * @param string $name Registry name
*
* @return mixed Class information or null if not registered * @return mixed Class information or null if not registered
*/ */
public function get($name) { public function get(string $name)
return isset($this->classes[$name]) ? $this->classes[$name] : null; {
return $this->classes[$name] ?? null;
} }
/** /**
* Resets the object to the initial state. * Resets the object to the initial state.
*/ */
public function reset() { public function reset(): void
$this->classes = array(); {
$this->instances = array(); $this->classes = [];
$this->instances = [];
} }
/*** Autoloading Functions ***/ // Autoloading Functions
/** /**
* Starts/stops autoloader. * Starts/stops autoloader.
* *
* @param bool $enabled Enable/disable autoloading * @param bool $enabled Enable/disable autoloading
* @param array $dirs Autoload directories * @param array $dirs Autoload directories
*/ */
public static function autoload($enabled = true, $dirs = array()) { public static function autoload(bool $enabled = true, array $dirs = []): void
{
if ($enabled) { if ($enabled) {
spl_autoload_register(array(__CLASS__, 'loadClass')); spl_autoload_register([__CLASS__, 'loadClass']);
} } else {
else { spl_autoload_unregister([__CLASS__, 'loadClass']);
spl_autoload_unregister(array(__CLASS__, 'loadClass'));
} }
if (!empty($dirs)) { if (!empty($dirs)) {
@ -185,13 +199,15 @@ class Loader {
* *
* @param string $class Class name * @param string $class Class name
*/ */
public static function loadClass($class) { public static function loadClass(string $class): void
$class_file = str_replace(array('\\', '_'), '/', $class).'.php'; {
$class_file = str_replace(['\\', '_'], '/', $class) . '.php';
foreach (self::$dirs as $dir) { foreach (self::$dirs as $dir) {
$file = $dir.'/'.$class_file; $file = $dir . '/' . $class_file;
if (file_exists($file)) { if (file_exists($file)) {
require $file; require $file;
return; return;
} }
} }
@ -202,14 +218,16 @@ class Loader {
* *
* @param mixed $dir Directory path * @param mixed $dir Directory path
*/ */
public static function addDirectory($dir) { public static function addDirectory($dir): void
if (is_array($dir) || is_object($dir)) { {
if (\is_array($dir) || \is_object($dir)) {
foreach ($dir as $value) { foreach ($dir as $value) {
self::addDirectory($value); self::addDirectory($value);
} }
} } elseif (\is_string($dir)) {
else if (is_string($dir)) { if (!\in_array($dir, self::$dirs, true)) {
if (!in_array($dir, self::$dirs)) self::$dirs[] = $dir; self::$dirs[] = $dir;
}
} }
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -34,112 +36,114 @@ use flight\util\Collection;
* accept - HTTP accept parameters * accept - HTTP accept parameters
* proxy_ip - Proxy IP address of the client * proxy_ip - Proxy IP address of the client
*/ */
class Request { final class Request
{
/** /**
* @var string URL being requested * @var string URL being requested
*/ */
public $url; public string $url;
/** /**
* @var string Parent subdirectory of the URL * @var string Parent subdirectory of the URL
*/ */
public $base; public string $base;
/** /**
* @var string Request method (GET, POST, PUT, DELETE) * @var string Request method (GET, POST, PUT, DELETE)
*/ */
public $method; public string $method;
/** /**
* @var string Referrer URL * @var string Referrer URL
*/ */
public $referrer; public string $referrer;
/** /**
* @var string IP address of the client * @var string IP address of the client
*/ */
public $ip; public string $ip;
/** /**
* @var bool Whether the request is an AJAX request * @var bool Whether the request is an AJAX request
*/ */
public $ajax; public bool $ajax;
/** /**
* @var string Server protocol (http, https) * @var string Server protocol (http, https)
*/ */
public $scheme; public string $scheme;
/** /**
* @var string Browser information * @var string Browser information
*/ */
public $user_agent; public string $user_agent;
/** /**
* @var string Content type * @var string Content type
*/ */
public $type; public string $type;
/** /**
* @var int Content length * @var int Content length
*/ */
public $length; public int $length;
/** /**
* @var \flight\util\Collection Query string parameters * @var Collection Query string parameters
*/ */
public $query; public Collection $query;
/** /**
* @var \flight\util\Collection Post parameters * @var Collection Post parameters
*/ */
public $data; public Collection $data;
/** /**
* @var \flight\util\Collection Cookie parameters * @var Collection Cookie parameters
*/ */
public $cookies; public Collection $cookies;
/** /**
* @var \flight\util\Collection Uploaded files * @var Collection Uploaded files
*/ */
public $files; public Collection $files;
/** /**
* @var bool Whether the connection is secure * @var bool Whether the connection is secure
*/ */
public $secure; public bool $secure;
/** /**
* @var string HTTP accept parameters * @var string HTTP accept parameters
*/ */
public $accept; public string $accept;
/** /**
* @var string Proxy IP address of the client * @var string Proxy IP address of the client
*/ */
public $proxy_ip; public string $proxy_ip;
/** /**
* @var string HTTP host name * @var string HTTP host name
*/ */
public $host; public string $host;
/** /**
* Constructor. * Constructor.
* *
* @param array $config Request configuration * @param array $config Request configuration
*/ */
public function __construct($config = array()) { public function __construct(array $config = [])
{
// Default properties // Default properties
if (empty($config)) { if (empty($config)) {
$config = array( $config = [
'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')), 'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')),
'base' => str_replace(array('\\',' '), array('/','%20'), dirname(self::getVar('SCRIPT_NAME'))), 'base' => str_replace(['\\', ' '], ['/', '%20'], \dirname(self::getVar('SCRIPT_NAME'))),
'method' => self::getMethod(), 'method' => self::getMethod(),
'referrer' => self::getVar('HTTP_REFERER'), 'referrer' => self::getVar('HTTP_REFERER'),
'ip' => self::getVar('REMOTE_ADDR'), 'ip' => self::getVar('REMOTE_ADDR'),
'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest', 'ajax' => 'XMLHttpRequest' == self::getVar('HTTP_X_REQUESTED_WITH'),
'scheme' => self::getScheme(), 'scheme' => self::getScheme(),
'user_agent' => self::getVar('HTTP_USER_AGENT'), 'user_agent' => self::getVar('HTTP_USER_AGENT'),
'type' => self::getVar('CONTENT_TYPE'), 'type' => self::getVar('CONTENT_TYPE'),
@ -148,11 +152,11 @@ class Request {
'data' => new Collection($_POST), 'data' => new Collection($_POST),
'cookies' => new Collection($_COOKIE), 'cookies' => new Collection($_COOKIE),
'files' => new Collection($_FILES), 'files' => new Collection($_FILES),
'secure' => self::getScheme() == 'https', 'secure' => 'https' == self::getScheme(),
'accept' => self::getVar('HTTP_ACCEPT'), 'accept' => self::getVar('HTTP_ACCEPT'),
'proxy_ip' => self::getProxyIpAddress(), 'proxy_ip' => self::getProxyIpAddress(),
'host' => self::getVar('HTTP_HOST'), 'host' => self::getVar('HTTP_HOST'),
); ];
} }
$this->init($config); $this->init($config);
@ -163,15 +167,16 @@ class Request {
* *
* @param array $properties Array of request properties * @param array $properties Array of request properties
*/ */
public function init($properties = array()) { public function init(array $properties = [])
{
// Set all the defined properties // Set all the defined properties
foreach ($properties as $name => $value) { foreach ($properties as $name => $value) {
$this->$name = $value; $this->$name = $value;
} }
// Get the requested URL without the base directory // Get the requested URL without the base directory
if ($this->base != '/' && strlen($this->base) > 0 && strpos($this->url, $this->base) === 0) { if ('/' != $this->base && \strlen($this->base) > 0 && 0 === strpos($this->url, $this->base)) {
$this->url = substr($this->url, strlen($this->base)); $this->url = substr($this->url, \strlen($this->base));
} }
// Default url // Default url
@ -186,11 +191,11 @@ class Request {
} }
// Check for JSON input // Check for JSON input
if (strpos($this->type, 'application/json') === 0) { if (0 === strpos($this->type, 'application/json')) {
$body = $this->getBody(); $body = $this->getBody();
if ($body != '') { if ('' != $body) {
$data = json_decode($body, true); $data = json_decode($body, true);
if ($data != null) { if (null != $data) {
$this->data->setData($data); $this->data->setData($data);
} }
} }
@ -202,16 +207,17 @@ class Request {
* *
* @return string Raw HTTP request body * @return string Raw HTTP request body
*/ */
public static function getBody() { public static function getBody(): ?string
{
static $body; static $body;
if (!is_null($body)) { if (null !== $body) {
return $body; return $body;
} }
$method = self::getMethod(); $method = self::getMethod();
if ($method == 'POST' || $method == 'PUT' || $method == 'DELETE' || $method == 'PATCH') { if ('POST' === $method || 'PUT' === $method || 'DELETE' === $method || 'PATCH' === $method) {
$body = file_get_contents('php://input'); $body = file_get_contents('php://input');
} }
@ -220,16 +226,14 @@ class Request {
/** /**
* Gets the request method. * Gets the request method.
*
* @return string
*/ */
public static function getMethod() { public static function getMethod(): string
{
$method = self::getVar('REQUEST_METHOD', 'GET'); $method = self::getVar('REQUEST_METHOD', 'GET');
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) { if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE']; $method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
} } elseif (isset($_REQUEST['_method'])) {
elseif (isset($_REQUEST['_method'])) {
$method = $_REQUEST['_method']; $method = $_REQUEST['_method'];
} }
@ -241,22 +245,23 @@ class Request {
* *
* @return string IP address * @return string IP address
*/ */
public static function getProxyIpAddress() { public static function getProxyIpAddress(): string
static $forwarded = array( {
static $forwarded = [
'HTTP_CLIENT_IP', 'HTTP_CLIENT_IP',
'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED_FOR',
'HTTP_X_FORWARDED', 'HTTP_X_FORWARDED',
'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_X_CLUSTER_CLIENT_IP',
'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED_FOR',
'HTTP_FORWARDED' 'HTTP_FORWARDED',
); ];
$flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE; $flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE;
foreach ($forwarded as $key) { foreach ($forwarded as $key) {
if (array_key_exists($key, $_SERVER)) { if (\array_key_exists($key, $_SERVER)) {
sscanf($_SERVER[$key], '%[^,]', $ip); sscanf($_SERVER[$key], '%[^,]', $ip);
if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) { if (false !== filter_var($ip, \FILTER_VALIDATE_IP, $flags)) {
return $ip; return $ip;
} }
} }
@ -268,22 +273,26 @@ class Request {
/** /**
* Gets a variable from $_SERVER using $default if not provided. * Gets a variable from $_SERVER using $default if not provided.
* *
* @param string $var Variable name * @param string $var Variable name
* @param string $default Default value to substitute * @param mixed $default Default value to substitute
* @return string Server variable value *
* @return mixed Server variable value
*/ */
public static function getVar($var, $default = '') { public static function getVar(string $var, $default = '')
return isset($_SERVER[$var]) ? $_SERVER[$var] : $default; {
return $_SERVER[$var] ?? $default;
} }
/** /**
* Parse query parameters from a URL. * Parse query parameters from a URL.
* *
* @param string $url URL string * @param string $url URL string
*
* @return array Query parameters * @return array Query parameters
*/ */
public static function parseQuery($url) { public static function parseQuery(string $url): array
$params = array(); {
$params = [];
$args = parse_url($url); $args = parse_url($url);
if (isset($args['query'])) { if (isset($args['query'])) {
@ -293,18 +302,20 @@ class Request {
return $params; return $params;
} }
public static function getScheme() { public static function getScheme(): string
{
if ( if (
(isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) === 'on') (isset($_SERVER['HTTPS']) && 'on' === strtolower($_SERVER['HTTPS']))
|| ||
(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'])
|| ||
(isset($_SERVER['HTTP_FRONT_END_HTTPS']) && $_SERVER['HTTP_FRONT_END_HTTPS'] === 'on') (isset($_SERVER['HTTP_FRONT_END_HTTPS']) && 'on' === $_SERVER['HTTP_FRONT_END_HTTPS'])
|| ||
(isset($_SERVER['REQUEST_SCHEME']) && $_SERVER['REQUEST_SCHEME'] === 'https') (isset($_SERVER['REQUEST_SCHEME']) && 'https' === $_SERVER['REQUEST_SCHEME'])
) { ) {
return 'https'; return 'https';
} }
return 'http'; return 'http';
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -13,38 +15,19 @@ namespace flight\net;
* contains the response headers, HTTP status code, and response * contains the response headers, HTTP status code, and response
* body. * body.
*/ */
class Response { 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 * header Content-Length.
* *
* @var boolean * @var bool
*/ */
public $content_length = true; public $content_length = true;
/** /**
* @var array HTTP status codes * @var array HTTP status codes
*/ */
public static $codes = array( public static $codes = [
100 => 'Continue', 100 => 'Continue',
101 => 'Switching Protocols', 101 => 'Switching Protocols',
102 => 'Processing', 102 => 'Processing',
@ -112,25 +95,46 @@ class Response {
508 => 'Loop Detected', 508 => 'Loop Detected',
510 => 'Not Extended', 510 => 'Not Extended',
511 => 'Network Authentication Required' 511 => 'Network Authentication Required',
); ];
/**
* @var int HTTP status
*/
protected $status = 200;
/**
* @var array HTTP headers
*/
protected $headers = [];
/**
* @var string HTTP response body
*/
protected $body;
/**
* @var bool HTTP response sent
*/
protected $sent = false;
/** /**
* Sets the HTTP status of the response. * Sets the HTTP status of the response.
* *
* @param int $code HTTP status code. * @param int $code HTTP status code.
* @return object|int Self reference *
* @throws \Exception If invalid status code * @throws \Exception If invalid status code
*
* @return int|object Self reference
*/ */
public function status($code = null) { public function status($code = null)
if ($code === null) { {
if (null === $code) {
return $this->status; return $this->status;
} }
if (array_key_exists($code, self::$codes)) { if (\array_key_exists($code, self::$codes)) {
$this->status = $code; $this->status = $code;
} } else {
else {
throw new \Exception('Invalid status code.'); throw new \Exception('Invalid status code.');
} }
@ -140,17 +144,18 @@ class Response {
/** /**
* Adds a header to the response. * Adds a header to the response.
* *
* @param string|array $name Header name or array of names and values * @param array|string $name Header name or array of names and values
* @param string $value Header value * @param string $value Header value
*
* @return object Self reference * @return object Self reference
*/ */
public function header($name, $value = null) { public function header($name, $value = null)
if (is_array($name)) { {
if (\is_array($name)) {
foreach ($name as $k => $v) { foreach ($name as $k => $v) {
$this->headers[$k] = $v; $this->headers[$k] = $v;
} }
} } else {
else {
$this->headers[$name] = $value; $this->headers[$name] = $value;
} }
@ -158,10 +163,12 @@ class Response {
} }
/** /**
* Returns the headers from the response * Returns the headers from the response.
*
* @return array * @return array
*/ */
public function headers() { public function headers()
{
return $this->headers; return $this->headers;
} }
@ -169,9 +176,11 @@ class Response {
* Writes content to the response body. * Writes content to the response body.
* *
* @param string $str Response content * @param string $str Response content
*
* @return object Self reference * @return object Self reference
*/ */
public function write($str) { public function write($str)
{
$this->body .= $str; $this->body .= $str;
return $this; return $this;
@ -182,9 +191,10 @@ class Response {
* *
* @return object Self reference * @return object Self reference
*/ */
public function clear() { public function clear()
{
$this->status = 200; $this->status = 200;
$this->headers = array(); $this->headers = [];
$this->body = ''; $this->body = '';
return $this; return $this;
@ -194,26 +204,28 @@ class Response {
* Sets caching headers for the response. * Sets caching headers for the response.
* *
* @param int|string $expires Expiration time * @param int|string $expires Expiration time
*
* @return object Self reference * @return object Self reference
*/ */
public function cache($expires) { public function cache($expires)
if ($expires === false) { {
if (false === $expires) {
$this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT'; $this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
$this->headers['Cache-Control'] = array( $this->headers['Cache-Control'] = [
'no-store, no-cache, must-revalidate', 'no-store, no-cache, must-revalidate',
'post-check=0, pre-check=0', 'post-check=0, pre-check=0',
'max-age=0' 'max-age=0',
); ];
$this->headers['Pragma'] = 'no-cache'; $this->headers['Pragma'] = 'no-cache';
} } else {
else { $expires = \is_int($expires) ? $expires : strtotime($expires);
$expires = is_int($expires) ? $expires : strtotime($expires);
$this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT'; $this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT';
$this->headers['Cache-Control'] = 'max-age='.($expires - time()); $this->headers['Cache-Control'] = 'max-age=' . ($expires - time());
if (isset($this->headers['Pragma']) && $this->headers['Pragma'] == 'no-cache'){ if (isset($this->headers['Pragma']) && 'no-cache' == $this->headers['Pragma']) {
unset($this->headers['Pragma']); unset($this->headers['Pragma']);
} }
} }
return $this; return $this;
} }
@ -222,9 +234,10 @@ class Response {
* *
* @return object Self reference * @return object Self reference
*/ */
public function sendHeaders() { public function sendHeaders()
{
// Send status code header // Send status code header
if (strpos(php_sapi_name(), 'cgi') !== false) { if (false !== strpos(\PHP_SAPI, 'cgi')) {
header( header(
sprintf( sprintf(
'Status: %d %s', 'Status: %d %s',
@ -233,12 +246,11 @@ class Response {
), ),
true true
); );
} } else {
else {
header( header(
sprintf( sprintf(
'%s %d %s', '%s %d %s',
(isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'), ($_SERVER['SERVER_PROTOCOL'] ?? 'HTTP/1.1'),
$this->status, $this->status,
self::$codes[$this->status]), self::$codes[$this->status]),
true, true,
@ -248,13 +260,12 @@ class Response {
// Send other headers // Send other headers
foreach ($this->headers as $field => $value) { foreach ($this->headers as $field => $value) {
if (is_array($value)) { if (\is_array($value)) {
foreach ($value as $v) { foreach ($value as $v) {
header($field.': '.$v, false); header($field . ': ' . $v, false);
} }
} } else {
else { header($field . ': ' . $value);
header($field.': '.$value);
} }
} }
@ -263,7 +274,7 @@ class Response {
$length = $this->getContentLength(); $length = $this->getContentLength();
if ($length > 0) { if ($length > 0) {
header('Content-Length: '.$length); header('Content-Length: ' . $length);
} }
} }
@ -275,23 +286,26 @@ class Response {
* *
* @return string Content length * @return string Content length
*/ */
public function getContentLength() { public function getContentLength()
return extension_loaded('mbstring') ? {
return \extension_loaded('mbstring') ?
mb_strlen($this->body, 'latin1') : mb_strlen($this->body, 'latin1') :
strlen($this->body); \strlen($this->body);
} }
/** /**
* Gets whether response was sent. * Gets whether response was sent.
*/ */
public function sent() { public function sent()
{
return $this->sent; return $this->sent;
} }
/** /**
* Sends a HTTP response. * Sends a HTTP response.
*/ */
public function send() { public function send()
{
if (ob_get_length() > 0) { if (ob_get_length() > 0) {
ob_end_clean(); ob_end_clean();
} }
@ -305,4 +319,3 @@ class Response {
$this->sent = true; $this->sent = true;
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -13,11 +15,12 @@ namespace flight\net;
* an assigned callback function. The Router tries to match the * an assigned callback function. The Router tries to match the
* requested URL against a series of URL patterns. * requested URL against a series of URL patterns.
*/ */
class Route { final class Route
{
/** /**
* @var string URL pattern * @var string URL pattern
*/ */
public $pattern; public string $pattern;
/** /**
* @var mixed Callback function * @var mixed Callback function
@ -27,37 +30,38 @@ class Route {
/** /**
* @var array HTTP methods * @var array HTTP methods
*/ */
public $methods = array(); public array $methods = [];
/** /**
* @var array Route parameters * @var array Route parameters
*/ */
public $params = array(); public array $params = [];
/** /**
* @var string Matching regular expression * @var string|null Matching regular expression
*/ */
public $regex; public ?string $regex;
/** /**
* @var string URL splat content * @var string URL splat content
*/ */
public $splat = ''; public string $splat = '';
/** /**
* @var boolean Pass self in callback parameters * @var bool Pass self in callback parameters
*/ */
public $pass = false; public bool $pass = false;
/** /**
* Constructor. * Constructor.
* *
* @param string $pattern URL pattern * @param string $pattern URL pattern
* @param mixed $callback Callback function * @param mixed $callback Callback function
* @param array $methods HTTP methods * @param array $methods HTTP methods
* @param boolean $pass Pass self in callback parameters * @param bool $pass Pass self in callback parameters
*/ */
public function __construct($pattern, $callback, $methods, $pass) { public function __construct(string $pattern, $callback, array $methods, bool $pass)
{
$this->pattern = $pattern; $this->pattern = $pattern;
$this->callback = $callback; $this->callback = $callback;
$this->methods = $methods; $this->methods = $methods;
@ -67,61 +71,67 @@ class Route {
/** /**
* Checks if a URL matches the route pattern. Also parses named parameters in the URL. * Checks if a URL matches the route pattern. Also parses named parameters in the URL.
* *
* @param string $url Requested URL * @param string $url Requested URL
* @param boolean $case_sensitive Case sensitive matching * @param bool $case_sensitive Case sensitive matching
* @return boolean Match status *
* @return bool Match status
*/ */
public function matchUrl($url, $case_sensitive = false) { public function matchUrl(string $url, bool $case_sensitive = false): bool
{
// Wildcard or exact match // Wildcard or exact match
if ($this->pattern === '*' || $this->pattern === $url) { if ('*' === $this->pattern || $this->pattern === $url) {
return true; return true;
} }
$ids = array(); $ids = [];
$last_char = substr($this->pattern, -1); $last_char = substr($this->pattern, -1);
// Get splat // Get splat
if ($last_char === '*') { if ('*' === $last_char) {
$n = 0; $n = 0;
$len = strlen($url); $len = \strlen($url);
$count = substr_count($this->pattern, '/'); $count = substr_count($this->pattern, '/');
for ($i = 0; $i < $len; $i++) { for ($i = 0; $i < $len; $i++) {
if ($url[$i] == '/') $n++; if ('/' === $url[$i]) {
if ($n == $count) break; $n++;
}
if ($n === $count) {
break;
}
} }
$this->splat = (string)substr($url, $i+1); $this->splat = (string) substr($url, $i + 1);
} }
// Build the regex for matching // Build the regex for matching
$regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern); $regex = str_replace([')', '/*'], [')?', '(/?|/.*?)'], $this->pattern);
$regex = preg_replace_callback( $regex = preg_replace_callback(
'#@([\w]+)(:([^/\(\)]*))?#', '#@([\w]+)(:([^/\(\)]*))?#',
function($matches) use (&$ids) { static function ($matches) use (&$ids) {
$ids[$matches[1]] = null; $ids[$matches[1]] = null;
if (isset($matches[3])) { if (isset($matches[3])) {
return '(?P<'.$matches[1].'>'.$matches[3].')'; return '(?P<' . $matches[1] . '>' . $matches[3] . ')';
} }
return '(?P<'.$matches[1].'>[^/\?]+)';
return '(?P<' . $matches[1] . '>[^/\?]+)';
}, },
$regex $regex
); );
// Fix trailing slash // Fix trailing slash
if ($last_char === '/') { if ('/' === $last_char) {
$regex .= '?'; $regex .= '?';
} } // Allow trailing slash
// Allow trailing slash
else { else {
$regex .= '/?'; $regex .= '/?';
} }
// Attempt to match route and named parameters // Attempt to match route and named parameters
if (preg_match('#^'.$regex.'(?:\?.*)?$#'.(($case_sensitive) ? '' : 'i'), $url, $matches)) { if (preg_match('#^' . $regex . '(?:\?.*)?$#' . (($case_sensitive) ? '' : 'i'), $url, $matches)) {
foreach ($ids as $k => $v) { foreach ($ids as $k => $v) {
$this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null; $this->params[$k] = (\array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
} }
$this->regex = $regex; $this->regex = $regex;
@ -136,9 +146,11 @@ class Route {
* Checks if an HTTP method matches the route methods. * Checks if an HTTP method matches the route methods.
* *
* @param string $method HTTP method * @param string $method HTTP method
*
* @return bool Match status * @return bool Match status
*/ */
public function matchMethod($method) { public function matchMethod(string $method): bool
return count(array_intersect(array($method, '*'), $this->methods)) > 0; {
return \count(array_intersect([$method, '*'], $this->methods)) > 0;
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -13,57 +15,54 @@ namespace flight\net;
* an assigned callback function. The Router tries to match the * an assigned callback function. The Router tries to match the
* requested URL against a series of URL patterns. * requested URL against a series of URL patterns.
*/ */
class Router { class Router
{
/** /**
* Mapped routes. * Case sensitive matching.
*
* @var array
*/ */
protected $routes = array(); public bool $case_sensitive = false;
/** /**
* Pointer to current route. * Mapped routes.
*
* @var int
*/ */
protected $index = 0; protected array $routes = [];
/** /**
* Case sensitive matching. * Pointer to current route.
*
* @var boolean
*/ */
public $case_sensitive = false; protected int $index = 0;
/** /**
* Gets mapped routes. * Gets mapped routes.
* *
* @return array Array of routes * @return array Array of routes
*/ */
public function getRoutes() { public function getRoutes(): array
{
return $this->routes; return $this->routes;
} }
/** /**
* Clears all routes in the router. * Clears all routes in the router.
*/ */
public function clear() { public function clear(): void
$this->routes = array(); {
$this->routes = [];
} }
/** /**
* Maps a URL pattern to a callback function. * Maps a URL pattern to a callback function.
* *
* @param string $pattern URL pattern to match * @param string $pattern URL pattern to match
* @param callback $callback Callback function * @param callback $callback Callback function
* @param boolean $pass_route Pass the matching route object to the callback * @param bool $pass_route Pass the matching route object to the callback
*/ */
public function map($pattern, $callback, $pass_route = false) { public function map(string $pattern, callable $callback, bool $pass_route = false): void
{
$url = $pattern; $url = $pattern;
$methods = array('*'); $methods = ['*'];
if (strpos($pattern, ' ') !== false) { if (false !== strpos($pattern, ' ')) {
list($method, $url) = explode(' ', trim($pattern), 2); [$method, $url] = explode(' ', trim($pattern), 2);
$url = trim($url); $url = trim($url);
$methods = explode('|', $method); $methods = explode('|', $method);
} }
@ -75,12 +74,14 @@ class Router {
* Routes the current request. * Routes the current request.
* *
* @param Request $request Request object * @param Request $request Request object
* @return Route|bool Matching route or false if no match *
* @return bool|Route Matching route or false if no match
*/ */
public function route(Request $request) { public function route(Request $request)
$url_decoded = urldecode( $request->url ); {
$url_decoded = urldecode($request->url);
while ($route = $this->current()) { while ($route = $this->current()) {
if ($route !== false && $route->matchMethod($request->method) && $route->matchUrl($url_decoded, $this->case_sensitive)) { if (false !== $route && $route->matchMethod($request->method) && $route->matchUrl($url_decoded, $this->case_sensitive)) {
return $route; return $route;
} }
$this->next(); $this->next();
@ -92,26 +93,26 @@ class Router {
/** /**
* Gets the current route. * Gets the current route.
* *
* @return Route * @return bool|Route
*/ */
public function current() { public function current()
return isset($this->routes[$this->index]) ? $this->routes[$this->index] : false; {
return $this->routes[$this->index] ?? false;
} }
/** /**
* Gets the next route. * Gets the next route.
*
* @return Route
*/ */
public function next() { public function next(): void
{
$this->index++; $this->index++;
} }
/** /**
* Reset to the first route. * Reset to the first route.
*/ */
public function reset() { public function reset(): void
{
$this->index = 0; $this->index = 0;
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -13,7 +15,8 @@ namespace flight\template;
* methods for managing view data and inserts the data into * methods for managing view data and inserts the data into
* view templates upon rendering. * view templates upon rendering.
*/ */
class View { class View
{
/** /**
* Location of view templates. * Location of view templates.
* *
@ -33,7 +36,7 @@ class View {
* *
* @var array * @var array
*/ */
protected $vars = array(); protected $vars = [];
/** /**
* Template file. * Template file.
@ -47,7 +50,8 @@ class View {
* *
* @param string $path Path to templates directory * @param string $path Path to templates directory
*/ */
public function __construct($path = '.') { public function __construct($path = '.')
{
$this->path = $path; $this->path = $path;
} }
@ -55,25 +59,27 @@ class View {
* Gets a template variable. * Gets a template variable.
* *
* @param string $key Key * @param string $key Key
*
* @return mixed Value * @return mixed Value
*/ */
public function get($key) { public function get($key)
return isset($this->vars[$key]) ? $this->vars[$key] : null; {
return $this->vars[$key] ?? null;
} }
/** /**
* Sets a template variable. * Sets a template variable.
* *
* @param mixed $key Key * @param mixed $key Key
* @param string $value Value * @param string $value Value
*/ */
public function set($key, $value = null) { public function set($key, $value = null)
if (is_array($key) || is_object($key)) { {
if (\is_array($key) || \is_object($key)) {
foreach ($key as $k => $v) { foreach ($key as $k => $v) {
$this->vars[$k] = $v; $this->vars[$k] = $v;
} }
} } else {
else {
$this->vars[$key] = $value; $this->vars[$key] = $value;
} }
} }
@ -82,9 +88,11 @@ class View {
* Checks if a template variable is set. * Checks if a template variable is set.
* *
* @param string $key Key * @param string $key Key
* @return boolean If key exists *
* @return bool If key exists
*/ */
public function has($key) { public function has($key)
{
return isset($this->vars[$key]); return isset($this->vars[$key]);
} }
@ -93,11 +101,11 @@ class View {
* *
* @param string $key Key * @param string $key Key
*/ */
public function clear($key = null) { public function clear($key = null)
if (is_null($key)) { {
$this->vars = array(); if (null === $key) {
} $this->vars = [];
else { } else {
unset($this->vars[$key]); unset($this->vars[$key]);
} }
} }
@ -106,17 +114,19 @@ class View {
* Renders a template. * Renders a template.
* *
* @param string $file Template file * @param string $file Template file
* @param array $data Template data * @param array $data Template data
*
* @throws \Exception If template not found * @throws \Exception If template not found
*/ */
public function render($file, $data = null) { public function render($file, $data = null)
{
$this->template = $this->getTemplate($file); $this->template = $this->getTemplate($file);
if (!file_exists($this->template)) { if (!file_exists($this->template)) {
throw new \Exception("Template file not found: {$this->template}."); throw new \Exception("Template file not found: {$this->template}.");
} }
if (is_array($data)) { if (\is_array($data)) {
$this->vars = array_merge($this->vars, $data); $this->vars = array_merge($this->vars, $data);
} }
@ -129,25 +139,28 @@ class View {
* Gets the output of a template. * Gets the output of a template.
* *
* @param string $file Template file * @param string $file Template file
* @param array $data Template data * @param array $data Template data
*
* @return string Output of template * @return string Output of template
*/ */
public function fetch($file, $data = null) { public function fetch($file, $data = null)
{
ob_start(); ob_start();
$this->render($file, $data); $this->render($file, $data);
$output = ob_get_clean();
return $output; return ob_get_clean();
} }
/** /**
* Checks if a template file exists. * Checks if a template file exists.
* *
* @param string $file Template file * @param string $file Template file
*
* @return bool Template file exists * @return bool Template file exists
*/ */
public function exists($file) { public function exists($file)
{
return file_exists($this->getTemplate($file)); return file_exists($this->getTemplate($file));
} }
@ -155,30 +168,33 @@ class View {
* Gets the full path to a template file. * Gets the full path to a template file.
* *
* @param string $file Template file * @param string $file Template file
*
* @return string Template file location * @return string Template file location
*/ */
public function getTemplate($file) { public function getTemplate($file)
{
$ext = $this->extension; $ext = $this->extension;
if (!empty($ext) && (substr($file, -1 * strlen($ext)) != $ext)) { if (!empty($ext) && (substr($file, -1 * \strlen($ext)) != $ext)) {
$file .= $ext; $file .= $ext;
} }
if ((substr($file, 0, 1) == '/')) { if (('/' == substr($file, 0, 1))) {
return $file; return $file;
} }
return $this->path.'/'.$file; return $this->path . '/' . $file;
} }
/** /**
* Displays escaped output. * Displays escaped output.
* *
* @param string $str String to escape * @param string $str String to escape
*
* @return string Escaped string * @return string Escaped string
*/ */
public function e($str) { public function e($str)
{
echo htmlentities($str); echo htmlentities($str);
} }
} }

@ -1,4 +1,6 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
@ -8,28 +10,34 @@
namespace flight\util; namespace flight\util;
use ArrayAccess;
use function count;
use Countable;
use Iterator;
use JsonSerializable;
if (!interface_exists('JsonSerializable')) { if (!interface_exists('JsonSerializable')) {
require_once dirname(__FILE__) . '/LegacyJsonSerializable.php'; require_once __DIR__ . '/LegacyJsonSerializable.php';
} }
/** /**
* The Collection class allows you to access a set of data * The Collection class allows you to access a set of data
* using both array and object notation. * using both array and object notation.
*/ */
class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializable { final class Collection implements ArrayAccess, Iterator, Countable, JsonSerializable
{
/** /**
* Collection data. * Collection data.
*
* @var array
*/ */
private $data; private array $data;
/** /**
* Constructor. * Constructor.
* *
* @param array $data Initial data * @param array $data Initial data
*/ */
public function __construct(array $data = array()) { public function __construct(array $data = [])
{
$this->data = $data; $this->data = $data;
} }
@ -37,19 +45,22 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* Gets an item. * Gets an item.
* *
* @param string $key Key * @param string $key Key
*
* @return mixed Value * @return mixed Value
*/ */
public function __get($key) { public function __get(string $key)
return isset($this->data[$key]) ? $this->data[$key] : null; {
return $this->data[$key] ?? null;
} }
/** /**
* Set an item. * Set an item.
* *
* @param string $key Key * @param string $key Key
* @param mixed $value Value * @param mixed $value Value
*/ */
public function __set($key, $value) { public function __set(string $key, $value): void
{
$this->data[$key] = $value; $this->data[$key] = $value;
} }
@ -57,9 +68,11 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* Checks if an item exists. * Checks if an item exists.
* *
* @param string $key Key * @param string $key Key
*
* @return bool Item status * @return bool Item status
*/ */
public function __isset($key) { public function __isset(string $key): bool
{
return isset($this->data[$key]); return isset($this->data[$key]);
} }
@ -68,7 +81,8 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @param string $key Key * @param string $key Key
*/ */
public function __unset($key) { public function __unset(string $key): void
{
unset($this->data[$key]); unset($this->data[$key]);
} }
@ -76,23 +90,25 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* Gets an item at the offset. * Gets an item at the offset.
* *
* @param string $offset Offset * @param string $offset Offset
*
* @return mixed Value * @return mixed Value
*/ */
public function offsetGet($offset) { public function offsetGet($offset)
return isset($this->data[$offset]) ? $this->data[$offset] : null; {
return $this->data[$offset] ?? null;
} }
/** /**
* Sets an item at the offset. * Sets an item at the offset.
* *
* @param string $offset Offset * @param string $offset Offset
* @param mixed $value Value * @param mixed $value Value
*/ */
public function offsetSet($offset, $value) { public function offsetSet($offset, $value)
if (is_null($offset)) { {
if (null === $offset) {
$this->data[] = $value; $this->data[] = $value;
} } else {
else {
$this->data[$offset] = $value; $this->data[$offset] = $value;
} }
} }
@ -101,9 +117,11 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* Checks if an item exists at the offset. * Checks if an item exists at the offset.
* *
* @param string $offset Offset * @param string $offset Offset
*
* @return bool Item status * @return bool Item status
*/ */
public function offsetExists($offset) { public function offsetExists($offset): bool
{
return isset($this->data[$offset]); return isset($this->data[$offset]);
} }
@ -112,14 +130,16 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @param string $offset Offset * @param string $offset Offset
*/ */
public function offsetUnset($offset) { public function offsetUnset($offset): void
{
unset($this->data[$offset]); unset($this->data[$offset]);
} }
/** /**
* Resets the collection. * Resets the collection.
*/ */
public function rewind() { public function rewind(): void
{
reset($this->data); reset($this->data);
} }
@ -128,7 +148,8 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return mixed Value * @return mixed Value
*/ */
public function current() { public function current()
{
return current($this->data); return current($this->data);
} }
@ -137,7 +158,8 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return mixed Value * @return mixed Value
*/ */
public function key() { public function key()
{
return key($this->data); return key($this->data);
} }
@ -156,10 +178,11 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return bool Key status * @return bool Key status
*/ */
public function valid() public function valid(): bool
{ {
$key = key($this->data); $key = key($this->data);
return ($key !== NULL && $key !== FALSE);
return null !== $key && false !== $key;
} }
/** /**
@ -167,8 +190,9 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return int Collection size * @return int Collection size
*/ */
public function count() { public function count(): int
return sizeof($this->data); {
return \count($this->data);
} }
/** /**
@ -176,7 +200,8 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return array Collection keys * @return array Collection keys
*/ */
public function keys() { public function keys(): array
{
return array_keys($this->data); return array_keys($this->data);
} }
@ -185,7 +210,8 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @return array Collection data * @return array Collection data
*/ */
public function getData() { public function getData(): array
{
return $this->data; return $this->data;
} }
@ -194,23 +220,26 @@ class Collection implements \ArrayAccess, \Iterator, \Countable, \JsonSerializab
* *
* @param array $data New collection data * @param array $data New collection data
*/ */
public function setData(array $data) { public function setData(array $data): void
{
$this->data = $data; $this->data = $data;
} }
/** /**
* Gets the collection data which can be serialized to JSON * Gets the collection data which can be serialized to JSON.
* *
* @return array Collection data which can be serialized by <b>json_encode</b> * @return array Collection data which can be serialized by <b>json_encode</b>
*/ */
public function jsonSerialize() { public function jsonSerialize(): array
{
return $this->data; return $this->data;
} }
/** /**
* Removes all items from the collection. * Removes all items from the collection.
*/ */
public function clear() { public function clear(): void
$this->data = array(); {
$this->data = [];
} }
} }

@ -1,11 +1,13 @@
<?php <?php
declare(strict_types=1);
/** /**
* Flight: An extensible micro-framework. * Flight: An extensible micro-framework.
* *
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com> * @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
* @license MIT, http://flightphp.com/license * @license MIT, http://flightphp.com/license
*/ */
interface LegacyJsonSerializable
interface JsonSerializable { {
public function jsonSerialize(); public function jsonSerialize();
} }
Loading…
Cancel
Save