kix / apiranha
PHP的API客户端
Requires
- php: ^7.1
- doctrine/annotations: @stable
- guzzlehttp/guzzle: ^6.2
- symfony/property-access: @stable
Requires (Dev)
- guzzlehttp/guzzle: @stable
- jms/serializer: @stable
- nikic/php-parser: @stable
- phpunit/phpunit: ^7.1
- sstalle/php7cc: ^1.1
- symfony/console: ^3.1
- symfony/serializer: @stable
Suggests
- guzzlehttp/guzzle: An HTTP client that's supported out-of-the-box
- jms/serializer: Adds support for (de)serializing common formats
- nikic/php-parser: Allows dumping definitions to PHP code
- symfony/serializer: Adds support for (de)serializing common formats
This package is auto-updated.
Last update: 2024-09-14 02:46:43 UTC
README
Apiranha是一个库,使消费API变得更加容易和快速。Apiranha的一些灵感来自Retrofit
使用示例
一个完整的示例可以在examples/
目录中找到。请参阅ExampleCommand
和BuilderExampleCommand
。
快速开始
确保您已安装Composer,然后在您的项目文件夹中运行以下命令
composer require kix/apiranha='dev-master'
此外,如果您不想提供自己的HTTP客户端实现,则需要要求guzzlehttp/guzzle
。
为了消费例如Github API,首先您需要在接口中声明您的端点并注释它们。在Kix\Apiranha\Annotation
命名空间下可用的注释有:
- HTTP/REST方法注释
Get
CGet
Delete
Post
Put
Returns
,一个特殊的注释,用于表示端点返回类型。
首先,您需要决定您实际上需要从端点获取哪些数据。在我们的例子中,我们只是从Github REST API获取ID、名称、语言和星标数量
namespace \Kix\Apiranha\Examples\Model; class Repository { private $id; private $name; private $language; private $stargazersCount; }
当然,您将需要读取属性,因此您可以将字段声明为public
,或者您可以添加字段的getter。
接下来,为了能够从Github API获取单个仓库,您会声明一个接口,如下所示(注意注释)
use Kix\Apiranha\Annotation as Rest; interface GithubApi { /** * @Rest\Returns("\Kix\Apiranha\Examples\Model\Repository") * @Rest\Get("/repos/{username}/{repo}") */ public function getRepo(string $username, string $repo); }
在这里,我们说getRepo
方法将从/repos
端点返回数据。请注意,URL参数名称一致,并且使用string
进行类型提示:这将帮助我们为特定调用生成正确的URL。
在您实现了端点接口和模型之后,是时候见证魔法发生了
use Kix\Apiranha\Builder; use Kix\Apiranha\Examples\Definition\GithubApi; /** @var $endpoint GithubApi */ $endpoint = Builder::createEndpoint('http://api.github.com', [GithubApi::class]);
调用Builder::createEndpoint
,您将得到一个对象,该对象以您之前声明的接口的形式表示您的API。这意味着您可以注释它为@var $endpoint GithubApi
。
从现在起,您在接口中声明的所有方法都可通过端点对象使用。这里是如何看起来的
> $endpoint->listRepos('kix'); => array(30) { => object(Kix\Apiranha\Examples\Model\Repository)#76 (4) { => ["id":"Kix\Apiranha\Examples\Model\Repository":private]=> => int(43456580) => ["name":"Kix\Apiranha\Examples\Model\Repository":private]=> => string(6) "apiranha" => ["language":"Kix\Apiranha\Examples\Model\Repository":private]=> => NULL => ["stargazersCount":"Kix\Apiranha\Examples\Model\Repository":private]=> => int(1) => } => ... => }
深入了解
嗯,底层发生了什么?您在此简单API客户端中使用的Builder
只是一个帮助您快速做事的伪层,隐藏了大量您可能不关心的实现细节。然而,这不仅仅是调用一个方法并获取结果。以下是构建器假设的内容
- 您的路由将始终在您提供的注释中声明,参数将按原样绑定到URI上;
- 您将使用Guzzle作为HTTP客户端(该包将在安装时建议您使用);
- API序列化格式将是JSON,由Symfony的序列化器处理;
- API将返回的模型实例将使用PHP的反射API进行注入(这可能很昂贵);
如果这些陈述中的某些不符合您的要求,您始终可以扩展Apiranha背后的逻辑。
构建器底层
让我们来看看Builder
的内部结构。以下是createEndpoint
伪层的样子
public static function createEndpoint($baseUrl, array $definitions, array $listeners = array(), HttpAdapterInterface $adapter = null, Router $router = null) { if (!$adapter) { $adapter = new GuzzleHttpAdapter(new Client()); } if (!$router) { $router = new Router(); } if (!count($listeners)) { $serializerAdapter = new SymfonySerializerAdapter( new Serializer([], [new JsonEncoder()]) ); $serializerAdapter->addContentType('application/json', 'json'); $listeners[Endpoint::LISTENER_AFTER_RESPONSE] = new ContentTypeListenener($serializerAdapter); $listeners[Endpoint::LISTENER_AFTER_DATA] = new ReflectionHydratorListener(); } $endpoint = new Endpoint($adapter, $router, $baseUrl); foreach ($listeners as $evt => $listener) { $endpoint->addListener($evt, $listener); } $driver = new AnnotationDriver(); foreach ($definitions as $interfaceName) { $resources = $driver->createDefinitions($interfaceName); foreach ($resources as $resource) { $endpoint->addResourceDefinition($resource); } } return $endpoint; }
请注意,您仍然可以将监听器数组、HTTP适配器和路由器作为参数传递给此工厂方法。然而,如果您想手动处理事情,或者您不喜欢事情被决定(或者,您只是讨厌静态外观),您始终可以手动重新实现此逻辑。基本上就是这样。
现在,让我们具体谈谈。为什么我们需要构建器为我们实例化的所有这些东西呢?
HTTP层
首先,为了使用API,您需要以某种方式与第三方服务器交互。为此,您需要一个HTTP适配器。它应该实现 Kix\Apiranha\HttpAdapter\HttpAdapterInterface
,而 GuzzleHttpAdapter
是您可以现在使用的示例。
您必须实现的方法是 send(RequestInterface $request): ResponseInterface
,其中 RequestInterface
和 ResponseInterface
是标准 Psr\Http
消息。
资源定义
当处理您之前创建的注释接口时,端点将其注册为资源。资源是 Kix\Apiranha\ResourceDefinitionInterface
的一个实例,它基本上包含执行API请求所需的所有数据。您可以将它想象成Swagger定义。
路由器
路由器负责在给定资源和请求执行时使用的参数数组的情况下生成具体的URI。内置路由器允许您传递在资源路径中未显式声明的额外参数。这些将作为查询参数添加。
监听器
您可以将自己的或内置的监听器附加到生命周期,它由三个事件组成
Endpoint::LISTENER_BEFORE_REQUEST
,用于在请求发送之前修改请求,Endpoint::LISTENER_AFTER_RESPONSE
,用于将序列化程序附加到工作流程,Endpoint::LISTENER_AFTER_DATA
,用于给模型实例填充数据。
所有监听器都可以实现为可调用函数,并且“响应后”和“数据后”监听器也可以实现 Kix\Apiranha\Listener\AfterResponseListenerInterface
或 Kix\Apiranha\Listener\AfterDataListenerInterface
。
BEFORE_REQUEST监听器
附加到 Endpoint::LISTENER_BEFORE_REQUEST
的监听器仅接收一个参数:为当前请求实例化的 ResponseInterface
实例。例如,您可以使用它向受保护的API传递授权头。
$endpoint = Builder::createEndpoint('http://api.github.com', [GithubApi::class]); $endpoint->addListener(Endpoint::LISTENER_BEFORE_REQUEST, function (RequestInterface $request) { return $request->withAddedHeader('Authorization', 'Basic 12345'); });
AFTER_RESPONSE监听器
附加到 Endpoint::LISTENER_AFTER_RESPONSE
的监听器可以是与 function(RequestInterface $request, ResponseInterface $response)
签名匹配的可调用函数,也可以是 Kix\Apiranha\Listener\AfterResponseListenerInterface
的实例。例如,此类监听器在您使用的HTTP客户端不抛出错误HTTP状态代码的情况下可能很有用。
例如,这是 StatusCodeListener
class StatusCodeListener implements AfterResponseListenerInterface { /** * @param RequestInterface $request * @param ResponseInterface $response * @throws \Exception * @return void */ public function process(RequestInterface $request, ResponseInterface $response) { if ($response->getStatusCode() > 400) { throw new \RuntimeException(sprintf( 'Bad status code: %s', $response->getStatusCode() )); } } }
请求也允许您记录精确的请求/响应交互,例如。
请注意,您允许返回 ApiResponse
实例,该实例包装PSR响应,但还具有可操作的 data
属性。
AFTER_DATA监听器
附加到 Endpoint::LISTENER_AFTER_DATA
的监听器可以是与 function(ResponseInterface $response, ResourceDefinitionInterface $resource)
签名匹配的可调用函数,也可以是 Kix\Apiranha\Listener\AfterDataListenerInterface
的实例。
此类监听器用于用API返回的数据填充对象。
填充
有几种可用的填充策略
- 通过反射进行填充,在
ReflectionHydratorListener
中实现 - 使用
ocramius/generated-hydrator
进行填充,在GeneratedHydratorListener
中实现
反射填充器始终可用,只要PHP有反射API即可。生成的填充器是另一种实现,可能更高效。
这两种策略都作为监听器实现,可以附加到 Endpoint::LISTENER_AFTER_DATA
事件
$endpoint = new Endpoint($adapter, new Router(), 'http://api.github.com'); $endpoint->addListener(Endpoint::LISTENER_AFTER_DATA, new ReflectionHydratorListener());
待办:基准测试
一旦模型实例被初始化,你将获得一个你声明为模型的类的实例。就这样!