PHP5 API 生成工具

1.2.3 2023-05-15 13:32 UTC

README

简单的API引导程序。

Monki是一个库,允许您快速为项目添加基本API。提供API变得越来越重要,原因如下:

  • 它允许您的库或项目轻松成为“物联网”的一部分,允许其他库或应用程序访问它;
  • 对于使用AngularJS等框架的SPA,提供API是必需的;
  • 即使在编写传统的PHP应用程序时,使用API也可以抽象出数据库等许多工作。

安装

Composer(推荐)

$ cd /path/to/project
$ composer require monomelodies/monki

手动

下载或克隆仓库,并将/path/to/monki/src添加到PSR-4自动加载器的Monki\\命名空间。

设置

Monki使用league/pipeline实现了流水线

<?php

use Monki\Api;

$monki = new Monki('/base/url/of/api/');

然后,您的前端控制器(例如index.php)可以将其添加到其流水线中(有关更多信息,请参阅league/pipeline文档)。

<?php

$pipeline = (new Pipeline)
    ->pipe($monki);

如果您没有使用流水线,您还可以调用Monki对象并检查其返回值。

<?php

use Psr\Http\Message\ResponseInterface;
use Zend\Diactoros\ServerRequestFactory;

$response = $monki(ServerRequestFactory::fromGlobals());
if ($response instanceof ResponseInterface) {
    // Emit the response
} else {
    // No an API URL; use e.g. your router to determine further handling.
}

对于输出,您可以使用例如Zend\Diactoros\Response\SapiEmitter。Diactoros是Monki的要求,因此您已经拥有它。但您可以使用其他任何东西。

添加状态和响应

内部,Monki的流水线利用了Monolyth\Reroute路由器。Reroute允许您使用when('url')then('statename', callable)指定对HTTP请求的期望处理方式。此外,您还可以指定postputdelete的可调用函数,并可以向流水线添加阶段,例如检查用户对某些功能的访问权限。由于Monki上的when方法返回实际的底层路由器,因此您可以手动指定所有这些。

但是,通常任何结构良好的API都将遵循更类似于以下模式的模式

GET /api/user/ <- browse all users
POST /api/user/ <- create a new user
GET /api/user/:id/ <- retrieve a specific user
POST /api/user/:id/ <- update a specific user
DELETE /api/user/:id/ <- delete a specific user

这正是Monki旨在使其易于引导的“默认”API行为!它使用crud方法来实现这一点

<?php

use Monomelodies\Monki\Handler\Crud;

class MyHandler extends Crud
{
}

$monki->crud('/api/user/', new MyHandler);

在上面的示例中,我们简单地传递了一个Crud处理程序接口的实例。crud方法的返回值还可以流水线,例如用于访问检查。第一个参数是端点的基路径,第二个是资源特定端点的路径。

如果您尝试访问URL /api/user/,您不会得到用户列表,而是会发现API路由器返回NULL。这是有道理的,因为我们的处理程序实际上没有指定任何处理方式!嘿,别急,Monki有一些默认设置,但不是先知...

让我们首先让它对browse-style请求做出响应

<?php

use Monomelodies\Monki\Handler\Crud;

class MyHandler extends Crud
{
    public function browse()
    {
        // This should e.g. do a database query in real life:
        $users = ['Marijn', 'Linus', 'Bill'];
        return $this->jsonResponse($users);
    }
}

默认支持的处理器列表如下

  • GET 处理器
  • POST 处理器
  • GET 处理器
  • POST 处理器
  • DELETE 处理器

要为多个表重用您的处理器,例如,您可以在构造函数中传递表名并将其私下存储。

完整示例

以下是一个使用Quibble\Query进行数据库查询的完整、工作示例(有关Quibble查询构建器的更多信息,请参阅他们的文档)。

<?php

use Quibble\Dabble\Adapter;
use Zend\Diactoros\Response\EmptyResponse;

class MyHandler extends Crud implements Browse, Create, Retrieve, Update, Delete
{
    private $db;
    private $table;

    public function __construct(Adapter $db, $table)
    {
        $this->db = $db;
        $this->table = $table;
    }

