parent
8505303e4a
commit
0741e2880f
@ -0,0 +1,109 @@
|
||||
{
|
||||
"_readme": [
|
||||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "d290fe8615d2fd09cfd976def6121adc",
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.7.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erusev/parsedown.git",
|
||||
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
|
||||
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Parsedown": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuil Rusev",
|
||||
"email": "hello@erusev.com",
|
||||
"homepage": "http://erusev.com"
|
||||
}
|
||||
],
|
||||
"description": "Parser for Markdown.",
|
||||
"homepage": "http://parsedown.org",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"parser"
|
||||
],
|
||||
"time": "2019-03-17T18:48:37+00:00"
|
||||
},
|
||||
{
|
||||
"name": "mikecao/flight",
|
||||
"version": "v1.3.7",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mikecao/flight.git",
|
||||
"reference": "7d5970f7617861c868a5c728764421d8950faaf0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mikecao/flight/zipball/7d5970f7617861c868a5c728764421d8950faaf0",
|
||||
"reference": "7d5970f7617861c868a5c728764421d8950faaf0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"flight/autoload.php",
|
||||
"flight/Flight.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike Cao",
|
||||
"email": "mike@mikecao.com",
|
||||
"homepage": "http://www.mikecao.com/",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.",
|
||||
"homepage": "http://flightphp.com",
|
||||
"time": "2018-11-23T23:05:33+00:00"
|
||||
}
|
||||
],
|
||||
"packages-dev": [],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"php": "^7.0"
|
||||
},
|
||||
"platform-dev": []
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
// autoload.php @generated by Composer
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
return ComposerAutoloaderInit892ac0856207f5cf8e7277d97176f281::getLoader();
|
@ -0,0 +1,445 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Composer.
|
||||
*
|
||||
* (c) Nils Adermann <naderman@naderman.de>
|
||||
* Jordi Boggiano <j.boggiano@seld.be>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
/**
|
||||
* ClassLoader implements a PSR-0, PSR-4 and classmap class loader.
|
||||
*
|
||||
* $loader = new \Composer\Autoload\ClassLoader();
|
||||
*
|
||||
* // register classes with namespaces
|
||||
* $loader->add('Symfony\Component', __DIR__.'/component');
|
||||
* $loader->add('Symfony', __DIR__.'/framework');
|
||||
*
|
||||
* // activate the autoloader
|
||||
* $loader->register();
|
||||
*
|
||||
* // to enable searching the include path (eg. for PEAR packages)
|
||||
* $loader->setUseIncludePath(true);
|
||||
*
|
||||
* In this example, if you try to use a class in the Symfony\Component
|
||||
* namespace or one of its children (Symfony\Component\Console for instance),
|
||||
* the autoloader will first look for the class under the component/
|
||||
* directory, and it will then fallback to the framework/ directory if not
|
||||
* found before giving up.
|
||||
*
|
||||
* This class is loosely based on the Symfony UniversalClassLoader.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Jordi Boggiano <j.boggiano@seld.be>
|
||||
* @see http://www.php-fig.org/psr/psr-0/
|
||||
* @see http://www.php-fig.org/psr/psr-4/
|
||||
*/
|
||||
class ClassLoader
|
||||
{
|
||||
// PSR-4
|
||||
private $prefixLengthsPsr4 = array();
|
||||
private $prefixDirsPsr4 = array();
|
||||
private $fallbackDirsPsr4 = array();
|
||||
|
||||
// PSR-0
|
||||
private $prefixesPsr0 = array();
|
||||
private $fallbackDirsPsr0 = array();
|
||||
|
||||
private $useIncludePath = false;
|
||||
private $classMap = array();
|
||||
private $classMapAuthoritative = false;
|
||||
private $missingClasses = array();
|
||||
private $apcuPrefix;
|
||||
|
||||
public function getPrefixes()
|
||||
{
|
||||
if (!empty($this->prefixesPsr0)) {
|
||||
return call_user_func_array('array_merge', $this->prefixesPsr0);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getPrefixesPsr4()
|
||||
{
|
||||
return $this->prefixDirsPsr4;
|
||||
}
|
||||
|
||||
public function getFallbackDirs()
|
||||
{
|
||||
return $this->fallbackDirsPsr0;
|
||||
}
|
||||
|
||||
public function getFallbackDirsPsr4()
|
||||
{
|
||||
return $this->fallbackDirsPsr4;
|
||||
}
|
||||
|
||||
public function getClassMap()
|
||||
{
|
||||
return $this->classMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $classMap Class to filename map
|
||||
*/
|
||||
public function addClassMap(array $classMap)
|
||||
{
|
||||
if ($this->classMap) {
|
||||
$this->classMap = array_merge($this->classMap, $classMap);
|
||||
} else {
|
||||
$this->classMap = $classMap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix, either
|
||||
* appending or prepending to the ones previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 root directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*/
|
||||
public function add($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr0
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr0 = array_merge(
|
||||
$this->fallbackDirsPsr0,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$first = $prefix[0];
|
||||
if (!isset($this->prefixesPsr0[$first][$prefix])) {
|
||||
$this->prefixesPsr0[$first][$prefix] = (array) $paths;
|
||||
|
||||
return;
|
||||
}
|
||||
if ($prepend) {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixesPsr0[$first][$prefix]
|
||||
);
|
||||
} else {
|
||||
$this->prefixesPsr0[$first][$prefix] = array_merge(
|
||||
$this->prefixesPsr0[$first][$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace, either
|
||||
* appending or prepending to the ones previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
* @param bool $prepend Whether to prepend the directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function addPsr4($prefix, $paths, $prepend = false)
|
||||
{
|
||||
if (!$prefix) {
|
||||
// Register directories for the root namespace.
|
||||
if ($prepend) {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
(array) $paths,
|
||||
$this->fallbackDirsPsr4
|
||||
);
|
||||
} else {
|
||||
$this->fallbackDirsPsr4 = array_merge(
|
||||
$this->fallbackDirsPsr4,
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
} elseif (!isset($this->prefixDirsPsr4[$prefix])) {
|
||||
// Register directories for a new namespace.
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
} elseif ($prepend) {
|
||||
// Prepend directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
(array) $paths,
|
||||
$this->prefixDirsPsr4[$prefix]
|
||||
);
|
||||
} else {
|
||||
// Append directories for an already registered namespace.
|
||||
$this->prefixDirsPsr4[$prefix] = array_merge(
|
||||
$this->prefixDirsPsr4[$prefix],
|
||||
(array) $paths
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-0 directories for a given prefix,
|
||||
* replacing any others previously set for this prefix.
|
||||
*
|
||||
* @param string $prefix The prefix
|
||||
* @param array|string $paths The PSR-0 base directories
|
||||
*/
|
||||
public function set($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr0 = (array) $paths;
|
||||
} else {
|
||||
$this->prefixesPsr0[$prefix[0]][$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a set of PSR-4 directories for a given namespace,
|
||||
* replacing any others previously set for this namespace.
|
||||
*
|
||||
* @param string $prefix The prefix/namespace, with trailing '\\'
|
||||
* @param array|string $paths The PSR-4 base directories
|
||||
*
|
||||
* @throws \InvalidArgumentException
|
||||
*/
|
||||
public function setPsr4($prefix, $paths)
|
||||
{
|
||||
if (!$prefix) {
|
||||
$this->fallbackDirsPsr4 = (array) $paths;
|
||||
} else {
|
||||
$length = strlen($prefix);
|
||||
if ('\\' !== $prefix[$length - 1]) {
|
||||
throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator.");
|
||||
}
|
||||
$this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length;
|
||||
$this->prefixDirsPsr4[$prefix] = (array) $paths;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns on searching the include path for class files.
|
||||
*
|
||||
* @param bool $useIncludePath
|
||||
*/
|
||||
public function setUseIncludePath($useIncludePath)
|
||||
{
|
||||
$this->useIncludePath = $useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be used to check if the autoloader uses the include path to check
|
||||
* for classes.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function getUseIncludePath()
|
||||
{
|
||||
return $this->useIncludePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Turns off searching the prefix and fallback directories for classes
|
||||
* that have not been registered with the class map.
|
||||
*
|
||||
* @param bool $classMapAuthoritative
|
||||
*/
|
||||
public function setClassMapAuthoritative($classMapAuthoritative)
|
||||
{
|
||||
$this->classMapAuthoritative = $classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should class lookup fail if not found in the current class map?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isClassMapAuthoritative()
|
||||
{
|
||||
return $this->classMapAuthoritative;
|
||||
}
|
||||
|
||||
/**
|
||||
* APCu prefix to use to cache found/not-found classes, if the extension is enabled.
|
||||
*
|
||||
* @param string|null $apcuPrefix
|
||||
*/
|
||||
public function setApcuPrefix($apcuPrefix)
|
||||
{
|
||||
$this->apcuPrefix = function_exists('apcu_fetch') && filter_var(ini_get('apc.enabled'), FILTER_VALIDATE_BOOLEAN) ? $apcuPrefix : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* The APCu prefix in use, or null if APCu caching is not enabled.
|
||||
*
|
||||
* @return string|null
|
||||
*/
|
||||
public function getApcuPrefix()
|
||||
{
|
||||
return $this->apcuPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers this instance as an autoloader.
|
||||
*
|
||||
* @param bool $prepend Whether to prepend the autoloader or not
|
||||
*/
|
||||
public function register($prepend = false)
|
||||
{
|
||||
spl_autoload_register(array($this, 'loadClass'), true, $prepend);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters this instance as an autoloader.
|
||||
*/
|
||||
public function unregister()
|
||||
{
|
||||
spl_autoload_unregister(array($this, 'loadClass'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the given class or interface.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
* @return bool|null True if loaded, null otherwise
|
||||
*/
|
||||
public function loadClass($class)
|
||||
{
|
||||
if ($file = $this->findFile($class)) {
|
||||
includeFile($file);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the path to the file where the class is defined.
|
||||
*
|
||||
* @param string $class The name of the class
|
||||
*
|
||||
* @return string|false The path if found, false otherwise
|
||||
*/
|
||||
public function findFile($class)
|
||||
{
|
||||
// class map lookup
|
||||
if (isset($this->classMap[$class])) {
|
||||
return $this->classMap[$class];
|
||||
}
|
||||
if ($this->classMapAuthoritative || isset($this->missingClasses[$class])) {
|
||||
return false;
|
||||
}
|
||||
if (null !== $this->apcuPrefix) {
|
||||
$file = apcu_fetch($this->apcuPrefix.$class, $hit);
|
||||
if ($hit) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
$file = $this->findFileWithExtension($class, '.php');
|
||||
|
||||
// Search for Hack files if we are running on HHVM
|
||||
if (false === $file && defined('HHVM_VERSION')) {
|
||||
$file = $this->findFileWithExtension($class, '.hh');
|
||||
}
|
||||
|
||||
if (null !== $this->apcuPrefix) {
|
||||
apcu_add($this->apcuPrefix.$class, $file);
|
||||
}
|
||||
|
||||
if (false === $file) {
|
||||
// Remember that this class does not exist.
|
||||
$this->missingClasses[$class] = true;
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
private function findFileWithExtension($class, $ext)
|
||||
{
|
||||
// PSR-4 lookup
|
||||
$logicalPathPsr4 = strtr($class, '\\', DIRECTORY_SEPARATOR) . $ext;
|
||||
|
||||
$first = $class[0];
|
||||
if (isset($this->prefixLengthsPsr4[$first])) {
|
||||
$subPath = $class;
|
||||
while (false !== $lastPos = strrpos($subPath, '\\')) {
|
||||
$subPath = substr($subPath, 0, $lastPos);
|
||||
$search = $subPath . '\\';
|
||||
if (isset($this->prefixDirsPsr4[$search])) {
|
||||
$pathEnd = DIRECTORY_SEPARATOR . substr($logicalPathPsr4, $lastPos + 1);
|
||||
foreach ($this->prefixDirsPsr4[$search] as $dir) {
|
||||
if (file_exists($file = $dir . $pathEnd)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-4 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr4 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr4)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 lookup
|
||||
if (false !== $pos = strrpos($class, '\\')) {
|
||||
// namespaced class name
|
||||
$logicalPathPsr0 = substr($logicalPathPsr4, 0, $pos + 1)
|
||||
. strtr(substr($logicalPathPsr4, $pos + 1), '_', DIRECTORY_SEPARATOR);
|
||||
} else {
|
||||
// PEAR-like class name
|
||||
$logicalPathPsr0 = strtr($class, '_', DIRECTORY_SEPARATOR) . $ext;
|
||||
}
|
||||
|
||||
if (isset($this->prefixesPsr0[$first])) {
|
||||
foreach ($this->prefixesPsr0[$first] as $prefix => $dirs) {
|
||||
if (0 === strpos($class, $prefix)) {
|
||||
foreach ($dirs as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 fallback dirs
|
||||
foreach ($this->fallbackDirsPsr0 as $dir) {
|
||||
if (file_exists($file = $dir . DIRECTORY_SEPARATOR . $logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
}
|
||||
|
||||
// PSR-0 include paths.
|
||||
if ($this->useIncludePath && $file = stream_resolve_include_path($logicalPathPsr0)) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope isolated include.
|
||||
*
|
||||
* Prevents access to $this/self from included files.
|
||||
*/
|
||||
function includeFile($file)
|
||||
{
|
||||
include $file;
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
|
||||
Copyright (c) Nils Adermann, Jordi Boggiano
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
// autoload_classmap.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
);
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
// autoload_files.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'fc73bab8d04e21bcdda37ca319c63800' => $vendorDir . '/mikecao/flight/flight/autoload.php',
|
||||
'5b7d984aab5ae919d3362ad9588977eb' => $vendorDir . '/mikecao/flight/flight/Flight.php',
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_namespaces.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'Parsedown' => array($vendorDir . '/erusev/parsedown'),
|
||||
);
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
// autoload_psr4.php @generated by Composer
|
||||
|
||||
$vendorDir = dirname(dirname(__FILE__));
|
||||
$baseDir = dirname($vendorDir);
|
||||
|
||||
return array(
|
||||
'SuperGear\\' => array($baseDir . '/src'),
|
||||
);
|
@ -0,0 +1,70 @@
|
||||
<?php
|
||||
|
||||
// autoload_real.php @generated by Composer
|
||||
|
||||
class ComposerAutoloaderInit892ac0856207f5cf8e7277d97176f281
|
||||
{
|
||||
private static $loader;
|
||||
|
||||
public static function loadClassLoader($class)
|
||||
{
|
||||
if ('Composer\Autoload\ClassLoader' === $class) {
|
||||
require __DIR__ . '/ClassLoader.php';
|
||||
}
|
||||
}
|
||||
|
||||
public static function getLoader()
|
||||
{
|
||||
if (null !== self::$loader) {
|
||||
return self::$loader;
|
||||
}
|
||||
|
||||
spl_autoload_register(array('ComposerAutoloaderInit892ac0856207f5cf8e7277d97176f281', 'loadClassLoader'), true, true);
|
||||
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
|
||||
spl_autoload_unregister(array('ComposerAutoloaderInit892ac0856207f5cf8e7277d97176f281', 'loadClassLoader'));
|
||||
|
||||
$useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
|
||||
if ($useStaticLoader) {
|
||||
require_once __DIR__ . '/autoload_static.php';
|
||||
|
||||
call_user_func(\Composer\Autoload\ComposerStaticInit892ac0856207f5cf8e7277d97176f281::getInitializer($loader));
|
||||
} else {
|
||||
$map = require __DIR__ . '/autoload_namespaces.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->set($namespace, $path);
|
||||
}
|
||||
|
||||
$map = require __DIR__ . '/autoload_psr4.php';
|
||||
foreach ($map as $namespace => $path) {
|
||||
$loader->setPsr4($namespace, $path);
|
||||
}
|
||||
|
||||
$classMap = require __DIR__ . '/autoload_classmap.php';
|
||||
if ($classMap) {
|
||||
$loader->addClassMap($classMap);
|
||||
}
|
||||
}
|
||||
|
||||
$loader->register(true);
|
||||
|
||||
if ($useStaticLoader) {
|
||||
$includeFiles = Composer\Autoload\ComposerStaticInit892ac0856207f5cf8e7277d97176f281::$files;
|
||||
} else {
|
||||
$includeFiles = require __DIR__ . '/autoload_files.php';
|
||||
}
|
||||
foreach ($includeFiles as $fileIdentifier => $file) {
|
||||
composerRequire892ac0856207f5cf8e7277d97176f281($fileIdentifier, $file);
|
||||
}
|
||||
|
||||
return $loader;
|
||||
}
|
||||
}
|
||||
|
||||
function composerRequire892ac0856207f5cf8e7277d97176f281($fileIdentifier, $file)
|
||||
{
|
||||
if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
|
||||
require $file;
|
||||
|
||||
$GLOBALS['__composer_autoload_files'][$fileIdentifier] = true;
|
||||
}
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
// autoload_static.php @generated by Composer
|
||||
|
||||
namespace Composer\Autoload;
|
||||
|
||||
class ComposerStaticInit892ac0856207f5cf8e7277d97176f281
|
||||
{
|
||||
public static $files = array (
|
||||
'fc73bab8d04e21bcdda37ca319c63800' => __DIR__ . '/..' . '/mikecao/flight/flight/autoload.php',
|
||||
'5b7d984aab5ae919d3362ad9588977eb' => __DIR__ . '/..' . '/mikecao/flight/flight/Flight.php',
|
||||
);
|
||||
|
||||
public static $prefixLengthsPsr4 = array (
|
||||
'S' =>
|
||||
array (
|
||||
'SuperGear\\' => 10,
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixDirsPsr4 = array (
|
||||
'SuperGear\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/../..' . '/src',
|
||||
),
|
||||
);
|
||||
|
||||
public static $prefixesPsr0 = array (
|
||||
'P' =>
|
||||
array (
|
||||
'Parsedown' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/erusev/parsedown',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
public static function getInitializer(ClassLoader $loader)
|
||||
{
|
||||
return \Closure::bind(function () use ($loader) {
|
||||
$loader->prefixLengthsPsr4 = ComposerStaticInit892ac0856207f5cf8e7277d97176f281::$prefixLengthsPsr4;
|
||||
$loader->prefixDirsPsr4 = ComposerStaticInit892ac0856207f5cf8e7277d97176f281::$prefixDirsPsr4;
|
||||
$loader->prefixesPsr0 = ComposerStaticInit892ac0856207f5cf8e7277d97176f281::$prefixesPsr0;
|
||||
|
||||
}, null, ClassLoader::class);
|
||||
}
|
||||
}
|
@ -0,0 +1,95 @@
|
||||
[
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"version": "1.7.3",
|
||||
"version_normalized": "1.7.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/erusev/parsedown.git",
|
||||
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/erusev/parsedown/zipball/6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
|
||||
"reference": "6d893938171a817f4e9bc9e86f2da1e370b7bcd7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-mbstring": "*",
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"time": "2019-03-17T18:48:37+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-0": {
|
||||
"Parsedown": ""
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuil Rusev",
|
||||
"email": "hello@erusev.com",
|
||||
"homepage": "http://erusev.com"
|
||||
}
|
||||
],
|
||||
"description": "Parser for Markdown.",
|
||||
"homepage": "http://parsedown.org",
|
||||
"keywords": [
|
||||
"markdown",
|
||||
"parser"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "mikecao/flight",
|
||||
"version": "v1.3.7",
|
||||
"version_normalized": "1.3.7.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mikecao/flight.git",
|
||||
"reference": "7d5970f7617861c868a5c728764421d8950faaf0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/mikecao/flight/zipball/7d5970f7617861c868a5c728764421d8950faaf0",
|
||||
"reference": "7d5970f7617861c868a5c728764421d8950faaf0",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6"
|
||||
},
|
||||
"time": "2018-11-23T23:05:33+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"flight/autoload.php",
|
||||
"flight/Flight.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike Cao",
|
||||
"email": "mike@mikecao.com",
|
||||
"homepage": "http://www.mikecao.com/",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.",
|
||||
"homepage": "http://flightphp.com"
|
||||
}
|
||||
]
|
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013-2018 Emanuil Rusev, erusev.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
"description": "Parser for Markdown.",
|
||||
"keywords": ["markdown", "parser"],
|
||||
"homepage": "http://parsedown.org",
|
||||
"type": "library",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Emanuil Rusev",
|
||||
"email": "hello@erusev.com",
|
||||
"homepage": "http://erusev.com"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0",
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^4.8.35"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-0": {"Parsedown": ""}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-0": {
|
||||
"TestParsedown": "test/",
|
||||
"ParsedownTest": "test/",
|
||||
"CommonMarkTest": "test/",
|
||||
"CommonMarkTestWeak": "test/"
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,4 @@
|
||||
.idea
|
||||
vendor/
|
||||
composer.phar
|
||||
composer.lock
|
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2011 Mike Cao <mike@mikecao.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -0,0 +1,917 @@
|
||||
# What is Flight?
|
||||
|
||||
Flight is a fast, simple, extensible framework for PHP. Flight enables you to
|
||||
quickly and easily build RESTful web applications.
|
||||
|
||||
```php
|
||||
require 'flight/Flight.php';
|
||||
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
|
||||
Flight::start();
|
||||
```
|
||||
|
||||
[Learn more](http://flightphp.com/learn)
|
||||
|
||||
# Requirements
|
||||
|
||||
Flight requires `PHP 5.3` or greater.
|
||||
|
||||
# License
|
||||
|
||||
Flight is released under the [MIT](http://flightphp.com/license) license.
|
||||
|
||||
# Installation
|
||||
|
||||
1\. Download the files.
|
||||
|
||||
If you're using [Composer](https://getcomposer.org/), you can run the following command:
|
||||
|
||||
```
|
||||
composer require mikecao/flight
|
||||
```
|
||||
|
||||
OR you can [download](https://github.com/mikecao/flight/archive/master.zip) them directly
|
||||
and extract them to your web directory.
|
||||
|
||||
2\. Configure your webserver.
|
||||
|
||||
For *Apache*, edit your `.htaccess` file with the following:
|
||||
|
||||
```
|
||||
RewriteEngine On
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [QSA,L]
|
||||
```
|
||||
|
||||
**Note**: If you need to use flight in a subdirectory add the line `RewriteBase /subdir/` just after `RewriteEngine On`.
|
||||
|
||||
For *Nginx*, add the following to your server declaration:
|
||||
|
||||
```
|
||||
server {
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php;
|
||||
}
|
||||
}
|
||||
```
|
||||
3\. Create your `index.php` file.
|
||||
|
||||
First include the framework.
|
||||
|
||||
```php
|
||||
require 'flight/Flight.php';
|
||||
```
|
||||
|
||||
If you're using Composer, run the autoloader instead.
|
||||
|
||||
```php
|
||||
require 'vendor/autoload.php';
|
||||
```
|
||||
|
||||
Then define a route and assign a function to handle the request.
|
||||
|
||||
```php
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
```
|
||||
|
||||
Finally, start the framework.
|
||||
|
||||
```php
|
||||
Flight::start();
|
||||
```
|
||||
|
||||
# Routing
|
||||
|
||||
Routing in Flight is done by matching a URL pattern with a callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
```
|
||||
|
||||
The callback can be any object that is callable. So you can use a regular function:
|
||||
|
||||
```php
|
||||
function hello(){
|
||||
echo 'hello world!';
|
||||
}
|
||||
|
||||
Flight::route('/', 'hello');
|
||||
```
|
||||
|
||||
Or a class method:
|
||||
|
||||
```php
|
||||
class Greeting {
|
||||
public static function hello() {
|
||||
echo 'hello world!';
|
||||
}
|
||||
}
|
||||
|
||||
Flight::route('/', array('Greeting', 'hello'));
|
||||
```
|
||||
|
||||
Or an object method:
|
||||
|
||||
```php
|
||||
class Greeting
|
||||
{
|
||||
public function __construct() {
|
||||
$this->name = 'John Doe';
|
||||
}
|
||||
|
||||
public function hello() {
|
||||
echo "Hello, {$this->name}!";
|
||||
}
|
||||
}
|
||||
|
||||
$greeting = new Greeting();
|
||||
|
||||
Flight::route('/', array($greeting, 'hello'));
|
||||
```
|
||||
|
||||
Routes are matched in the order they are defined. The first route to match a
|
||||
request will be invoked.
|
||||
|
||||
## Method Routing
|
||||
|
||||
By default, route patterns are matched against all request methods. You can respond
|
||||
to specific methods by placing an identifier before the URL.
|
||||
|
||||
```php
|
||||
Flight::route('GET /', function(){
|
||||
echo 'I received a GET request.';
|
||||
});
|
||||
|
||||
Flight::route('POST /', function(){
|
||||
echo 'I received a POST request.';
|
||||
});
|
||||
```
|
||||
|
||||
You can also map multiple methods to a single callback by using a `|` delimiter:
|
||||
|
||||
```php
|
||||
Flight::route('GET|POST /', function(){
|
||||
echo 'I received either a GET or a POST request.';
|
||||
});
|
||||
```
|
||||
|
||||
## Regular Expressions
|
||||
|
||||
You can use regular expressions in your routes:
|
||||
|
||||
```php
|
||||
Flight::route('/user/[0-9]+', function(){
|
||||
// This will match /user/1234
|
||||
});
|
||||
```
|
||||
|
||||
## Named Parameters
|
||||
|
||||
You can specify named parameters in your routes which will be passed along to
|
||||
your callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/@name/@id', function($name, $id){
|
||||
echo "hello, $name ($id)!";
|
||||
});
|
||||
```
|
||||
|
||||
You can also include regular expressions with your named parameters by using
|
||||
the `:` delimiter:
|
||||
|
||||
```php
|
||||
Flight::route('/@name/@id:[0-9]{3}', function($name, $id){
|
||||
// This will match /bob/123
|
||||
// But will not match /bob/12345
|
||||
});
|
||||
```
|
||||
|
||||
## Optional Parameters
|
||||
|
||||
You can specify named parameters that are optional for matching by wrapping
|
||||
segments in parentheses.
|
||||
|
||||
```php
|
||||
Flight::route('/blog(/@year(/@month(/@day)))', function($year, $month, $day){
|
||||
// This will match the following URLS:
|
||||
// /blog/2012/12/10
|
||||
// /blog/2012/12
|
||||
// /blog/2012
|
||||
// /blog
|
||||
});
|
||||
```
|
||||
|
||||
Any optional parameters that are not matched will be passed in as NULL.
|
||||
|
||||
## Wildcards
|
||||
|
||||
Matching is only done on individual URL segments. If you want to match multiple
|
||||
segments you can use the `*` wildcard.
|
||||
|
||||
```php
|
||||
Flight::route('/blog/*', function(){
|
||||
// This will match /blog/2000/02/01
|
||||
});
|
||||
```
|
||||
|
||||
To route all requests to a single callback, you can do:
|
||||
|
||||
```php
|
||||
Flight::route('*', function(){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
## Passing
|
||||
|
||||
You can pass execution on to the next matching route by returning `true` from
|
||||
your callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/user/@name', function($name){
|
||||
// Check some condition
|
||||
if ($name != "Bob") {
|
||||
// Continue to next route
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
Flight::route('/user/*', function(){
|
||||
// This will get called
|
||||
});
|
||||
```
|
||||
|
||||
## Route Info
|
||||
|
||||
If you want to inspect the matching route information, you can request for the route
|
||||
object to be passed to your callback by passing in `true` as the third parameter in
|
||||
the route method. The route object will always be the last parameter passed to your
|
||||
callback function.
|
||||
|
||||
```php
|
||||
Flight::route('/', function($route){
|
||||
// Array of HTTP methods matched against
|
||||
$route->methods;
|
||||
|
||||
// Array of named parameters
|
||||
$route->params;
|
||||
|
||||
// Matching regular expression
|
||||
$route->regex;
|
||||
|
||||
// Contains the contents of any '*' used in the URL pattern
|
||||
$route->splat;
|
||||
}, true);
|
||||
```
|
||||
|
||||
# Extending
|
||||
|
||||
Flight is designed to be an extensible framework. The framework comes with a set
|
||||
of default methods and components, but it allows you to map your own methods,
|
||||
register your own classes, or even override existing classes and methods.
|
||||
|
||||
## Mapping Methods
|
||||
|
||||
To map your own custom method, you use the `map` function:
|
||||
|
||||
```php
|
||||
// Map your method
|
||||
Flight::map('hello', function($name){
|
||||
echo "hello $name!";
|
||||
});
|
||||
|
||||
// Call your custom method
|
||||
Flight::hello('Bob');
|
||||
```
|
||||
|
||||
## Registering Classes
|
||||
|
||||
To register your own class, you use the `register` function:
|
||||
|
||||
```php
|
||||
// Register your class
|
||||
Flight::register('user', 'User');
|
||||
|
||||
// Get an instance of your class
|
||||
$user = Flight::user();
|
||||
```
|
||||
|
||||
The register method also allows you to pass along parameters to your class
|
||||
constructor. So when you load your custom class, it will come pre-initialized.
|
||||
You can define the constructor parameters by passing in an additional array.
|
||||
Here's an example of loading a database connection:
|
||||
|
||||
```php
|
||||
// Register class with constructor parameters
|
||||
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'));
|
||||
|
||||
// Get an instance of your class
|
||||
// This will create an object with the defined parameters
|
||||
//
|
||||
// new PDO('mysql:host=localhost;dbname=test','user','pass');
|
||||
//
|
||||
$db = Flight::db();
|
||||
```
|
||||
|
||||
If you pass in an additional callback parameter, it will be executed immediately
|
||||
after class construction. This allows you to perform any set up procedures for your
|
||||
new object. The callback function takes one parameter, an instance of the new object.
|
||||
|
||||
```php
|
||||
// The callback will be passed the object that was constructed
|
||||
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'), function($db){
|
||||
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
});
|
||||
```
|
||||
|
||||
By default, every time you load your class you will get a shared instance.
|
||||
To get a new instance of a class, simply pass in `false` as a parameter:
|
||||
|
||||
```php
|
||||
// Shared instance of the class
|
||||
$shared = Flight::db();
|
||||
|
||||
// New instance of the class
|
||||
$new = Flight::db(false);
|
||||
```
|
||||
|
||||
Keep in mind that mapped methods have precedence over registered classes. If you
|
||||
declare both using the same name, only the mapped method will be invoked.
|
||||
|
||||
# Overriding
|
||||
|
||||
Flight allows you to override its default functionality to suit your own needs,
|
||||
without having to modify any code.
|
||||
|
||||
For example, when Flight cannot match a URL to a route, it invokes the `notFound`
|
||||
method which sends a generic `HTTP 404` response. You can override this behavior
|
||||
by using the `map` method:
|
||||
|
||||
```php
|
||||
Flight::map('notFound', function(){
|
||||
// Display custom 404 page
|
||||
include 'errors/404.html';
|
||||
});
|
||||
```
|
||||
|
||||
Flight also allows you to replace core components of the framework.
|
||||
For example you can replace the default Router class with your own custom class:
|
||||
|
||||
```php
|
||||
// Register your custom class
|
||||
Flight::register('router', 'MyRouter');
|
||||
|
||||
// When Flight loads the Router instance, it will load your class
|
||||
$myrouter = Flight::router();
|
||||
```
|
||||
|
||||
Framework methods like `map` and `register` however cannot be overridden. You will
|
||||
get an error if you try to do so.
|
||||
|
||||
# Filtering
|
||||
|
||||
Flight allows you to filter methods before and after they are called. There are no
|
||||
predefined hooks you need to memorize. You can filter any of the default framework
|
||||
methods as well as any custom methods that you've mapped.
|
||||
|
||||
A filter function looks like this:
|
||||
|
||||
```php
|
||||
function(&$params, &$output) {
|
||||
// Filter code
|
||||
}
|
||||
```
|
||||
|
||||
Using the passed in variables you can manipulate the input parameters and/or the output.
|
||||
|
||||
You can have a filter run before a method by doing:
|
||||
|
||||
```php
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
You can have a filter run after a method by doing:
|
||||
|
||||
```php
|
||||
Flight::after('start', function(&$params, &$output){
|
||||
// Do something
|
||||
});
|
||||
```
|
||||
|
||||
You can add as many filters as you want to any method. They will be called in the
|
||||
order that they are declared.
|
||||
|
||||
Here's an example of the filtering process:
|
||||
|
||||
```php
|
||||
// Map a custom method
|
||||
Flight::map('hello', function($name){
|
||||
return "Hello, $name!";
|
||||
});
|
||||
|
||||
// Add a before filter
|
||||
Flight::before('hello', function(&$params, &$output){
|
||||
// Manipulate the parameter
|
||||
$params[0] = 'Fred';
|
||||
});
|
||||
|
||||
// Add an after filter
|
||||
Flight::after('hello', function(&$params, &$output){
|
||||
// Manipulate the output
|
||||
$output .= " Have a nice day!";
|
||||
});
|
||||
|
||||
// Invoke the custom method
|
||||
echo Flight::hello('Bob');
|
||||
```
|
||||
|
||||
This should display:
|
||||
|
||||
Hello Fred! Have a nice day!
|
||||
|
||||
If you have defined multiple filters, you can break the chain by returning `false`
|
||||
in any of your filter functions:
|
||||
|
||||
```php
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'one';
|
||||
});
|
||||
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'two';
|
||||
|
||||
// This will end the chain
|
||||
return false;
|
||||
});
|
||||
|
||||
// This will not get called
|
||||
Flight::before('start', function(&$params, &$output){
|
||||
echo 'three';
|
||||
});
|
||||
```
|
||||
|
||||
Note, core methods such as `map` and `register` cannot be filtered because they
|
||||
are called directly and not invoked dynamically.
|
||||
|
||||
# Variables
|
||||
|
||||
Flight allows you to save variables so that they can be used anywhere in your application.
|
||||
|
||||
```php
|
||||
// Save your variable
|
||||
Flight::set('id', 123);
|
||||
|
||||
// Elsewhere in your application
|
||||
$id = Flight::get('id');
|
||||
```
|
||||
To see if a variable has been set you can do:
|
||||
|
||||
```php
|
||||
if (Flight::has('id')) {
|
||||
// Do something
|
||||
}
|
||||
```
|
||||
|
||||
You can clear a variable by doing:
|
||||
|
||||
```php
|
||||
// Clears the id variable
|
||||
Flight::clear('id');
|
||||
|
||||
// Clears all variables
|
||||
Flight::clear();
|
||||
```
|
||||
|
||||
Flight also uses variables for configuration purposes.
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
# Views
|
||||
|
||||
Flight provides some basic templating functionality by default. To display a view
|
||||
template call the `render` method with the name of the template file and optional
|
||||
template data:
|
||||
|
||||
```php
|
||||
Flight::render('hello.php', array('name' => 'Bob'));
|
||||
```
|
||||
|
||||
The template data you pass in is automatically injected into the template and can
|
||||
be reference like a local variable. Template files are simply PHP files. If the
|
||||
content of the `hello.php` template file is:
|
||||
|
||||
```php
|
||||
Hello, '<?php echo $name; ?>'!
|
||||
```
|
||||
|
||||
The output would be:
|
||||
|
||||
Hello, Bob!
|
||||
|
||||
You can also manually set view variables by using the set method:
|
||||
|
||||
```php
|
||||
Flight::view()->set('name', 'Bob');
|
||||
```
|
||||
|
||||
The variable `name` is now available across all your views. So you can simply do:
|
||||
|
||||
```php
|
||||
Flight::render('hello');
|
||||
```
|
||||
|
||||
Note that when specifying the name of the template in the render method, you can
|
||||
leave out the `.php` extension.
|
||||
|
||||
By default Flight will look for a `views` directory for template files. You can
|
||||
set an alternate path for your templates by setting the following config:
|
||||
|
||||
```php
|
||||
Flight::set('flight.views.path', '/path/to/views');
|
||||
```
|
||||
|
||||
## Layouts
|
||||
|
||||
It is common for websites to have a single layout template file with interchanging
|
||||
content. To render content to be used in a layout, you can pass in an optional
|
||||
parameter to the `render` method.
|
||||
|
||||
```php
|
||||
Flight::render('header', array('heading' => 'Hello'), 'header_content');
|
||||
Flight::render('body', array('body' => 'World'), 'body_content');
|
||||
```
|
||||
|
||||
Your view will then have saved variables called `header_content` and `body_content`.
|
||||
You can then render your layout by doing:
|
||||
|
||||
```php
|
||||
Flight::render('layout', array('title' => 'Home Page'));
|
||||
```
|
||||
|
||||
If the template files looks like this:
|
||||
|
||||
`header.php`:
|
||||
|
||||
```php
|
||||
<h1><?php echo $heading; ?></h1>
|
||||
```
|
||||
|
||||
`body.php`:
|
||||
|
||||
```php
|
||||
<div><?php echo $body; ?></div>
|
||||
```
|
||||
|
||||
`layout.php`:
|
||||
|
||||
```php
|
||||
<html>
|
||||
<head>
|
||||
<title><?php echo $title; ?></title>
|
||||
</head>
|
||||
<body>
|
||||
<?php echo $header_content; ?>
|
||||
<?php echo $body_content; ?>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
The output would be:
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<title>Home Page</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello</h1>
|
||||
<div>World</div>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Custom Views
|
||||
|
||||
Flight allows you to swap out the default view engine simply by registering your
|
||||
own view class. Here's how you would use the [Smarty](http://www.smarty.net/)
|
||||
template engine for your views:
|
||||
|
||||
```php
|
||||
// Load Smarty library
|
||||
require './Smarty/libs/Smarty.class.php';
|
||||
|
||||
// Register Smarty as the view class
|
||||
// Also pass a callback function to configure Smarty on load
|
||||
Flight::register('view', 'Smarty', array(), function($smarty){
|
||||
$smarty->template_dir = './templates/';
|
||||
$smarty->compile_dir = './templates_c/';
|
||||
$smarty->config_dir = './config/';
|
||||
$smarty->cache_dir = './cache/';
|
||||
});
|
||||
|
||||
// Assign template data
|
||||
Flight::view()->assign('name', 'Bob');
|
||||
|
||||
// Display the template
|
||||
Flight::view()->display('hello.tpl');
|
||||
```
|
||||
|
||||
For completeness, you should also override Flight's default render method:
|
||||
|
||||
```php
|
||||
Flight::map('render', function($template, $data){
|
||||
Flight::view()->assign($data);
|
||||
Flight::view()->display($template);
|
||||
});
|
||||
```
|
||||
# Error Handling
|
||||
|
||||
## Errors and Exceptions
|
||||
|
||||
All errors and exceptions are caught by Flight and passed to the `error` method.
|
||||
The default behavior is to send a generic `HTTP 500 Internal Server Error`
|
||||
response with some error information.
|
||||
|
||||
You can override this behavior for your own needs:
|
||||
|
||||
```php
|
||||
Flight::map('error', function(Exception $ex){
|
||||
// Handle error
|
||||
echo $ex->getTraceAsString();
|
||||
});
|
||||
```
|
||||
|
||||
By default errors are not logged to the web server. You can enable this by
|
||||
changing the config:
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
## Not Found
|
||||
|
||||
When a URL can't be found, Flight calls the `notFound` method. The default
|
||||
behavior is to send an `HTTP 404 Not Found` response with a simple message.
|
||||
|
||||
You can override this behavior for your own needs:
|
||||
|
||||
```php
|
||||
Flight::map('notFound', function(){
|
||||
// Handle not found
|
||||
});
|
||||
```
|
||||
|
||||
# Redirects
|
||||
|
||||
You can redirect the current request by using the `redirect` method and passing
|
||||
in a new URL:
|
||||
|
||||
```php
|
||||
Flight::redirect('/new/location');
|
||||
```
|
||||
|
||||
By default Flight sends a HTTP 303 status code. You can optionally set a
|
||||
custom code:
|
||||
|
||||
```php
|
||||
Flight::redirect('/new/location', 401);
|
||||
```
|
||||
|
||||
# Requests
|
||||
|
||||
Flight encapsulates the HTTP request into a single object, which can be
|
||||
accessed by doing:
|
||||
|
||||
```php
|
||||
$request = Flight::request();
|
||||
```
|
||||
|
||||
The request object provides the following properties:
|
||||
|
||||
```
|
||||
url - The URL being requested
|
||||
base - The parent subdirectory of the URL
|
||||
method - The request method (GET, POST, PUT, DELETE)
|
||||
referrer - The referrer URL
|
||||
ip - IP address of the client
|
||||
ajax - Whether the request is an AJAX request
|
||||
scheme - The server protocol (http, https)
|
||||
user_agent - Browser information
|
||||
type - The content type
|
||||
length - The content length
|
||||
query - Query string parameters
|
||||
data - Post data or JSON data
|
||||
cookies - Cookie data
|
||||
files - Uploaded files
|
||||
secure - Whether the connection is secure
|
||||
accept - HTTP accept parameters
|
||||
proxy_ip - Proxy IP address of the client
|
||||
```
|
||||
|
||||
You can access the `query`, `data`, `cookies`, and `files` properties
|
||||
as arrays or objects.
|
||||
|
||||
So, to get a query string parameter, you can do:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->query['id'];
|
||||
```
|
||||
|
||||
Or you can do:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->query->id;
|
||||
```
|
||||
|
||||
## RAW Request Body
|
||||
|
||||
To get the raw HTTP request body, for example when dealing with PUT requests, you can do:
|
||||
|
||||
```php
|
||||
$body = Flight::request()->getBody();
|
||||
```
|
||||
|
||||
## JSON Input
|
||||
|
||||
If you send a request with the type `application/json` and the data `{"id": 123}` it will be available
|
||||
from the `data` property:
|
||||
|
||||
```php
|
||||
$id = Flight::request()->data->id;
|
||||
```
|
||||
|
||||
# HTTP Caching
|
||||
|
||||
Flight provides built-in support for HTTP level caching. If the caching condition
|
||||
is met, Flight will return an HTTP `304 Not Modified` response. The next time the
|
||||
client requests the same resource, they will be prompted to use their locally
|
||||
cached version.
|
||||
|
||||
## Last-Modified
|
||||
|
||||
You can use the `lastModified` method and pass in a UNIX timestamp to set the date
|
||||
and time a page was last modified. The client will continue to use their cache until
|
||||
the last modified value is changed.
|
||||
|
||||
```php
|
||||
Flight::route('/news', function(){
|
||||
Flight::lastModified(1234567890);
|
||||
echo 'This content will be cached.';
|
||||
});
|
||||
```
|
||||
|
||||
## ETag
|
||||
|
||||
`ETag` caching is similar to `Last-Modified`, except you can specify any id you
|
||||
want for the resource:
|
||||
|
||||
```php
|
||||
Flight::route('/news', function(){
|
||||
Flight::etag('my-unique-id');
|
||||
echo 'This content will be cached.';
|
||||
});
|
||||
```
|
||||
|
||||
Keep in mind that calling either `lastModified` or `etag` will both set and check the
|
||||
cache value. If the cache value is the same between requests, Flight will immediately
|
||||
send an `HTTP 304` response and stop processing.
|
||||
|
||||
# Stopping
|
||||
|
||||
You can stop the framework at any point by calling the `halt` method:
|
||||
|
||||
```php
|
||||
Flight::halt();
|
||||
```
|
||||
|
||||
You can also specify an optional `HTTP` status code and message:
|
||||
|
||||
```php
|
||||
Flight::halt(200, 'Be right back...');
|
||||
```
|
||||
|
||||
Calling `halt` will discard any response content up to that point. If you want to stop
|
||||
the framework and output the current response, use the `stop` method:
|
||||
|
||||
```php
|
||||
Flight::stop();
|
||||
```
|
||||
|
||||
# JSON
|
||||
|
||||
Flight provides support for sending JSON and JSONP responses. To send a JSON response you
|
||||
pass some data to be JSON encoded:
|
||||
|
||||
```php
|
||||
Flight::json(array('id' => 123));
|
||||
```
|
||||
|
||||
For JSONP requests you, can optionally pass in the query parameter name you are
|
||||
using to define your callback function:
|
||||
|
||||
```php
|
||||
Flight::jsonp(array('id' => 123), 'q');
|
||||
```
|
||||
|
||||
So, when making a GET request using `?q=my_func`, you should receive the output:
|
||||
|
||||
```
|
||||
my_func({"id":123});
|
||||
```
|
||||
|
||||
If you don't pass in a query parameter name it will default to `jsonp`.
|
||||
|
||||
|
||||
# Configuration
|
||||
|
||||
You can customize certain behaviors of Flight by setting configuration values
|
||||
through the `set` method.
|
||||
|
||||
```php
|
||||
Flight::set('flight.log_errors', true);
|
||||
```
|
||||
|
||||
The following is a list of all the available configuration settings:
|
||||
|
||||
flight.base_url - Override the base url of the request. (default: null)
|
||||
flight.case_sensitive - Case sensitive matching for URLs. (default: false)
|
||||
flight.handle_errors - Allow Flight to handle all errors internally. (default: true)
|
||||
flight.log_errors - Log errors to the web server's error log file. (default: false)
|
||||
flight.views.path - Directory containing view template files. (default: ./views)
|
||||
flight.views.extension - View template file extension. (default: .php)
|
||||
|
||||
# Framework Methods
|
||||
|
||||
Flight is designed to be easy to use and understand. The following is the complete
|
||||
set of methods for the framework. It consists of core methods, which are regular
|
||||
static methods, and extensible methods, which are mapped methods that can be filtered
|
||||
or overridden.
|
||||
|
||||
## Core Methods
|
||||
|
||||
```php
|
||||
Flight::map($name, $callback) // Creates a custom framework method.
|
||||
Flight::register($name, $class, [$params], [$callback]) // Registers a class to a framework method.
|
||||
Flight::before($name, $callback) // Adds a filter before a framework method.
|
||||
Flight::after($name, $callback) // Adds a filter after a framework method.
|
||||
Flight::path($path) // Adds a path for autoloading classes.
|
||||
Flight::get($key) // Gets a variable.
|
||||
Flight::set($key, $value) // Sets a variable.
|
||||
Flight::has($key) // Checks if a variable is set.
|
||||
Flight::clear([$key]) // Clears a variable.
|
||||
Flight::init() // Initializes the framework to its default settings.
|
||||
Flight::app() // Gets the application object instance
|
||||
```
|
||||
|
||||
## Extensible Methods
|
||||
|
||||
```php
|
||||
Flight::start() // Starts the framework.
|
||||
Flight::stop() // Stops the framework and sends a response.
|
||||
Flight::halt([$code], [$message]) // Stop the framework with an optional status code and message.
|
||||
Flight::route($pattern, $callback) // Maps a URL pattern to a callback.
|
||||
Flight::redirect($url, [$code]) // Redirects to another URL.
|
||||
Flight::render($file, [$data], [$key]) // Renders a template file.
|
||||
Flight::error($exception) // Sends an HTTP 500 response.
|
||||
Flight::notFound() // Sends an HTTP 404 response.
|
||||
Flight::etag($id, [$type]) // Performs ETag HTTP caching.
|
||||
Flight::lastModified($time) // Performs last modified HTTP caching.
|
||||
Flight::json($data, [$code], [$encode], [$charset], [$option]) // Sends a JSON response.
|
||||
Flight::jsonp($data, [$param], [$code], [$encode], [$charset], [$option]) // Sends a JSONP response.
|
||||
```
|
||||
|
||||
Any custom methods added with `map` and `register` can also be filtered.
|
||||
|
||||
|
||||
# Framework Instance
|
||||
|
||||
Instead of running Flight as a global static class, you can optionally run it
|
||||
as an object instance.
|
||||
|
||||
```php
|
||||
require 'flight/autoload.php';
|
||||
|
||||
use flight\Engine;
|
||||
|
||||
$app = new Engine();
|
||||
|
||||
$app->route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
|
||||
$app->start();
|
||||
```
|
||||
|
||||
So instead of calling the static method, you would call the instance method with
|
||||
the same name on the Engine object.
|
@ -0,0 +1 @@
|
||||
1.3.7
|
@ -0,0 +1,23 @@
|
||||
{
|
||||
"name": "mikecao/flight",
|
||||
"description": "Flight is a fast, simple, extensible framework for PHP. Flight enables you to quickly and easily build RESTful web applications.",
|
||||
"homepage": "http://flightphp.com",
|
||||
"license": "MIT",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Mike Cao",
|
||||
"email": "mike@mikecao.com",
|
||||
"homepage": "http://www.mikecao.com/",
|
||||
"role": "Original Developer"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=5.3.0"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [ "flight/autoload.php", "flight/Flight.php" ]
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "~4.6"
|
||||
}
|
||||
}
|
@ -0,0 +1,574 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight;
|
||||
|
||||
use flight\core\Loader;
|
||||
use flight\core\Dispatcher;
|
||||
|
||||
/**
|
||||
* The Engine class contains the core functionality of the framework.
|
||||
* It is responsible for loading an HTTP request, running the assigned services,
|
||||
* and generating an HTTP response.
|
||||
*
|
||||
* Core methods
|
||||
* @method void start() Starts engine
|
||||
* @method void stop() Stops framework and outputs current response
|
||||
* @method void halt(int $code = 200, string $message = '') Stops processing and returns a given response.
|
||||
*
|
||||
*
|
||||
* Routing
|
||||
* @method void route(string $pattern, callable $callback, bool $pass_route = false) Routes a URL to a callback function.
|
||||
* @method \flight\net\Router router() Gets router
|
||||
*
|
||||
* Views
|
||||
* @method void render(string $file, array $data = null, string $key = null) Renders template
|
||||
* @method \flight\template\View view() Gets current view
|
||||
*
|
||||
* Request-response
|
||||
* @method \flight\net\Request request() Gets current request
|
||||
* @method \flight\net\Response response() Gets current response
|
||||
* @method void error(\Exception $e) Sends an HTTP 500 response for any errors.
|
||||
* @method void notFound() Sends an HTTP 404 response when a URL is not found.
|
||||
* @method void redirect(string $url, int $code = 303) Redirects the current request to another URL.
|
||||
* @method void json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) Sends a JSON response.
|
||||
* @method void jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf-8', int $option = 0) Sends a JSONP response.
|
||||
*
|
||||
* HTTP caching
|
||||
* @method void etag($id, string $type = 'strong') Handles ETag HTTP caching.
|
||||
* @method void lastModified(int $time) Handles last modified HTTP caching.
|
||||
*/
|
||||
class Engine {
|
||||
/**
|
||||
* Stored variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vars;
|
||||
|
||||
/**
|
||||
* Class loader.
|
||||
*
|
||||
* @var Loader
|
||||
*/
|
||||
protected $loader;
|
||||
|
||||
/**
|
||||
* Event dispatcher.
|
||||
*
|
||||
* @var Dispatcher
|
||||
*/
|
||||
protected $dispatcher;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public function __construct() {
|
||||
$this->vars = array();
|
||||
|
||||
$this->loader = new Loader();
|
||||
$this->dispatcher = new Dispatcher();
|
||||
|
||||
$this->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles calls to class methods.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param array $params Method parameters
|
||||
* @return mixed Callback results
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function __call($name, $params) {
|
||||
$callback = $this->dispatcher->get($name);
|
||||
|
||||
if (is_callable($callback)) {
|
||||
return $this->dispatcher->run($name, $params);
|
||||
}
|
||||
|
||||
if (!$this->loader->get($name)) {
|
||||
throw new \Exception("{$name} must be a mapped method.");
|
||||
}
|
||||
|
||||
$shared = (!empty($params)) ? (bool)$params[0] : true;
|
||||
|
||||
return $this->loader->load($name, $shared);
|
||||
}
|
||||
|
||||
/*** Core Methods ***/
|
||||
|
||||
/**
|
||||
* Initializes the framework.
|
||||
*/
|
||||
public function init() {
|
||||
static $initialized = false;
|
||||
$self = $this;
|
||||
|
||||
if ($initialized) {
|
||||
$this->vars = array();
|
||||
$this->loader->reset();
|
||||
$this->dispatcher->reset();
|
||||
}
|
||||
|
||||
// Register default components
|
||||
$this->loader->register('request', '\flight\net\Request');
|
||||
$this->loader->register('response', '\flight\net\Response');
|
||||
$this->loader->register('router', '\flight\net\Router');
|
||||
$this->loader->register('view', '\flight\template\View', array(), function($view) use ($self) {
|
||||
$view->path = $self->get('flight.views.path');
|
||||
$view->extension = $self->get('flight.views.extension');
|
||||
});
|
||||
|
||||
// Register framework methods
|
||||
$methods = array(
|
||||
'start','stop','route','halt','error','notFound',
|
||||
'render','redirect','etag','lastModified','json','jsonp'
|
||||
);
|
||||
foreach ($methods as $name) {
|
||||
$this->dispatcher->set($name, array($this, '_'.$name));
|
||||
}
|
||||
|
||||
// Default configuration settings
|
||||
$this->set('flight.base_url', null);
|
||||
$this->set('flight.case_sensitive', false);
|
||||
$this->set('flight.handle_errors', true);
|
||||
$this->set('flight.log_errors', false);
|
||||
$this->set('flight.views.path', './views');
|
||||
$this->set('flight.views.extension', '.php');
|
||||
|
||||
// Startup configuration
|
||||
$this->before('start', function() use ($self) {
|
||||
// Enable error handling
|
||||
if ($self->get('flight.handle_errors')) {
|
||||
set_error_handler(array($self, 'handleError'));
|
||||
set_exception_handler(array($self, 'handleException'));
|
||||
}
|
||||
|
||||
// Set case-sensitivity
|
||||
$self->router()->case_sensitive = $self->get('flight.case_sensitive');
|
||||
});
|
||||
|
||||
$initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom error handler. Converts errors into exceptions.
|
||||
*
|
||||
* @param int $errno Error number
|
||||
* @param int $errstr Error string
|
||||
* @param int $errfile Error file name
|
||||
* @param int $errline Error file line number
|
||||
* @throws \ErrorException
|
||||
*/
|
||||
public function handleError($errno, $errstr, $errfile, $errline) {
|
||||
if ($errno & error_reporting()) {
|
||||
throw new \ErrorException($errstr, $errno, 0, $errfile, $errline);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom exception handler. Logs exceptions.
|
||||
*
|
||||
* @param \Exception $e Thrown exception
|
||||
*/
|
||||
public function handleException($e) {
|
||||
if ($this->get('flight.log_errors')) {
|
||||
error_log($e->getMessage());
|
||||
}
|
||||
|
||||
$this->error($e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a callback to a framework method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
* @throws \Exception If trying to map over a framework method
|
||||
*/
|
||||
public function map($name, $callback) {
|
||||
if (method_exists($this, $name)) {
|
||||
throw new \Exception('Cannot override an existing framework method.');
|
||||
}
|
||||
|
||||
$this->dispatcher->set($name, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a class to a framework method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param string $class Class name
|
||||
* @param array $params Class initialization parameters
|
||||
* @param callback $callback Function to call after object instantiation
|
||||
* @throws \Exception If trying to map over a framework method
|
||||
*/
|
||||
public function register($name, $class, array $params = array(), $callback = null) {
|
||||
if (method_exists($this, $name)) {
|
||||
throw new \Exception('Cannot override an existing framework method.');
|
||||
}
|
||||
|
||||
$this->loader->register($name, $class, $params, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a pre-filter to a method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function before($name, $callback) {
|
||||
$this->dispatcher->hook($name, 'before', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a post-filter to a method.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function after($name, $callback) {
|
||||
$this->dispatcher->hook($name, 'after', $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a variable.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($key = null) {
|
||||
if ($key === null) return $this->vars;
|
||||
|
||||
return isset($this->vars[$key]) ? $this->vars[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a variable.
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @param string $value Value
|
||||
*/
|
||||
public function set($key, $value = null) {
|
||||
if (is_array($key) || is_object($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
$this->vars[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a variable has been set.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return bool Variable status
|
||||
*/
|
||||
public function has($key) {
|
||||
return isset($this->vars[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a variable. If no key is passed in, clear all variables.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function clear($key = null) {
|
||||
if (is_null($key)) {
|
||||
$this->vars = array();
|
||||
}
|
||||
else {
|
||||
unset($this->vars[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path for class autoloading.
|
||||
*
|
||||
* @param string $dir Directory path
|
||||
*/
|
||||
public function path($dir) {
|
||||
$this->loader->addDirectory($dir);
|
||||
}
|
||||
|
||||
/*** Extensible Methods ***/
|
||||
|
||||
/**
|
||||
* Starts the framework.
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _start() {
|
||||
$dispatched = false;
|
||||
$self = $this;
|
||||
$request = $this->request();
|
||||
$response = $this->response();
|
||||
$router = $this->router();
|
||||
|
||||
// Allow filters to run
|
||||
$this->after('start', function() use ($self) {
|
||||
$self->stop();
|
||||
});
|
||||
|
||||
// Flush any existing output
|
||||
if (ob_get_length() > 0) {
|
||||
$response->write(ob_get_clean());
|
||||
}
|
||||
|
||||
// Enable output buffering
|
||||
ob_start();
|
||||
|
||||
// Route the request
|
||||
while ($route = $router->route($request)) {
|
||||
$params = array_values($route->params);
|
||||
|
||||
// Add route info to the parameter list
|
||||
if ($route->pass) {
|
||||
$params[] = $route;
|
||||
}
|
||||
|
||||
// Call route handler
|
||||
$continue = $this->dispatcher->execute(
|
||||
$route->callback,
|
||||
$params
|
||||
);
|
||||
|
||||
$dispatched = true;
|
||||
|
||||
if (!$continue) break;
|
||||
|
||||
$router->next();
|
||||
|
||||
$dispatched = false;
|
||||
}
|
||||
|
||||
if (!$dispatched) {
|
||||
$this->notFound();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the framework and outputs the current response.
|
||||
*
|
||||
* @param int $code HTTP status code
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _stop($code = null) {
|
||||
$response = $this->response();
|
||||
|
||||
if (!$response->sent()) {
|
||||
if ($code !== null) {
|
||||
$response->status($code);
|
||||
}
|
||||
|
||||
$response->write(ob_get_clean());
|
||||
|
||||
$response->send();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes a URL to a callback function.
|
||||
*
|
||||
* @param string $pattern URL pattern to match
|
||||
* @param callback $callback Callback function
|
||||
* @param boolean $pass_route Pass the matching route object to the callback
|
||||
*/
|
||||
public function _route($pattern, $callback, $pass_route = false) {
|
||||
$this->router()->map($pattern, $callback, $pass_route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops processing and returns a given response.
|
||||
*
|
||||
* @param int $code HTTP status code
|
||||
* @param string $message Response message
|
||||
*/
|
||||
public function _halt($code = 200, $message = '') {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status($code)
|
||||
->write($message)
|
||||
->send();
|
||||
exit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP 500 response for any errors.
|
||||
*
|
||||
* @param \Exception|\Throwable $e Thrown exception
|
||||
*/
|
||||
public function _error($e) {
|
||||
$msg = sprintf('<h1>500 Internal Server Error</h1>'.
|
||||
'<h3>%s (%s)</h3>'.
|
||||
'<pre>%s</pre>',
|
||||
$e->getMessage(),
|
||||
$e->getCode(),
|
||||
$e->getTraceAsString()
|
||||
);
|
||||
|
||||
try {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status(500)
|
||||
->write($msg)
|
||||
->send();
|
||||
}
|
||||
catch (\Throwable $t) { // PHP 7.0+
|
||||
exit($msg);
|
||||
} catch(\Exception $e) { // PHP < 7
|
||||
exit($msg);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an HTTP 404 response when a URL is not found.
|
||||
*/
|
||||
public function _notFound() {
|
||||
$this->response()
|
||||
->clear()
|
||||
->status(404)
|
||||
->write(
|
||||
'<h1>404 Not Found</h1>'.
|
||||
'<h3>The page you have requested could not be found.</h3>'.
|
||||
str_repeat(' ', 512)
|
||||
)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirects the current request to another URL.
|
||||
*
|
||||
* @param string $url URL
|
||||
* @param int $code HTTP status code
|
||||
*/
|
||||
public function _redirect($url, $code = 303) {
|
||||
$base = $this->get('flight.base_url');
|
||||
|
||||
if ($base === null) {
|
||||
$base = $this->request()->base;
|
||||
}
|
||||
|
||||
// Append base url to redirect url
|
||||
if ($base != '/' && strpos($url, '://') === false) {
|
||||
$url = $base . preg_replace('#/+#', '/', '/' . $url);
|
||||
}
|
||||
|
||||
$this->response()
|
||||
->clear()
|
||||
->status($code)
|
||||
->header('Location', $url)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @param string $key View variable name
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _render($file, $data = null, $key = null) {
|
||||
if ($key !== null) {
|
||||
$this->view()->set($key, $this->view()->fetch($file, $data));
|
||||
}
|
||||
else {
|
||||
$this->view()->render($file, $data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a JSON response.
|
||||
*
|
||||
* @param mixed $data JSON data
|
||||
* @param int $code HTTP status code
|
||||
* @param bool $encode Whether to perform JSON encoding
|
||||
* @param string $charset Charset
|
||||
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _json(
|
||||
$data,
|
||||
$code = 200,
|
||||
$encode = true,
|
||||
$charset = 'utf-8',
|
||||
$option = 0
|
||||
) {
|
||||
$json = ($encode) ? json_encode($data, $option) : $data;
|
||||
|
||||
$this->response()
|
||||
->status($code)
|
||||
->header('Content-Type', 'application/json; charset='.$charset)
|
||||
->write($json)
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a JSONP response.
|
||||
*
|
||||
* @param mixed $data JSON data
|
||||
* @param string $param Query parameter that specifies the callback name.
|
||||
* @param int $code HTTP status code
|
||||
* @param bool $encode Whether to perform JSON encoding
|
||||
* @param string $charset Charset
|
||||
* @param int $option Bitmask Json constant such as JSON_HEX_QUOT
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function _jsonp(
|
||||
$data,
|
||||
$param = 'jsonp',
|
||||
$code = 200,
|
||||
$encode = true,
|
||||
$charset = 'utf-8',
|
||||
$option = 0
|
||||
) {
|
||||
$json = ($encode) ? json_encode($data, $option) : $data;
|
||||
|
||||
$callback = $this->request()->query[$param];
|
||||
|
||||
$this->response()
|
||||
->status($code)
|
||||
->header('Content-Type', 'application/javascript; charset='.$charset)
|
||||
->write($callback.'('.$json.');')
|
||||
->send();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles ETag HTTP caching.
|
||||
*
|
||||
* @param string $id ETag identifier
|
||||
* @param string $type ETag type
|
||||
*/
|
||||
public function _etag($id, $type = 'strong') {
|
||||
$id = (($type === 'weak') ? 'W/' : '').$id;
|
||||
|
||||
$this->response()->header('ETag', $id);
|
||||
|
||||
if (isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
|
||||
$_SERVER['HTTP_IF_NONE_MATCH'] === $id) {
|
||||
$this->halt(304);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles last modified HTTP caching.
|
||||
*
|
||||
* @param int $time Unix timestamp
|
||||
*/
|
||||
public function _lastModified($time) {
|
||||
$this->response()->header('Last-Modified', gmdate('D, d M Y H:i:s \G\M\T', $time));
|
||||
|
||||
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
|
||||
strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) === $time) {
|
||||
$this->halt(304);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Flight class is a static representation of the framework.
|
||||
*
|
||||
* Core.
|
||||
* @method static void start() Starts the framework.
|
||||
* @method static void path($path) Adds a path for autoloading classes.
|
||||
* @method static void stop() Stops the framework and sends a response.
|
||||
* @method static void halt($code = 200, $message = '') Stop the framework with an optional status code and message.
|
||||
*
|
||||
* Routing.
|
||||
* @method static void route($pattern, $callback) Maps a URL pattern to a callback.
|
||||
* @method static \flight\net\Router router() Returns Router instance.
|
||||
*
|
||||
* Extending & Overriding.
|
||||
* @method static void map($name, $callback) Creates a custom framework method.
|
||||
* @method static void register($name, $class, array $params = array(), $callback = null) Registers a class to a framework method.
|
||||
*
|
||||
* Filtering.
|
||||
* @method static void before($name, $callback) Adds a filter before a framework method.
|
||||
* @method static void after($name, $callback) Adds a filter after a framework method.
|
||||
*
|
||||
* Variables.
|
||||
* @method static void set($key, $value) Sets a variable.
|
||||
* @method static mixed get($key) Gets a variable.
|
||||
* @method static bool has($key) Checks if a variable is set.
|
||||
* @method static void clear($key = null) Clears a variable.
|
||||
*
|
||||
* Views.
|
||||
* @method static void render($file, array $data = null, $key = null) Renders a template file.
|
||||
* @method static \flight\template\View view() Returns View instance.
|
||||
*
|
||||
* Request & Response.
|
||||
* @method static \flight\net\Request request() Returns Request instance.
|
||||
* @method static \flight\net\Response response() Returns Response instance.
|
||||
* @method static void redirect($url, $code = 303) Redirects to another URL.
|
||||
* @method static void json($data, $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSON response.
|
||||
* @method static void jsonp($data, $param = 'jsonp', $code = 200, $encode = true, $charset = "utf8", $encodeOption = 0, $encodeDepth = 512) Sends a JSONP response.
|
||||
* @method static void error($exception) Sends an HTTP 500 response.
|
||||
* @method static void notFound() Sends an HTTP 404 response.
|
||||
*
|
||||
* HTTP Caching.
|
||||
* @method static void etag($id, $type = 'strong') Performs ETag HTTP caching.
|
||||
* @method static void lastModified($time) Performs last modified HTTP caching.
|
||||
*/
|
||||
class Flight {
|
||||
/**
|
||||
* Framework engine.
|
||||
*
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private static $engine;
|
||||
|
||||
// Don't allow object instantiation
|
||||
private function __construct() {}
|
||||
private function __destruct() {}
|
||||
private function __clone() {}
|
||||
|
||||
/**
|
||||
* Handles calls to static methods.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param array $params Method parameters
|
||||
* @return mixed Callback results
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function __callStatic($name, $params) {
|
||||
$app = Flight::app();
|
||||
|
||||
return \flight\core\Dispatcher::invokeMethod(array($app, $name), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \flight\Engine Application instance
|
||||
*/
|
||||
public static function app() {
|
||||
static $initialized = false;
|
||||
|
||||
if (!$initialized) {
|
||||
require_once __DIR__.'/autoload.php';
|
||||
|
||||
self::$engine = new \flight\Engine();
|
||||
|
||||
$initialized = true;
|
||||
}
|
||||
|
||||
return self::$engine;
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2013, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once __DIR__.'/core/Loader.php';
|
||||
|
||||
\flight\core\Loader::autoload(true, dirname(__DIR__));
|
@ -0,0 +1,232 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\core;
|
||||
|
||||
/**
|
||||
* The Dispatcher class is responsible for dispatching events. Events
|
||||
* are simply aliases for class methods or functions. The Dispatcher
|
||||
* allows you to hook other functions to an event that can modify the
|
||||
* input parameters and/or the output.
|
||||
*/
|
||||
class Dispatcher {
|
||||
/**
|
||||
* Mapped events.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $events = array();
|
||||
|
||||
/**
|
||||
* Method filters.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $filters = array();
|
||||
|
||||
/**
|
||||
* Dispatches an event.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @param array $params Callback parameters
|
||||
* @return string Output of callback
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function run($name, array $params = array()) {
|
||||
$output = '';
|
||||
|
||||
// Run pre-filters
|
||||
if (!empty($this->filters[$name]['before'])) {
|
||||
$this->filter($this->filters[$name]['before'], $params, $output);
|
||||
}
|
||||
|
||||
// Run requested method
|
||||
$output = $this->execute($this->get($name), $params);
|
||||
|
||||
// Run post-filters
|
||||
if (!empty($this->filters[$name]['after'])) {
|
||||
$this->filter($this->filters[$name]['after'], $params, $output);
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns a callback to an event.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function set($name, $callback) {
|
||||
$this->events[$name] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an assigned callback.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @return callback $callback Callback function
|
||||
*/
|
||||
public function get($name) {
|
||||
return isset($this->events[$name]) ? $this->events[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an event has been set.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @return bool Event status
|
||||
*/
|
||||
public function has($name) {
|
||||
return isset($this->events[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears an event. If no name is given,
|
||||
* all events are removed.
|
||||
*
|
||||
* @param string $name Event name
|
||||
*/
|
||||
public function clear($name = null) {
|
||||
if ($name !== null) {
|
||||
unset($this->events[$name]);
|
||||
unset($this->filters[$name]);
|
||||
}
|
||||
else {
|
||||
$this->events = array();
|
||||
$this->filters = array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks a callback to an event.
|
||||
*
|
||||
* @param string $name Event name
|
||||
* @param string $type Filter type
|
||||
* @param callback $callback Callback function
|
||||
*/
|
||||
public function hook($name, $type, $callback) {
|
||||
$this->filters[$name][$type][] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a chain of method filters.
|
||||
*
|
||||
* @param array $filters Chain of filters
|
||||
* @param array $params Method parameters
|
||||
* @param mixed $output Method output
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function filter($filters, &$params, &$output) {
|
||||
$args = array(&$params, &$output);
|
||||
foreach ($filters as $callback) {
|
||||
$continue = $this->execute($callback, $args);
|
||||
if ($continue === false) break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes a callback function.
|
||||
*
|
||||
* @param callback $callback Callback function
|
||||
* @param array $params Function parameters
|
||||
* @return mixed Function results
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function execute($callback, array &$params = array()) {
|
||||
if (is_callable($callback)) {
|
||||
return is_array($callback) ?
|
||||
self::invokeMethod($callback, $params) :
|
||||
self::callFunction($callback, $params);
|
||||
}
|
||||
else {
|
||||
throw new \Exception('Invalid callback specified.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls a function.
|
||||
*
|
||||
* @param string $func Name of function to call
|
||||
* @param array $params Function parameters
|
||||
* @return mixed Function results
|
||||
*/
|
||||
public static function callFunction($func, array &$params = array()) {
|
||||
// Call static method
|
||||
if (is_string($func) && strpos($func, '::') !== false) {
|
||||
return call_user_func_array($func, $params);
|
||||
}
|
||||
|
||||
switch (count($params)) {
|
||||
case 0:
|
||||
return $func();
|
||||
case 1:
|
||||
return $func($params[0]);
|
||||
case 2:
|
||||
return $func($params[0], $params[1]);
|
||||
case 3:
|
||||
return $func($params[0], $params[1], $params[2]);
|
||||
case 4:
|
||||
return $func($params[0], $params[1], $params[2], $params[3]);
|
||||
case 5:
|
||||
return $func($params[0], $params[1], $params[2], $params[3], $params[4]);
|
||||
default:
|
||||
return call_user_func_array($func, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes a method.
|
||||
*
|
||||
* @param mixed $func Class method
|
||||
* @param array $params Class method parameters
|
||||
* @return mixed Function results
|
||||
*/
|
||||
public static function invokeMethod($func, array &$params = array()) {
|
||||
list($class, $method) = $func;
|
||||
|
||||
$instance = is_object($class);
|
||||
|
||||
switch (count($params)) {
|
||||
case 0:
|
||||
return ($instance) ?
|
||||
$class->$method() :
|
||||
$class::$method();
|
||||
case 1:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0]) :
|
||||
$class::$method($params[0]);
|
||||
case 2:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1]) :
|
||||
$class::$method($params[0], $params[1]);
|
||||
case 3:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2]) :
|
||||
$class::$method($params[0], $params[1], $params[2]);
|
||||
case 4:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2], $params[3]) :
|
||||
$class::$method($params[0], $params[1], $params[2], $params[3]);
|
||||
case 5:
|
||||
return ($instance) ?
|
||||
$class->$method($params[0], $params[1], $params[2], $params[3], $params[4]) :
|
||||
$class::$method($params[0], $params[1], $params[2], $params[3], $params[4]);
|
||||
default:
|
||||
return call_user_func_array($func, $params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the object to the initial state.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->events = array();
|
||||
$this->filters = array();
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\core;
|
||||
|
||||
/**
|
||||
* The Loader class is responsible for loading objects. It maintains
|
||||
* a list of reusable class instances and can generate a new class
|
||||
* instances with custom initialization parameters. It also performs
|
||||
* class autoloading.
|
||||
*/
|
||||
class Loader {
|
||||
/**
|
||||
* Registered classes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $classes = array();
|
||||
|
||||
/**
|
||||
* Class instances.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $instances = array();
|
||||
|
||||
/**
|
||||
* Autoload directories.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected static $dirs = array();
|
||||
|
||||
/**
|
||||
* Registers a class.
|
||||
*
|
||||
* @param string $name Registry name
|
||||
* @param string|callable $class Class name or function to instantiate class
|
||||
* @param array $params Class initialization parameters
|
||||
* @param callback $callback Function to call after object instantiation
|
||||
*/
|
||||
public function register($name, $class, array $params = array(), $callback = null) {
|
||||
unset($this->instances[$name]);
|
||||
|
||||
$this->classes[$name] = array($class, $params, $callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a class.
|
||||
*
|
||||
* @param string $name Registry name
|
||||
*/
|
||||
public function unregister($name) {
|
||||
unset($this->classes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a registered class.
|
||||
*
|
||||
* @param string $name Method name
|
||||
* @param bool $shared Shared instance
|
||||
* @return object Class instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function load($name, $shared = true) {
|
||||
$obj = null;
|
||||
|
||||
if (isset($this->classes[$name])) {
|
||||
list($class, $params, $callback) = $this->classes[$name];
|
||||
|
||||
$exists = isset($this->instances[$name]);
|
||||
|
||||
if ($shared) {
|
||||
$obj = ($exists) ?
|
||||
$this->getInstance($name) :
|
||||
$this->newInstance($class, $params);
|
||||
|
||||
if (!$exists) {
|
||||
$this->instances[$name] = $obj;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$obj = $this->newInstance($class, $params);
|
||||
}
|
||||
|
||||
if ($callback && (!$shared || !$exists)) {
|
||||
$ref = array(&$obj);
|
||||
call_user_func_array($callback, $ref);
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single instance of a class.
|
||||
*
|
||||
* @param string $name Instance name
|
||||
* @return object Class instance
|
||||
*/
|
||||
public function getInstance($name) {
|
||||
return isset($this->instances[$name]) ? $this->instances[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new instance of a class.
|
||||
*
|
||||
* @param string|callable $class Class name or callback function to instantiate class
|
||||
* @param array $params Class initialization parameters
|
||||
* @return object Class instance
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function newInstance($class, array $params = array()) {
|
||||
if (is_callable($class)) {
|
||||
return call_user_func_array($class, $params);
|
||||
}
|
||||
|
||||
switch (count($params)) {
|
||||
case 0:
|
||||
return new $class();
|
||||
case 1:
|
||||
return new $class($params[0]);
|
||||
case 2:
|
||||
return new $class($params[0], $params[1]);
|
||||
case 3:
|
||||
return new $class($params[0], $params[1], $params[2]);
|
||||
case 4:
|
||||
return new $class($params[0], $params[1], $params[2], $params[3]);
|
||||
case 5:
|
||||
return new $class($params[0], $params[1], $params[2], $params[3], $params[4]);
|
||||
default:
|
||||
try {
|
||||
$refClass = new \ReflectionClass($class);
|
||||
return $refClass->newInstanceArgs($params);
|
||||
} catch (\ReflectionException $e) {
|
||||
throw new \Exception("Cannot instantiate {$class}", 0, $e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name Registry name
|
||||
* @return mixed Class information or null if not registered
|
||||
*/
|
||||
public function get($name) {
|
||||
return isset($this->classes[$name]) ? $this->classes[$name] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the object to the initial state.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->classes = array();
|
||||
$this->instances = array();
|
||||
}
|
||||
|
||||
/*** Autoloading Functions ***/
|
||||
|
||||
/**
|
||||
* Starts/stops autoloader.
|
||||
*
|
||||
* @param bool $enabled Enable/disable autoloading
|
||||
* @param array $dirs Autoload directories
|
||||
*/
|
||||
public static function autoload($enabled = true, $dirs = array()) {
|
||||
if ($enabled) {
|
||||
spl_autoload_register(array(__CLASS__, 'loadClass'));
|
||||
}
|
||||
else {
|
||||
spl_autoload_unregister(array(__CLASS__, 'loadClass'));
|
||||
}
|
||||
|
||||
if (!empty($dirs)) {
|
||||
self::addDirectory($dirs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Autoloads classes.
|
||||
*
|
||||
* @param string $class Class name
|
||||
*/
|
||||
public static function loadClass($class) {
|
||||
$class_file = str_replace(array('\\', '_'), '/', $class).'.php';
|
||||
|
||||
foreach (self::$dirs as $dir) {
|
||||
$file = $dir.'/'.$class_file;
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a directory for autoloading classes.
|
||||
*
|
||||
* @param mixed $dir Directory path
|
||||
*/
|
||||
public static function addDirectory($dir) {
|
||||
if (is_array($dir) || is_object($dir)) {
|
||||
foreach ($dir as $value) {
|
||||
self::addDirectory($value);
|
||||
}
|
||||
}
|
||||
else if (is_string($dir)) {
|
||||
if (!in_array($dir, self::$dirs)) self::$dirs[] = $dir;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,289 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
use flight\util\Collection;
|
||||
|
||||
/**
|
||||
* The Request class represents an HTTP request. Data from
|
||||
* all the super globals $_GET, $_POST, $_COOKIE, and $_FILES
|
||||
* are stored and accessible via the Request object.
|
||||
*
|
||||
* The default request properties are:
|
||||
* url - The URL being requested
|
||||
* base - The parent subdirectory of the URL
|
||||
* method - The request method (GET, POST, PUT, DELETE)
|
||||
* referrer - The referrer URL
|
||||
* ip - IP address of the client
|
||||
* ajax - Whether the request is an AJAX request
|
||||
* scheme - The server protocol (http, https)
|
||||
* user_agent - Browser information
|
||||
* type - The content type
|
||||
* length - The content length
|
||||
* query - Query string parameters
|
||||
* data - Post parameters
|
||||
* cookies - Cookie parameters
|
||||
* files - Uploaded files
|
||||
* secure - Connection is secure
|
||||
* accept - HTTP accept parameters
|
||||
* proxy_ip - Proxy IP address of the client
|
||||
*/
|
||||
class Request {
|
||||
/**
|
||||
* @var string URL being requested
|
||||
*/
|
||||
public $url;
|
||||
|
||||
/**
|
||||
* @var string Parent subdirectory of the URL
|
||||
*/
|
||||
public $base;
|
||||
|
||||
/**
|
||||
* @var string Request method (GET, POST, PUT, DELETE)
|
||||
*/
|
||||
public $method;
|
||||
|
||||
/**
|
||||
* @var string Referrer URL
|
||||
*/
|
||||
public $referrer;
|
||||
|
||||
/**
|
||||
* @var string IP address of the client
|
||||
*/
|
||||
public $ip;
|
||||
|
||||
/**
|
||||
* @var bool Whether the request is an AJAX request
|
||||
*/
|
||||
public $ajax;
|
||||
|
||||
/**
|
||||
* @var string Server protocol (http, https)
|
||||
*/
|
||||
public $scheme;
|
||||
|
||||
/**
|
||||
* @var string Browser information
|
||||
*/
|
||||
public $user_agent;
|
||||
|
||||
/**
|
||||
* @var string Content type
|
||||
*/
|
||||
public $type;
|
||||
|
||||
/**
|
||||
* @var int Content length
|
||||
*/
|
||||
public $length;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Query string parameters
|
||||
*/
|
||||
public $query;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Post parameters
|
||||
*/
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Cookie parameters
|
||||
*/
|
||||
public $cookies;
|
||||
|
||||
/**
|
||||
* @var \flight\util\Collection Uploaded files
|
||||
*/
|
||||
public $files;
|
||||
|
||||
/**
|
||||
* @var bool Whether the connection is secure
|
||||
*/
|
||||
public $secure;
|
||||
|
||||
/**
|
||||
* @var string HTTP accept parameters
|
||||
*/
|
||||
public $accept;
|
||||
|
||||
/**
|
||||
* @var string Proxy IP address of the client
|
||||
*/
|
||||
public $proxy_ip;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $config Request configuration
|
||||
*/
|
||||
public function __construct($config = array()) {
|
||||
// Default properties
|
||||
if (empty($config)) {
|
||||
$config = array(
|
||||
'url' => str_replace('@', '%40', self::getVar('REQUEST_URI', '/')),
|
||||
'base' => str_replace(array('\\',' '), array('/','%20'), dirname(self::getVar('SCRIPT_NAME'))),
|
||||
'method' => self::getMethod(),
|
||||
'referrer' => self::getVar('HTTP_REFERER'),
|
||||
'ip' => self::getVar('REMOTE_ADDR'),
|
||||
'ajax' => self::getVar('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest',
|
||||
'scheme' => self::getVar('SERVER_PROTOCOL', 'HTTP/1.1'),
|
||||
'user_agent' => self::getVar('HTTP_USER_AGENT'),
|
||||
'type' => self::getVar('CONTENT_TYPE'),
|
||||
'length' => self::getVar('CONTENT_LENGTH', 0),
|
||||
'query' => new Collection($_GET),
|
||||
'data' => new Collection($_POST),
|
||||
'cookies' => new Collection($_COOKIE),
|
||||
'files' => new Collection($_FILES),
|
||||
'secure' => self::getVar('HTTPS', 'off') != 'off',
|
||||
'accept' => self::getVar('HTTP_ACCEPT'),
|
||||
'proxy_ip' => self::getProxyIpAddress()
|
||||
);
|
||||
}
|
||||
|
||||
$this->init($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize request properties.
|
||||
*
|
||||
* @param array $properties Array of request properties
|
||||
*/
|
||||
public function init($properties = array()) {
|
||||
// Set all the defined properties
|
||||
foreach ($properties as $name => $value) {
|
||||
$this->$name = $value;
|
||||
}
|
||||
|
||||
// Get the requested URL without the base directory
|
||||
if ($this->base != '/' && strlen($this->base) > 0 && strpos($this->url, $this->base) === 0) {
|
||||
$this->url = substr($this->url, strlen($this->base));
|
||||
}
|
||||
|
||||
// Default url
|
||||
if (empty($this->url)) {
|
||||
$this->url = '/';
|
||||
}
|
||||
// Merge URL query parameters with $_GET
|
||||
else {
|
||||
$_GET += self::parseQuery($this->url);
|
||||
|
||||
$this->query->setData($_GET);
|
||||
}
|
||||
|
||||
// Check for JSON input
|
||||
if (strpos($this->type, 'application/json') === 0) {
|
||||
$body = $this->getBody();
|
||||
if ($body != '') {
|
||||
$data = json_decode($body, true);
|
||||
if ($data != null) {
|
||||
$this->data->setData($data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the body of the request.
|
||||
*
|
||||
* @return string Raw HTTP request body
|
||||
*/
|
||||
public static function getBody() {
|
||||
static $body;
|
||||
|
||||
if (!is_null($body)) {
|
||||
return $body;
|
||||
}
|
||||
|
||||
$method = self::getMethod();
|
||||
|
||||
if ($method == 'POST' || $method == 'PUT' || $method == 'PATCH') {
|
||||
$body = file_get_contents('php://input');
|
||||
}
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the request method.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getMethod() {
|
||||
$method = self::getVar('REQUEST_METHOD', 'GET');
|
||||
|
||||
if (isset($_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'])) {
|
||||
$method = $_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'];
|
||||
}
|
||||
elseif (isset($_REQUEST['_method'])) {
|
||||
$method = $_REQUEST['_method'];
|
||||
}
|
||||
|
||||
return strtoupper($method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the real remote IP address.
|
||||
*
|
||||
* @return string IP address
|
||||
*/
|
||||
public static function getProxyIpAddress() {
|
||||
static $forwarded = array(
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED',
|
||||
'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED'
|
||||
);
|
||||
|
||||
$flags = \FILTER_FLAG_NO_PRIV_RANGE | \FILTER_FLAG_NO_RES_RANGE;
|
||||
|
||||
foreach ($forwarded as $key) {
|
||||
if (array_key_exists($key, $_SERVER)) {
|
||||
sscanf($_SERVER[$key], '%[^,]', $ip);
|
||||
if (filter_var($ip, \FILTER_VALIDATE_IP, $flags) !== false) {
|
||||
return $ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a variable from $_SERVER using $default if not provided.
|
||||
*
|
||||
* @param string $var Variable name
|
||||
* @param string $default Default value to substitute
|
||||
* @return string Server variable value
|
||||
*/
|
||||
public static function getVar($var, $default = '') {
|
||||
return isset($_SERVER[$var]) ? $_SERVER[$var] : $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse query parameters from a URL.
|
||||
*
|
||||
* @param string $url URL string
|
||||
* @return array Query parameters
|
||||
*/
|
||||
public static function parseQuery($url) {
|
||||
$params = array();
|
||||
|
||||
$args = parse_url($url);
|
||||
if (isset($args['query'])) {
|
||||
parse_str($args['query'], $params);
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
}
|
@ -0,0 +1,299 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
/**
|
||||
* The Response class represents an HTTP response. The object
|
||||
* contains the response headers, HTTP status code, and response
|
||||
* body.
|
||||
*/
|
||||
class Response {
|
||||
/**
|
||||
* @var int HTTP status
|
||||
*/
|
||||
protected $status = 200;
|
||||
|
||||
/**
|
||||
* @var array HTTP headers
|
||||
*/
|
||||
protected $headers = array();
|
||||
|
||||
/**
|
||||
* @var string HTTP response body
|
||||
*/
|
||||
protected $body;
|
||||
|
||||
/**
|
||||
* @var bool HTTP response sent
|
||||
*/
|
||||
protected $sent = false;
|
||||
|
||||
/**
|
||||
* @var array HTTP status codes
|
||||
*/
|
||||
public static $codes = array(
|
||||
100 => 'Continue',
|
||||
101 => 'Switching Protocols',
|
||||
102 => 'Processing',
|
||||
|
||||
200 => 'OK',
|
||||
201 => 'Created',
|
||||
202 => 'Accepted',
|
||||
203 => 'Non-Authoritative Information',
|
||||
204 => 'No Content',
|
||||
205 => 'Reset Content',
|
||||
206 => 'Partial Content',
|
||||
207 => 'Multi-Status',
|
||||
208 => 'Already Reported',
|
||||
|
||||
226 => 'IM Used',
|
||||
|
||||
300 => 'Multiple Choices',
|
||||
301 => 'Moved Permanently',
|
||||
302 => 'Found',
|
||||
303 => 'See Other',
|
||||
304 => 'Not Modified',
|
||||
305 => 'Use Proxy',
|
||||
306 => '(Unused)',
|
||||
307 => 'Temporary Redirect',
|
||||
308 => 'Permanent Redirect',
|
||||
|
||||
400 => 'Bad Request',
|
||||
401 => 'Unauthorized',
|
||||
402 => 'Payment Required',
|
||||
403 => 'Forbidden',
|
||||
404 => 'Not Found',
|
||||
405 => 'Method Not Allowed',
|
||||
406 => 'Not Acceptable',
|
||||
407 => 'Proxy Authentication Required',
|
||||
408 => 'Request Timeout',
|
||||
409 => 'Conflict',
|
||||
410 => 'Gone',
|
||||
411 => 'Length Required',
|
||||
412 => 'Precondition Failed',
|
||||
413 => 'Payload Too Large',
|
||||
414 => 'URI Too Long',
|
||||
415 => 'Unsupported Media Type',
|
||||
416 => 'Range Not Satisfiable',
|
||||
417 => 'Expectation Failed',
|
||||
|
||||
422 => 'Unprocessable Entity',
|
||||
423 => 'Locked',
|
||||
424 => 'Failed Dependency',
|
||||
|
||||
426 => 'Upgrade Required',
|
||||
|
||||
428 => 'Precondition Required',
|
||||
429 => 'Too Many Requests',
|
||||
|
||||
431 => 'Request Header Fields Too Large',
|
||||
|
||||
500 => 'Internal Server Error',
|
||||
501 => 'Not Implemented',
|
||||
502 => 'Bad Gateway',
|
||||
503 => 'Service Unavailable',
|
||||
504 => 'Gateway Timeout',
|
||||
505 => 'HTTP Version Not Supported',
|
||||
506 => 'Variant Also Negotiates',
|
||||
507 => 'Insufficient Storage',
|
||||
508 => 'Loop Detected',
|
||||
|
||||
510 => 'Not Extended',
|
||||
511 => 'Network Authentication Required'
|
||||
);
|
||||
|
||||
/**
|
||||
* Sets the HTTP status of the response.
|
||||
*
|
||||
* @param int $code HTTP status code.
|
||||
* @return object|int Self reference
|
||||
* @throws \Exception If invalid status code
|
||||
*/
|
||||
public function status($code = null) {
|
||||
if ($code === null) {
|
||||
return $this->status;
|
||||
}
|
||||
|
||||
if (array_key_exists($code, self::$codes)) {
|
||||
$this->status = $code;
|
||||
}
|
||||
else {
|
||||
throw new \Exception('Invalid status code.');
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header to the response.
|
||||
*
|
||||
* @param string|array $name Header name or array of names and values
|
||||
* @param string $value Header value
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function header($name, $value = null) {
|
||||
if (is_array($name)) {
|
||||
foreach ($name as $k => $v) {
|
||||
$this->headers[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->headers[$name] = $value;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the headers from the response
|
||||
* @return array
|
||||
*/
|
||||
public function headers() {
|
||||
return $this->headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes content to the response body.
|
||||
*
|
||||
* @param string $str Response content
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function write($str) {
|
||||
$this->body .= $str;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the response.
|
||||
*
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function clear() {
|
||||
$this->status = 200;
|
||||
$this->headers = array();
|
||||
$this->body = '';
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets caching headers for the response.
|
||||
*
|
||||
* @param int|string $expires Expiration time
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function cache($expires) {
|
||||
if ($expires === false) {
|
||||
$this->headers['Expires'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
|
||||
$this->headers['Cache-Control'] = array(
|
||||
'no-store, no-cache, must-revalidate',
|
||||
'post-check=0, pre-check=0',
|
||||
'max-age=0'
|
||||
);
|
||||
$this->headers['Pragma'] = 'no-cache';
|
||||
}
|
||||
else {
|
||||
$expires = is_int($expires) ? $expires : strtotime($expires);
|
||||
$this->headers['Expires'] = gmdate('D, d M Y H:i:s', $expires) . ' GMT';
|
||||
$this->headers['Cache-Control'] = 'max-age='.($expires - time());
|
||||
if (isset($this->headers['Pragma']) && $this->headers['Pragma'] == 'no-cache'){
|
||||
unset($this->headers['Pragma']);
|
||||
}
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends HTTP headers.
|
||||
*
|
||||
* @return object Self reference
|
||||
*/
|
||||
public function sendHeaders() {
|
||||
// Send status code header
|
||||
if (strpos(php_sapi_name(), 'cgi') !== false) {
|
||||
header(
|
||||
sprintf(
|
||||
'Status: %d %s',
|
||||
$this->status,
|
||||
self::$codes[$this->status]
|
||||
),
|
||||
true
|
||||
);
|
||||
}
|
||||
else {
|
||||
header(
|
||||
sprintf(
|
||||
'%s %d %s',
|
||||
(isset($_SERVER['SERVER_PROTOCOL']) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.1'),
|
||||
$this->status,
|
||||
self::$codes[$this->status]),
|
||||
true,
|
||||
$this->status
|
||||
);
|
||||
}
|
||||
|
||||
// Send other headers
|
||||
foreach ($this->headers as $field => $value) {
|
||||
if (is_array($value)) {
|
||||
foreach ($value as $v) {
|
||||
header($field.': '.$v, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
header($field.': '.$value);
|
||||
}
|
||||
}
|
||||
|
||||
// Send content length
|
||||
$length = $this->getContentLength();
|
||||
|
||||
if ($length > 0) {
|
||||
header('Content-Length: '.$length);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the content length.
|
||||
*
|
||||
* @return string Content length
|
||||
*/
|
||||
public function getContentLength() {
|
||||
return extension_loaded('mbstring') ?
|
||||
mb_strlen($this->body, 'latin1') :
|
||||
strlen($this->body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether response was sent.
|
||||
*/
|
||||
public function sent() {
|
||||
return $this->sent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a HTTP response.
|
||||
*/
|
||||
public function send() {
|
||||
if (ob_get_length() > 0) {
|
||||
ob_end_clean();
|
||||
}
|
||||
|
||||
if (!headers_sent()) {
|
||||
$this->sendHeaders();
|
||||
}
|
||||
|
||||
echo $this->body;
|
||||
|
||||
$this->sent = true;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,144 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
/**
|
||||
* The Route class is responsible for routing an HTTP request to
|
||||
* an assigned callback function. The Router tries to match the
|
||||
* requested URL against a series of URL patterns.
|
||||
*/
|
||||
class Route {
|
||||
/**
|
||||
* @var string URL pattern
|
||||
*/
|
||||
public $pattern;
|
||||
|
||||
/**
|
||||
* @var mixed Callback function
|
||||
*/
|
||||
public $callback;
|
||||
|
||||
/**
|
||||
* @var array HTTP methods
|
||||
*/
|
||||
public $methods = array();
|
||||
|
||||
/**
|
||||
* @var array Route parameters
|
||||
*/
|
||||
public $params = array();
|
||||
|
||||
/**
|
||||
* @var string Matching regular expression
|
||||
*/
|
||||
public $regex;
|
||||
|
||||
/**
|
||||
* @var string URL splat content
|
||||
*/
|
||||
public $splat = '';
|
||||
|
||||
/**
|
||||
* @var boolean Pass self in callback parameters
|
||||
*/
|
||||
public $pass = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $pattern URL pattern
|
||||
* @param mixed $callback Callback function
|
||||
* @param array $methods HTTP methods
|
||||
* @param boolean $pass Pass self in callback parameters
|
||||
*/
|
||||
public function __construct($pattern, $callback, $methods, $pass) {
|
||||
$this->pattern = $pattern;
|
||||
$this->callback = $callback;
|
||||
$this->methods = $methods;
|
||||
$this->pass = $pass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a URL matches the route pattern. Also parses named parameters in the URL.
|
||||
*
|
||||
* @param string $url Requested URL
|
||||
* @param boolean $case_sensitive Case sensitive matching
|
||||
* @return boolean Match status
|
||||
*/
|
||||
public function matchUrl($url, $case_sensitive = false) {
|
||||
// Wildcard or exact match
|
||||
if ($this->pattern === '*' || $this->pattern === $url) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$ids = array();
|
||||
$last_char = substr($this->pattern, -1);
|
||||
|
||||
// Get splat
|
||||
if ($last_char === '*') {
|
||||
$n = 0;
|
||||
$len = strlen($url);
|
||||
$count = substr_count($this->pattern, '/');
|
||||
|
||||
for ($i = 0; $i < $len; $i++) {
|
||||
if ($url[$i] == '/') $n++;
|
||||
if ($n == $count) break;
|
||||
}
|
||||
|
||||
$this->splat = (string)substr($url, $i+1);
|
||||
}
|
||||
|
||||
// Build the regex for matching
|
||||
$regex = str_replace(array(')','/*'), array(')?','(/?|/.*?)'), $this->pattern);
|
||||
|
||||
$regex = preg_replace_callback(
|
||||
'#@([\w]+)(:([^/\(\)]*))?#',
|
||||
function($matches) use (&$ids) {
|
||||
$ids[$matches[1]] = null;
|
||||
if (isset($matches[3])) {
|
||||
return '(?P<'.$matches[1].'>'.$matches[3].')';
|
||||
}
|
||||
return '(?P<'.$matches[1].'>[^/\?]+)';
|
||||
},
|
||||
$regex
|
||||
);
|
||||
|
||||
// Fix trailing slash
|
||||
if ($last_char === '/') {
|
||||
$regex .= '?';
|
||||
}
|
||||
// Allow trailing slash
|
||||
else {
|
||||
$regex .= '/?';
|
||||
}
|
||||
|
||||
// Attempt to match route and named parameters
|
||||
if (preg_match('#^'.$regex.'(?:\?.*)?$#'.(($case_sensitive) ? '' : 'i'), $url, $matches)) {
|
||||
foreach ($ids as $k => $v) {
|
||||
$this->params[$k] = (array_key_exists($k, $matches)) ? urldecode($matches[$k]) : null;
|
||||
}
|
||||
|
||||
$this->regex = $regex;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an HTTP method matches the route methods.
|
||||
*
|
||||
* @param string $method HTTP method
|
||||
* @return bool Match status
|
||||
*/
|
||||
public function matchMethod($method) {
|
||||
return count(array_intersect(array($method, '*'), $this->methods)) > 0;
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\net;
|
||||
|
||||
/**
|
||||
* The Router class is responsible for routing an HTTP request to
|
||||
* an assigned callback function. The Router tries to match the
|
||||
* requested URL against a series of URL patterns.
|
||||
*/
|
||||
class Router {
|
||||
/**
|
||||
* Mapped routes.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routes = array();
|
||||
|
||||
/**
|
||||
* Pointer to current route.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
protected $index = 0;
|
||||
|
||||
/**
|
||||
* Case sensitive matching.
|
||||
*
|
||||
* @var boolean
|
||||
*/
|
||||
public $case_sensitive = false;
|
||||
|
||||
/**
|
||||
* Gets mapped routes.
|
||||
*
|
||||
* @return array Array of routes
|
||||
*/
|
||||
public function getRoutes() {
|
||||
return $this->routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all routes in the router.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->routes = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a URL pattern to a callback function.
|
||||
*
|
||||
* @param string $pattern URL pattern to match
|
||||
* @param callback $callback Callback function
|
||||
* @param boolean $pass_route Pass the matching route object to the callback
|
||||
*/
|
||||
public function map($pattern, $callback, $pass_route = false) {
|
||||
$url = $pattern;
|
||||
$methods = array('*');
|
||||
|
||||
if (strpos($pattern, ' ') !== false) {
|
||||
list($method, $url) = explode(' ', trim($pattern), 2);
|
||||
|
||||
$methods = explode('|', $method);
|
||||
}
|
||||
|
||||
$this->routes[] = new Route($url, $callback, $methods, $pass_route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Routes the current request.
|
||||
*
|
||||
* @param Request $request Request object
|
||||
* @return Route|bool Matching route or false if no match
|
||||
*/
|
||||
public function route(Request $request) {
|
||||
while ($route = $this->current()) {
|
||||
if ($route !== false && $route->matchMethod($request->method) && $route->matchUrl($request->url, $this->case_sensitive)) {
|
||||
return $route;
|
||||
}
|
||||
$this->next();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current route.
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function current() {
|
||||
return isset($this->routes[$this->index]) ? $this->routes[$this->index] : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next route.
|
||||
*
|
||||
* @return Route
|
||||
*/
|
||||
public function next() {
|
||||
$this->index++;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to the first route.
|
||||
*/
|
||||
public function reset() {
|
||||
$this->index = 0;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,184 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
namespace flight\template;
|
||||
|
||||
/**
|
||||
* The View class represents output to be displayed. It provides
|
||||
* methods for managing view data and inserts the data into
|
||||
* view templates upon rendering.
|
||||
*/
|
||||
class View {
|
||||
/**
|
||||
* Location of view templates.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $path;
|
||||
|
||||
/**
|
||||
* File extension.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $extension = '.php';
|
||||
|
||||
/**
|
||||
* View variables.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $vars = array();
|
||||
|
||||
/**
|
||||
* Template file.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $template;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string $path Path to templates directory
|
||||
*/
|
||||
public function __construct($path = '.') {
|
||||
$this->path = $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a template variable.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function get($key) {
|
||||
return isset($this->vars[$key]) ? $this->vars[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a template variable.
|
||||
*
|
||||
* @param mixed $key Key
|
||||
* @param string $value Value
|
||||
*/
|
||||
public function set($key, $value = null) {
|
||||
if (is_array($key) || is_object($key)) {
|
||||
foreach ($key as $k => $v) {
|
||||
$this->vars[$k] = $v;
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a template variable is set.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return boolean If key exists
|
||||
*/
|
||||
public function has($key) {
|
||||
return isset($this->vars[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets a template variable. If no key is passed in, clear all variables.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function clear($key = null) {
|
||||
if (is_null($key)) {
|
||||
$this->vars = array();
|
||||
}
|
||||
else {
|
||||
unset($this->vars[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @throws \Exception If template not found
|
||||
*/
|
||||
public function render($file, $data = null) {
|
||||
$this->template = $this->getTemplate($file);
|
||||
|
||||
if (!file_exists($this->template)) {
|
||||
throw new \Exception("Template file not found: {$this->template}.");
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
$this->vars = array_merge($this->vars, $data);
|
||||
}
|
||||
|
||||
extract($this->vars);
|
||||
|
||||
include $this->template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the output of a template.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @param array $data Template data
|
||||
* @return string Output of template
|
||||
*/
|
||||
public function fetch($file, $data = null) {
|
||||
ob_start();
|
||||
|
||||
$this->render($file, $data);
|
||||
$output = ob_get_clean();
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a template file exists.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @return bool Template file exists
|
||||
*/
|
||||
public function exists($file) {
|
||||
return file_exists($this->getTemplate($file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the full path to a template file.
|
||||
*
|
||||
* @param string $file Template file
|
||||
* @return string Template file location
|
||||
*/
|
||||
public function getTemplate($file) {
|
||||
$ext = $this->extension;
|
||||
|
||||
if (!empty($ext) && (substr($file, -1 * strlen($ext)) != $ext)) {
|
||||
$file .= $ext;
|
||||
}
|
||||
|
||||
if ((substr($file, 0, 1) == '/')) {
|
||||
return $file;
|
||||
}
|
||||
|
||||
return $this->path.'/'.$file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays escaped output.
|
||||
*
|
||||
* @param string $str String to escape
|
||||
* @return string Escaped string
|
||||
*/
|
||||
public function e($str) {
|
||||
echo htmlentities($str);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
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 isset($this->data[$key]) ? $this->data[$key] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an item.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @param mixed $value Value
|
||||
*/
|
||||
public function __set($key, $value) {
|
||||
$this->data[$key] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item exists.
|
||||
*
|
||||
* @param string $key Key
|
||||
* @return bool Item status
|
||||
*/
|
||||
public function __isset($key) {
|
||||
return isset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item.
|
||||
*
|
||||
* @param string $key Key
|
||||
*/
|
||||
public function __unset($key) {
|
||||
unset($this->data[$key]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function offsetGet($offset) {
|
||||
return isset($this->data[$offset]) ? $this->data[$offset] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @param mixed $value Value
|
||||
*/
|
||||
public function offsetSet($offset, $value) {
|
||||
if (is_null($offset)) {
|
||||
$this->data[] = $value;
|
||||
}
|
||||
else {
|
||||
$this->data[$offset] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if an item exists at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
* @return bool Item status
|
||||
*/
|
||||
public function offsetExists($offset) {
|
||||
return isset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an item at the offset.
|
||||
*
|
||||
* @param string $offset Offset
|
||||
*/
|
||||
public function offsetUnset($offset) {
|
||||
unset($this->data[$offset]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the collection.
|
||||
*/
|
||||
public function rewind() {
|
||||
reset($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current collection item.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function current() {
|
||||
return current($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets current collection key.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function key() {
|
||||
return key($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the next collection value.
|
||||
*
|
||||
* @return mixed Value
|
||||
*/
|
||||
public function next()
|
||||
{
|
||||
return next($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the current collection key is valid.
|
||||
*
|
||||
* @return bool Key status
|
||||
*/
|
||||
public function valid()
|
||||
{
|
||||
$key = key($this->data);
|
||||
return ($key !== NULL && $key !== FALSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the size of the collection.
|
||||
*
|
||||
* @return int Collection size
|
||||
*/
|
||||
public function count() {
|
||||
return sizeof($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the item keys.
|
||||
*
|
||||
* @return array Collection keys
|
||||
*/
|
||||
public function keys() {
|
||||
return array_keys($this->data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the collection data.
|
||||
*
|
||||
* @return array Collection data
|
||||
*/
|
||||
public function getData() {
|
||||
return $this->data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the collection data.
|
||||
*
|
||||
* @param array $data New collection data
|
||||
*/
|
||||
public function setData(array $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all items from the collection.
|
||||
*/
|
||||
public function clear() {
|
||||
$this->data = array();
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
require 'flight/Flight.php';
|
||||
|
||||
Flight::route('/', function(){
|
||||
echo 'hello world!';
|
||||
});
|
||||
|
||||
Flight::start();
|
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class AutoloadTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
$this->app->path(__DIR__.'/classes');
|
||||
}
|
||||
|
||||
// Autoload a class
|
||||
function testAutoload(){
|
||||
$this->app->register('user', 'User');
|
||||
|
||||
$loaders = spl_autoload_functions();
|
||||
|
||||
$user = $this->app->user();
|
||||
|
||||
$this->assertTrue(sizeof($loaders) > 0);
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
}
|
||||
|
||||
// Check autoload failure
|
||||
function testMissingClass(){
|
||||
$test = null;
|
||||
$this->app->register('test', 'NonExistentClass');
|
||||
|
||||
if (class_exists('NonExistentClass')) {
|
||||
$test = $this->app->test();
|
||||
}
|
||||
|
||||
$this->assertEquals(null, $test);
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/classes/Hello.php';
|
||||
|
||||
class DispatcherTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\core\Dispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
function setUp(){
|
||||
$this->dispatcher = new \flight\core\Dispatcher();
|
||||
}
|
||||
|
||||
// Map a closure
|
||||
function testClosureMapping(){
|
||||
$this->dispatcher->set('map1', function(){
|
||||
return 'hello';
|
||||
});
|
||||
|
||||
$result = $this->dispatcher->run('map1');
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a function
|
||||
function testFunctionMapping(){
|
||||
$this->dispatcher->set('map2', function(){
|
||||
return 'hello';
|
||||
});
|
||||
|
||||
$result = $this->dispatcher->run('map2');
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a class method
|
||||
function testClassMethodMapping(){
|
||||
$h = new Hello();
|
||||
|
||||
$this->dispatcher->set('map3', array($h, 'sayHi'));
|
||||
|
||||
$result = $this->dispatcher->run('map3');
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a static class method
|
||||
function testStaticClassMethodMapping(){
|
||||
$this->dispatcher->set('map4', array('Hello', 'sayBye'));
|
||||
|
||||
$result = $this->dispatcher->run('map4');
|
||||
|
||||
$this->assertEquals('goodbye', $result);
|
||||
}
|
||||
|
||||
// Run before and after filters
|
||||
function testBeforeAndAfter() {
|
||||
$this->dispatcher->set('hello', function($name){
|
||||
return "Hello, $name!";
|
||||
});
|
||||
|
||||
$this->dispatcher->hook('hello', 'before', function(&$params, &$output){
|
||||
// Manipulate the parameter
|
||||
$params[0] = 'Fred';
|
||||
});
|
||||
|
||||
$this->dispatcher->hook('hello', 'after', function(&$params, &$output){
|
||||
// Manipulate the output
|
||||
$output .= " Have a nice day!";
|
||||
});
|
||||
|
||||
$result = $this->dispatcher->run('hello', array('Bob'));
|
||||
|
||||
$this->assertEquals('Hello, Fred! Have a nice day!', $result);
|
||||
}
|
||||
|
||||
// Test an invalid callback
|
||||
function testInvalidCallback() {
|
||||
$this->setExpectedException('Exception', 'Invalid callback specified.');
|
||||
|
||||
$this->dispatcher->execute(array('NonExistentClass', 'nonExistentMethod'));
|
||||
}
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class FilterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
}
|
||||
|
||||
// Run before and after filters
|
||||
function testBeforeAndAfter() {
|
||||
$this->app->map('hello', function($name){
|
||||
return "Hello, $name!";
|
||||
});
|
||||
|
||||
$this->app->before('hello', function(&$params, &$output){
|
||||
// Manipulate the parameter
|
||||
$params[0] = 'Fred';
|
||||
});
|
||||
|
||||
$this->app->after('hello', function(&$params, &$output){
|
||||
// Manipulate the output
|
||||
$output .= " Have a nice day!";
|
||||
});
|
||||
|
||||
$result = $this->app->hello('Bob');
|
||||
|
||||
$this->assertEquals('Hello, Fred! Have a nice day!', $result);
|
||||
}
|
||||
|
||||
// Break out of a filter chain by returning false
|
||||
function testFilterChaining() {
|
||||
$this->app->map('bye', function($name){
|
||||
return "Bye, $name!";
|
||||
});
|
||||
|
||||
$this->app->before('bye', function(&$params, &$output){
|
||||
$params[0] = 'Bob';
|
||||
});
|
||||
$this->app->before('bye', function(&$params, &$output){
|
||||
$params[0] = 'Fred';
|
||||
return false;
|
||||
});
|
||||
$this->app->before('bye', function(&$params, &$output){
|
||||
$params[0] = 'Ted';
|
||||
});
|
||||
|
||||
$result = $this->app->bye('Joe');
|
||||
|
||||
$this->assertEquals('Bye, Fred!', $result);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/Flight.php';
|
||||
|
||||
class FlightTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
function setUp() {
|
||||
Flight::init();
|
||||
}
|
||||
|
||||
// Checks that default components are loaded
|
||||
function testDefaultComponents(){
|
||||
$request = Flight::request();
|
||||
$response = Flight::response();
|
||||
$router = Flight::router();
|
||||
$view = Flight::view();
|
||||
|
||||
$this->assertEquals('flight\net\Request', get_class($request));
|
||||
$this->assertEquals('flight\net\Response', get_class($response));
|
||||
$this->assertEquals('flight\net\Router', get_class($router));
|
||||
$this->assertEquals('flight\template\View', get_class($view));
|
||||
}
|
||||
|
||||
// Test get/set of variables
|
||||
function testGetAndSet(){
|
||||
Flight::set('a', 1);
|
||||
$var = Flight::get('a');
|
||||
|
||||
$this->assertEquals(1, $var);
|
||||
|
||||
Flight::clear();
|
||||
$vars = Flight::get();
|
||||
|
||||
$this->assertEquals(0, count($vars));
|
||||
|
||||
Flight::set('a', 1);
|
||||
Flight::set('b', 2);
|
||||
$vars = Flight::get();
|
||||
|
||||
$this->assertEquals(2, count($vars));
|
||||
$this->assertEquals(1, $vars['a']);
|
||||
$this->assertEquals(2, $vars['b']);
|
||||
}
|
||||
|
||||
// Register a class
|
||||
function testRegister(){
|
||||
Flight::path(__DIR__.'/classes');
|
||||
|
||||
Flight::register('user', 'User');
|
||||
$user = Flight::user();
|
||||
|
||||
$loaders = spl_autoload_functions();
|
||||
|
||||
$this->assertTrue(sizeof($loaders) > 0);
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
}
|
||||
|
||||
// Map a function
|
||||
function testMap(){
|
||||
Flight::map('map1', function(){
|
||||
return 'hello';
|
||||
});
|
||||
|
||||
$result = Flight::map1();
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Unmapped method
|
||||
function testUnmapped() {
|
||||
$this->setExpectedException('Exception', 'doesNotExist must be a mapped method.');
|
||||
|
||||
Flight::doesNotExist();
|
||||
}
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/classes/User.php';
|
||||
require_once __DIR__.'/classes/Factory.php';
|
||||
|
||||
class LoaderTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\core\Loader
|
||||
*/
|
||||
private $loader;
|
||||
|
||||
function setUp(){
|
||||
$this->loader = new \flight\core\Loader();
|
||||
$this->loader->autoload(true, __DIR__.'/classes');
|
||||
}
|
||||
|
||||
// Autoload a class
|
||||
function testAutoload(){
|
||||
$this->loader->register('tests', 'User');
|
||||
|
||||
$test = $this->loader->load('tests');
|
||||
|
||||
$this->assertTrue(is_object($test));
|
||||
$this->assertEquals('User', get_class($test));
|
||||
}
|
||||
|
||||
// Register a class
|
||||
function testRegister(){
|
||||
$this->loader->register('a', 'User');
|
||||
|
||||
$user = $this->loader->load('a');
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('', $user->name);
|
||||
}
|
||||
|
||||
// Register a class with constructor parameters
|
||||
function testRegisterWithConstructor(){
|
||||
$this->loader->register('b', 'User', array('Bob'));
|
||||
|
||||
$user = $this->loader->load('b');
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('Bob', $user->name);
|
||||
}
|
||||
|
||||
// Register a class with initialization
|
||||
function testRegisterWithInitialization(){
|
||||
$this->loader->register('c', 'User', array('Bob'), function($user){
|
||||
$user->name = 'Fred';
|
||||
});
|
||||
|
||||
$user = $this->loader->load('c');
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('Fred', $user->name);
|
||||
}
|
||||
|
||||
// Get a non-shared instance of a class
|
||||
function testSharedInstance() {
|
||||
$this->loader->register('d', 'User');
|
||||
|
||||
$user1 = $this->loader->load('d');
|
||||
$user2 = $this->loader->load('d');
|
||||
$user3 = $this->loader->load('d', false);
|
||||
|
||||
$this->assertTrue($user1 === $user2);
|
||||
$this->assertTrue($user1 !== $user3);
|
||||
}
|
||||
|
||||
// Gets an object from a factory method
|
||||
function testRegisterUsingCallable(){
|
||||
$this->loader->register('e', array('Factory','create'));
|
||||
|
||||
$obj = $this->loader->load('e');
|
||||
|
||||
$this->assertTrue(is_object($obj));
|
||||
$this->assertEquals('Factory', get_class($obj));
|
||||
|
||||
$obj2 = $this->loader->load('e');
|
||||
|
||||
$this->assertTrue(is_object($obj2));
|
||||
$this->assertEquals('Factory', get_class($obj2));
|
||||
$this->assertTrue($obj === $obj2);
|
||||
|
||||
$obj3 = $this->loader->load('e', false);
|
||||
$this->assertTrue(is_object($obj3));
|
||||
$this->assertEquals('Factory', get_class($obj3));
|
||||
$this->assertTrue($obj !== $obj3);
|
||||
}
|
||||
|
||||
// Gets an object from a callback function
|
||||
function testRegisterUsingCallback(){
|
||||
$this->loader->register('f', function(){
|
||||
return Factory::create();
|
||||
});
|
||||
|
||||
$obj = $this->loader->load('f');
|
||||
|
||||
$this->assertTrue(is_object($obj));
|
||||
$this->assertEquals('Factory', get_class($obj));
|
||||
}
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
require_once __DIR__.'/classes/Hello.php';
|
||||
|
||||
class MapTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
}
|
||||
|
||||
// Map a closure
|
||||
function testClosureMapping(){
|
||||
$this->app->map('map1', function(){
|
||||
return 'hello';
|
||||
});
|
||||
|
||||
$result = $this->app->map1();
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a function
|
||||
function testFunctionMapping(){
|
||||
$this->app->map('map2', function(){
|
||||
return 'hello';
|
||||
});
|
||||
|
||||
$result = $this->app->map2();
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a class method
|
||||
function testClassMethodMapping(){
|
||||
$h = new Hello();
|
||||
|
||||
$this->app->map('map3', array($h, 'sayHi'));
|
||||
|
||||
$result = $this->app->map3();
|
||||
|
||||
$this->assertEquals('hello', $result);
|
||||
}
|
||||
|
||||
// Map a static class method
|
||||
function testStaticClassMethodMapping(){
|
||||
$this->app->map('map4', array('Hello', 'sayBye'));
|
||||
|
||||
$result = $this->app->map4();
|
||||
|
||||
$this->assertEquals('goodbye', $result);
|
||||
}
|
||||
|
||||
// Unmapped method
|
||||
function testUnmapped() {
|
||||
$this->setExpectedException('Exception', 'doesNotExist must be a mapped method.');
|
||||
|
||||
$this->app->doesNotExist();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
## Flight Tests
|
||||
|
||||
This directory contains unit tests for Flight. The tests were written for PHPUnit 3.7.10
|
||||
|
||||
To run the tests do:
|
||||
|
||||
composer install
|
||||
vendor/bin/phpunit tests
|
||||
|
||||
Learn more about PHPUnit at [http://www.phpunit.de](http://www.phpunit.de/manual/current/en/index.html)
|
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2013, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class RedirectTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function getBaseUrl($base, $url){
|
||||
if ($base != '/' && strpos($url, '://') === false) {
|
||||
$url = preg_replace('#/+#', '/', $base.'/'.$url);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
function setUp() {
|
||||
$_SERVER['SCRIPT_NAME'] = '/subdir/index.php';
|
||||
|
||||
$this->app = new \flight\Engine();
|
||||
$this->app->set('flight.base_url', '/testdir');
|
||||
}
|
||||
|
||||
// The base should be the subdirectory
|
||||
function testBase(){
|
||||
$base = $this->app->request()->base;
|
||||
|
||||
$this->assertEquals('/subdir', $base);
|
||||
}
|
||||
|
||||
// Absolute URLs should include the base
|
||||
function testAbsoluteUrl(){
|
||||
$url = '/login';
|
||||
$base = $this->app->request()->base;
|
||||
|
||||
$this->assertEquals('/subdir/login', $this->getBaseUrl($base, $url));
|
||||
}
|
||||
|
||||
// Relative URLs should include the base
|
||||
function testRelativeUrl(){
|
||||
$url = 'login';
|
||||
$base = $this->app->request()->base;
|
||||
|
||||
$this->assertEquals('/subdir/login', $this->getBaseUrl($base, $url));
|
||||
}
|
||||
|
||||
// External URLs should ignore the base
|
||||
function testHttpUrl(){
|
||||
$url = 'http://www.yahoo.com';
|
||||
$base = $this->app->request()->base;
|
||||
|
||||
$this->assertEquals('http://www.yahoo.com', $this->getBaseUrl($base, $url));
|
||||
}
|
||||
|
||||
// Configuration should override derived value
|
||||
function testBaseOverride(){
|
||||
$url = 'login';
|
||||
if ($this->app->get('flight.base_url') !== null) {
|
||||
$base = $this->app->get('flight.base_url');
|
||||
}
|
||||
else {
|
||||
$base = $this->app->request()->base;
|
||||
}
|
||||
|
||||
$this->assertEquals('/testdir/login', $this->getBaseUrl($base, $url));
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
require_once __DIR__.'/classes/User.php';
|
||||
|
||||
class RegisterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
}
|
||||
|
||||
// Register a class
|
||||
function testRegister(){
|
||||
$this->app->register('reg1', 'User');
|
||||
|
||||
$user = $this->app->reg1();
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('', $user->name);
|
||||
}
|
||||
|
||||
// Register a class with constructor parameters
|
||||
function testRegisterWithConstructor(){
|
||||
$this->app->register('reg2', 'User', array('Bob'));
|
||||
|
||||
$user = $this->app->reg2();
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('Bob', $user->name);
|
||||
}
|
||||
|
||||
// Register a class with initialization
|
||||
function testRegisterWithInitialization(){
|
||||
$this->app->register('reg3', 'User', array('Bob'), function($user){
|
||||
$user->name = 'Fred';
|
||||
});
|
||||
|
||||
$user = $this->app->reg3();
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
$this->assertEquals('User', get_class($user));
|
||||
$this->assertEquals('Fred', $user->name);
|
||||
}
|
||||
|
||||
// Get a non-shared instance of a class
|
||||
function testSharedInstance() {
|
||||
$this->app->register('reg4', 'User');
|
||||
|
||||
$user1 = $this->app->reg4();
|
||||
$user2 = $this->app->reg4();
|
||||
$user3 = $this->app->reg4(false);
|
||||
|
||||
$this->assertTrue($user1 === $user2);
|
||||
$this->assertTrue($user1 !== $user3);
|
||||
}
|
||||
|
||||
// Map method takes precedence over register
|
||||
function testMapOverridesRegister(){
|
||||
$this->app->register('reg5', 'User');
|
||||
|
||||
$user = $this->app->reg5();
|
||||
|
||||
$this->assertTrue(is_object($user));
|
||||
|
||||
$this->app->map('reg5', function(){
|
||||
return 123;
|
||||
});
|
||||
|
||||
$user = $this->app->reg5();
|
||||
|
||||
$this->assertEquals(123, $user);
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/Flight.php';
|
||||
|
||||
class RenderTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
$this->app->set('flight.views.path', __DIR__.'/views');
|
||||
}
|
||||
|
||||
// Render a view
|
||||
function testRenderView(){
|
||||
$this->app->render('hello', array('name' => 'Bob'));
|
||||
|
||||
$this->expectOutputString('Hello, Bob!');
|
||||
}
|
||||
|
||||
// Renders a view into a layout
|
||||
function testRenderLayout(){
|
||||
$this->app->render('hello', array('name' => 'Bob'), 'content');
|
||||
$this->app->render('layouts/layout');
|
||||
|
||||
$this->expectOutputString('<html>Hello, Bob!</html>');
|
||||
}
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class RequestTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\net\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
function setUp() {
|
||||
$_SERVER['REQUEST_URI'] = '/';
|
||||
$_SERVER['SCRIPT_NAME'] = '/index.php';
|
||||
$_SERVER['REQUEST_METHOD'] = 'GET';
|
||||
$_SERVER['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest';
|
||||
$_SERVER['REMOTE_ADDR'] = '8.8.8.8';
|
||||
$_SERVER['HTTPS'] = 'on';
|
||||
$_SERVER['HTTP_X_FORWARDED_FOR'] = '32.32.32.32';
|
||||
|
||||
$this->request = new \flight\net\Request();
|
||||
}
|
||||
|
||||
function testDefaults() {
|
||||
$this->assertEquals('/', $this->request->url);
|
||||
$this->assertEquals('/', $this->request->base);
|
||||
$this->assertEquals('GET', $this->request->method);
|
||||
$this->assertEquals('', $this->request->referrer);
|
||||
$this->assertEquals(true, $this->request->ajax);
|
||||
$this->assertEquals('HTTP/1.1', $this->request->scheme);
|
||||
$this->assertEquals('', $this->request->type);
|
||||
$this->assertEquals(0, $this->request->length);
|
||||
$this->assertEquals(true, $this->request->secure);
|
||||
$this->assertEquals('', $this->request->accept);
|
||||
}
|
||||
|
||||
function testIpAddress() {
|
||||
$this->assertEquals('8.8.8.8', $this->request->ip);
|
||||
$this->assertEquals('32.32.32.32', $this->request->proxy_ip);
|
||||
}
|
||||
|
||||
function testSubdirectory() {
|
||||
$_SERVER['SCRIPT_NAME'] = '/subdir/index.php';
|
||||
|
||||
$request = new \flight\net\Request();
|
||||
|
||||
$this->assertEquals('/subdir', $request->base);
|
||||
}
|
||||
|
||||
function testQueryParameters() {
|
||||
$_SERVER['REQUEST_URI'] = '/page?id=1&name=bob';
|
||||
|
||||
$request = new \flight\net\Request();
|
||||
|
||||
$this->assertEquals('/page?id=1&name=bob', $request->url);
|
||||
$this->assertEquals(1, $request->query->id);
|
||||
$this->assertEquals('bob', $request->query->name);
|
||||
}
|
||||
|
||||
function testCollections() {
|
||||
$_SERVER['REQUEST_URI'] = '/page?id=1';
|
||||
|
||||
$_GET['q'] = 1;
|
||||
$_POST['q'] = 1;
|
||||
$_COOKIE['q'] = 1;
|
||||
$_FILES['q'] = 1;
|
||||
|
||||
$request = new \flight\net\Request();
|
||||
|
||||
$this->assertEquals(1, $request->query->q);
|
||||
$this->assertEquals(1, $request->query->id);
|
||||
$this->assertEquals(1, $request->data->q);
|
||||
$this->assertEquals(1, $request->cookies->q);
|
||||
$this->assertEquals(1, $request->files->q);
|
||||
}
|
||||
|
||||
function testMethodOverrideWithHeader() {
|
||||
$_SERVER['HTTP_X_HTTP_METHOD_OVERRIDE'] = 'PUT';
|
||||
|
||||
$request = new \flight\net\Request();
|
||||
|
||||
$this->assertEquals('PUT', $request->method);
|
||||
}
|
||||
|
||||
function testMethodOverrideWithPost() {
|
||||
$_REQUEST['_method'] = 'PUT';
|
||||
|
||||
$request = new \flight\net\Request();
|
||||
|
||||
$this->assertEquals('PUT', $request->method);
|
||||
}
|
||||
}
|
@ -0,0 +1,266 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2011, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class RouterTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\net\Router
|
||||
*/
|
||||
private $router;
|
||||
|
||||
/**
|
||||
* @var \flight\net\Request
|
||||
*/
|
||||
private $request;
|
||||
|
||||
/**
|
||||
* @var \flight\core\Dispatcher
|
||||
*/
|
||||
private $dispatcher;
|
||||
|
||||
function setUp(){
|
||||
$this->router = new \flight\net\Router();
|
||||
$this->request = new \flight\net\Request();
|
||||
$this->dispatcher = new \flight\core\Dispatcher();
|
||||
}
|
||||
|
||||
// Simple output
|
||||
function ok(){
|
||||
echo 'OK';
|
||||
}
|
||||
|
||||
// Checks if a route was matched with a given output
|
||||
function check($str = '') {
|
||||
/*
|
||||
$route = $this->router->route($this->request);
|
||||
|
||||
$params = array_values($route->params);
|
||||
|
||||
$this->assertTrue(is_callable($route->callback));
|
||||
|
||||
call_user_func_array($route->callback, $params);
|
||||
*/
|
||||
|
||||
$this->routeRequest();
|
||||
$this->expectOutputString($str);
|
||||
}
|
||||
|
||||
function routeRequest() {
|
||||
$dispatched = false;
|
||||
|
||||
while ($route = $this->router->route($this->request)) {
|
||||
$params = array_values($route->params);
|
||||
|
||||
if ($route->pass) {
|
||||
$params[] = $route;
|
||||
}
|
||||
|
||||
$continue = $this->dispatcher->execute(
|
||||
$route->callback,
|
||||
$params
|
||||
);
|
||||
|
||||
$dispatched = true;
|
||||
|
||||
if (!$continue) break;
|
||||
|
||||
$this->router->next();
|
||||
|
||||
$dispatched = false;
|
||||
}
|
||||
|
||||
if (!$dispatched) {
|
||||
echo '404';
|
||||
}
|
||||
}
|
||||
|
||||
// Default route
|
||||
function testDefaultRoute(){
|
||||
$this->router->map('/', array($this, 'ok'));
|
||||
$this->request->url = '/';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// Simple path
|
||||
function testPathRoute(){
|
||||
$this->router->map('/path', array($this, 'ok'));
|
||||
$this->request->url = '/path';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// POST route
|
||||
function testPostRoute(){
|
||||
$this->router->map('POST /', array($this, 'ok'));
|
||||
$this->request->url = '/';
|
||||
$this->request->method = 'POST';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// Either GET or POST route
|
||||
function testGetPostRoute(){
|
||||
$this->router->map('GET|POST /', array($this, 'ok'));
|
||||
$this->request->url = '/';
|
||||
$this->request->method = 'GET';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// Test regular expression matching
|
||||
function testRegEx(){
|
||||
$this->router->map('/num/[0-9]+', array($this, 'ok'));
|
||||
$this->request->url = '/num/1234';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// Passing URL parameters
|
||||
function testUrlParameters(){
|
||||
$this->router->map('/user/@id', function($id){
|
||||
echo $id;
|
||||
});
|
||||
$this->request->url = '/user/123';
|
||||
|
||||
$this->check('123');
|
||||
}
|
||||
|
||||
// Passing URL parameters matched with regular expression
|
||||
function testRegExParameters(){
|
||||
$this->router->map('/test/@name:[a-z]+', function($name){
|
||||
echo $name;
|
||||
});
|
||||
$this->request->url = '/test/abc';
|
||||
|
||||
$this->check('abc');
|
||||
}
|
||||
|
||||
// Optional parameters
|
||||
function testOptionalParameters(){
|
||||
$this->router->map('/blog(/@year(/@month(/@day)))', function($year, $month, $day){
|
||||
echo "$year,$month,$day";
|
||||
});
|
||||
$this->request->url = '/blog/2000';
|
||||
|
||||
$this->check('2000,,');
|
||||
}
|
||||
|
||||
// Regex in optional parameters
|
||||
function testRegexOptionalParameters(){
|
||||
$this->router->map('/@controller/@method(/@id:[0-9]+)', function($controller, $method, $id){
|
||||
echo "$controller,$method,$id";
|
||||
});
|
||||
$this->request->url = '/user/delete/123';
|
||||
|
||||
$this->check('user,delete,123');
|
||||
}
|
||||
|
||||
// Regex in optional parameters
|
||||
function testRegexEmptyOptionalParameters(){
|
||||
$this->router->map('/@controller/@method(/@id:[0-9]+)', function($controller, $method, $id){
|
||||
echo "$controller,$method,$id";
|
||||
});
|
||||
$this->request->url = '/user/delete/';
|
||||
|
||||
$this->check('user,delete,');
|
||||
}
|
||||
|
||||
// Wildcard matching
|
||||
function testWildcard(){
|
||||
$this->router->map('/account/*', array($this, 'ok'));
|
||||
$this->request->url = '/account/123/abc/xyz';
|
||||
|
||||
$this->check('OK');
|
||||
}
|
||||
|
||||
// Check if route object was passed
|
||||
function testRouteObjectPassing(){
|
||||
$this->router->map('/yes_route', function($route){
|
||||
$this->assertTrue(is_object($route));
|
||||
$this->assertTrue(is_array($route->methods));
|
||||
$this->assertTrue(is_array($route->params));
|
||||
$this->assertEquals(sizeof($route->params), 0);
|
||||
$this->assertEquals($route->regex, null);
|
||||
$this->assertEquals($route->splat, '');
|
||||
$this->assertTrue($route->pass);
|
||||
}, true);
|
||||
$this->request->url = '/yes_route';
|
||||
|
||||
$this->check();
|
||||
|
||||
$this->router->map('/no_route', function($route = null){
|
||||
$this->assertTrue(is_null($route));
|
||||
}, false);
|
||||
$this->request->url = '/no_route';
|
||||
|
||||
$this->check();
|
||||
}
|
||||
|
||||
function testRouteWithParameters() {
|
||||
$this->router->map('/@one/@two', function($one, $two, $route){
|
||||
$this->assertEquals(sizeof($route->params), 2);
|
||||
$this->assertEquals($route->params['one'], $one);
|
||||
$this->assertEquals($route->params['two'], $two);
|
||||
}, true);
|
||||
$this->request->url = '/1/2';
|
||||
|
||||
$this->check();
|
||||
}
|
||||
|
||||
// Test splat
|
||||
function testSplatWildcard(){
|
||||
$this->router->map('/account/*', function($route){
|
||||
echo $route->splat;
|
||||
}, true);
|
||||
$this->request->url = '/account/456/def/xyz';
|
||||
|
||||
$this->check('456/def/xyz');
|
||||
}
|
||||
|
||||
// Test splat without trailing slash
|
||||
function testSplatWildcardTrailingSlash(){
|
||||
$this->router->map('/account/*', function($route){
|
||||
echo $route->splat;
|
||||
}, true);
|
||||
$this->request->url = '/account';
|
||||
|
||||
$this->check();
|
||||
}
|
||||
|
||||
// Test splat with named parameters
|
||||
function testSplatNamedPlusWildcard(){
|
||||
$this->router->map('/account/@name/*', function($name, $route){
|
||||
echo $route->splat;
|
||||
$this->assertEquals('abc', $name);
|
||||
}, true);
|
||||
$this->request->url = '/account/abc/456/def/xyz';
|
||||
|
||||
$this->check('456/def/xyz');
|
||||
}
|
||||
|
||||
// Test not found
|
||||
function testNotFound() {
|
||||
$this->router->map('/does_exist', array($this, 'ok'));
|
||||
$this->request->url = '/does_not_exist';
|
||||
|
||||
$this->check('404');
|
||||
}
|
||||
|
||||
// Test case sensitivity
|
||||
function testCaseSensitivity() {
|
||||
$this->router->map('/hello', array($this, 'ok'));
|
||||
$this->request->url = '/HELLO';
|
||||
$this->router->case_sensitive = true;
|
||||
|
||||
$this->check('404');
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class VariableTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\Engine
|
||||
*/
|
||||
private $app;
|
||||
|
||||
function setUp() {
|
||||
$this->app = new \flight\Engine();
|
||||
}
|
||||
// Set and get a variable
|
||||
function testSetAndGet() {
|
||||
$this->app->set('a', 1);
|
||||
$var = $this->app->get('a');
|
||||
$this->assertEquals(1, $var);
|
||||
}
|
||||
|
||||
// Clear a specific variable
|
||||
function testClear() {
|
||||
$this->app->set('b', 1);
|
||||
$this->app->clear('b');
|
||||
$var = $this->app->get('b');
|
||||
$this->assertEquals(null, $var);
|
||||
}
|
||||
|
||||
// Clear all variables
|
||||
function testClearAll() {
|
||||
$this->app->set('c', 1);
|
||||
$this->app->clear();
|
||||
$var = $this->app->get('c');
|
||||
$this->assertEquals(null, $var);
|
||||
}
|
||||
|
||||
// Check if a variable exists
|
||||
function testHas() {
|
||||
$this->app->set('d', 1);
|
||||
$this->assertTrue($this->app->has('d'));
|
||||
}
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
<?php
|
||||
/**
|
||||
* Flight: An extensible micro-framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2012, Mike Cao <mike@mikecao.com>
|
||||
* @license MIT, http://flightphp.com/license
|
||||
*/
|
||||
|
||||
require_once 'vendor/autoload.php';
|
||||
require_once __DIR__.'/../flight/autoload.php';
|
||||
|
||||
class ViewTest extends PHPUnit_Framework_TestCase
|
||||
{
|
||||
/**
|
||||
* @var \flight\template\View
|
||||
*/
|
||||
private $view;
|
||||
|
||||
function setUp() {
|
||||
$this->view = new \flight\template\View();
|
||||
$this->view->path = __DIR__.'/views';
|
||||
}
|
||||
|
||||
// Set template variables
|
||||
function testVariables() {
|
||||
$this->view->set('test', 123);
|
||||
|
||||
$this->assertEquals(123, $this->view->get('test'));
|
||||
|
||||
$this->assertTrue($this->view->has('test'));
|
||||
$this->assertTrue(!$this->view->has('unknown'));
|
||||
|
||||
$this->view->clear('test');
|
||||
|
||||
$this->assertEquals(null, $this->view->get('test'));
|
||||
}
|
||||
|
||||
// Check if template files exist
|
||||
function testTemplateExists() {
|
||||
$this->assertTrue($this->view->exists('hello.php'));
|
||||
$this->assertTrue(!$this->view->exists('unknown.php'));
|
||||
}
|
||||
|
||||
// Render a template
|
||||
function testRender() {
|
||||
$this->view->render('hello', array('name' => 'Bob'));
|
||||
|
||||
$this->expectOutputString('Hello, Bob!');
|
||||
}
|
||||
|
||||
// Fetch template output
|
||||
function testFetch() {
|
||||
$output = $this->view->fetch('hello', array('name' => 'Bob'));
|
||||
|
||||
$this->assertEquals('Hello, Bob!', $output);
|
||||
}
|
||||
|
||||
// Default extension
|
||||
function testTemplateWithExtension() {
|
||||
$this->view->set('name', 'Bob');
|
||||
|
||||
$this->view->render('hello.php');
|
||||
|
||||
$this->expectOutputString('Hello, Bob!');
|
||||
}
|
||||
|
||||
// Custom extension
|
||||
function testTemplateWithCustomExtension() {
|
||||
$this->view->set('name', 'Bob');
|
||||
$this->view->extension = '.html';
|
||||
|
||||
$this->view->render('world');
|
||||
|
||||
$this->expectOutputString('Hello world, Bob!');
|
||||
}
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<?php
|
||||
class Factory {
|
||||
// Cannot be instantiated
|
||||
private function __construct() {
|
||||
|
||||
}
|
||||
|
||||
public static function create() {
|
||||
return new self();
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
class Hello {
|
||||
public function sayHi() {
|
||||
return 'hello';
|
||||
}
|
||||
|
||||
public static function sayBye() {
|
||||
return 'goodbye';
|
||||
}
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
<?php
|
||||
class User {
|
||||
public $name;
|
||||
|
||||
public function __construct($name = ''){
|
||||
$this->name = $name;
|
||||
}
|
||||
}
|
@ -0,0 +1 @@
|
||||
Hello, <?php echo $name; ?>!
|
@ -0,0 +1 @@
|
||||
<html><?php echo $content; ?></html>
|
@ -0,0 +1 @@
|
||||
Hello world, <?php echo $name; ?>!
|
Loading…
Reference in new issue