Passed
Pull Request — master (#7)
by
unknown
10:24
created

Gravatar::setDefaultImage()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 5.4042

Importance

Changes 1
Bugs 0 Features 0
Metric Value
dl 0
loc 24
rs 8.6845
c 1
b 0
f 0
ccs 5
cts 9
cp 0.5556
cc 4
eloc 12
nc 4
nop 1
crap 5.4042
1
<?php
2
/**
3
 * Gravatar.php
4
 *
5
 * @copyright      More in license.md
6
 * @license        http://www.ipublikuj.eu
7
 * @author         Adam Kadlec http://www.ipublikuj.eu
8
 * @package        iPublikuj:Gravatar!
9
 * @subpackage     common
10
 * @since          1.0.0
11
 *
12
 * @date           05.04.14
13
 */
14
15
declare(strict_types = 1);
16
17
namespace IPub\Gravatar;
18
19
use Nette;
20
use Nette\Http;
21
use Nette\Utils;
22
23
use IPub;
24
use IPub\Gravatar\Caching;
25
use IPub\Gravatar\Exceptions;
26
use IPub\Gravatar\Templating;
27
28
/**
29
 * Gravatar service
30
 *
31
 * @package        iPublikuj:Gravatar!
32
 * @subpackage     common
33
 *
34
 * @author         Adam Kadlec <[email protected]>
35
 */
36 1
final class Gravatar
37
{
38 1
	use Nette\SmartObject;
39
	/**
40
	 * @var string - URL constants for the avatar images
41
	 */
42
	const HTTP_URL = 'http://www.gravatar.com/avatar/';
43
	const HTTPS_URL = 'https://secure.gravatar.com/avatar/';
44
45
	/**
46
	 * @var int
47
	 */
48
	private $expiration = 172800; // two days
49
50
	/**
51
	 * The size to use for avatars.
52
	 *
53
	 * @var int
54
	 */
55
	private $size = 80;
56
57
	/**
58
	 * The default image to use
59
	 * Either a string of the gravatar-recognized default image "type" to use, a URL, or FALSE if using the...default gravatar default image (hah)
60
	 *
61
	 * @var mixed
62
	 */
63
	private $defaultImage = FALSE;
64
65
	/**
66
	 * The maximum rating to allow for the avatar.
67
	 *
68
	 * @var string
69
	 */
70
	private $maxRating = 'g';
71
72
	/**
73
	 * Should we use the secure (HTTPS) URL base?
74
	 *
75
	 * @var bool
76
	 */
77
	private $useSecureUrl = FALSE;
78
79
	/**
80
	 * @var Utils\Image
81
	 */
82
	private $image;
83
84
	/**
85
	 * @var string
86
	 */
87
	private $type;
88
89
	/**
90
	 * @var bool
91
	 */
92
	private $hashEmail = TRUE;
93
94
	/**
95
	 * @var Caching\Cache
96
	 */
97
	private $cache;
98
99
	/**
100
	 * @param Http\Request $httpRequest
101
	 * @param Caching\Cache $cache
102
	 */
103
	public function __construct(
104
		Http\Request $httpRequest,
105
		Caching\Cache $cache
106
	) {
107 1
		$this->useSecureUrl = $httpRequest->isSecured();
108
109
		// Init cache
110 1
		$this->cache = $cache;
111 1
	}
112
113
	/**
114
	 * Get the email hash to use (after cleaning the string)
115
	 *
116
	 * @param string|NULL $email
117
	 *
118
	 * @return string - The hashed form of the email, post cleaning
119
	 */
120
	public function getEmailHash(string $email = NULL) : string
121
	{
122
		// Tack the email hash onto the end.
123 1
		if ($this->hashEmail === TRUE && $email !== NULL) {
124
			// Using md5 as per gravatar docs
125 1
			return hash('md5', strtolower(trim($email)));
126
127
		} elseif ($email !== NULL) {
128
			return $email;
129
130
		} else {
131
			return str_repeat('0', 32);
132
		}
133
	}
134
135
	/**
136
	 * Set the avatar size to use
137
	 *
138
	 * @param int $size - The avatar size to use, must be less than 512 and greater than 0
139
	 *
140
	 * @return void
141
	 */
142
	public function setSize(int $size)
143
	{
144 1
		if ($this->isSizeValid($size)) {
145 1
			$this->size = $size;
146
		}
147 1
	}
148
149
	/**
150
	 * Get the currently set avatar size
151
	 *
152
	 * @return int
153
	 */
154
	public function getSize() : int
155
	{
156 1
		return $this->size;
157
	}
158
159
	/**
160
	 * @param int $size
161
	 *
162
	 * @return bool
163
	 *
164
	 * @throws Exceptions\InvalidArgumentException
165
	 */
166
	public function isSizeValid(int $size) : bool
167
	{
168 1
		if ($size > 512 || $size < 0) {
169
			throw new Exceptions\InvalidArgumentException('Size must be within 0 pixels and 512 pixels');
170
		}
171
172 1
		return TRUE;
173
	}
174
175
	/**
176
	 * Set image cache expiration
177
	 *
178
	 * @param int $expiration
179
	 *
180
	 * @return void
181
	 */
182
	public function setExpiration(int $expiration)
183
	{
184 1
		$this->expiration = $expiration;
185 1
	}
186
187
	/**
188
	 * Set the default image to use for avatars
189
	 *
190
	 * @param mixed $image - The default image to use. Use boolean FALSE for the gravatar default, a string containing a valid image URL, or a string specifying a recognized gravatar "default".
191
	 *
192
	 * @return void
193
	 *
194
	 * @throws Exceptions\InvalidArgumentException
195
	 */
196
	public function setDefaultImage($image)
197
	{
198
		// Quick check against boolean FALSE.
199 1
		if ($image === FALSE) {
200
			$this->defaultImage = FALSE;
201
202
		} else {
203
			// Check $image against recognized gravatar "defaults"
204
			// and if it doesn't match any of those we need to see if it is a valid URL.
205 1
			$_image = strtolower($image);
206
207 1
			if (in_array($_image, ['404', 'mm', 'identicon', 'monsterid', 'wavatar', 'retro'])) {
208 1
				$this->defaultImage = $_image;
209
210
			} else {
211
				if (filter_var($image, FILTER_VALIDATE_URL)) {
212
					$this->defaultImage = rawurlencode($image);
213
214
				} else {
215
					throw new Exceptions\InvalidArgumentException('The default image is not a valid gravatar "default" and is not a valid URL');
216
				}
217
			}
218
		}
219 1
	}
220
221
	/**
222
	 * Get the current default image setting
223
	 *
224
	 * @param string|NULL $defaultImage
225
	 *
226
	 * @return mixed - False if no default image set, string if one is set
227
	 */
228
	public function getDefaultImage(string $defaultImage = NULL)
229
	{
230 1
		if ($defaultImage !== NULL && in_array($defaultImage, ['404', 'mm', 'identicon', 'monsterid', 'wavatar', 'retro'])) {
231 1
			return $defaultImage;
232
		}
233
234 1
		if (filter_var($defaultImage, FILTER_VALIDATE_URL)) {
235
			return rawurldecode($defaultImage);
236
		}
237
238 1
		return $this->defaultImage;
239
	}
240
241
	/**
242
	 * Set the maximum allowed rating for avatars.
243
	 *
244
	 * @param string $rating - The maximum rating to use for avatars ('g', 'pg', 'r', 'x')
245
	 *
246
	 * @return void
247
	 *
248
	 * @throws Exceptions\InvalidArgumentException
249
	 */
250
	public function setMaxRating(string $rating)
251
	{
252 1
		$rating = strtolower($rating);
253
254 1
		if (!in_array($rating, ['g', 'pg', 'r', 'x'])) {
255
			throw new Exceptions\InvalidArgumentException(sprintf('Invalid rating "%s" specified, only "g", "pg", "r", or "x" are allowed to be used.', $rating));
256
		}
257
258 1
		$this->maxRating = $rating;
259 1
	}
260
261
	/**
262
	 * Get the current maximum allowed rating for avatars
263
	 *
264
	 * @param string|NULL $maxRating
265
	 *
266
	 * @return string - The string representing the current maximum allowed rating ('g', 'pg', 'r', 'x').
267
	 */
268
	public function getMaxRating(string $maxRating = NULL) : string
269
	{
270 1
		if ($maxRating !== NULL && in_array($maxRating, ['g', 'pg', 'r', 'x'])) {
271
			return $maxRating;
272
		}
273
274 1
		return $this->maxRating;
275
	}
276
277
	/**
278
	 * Returns the Nette\Image instance
279
	 *
280
	 * @return Utils\Image
281
	 */
282
	public function getImage() : Utils\Image
283
	{
284
		return $this->image;
285
	}
286
287
	/**
288
	 * Returns the type of a image
289
	 *
290
	 * @return string
291
	 */
292
	public function getImageType() : string
293
	{
294
		return $this->type;
295
	}
296
297
	/**
298
	 * Check if we are using the secure protocol for the image URLs
299
	 *
300
	 * @return bool - Are we supposed to use the secure protocol?
301
	 */
302
	public function usingSecureImages() : bool
303
	{
304
		return $this->useSecureUrl;
305
	}
306
307
	/**
308
	 * Enable the use of the secure protocol for image URLs
309
	 *
310
	 * @return void
311
	 */
312
	public function enableSecureImages()
313
	{
314 1
		$this->useSecureUrl = TRUE;
315 1
	}
316
317
	/**
318
	 * Disable the use of the secure protocol for image URLs
319
	 *
320
	 * @return void
321
	 */
322
	public function disableSecureImages()
323
	{
324
		$this->useSecureUrl = FALSE;
325
	}
326
327
	/**
328
	 * Create gravatar image
329
	 *
330
	 * @param string|NULL $email
331
	 * @param int|NULL $size
332
	 *
333
	 * @return Utils\Image
334
	 *
335
	 * @throws Exceptions\InvalidArgumentException
336
	 */
337
	public function get(string $email = NULL, int $size = NULL) : Utils\Image
338
	{
339
		// Set user email address
340
		if ($email !== NULL && !Utils\Validators::isEmail($email)) {
341
			throw new Exceptions\InvalidArgumentException('Inserted email is not valid email address');
342
		}
343
344
		if ($size === NULL || !$this->isSizeValid($size)) {
345
			$size = $this->getSize();
346
		}
347
348
		// Check if avatar is in cache
349
		if (!$gravatar = $this->cache->load($this->getEmailHash($email) . ($size ? '.' . $size : ''))) {
350
			// Get gravatar content
351
			$gravatar = @file_get_contents($this->buildUrl($email, $size));
352
353
			// Store facebook avatar url into cache
354
			$this->cache->save($this->getEmailHash($email) . ($size ? '.' . $size : ''), $gravatar, [
355
				Caching\Cache::EXPIRE => '7 days',
356
			]);
357
		}
358
359
		$this->image = Utils\Image::fromString($gravatar);
360
		$this->type = Utils\Image::JPEG;
361
362
		return $this->image;
363
	}
364
365
	/**
366
	 * Build the avatar URL based on the provided email address
367
	 *
368
	 * @param string|NULL $email
369
	 * @param int|NULL $size
370
	 * @param string|NULL $maxRating
371
	 * @param string|NULL $defaultImage
372
	 *
373
	 * @return string
374
	 *
375
	 * @throws Exceptions\InvalidArgumentException
376
	 */
377
	public function buildUrl(string $email = NULL, int $size = NULL, string $maxRating = NULL, string $defaultImage = NULL) : string
378
	{
379
		// Set user email address
380 1
		if ($email !== NULL && !Utils\Validators::isEmail($email)) {
381
			throw new Exceptions\InvalidArgumentException('Inserted email is not valid email address');
382
		}
383
384
		// Create base url
385 1
		$url = $this->createUrl($email, $size, $maxRating, $defaultImage);
386
387
		// And we're done.
388 1
		return $url->getAbsoluteUrl();
389
	}
390
391
	/**
392
	 * Checks if a gravatar exists for the email. It does this by checking for the presence of 404 in the header
393
	 * returned. Will return null if fsockopen fails, for example when the hostname cannot be resolved.
394
	 *
395
	 * @param string $email
396
	 *
397
	 * @return bool|NULL Boolean if we could connect, null if no connection to gravatar.com
398
	 */
399
	public function exists(string $email)
400
	{
401 1
		$path = $this->buildUrl($email, NULL, NULL, '404');
402
403 1
		if (!$sock = @fsockopen('gravatar.com', 80, $errorNo, $error)) {
404
			return NULL;
405
		}
406
407 1
		fputs($sock, "HEAD " . $path . " HTTP/1.0\r\n\r\n");
408 1
		$header = fgets($sock, 128);
409 1
		fclose($sock);
410
411 1
		return strpos($header, '404') ? FALSE : TRUE;
412
	}
413
414
	/**
415
	 * @return Templating\Helpers
416
	 */
417
	public function createTemplateHelpers() : Templating\Helpers
418
	{
419 1
		return new Templating\Helpers($this);
420
	}
421
422
	/**
423
	 * @param string $email
424
	 * @param int|NULL $size
425
	 * @param string|NULL $maxRating
426
	 * @param string|NULL $defaultImage
427
	 *
428
	 * @return Http\Url
429
	 */
430
	private function createUrl(string $email, int $size = NULL, string $maxRating = NULL, string $defaultImage = NULL) : Http\Url
431
	{
432
		// Tack the email hash onto the end.
433 1
		$emailHash = $this->getEmailHash($email);
434
435
		// Start building the URL, and deciding if we're doing this via HTTPS or HTTP.
436 1
		$url = new Nette\Http\Url(($this->useSecureUrl ? static::HTTPS_URL : static::HTTP_URL) . $emailHash);
437
438 1
		if ($size === NULL || !$this->isSizeValid($size)) {
439 1
			$size = $this->getSize();
440
		}
441
442
		// Time to figure out our request params
443
		$params = [
444 1
			's' => $size,
445 1
			'r' => $this->getMaxRating($maxRating),
446 1
			'd' => $this->getDefaultImage($defaultImage),
447 1
			'f' => is_null($email) ? 'y' : NULL,
448
		];
449
450
		// Add query params
451 1
		$url->appendQuery($params);
452
453 1
		return $url;
454
	}
455
}
456