Completed
Push — ticket/25 ( c33bc4...04ae45 )
by Marc
02:02
created

StreamReader::getImageData()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 26
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6.0163

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 26
ccs 12
cts 13
cp 0.9231
rs 8.439
cc 6
eloc 9
nc 8
nop 3
crap 6.0163
1
<?php
2
3
/**
4
 * fast-image-size stream reader
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
use FastImageSize\Type\TypeJpegHelper;
15
16
class StreamReader
17
{
18
	/** @var bool Flag whether allow_url_fopen is enabled */
19
	protected $isFopenEnabled = false;
20
21
	/** @var string Data retrieved from remote */
22
	public $data = '';
23
24
	/**
25
	 * Constructor for fastImageSize class
26
	 */
27 72
	public function __construct()
28
	{
29 72
		$iniGet = new \bantu\IniGetWrapper\IniGetWrapper();
30 72
		$this->isFopenEnabled = $iniGet->getBool('allow_url_fopen');
31 72
	}
32
33
	/**
34
	 * Reset stream reader data
35
	 */
36 72
	public function resetData()
37
	{
38 72
		$this->data = '';
39 72
	}
40
41
	/**
42
	 * Get force length data
43
	 *
44
	 * @param int $offset
45
	 * @param int $length
46
	 * @return bool|string Returned data if long enough, false if not
47
	 */
48 47
	protected function getForcedLengthData($offset, $length)
49
	{
50 47
		return (strlen($this->data) < ($length + $offset)) ? false : substr($this->data, $offset, $length);
51
	}
52
53
	/**
54
	 * Get image from specified path/source
55
	 *
56
	 * @param string $filename Path to image
57
	 * @param int $offset Offset at which reading of the image should start
58
	 * @param int $length Maximum length that should be read
59
	 * @param bool $forceLength True if the length needs to be the specified
60
	 *			length, false if not. Default: true
61
	 *
62
	 * @return false|string Image data or false if result was empty
63
	 */
64 69
	public function getImage($filename, $offset, $length, $forceLength = true)
65
	{
66 69
		if (empty($this->data))
67 69
		{
68 69
			$this->getImageData($filename, $offset, $length);
69 69
		}
70
71
		// Force length to expected one. Return false if data length
72
		// is smaller than expected length
73 69
		if ($forceLength === true)
74 69
		{
75 47
			return $this->getForcedLengthData($offset, $length);
76
		}
77
78 39
		return empty($this->data) ? false : $this->data;
79
	}
80
81
	/**
82
	 * Get image data for specified filename with offset and length
83
	 *
84
	 * @param string $filename Path to image
85
	 * @param int $offset Offset at which reading of the image should start
86
	 * @param int $length Maximum length that should be read
87
	 */
88 69
	protected function getImageData($filename, $offset, $length)
89
	{
90
		// Check if we don't have a valid scheme according to RFC 3986 and
91
		// try to use file_get_contents in that case
92 69
		if (preg_match('#^([a-z][a-z0-9+\-.]+://)#i', $filename))
93 69
		{
94
			try
95
			{
96 1
				$body = $this->getSeekableImageData($filename, $offset);
97
98 1
				while ($body !== null && !$body->eof())
99
				{
100 1
					if (!$this->readDataFromBody($body, $length))
101 1
					{
102
						break;
103
					}
104 1
				}
105
			}
106 1
			catch (\GuzzleHttp\Exception\RequestException $exception)
107
			{
108
				// Silently fail in case of issues during guzzle request
109
			}
110 1
		}
111
112 69
		$this->getImageDataFopen($filename, $offset, $length);
113 69
	}
114
115
	/**
116
	 * Read data from guzzle request body
117
	 *
118
	 * @param \GuzzleHttp\Stream\StreamInterface $body Request body as stream
119
	 * @param int $length Requested length for file
120
	 * @return bool True while reading and not having reached the end of the
121
	 *		targeted length, false if end of targeted length has been reached
122
	 */
123 1
	protected function readDataFromBody(\GuzzleHttp\Stream\StreamInterface $body, $length)
124
	{
125 1
		$readLength = min($length - strlen($this->data), TypeJpegHelper::JPEG_CHUNK_SIZE);
126 1
		$this->data .= $body->read($readLength);
127 1
		if ($readLength < TypeJpegHelper::JPEG_CHUNK_SIZE || strlen($this->data == $readLength))
128 1
		{
129
			return false;
130
		}
131
132 1
		return true;
133
	}
134
135
	/**
136
	 * Get image data using file get contents if data is empty and
137
	 *		allow_url_fopen is enabled
138
	 *
139
	 * @param string $filename Path to image
140
	 * @param int $offset Offset at which reading of the image should start
141
	 * @param int $length Maximum length that should be read
142
	 */
143 69
	protected function getImageDataFopen($filename, $offset, $length)
144
	{
145 69
		if ($this->isFopenEnabled && empty($this->data))
146 69
		{
147 68
			$this->data = @file_get_contents($filename, null, null, $offset, $length);
148 68
		}
149 69
	}
150
151
	/**
152
	 * Get seekable image data in form of Guzzle stream interface
153
	 *
154
	 * @param string $filename Filename / URL to get
155
	 * @param int $offset Offset for response body
156
	 * @return \GuzzleHttp\Stream\StreamInterface|null Stream interface of
157
	 *		requested image or null if it could not be retrieved
158
	 */
159 27
	public function getSeekableImageData($filename, $offset)
160
	{
161 27
		$guzzleClient = new \GuzzleHttp\Client();
162
		// Set stream to true to not read full file data during request
163 27
		$response = $guzzleClient->get($filename, ['stream' => true]);
164
165 3
		$body = $response->getBody();
166
167 3
		if ($offset > 0 && !$body->eof())
168 3
		{
169
			$body->seek($offset);
170
		}
171
172 3
		return $body;
173
	}
174
}
175