masroore/flight

Flight 是一个快速、简单、可扩展的 PHP 框架。Flight 可以帮助您快速轻松地构建 RESTful 网络应用程序。

v1.3.9 2021-04-05 01:36 UTC

README

Flight 是一个快速、简单、可扩展的 PHP 框架。Flight 可以帮助您快速轻松地构建 RESTful 网络应用程序。

require 'flight/Flight.php';

Flight::route('/', function(){
    echo 'hello world!';
});

Flight::start();

了解更多

要求

Flight 需要 PHP 7.4 或更高版本。

许可证

Flight 在 MIT 许可证下发布。

安装

1. 下载文件。

如果您使用 Composer,可以运行以下命令

composer require mikecao/flight

或者您可以直接下载并将其解压缩到您的网页目录中。

2. 配置您的网络服务器。

对于 Apache,使用以下内容编辑您的 .htaccess 文件

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

注意:如果您需要在子目录中使用 flight,请在 RewriteEngine On 之后添加行 RewriteBase /subdir/

对于 Nginx,将以下内容添加到您的服务器声明中

server {
    location / {
        try_files $uri $uri/ /index.php;
    }
}

3. 创建您的 index.php 文件。

首先引入框架。

require 'flight/Flight.php';

如果您使用 Composer,请运行自动加载器。

require 'vendor/autoload.php';

然后定义一个路由并分配一个函数来处理请求。

Flight::route('/', function(){
    echo 'hello world!';
});

最后,启动框架。

Flight::start();

路由

Flight 中的路由是通过匹配 URL 模式与回调函数来完成的。

Flight::route('/', function(){
    echo 'hello world!';
});

回调可以是任何可调用的对象。因此,您可以使用普通函数

function hello(){
    echo 'hello world!';
}

Flight::route('/', 'hello');

或类方法

class Greeting {
    public static function hello() {
        echo 'hello world!';
    }
}

Flight::route('/', array('Greeting', 'hello'));

或对象方法

class Greeting
{
    public function __construct() {
        $this->name = 'John Doe';
    }

    public function hello() {
        echo "Hello, {$this->name}!";
    }
}

$greeting = new Greeting();

Flight::route('/', array($greeting, 'hello')); 

路由是按定义的顺序匹配的。首先匹配到请求的路由将被调用。

方法路由

默认情况下,路由模式与所有请求方法匹配。您可以通过在 URL 前放置标识符来响应特定方法。

Flight::route('GET /', function(){
    echo 'I received a GET request.';
});

Flight::route('POST /', function(){
    echo 'I received a POST request.';
});

您还可以使用 | 分隔符将多个方法映射到单个回调

Flight::route('GET|POST /', function(){
    echo 'I received either a GET or a POST request.';
});

正则表达式

您可以在路由中使用正则表达式

Flight::route('/user/[0-9]+', function(){
    // This will match /user/1234
});

命名参数

您可以在路由中指定命名参数,这些参数将被传递到回调函数。

Flight::route('/@name/@id', function($name, $id){
    echo "hello, $name ($id)!";
});

您还可以使用 : 分隔符在命名参数中使用正则表达式

Flight::route('/@name/@id:[0-9]{3}', function($name, $id){
    // This will match /bob/123
    // But will not match /bob/12345
});

不支持使用命名参数匹配正则表达式中的分组 ()

可选参数

您可以通过将段包裹在括号中来指定可选的命名参数以进行匹配。

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
});

未匹配的任何可选参数都将作为 NULL 传递。

通配符

匹配仅针对单个 URL 段落进行。如果您想匹配多个段,可以使用 * 通配符。

Flight::route('/blog/*', function(){
    // This will match /blog/2000/02/01
});

要将所有请求路由到单个回调,您可以这样做

Flight::route('*', function(){
    // Do something
});

传递

您可以通过从回调函数中返回 true 将执行传递给下一个匹配的路由。

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
});

路由信息

如果您想检查匹配的路由信息,可以将路由对象传递到您的回调函数中,作为路由方法的第三个参数传递 true。路由对象将始终作为最后一个参数传递给回调函数。

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);

扩展

Flight 被设计成一个可扩展的框架。框架附带了一些默认方法和组件,但它允许您映射自己的方法、注册自己的类,甚至覆盖现有的类和方法。

映射方法

要映射自己的自定义方法,您使用 map 函数

// Map your method
Flight::map('hello', function($name){
    echo "hello $name!";
});

// Call your custom method
Flight::hello('Bob');

注册类

要注册自己的类,您使用 register 函数

// Register your class
Flight::register('user', 'User');

// Get an instance of your class
$user = Flight::user();

注册方法还允许您将参数传递给类构造函数。因此,当您加载自定义类时,它将预先初始化。您可以通过传递一个额外的数组来定义构造函数参数。以下是一个加载数据库连接的示例

