1: <?php
2:
3: namespace Scopus;
4:
5: use Exception;
6: use GuzzleHttp\Client;
7: use Scopus\Exception\JsonException;
8: use Scopus\Exception\XmlException;
9: use Scopus\Response\Abstracts;
10: use Scopus\Response\Author;
11: use Scopus\Response\SearchResults;
12: use Scopus\Util\XmlUtil;
13:
14: class ScopusApi
15: {
16: const SEARCH_URI = 'https://api.elsevier.com/content/search/scopus';
17: const ABSTRACT_URI = 'https://api.elsevier.com/content/abstract/scopus_id/';
18: const AUTHOR_URI = 'https://api.elsevier.com/content/author/author_id/';
19: const AFFILIATION_URI = 'https://api.elsevier.com/content/affiliation/affiliation_id/';
20: const TIMEOUT = 30.0;
21:
22: protected $apiKey;
23:
24: 25: 26: 27: 28:
29: public function __construct($apiKey, $timeout = self::TIMEOUT)
30: {
31: $this->apiKey = $apiKey;
32: $this->client = new Client([
33: 'timeout' => $timeout,
34: 'headers' => [
35: 'Accept' => 'application/json',
36: ],
37: ]);
38: }
39:
40: 41: 42:
43: public function getApiKey()
44: {
45: return $this->apiKey;
46: }
47:
48: 49: 50:
51: public function setApiKey($apiKey)
52: {
53: $this->apiKey = $apiKey;
54: }
55:
56: 57: 58:
59: public function query($query)
60: {
61: return new SearchQuery($this, $query);
62: }
63:
64: 65: 66: 67: 68: 69:
70: public function retrieve($uri, array $options = [])
71: {
72: if (!isset($options['query']['apiKey']) && $this->apiKey) {
73: $options['query']['apiKey'] = $this->apiKey;
74: }
75:
76: $response = $this->client->get($uri, $options);
77:
78: if ($response->getStatusCode() === 200) {
79: $body = $response->getBody();
80: $contentType = $response->getHeader('Content-Type');
81: if ($contentType && strpos(strtolower($contentType[0]), '/xml') !== false) {
82: $xml = simplexml_load_string($body, "SimpleXMLElement", LIBXML_NOCDATA);
83: if ($xml === false) {
84: $error = libxml_get_last_error();
85: throw new XmlException(sprintf('Xml response could not be parsed "%s" (%d) for %s', $error->message, $error->code, $uri), $error->code);
86: }
87: $body = json_encode(XmlUtil::toArray($xml));
88: }
89: $json = json_decode($body, true);
90: if (!is_array($json)) {
91: $message = json_last_error_msg();
92: $error = json_last_error();
93: throw new JsonException(sprintf('Json response could not be decoded "%s" (%d) for "%s"', $message, $error, $uri), $error);
94: }
95: $type = key($json);
96: switch ($type) {
97: case 'search-results':
98: return new SearchResults($json['search-results']);
99: case 'abstracts-retrieval-response':
100: return new Abstracts($json['abstracts-retrieval-response']);
101: case 'abstracts-retrieval-multidoc-response':
102: return array_map(function($data) {
103: return new Abstracts($data);
104: }, $json['abstracts-retrieval-multidoc-response']['abstracts-retrieval-response']);
105: case 'author-retrieval-response':
106: return new Author($json['author-retrieval-response'][0]);
107: case 'author-retrieval-response-list':
108: return array_map(function($data) {
109: if ($data['@status'] === 'found') {
110: return new Author($data);
111: }
112: }, $json['author-retrieval-response-list']['author-retrieval-response']);
113: default:
114: throw new Exception(sprintf('Unsupported response type: "%s" for "%s"', $type, $uri));
115: }
116: }
117: }
118:
119: 120: 121: 122:
123: public function search(array $query)
124: {
125: return $this->retrieve(self::SEARCH_URI, [
126: 'query' => $query,
127: ]);
128: }
129:
130: 131: 132: 133: 134: 135:
136: public function retrieveAbstract($scopusId, array $options = [])
137: {
138: if (is_array($scopusId)) {
139: $scopusId = implode(',', $scopusId);
140: }
141: if (count(explode(',', $scopusId)) > 25) {
142: throw new Exception("The maximum number of 25 abstract id's exceeded!");
143: }
144: return $this->retrieve(self::ABSTRACT_URI . $scopusId, $options);
145: }
146:
147: 148: 149: 150: 151:
152: public function retrieveAbstracts($scopusIds, array $options = [])
153: {
154: $scopusIds = array_unique($scopusIds);
155: if (count($scopusIds) > 1) {
156: $chunks = array_chunk($scopusIds, 25);
157: $abstracts = [];
158: foreach ($chunks as $chunk) {
159: $abstracts = array_merge($abstracts, array_combine($chunk, $this->retrieveAbstract($chunk, $options)));
160: }
161: return $abstracts;
162: }
163: else {
164: try {
165: return [
166: $scopusIds[0] => $this->retrieveAbstract($scopusIds[0], $options),
167: ];
168: }
169: catch (Exception $e) {
170:
171: }
172: }
173: }
174:
175: 176: 177: 178: 179: 180:
181: public function retrieveAuthor($authorId, array $options = [])
182: {
183: if (is_array($authorId)) {
184: $authorId = implode(',', $authorId);
185: }
186: if (count(explode(',', $authorId)) > 25) {
187: throw new Exception("The maximum number of 25 author id's exceeded!");
188: }
189: return $this->retrieve(self::AUTHOR_URI . $authorId, $options);
190: }
191:
192: 193: 194: 195: 196:
197: public function retrieveAuthors($authorIds, array $options = [])
198: {
199: $scopusIds = array_unique($authorIds);
200: if (count($scopusIds) > 1) {
201: $chunks = array_chunk($authorIds, 25);
202: $authors = [];
203: foreach ($chunks as $chunk) {
204: $authors = array_merge($authors, array_combine($chunk, $this->retrieveAuthor($chunk, $options)));
205: }
206: return $authors;
207: }
208: else {
209: try {
210: return [
211: $authorIds[0] => $this->retrieveAuthor($authorIds[0], $options),
212: ];
213: }
214: catch (Exception $e) {
215:
216: }
217: }
218: }
219:
220: public function retrieveAffiliation($affiliationId, array $options = [])
221: {
222: return $this->retrieve(self::AFFILIATION_URI . $affiliationId, $options);
223: }
224: }