Completed
Push — master ( ddda9f...32824e )
by Adam
02:20
created

Gravatar::createTemplateHelpers()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

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