server/src/app/Libraries/Sparql/GuzzleSparqlClient.php
changeset 531 48f5380c26d0
child 537 d2e6ee099125
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/server/src/app/Libraries/Sparql/GuzzleSparqlClient.php	Fri Jun 09 15:22:02 2017 +0200
@@ -0,0 +1,284 @@
+<?php
+namespace CorpusParole\Libraries\Sparql;
+
+use EasyRdf\Sparql\Client;
+use EasyRdf\Sparql\Result;
+use EasyRdf\Exception;
+use EasyRdf\Format;
+use EasyRdf\RdfNamespace;
+
+/**
+ * Wraps a Guzzle psr7 response into a EasyRdf\Http\Response interface
+ **/
+class ResponseWrapper {
+
+    /**
+     * Constructor.
+     *
+     * @param  Response     Guzzle psr7 response
+     */
+    public function __construct($response) {
+        $this->response = $response;
+    }
+
+    /**
+     * Check whether the response in successful
+     *
+     * @return boolean
+     */
+    public function isSuccessful()
+    {
+        $status = $this->getStatus();
+        return ($status >= 200 && $status < 300);
+    }
+
+    /**
+     * Check whether the response is an error
+     *
+     * @return boolean
+     */
+    public function isError()
+    {
+        $status = $this->getStatus();
+        return ($status >= 400 && $status < 600);
+    }
+
+    /**
+     * Check whether the response is a redirection
+     *
+     * @return boolean
+     */
+    public function isRedirect()
+    {
+        $status = $this->getStatus();
+        return ($status >= 300 && $status < 400);
+    }
+
+    /**
+     * Get the HTTP response status code
+     *
+     * @return int
+     */
+    public function getStatus()
+    {
+        return $this->response->getStatusCode();
+    }
+
+    /**
+     * Return a message describing the HTTP response code
+     * (Eg. "OK", "Not Found", "Moved Permanently")
+     *
+     * @return string
+     */
+    public function getMessage()
+    {
+        return $this->response->getReasonPhrase();
+    }
+
+    /**
+     * Get the response body as string
+     *
+     * @return string
+     */
+    public function getBody()
+    {
+        return $this->response->getBody();
+    }
+
+    /**
+     * Get the raw response body (as transfered "on wire") as string
+     *
+     * If the body is encoded (with Transfer-Encoding, not content-encoding -
+     * IE "chunked" body), gzip compressed, etc. it will not be decoded.
+     *
+     * @return string
+     */
+    public function getRawBody()
+    {
+        return $this->getBody();
+    }
+
+    /**
+     * Get the HTTP version of the response
+     *
+     * @return string
+     */
+    public function getVersion()
+    {
+        return $this->response->getProtocolVersion();
+    }
+
+    /**
+     * Get the response headers
+     *
+     * @return array
+     */
+    public function getHeaders()
+    {
+        return $this->response->getHeaders();
+    }
+
+    /**
+     * Get a specific header as string, or null if it is not set
+     *
+     * @param string$header
+     *
+     * @return string|array|null
+     */
+    public function getHeader($header)
+    {
+        $header = $this->response->getHeader($header);
+        if(is_array($header) && count($header) == 1) {
+            return $header[0];
+        } else {
+            return $header;
+        }
+    }
+
+    /**
+     * Get all headers as string
+     *
+     * @param boolean $statusLine Whether to return the first status line (ie "HTTP 200 OK")
+     * @param string  $br         Line breaks (eg. "\n", "\r\n", "<br />")
+     *
+     * @return string
+     */
+    public function getHeadersAsString($statusLine = true, $br = "\n")
+    {
+        $str = '';
+
+        if ($statusLine) {
+            $version = $this->getVersion();
+            $status = $this->getStatus();
+            $message = $this->getMessage();
+            $str = "HTTP/{$version} {$status} {$message}{$br}";
+        }
+
+        // Iterate over the headers and stringify them
+        foreach ($this->getHeaders() as $name => $value) {
+            if (is_string($value)) {
+                $str .= "{$name}: {$value}{$br}";
+            } elseif (is_array($value)) {
+                foreach ($value as $subval) {
+                    $str .= "{$name}: {$subval}{$br}";
+                }
+            }
+        }
+
+        return $str;
+    }
+
+    /**
+     * Get the entire response as string
+     *
+     * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
+     *
+     * @return string
+     */
+    public function asString($br = "\n")
+    {
+        return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody();
+    }
+
+    /**
+     * Implements magic __toString()
+     *
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->asString();
+    }
+}
+
+class GuzzleSparqlClient extends Client {
+
+    public function __construct($httpClient, $queryUri, $updateUri = null) {
+        parent::__construct($queryUri, $updateUri);
+        $this->httpClient = $httpClient;
+    }
+
+    private function queryUriHasParams() {
+        return strlen(parse_url($this->getQueryUri(), PHP_URL_QUERY)) > 0;
+    }
+
+    /**
+     * Build http-client object, execute request and return a response
+     *
+     * @param string $processed_query
+     * @param string $type            Should be either "query" or "update"
+     *
+     * @return Http\Response|\Zend\Http\Response
+     * @throws Exception
+     */
+    protected function executeQuery($processed_query, $type)
+    {
+        // Tell the server which response formats we can parse
+        $sparql_results_types = array(
+            'application/sparql-results+json' => 1.0,
+            'application/sparql-results+xml' => 0.8
+        );
+        $request_options = [];
+        $uri = null;
+        $method = 'GET';
+        if ($type == 'update') {
+            // accept anything, as "response body of a […] update request is implementation defined"
+            // @see http://www.w3.org/TR/sparql11-protocol/#update-success
+            $accept = Format::getHttpAcceptHeader($sparql_results_types);
+            $request_options['headers'] = [
+                'Accept' => $accept,
+                'Content-Type' => 'application/sparql-update'
+            ];
+            $uri = $this->getUpdateUri();
+            $method = 'POST';
+            $request_options['body'] = $processed_query;
+        } elseif ($type == 'query') {
+            $re = '(?:(?:\s*BASE\s*<.*?>\s*)|(?:\s*PREFIX\s+.+:\s*<.*?>\s*))*'.
+                '(CONSTRUCT|SELECT|ASK|DESCRIBE)[\W]';
+            $result = null;
+            $matched = mb_eregi($re, $processed_query, $result);
+            if (false === $matched or count($result) !== 2) {
+                // non-standard query. is this something non-standard?
+                $query_verb = null;
+            } else {
+                $query_verb = strtoupper($result[1]);
+            }
+            if ($query_verb === 'SELECT' or $query_verb === 'ASK') {
+                // only "results"
+                $accept = Format::formatAcceptHeader($sparql_results_types);
+            } elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') {
+                // only "graph"
+                $accept = Format::getHttpAcceptHeader();
+            } else {
+                // both
+                $accept = Format::getHttpAcceptHeader($sparql_results_types);
+            }
+            $encodedQuery = 'query=' . urlencode($processed_query);
+            // Use GET if the query is less than 2kB
+            // 2046 = 2kB minus 1 for '?' and 1 for NULL-terminated string on server
+            if (strlen($encodedQuery) + strlen($this->getQueryUri()) <= 2046) {
+                $delimiter = $this->queryUriHasParams() ? '&' : '?';
+                $request_options['headers'] = [
+                    'Accept' => $accept,
+                ];
+                $method = 'GET';
+                $uri = $this->getQueryUri() . $delimiter . $encodedQuery;
+            } else {
+                $request_options['headers'] = [
+                    'Accept' => $accept,
+                    'Content-Type' => 'application/x-www-form-urlencoded'
+                ];
+                // Fall back to POST instead (which is un-cacheable)
+                $method = 'POST';
+                $uri = $this->getQueryUri();
+                $request_options['body'] = $encodedQuery;
+            }
+        } else {
+            throw new Exception('unexpected request-type: '.$type);
+        }
+        $response = $this->httpClient->request($method, $uri, $request_options);
+        return new ResponseWrapper($response);
+    }
+
+}
+