    public function browse($id = null)
    {
        $items = $this->db->selectFrom($this->table)
            ->fetchAll(PDO::FETCH_ASSOC);
        return $this->jsonResponse($items);
    }

    public function create($id = null)
    {
        $this->db->insertInto($this->table)
            ->execute($_POST);
        $id = $this->db->lastInsertId($this->table);
        return $this->retrieve($id, 201);
    }

    public function retrieve($id, $status = 200)
    {
        $item = $this->db->selectFrom($this->table)
            ->where('id = ?', $id)
            ->fetch(PDO::FETCH_ASSOC);
        return $this->jsonResponse($item, $status);
    }

    public function update($id)
    {
        $this->db->updateTable($this->table)
            ->where('id = ?', $id)
            ->execute($_POST);
        return $this->retrieve($id);
    }

    public function delete($id)
    {
        $this->db->deleteFrom($this->table)
            ->where('id = ?', $id)
            ->execute();
        return $this->emptyResponse(204);
    }
}

// Assuming $user contains the currently logged in user...
$monki->crud('/api/user/', '/:id/', new MyHandler($db, 'user'))
    ->pipe(function ($payload) use ($user) {
        if ($user->name != 'Marijn') {
            // Bad user! No access.
            return new EmptyResponse(403);
        }
        return $payload;
    });

显然,这相当简单,因为它没有进行任何错误检查,但您已经了解了这种方法。使用此处理器类,您可以快速设置对一些数据库表的处理。

<?php

$check = function ($payload) use ($user) {
    if ($user->name != 'Marijn') {
        // Bad user! No access.
        return new EmptyResponse(403);
    }
    return $payload;
};
$monki->crud('/api/user/', '/:id/', new MyHandler($db, 'user'))->pipe($check);
$monki->crud('/api/message/', '/:id/', new MyHandler($db, 'message'))->pipe($check);
$monki->crud('/api/foo/', '/:id/', new MyHandler($db, 'foo'))->pipe($check);
$monki->crud('/api/bar/', '/:id/', new MyHandler($db, 'bar'))->pipe($check);
// This endpoint is open for the world (usually a bad idea ;)):
$monki->crud('/api/baz/', '/:id/', new MyHandler($db, 'baz'));
// ...

转换响应

与在处理器构造函数中传递PDO适配器和表名类似,也可以传递一个转换器(或通过其他方式注入)。转换器是类或可调用对象,用于在将原始数据“倾倒”到开放环境中之前对其进行“按摩”。这是你自己需要实现的事情;Monki不希望做出假设。但我们推荐使用league/fractal;这是一个非常通用的转换工具。对于更简单的查询结果转换,也可以考虑使用quibble/transformer

例如,在我们的用户示例中,表可能还包含一个我们不想公开的pass列。或者我们可以用它将用户ID转换为实际的整数,而不是字符串。

添加自定义方法

假设我们还需要为API添加端点来计算集合中项目总数的API端点(例如,用于分页)。这可以通过使用注解来实现

<?php

class MyHandler extends Crud
{
    /**
     * @Method GET
     * @Url /count/
     */
    public function countIt()
    {
        return $this->jsonResponse(3);
    }
}

要为具有资源的端点定义一个自定义方法,只需在方法中要求关联的参数并指定一个自定义URL

<?php

class MyHandler extends Crud
{
    /**
     * @Method PUT
     * @Url /custom/:id/
     */
    public function thisIsSomethingCustom($id)
    {
        // ...
    }
}

在内部,Crud处理器的默认方法始终可用,使用指定的HTTP方法和URL。如果您需要以任何原因覆盖它们,也可以注解它们。`@Url`注解附加到您给`Api`构造函数提供的任何基本URL上。如果省略,则将使用该URL原样使用(例如,用于处理`HEAD`或`OPTIONS`)。

根据GET或POST参数处理分页等问题

在您的处理器中尽情发挥吧!您也可以在这里转换数据,例如,在存储之前对某些字段进行`json_encode`。这个场景下可能用到的有用包包括League/FractalQuibble/Transformer

Monki这个名字是什么意思?

我是双语使用者,在荷兰语中,“API”发音就像“小猴子”这个词。所以这就是原因。