Completed
Push — ticket/25 ( c2a4db...26e02b )
by Marc
01:59
created

FastImageSize::getImageSize()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 4

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 9
cts 9
cp 1
rs 9.2
c 0
b 0
f 0
cc 4
eloc 8
nc 3
nop 2
crap 4
1
<?php
2
3
/**
4
 * fast-image-size base class
5
 * @package fast-image-size
6
 * @copyright (c) Marc Alexander <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
namespace FastImageSize;
13
14
class FastImageSize
15
{
16
	/** @var bool Flag whether allow_url_fopen is enabled */
17
	protected $isFopenEnabled = false;
18
19
	/** @var array Size info that is returned */
20
	protected $size = array();
21
22
	/** @var string Data retrieved from remote */
23
	protected $data = '';
24
25
	/** @var array List of supported image types and associated image types */
26
	protected $supportedTypes = array(
27
		'png'	=> array('png'),
28
		'gif'	=> array('gif'),
29
		'jpeg'	=> array(
30
				'jpeg',
31
				'jpg',
32
				'jpe',
33
				'jif',
34
				'jfif',
35
				'jfi',
36
			),
37
		'jp2'	=> array(
38
				'jp2',
39
				'j2k',
40
				'jpf',
41
				'jpg2',
42
				'jpx',
43
				'jpm',
44
			),
45
		'psd'	=> array(
46
				'psd',
47
				'photoshop',
48
			),
49
		'bmp'	=> array('bmp'),
50
		'tif'	=> array(
51
				'tif',
52
				'tiff',
53
			),
54
		'wbmp'	=> array(
55
				'wbm',
56
				'wbmp',
57
				'vnd.wap.wbmp',
58
			),
59
		'iff'	=> array(
60
				'iff',
61
				'x-iff',
62
		),
63
		'ico'	=> array(
64
				'ico',
65
				'vnd.microsoft.icon',
66
				'x-icon',
67
				'icon',
68
		),
69
	);
70
71
	/** @var array Class map that links image extensions/mime types to class */
72
	protected $classMap;
73
74
	/** @var array An array containing the classes of supported image types */
75
	protected $type;
76
77
	/**
78
	 * Constructor for fastImageSize class
79
	 */
80 72
	public function __construct()
81
	{
82 72
		$iniGet = new \bantu\IniGetWrapper\IniGetWrapper();
83 72
		$this->isFopenEnabled = $iniGet->getBool('allow_url_fopen');
84
85 72
		foreach ($this->supportedTypes as $imageType => $extension)
86
		{
87 72
			$className = '\FastImageSize\Type\Type' . mb_convert_case(mb_strtolower($imageType), MB_CASE_TITLE);
88 72
			$this->type[$imageType] = new $className($this);
89
90
			// Create class map
91 72
			foreach ($extension as $ext)
92
			{
93
				/** @var Type\TypeInterface */
94 72
				$this->classMap[$ext] = $this->type[$imageType];
95 72
			}
96 72
		}
97 72
	}
98
99
	/**
100
	 * Get size array
101
	 *
102
	 * @return array|bool Size array if size could be evaluated, false if not
103
	 */
104 72
	protected function getSize()
105
	{
106 72
		return sizeof($this->size) > 1 ? $this->size : false;
107
	}
108
109
	/**
110
	 * Get image dimensions of supplied image
111
	 *
112
	 * @param string $file Path to image that should be checked
113
	 * @param string $type Mimetype of image
114
	 * @return array|bool Array with image dimensions if successful, false if not
115
	 */
116 72
	public function getImageSize($file, $type = '')
117
	{
118
		// Reset values
119 72
		$this->resetValues();
120
121
		// Treat image type as unknown if extension or mime type is unknown
122 72
		if (!preg_match('/\.([a-z0-9]+)$/i', $file, $match) && empty($type))
123 72
		{
124 18
			$this->getImagesizeUnknownType($file);
125 18
		}
126
		else
127
		{
128 54
			$extension = (isset($match[1])) ? $match[1] : preg_replace('/.+\/([a-z0-9-.]+)$/i', '$1', $type);
129
130 54
			$this->getImageSizeByExtension($file, strtolower($extension));
131
		}
132
133 72
		return $this->getSize();
134
	}
135
136
	/**
137
	 * Get dimensions of image if type is unknown
138
	 *
139
	 * @param string $filename Path to file
140
	 */
141 18
	protected function getImagesizeUnknownType($filename)
142
	{
143
		// Grab the maximum amount of bytes we might need
144 18
		$data = $this->getImage($filename, 0, Type\TypeJpeg::JPEG_MAX_HEADER_SIZE, false);
145
146 18
		if ($data !== false)
147 18
		{
148 17
			foreach ($this->type as $imageType)
149
			{
150 17
				$imageType->getSize($filename);
151
152 17
				if (sizeof($this->size) > 1)
153 17
				{
154 17
					break;
155
				}
156 17
			}
157 17
		}
158 18
	}