// 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();

如果传递一个额外的回调参数,它将在类构造后立即执行。这允许您为新对象执行任何设置程序。回调函数接受一个参数,即新对象的实例。

// 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);
});

默认情况下,每次您加载类时都会得到一个共享实例。要获取类的新的实例,只需将false作为参数传递

// Shared instance of the class
$shared = Flight::db();

// New instance of the class
$new = Flight::db(false);

请注意,映射方法具有注册类的优先级。如果您使用相同的名称声明两者,则只会调用映射方法。

重写

Flight 允许您在不修改任何代码的情况下重写其默认功能以满足您的需求。

例如,当 Flight 无法将 URL 与路由匹配时,它将调用 notFound 方法,发送通用的 HTTP 404 响应。您可以使用 map 方法重写此行为

Flight::map('notFound', function(){
    // Display custom 404 page
    include 'errors/404.html';
});

Flight 还允许您替换框架的核心组件。例如,您可以用您自己的自定义类替换默认的 Router 类

// Register your custom class
Flight::register('router', 'MyRouter');

// When Flight loads the Router instance, it will load your class
$myrouter = Flight::router();

然而,像 mapregister 这样的框架方法不能被重写。如果您尝试这样做,将会出错。

过滤

Flight 允许您在方法被调用之前和之后过滤方法。没有需要记住的预定义钩子。您可以过滤任何默认框架方法以及任何您已映射的自定义方法。

过滤器函数看起来像这样

function(&$params, &$output) {
    // Filter code
}

使用传入的变量,您可以操作输入参数和/或输出。

您可以通过这样做来让过滤器在方法之前运行

Flight::before('start', function(&$params, &$output){
    // Do something
});

您可以通过这样做来让过滤器在方法之后运行

Flight::after('start', function(&$params, &$output){
    // Do something
});

您可以为任何方法添加任意数量的过滤器。它们将以它们声明的顺序被调用。

以下是一个过滤过程的示例

// 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');

这应该显示

Hello Fred! Have a nice day!

如果您已定义多个过滤器,您可以通过在任意一个过滤器函数中返回 false 来断开链。

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';
});

请注意,核心方法如 mapregister 不能被过滤,因为它们是直接调用而不是动态调用的。

变量

Flight 允许您保存变量,以便在应用程序的任何地方使用。

// Save your variable
Flight::set('id', 123);

// Elsewhere in your application
$id = Flight::get('id');

要检查变量是否已设置,您可以这样做

if (Flight::has('id')) {
     // Do something
}

您可以通过这样做来清除变量

// Clears the id variable
Flight::clear('id');

// Clears all variables
Flight::clear();

Flight 还使用变量进行配置。

Flight::set('flight.log_errors', true);

视图

Flight 默认提供一些基本的模板功能。要显示视图模板,请调用 render 方法并传递模板文件的名称和可选的模板数据

Flight::render('hello.php', array('name' => 'Bob'));

您传递的模板数据将自动注入到模板中,可以像本地变量一样引用。模板文件只是 PHP 文件。如果 hello.php 模板文件的内容是

Hello, '<?php echo $name; ?>'!

输出将是

Hello, Bob!

您也可以使用 set 方法手动设置视图变量

Flight::view()->set('name', 'Bob');

变量 name 现在可以在所有视图中使用。因此,您可以简单地这样做

Flight::render('hello');

请注意,在指定 render 方法中模板的名称时,您可以省略 .php 扩展名。

默认情况下,Flight 将在 views 目录中查找模板文件。您可以通过设置以下配置来为模板设置一个备用路径

Flight::set('flight.views.path', '/path/to/views');

布局

网站通常使用单个布局模板文件,内容可以互换。为了将内容渲染到布局中,您可以在render方法中传递一个可选参数。

Flight::render('header', array('heading' => 'Hello'), 'header_content');
Flight::render('body', array('body' => 'World'), 'body_content');

您的视图将保存名为header_contentbody_content的变量。然后,您可以通过以下方式渲染布局:

Flight::render('layout', array('title' => 'Home Page'));

如果模板文件看起来像这样

header.php:

<h1><?php echo $heading; ?></h1>

body.php:

<div><?php echo $body; ?></div>

layout.php:

<html>
<head>
<title><?php echo $title; ?></title>
</head>
<body>
<?php echo $header_content; ?>
<?php echo $body_content; ?>
</body>
</html>

输出将是

<html>
<head>
<title>Home Page</title>
</head>
<body>
<h1>Hello</h1>
<div>World</div>
</body>
</html>

自定义视图

Flight允许您通过注册自己的视图类来简单地替换默认视图引擎。以下是如何为您的视图使用Smarty模板引擎的示例

// 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');

为了完整性,您还应该覆盖Flight的默认渲染方法

