server/src/app/Libraries/Sparql/GuzzleSparqlClient.php
changeset 531 48f5380c26d0
child 537 d2e6ee099125
equal deleted inserted replaced
530:a76bae4795d5 531:48f5380c26d0
       
     1 <?php
       
     2 namespace CorpusParole\Libraries\Sparql;
       
     3 
       
     4 use EasyRdf\Sparql\Client;
       
     5 use EasyRdf\Sparql\Result;
       
     6 use EasyRdf\Exception;
       
     7 use EasyRdf\Format;
       
     8 use EasyRdf\RdfNamespace;
       
     9 
       
    10 /**
       
    11  * Wraps a Guzzle psr7 response into a EasyRdf\Http\Response interface
       
    12  **/
       
    13 class ResponseWrapper {
       
    14 
       
    15     /**
       
    16      * Constructor.
       
    17      *
       
    18      * @param  Response     Guzzle psr7 response
       
    19      */
       
    20     public function __construct($response) {
       
    21         $this->response = $response;
       
    22     }
       
    23 
       
    24     /**
       
    25      * Check whether the response in successful
       
    26      *
       
    27      * @return boolean
       
    28      */
       
    29     public function isSuccessful()
       
    30     {
       
    31         $status = $this->getStatus();
       
    32         return ($status >= 200 && $status < 300);
       
    33     }
       
    34 
       
    35     /**
       
    36      * Check whether the response is an error
       
    37      *
       
    38      * @return boolean
       
    39      */
       
    40     public function isError()
       
    41     {
       
    42         $status = $this->getStatus();
       
    43         return ($status >= 400 && $status < 600);
       
    44     }
       
    45 
       
    46     /**
       
    47      * Check whether the response is a redirection
       
    48      *
       
    49      * @return boolean
       
    50      */
       
    51     public function isRedirect()
       
    52     {
       
    53         $status = $this->getStatus();
       
    54         return ($status >= 300 && $status < 400);
       
    55     }
       
    56 
       
    57     /**
       
    58      * Get the HTTP response status code
       
    59      *
       
    60      * @return int
       
    61      */
       
    62     public function getStatus()
       
    63     {
       
    64         return $this->response->getStatusCode();
       
    65     }
       
    66 
       
    67     /**
       
    68      * Return a message describing the HTTP response code
       
    69      * (Eg. "OK", "Not Found", "Moved Permanently")
       
    70      *
       
    71      * @return string
       
    72      */
       
    73     public function getMessage()
       
    74     {
       
    75         return $this->response->getReasonPhrase();
       
    76     }
       
    77 
       
    78     /**
       
    79      * Get the response body as string
       
    80      *
       
    81      * @return string
       
    82      */
       
    83     public function getBody()
       
    84     {
       
    85         return $this->response->getBody();
       
    86     }
       
    87 
       
    88     /**
       
    89      * Get the raw response body (as transfered "on wire") as string
       
    90      *
       
    91      * If the body is encoded (with Transfer-Encoding, not content-encoding -
       
    92      * IE "chunked" body), gzip compressed, etc. it will not be decoded.
       
    93      *
       
    94      * @return string
       
    95      */
       
    96     public function getRawBody()
       
    97     {
       
    98         return $this->getBody();
       
    99     }
       
   100 
       
   101     /**
       
   102      * Get the HTTP version of the response
       
   103      *
       
   104      * @return string
       
   105      */
       
   106     public function getVersion()
       
   107     {
       
   108         return $this->response->getProtocolVersion();
       
   109     }
       
   110 
       
   111     /**
       
   112      * Get the response headers
       
   113      *
       
   114      * @return array
       
   115      */
       
   116     public function getHeaders()
       
   117     {
       
   118         return $this->response->getHeaders();
       
   119     }
       
   120 
       
   121     /**
       
   122      * Get a specific header as string, or null if it is not set
       
   123      *
       
   124      * @param string$header
       
   125      *
       
   126      * @return string|array|null
       
   127      */
       
   128     public function getHeader($header)
       
   129     {
       
   130         $header = $this->response->getHeader($header);
       
   131         if(is_array($header) && count($header) == 1) {
       
   132             return $header[0];
       
   133         } else {
       
   134             return $header;
       
   135         }
       
   136     }
       
   137 
       
   138     /**
       
   139      * Get all headers as string
       
   140      *
       
   141      * @param boolean $statusLine Whether to return the first status line (ie "HTTP 200 OK")
       
   142      * @param string  $br         Line breaks (eg. "\n", "\r\n", "<br />")
       
   143      *
       
   144      * @return string
       
   145      */
       
   146     public function getHeadersAsString($statusLine = true, $br = "\n")
       
   147     {
       
   148         $str = '';
       
   149 
       
   150         if ($statusLine) {
       
   151             $version = $this->getVersion();
       
   152             $status = $this->getStatus();
       
   153             $message = $this->getMessage();
       
   154             $str = "HTTP/{$version} {$status} {$message}{$br}";
       
   155         }
       
   156 
       
   157         // Iterate over the headers and stringify them
       
   158         foreach ($this->getHeaders() as $name => $value) {
       
   159             if (is_string($value)) {
       
   160                 $str .= "{$name}: {$value}{$br}";
       
   161             } elseif (is_array($value)) {
       
   162                 foreach ($value as $subval) {
       
   163                     $str .= "{$name}: {$subval}{$br}";
       
   164                 }
       
   165             }
       
   166         }
       
   167 
       
   168         return $str;
       
   169     }
       
   170 
       
   171     /**
       
   172      * Get the entire response as string
       
   173      *
       
   174      * @param string $br Line breaks (eg. "\n", "\r\n", "<br />")
       
   175      *
       
   176      * @return string
       
   177      */
       
   178     public function asString($br = "\n")
       
   179     {
       
   180         return $this->getHeadersAsString(true, $br) . $br . $this->getRawBody();
       
   181     }
       
   182 
       
   183     /**
       
   184      * Implements magic __toString()
       
   185      *
       
   186      * @return string
       
   187      */
       
   188     public function __toString()
       
   189     {
       
   190         return $this->asString();
       
   191     }
       
   192 }
       
   193 
       
   194 class GuzzleSparqlClient extends Client {
       
   195 
       
   196     public function __construct($httpClient, $queryUri, $updateUri = null) {
       
   197         parent::__construct($queryUri, $updateUri);
       
   198         $this->httpClient = $httpClient;
       
   199     }
       
   200 
       
   201     private function queryUriHasParams() {
       
   202         return strlen(parse_url($this->getQueryUri(), PHP_URL_QUERY)) > 0;
       
   203     }
       
   204 
       
   205     /**
       
   206      * Build http-client object, execute request and return a response
       
   207      *
       
   208      * @param string $processed_query
       
   209      * @param string $type            Should be either "query" or "update"
       
   210      *
       
   211      * @return Http\Response|\Zend\Http\Response
       
   212      * @throws Exception
       
   213      */
       
   214     protected function executeQuery($processed_query, $type)
       
   215     {
       
   216         // Tell the server which response formats we can parse
       
   217         $sparql_results_types = array(
       
   218             'application/sparql-results+json' => 1.0,
       
   219             'application/sparql-results+xml' => 0.8
       
   220         );
       
   221         $request_options = [];
       
   222         $uri = null;
       
   223         $method = 'GET';
       
   224         if ($type == 'update') {
       
   225             // accept anything, as "response body of a […] update request is implementation defined"
       
   226             // @see http://www.w3.org/TR/sparql11-protocol/#update-success
       
   227             $accept = Format::getHttpAcceptHeader($sparql_results_types);
       
   228             $request_options['headers'] = [
       
   229                 'Accept' => $accept,
       
   230                 'Content-Type' => 'application/sparql-update'
       
   231             ];
       
   232             $uri = $this->getUpdateUri();
       
   233             $method = 'POST';
       
   234             $request_options['body'] = $processed_query;
       
   235         } elseif ($type == 'query') {
       
   236             $re = '(?:(?:\s*BASE\s*<.*?>\s*)|(?:\s*PREFIX\s+.+:\s*<.*?>\s*))*'.
       
   237                 '(CONSTRUCT|SELECT|ASK|DESCRIBE)[\W]';
       
   238             $result = null;
       
   239             $matched = mb_eregi($re, $processed_query, $result);
       
   240             if (false === $matched or count($result) !== 2) {
       
   241                 // non-standard query. is this something non-standard?
       
   242                 $query_verb = null;
       
   243             } else {
       
   244                 $query_verb = strtoupper($result[1]);
       
   245             }
       
   246             if ($query_verb === 'SELECT' or $query_verb === 'ASK') {
       
   247                 // only "results"
       
   248                 $accept = Format::formatAcceptHeader($sparql_results_types);
       
   249             } elseif ($query_verb === 'CONSTRUCT' or $query_verb === 'DESCRIBE') {
       
   250                 // only "graph"
       
   251                 $accept = Format::getHttpAcceptHeader();
       
   252             } else {
       
   253                 // both
       
   254                 $accept = Format::getHttpAcceptHeader($sparql_results_types);
       
   255             }
       
   256             $encodedQuery = 'query=' . urlencode($processed_query);
       
   257             // Use GET if the query is less than 2kB
       
   258             // 2046 = 2kB minus 1 for '?' and 1 for NULL-terminated string on server
       
   259             if (strlen($encodedQuery) + strlen($this->getQueryUri()) <= 2046) {
       
   260                 $delimiter = $this->queryUriHasParams() ? '&' : '?';
       
   261                 $request_options['headers'] = [
       
   262                     'Accept' => $accept,
       
   263                 ];
       
   264                 $method = 'GET';
       
   265                 $uri = $this->getQueryUri() . $delimiter . $encodedQuery;
       
   266             } else {
       
   267                 $request_options['headers'] = [
       
   268                     'Accept' => $accept,
       
   269                     'Content-Type' => 'application/x-www-form-urlencoded'
       
   270                 ];
       
   271                 // Fall back to POST instead (which is un-cacheable)
       
   272                 $method = 'POST';
       
   273                 $uri = $this->getQueryUri();
       
   274                 $request_options['body'] = $encodedQuery;
       
   275             }
       
   276         } else {
       
   277             throw new Exception('unexpected request-type: '.$type);
       
   278         }
       
   279         $response = $this->httpClient->request($method, $uri, $request_options);
       
   280         return new ResponseWrapper($response);
       
   281     }
       
   282 
       
   283 }
       
   284