Learn/learn

学习

此页面是学习 Flight 的指南。它涵盖了框架的基础知识以及如何使用它。

路由

在 Flight 中,路由通过将 URL 模式与回调函数进行匹配来完成。

Flight::route('/', function(){
    echo '你好,世界!';
});

回调可以是任何可调用的对象。因此,您可以使用常规函数:

function hello(){
    echo '你好,世界!';
}

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

或类方法:

class Greeting {
    public static function hello() {
        echo '你好,世界!';
    }
}

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

或对象方法:

class Greeting
{
    public function __construct() {
        $this->name = '约翰·多';
    }

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

$greeting = new Greeting();

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

路由按定义的顺序匹配。第一个匹配请求的路由将被调用。

方法路由

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

Flight::route('GET /', function(){
    echo '我收到了一个 GET 请求。';
});

Flight::route('POST /', function(){
    echo '我收到了一个 POST 请求。';
});

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

Flight::route('GET|POST /', function(){
    echo '我收到了一个 GET 或 POST 请求。';
});

正则表达式

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

Flight::route('/user/[0-9]+', function(){
    // 这将匹配 /user/1234
});

命名参数

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

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

您还可以通过使用 : 分隔符包含正则表达式与命名参数:

Flight::route('/@name/@id:[0-9]{3}', function($name, $id){
    // 这将匹配 /bob/123
    // 但不会匹配 /bob/12345
});

可选参数

您可以通过将片段放在括号中来指定可选参数进行匹配。

Flight::route('/blog(/@year(/@month(/@day)))', function($year, $month, $day){
    // 这将匹配以下 URL:
    // /blog/2012/12/10
    // /blog/2012/12
    // /blog/2012
    // /blog
});

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

通配符

匹配仅在单个 URL 片段上进行。如果要匹配多个片段,可以使用 * 通配符。

Flight::route('/blog/*', function(){
    // 这将匹配 /blog/2000/02/01
});

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

Flight::route('*', function(){
    // 做一些事情
});

继续

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

Flight::route('/user/@name', function($name){
    // 检查某个条件
    if ($name != "Bob") {
        // 继续到下一个路由
        return true;
    }
});

Flight::route('/user/*', function(){
    // 这将被调用
});

路由信息

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

Flight::route('/', function($route){
    // 匹配的 HTTP 方法数组
    $route->methods;

    // 命名参数数组
    $route->params;

    // 匹配的正则表达式
    $route->regex;

    // 包含 URL 模式中使用的任何 '*' 的内容
    $route->splat;
}, true);

路由分组

有时您可能想将相关路由分组在一起(如 /api/v1)。您可以使用 group 方法做到这一点:

Flight::group('/api/v1', function () {
  Flight::route('/users', function () {
    // 匹配 /api/v1/users
  });

  Flight::route('/posts', function () {
    // 匹配 /api/v1/posts
  });
});

您甚至可以嵌套分组:

Flight::group('/api', function () {
  Flight::group('/v1', function () {
    // Flight::get() 获取变量,而不是设置路由!请查看下面的对象上下文
    Flight::route('GET /users', function () {
      // 匹配 GET /api/v1/users
    });

    Flight::post('/posts', function () {
      // 匹配 POST /api/v1/posts
    });

    Flight::put('/posts/1', function () {
      // 匹配 PUT /api/v1/posts
    });
  });
  Flight::group('/v2', function () {
    // Flight::get() 获取变量,而不是设置路由!请查看下面的对象上下文
    Flight::route('GET /users', function () {
      // 匹配 GET /api/v2/users
    });
  });
});

使用对象上下文进行分组

您仍然可以使用 Engine 对象进行路由分组,如下所示:

$app = new \flight\Engine();
$app->group('/api/v1', function (Router $router) {
  $router->get('/users', function () {
    // 匹配 GET /api/v1/users
  });

  $router->post('/posts', function () {
    // 匹配 POST /api/v1/posts
  });
});

路由别名

您可以为路由分配别名,以便在代码中可以动态生成 URL(例如,一个模板)。

Flight::route('/users/@id', function($id) { echo '用户:'.$id; }, false, 'user_view');

// 稍后在代码的某个地方
Flight::getUrl('user_view', [ 'id' => 5 ]); // 将返回 '/users/5'

这在您的 URL 发生变化时尤其有用。在上面的示例中,假设用户被移动到了 /admin/users/@id。 有了别名,您不必更改引用别名的任何地方,因为别名将现在返回 /admin/users/5,就像上面的示例一样。

路由别名仍然在分组中有效:

Flight::group('/users', function() {
    Flight::route('/@id', function($id) { echo '用户:'.$id; }, false, 'user_view');
});

// 稍后在代码的某个地方
Flight::getUrl('user_view', [ 'id' => 5 ]); // 将返回 '/users/5'

扩展

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

映射方法

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

// 映射您的方法
Flight::map('hello', function($name){
    echo "你好 $name!";
});

// 调用您的自定义方法
Flight::hello('Bob');

注册类

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

// 注册您的类
Flight::register('user', 'User');

// 获取您的类的实例
$user = Flight::user();

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

// 注册带有构造函数参数的类
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'));

// 获取您的类的实例
// 这将使用定义的参数创建一个对象
//
//     new PDO('mysql:host=localhost;dbname=test','user','pass');
//
$db = Flight::db();

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

// 回调将传递构造的对象
Flight::register('db', 'PDO', array('mysql:host=localhost;dbname=test','user','pass'),
  function($db){
    $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  }
);

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

// 类的共享实例
$shared = Flight::db();

// 类的新实例
$new = Flight::db(false);

请记住,映射的方法优先于注册的类。如果您使用相同的名称同时声明两者,则仅调用映射的方法。

重写

Flight 允许您重写其默认功能,以适应您的需求,而不必修改任何代码。

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

Flight::map('notFound', function(){
    // 显示自定义 404 页面
    include 'errors/404.html';
});

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

// 注册您的自定义类
Flight::register('router', 'MyRouter');

// 当 Flight 加载 Router 实例时,它将加载您的类
$myrouter = Flight::router();

然而,像 mapregister 这样的框架方法是无法被重写的。如果您尝试这样做,将会引发错误。

过滤

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

过滤函数如下所示:

function(&$params, &$output) {
    // 过滤代码
}

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

您可以在方法之前运行过滤:

Flight::before('start', function(&$params, &$output){
    // 做一些事情
});

您可以在方法之后运行过滤:

Flight::after('start', function(&$params, &$output){
    // 做一些事情
});

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

以下是过滤过程的示例:

// 映射一个自定义方法
Flight::map('hello', function($name){
    return "你好, $name!";
});

// 添加一个之前的过滤器
Flight::before('hello', function(&$params, &$output){
    // 操纵参数
    $params[0] = 'Fred';
});

// 添加一个之后的过滤器
Flight::after('hello', function(&$params, &$output){
    // 操纵输出
    $output .= " 祝您有个愉快的一天!";
});

// 调用自定义方法
echo Flight::hello('Bob');

这应该显示:

你好 Fred! 祝您有个愉快的一天!

如果您定义了多个过滤器,您可以通过在任何过滤函数中返回 false 来打破链:

Flight::before('start', function(&$params, &$output){
    echo '一';
});

Flight::before('start', function(&$params, &$output){
    echo '二';

    // 这将结束链
    return false;
});

// 这将不会被调用
Flight::before('start', function(&$params, &$output){
    echo '三';
});

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

变量

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

// 保存您的变量
Flight::set('id', 123);

// 在您应用程序的其他地方
$id = Flight::get('id');

要查看变量是否已设置,您可以执行:

if (Flight::has('id')) {
     // 做一些事情
}

您可以通过以下方式清除变量:

// 清除 id 变量
Flight::clear('id');

// 清除所有变量
Flight::clear();

Flight 还使用变量进行配置。

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

视图

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

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

您传入的模板数据会自动注入到模板中,并可以像局部变量一样引用。模板文件仅是 PHP 文件。如果 hello.php 模板文件的内容为:

你好,'<?php echo $name; ?>'!

输出将是:

你好,Bob!

您还可以通过使用 set 方法手动设置视图变量:

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

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

Flight::render('hello');

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

默认情况下,Flight 将查找一个 views 目录来查找模板文件。您可以通过设置以下配置来指定模板的替代路径:

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

布局

网站通常有一个单一的布局模板文件,具有互换内容。要呈现内容以在布局中使用,您可以将可选参数传递给 render 方法。

Flight::render('header', array('heading' => '你好'), 'header_content');
Flight::render('body', array('body' => '世界'), 'body_content');

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

Flight::render('layout', array('title' => '主页'));

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

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>主页</title>
</head>
<body>
<h1>你好</h1>
<div>世界</div>
</body>
</html>

自定义视图

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

// 加载 Smarty 库
require './Smarty/libs/Smarty.class.php';

// 将 Smarty 注册为视图类
// 还传入一个回调函数以在加载时配置 Smarty
Flight::register('view', 'Smarty', array(), function($smarty){
    $smarty->template_dir = './templates/';
    $smarty->compile_dir = './templates_c/';
    $smarty->config_dir = './config/';
    $smarty->cache_dir = './cache/';
});

// 分配模板数据
Flight::view()->assign('name', 'Bob');

// 显示模板
Flight::view()->display('hello.tpl');

为了完整性,您还应该重写 Flight 的默认 render 方法:

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

错误处理

错误和异常

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

您可以重写此行为以满足您的需求:

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

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

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

找不到

当找不到 URL 时,Flight 会调用 notFound 方法。默认行为是发送 HTTP 404 找不到 响应,并附带一条简单的消息。

您可以重写此行为以满足您的需求:

Flight::map('notFound', function(){
    // 处理未找到
});

重定向

您可以使用 redirect 方法通过传入新 URL 来重定向当前请求:

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

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

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

请求

Flight 将 HTTP 请求封装为一个单一对象,可以通过以下方式访问:

$request = Flight::request();

请求对象提供以下属性:

url - 请求的 URL
base - URL 的父子目录
method - 请求方法 (GET, POST, PUT, DELETE)
referrer - 引用 URL
ip - 客户端的 IP 地址
ajax - 请求是否为 AJAX 请求
scheme - 服务器协议 (http, https)
user_agent - 浏览器信息
type - 内容类型
length - 内容长度
query - 查询字符串参数
data - POST 数据或 JSON 数据
cookies - Cookie 数据
files - 上传的文件
secure - 连接是否安全
accept - HTTP 接受参数
proxy_ip - 客户端的 Proxy IP 地址

您可以将 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;

停止

您可以通过调用 halt 方法在任何时刻停止框架:

Flight::halt();

您还可以指定可选的 HTTP 状态代码和消息:

Flight::halt(200, '稍后回来...');

调用 halt 将丢弃到那时为止的任何响应内容。如果您想停止框架并输出当前响应,请使用 stop 方法:

Flight::stop();

HTTP 缓存

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

最后修改

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

Flight::route('/news', function(){
    Flight::lastModified(1234567890);
    echo '此内容将被缓存。';
});

ETag

ETag 缓存类似于 Last-Modified,但您可以为资源指定任何您想要的 ID:

Flight::route('/news', function(){
    Flight::etag('my-unique-id');
    echo '此内容将被缓存。';
});

请记住,调用 lastModifiedetag 将设置并检查缓存值。如果请求之间的缓存值相同,Flight 将立即发送 HTTP 304 响应并停止处理。

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

配置

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

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

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

flight.base_url - 覆盖请求的基本 URL。(默认为:null)
flight.case_sensitive - 对 URL 进行大小写敏感匹配。(默认为:false)
flight.handle_errors - 允许 Flight 内部处理所有错误。(默认为:true)
flight.log_errors - 将错误记录到 Web 服务器的错误日志文件。(默认为:false)
flight.views.path - 包含视图模板文件的目录。(默认为:./views)
flight.views.extension - 视图模板文件扩展名。(默认为:.php)

框架方法

Flight 旨在易于使用和理解。以下是框架的完整方法集。它由核心方法(常规静态方法)和可扩展方法(可过滤或重写的映射方法)组成。

核心方法

Flight::map(string $name, callable $callback, bool $pass_route = false) // 创建自定义框架方法。
Flight::register(string $name, string $class, array $params = [], ?callable $callback = null) // 将类注册到框架方法。
Flight::before(string $name, callable $callback) // 在框架方法之前添加过滤器。
Flight::after(string $name, callable $callback) // 在框架方法之后添加过滤器。
Flight::path(string $path) // 添加用于自动加载类的路径。
Flight::get(string $key) // 获取变量。
Flight::set(string $key, mixed $value) // 设置变量。
Flight::has(string $key) // 检查变量是否已设置。
Flight::clear(array|string $key = []) // 清除变量。
Flight::init() // 将框架初始化为默认设置。
Flight::app() // 获取应用程序对象实例

可扩展方法

Flight::start() // 启动框架。
Flight::stop() // 停止框架并发送响应。
Flight::halt(int $code = 200, string $message = '') // 以可选状态码和消息停止框架。
Flight::route(string $pattern, callable $callback, bool $pass_route = false) // 将 URL 模式映射到回调。
Flight::group(string $pattern, callable $callback) // 创建 URL 分组,模式必须是字符串。
Flight::redirect(string $url, int $code) // 重定向到另一个 URL。
Flight::render(string $file, array $data, ?string $key = null) // 渲染模板文件。
Flight::error(Throwable $error) // 发送 HTTP 500 响应。
Flight::notFound() // 发送 HTTP 404 响应。
Flight::etag(string $id, string $type = 'string') // 执行 ETag HTTP 缓存。
Flight::lastModified(int $time) // 执行最后修改的 HTTP 缓存。
Flight::json(mixed $data, int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // 发送 JSON 响应。
Flight::jsonp(mixed $data, string $param = 'jsonp', int $code = 200, bool $encode = true, string $charset = 'utf8', int $option) // 发送 JSONP 响应。

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

框架实例

您可以选择以对象实例的方式运行 Flight,而不是以全局静态类的方式运行。

require 'flight/autoload.php';

use flight\Engine;

$app = new Engine();

$app->route('/', function(){
    echo '你好,世界!';
});

$app->start();

因此,您将以名为 Engine 的对象调用相同名称的实例方法,而不是调用静态方法。

Install

安装

1. 下载文件。

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

composer require flightphp/core

或者您可以直接 下载 文件并将其提取到您的 Web 目录中。

2. 配置您的 Web 服务器。

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

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

注意:如果您需要在子目录中使用 flight,请在 RewriteEngine On 后添加一行 RewriteBase /subdir/注意:如果您想保护所有服务器文件,比如数据库或环境文件。 请将其放入您的 .htaccess 文件中:

RewriteEngine On
RewriteRule ^(.*)$ index.php

对于 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 '你好,世界!';
});

最后,启动框架。

Flight::start();

About

什么是 Flight ?

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

require 'flight/Flight.php';

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

// 启动 Flight
Flight::start();

了解更多

需求

Flight 需要 PHP 7.4 或更高版本。

许可

Flight 根据 MIT 许可发布。

社区

我们在 Matrix 上!请在 #flight-php-framework:matrix.org 与我们聊天。

贡献

本网站托管在 Github 上。 欢迎更新和语言翻译。