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