Passed
Branch v3.x (ce3548)
by Chad
04:10
created

Client::__call()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.2
c 0
b 0
f 0
cc 4
eloc 10
nc 6
nop 2
1
<?php
2
3
namespace Chadicus\Marvel\Api;
4
5
use GuzzleHttp;
6
use Psr\SimpleCache\CacheInterface;
7
use Psr\Http\Message\ResponseInterface;
8
9
/**
10
 * PHP Client for the Marvel API.
11
 */
12
class Client implements ClientInterface
13
{
14
    /**
15
     * The default ttl for cached responses (24 hours).
16
     *
17
     * @link http://developer.marvel.com/documentation/attribution Marvel's rules for caching.
18
     *
19
     * @const integer
20
     */
21
    const MAX_TTL = 86400;
22
23
    /**
24
     * The public api key issued by Marvel.
25
     *
26
     * @var string
27
     */
28
    private $publicApiKey;
29
30
    /**
31
     * The private api key issued by Marvel.
32
     *
33
     * @var string
34
     */
35
    private $privateApiKey;
36
37
    /**
38
     * Guzzle HTTP Client implementation.
39
     *
40
     * @var GuzzleHttp\ClientInterface
41
     */
42
    private $guzzleClient;
43
44
    /**
45
     * Cache implementation.
46
     *
47
     * @var CacheInterface
48
     */
49
    private $cache;
50
51
    /**
52
     * The Marvel API url.
53
     *
54
     * @const string
55
     */
56
    const BASE_URL = 'http://gateway.marvel.com/v1/public/';
57
58
    /**
59
     * Construct a new Client.
60
     *
61
     * @param string                     $privateApiKey The private api key issued by Marvel.
62
     * @param string                     $publicApiKey  The public api key issued by Marvel.
63
     * @param GuzzleHttp\ClientInterface $guzzleClient  Implementation of a Guzzle HTTP client.
64
     * @param CacheInterface             $cache         Implementation of Cache.
65
     */
66
    final public function __construct(
67
        string $privateApiKey,
68
        string $publicApiKey,
69
        GuzzleHttp\ClientInterface $guzzleClient = null,
70
        CacheInterface $cache = null
71
    ) {
72
        $this->privateApiKey = $privateApiKey;
73
        $this->publicApiKey = $publicApiKey;
74
        $this->guzzleClient = $guzzleClient ?: new GuzzleHttp\Client();
75
        $this->cache = $cache ?: new Cache\NullCache();
76
    }
77
78
    /**
79
     * Execute a search request against the Marvel API.
80
     *
81
     * @param string $resource The API resource to search for.
82
     * @param array  $filters  Array of search criteria to use in request.
83
     *
84
     * @return null|DataWrapper
85
     *
86
     * @throws \InvalidArgumentException Thrown if $resource is empty or not a string.
87
     */
88
    final public function search(string $resource, array $filters = [])
89
    {
90
        $filters['apikey'] = $this->publicApiKey;
91
        $timestamp = time();
92
        $filters['ts'] = $timestamp;
93
        $filters['hash'] = md5($timestamp . $this->privateApiKey . $this->publicApiKey);
94
        $url = self::BASE_URL . urlencode($resource) . '?' . http_build_query($filters);
95
96
        $response = $this->send($url);
97
        if ($response->getStatusCode() !== 200) {
98
            return null;
99
        }
100
101
        return DataWrapper::fromJson((string)$response->getBody());
102
    }
103
104
    /**
105
     * Execute a GET request against the Marvel API for a single resource.
106
     *
107
     * @param string  $resource The API resource to search for.
108
     * @param integer $id       The id of the API resource.
109
     *
110
     * @return null|DataWrapper
111
     */
112
    final public function get(string $resource, int $id)
113
    {
114
        $timestamp = time();
115
        $query = [
116
            'apikey' => $this->publicApiKey,
117
            'ts' => $timestamp,
118
            'hash' => md5($timestamp . $this->privateApiKey . $this->publicApiKey),
119
        ];
120
121
        $url = self::BASE_URL . urlencode($resource) . "/{$id}?" . http_build_query($query);
122
        $response =  $this->send($url);
123
        if ($response->getStatusCode() !== 200) {
124
            return null;
125
        }
126
127
        return DataWrapper::fromJson((string)$response->getBody());
128
    }
129
130
    /**
131
     * Send the given API url request.
132
     *
133
     * @param string $url The URL to request.
134
     *
135
     * @return ResponseInterface
136
     */
137
    final private function send(string $url) : ResponseInterface
138
    {
139
        $response = $this->cache->get($url);
140
        if ($response !== null) {
141
            return $response;
142
        }
143
144
        $response = $this->guzzleClient->request(
145
            'GET',
146
            $url,
147
            [
148
               'http_errors' => false,
149
               'headers' => ['Accept' =>  'application/json'],
150
            ]
151
        );
152
        $this->cache->set($url, $response, self::MAX_TTL);
153
        return $response;
154
    }
155
156
    /**
157
     * Allow calls such as $client->characters();
158
     *
159
     * @param string $name      The name of the api resource.
160
     * @param array  $arguments The parameters to pass to get() or search().
161
     *
162
     * @return Collection|EntityInterface|null
163
     */
164
    final public function __call(string $name, array $arguments)
165
    {
166
        $resource = strtolower($name);
167
        $idOrFilters = array_shift($arguments) ?: [];
168
169
        if (is_array($idOrFilters)) {
170
            return new Collection($this, $resource, $idOrFilters);
171
        }
172
173
        $dataWrapper = $this->get($resource, $idOrFilters);
174
        if ($dataWrapper === null) {
175
            return null;
176
        }
177
178
        $results = $dataWrapper->getData()->getResults();
179
        return array_shift($results);
180
    }
181
}
182