Flight::map('render', function($template, $data){
    Flight::view()->assign($data);
    Flight::view()->display($template);
});

错误处理

错误和异常

所有错误和异常都被Flight捕获并传递到error方法。默认行为是发送一个通用的HTTP 500 内部服务器错误响应,并包含一些错误信息。

您可以为此覆盖此行为以满足您的需求

Flight::map('error', function(Exception $ex){
    // Handle error
    echo $ex->getTraceAsString();
});

默认情况下,错误不会记录到Web服务器。您可以通过更改配置来启用此功能

Flight::set('flight.log_errors', true);

未找到

当URL无法找到时,Flight将调用notFound方法。默认行为是发送一个HTTP 404 未找到响应,并包含一条简单消息。

您可以为此覆盖此行为以满足您的需求

Flight::map('notFound', function(){
    // Handle not found
});

重定向

您可以通过使用redirect方法并传递一个新的URL来重定向当前请求

Flight::redirect('/new/location');

默认情况下,Flight发送HTTP 303状态码。您可以选择设置一个自定义的状态码

Flight::redirect('/new/location', 401);

请求

Flight将HTTP请求封装到一个单独的对象中,您可以通过以下方式访问它

$request = Flight::request();

请求对象提供了以下属性

url - The URL being requested
base - The parent subdirectory of the URL
method - The request method (GET, POST, PUT, DELETE)
referrer - The referrer URL
ip - IP address of the client
ajax - Whether the request is an AJAX request
scheme - The server protocol (http, https)
user_agent - Browser information
type - The content type
length - The content length
query - Query string parameters
data - Post data or JSON data
cookies - Cookie data
files - Uploaded files
secure - Whether the connection is secure
accept - HTTP accept parameters
proxy_ip - Proxy IP address of the client
host - The request host name

您可以将querydatacookiesfiles属性作为数组或对象访问。

因此,要获取查询字符串参数,您可以这样做

$id = Flight::request()->query['id'];

或者您也可以这样做

$id = Flight::request()->query->id;

原始请求体

要获取原始HTTP请求体,例如处理PUT请求时,您可以这样做

$body = Flight::request()->getBody();

JSON输入

如果您发送一个类型为application/json且数据为{"id": 123}的请求,它将从data属性中可用

$id = Flight::request()->data->id;

HTTP缓存

Flight提供了内置的HTTP级缓存支持。如果满足缓存条件,Flight将返回一个HTTP 304 Not Modified响应。下次客户端请求同一资源时,将提示使用其本地缓存的版本。

最后修改时间

您可以使用lastModified方法并传入一个UNIX时间戳来设置页面最后修改的日期和时间。客户端将继续使用其缓存,直到最后修改值更改。

Flight::route('/news', function(){
    Flight::lastModified(1234567890);
    echo 'This content will be cached.';
});

ETag

ETag缓存类似于Last-Modified,但是您可以指定资源所需的任何id

Flight::route('/news', function(){
    Flight::etag('my-unique-id');
    echo 'This content will be cached.';
});

请注意,调用lastModifiedetag都将设置和检查缓存值。如果在请求之间缓存值相同,Flight将立即发送一个HTTP 304响应并停止处理。

停止

您可以在任何位置通过调用halt方法来停止框架

Flight::halt();

您还可以指定一个可选的HTTP状态码和消息

Flight::halt(200, 'Be right back...');

调用halt将丢弃到该点为止的所有响应内容。如果您想要停止框架并输出当前的响应,请使用stop方法

Flight::stop();

JSON

Flight提供了发送JSON和JSONP响应的支持。要发送JSON响应,您可以将一些要JSON编码的数据传递过去

Flight::json(array('id' => 123));

对于JSONP请求,您可以可选地传递用于定义回调函数的查询参数名称

Flight::jsonp(array('id' => 123), 'q');

因此,当使用?q=my_func进行GET请求时,您应该收到以下输出

my_func({"id":123});

如果您没有传入查询参数名称,它将默认为 jsonp

配置

您可以通过 set 方法设置配置值来自定义 Flight 的某些行为。

Flight::set('flight.log_errors', true);

以下是可以用的所有配置设置的列表

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)

框架方法

Flight 被设计成易于使用和理解。以下为框架的完整方法集合。它包括核心方法,这些是常规静态方法,以及可扩展的方法,这些是映射方法,可以进行过滤或重写。

核心方法

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

可扩展方法

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.

通过 mapregister 添加的任何自定义方法也可以被过滤。

框架实例

您可以选择将 Flight 作为对象实例运行,而不是作为全局静态类。

require 'flight/autoload.php';

use flight\Engine;

$app = new Engine();

$app->route('/', function(){
    echo 'hello world!';
});

$app->start();

因此,您将使用与 Engine 对象上具有相同名称的实例方法来调用该方法,而不是调用静态方法。