Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.67% covered (success)
96.67%
29 / 30
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
Communicator
96.67% covered (success)
96.67%
29 / 30
75.00% covered (warning)
75.00%
3 / 4
10
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 sendRequest
85.71% covered (warning)
85.71%
6 / 7
0.00% covered (danger)
0.00%
0 / 1
2.01
 getOaiResponse
100.00% covered (success)
100.00%
17 / 17
100.00% covered (success)
100.00%
1 / 1
5
 request
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3/**
4 * OAI-PMH Communicator (handles low-level request/response processing).
5 *
6 * PHP version 7
7 *
8 * Copyright (c) Demian Katz 2016.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Harvest_Tools
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/indexing:oai-pmh Wiki
28 */
29
30namespace VuFindHarvest\OaiPmh;
31
32use Laminas\Http\Client;
33use Laminas\Uri\Http;
34use VuFindHarvest\ConsoleOutput\WriterAwareTrait;
35use VuFindHarvest\ResponseProcessor\ResponseProcessorInterface;
36
37use function is_object;
38
39/**
40 * OAI-PMH Communicator (handles low-level request/response processing).
41 *
42 * @category VuFind
43 * @package  Harvest_Tools
44 * @author   Demian Katz <demian.katz@villanova.edu>
45 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
46 * @link     https://vufind.org/wiki/indexing:oai-pmh Wiki
47 */
48class Communicator
49{
50    use WriterAwareTrait;
51
52    /**
53     * HTTP client
54     *
55     * @var Client
56     */
57    protected $client;
58
59    /**
60     * URL to harvest from
61     *
62     * @var string
63     */
64    protected $baseUrl;
65
66    /**
67     * Response processor
68     *
69     * @var ResponseProcessorInterface
70     */
71    protected $responseProcessor;
72
73    /**
74     * Constructor
75     *
76     * @param string                     $uri       Base URI for OAI-PMH server
77     * @param Client                     $client    HTTP client
78     * @param ResponseProcessorInterface $processor Response processor (optional)
79     */
80    public function __construct(
81        $uri,
82        Client $client,
83        ResponseProcessorInterface $processor = null
84    ) {
85        $this->baseUrl = $uri;
86        $this->client = $client;
87        $this->responseProcessor = $processor;
88    }
89
90    /**
91     * Perform a single OAI-PMH request.
92     *
93     * @param string $verb   OAI-PMH verb to execute.
94     * @param array  $params GET parameters for ListRecords method.
95     *
96     * @return string
97     */
98    protected function sendRequest($verb, $params)
99    {
100        // Set up the request:
101        $this->client->resetParameters(false, false); // keep cookies/auth
102        $this->client->setUri($this->baseUrl);
103
104        // Load request parameters:
105        $query = $this->client->getRequest()->getQuery();
106        $query->set('verb', $verb);
107        foreach ($params as $key => $value) {
108            $query->set($key, $value);
109        }
110
111        // Perform request:
112        return $this->client->setMethod('GET')->send();
113    }
114
115    /**
116     * Make an OAI-PMH request. Throw an exception if there is an error; return
117     * an XML string on success.
118     *
119     * @param string $verb   OAI-PMH verb to execute.
120     * @param array  $params GET parameters for ListRecords method.
121     *
122     * @return string
123     */
124    protected function getOaiResponse($verb, $params)
125    {
126        // Debug:
127        $this->write(
128            "Sending request: verb = {$verb}, params = " . print_r($params, true)
129        );
130
131        // Set up retry loop:
132        do {
133            $result = $this->sendRequest($verb, $params);
134            if ($result->getStatusCode() == 503) {
135                $delayHeader = $result->getHeaders()->get('Retry-After');
136                $delay = is_object($delayHeader)
137                    ? $delayHeader->getDeltaSeconds() : 0;
138                if ($delay > 0) {
139                    $this->writeLine(
140                        "Received 503 response; waiting {$delay} seconds..."
141                    );
142                    sleep($delay);
143                }
144            } elseif (!$result->isSuccess()) {
145                throw new \Exception('HTTP Error ' . $result->getStatusCode());
146            }
147        } while ($result->getStatusCode() == 503);
148
149        // If we got this far, there was no error -- send back response.
150        return $result->getBody();
151    }
152
153    /**
154     * Make an OAI-PMH request.  Throw an exception if there is an error; return
155     * the processed response on success.
156     *
157     * @param string $verb   OAI-PMH verb to execute.
158     * @param array  $params GET parameters for ListRecords method.
159     *
160     * @return mixed
161     */
162    public function request($verb, $params = [])
163    {
164        $xml = $this->getOaiResponse($verb, $params);
165        return $this->responseProcessor
166            ? $this->responseProcessor->process($xml) : $xml;
167    }
168}