sammyk / facebook-query-builder
一种优雅且高效的方法,用于生成Facebook Graph API的嵌套请求。
Requires
- php: >=5.4.0
Requires (Dev)
- phpunit/phpunit: ~4.0
- squizlabs/php_codesniffer: ~2.0
README
一个查询构建器,可以轻松创建复杂的嵌套请求,以便通过Facebook Graph API获取大量特定数据。
Facebook Query Builder没有生产依赖。
$fqb = new SammyK\FacebookQueryBuilder\FQB; $photosEdge = $fqb->edge('photos')->fields(['id', 'source'])->limit(5); $request = $fqb->node('me')->fields(['id', 'email', $photosEdge]); echo (string) $request; # https://graph.facebook.com/me?fields=id,email,photos.limit(5){id,source}
简介
Facebook Query Builder使用与Graph API相同的命名约定来表示三个主要概念
- 节点:节点表示Facebook上的“现实世界的事物”,例如用户或页面。
- 边:边表示两个或更多节点之间的关系。例如,“照片”节点会有一个“评论”边。
- 字段:节点与其关联属性。这些属性称为字段。例如,用户有“id”和“name”字段。
当您向Graph API发送请求时,URL的结构如下
https://graph.facebook.com/node-id/edge-name?fields=field-name
要使用Facebook Query Builder生成相同的URL,您会这样做
$edge = $fqb->edge('edge-name')->fields('field-name'); echo $fqb->node('node-id')->fields($edge);
如果您执行该脚本,可能会惊讶地看到URL略有不同,因为它会输出
https://graph.facebook.com/node-id?fields=edge-name{field-name}
这两个URL在功能上相同,唯一的区别是Graph API返回响应数据的方式。Facebook Query Builder生成的URL的不同之处在于,它被表示为嵌套请求。
这正是Facebook Query Builder之所以强大的原因。它通过PHP界面生成流畅、易于阅读的格式化嵌套请求,来完成繁重的工作。
安装
Facebook Query Builder使用Composer安装。将Facebook Query Builder包添加到您的composer.json
文件中。
{ "require": { "sammyk/facebook-query-builder": "^2.0" } }
用法
初始化
要开始与Facebook Query Builder交互,您只需实例化一个FQB
对象。
// Assuming you've included your composer autoload.php file before this line. $fqb = new SammyK\FacebookQueryBuilder\FQB;
您可以将许多配置选项传递给FQB
构造函数。
一个基本示例
以下是一个基本示例,获取登录用户的id
和email
(假设用户授予您的应用email权限)。
$fqb = new SammyK\FacebookQueryBuilder\FQB; $request = $fqb->node('me') ->fields(['id', 'email']) ->accessToken('user-access-token') ->graphVersion('v3.1'); echo $request; # https://graph.facebook.com/v3.1/me?access_token=user-access-token&fields=id,email $response = file_get_contents((string) $request); var_dump($response); # string(50) "{"id":"12345678","email":"foo-bar\u0040gmail.com"}"
获取多个边的跨数据
Facebook Query Builder的核心是它对嵌套请求的支持。嵌套请求允许您通过一个请求从Graph API获取大量数据。
以下示例将获取登录用户的姓名和前5张他们被标记的照片,只需一次Graph调用即可。
$fqb = new SammyK\FacebookQueryBuilder\FQB([/* . . . */]); $photosEdge = $fqb->edge('photos')->fields(['id', 'source'])->limit(5); $request = $fqb->node('me')->fields(['name', $photosEdge]); echo $request; # https://graph.facebook.com/me?fields=name,photos.limit(5){id,source} // Assumes you've set a default access token $response = file_get_contents((string) $request); var_dump($response); # string(1699) "{"name":"Sammy Kaye Powers","photos":{"data":[{"id":"123","source":"https:\/\/scontent.xx.fbcdn.net\/hphotos-xfp1 . . .
边可以包含其他边,以实现无限深度。这使得在保持代码可读性的同时,能够执行相当复杂的图调用。
以下示例将获取用户 1234
的姓名和他们在其中标记的前10张照片。对于每张照片,它获取前2条评论和所有点赞。
$fqb = new SammyK\FacebookQueryBuilder\FQB([/* . . . */]); $likesEdge = $fqb->edge('likes'); $commentsEdge = $fqb->edge('comments')->fields('message')->limit(2); $photosEdge = $fqb->edge('photos') ->fields(['id', 'source', $commentsEdge, $likesEdge]) ->limit(10); $request = $fqb->node('1234')->fields(['name', $photosEdge]); echo $request; # https://graph.facebook.com/1234?fields=name,photos.limit(10){id,source,comments.limit(2){message},likes} // Assumes you've set a default access token $response = file_get_contents((string) $request); var_dump($response); # string(10780) "{"name":"Some Foo User","photos":{"data":[ . . .
发送嵌套请求
由于Facebook查询构建器只是一个生成嵌套请求语法的工具,因此它不会为您向Graph API发起请求。您必须使用某种HTTP客户端来发送请求。
我们假设您已经在Facebook上创建了一个应用,并且获得了访问令牌。
使用Facebook PHP SDK进行请求
发送请求并接收响应的推荐方式是使用官方的 Facebook PHP SDK v5。您需要从原生Facebook PHP SDK中创建一个Facebook\Facebook
超级服务类的实例。
$fb = new Facebook\Facebook([ 'app_id' => 'your-app-id', 'app_secret' => 'your-app-secret', 'default_graph_version' => 'v3.1', ]); $fqb = new SammyK\FacebookQueryBuilder\FQB; $fb->setDefaultAccessToken('my-access-token'); $request = $fqb->node('me')->fields(['id', 'name', 'email']); echo $request->asEndpoint(); # /me?fields=id,name,email try { $response = $fb->get($request->asEndpoint()); } catch (Facebook\Exceptions\FacebookSDKException $e) { echo $e->getMessage(); exit; } var_dump($response->getDecodedBody());
您会发现我们正在使用asEndpoint()
方法将生成的请求发送到SDK。这是因为SDK会自动将Graph API主机名添加到URL前缀。asEndpoint()
方法将返回不带前缀的URL版本。
官方的Facebook PHP SDK会自动将Graph API版本、应用密钥证明和访问令牌添加到URL中,因此您不需要在FQB
对象上设置这些选项。
使用原生PHP进行请求
正如您在上面的基本示例中看到的,您可以使用PHP灵活的file_get_contents()
将请求发送到Graph API。只需确保使用default_graph_version
设置您的Graph API版本前缀,并使用app_secret
设置您的应用密钥,以确保所有请求都使用应用密钥证明签名。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'default_graph_version' => 'v3.1', 'app_secret' => 'your-app-secret', ]); // Grab Mark Zuckerberg's public info $request = $fqb->node('4')->accessToken('my-access-token'); echo $request; # https://graph.facebook.com/v3.1/4?access_token=my-access-token&appsecret_proof=2ad43b865030f51531ac36bb00ce4f59d9f879ecce31b0977dbfd73fa4eca7b6 $response = file_get_contents((string) $request); var_dump($response);
有关处理响应的更多信息,请参阅下文的使用原生PHP处理响应。
获取访问令牌
由于Facebook查询构建器是专门用于构建嵌套请求语法的,因此它不能直接用于获取访问令牌。
Facebook登录过程在后台使用OAuth 2.0。因此,您可以使用任何OAuth 2.0客户端库从Facebook获取用户访问令牌。以下是一些建议:
- 官方的Facebook PHP SDK v5 (推荐)
- PHP League的OAuth 2.0客户端和相应的Facebook提供者
- Laravel 5的Socialite库
配置设置
可以通过FQB
构造函数设置多个配置设置。
default_access_token
选项允许您为所有生成的查询定义一个默认的回退访问令牌。default_graph_version
选项允许您为所有生成的查询定义默认的Graph API版本 URL前缀。- Graph API 的重要安全层是应用密钥证明,该功能应在您的应用程序中默认启用。您可以通过设置
app_secret
选项使用您的应用程序密钥来对Facebook查询构建器生成的每个请求进行签名。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'default_access_token' => 'your-access-token', 'default_graph_version' => 'v3.1', 'app_secret' => 'your-app-secret', ]);
设置访问令牌
如果您正在使用Facebook PHP SDK并已为SDK设置了默认访问令牌,那么您无需担心将访问令牌附加到请求中。
如果您使用其他HTTP客户端,您可以在使用default_access_token
选项实例化FQB
服务时设置默认回退访问令牌,或者您可以使用accessToken()
方法将访问令牌附加到嵌套请求中。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'default_access_token' => 'fallback_access_token', ]); $request = $fqb->node('me'); echo $request->asEndpoint(); # /me?access_token=fallback_access_token $request = $fqb->node('me')->accessToken('bar_token'); echo $request->asEndpoint(); # /me?access_token=bar_token
设置Graph版本
设置您想要使用的Graph API版本非常重要,因为Graph API受到破坏性更改计划的影响。
如果您正在使用Facebook PHP SDK并已为SDK设置了默认的Graph版本,那么您无需担心在Facebook查询构建器中设置Graph版本。
如果您使用其他HTTP客户端,您可以在使用default_graph_version
选项实例化FQB
服务时设置默认回退Graph版本,或者您可以使用graphVersion()
方法按请求设置它。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'default_graph_version' => 'v3.1', ]); $request = $fqb->node('me'); echo $request->asEndpoint(); # /v3.1/me $request = $fqb->node('me')->graphVersion('v1.0'); echo $request->asEndpoint(); # /v1.0/me
PS:Graph v1.0已停用。💀
启用应用密钥证明
作为额外的安全功能,您可以使用应用密钥证明对Graph API的每个请求进行签名。强烈建议您编辑您的应用程序设置,要求所有请求都需要应用密钥证明。
如果您使用Facebook PHP SDK向Graph API发送请求,它将自动为您附加应用密钥证明。
如果您使用其他HTTP客户端,如果请求中设置了访问令牌和应用密钥,则将生成应用密钥证明。您可以在使用app_secret
选项实例化FQB
服务时设置应用密钥。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'app_secret' => 'foo_secret', ]); $request = $fqb->node('me')->accessToken('bar_token'); echo $request->asEndpoint(); # /me?access_token=bar_token&appsecret_proof=2ceec40b7b9fd7d38fff1767b766bcc6b1f9feb378febac4612c156e6a8354bd
启用Graph的测试版
在Graph API的更改推出到生产之前,它们首先部署到测试层。
默认情况下,当您生成嵌套请求时,它将以Graph API的生产主机名https://graph.facebook.com/开头。
echo (string) $fqb->node('4'); # https://graph.facebook.com/4
要为由FQB
生成的请求启用测试层,请设置enable_beta_mode
选项为true
。启用后,所有生成的URL都将以Graph API的测试版主机名https://graph.beta.facebook.com/开头。
$fqb = new SammyK\FacebookQueryBuilder\FQB([ 'enable_beta_mode' => true, ]); echo (string) $fqb->node('4'); # https://graph.beta.facebook.com/4
方法参考
node()
node(string $graphNodeName): FQB
返回一个新的可变FQB
实体实例。任何有效的Graph节点或端点都可以传递给node()
。
$userNode = $fqb->node('me');
edge()
node(string $edgeName): GraphEdge
返回一个可变的GraphEdge
实体实例,以便传递给FQB::fields()
方法。
$photosEdge = $fqb->edge('photos');
fields()
fields(mixed $fieldNameOrEdge[, mixed $fieldNameOrEdge[, ...]]): FQB
为GraphNode
或GraphEdge
实体设置字段和边。字段和边可以作为参数数组或列表传递。
$edge = $fqb->edge('some_edge')->fields(['field_one', 'field_two']); $node = $fqb->node('some_node')->fields('my_field', 'my_other_field', $edge);
modifiers()
modifiers(array $modifiers): FQB
一些Graph API端点支持名为“修饰符”的额外参数。
支持修饰符的示例端点是/{object-id}/comments
边。
// Order the comments in chronological order $commentsEdge = $fqb->edge('comments')->modifiers(['filter' => 'stream']); $request = $fqb->node('1044180305609983')->fields('name', $commentsEdge);
limit()
limit(int $numberOfResultsToReturn): FQB
您可以使用limit()
方法指定Graph API应从一个边返回的结果数量。
$edge = $fqb->edge('photos')->limit(7);
由于“limit”功能只是Graph API中的一个修饰符,因此limit()
方法是将limit
参数发送到modifiers()
方法的便捷方法。因此,相同的功能可以表示为
$edge = $fqb->edge('photos')->modifiers(['limit' => 7]);
accessToken()
accessToken(string $accessToken): FQB
您可以使用accessToken()
方法为特定请求设置访问令牌。
$request = $fqb->node('BradfordWhelanPhotography')->accessToken('foo-token'); echo $request->asEndpoint(); # /BradfordWhelanPhotography?access_token=foo-token
graphVersion()
graphVersion(string $graphApiVersion): FQB
您可以使用graphVersion()
方法为特定请求设置Graph版本URL前缀。
$request = $fqb->node('me')->graphVersion('v3.1'); echo $request->asEndpoint(); # /v3.1/me
asUrl()
asUrl(): string
您可以使用asUrl()
方法获取生成的请求作为完整URL。
$request = $fqb->node('me'); echo $request->asUrl(); # https://graph.facebook.com/me
魔法方法__toString()
是asUrl()
方法的别名,因此将FQB
实例转换为字符串将执行相同操作。
$request = $fqb->node('me'); echo (string) $request; # https://graph.facebook.com/me
asEndpoint()
asEndpoint(): string
asEndpoint()
与asUrl()
方法相同,但返回的URL不带Graph API主机名前缀。
$request = $fqb->node('me'); echo $request->asEndpoint(); # /me
这对于与官方Facebook PHP SDK一起使用尤其方便,因为SDK会自动将Graph主机名前缀添加到URL中。
处理响应
响应将取决于您使用哪个HTTP客户端与Graph API接口。
使用Facebook PHP SDK的响应
get()
、post()
和delete()
方法的所有响应都返回来自原生Facebook PHP SDK的Facebook\FacebookResponse
。
$fb = new Facebook\Facebook([/* . . . */]); $fqb = new SammyK\FacebookQueryBuilder\FQB([/* . . . */]); $fb->setDefaultAccessToken('my-access-token'); $request = $fqb->node('me')->fields(['email', 'photos']); echo $request->asEndpoint(); # /me?fields=email,photos try { $response = $fb->get($request->asEndpoint()); } catch (Facebook\Exceptions\FacebookSDKException $e) { echo $e->getMessage(); exit; } $userNode = $response->getGraphUser(); // Access properties like an array $email = $userNode['email']; // Get data as array $userNodeAsArray = $userNode->asArray(); // Get data as JSON string $userNodeAsJson = $userNode->asJson(); // Iterate over the /photos edge foreach ($userNode['photos'] as $photo) { // . . . } // Morph the data with a closure $userNode['photos']->each(function ($value) { $value->new_height = $value->height + 22; });
有关FacebookResponse
实体的更多信息,请参阅官方文档FacebookResponse
entity。
使用原生PHP的响应
如果您使用file_get_contents()
将请求发送到Graph API,响应将是从Graph API返回的JSON字符串。您可以将JSON响应解码为普通的PHP数组。
$fqb = new SammyK\FacebookQueryBuilder\FQB([/* . . . */]); $request = $fqb->node('4') ->fields(['id', 'name']) ->accessToken('user-access-token'); echo $request; # https://graph.facebook.com/4?access_token=user-access-token&fields=id,name $response = file_get_contents((string) $request); $data = json_decode($response, true); var_dump($data); # array(2) { ["id"]=> string(1) "4" ["name"]=> string(15) "Mark Zuckerberg" }
如果Graph API返回了错误响应,file_get_contents()
将返回false
并引发警告。这可能会引起问题,因为Graph API在响应体中返回错误详情。
要获取错误响应数据,我们需要告诉file_get_contents()
在响应包含错误HTTP状态代码时也返回响应体。我们可以通过将ignore_errors
设置为true
使用stream_context_create()
函数来做到这一点。
您还可以通过检查响应头来进一步调查错误响应。头存储在$http_response_header
变量中,该变量由file_get_contents()
自动设置。
$fqb = new SammyK\FacebookQueryBuilder\FQB([/* . . . */]); $request = $fqb->node('Some-Invalid-Node')->accessToken('user-access-token'); echo $request; # https://graph.facebook.com/Some-Invalid-Node?access_token=user-access-token $context = stream_context_create(['http' => ['ignore_errors' => true]]); $response = file_get_contents((string) $request, null, $context); $data = json_decode($response, true); var_dump($data); /* array(1) { ["error"]=> array(4) { ["message"]=> string(27) "Invalid OAuth access token." ["type"]=> string(14) "OAuthException" ["code"]=> int(190) ["fbtrace_id"]=> string(11) "A8oB9BtqtZ4" } } */ var_dump($http_response_header); /* array(14) { [0]=> string(24) "HTTP/1.1 400 Bad Request" [1]=> string(89) "WWW-Authenticate: OAuth "Facebook Platform" "invalid_token" "Invalid OAuth access token."" [2]=> string(30) "Access-Control-Allow-Origin: *" [3]=> string(44) "Content-Type: text/javascript; charset=UTF-8" [4]=> string(26) "X-FB-Trace-ID: h8oB7BtrtZ3" [5]=> string(17) "X-FB-Rev: 4971439" [6]=> string(16) "Pragma: no-cache" [7]=> string(23) "Cache-Control: no-store" [8]=> string(38) "Expires: Sat, 01 Jan 2000 00:00:00 GMT" [9]=> string(21) "Vary: Accept-Encoding" [10]=> string(100) "X-FB-Debug: FOOE54KJadh9P2HOSUlSFQmNEEf/9CF4ZtgZQ==" [11]=> string(35) "Date: Fri, 09 Oct 2015 04:43:44 GMT" [12]=> string(17) "Connection: close" [13]=> string(19) "Content-Length: 113" } */
测试
只需从这个项目根目录运行phpunit
。
$ ./vendor/bin/phpunit
贡献
有关详细信息,请参阅CONTRIBUTING。
更改日志
请参阅CHANGELOG以获取历史记录。
鸣谢
许可
MIT许可(MIT)。有关更多信息,请参阅许可文件。
安全
如果您在这个库中发现安全漏洞,请私下通知项目维护者以解决问题。