Skip to content

Instantly share code, notes, and snippets.

@titpetric
Created April 11, 2017 08:47
Show Gist options
  • Select an option

  • Save titpetric/e1414880099e0e5a90d87d56736bd87f to your computer and use it in GitHub Desktop.

Select an option

Save titpetric/e1414880099e0e5a90d87d56736bd87f to your computer and use it in GitHub Desktop.
Transform GraphQL fields into JSON to use them for filtering native PHP arrays
<?php
/** A poor mans GraphQL fields parser
*
* Try to convert graphQL fields to JSON and then just use json_decode to produce an array.
*/
class Fields
{
/** Parse GraphQL fields into an array */
public static function parse($query)
{
$query = str_replace("{", "{\n", $query);
$query = str_replace("}", "\n}\n", $query);
$query = array_map("trim", explode("\n", $query));
foreach ($query as $k => $line) {
// strip comments
$line = explode("#", $line);
$line = $line[0];
// skip opening or closing tags
if ($line === "{" || $line === "") {
continue;
}
// declare as object value
if (strpos($line, "{") !== false) {
$name = trim(str_replace("{", "", $line));
$query[$k] = '"' . $name . '": {';
continue;
}
if (strpos($line, "}") !== false) {
$query[$k] .= ',';
continue;
}
$query[$k] = '"' . $line . '": true,';
}
$query = implode("", $query);
// cut last comma
$query = substr($query, 0, -1);
// cut trailing commas
$query = str_replace(",}", "}", $query);
// produce php array
$retval = json_decode($query, true);
if (is_null($retval)) {
throw new \Exception(sprintf("Error when parsing GraphQL fields: '%s'", $query));
}
return $retval;
}
/** Filter a PHP array with the GraphQL fields provided by parse() */
public static function execute($query, $data)
{
$filter = self::parse($query);
$result = array_intersect_key_recursive($data, $filter);
return $result;
}
}
function array_intersect_key_recursive($arr, $filter)
{
$is_int = true;
foreach ($arr as $k => $v) {
if (!is_int($k) || !is_array($v)) {
$is_int = false;
break;
}
}
if ($is_int) {
foreach ($arr as $k => $v) {
$arr[$k] = array_intersect_key_recursive($v, $filter);
}
return $arr;
}
$retval = array();
foreach ($filter as $key => $value) {
if (!isset($arr[$key])) {
continue;
}
if (is_array($value)) {
$retval[$key] = array_intersect_key_recursive($arr[$key], $value);
continue;
}
$retval[$key] = $arr[$key];
}
return $retval;
}
@titpetric

Copy link
Copy Markdown
Author

The above piece of code parses a very small subset of GraphQL, dealing with fields, for example:

{
    name
    id
    #comment
    value {
        name
        id
    }
}

This will be naively converted to JSON, and parsed into a PHP array:

array (
  'name' => true,
  'id' => true,
  'value' =>
  array (
    'name' => true,
    'id' => true,
  ),
)

The array_intersect_key_recursive function is the glue that allows to use the above notation to filter possible untyped/non-object API responses that you may produce with your existing API. To use it to filter your API responses might be as simple as:

if (isset($_GET['graphql_query'])) {
    $response = Fields::execute($_GET['graphql_query'], $response);
}

The code is PHP 5.3+ compatible (might work on even older PHP versions). As said, it only supports a narrow subset of GraphQL field queries, doesn't implement any kind of AST parser, no references, objects, and many other things. It's usable only as a minimal filter that will reduce your API responses to fields which you define with GraphQL.

@jorge-wheresweed

Copy link
Copy Markdown

when calling $response = Fields::execute($_GET['graphql_query'], $response); what its response?

@titpetric

titpetric commented Jan 28, 2022 via email

Copy link
Copy Markdown
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment