Issues (901)

exif/exif.php (1 issue)

1
<?php
2
/**
3
 * phpBB Gallery - ACP Exif Extension
4
 *
5
 * @package   phpbbgallery/exif
6
 * @author    nickvergessen
7
 * @author    satanasov
8
 * @author    Leinad4Mind
9
 * @copyright 2007-2012 nickvergessen, 2014- satanasov, 2018- Leinad4Mind
10
 * @license   GPL-2.0-only
11
 */
12
13
namespace phpbbgallery\exif;
14
15
/**
16
* Base class for Exif handling
17
*/
18
class exif
19
{
20
	/**
21
	* Default value for new users
22
	*/
23
	const DEFAULT_DISPLAY	= true;
24
25
	/**
26
	* phpBB will treat the time from the Exif data like UTC.
27
	* If your images were taken with an other timezone, you can insert an offset here.
28
	* The offset is than added to the timestamp before it is converted into the users time.
29
	*
30
	* Offset must be set in seconds.
31
	*/
32
	const TIME_OFFSET	= 0;
33
34
	/**
35
	* Constants for the status of the Exif data.
36
	*/
37
	const UNAVAILABLE	= 0;
38
	const AVAILABLE		= 1;
39
	const UNKNOWN		= 2;
40
	const DBSAVED		= 3;
41
42
	/**
43
	* Is the function available?
44
	*/
45
	static public $function_exists = null;
46
47
	/**
48
	* Exif data array with all allowed groups and keys.
49
	*/
50
	public $data		= array();
51
52
	/**
53
	* Filtered data array. We don't have empty or invalid values here.
54
	*/
55
	public $prepared_data	= array();
56
57
	/**
58
	* Does the image have exif data?
59
	* Values see constant declaration at the beginning of the class.
60
	*/
61
	public $status		= 2;
62
63
	/**
64
	* Full data array, but serialized to a string
65
	*/
66
	public $serialized	= '';
67
68
	/**
69
	* Full link to the image-file
70
	*/
71
	public $file		= '';
72
73
	/**
74
	* Original status of the Exif data.
75
	*/
76
	public $orig_status = null;
77
78
	/**
79
	* Image-ID, just needed to update the Exif status
80
	*/
81
	public $image_id	= false;
82
83
	/**
84
	* Constructor
85
	*
86
	* @param	string	$file		Full link to the image-file
87
	* @param	mixed	$image_id	False or integer
88
	*/
89
	public function __construct($file, $image_id = false)
90
	{
91
		if (self::$function_exists === null)
92
		{
93
			self::$function_exists = (function_exists('exif_read_data')) ? true : false;
94
		}
95
		if ($image_id)
96
		{
97
			$this->image_id = (int) $image_id;
98
		}
99
100
		$this->file = $file;
101
	}
102
103
	/**
104
	* Interpret the values from the database, and read the data if we don't have it.
105
	*
106
	* @param	int		$status		Value of a status constant (see beginning of the class)
107
	* @param	mixed	$data		Either an empty string or the serialized array of the Exif from the database
108
	*/
109
	public function interpret($status, $data)
110
	{
111
		$this->orig_status = $status;
112
		$this->status = $status;
113
		if ($this->status == self::DBSAVED)
114
		{
115
			$this->data = @unserialize($data);
116
		}
117
		else if (($this->status == self::AVAILABLE) || ($this->status == self::UNKNOWN))
118
		{
119
			$this->read();
120
		}
121
	}
122
123
	/**
124
	* Read Exif data from the image
125
	*/
126
	public function read()
127
	{
128
		if (!self::$function_exists || !$this->file || !file_exists($this->file))
129
		{
130
			return;
131
		}
132
133
		$this->data = @exif_read_data($this->file, 0, true);
134
135
		if (!empty($this->data["EXIF"]))
136
		{
137
			// Unset invalid Exif's
138
			foreach ($this->data as $key => $array)
139
			{
140
				if (!in_array($key, self::$allowed_groups))
141
				{
142
					unset($this->data[$key]);
143
				}
144
				else
145
				{
146
					foreach ($this->data[$key] as $subkey => $array)
0 ignored issues
show
Comprehensibility Bug introduced by
$array is overwriting a variable from outer foreach loop.
Loading history...
147
					{
148
						if (!in_array($subkey, self::$allowed_keys))
149
						{
150
							unset($this->data[$key][$subkey]);
151
						}
152
					}
153
				}
154
			}
155
156
			$this->serialized = serialize($this->data);
157
			$this->status = self::DBSAVED;
158
		}
159
		else
160
		{
161
			$this->status = self::UNAVAILABLE;
162
		}
163
164
		if ($this->image_id)
165
		{
166
			$this->set_status();
167
		}
168
	}
169
170
	/**
171
	* Validate and prepare the data, so we can send it into the template.
172
	*/
173
	private function prepare_data()
174
	{
175
		global $user;
176
177
		$user->add_lang_ext('phpbbgallery/exif', 'exif');
178
179
		$this->prepared_data = array();
180
		if (isset($this->data["EXIF"]["DateTimeOriginal"]))
181
		{
182
			$timestamp_year = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 0, 4);
183
			$timestamp_month = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 5, 2);
184
			$timestamp_day = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 8, 2);
185
			$timestamp_hour = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 11, 2);
186
			$timestamp_minute = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 14, 2);
187
			$timestamp_second = (int) substr($this->data["EXIF"]["DateTimeOriginal"], 17, 2);
188
			$timestamp = (int) @mktime($timestamp_hour, $timestamp_minute, $timestamp_second, $timestamp_month, $timestamp_day, $timestamp_year);
189
			if ($timestamp)
190
			{
191
				$this->prepared_data['exif_date'] = $user->format_date($timestamp + self::TIME_OFFSET);
192
			}
193
		}
194
		if (isset($this->data["EXIF"]["FocalLength"]))
195
		{
196
			list($num, $den) = explode("/", $this->data["EXIF"]["FocalLength"]);
197
			if ($den)
198
			{
199
				$this->prepared_data['exif_focal'] = sprintf($user->lang['EXIF_FOCAL_EXP'], ($num / $den));
200
			}
201
		}
202
		if (isset($this->data["EXIF"]["ExposureTime"]))
203
		{
204
			list($num, $den) = explode("/", $this->data["EXIF"]["ExposureTime"]);
205
			$exif_exposure = '';
206
			if (($num > $den) && $den)
207
			{
208
				$exif_exposure = $num / $den;
209
			}
210
			else if ($num)
211
			{
212
				$exif_exposure = ' 1/' . $den / $num ;
213
			}
214
			if ($exif_exposure)
215
			{
216
				$this->prepared_data['exif_exposure'] = sprintf($user->lang['EXIF_EXPOSURE_EXP'], $exif_exposure);
217
			}
218
		}
219
		if (isset($this->data["EXIF"]["FNumber"]))
220
		{
221
			list($num, $den) = explode("/", $this->data["EXIF"]["FNumber"]);
222
			if ($den)
223
			{
224
				$this->prepared_data['exif_aperture'] = "F/" . ($num / $den);
225
			}
226
		}
227
		if (isset($this->data["EXIF"]["ISOSpeedRatings"]) && !is_array($this->data["EXIF"]["ISOSpeedRatings"]))
228
		{
229
			$this->prepared_data['exif_iso'] = $this->data["EXIF"]["ISOSpeedRatings"];
230
		}
231
		if (isset($this->data["EXIF"]["WhiteBalance"]))
232
		{
233
			$this->prepared_data['exif_whiteb'] = $user->lang['EXIF_WHITEB_' . (($this->data["EXIF"]["WhiteBalance"]) ? 'MANU' : 'AUTO')];
234
		}
235
		if (isset($this->data["EXIF"]["Flash"]))
236
		{
237
			if (isset($user->lang['EXIF_FLASH_CASE_' . $this->data["EXIF"]["Flash"]]))
238
			{
239
				$this->prepared_data['exif_flash'] = $user->lang['EXIF_FLASH_CASE_' . $this->data["EXIF"]["Flash"]];
240
			}
241
		}
242
		if (isset($this->data["IFD0"]["Model"]))
243
		{
244
			$this->prepared_data['exif_cam_model'] = ucwords($this->data["IFD0"]["Model"]);
245
		}
246
		if (isset($this->data["EXIF"]["ExposureProgram"]))
247
		{
248
			if (isset($user->lang['EXIF_EXPOSURE_PROG_' . $this->data["EXIF"]["ExposureProgram"]]))
249
			{
250
				$this->prepared_data['exif_exposure_prog'] = $user->lang['EXIF_EXPOSURE_PROG_' . $this->data["EXIF"]["ExposureProgram"]];
251
			}
252
		}
253
		if (isset($this->data["EXIF"]["ExposureBiasValue"]))
254
		{
255
			list($num,$den) = explode("/", $this->data["EXIF"]["ExposureBiasValue"]);
256
			if ($den)
257
			{
258
				if (($num / $den) == 0)
259
				{
260
					$exif_exposure_bias = 0;
261
				}
262
				else
263
				{
264
					$exif_exposure_bias = $this->data["EXIF"]["ExposureBiasValue"];
265
				}
266
				$this->prepared_data['exif_exposure_bias'] = sprintf($user->lang['EXIF_EXPOSURE_BIAS_EXP'], $exif_exposure_bias);
267
			}
268
		}
269
		if (isset($this->data["EXIF"]["MeteringMode"]))
270
		{
271
			if (isset($user->lang['EXIF_METERING_MODE_' . $this->data["EXIF"]["MeteringMode"]]))
272
			{
273
				$this->prepared_data['exif_metering_mode'] = $user->lang['EXIF_METERING_MODE_' . $this->data["EXIF"]["MeteringMode"]];
274
			}
275
		}
276
	}
277
278
	/**
279
	* Sends the Exif into the template
280
	*
281
	* @param	bool	$expand_view	Shall we expand the Exif data on page view or collapse?
282
	* @param	string	$block			Name of the template loop the Exif's are displayed in.
283
	*/
284
	public function send_to_template($expand_view = true, $block = 'exif_value')
285
	{
286
		$this->prepare_data();
287
288
		if (!empty($this->prepared_data))
289
		{
290
			global $template, $user;
291
292
			foreach ($this->prepared_data as $exif => $value)
293
			{
294
				$template->assign_block_vars($block, array(
295
					'EXIF_NAME'			=> $user->lang[strtoupper($exif)],
296
					'EXIF_VALUE'		=> htmlspecialchars($value),
297
				));
298
			}
299
			$template->assign_vars(array(
300
				'S_EXIF_DATA'	=> true,
301
				'S_VIEWEXIF'	=> $expand_view,
302
			));
303
		}
304
	}
305
306
	/**
307
	* Save the new Exif status in the database
308
	*/
309
	public function set_status()
310
	{
311
		if (!$this->image_id || ($this->orig_status == $this->status))
312
		{
313
			return false;
314
		}
315
316
		global $db, $table_prefix;
317
318
		$update_data = ($this->status == self::DBSAVED) ? ", image_exif_data = '" . $db->sql_escape($this->serialized) . "'" : '';
319
		$sql = 'UPDATE ' . $table_prefix . 'gallery_images 
320
			SET image_has_exif = ' . $this->status . $update_data . '
321
			WHERE image_id = ' . (int) $this->image_id;
322
		$db->sql_query($sql);
323
	}
324
325
	/**
326
	* There are lots of possible Exif Groups and Values.
327
	* But you will never heard of the missing ones. so we just allow the most common ones.
328
	*/
329
	static private $allowed_groups		= array(
330
		'EXIF',
331
		'IFD0',
332
	);
333
334
	static private $allowed_keys		= array(
335
		'DateTimeOriginal',
336
		'FocalLength',
337
		'ExposureTime',
338
		'FNumber',
339
		'ISOSpeedRatings',
340
		'WhiteBalance',
341
		'Flash',
342
		'Model',
343
		'ExposureProgram',
344
		'ExposureBiasValue',
345
		'MeteringMode',
346
	);
347
}
348