1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Gravatar; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Gravatar URL Builder. |
9
|
|
|
* |
10
|
|
|
* @author Márk Sági-Kazár <[email protected]> |
11
|
|
|
*/ |
12
|
|
|
final class Gravatar |
13
|
|
|
{ |
14
|
|
|
/** |
15
|
|
|
* Gravatar endpoints. |
16
|
|
|
*/ |
17
|
|
|
private const HTTP_ENDPOINT = 'http://www.gravatar.com'; |
18
|
|
|
private const HTTPS_ENDPOINT = 'https://secure.gravatar.com'; |
19
|
|
|
|
20
|
|
|
/** |
21
|
|
|
* Minimum image size |
22
|
|
|
* |
23
|
|
|
* @var int |
24
|
|
|
*/ |
25
|
|
|
private const MINIMUM_IMAGE_SIZE = 1; |
26
|
|
|
|
27
|
|
|
/** |
28
|
|
|
* Maximum image size |
29
|
|
|
* |
30
|
|
|
* @var int |
31
|
|
|
*/ |
32
|
|
|
private const MAXIMUM_IMAGE_SIZE = 2048; |
33
|
|
|
|
34
|
16 |
|
/** |
35
|
|
|
* Default Image Keywords |
36
|
16 |
|
* |
37
|
16 |
|
* @var array |
38
|
16 |
|
*/ |
39
|
|
|
private const DEFAULT_IMAGE_KEYWORDS = [ |
40
|
|
|
'404', |
41
|
|
|
'mp', |
42
|
|
|
'identicon', |
43
|
|
|
'monsterid', |
44
|
|
|
'wavatar', |
45
|
|
|
'retro', |
46
|
|
|
'robohash', |
47
|
|
|
'blank' |
48
|
|
|
]; |
49
|
5 |
|
|
50
|
|
|
/** |
51
|
5 |
|
* Image ratings |
52
|
4 |
|
* |
53
|
|
|
* @var array |
54
|
4 |
|
*/ |
55
|
2 |
|
private const IMAGE_RATINGS = [ |
56
|
2 |
|
'g', |
57
|
|
|
'pg', |
58
|
4 |
|
'r', |
59
|
|
|
'x' |
60
|
|
|
]; |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* @var array |
64
|
|
|
*/ |
65
|
|
|
private $defaults = []; |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Whether to use HTTPS endpoint. |
69
|
10 |
|
* |
70
|
|
|
* @var bool |
71
|
10 |
|
*/ |
72
|
|
|
private $secure; |
73
|
|
|
|
74
|
|
|
public function __construct(array $defaults = [], bool $secure = true) |
75
|
|
|
{ |
76
|
|
|
$this->defaults = array_filter($defaults); |
77
|
|
|
$this->secure = $secure; |
78
|
|
|
} |
79
|
|
|
|
80
|
|
|
/** |
81
|
|
|
* Returns an Avatar URL. |
82
|
3 |
|
*/ |
83
|
|
|
public function avatar(string $email, array $options = [], ?bool $secure = null, bool $validateOptions = false): string |
84
|
3 |
|
{ |
85
|
|
|
$url = 'avatar/'.$this->createEmailHash($email); |
86
|
|
|
|
87
|
|
|
$options = array_merge($this->defaults, array_filter($options)); |
88
|
|
|
|
89
|
|
|
if ($validateOptions) { |
90
|
|
|
$this->validateOptions($options); |
91
|
|
|
} |
92
|
|
|
|
93
|
|
|
if (!empty($options)) { |
94
|
|
|
$url .= '?'.http_build_query($options); |
95
|
3 |
|
} |
96
|
|
|
|
97
|
3 |
|
return $this->buildUrl($url, $secure); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Returns a profile URL. |
102
|
|
|
*/ |
103
|
|
|
public function profile(string $email, ?bool $secure = null): string |
104
|
|
|
{ |
105
|
|
|
return $this->buildUrl($this->createEmailHash($email), $secure); |
106
|
|
|
} |
107
|
15 |
|
|
108
|
|
|
/** |
109
|
15 |
|
* Returns a vCard URL. |
110
|
4 |
|
*/ |
111
|
|
|
public function vcard(string $email, ?bool $secure = null): string |
112
|
|
|
{ |
113
|
11 |
|
return $this->profile($email, $secure).'.vcf'; |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
/** |
117
|
|
|
* Returns a QR Code URL. |
118
|
|
|
*/ |
119
|
|
|
public function qrCode(string $email, ?bool $secure = null): string |
120
|
|
|
{ |
121
|
|
|
return $this->profile($email, $secure).'.qr'; |
122
|
|
|
} |
123
|
|
|
|
124
|
11 |
|
/** |
125
|
|
|
* Creates a hash from an email address. |
126
|
11 |
|
*/ |
127
|
|
|
private function createEmailHash(string $email): string |
128
|
11 |
|
{ |
129
|
|
|
if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { |
130
|
11 |
|
throw new \InvalidArgumentException('Invalid email address'); |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
return md5(strtolower(trim($email))); |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
private function validateOptions(array $options): array |
137
|
|
|
{ |
138
|
|
|
// Image size |
139
|
|
|
if (array_key_exists('s', $options)) { |
140
|
|
|
$size = filter_var($options['s'], FILTER_VALIDATE_INT); |
141
|
|
|
|
142
|
|
|
if ($size === false) { |
143
|
|
|
throw new \InvalidArgumentException('The size parameter ' . $options['s'] . ' is not an integer.'); |
144
|
|
|
} |
145
|
|
|
|
146
|
|
|
if ($size < self::MINIMUM_IMAGE_SIZE || $size > self::MAXIMUM_IMAGE_SIZE) { |
147
|
|
|
throw new \InvalidArgumentException('The parameter ' . $options['s'] . ' is outside the allowed range of ' . self::MINIMUM_IMAGE_SIZE . ' to ' . self::MAXIMUM_IMAGE_SIZE . '.'); |
148
|
|
|
} |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
if (array_key_exists('size', $options)) { |
152
|
|
|
$size = filter_var($options['size'], FILTER_VALIDATE_INT); |
153
|
|
|
|
154
|
|
|
if ($size === false) { |
155
|
|
|
throw new \InvalidArgumentException('The size parameter ' . $options['size'] . ' is not an integer.'); |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
if ($size < self::MINIMUM_IMAGE_SIZE || $size > self::MAXIMUM_IMAGE_SIZE) { |
159
|
|
|
throw new \InvalidArgumentException('The size parameter ' . $options['size'] . ' is outside the allowed range of ' . self::MINIMUM_IMAGE_SIZE . ' to ' . self::MAXIMUM_IMAGE_SIZE . '.'); |
160
|
|
|
} |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
// Default image |
164
|
|
|
if (array_key_exists('d', $options)) { |
165
|
|
|
$defaultImage = $options['d']; |
166
|
|
|
|
167
|
|
|
if (filter_var($defaultImage, FILTER_VALIDATE_URL) === false && !in_array(strtolower($defaultImage), self::DEFAULT_IMAGE_KEYWORDS)) { |
168
|
|
|
throw new \InvalidArgumentException('The default image parameter ' . $options['d'] . ' is not a URL or one of the allowed image keywords.'); |
169
|
|
|
} |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
if (array_key_exists('default', $options)) { |
173
|
|
|
$defaultImage = $options['default']; |
174
|
|
|
|
175
|
|
|
if (filter_var($defaultImage, FILTER_VALIDATE_URL) === false && !in_array(strtolower($defaultImage), self::DEFAULT_IMAGE_KEYWORDS)) { |
176
|
|
|
throw new \InvalidArgumentException('The default image parameter ' . $options['default'] . ' is not a URL or one of the allowed image keywords.'); |
177
|
|
|
} |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
// Force Default |
181
|
|
|
if (array_key_exists('f', $options)) { |
182
|
|
|
$forceDefault = $options['f']; |
183
|
|
|
|
184
|
|
|
if ($forceDefault !== 'y') { |
185
|
|
|
throw new \InvalidArgumentException('The force default parameter ' . $options['f'] . ' is invalid.'); |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
if (array_key_exists('forcedefault', $options)) { |
190
|
|
|
$forceDefault = $options['forcedefault']; |
191
|
|
|
|
192
|
|
|
if ($forceDefault !== 'y') { |
193
|
|
|
throw new \InvalidArgumentException('The force default parameter ' . $options['forcedefault'] . ' is invalid.'); |
194
|
|
|
} |
195
|
|
|
} |
196
|
|
|
|
197
|
|
|
// Rating |
198
|
|
|
if (array_key_exists('r', $options)) { |
199
|
|
|
$rating = strtolower($options['r']); |
200
|
|
|
|
201
|
|
|
if (!in_array($rating, self::IMAGE_RATINGS)) { |
202
|
|
|
throw new \InvalidArgumentException('The rating parameter ' . $options['r'] . ' is invalid.'); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
if (array_key_exists('rating', $options)) { |
207
|
|
|
$rating = strtolower($options['rating']); |
208
|
|
|
|
209
|
|
|
if (!in_array($rating, self::IMAGE_RATINGS)) { |
210
|
|
|
throw new \InvalidArgumentException('The rating parameter ' . $options['rating'] . ' is invalid.'); |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
return $options; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
/** |
218
|
|
|
* Builds the URL based on the given parameters. |
219
|
|
|
*/ |
220
|
|
|
private function buildUrl(string $resource, ?bool $secure): string |
221
|
|
|
{ |
222
|
|
|
$secure = isset($secure) ? (bool) $secure : $this->secure; |
223
|
|
|
|
224
|
|
|
$endpoint = $secure ? self::HTTPS_ENDPOINT : self::HTTP_ENDPOINT; |
225
|
|
|
|
226
|
|
|
return sprintf('%s/%s', $endpoint, $resource); |
227
|
|
|
} |
228
|
|
|
} |
229
|
|
|
|