159
160
	/**
161
	 * Get image size by file extension
162
	 *
163
	 * @param string $file Path to image that should be checked
164
	 * @param string $extension Extension/type of image
165
	 */
166 54
	protected function getImageSizeByExtension($file, $extension)
167
	{
168 54
		if (isset($this->classMap[$extension]))
169 54
		{
170 53
			$this->classMap[$extension]->getSize($file);
171 53
		}
172 54
	}
173
174
	/**
175
	 * Reset values to default
176
	 */
177 72
	protected function resetValues()
178
	{
179 72
		$this->size = array();
180 72
		$this->data = '';
181 72
	}
182
183
	/**
184
	 * Set mime type based on supplied image
185
	 *
186
	 * @param int $type Type of image
187
	 */
188 47
	public function setImageType($type)
189
	{
190 47
		$this->size['type'] = $type;
191 47
	}
192
193
	/**
194
	 * Set size info
195
	 *
196
	 * @param array $size Array containing size info for image
197
	 */
198 53
	public function setSize($size)
199
	{
200 53
		$this->size = $size;
201 53
	}
202
203
	/**
204
	 * Get image from specified path/source
205
	 *
206
	 * @param string $filename Path to image
207
	 * @param int $offset Offset at which reading of the image should start
208
	 * @param int $length Maximum length that should be read
209
	 * @param bool $forceLength True if the length needs to be the specified
210
	 *			length, false if not. Default: true
211
	 *
212
	 * @return false|string Image data or false if result was empty
213
	 */
214 69
	public function getImage($filename, $offset, $length, $forceLength = true)
215
	{
216 69
		if (empty($this->data))
217 69
		{
218 69
			$this->getImageData($filename, $offset, $length);
219 69
		}
220
221
		// Force length to expected one. Return false if data length
222
		// is smaller than expected length
223 69
		if ($forceLength === true)
224 69
		{
225 47
			return (strlen($this->data) < $length) ? false : substr($this->data, $offset, $length) ;
226
		}
227
228 39
		return empty($this->data) ? false : $this->data;
229
	}
230
231
	/**
232
	 * Get return data
233
	 *
234
	 * @return array|bool Size array if dimensions could be found, false if not
235
	 */
236
	protected function getReturnData()
237
	{
238
		return sizeof($this->size) > 1 ? $this->size : false;
239
	}
240
241
	/**
242
	 * Get image data for specified filename with offset and length
243
	 *
244
	 * @param string $filename Path to image
245
	 * @param int $offset Offset at which reading of the image should start
246
	 * @param int $length Maximum length that should be read
247
	 */
248 69
	protected function getImageData($filename, $offset, $length)
249
	{
250
		// Check if we don't have a valid scheme according to RFC 3986 and
251
		// try to use file_get_contents in that case
252 69
		if (preg_match('#^([a-z][a-z0-9+\-.]+://)#i', $filename))
253 69
		{
254
			try
255
			{
256 1
				$body = $this->getSeekableImageData($filename, $offset);
257
258 1
				while (!$body->eof())
259
				{
260 1
					$readLength = min($length - strlen($this->data), 8192);
261 1
					$this->data .= $body->read($readLength);
262 1
					if ($readLength < 8192 || strlen($this->data == $readLength))
263 1
					{
264
						break;
265
					}
266 1
				}
267
			}
268 1
			catch (\GuzzleHttp\Exception\RequestException $exception)
269
			{
270
				// Silently fail in case of issues during guzzle request
271
			}
272 1
		}
273
274 69
		if (empty($this->data) && $this->isFopenEnabled)
275 69
		{
276 68
			$this->data = @file_get_contents($filename, null, null, $offset, $length);
277 68
		}
278 69
	}
279
280
	/**
281
	 * Get seekable image data in form of Guzzle stream interface
282
	 *
283
	 * @param string $filename Filename / URL to get
284
	 * @param int $offset Offset for response body
285
	 * @return \GuzzleHttp\Stream\StreamInterface|null Stream interface of
286
	 *		requested image or null if it could not be retrieved
287
	 */
288 27
	public function getSeekableImageData($filename, $offset)
289
	{
290 27
		$guzzleClient = new \GuzzleHttp\Client();
291
		// Set stream to true to not read full file data during request
292 27
		$response = $guzzleClient->get($filename, ['stream' => true]);
293
294 3
		$body = $response->getBody();
295
296 3
		if ($offset > 0 && !$body->eof())
297 3
		{
298
			$body->seek($offset);
299
		}
300
301 3
		return $body;
302
	}
303
}
304