MediaWiki REL1_31
XMPValidate.php
Go to the documentation of this file.
1<?php
24use Psr\Log\LoggerInterface;
25use Psr\Log\LoggerAwareInterface;
26use Wikimedia\Timestamp\ConvertibleTimestamp;
27
47class XMPValidate implements LoggerAwareInterface {
48
52 private $logger;
53
54 public function __construct( LoggerInterface $logger ) {
55 $this->setLogger( $logger );
56 }
57
58 public function setLogger( LoggerInterface $logger ) {
59 $this->logger = $logger;
60 }
68 public function validateBoolean( $info, &$val, $standalone ) {
69 if ( !$standalone ) {
70 // this only validates standalone properties, not arrays, etc
71 return;
72 }
73 if ( $val !== 'True' && $val !== 'False' ) {
74 $this->logger->info( __METHOD__ . " Expected True or False but got $val" );
75 $val = null;
76 }
77 }
78
86 public function validateRational( $info, &$val, $standalone ) {
87 if ( !$standalone ) {
88 // this only validates standalone properties, not arrays, etc
89 return;
90 }
91 if ( !preg_match( '/^(?:-?\d+)\/(?:\d+[1-9]|[1-9]\d*)$/D', $val ) ) {
92 $this->logger->info( __METHOD__ . " Expected rational but got $val" );
93 $val = null;
94 }
95 }
96
107 public function validateRating( $info, &$val, $standalone ) {
108 if ( !$standalone ) {
109 // this only validates standalone properties, not arrays, etc
110 return;
111 }
112 if ( !preg_match( '/^[-+]?\d*(?:\.?\d*)$/D', $val )
113 || !is_numeric( $val )
114 ) {
115 $this->logger->info( __METHOD__ . " Expected rating but got $val" );
116 $val = null;
117
118 return;
119 } else {
120 $nVal = (float)$val;
121 if ( $nVal < 0 ) {
122 // We do < 0 here instead of < -1 here, since
123 // the values between 0 and -1 are also illegal
124 // as -1 is meant as a special reject rating.
125 $this->logger->info( __METHOD__ . " Rating too low, setting to -1 (Rejected)" );
126 $val = '-1';
127
128 return;
129 }
130 if ( $nVal > 5 ) {
131 $this->logger->info( __METHOD__ . " Rating too high, setting to 5" );
132 $val = '5';
133
134 return;
135 }
136 }
137 }
138
146 public function validateInteger( $info, &$val, $standalone ) {
147 if ( !$standalone ) {
148 // this only validates standalone properties, not arrays, etc
149 return;
150 }
151 if ( !preg_match( '/^[-+]?\d+$/D', $val ) ) {
152 $this->logger->info( __METHOD__ . " Expected integer but got $val" );
153 $val = null;
154 }
155 }
156
165 public function validateClosed( $info, &$val, $standalone ) {
166 if ( !$standalone ) {
167 // this only validates standalone properties, not arrays, etc
168 return;
169 }
170
171 // check if its in a numeric range
172 $inRange = false;
173 if ( isset( $info['rangeLow'] )
174 && isset( $info['rangeHigh'] )
175 && is_numeric( $val )
176 && ( intval( $val ) <= $info['rangeHigh'] )
177 && ( intval( $val ) >= $info['rangeLow'] )
178 ) {
179 $inRange = true;
180 }
181
182 if ( !isset( $info['choices'][$val] ) && !$inRange ) {
183 $this->logger->info( __METHOD__ . " Expected closed choice, but got $val" );
184 $val = null;
185 }
186 }
187
195 public function validateFlash( $info, &$val, $standalone ) {
196 if ( $standalone ) {
197 // this only validates flash structs, not individual properties
198 return;
199 }
200 if ( !( isset( $val['Fired'] )
201 && isset( $val['Function'] )
202 && isset( $val['Mode'] )
203 && isset( $val['RedEyeMode'] )
204 && isset( $val['Return'] )
205 ) ) {
206 $this->logger->info( __METHOD__ . " Flash structure did not have all the required components" );
207 $val = null;
208 } else {
209 $val = ( 0 | ( $val['Fired'] === 'True' )
210 | ( intval( $val['Return'] ) << 1 )
211 | ( intval( $val['Mode'] ) << 3 )
212 | ( ( $val['Function'] === 'True' ) << 5 )
213 | ( ( $val['RedEyeMode'] === 'True' ) << 6 ) );
214 }
215 }
216
230 public function validateLangCode( $info, &$val, $standalone ) {
231 if ( !$standalone ) {
232 // this only validates standalone properties, not arrays, etc
233 return;
234 }
235 if ( !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $val ) ) {
236 // this is a rather naive check.
237 $this->logger->info( __METHOD__ . " Expected Lang code but got $val" );
238 $val = null;
239 }
240 }
241
259 public function validateDate( $info, &$val, $standalone ) {
260 if ( !$standalone ) {
261 // this only validates standalone properties, not arrays, etc
262 return;
263 }
264 $res = [];
265 if ( !preg_match(
266 /* ahh! scary regex... */
267 // phpcs:ignore Generic.Files.LineLength
268 '/^([0-3]\d{3})(?:-([01]\d)(?:-([0-3]\d)(?:T([0-2]\d):([0-6]\d)(?::([0-6]\d)(?:\.\d+)?)?([-+]\d{2}:\d{2}|Z)?)?)?)?$/D',
269 $val, $res )
270 ) {
271 $this->logger->info( __METHOD__ . " Expected date but got $val" );
272 $val = null;
273 } else {
274 /*
275 * $res is formatted as follows:
276 * 0 -> full date.
277 * 1 -> year, 2-> month, 3-> day, 4-> hour, 5-> minute, 6->second
278 * 7-> Timezone specifier (Z or something like +12:30 )
279 * many parts are optional, some aren't. For example if you specify
280 * minute, you must specify hour, day, month, and year but not second or TZ.
281 */
282
283 /*
284 * First of all, if year = 0000, Something is wrongish,
285 * so don't extract. This seems to happen when
286 * some programs convert between metadata formats.
287 */
288 if ( $res[1] === '0000' ) {
289 $this->logger->info( __METHOD__ . " Invalid date (year 0): $val" );
290 $val = null;
291
292 return;
293 }
294
295 if ( !isset( $res[4] ) ) { // hour
296 // just have the year month day (if that)
297 $val = $res[1];
298 if ( isset( $res[2] ) ) {
299 $val .= ':' . $res[2];
300 }
301 if ( isset( $res[3] ) ) {
302 $val .= ':' . $res[3];
303 }
304
305 return;
306 }
307
308 if ( !isset( $res[7] ) || $res[7] === 'Z' ) {
309 // if hour is set, then minute must also be or regex above will fail.
310 $val = $res[1] . ':' . $res[2] . ':' . $res[3]
311 . ' ' . $res[4] . ':' . $res[5];
312 if ( isset( $res[6] ) && $res[6] !== '' ) {
313 $val .= ':' . $res[6];
314 }
315
316 return;
317 }
318
319 // Extra check for empty string necessary due to TZ but no second case.
320 $stripSeconds = false;
321 if ( !isset( $res[6] ) || $res[6] === '' ) {
322 $res[6] = '00';
323 $stripSeconds = true;
324 }
325
326 // Do timezone processing. We've already done the case that tz = Z.
327
328 // We know that if we got to this step, year, month day hour and min must be set
329 // by virtue of regex not failing.
330
331 $unix = ConvertibleTimestamp::convert( TS_UNIX,
332 $res[1] . $res[2] . $res[3] . $res[4] . $res[5] . $res[6]
333 );
334 $offset = intval( substr( $res[7], 1, 2 ) ) * 60 * 60;
335 $offset += intval( substr( $res[7], 4, 2 ) ) * 60;
336 if ( substr( $res[7], 0, 1 ) === '-' ) {
337 $offset = -$offset;
338 }
339 $val = ConvertibleTimestamp::convert( TS_EXIF, $unix + $offset );
340
341 if ( $stripSeconds ) {
342 // If seconds weren't specified, remove the trailing ':00'.
343 $val = substr( $val, 0, -3 );
344 }
345 }
346 }
347
360 public function validateGPS( $info, &$val, $standalone ) {
361 if ( !$standalone ) {
362 return;
363 }
364
365 $m = [];
366 if ( preg_match(
367 '/(\d{1,3}),(\d{1,2}),(\d{1,2})([NWSE])/D',
368 $val, $m )
369 ) {
370 $coord = intval( $m[1] );
371 $coord += intval( $m[2] ) * ( 1 / 60 );
372 $coord += intval( $m[3] ) * ( 1 / 3600 );
373 if ( $m[4] === 'S' || $m[4] === 'W' ) {
374 $coord = -$coord;
375 }
376 $val = $coord;
377
378 return;
379 } elseif ( preg_match(
380 '/(\d{1,3}),(\d{1,2}(?:.\d*)?)([NWSE])/D',
381 $val, $m )
382 ) {
383 $coord = intval( $m[1] );
384 $coord += floatval( $m[2] ) * ( 1 / 60 );
385 if ( $m[3] === 'S' || $m[3] === 'W' ) {
386 $coord = -$coord;
387 }
388 $val = $coord;
389
390 return;
391 } else {
392 $this->logger->info( __METHOD__
393 . " Expected GPSCoordinate, but got $val." );
394 $val = null;
395
396 return;
397 }
398 }
399}
This contains some static methods for validating XMP properties.
validateRating( $info, &$val, $standalone)
function to validate rating properties -1, 0-5
validateInteger( $info, &$val, $standalone)
function to validate integers
validateClosed( $info, &$val, $standalone)
function to validate properties with a fixed number of allowed choices.
setLogger(LoggerInterface $logger)
validateBoolean( $info, &$val, $standalone)
Function to validate boolean properties ( True or False )
validateDate( $info, &$val, $standalone)
function to validate date properties, and convert to (partial) Exif format.
validateGPS( $info, &$val, $standalone)
function to validate, and more importantly translate the XMP DMS form of gps coords to the decimal fo...
__construct(LoggerInterface $logger)
LoggerInterface $logger
validateRational( $info, &$val, $standalone)
function to validate rational properties ( 12/10 )
validateFlash( $info, &$val, $standalone)
function to validate and modify flash structure
validateLangCode( $info, &$val, $standalone)
function to validate LangCode properties ( en-GB, etc )
$res
Definition database.txt:21