Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
95.65% covered (success)
95.65%
22 / 23
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
Google
95.65% covered (success)
95.65%
22 / 23
50.00% covered (danger)
50.00%
1 / 2
15
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getUrl
95.24% covered (success)
95.24%
20 / 21
0.00% covered (danger)
0.00%
0 / 1
14
1<?php
2
3/**
4 * Google cover content loader.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
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  Content
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/development Wiki
28 */
29
30namespace VuFind\Content\Covers;
31
32use VuFind\Exception\HttpDownloadException;
33
34use function is_callable;
35
36/**
37 * Google cover content loader.
38 *
39 * @category VuFind
40 * @package  Content
41 * @author   Demian Katz <demian.katz@villanova.edu>
42 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
43 * @link     https://vufind.org/wiki/development Wiki
44 */
45class Google extends \VuFind\Content\AbstractCover implements \VuFind\Http\CachingDownloaderAwareInterface
46{
47    use \VuFind\Http\CachingDownloaderAwareTrait;
48
49    /**
50     * Constructor
51     */
52    public function __construct()
53    {
54        $this->supportsIsbn = $this->supportsOclc = true;
55        $this->cacheOptionsSection = 'GoogleCover';
56    }
57
58    /**
59     * Get image URL for a particular API key and set of IDs (or false if invalid).
60     *
61     * @param string $key  API key
62     * @param string $size Size of image to load (small/medium/large)
63     * @param array  $ids  Associative array of identifiers (keys may include 'isbn'
64     * pointing to an ISBN object and 'issn' pointing to a string)
65     *
66     * @return string|bool
67     *
68     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
69     */
70    public function getUrl($key, $size, $ids)
71    {
72        // Don't bother trying if we can't read JSON or ISBN and OCLC are missing:
73        if (!is_callable('json_decode') || (!isset($ids['isbn']) && !isset($ids['oclc']))) {
74            return false;
75        }
76
77        // Construct the request URL and make the HTTP request:
78        if (isset($ids['isbn']) && $ids['isbn']->isValid()) {
79            $ident = "ISBN:{$ids['isbn']->get13()}";
80        } elseif (isset($ids['oclc'])) {
81            $ident = "OCLC:{$ids['oclc']}";
82        } else {
83            return false;
84        }
85        $url = 'https://books.google.com/books?jscmd=viewapi&' .
86               'bibkeys=' . $ident . '&callback=addTheCover';
87
88        $decodeCallback = function (\Laminas\Http\Response $response, $url) {
89            if (
90                !preg_match(
91                    '/^[^{]*({.*})[^}]*$/',
92                    $response->getBody(),
93                    $matches
94                )
95            ) {
96                throw new HttpDownloadException(
97                    'Invalid response body (raw)',
98                    $url,
99                    $response->getStatusCode(),
100                    $response->getHeaders(),
101                    $response->getBody()
102                );
103            }
104
105            // convert \x26 or \u0026 to &
106            $json = json_decode(
107                str_replace(['\\x26', '\\u0026'], '&', $matches[1]),
108                true
109            );
110
111            if ($json === null) {
112                throw new HttpDownloadException(
113                    'Invalid response body (json)',
114                    $url,
115                    $response->getStatusCode(),
116                    $response->getHeaders(),
117                    $response->getBody()
118                );
119            }
120
121            return $json;
122        };
123
124        if (!isset($this->cachingDownloader)) {
125            throw new \Exception('CachingDownloader initialization failed.');
126        }
127
128        $json = $this->cachingDownloader->download($url, [], $decodeCallback);
129        // find the first thumbnail URL and process it:
130        foreach ((array)$json as $current) {
131            if (isset($current['thumbnail_url'])) {
132                $imageUrl = $current['thumbnail_url'];
133                if ($size == 'medium' || $size == 'large') {
134                    $imageUrl = str_replace('zoom=5', 'zoom=1', $imageUrl);
135                }
136                return $imageUrl;
137            }
138        }
139        return false;
140    }
141}