MediaWiki  1.23.14
XMP.php
Go to the documentation of this file.
1 <?php
49 class XMPReader {
51  protected $items;
52 
54  private $curItem = array();
55 
57  private $ancestorStruct = false;
58 
60  private $charContent = false;
61 
63  private $mode = array();
64 
66  private $results = array();
67 
69  private $processingArray = false;
70 
72  private $itemLang = false;
73 
75  private $xmlParser;
76 
78  private $charset = false;
79 
81  private $extendedXMPOffset = 0;
82 
84  private $parsable = 0;
85 
87  private $xmlParsableBuffer = '';
88 
98  const MODE_INITIAL = 0;
99  const MODE_IGNORE = 1;
100  const MODE_LI = 2;
101  const MODE_LI_LANG = 3;
102  const MODE_QDESC = 4;
103 
104  // The following MODE constants are also used in the
105  // $items array to denote what type of property the item is.
106  const MODE_SIMPLE = 10;
107  const MODE_STRUCT = 11; // structure (associative array)
108  const MODE_SEQ = 12; // ordered list
109  const MODE_BAG = 13; // unordered list
110  const MODE_LANG = 14;
111  const MODE_ALT = 15; // non-language alt. Currently not implemented, and not needed atm.
112  const MODE_BAGSTRUCT = 16; // A BAG of Structs.
113 
114  const NS_RDF = 'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
115  const NS_XML = 'http://www.w3.org/XML/1998/namespace';
116 
117  // States used while determining if XML is safe to parse
118  const PARSABLE_UNKNOWN = 0;
119  const PARSABLE_OK = 1;
120  const PARSABLE_BUFFERING = 2;
121  const PARSABLE_NO = 3;
122 
128  function __construct() {
129 
130  if ( !function_exists( 'xml_parser_create_ns' ) ) {
131  // this should already be checked by this point
132  throw new MWException( 'XMP support requires XML Parser' );
133  }
134 
135  $this->items = XMPInfo::getItems();
136 
137  $this->resetXMLParser();
138  }
139 
144  private function resetXMLParser() {
145 
146  if ( $this->xmlParser ) {
147  //is this needed?
148  xml_parser_free( $this->xmlParser );
149  }
150 
151  $this->xmlParser = xml_parser_create_ns( 'UTF-8', ' ' );
152  xml_parser_set_option( $this->xmlParser, XML_OPTION_CASE_FOLDING, 0 );
153  xml_parser_set_option( $this->xmlParser, XML_OPTION_SKIP_WHITE, 1 );
154 
155  xml_set_element_handler( $this->xmlParser,
156  array( $this, 'startElement' ),
157  array( $this, 'endElement' ) );
158 
159  xml_set_character_data_handler( $this->xmlParser, array( $this, 'char' ) );
160 
161  $this->parsable = self::PARSABLE_UNKNOWN;
162  $this->xmlParsableBuffer = '';
163  }
164 
169  function __destruct() {
170  // not sure if this is needed.
171  xml_parser_free( $this->xmlParser );
172  }
173 
177  public static function isSupported() {
178  return function_exists( 'xml_parser_create_ns' ) && class_exists( 'XMLReader' );
179  }
180 
187  public function getResults() {
188  // xmp-special is for metadata that affects how stuff
189  // is extracted. For example xmpNote:HasExtendedXMP.
190 
191  // It is also used to handle photoshop:AuthorsPosition
192  // which is weird and really part of another property,
193  // see 2:85 in IPTC. See also pg 21 of IPTC4XMP standard.
194  // The location fields also use it.
195 
196  $data = $this->results;
197 
198  wfRunHooks( 'XMPGetResults', array( &$data ) );
199 
200  if ( isset( $data['xmp-special']['AuthorsPosition'] )
201  && is_string( $data['xmp-special']['AuthorsPosition'] )
202  && isset( $data['xmp-general']['Artist'][0] )
203  ) {
204  // Note, if there is more than one creator,
205  // this only applies to first. This also will
206  // only apply to the dc:Creator prop, not the
207  // exif:Artist prop.
208 
209  $data['xmp-general']['Artist'][0] =
210  $data['xmp-special']['AuthorsPosition'] . ', '
211  . $data['xmp-general']['Artist'][0];
212  }
213 
214  // Go through the LocationShown and LocationCreated
215  // changing it to the non-hierarchal form used by
216  // the other location fields.
217 
218  if ( isset( $data['xmp-special']['LocationShown'][0] )
219  && is_array( $data['xmp-special']['LocationShown'][0] )
220  ) {
221  // the is_array is just paranoia. It should always
222  // be an array.
223  foreach ( $data['xmp-special']['LocationShown'] as $loc ) {
224  if ( !is_array( $loc ) ) {
225  // To avoid copying over the _type meta-fields.
226  continue;
227  }
228  foreach ( $loc as $field => $val ) {
229  $data['xmp-general'][$field . 'Dest'][] = $val;
230  }
231  }
232  }
233  if ( isset( $data['xmp-special']['LocationCreated'][0] )
234  && is_array( $data['xmp-special']['LocationCreated'][0] )
235  ) {
236  // the is_array is just paranoia. It should always
237  // be an array.
238  foreach ( $data['xmp-special']['LocationCreated'] as $loc ) {
239  if ( !is_array( $loc ) ) {
240  // To avoid copying over the _type meta-fields.
241  continue;
242  }
243  foreach ( $loc as $field => $val ) {
244  $data['xmp-general'][$field . 'Created'][] = $val;
245  }
246  }
247  }
248 
249  // We don't want to return the special values, since they're
250  // special and not info to be stored about the file.
251  unset( $data['xmp-special'] );
252 
253  // Convert GPSAltitude to negative if below sea level.
254  if ( isset( $data['xmp-exif']['GPSAltitudeRef'] )
255  && isset( $data['xmp-exif']['GPSAltitude'] )
256  ) {
257 
258  // Must convert to a real before multiplying by -1
259  // XMPValidate guarantees there will always be a '/' in this value.
260  list( $nom, $denom ) = explode( '/', $data['xmp-exif']['GPSAltitude'] );
261  $data['xmp-exif']['GPSAltitude'] = $nom / $denom;
262 
263  if ( $data['xmp-exif']['GPSAltitudeRef'] == '1' ) {
264  $data['xmp-exif']['GPSAltitude'] *= -1;
265  }
266  unset( $data['xmp-exif']['GPSAltitudeRef'] );
267  }
268 
269  return $data;
270  }
271 
285  public function parse( $content, $allOfIt = true, $reset = false ) {
286  if ( $reset ) {
287  $this->resetXMLParser();
288  }
289  try {
290 
291  // detect encoding by looking for BOM which is supposed to be in processing instruction.
292  // see page 12 of http://www.adobe.com/devnet/xmp/pdfs/XMPSpecificationPart3.pdf
293  if ( !$this->charset ) {
294  $bom = array();
295  if ( preg_match( '/\xEF\xBB\xBF|\xFE\xFF|\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\xFF\xFE/',
296  $content, $bom )
297  ) {
298  switch ( $bom[0] ) {
299  case "\xFE\xFF":
300  $this->charset = 'UTF-16BE';
301  break;
302  case "\xFF\xFE":
303  $this->charset = 'UTF-16LE';
304  break;
305  case "\x00\x00\xFE\xFF":
306  $this->charset = 'UTF-32BE';
307  break;
308  case "\xFF\xFE\x00\x00":
309  $this->charset = 'UTF-32LE';
310  break;
311  case "\xEF\xBB\xBF":
312  $this->charset = 'UTF-8';
313  break;
314  default:
315  //this should be impossible to get to
316  throw new MWException( "Invalid BOM" );
317  }
318  } else {
319  // standard specifically says, if no bom assume utf-8
320  $this->charset = 'UTF-8';
321  }
322  }
323  if ( $this->charset !== 'UTF-8' ) {
324  //don't convert if already utf-8
326  $content = iconv( $this->charset, 'UTF-8//IGNORE', $content );
328  }
329 
330  // Ensure the XMP block does not have an xml doctype declaration, which
331  // could declare entities unsafe to parse with xml_parse (T85848/T71210).
332  if ( $this->parsable !== self::PARSABLE_OK ) {
333  if ( $this->parsable === self::PARSABLE_NO ) {
334  throw new MWException( 'Unsafe doctype declaration in XML.' );
335  }
336 
337  $content = $this->xmlParsableBuffer . $content;
338  if ( !$this->checkParseSafety( $content ) ) {
339  if ( !$allOfIt && $this->parsable !== self::PARSABLE_NO ) {
340  // parse wasn't Unsuccessful yet, so return true
341  // in this case.
342  return true;
343  }
344  $msg = ( $this->parsable === self::PARSABLE_NO ) ?
345  'Unsafe doctype declaration in XML.' :
346  'No root element found in XML.';
347  throw new MWException( $msg );
348  }
349  }
350 
351  $ok = xml_parse( $this->xmlParser, $content, $allOfIt );
352  if ( !$ok ) {
353  $error = xml_error_string( xml_get_error_code( $this->xmlParser ) );
354  $where = 'line: ' . xml_get_current_line_number( $this->xmlParser )
355  . ' column: ' . xml_get_current_column_number( $this->xmlParser )
356  . ' byte offset: ' . xml_get_current_byte_index( $this->xmlParser );
357 
358  wfDebugLog( 'XMP', "XMPReader::parse : Error reading XMP content: $error ($where)" );
359  $this->results = array(); // blank if error.
360  return false;
361  }
362  } catch ( MWException $e ) {
363  wfDebugLog( 'XMP', 'XMP parse error: ' . $e );
364  $this->results = array();
365 
366  return false;
367  }
368 
369  return true;
370  }
371 
379  public function parseExtended( $content ) {
380  // @todo FIXME: This is untested. Hard to find example files
381  // or programs that make such files..
382  $guid = substr( $content, 0, 32 );
383  if ( !isset( $this->results['xmp-special']['HasExtendedXMP'] )
384  || $this->results['xmp-special']['HasExtendedXMP'] !== $guid
385  ) {
386  wfDebugLog( 'XMP', __METHOD__ .
387  " Ignoring XMPExtended block due to wrong guid (guid= '$guid')" );
388 
389  return false;
390  }
391  $len = unpack( 'Nlength/Noffset', substr( $content, 32, 8 ) );
392 
393  if ( !$len || $len['length'] < 4 || $len['offset'] < 0 || $len['offset'] > $len['length'] ) {
394  wfDebugLog( 'XMP', __METHOD__ . 'Error reading extended XMP block, invalid length or offset.' );
395 
396  return false;
397  }
398 
399  // we're not very robust here. we should accept it in the wrong order.
400  // To quote the XMP standard:
401  // "A JPEG writer should write the ExtendedXMP marker segments in order,
402  // immediately following the StandardXMP. However, the JPEG standard
403  // does not require preservation of marker segment order. A robust JPEG
404  // reader should tolerate the marker segments in any order."
405  //
406  // otoh the probability that an image will have more than 128k of
407  // metadata is rather low... so the probability that it will have
408  // > 128k, and be in the wrong order is very low...
409 
410  if ( $len['offset'] !== $this->extendedXMPOffset ) {
411  wfDebugLog( 'XMP', __METHOD__ . 'Ignoring XMPExtended block due to wrong order. (Offset was '
412  . $len['offset'] . ' but expected ' . $this->extendedXMPOffset . ')' );
413 
414  return false;
415  }
416 
417  if ( $len['offset'] === 0 ) {
418  // if we're starting the extended block, we've probably already
419  // done the XMPStandard block, so reset.
420  $this->resetXMLParser();
421  }
422 
423  $this->extendedXMPOffset += $len['length'];
424 
425  $actualContent = substr( $content, 40 );
426 
427  if ( $this->extendedXMPOffset === strlen( $actualContent ) ) {
428  $atEnd = true;
429  } else {
430  $atEnd = false;
431  }
432 
433  wfDebugLog( 'XMP', __METHOD__ . 'Parsing a XMPExtended block' );
434 
435  return $this->parse( $actualContent, $atEnd );
436  }
437 
454  function char( $parser, $data ) {
455 
456  $data = trim( $data );
457  if ( trim( $data ) === "" ) {
458  return;
459  }
460 
461  if ( !isset( $this->mode[0] ) ) {
462  throw new MWException( 'Unexpected character data before first rdf:Description element' );
463  }
464 
465  if ( $this->mode[0] === self::MODE_IGNORE ) {
466  return;
467  }
468 
469  if ( $this->mode[0] !== self::MODE_SIMPLE
470  && $this->mode[0] !== self::MODE_QDESC
471  ) {
472  throw new MWException( 'character data where not expected. (mode ' . $this->mode[0] . ')' );
473  }
474 
475  // to check, how does this handle w.s.
476  if ( $this->charContent === false ) {
477  $this->charContent = $data;
478  } else {
479  $this->charContent .= $data;
480  }
481  }
482 
491  private function checkParseSafety( $content ) {
492  $reader = new XMLReader();
493  $result = null;
494 
495  // For XMLReader to parse incomplete/invalid XML, it has to be open()'ed
496  // instead of using XML().
497  $reader->open(
498  'data://text/plain,' . urlencode( $content ),
499  null,
500  LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET
501  );
502 
503  $oldDisable = libxml_disable_entity_loader( true );
504  $reader->setParserProperty( XMLReader::SUBST_ENTITIES, false );
505 
506  // Even with LIBXML_NOWARNING set, XMLReader::read gives a warning
507  // when parsing truncated XML, which causes unit tests to fail.
509  while ( $reader->read() ) {
510  if ( $reader->nodeType === XMLReader::ELEMENT ) {
511  // Reached the first element without hitting a doctype declaration
512  $this->parsable = self::PARSABLE_OK;
513  $result = true;
514  break;
515  }
516  if ( $reader->nodeType === XMLReader::DOC_TYPE ) {
517  $this->parsable = self::PARSABLE_NO;
518  $result = false;
519  break;
520  }
521  }
523  libxml_disable_entity_loader( $oldDisable );
524 
525  if ( !is_null( $result ) ) {
526  return $result;
527  }
528 
529  // Reached the end of the parsable xml without finding an element
530  // or doctype. Buffer and try again.
531  $this->parsable = self::PARSABLE_BUFFERING;
532  $this->xmlParsableBuffer = $content;
533  return false;
534  }
535 
542  private function endElementModeIgnore( $elm ) {
543  if ( $this->curItem[0] === $elm ) {
544  array_shift( $this->curItem );
545  array_shift( $this->mode );
546  }
547  }
548 
564  private function endElementModeSimple( $elm ) {
565  if ( $this->charContent !== false ) {
566  if ( $this->processingArray ) {
567  // if we're processing an array, use the original element
568  // name instead of rdf:li.
569  list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 );
570  } else {
571  list( $ns, $tag ) = explode( ' ', $elm, 2 );
572  }
573  $this->saveValue( $ns, $tag, $this->charContent );
574 
575  $this->charContent = false; // reset
576  }
577  array_shift( $this->curItem );
578  array_shift( $this->mode );
579  }
580 
599  private function endElementNested( $elm ) {
600 
601  /* cur item must be the same as $elm, unless if in MODE_STRUCT
602  in which case it could also be rdf:Description */
603  if ( $this->curItem[0] !== $elm
604  && !( $elm === self::NS_RDF . ' Description'
605  && $this->mode[0] === self::MODE_STRUCT )
606  ) {
607  throw new MWException( "nesting mismatch. got a </$elm> but expected a </" .
608  $this->curItem[0] . '>' );
609  }
610 
611  // Validate structures.
612  list( $ns, $tag ) = explode( ' ', $elm, 2 );
613  if ( isset( $this->items[$ns][$tag]['validate'] ) ) {
614 
615  $info =& $this->items[$ns][$tag];
616  $finalName = isset( $info['map_name'] )
617  ? $info['map_name'] : $tag;
618 
619  $validate = is_array( $info['validate'] ) ? $info['validate']
620  : array( 'XMPValidate', $info['validate'] );
621 
622  if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) {
623  // This can happen if all the members of the struct failed validation.
624  wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> has no valid members." );
625  } elseif ( is_callable( $validate ) ) {
626  $val =& $this->results['xmp-' . $info['map_group']][$finalName];
627  call_user_func_array( $validate, array( $info, &$val, false ) );
628  if ( is_null( $val ) ) {
629  // the idea being the validation function will unset the variable if
630  // its invalid.
631  wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> failed validation." );
632  unset( $this->results['xmp-' . $info['map_group']][$finalName] );
633  }
634  } else {
635  wfDebugLog( 'XMP', __METHOD__ . " Validation function for $finalName ("
636  . $validate[0] . '::' . $validate[1] . '()) is not callable.' );
637  }
638  }
639 
640  array_shift( $this->curItem );
641  array_shift( $this->mode );
642  $this->ancestorStruct = false;
643  $this->processingArray = false;
644  $this->itemLang = false;
645  }
646 
666  private function endElementModeLi( $elm ) {
667 
668  list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 );
669  $info = $this->items[$ns][$tag];
670  $finalName = isset( $info['map_name'] )
671  ? $info['map_name'] : $tag;
672 
673  array_shift( $this->mode );
674 
675  if ( !isset( $this->results['xmp-' . $info['map_group']][$finalName] ) ) {
676  wfDebugLog( 'XMP', __METHOD__ . " Empty compund element $finalName." );
677 
678  return;
679  }
680 
681  if ( $elm === self::NS_RDF . ' Seq' ) {
682  $this->results['xmp-' . $info['map_group']][$finalName]['_type'] = 'ol';
683  } elseif ( $elm === self::NS_RDF . ' Bag' ) {
684  $this->results['xmp-' . $info['map_group']][$finalName]['_type'] = 'ul';
685  } elseif ( $elm === self::NS_RDF . ' Alt' ) {
686  // extra if needed as you could theoretically have a non-language alt.
687  if ( $info['mode'] === self::MODE_LANG ) {
688  $this->results['xmp-' . $info['map_group']][$finalName]['_type'] = 'lang';
689  }
690  } else {
691  throw new MWException( __METHOD__ . " expected </rdf:seq> or </rdf:bag> but instead got $elm." );
692  }
693  }
694 
705  private function endElementModeQDesc( $elm ) {
706 
707  if ( $elm === self::NS_RDF . ' value' ) {
708  list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 );
709  $this->saveValue( $ns, $tag, $this->charContent );
710 
711  return;
712  } else {
713  array_shift( $this->mode );
714  array_shift( $this->curItem );
715  }
716  }
717 
731  function endElement( $parser, $elm ) {
732  if ( $elm === ( self::NS_RDF . ' RDF' )
733  || $elm === 'adobe:ns:meta/ xmpmeta'
734  || $elm === 'adobe:ns:meta/ xapmeta'
735  ) {
736  // ignore these.
737  return;
738  }
739 
740  if ( $elm === self::NS_RDF . ' type' ) {
741  // these aren't really supported properly yet.
742  // However, it appears they almost never used.
743  wfDebugLog( 'XMP', __METHOD__ . ' encountered <rdf:type>' );
744  }
745 
746  if ( strpos( $elm, ' ' ) === false ) {
747  // This probably shouldn't happen.
748  // However, there is a bug in an adobe product
749  // that forgets the namespace on some things.
750  // (Luckily they are unimportant things).
751  wfDebugLog( 'XMP', __METHOD__ . " Encountered </$elm> which has no namespace. Skipping." );
752 
753  return;
754  }
755 
756  if ( count( $this->mode[0] ) === 0 ) {
757  // This should never ever happen and means
758  // there is a pretty major bug in this class.
759  throw new MWException( 'Encountered end element with no mode' );
760  }
761 
762  if ( count( $this->curItem ) == 0 && $this->mode[0] !== self::MODE_INITIAL ) {
763  // just to be paranoid. Should always have a curItem, except for initially
764  // (aka during MODE_INITAL).
765  throw new MWException( "Hit end element </$elm> but no curItem" );
766  }
767 
768  switch ( $this->mode[0] ) {
769  case self::MODE_IGNORE:
770  $this->endElementModeIgnore( $elm );
771  break;
772  case self::MODE_SIMPLE:
773  $this->endElementModeSimple( $elm );
774  break;
775  case self::MODE_STRUCT:
776  case self::MODE_SEQ:
777  case self::MODE_BAG:
778  case self::MODE_LANG:
780  $this->endElementNested( $elm );
781  break;
782  case self::MODE_INITIAL:
783  if ( $elm === self::NS_RDF . ' Description' ) {
784  array_shift( $this->mode );
785  } else {
786  throw new MWException( 'Element ended unexpectedly while in MODE_INITIAL' );
787  }
788  break;
789  case self::MODE_LI:
790  case self::MODE_LI_LANG:
791  $this->endElementModeLi( $elm );
792  break;
793  case self::MODE_QDESC:
794  $this->endElementModeQDesc( $elm );
795  break;
796  default:
797  wfDebugLog( 'XMP', __METHOD__ . " no mode (elm = $elm)" );
798  break;
799  }
800  }
801 
813  private function startElementModeIgnore( $elm ) {
814  if ( $elm === $this->curItem[0] ) {
815  array_unshift( $this->curItem, $elm );
816  array_unshift( $this->mode, self::MODE_IGNORE );
817  }
818  }
819 
827  private function startElementModeBag( $elm ) {
828  if ( $elm === self::NS_RDF . ' Bag' ) {
829  array_unshift( $this->mode, self::MODE_LI );
830  } else {
831  throw new MWException( "Expected <rdf:Bag> but got $elm." );
832  }
833  }
834 
842  private function startElementModeSeq( $elm ) {
843  if ( $elm === self::NS_RDF . ' Seq' ) {
844  array_unshift( $this->mode, self::MODE_LI );
845  } elseif ( $elm === self::NS_RDF . ' Bag' ) {
846  # bug 27105
847  wfDebugLog( 'XMP', __METHOD__ . ' Expected an rdf:Seq, but got an rdf:Bag. Pretending'
848  . ' it is a Seq, since some buggy software is known to screw this up.' );
849  array_unshift( $this->mode, self::MODE_LI );
850  } else {
851  throw new MWException( "Expected <rdf:Seq> but got $elm." );
852  }
853  }
854 
869  private function startElementModeLang( $elm ) {
870  if ( $elm === self::NS_RDF . ' Alt' ) {
871  array_unshift( $this->mode, self::MODE_LI_LANG );
872  } else {
873  throw new MWException( "Expected <rdf:Seq> but got $elm." );
874  }
875  }
876 
895  private function startElementModeSimple( $elm, $attribs ) {
896  if ( $elm === self::NS_RDF . ' Description' ) {
897  // If this value has qualifiers
898  array_unshift( $this->mode, self::MODE_QDESC );
899  array_unshift( $this->curItem, $this->curItem[0] );
900 
901  if ( isset( $attribs[self::NS_RDF . ' value'] ) ) {
902  list( $ns, $tag ) = explode( ' ', $this->curItem[0], 2 );
903  $this->saveValue( $ns, $tag, $attribs[self::NS_RDF . ' value'] );
904  }
905  } elseif ( $elm === self::NS_RDF . ' value' ) {
906  // This should not be here.
907  throw new MWException( __METHOD__ . ' Encountered <rdf:value> where it was unexpected.' );
908  } else {
909  // something else we don't recognize, like a qualifier maybe.
910  wfDebugLog( 'XMP', __METHOD__ .
911  " Encountered element <$elm> where only expecting character data as value of " .
912  $this->curItem[0] );
913  array_unshift( $this->mode, self::MODE_IGNORE );
914  array_unshift( $this->curItem, $elm );
915  }
916  }
917 
932  private function startElementModeQDesc( $elm ) {
933  if ( $elm === self::NS_RDF . ' value' ) {
934  return; // do nothing
935  } else {
936  // otherwise its a qualifier, which we ignore
937  array_unshift( $this->mode, self::MODE_IGNORE );
938  array_unshift( $this->curItem, $elm );
939  }
940  }
941 
954  private function startElementModeInitial( $ns, $tag, $attribs ) {
955  if ( $ns !== self::NS_RDF ) {
956 
957  if ( isset( $this->items[$ns][$tag] ) ) {
958  if ( isset( $this->items[$ns][$tag]['structPart'] ) ) {
959  // If this element is supposed to appear only as
960  // a child of a structure, but appears here (not as
961  // a child of a struct), then something weird is
962  // happening, so ignore this element and its children.
963 
964  wfDebugLog( 'XMP', "Encountered <$ns:$tag> outside"
965  . " of its expected parent. Ignoring." );
966 
967  array_unshift( $this->mode, self::MODE_IGNORE );
968  array_unshift( $this->curItem, $ns . ' ' . $tag );
969 
970  return;
971  }
972  $mode = $this->items[$ns][$tag]['mode'];
973  array_unshift( $this->mode, $mode );
974  array_unshift( $this->curItem, $ns . ' ' . $tag );
975  if ( $mode === self::MODE_STRUCT ) {
976  $this->ancestorStruct = isset( $this->items[$ns][$tag]['map_name'] )
977  ? $this->items[$ns][$tag]['map_name'] : $tag;
978  }
979  if ( $this->charContent !== false ) {
980  // Something weird.
981  // Should not happen in valid XMP.
982  throw new MWException( 'tag nested in non-whitespace characters.' );
983  }
984  } else {
985  // This element is not on our list of allowed elements so ignore.
986  wfDebugLog( 'XMP', __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
987  array_unshift( $this->mode, self::MODE_IGNORE );
988  array_unshift( $this->curItem, $ns . ' ' . $tag );
989 
990  return;
991  }
992  }
993  // process attributes
994  $this->doAttribs( $attribs );
995  }
996 
1016  private function startElementModeStruct( $ns, $tag, $attribs ) {
1017  if ( $ns !== self::NS_RDF ) {
1018 
1019  if ( isset( $this->items[$ns][$tag] ) ) {
1020  if ( isset( $this->items[$ns][$this->ancestorStruct]['children'] )
1021  && !isset( $this->items[$ns][$this->ancestorStruct]['children'][$tag] )
1022  ) {
1023  // This assumes that we don't have inter-namespace nesting
1024  // which we don't in all the properties we're interested in.
1025  throw new MWException( " <$tag> appeared nested in <" . $this->ancestorStruct
1026  . "> where it is not allowed." );
1027  }
1028  array_unshift( $this->mode, $this->items[$ns][$tag]['mode'] );
1029  array_unshift( $this->curItem, $ns . ' ' . $tag );
1030  if ( $this->charContent !== false ) {
1031  // Something weird.
1032  // Should not happen in valid XMP.
1033  throw new MWException( "tag <$tag> nested in non-whitespace characters (" .
1034  $this->charContent . ")." );
1035  }
1036  } else {
1037  array_unshift( $this->mode, self::MODE_IGNORE );
1038  array_unshift( $this->curItem, $elm );
1039 
1040  return;
1041  }
1042  }
1043 
1044  if ( $ns === self::NS_RDF && $tag === 'Description' ) {
1045  $this->doAttribs( $attribs );
1046  array_unshift( $this->mode, self::MODE_STRUCT );
1047  array_unshift( $this->curItem, $this->curItem[0] );
1048  }
1049  }
1050 
1064  private function startElementModeLi( $elm, $attribs ) {
1065  if ( ( $elm ) !== self::NS_RDF . ' li' ) {
1066  throw new MWException( "<rdf:li> expected but got $elm." );
1067  }
1068 
1069  if ( !isset( $this->mode[1] ) ) {
1070  // This should never ever ever happen. Checking for it
1071  // to be paranoid.
1072  throw new MWException( 'In mode Li, but no 2xPrevious mode!' );
1073  }
1074 
1075  if ( $this->mode[1] === self::MODE_BAGSTRUCT ) {
1076  // This list item contains a compound (STRUCT) value.
1077  array_unshift( $this->mode, self::MODE_STRUCT );
1078  array_unshift( $this->curItem, $elm );
1079  $this->processingArray = true;
1080 
1081  if ( !isset( $this->curItem[1] ) ) {
1082  // be paranoid.
1083  throw new MWException( 'Can not find parent of BAGSTRUCT.' );
1084  }
1085  list( $curNS, $curTag ) = explode( ' ', $this->curItem[1] );
1086  $this->ancestorStruct = isset( $this->items[$curNS][$curTag]['map_name'] )
1087  ? $this->items[$curNS][$curTag]['map_name'] : $curTag;
1088 
1089  $this->doAttribs( $attribs );
1090  } else {
1091  // Normal BAG or SEQ containing simple values.
1092  array_unshift( $this->mode, self::MODE_SIMPLE );
1093  // need to add curItem[0] on again since one is for the specific item
1094  // and one is for the entire group.
1095  array_unshift( $this->curItem, $this->curItem[0] );
1096  $this->processingArray = true;
1097  }
1098  }
1099 
1114  private function startElementModeLiLang( $elm, $attribs ) {
1115  if ( $elm !== self::NS_RDF . ' li' ) {
1116  throw new MWException( __METHOD__ . " <rdf:li> expected but got $elm." );
1117  }
1118  if ( !isset( $attribs[self::NS_XML . ' lang'] )
1119  || !preg_match( '/^[-A-Za-z0-9]{2,}$/D', $attribs[self::NS_XML . ' lang'] )
1120  ) {
1121  throw new MWException( __METHOD__
1122  . " <rdf:li> did not contain, or has invalid xml:lang attribute in lang alternative" );
1123  }
1124 
1125  // Lang is case-insensitive.
1126  $this->itemLang = strtolower( $attribs[self::NS_XML . ' lang'] );
1127 
1128  // need to add curItem[0] on again since one is for the specific item
1129  // and one is for the entire group.
1130  array_unshift( $this->curItem, $this->curItem[0] );
1131  array_unshift( $this->mode, self::MODE_SIMPLE );
1132  $this->processingArray = true;
1133  }
1134 
1145  function startElement( $parser, $elm, $attribs ) {
1146 
1147  if ( $elm === self::NS_RDF . ' RDF'
1148  || $elm === 'adobe:ns:meta/ xmpmeta'
1149  || $elm === 'adobe:ns:meta/ xapmeta'
1150  ) {
1151  /* ignore. */
1152  return;
1153  } elseif ( $elm === self::NS_RDF . ' Description' ) {
1154  if ( count( $this->mode ) === 0 ) {
1155  // outer rdf:desc
1156  array_unshift( $this->mode, self::MODE_INITIAL );
1157  }
1158  } elseif ( $elm === self::NS_RDF . ' type' ) {
1159  // This doesn't support rdf:type properly.
1160  // In practise I have yet to see a file that
1161  // uses this element, however it is mentioned
1162  // on page 25 of part 1 of the xmp standard.
1163  //
1164  // also it seems as if exiv2 and exiftool do not support
1165  // this either (That or I misunderstand the standard)
1166  wfDebugLog( 'XMP', __METHOD__ . ' Encountered <rdf:type> which isn\'t currently supported' );
1167  }
1168 
1169  if ( strpos( $elm, ' ' ) === false ) {
1170  // This probably shouldn't happen.
1171  wfDebugLog( 'XMP', __METHOD__ . " Encountered <$elm> which has no namespace. Skipping." );
1172 
1173  return;
1174  }
1175 
1176  list( $ns, $tag ) = explode( ' ', $elm, 2 );
1177 
1178  if ( count( $this->mode ) === 0 ) {
1179  // This should not happen.
1180  throw new MWException( 'Error extracting XMP, '
1181  . "encountered <$elm> with no mode" );
1182  }
1183 
1184  switch ( $this->mode[0] ) {
1185  case self::MODE_IGNORE:
1186  $this->startElementModeIgnore( $elm );
1187  break;
1188  case self::MODE_SIMPLE:
1189  $this->startElementModeSimple( $elm, $attribs );
1190  break;
1191  case self::MODE_INITIAL:
1192  $this->startElementModeInitial( $ns, $tag, $attribs );
1193  break;
1194  case self::MODE_STRUCT:
1195  $this->startElementModeStruct( $ns, $tag, $attribs );
1196  break;
1197  case self::MODE_BAG:
1198  case self::MODE_BAGSTRUCT:
1199  $this->startElementModeBag( $elm );
1200  break;
1201  case self::MODE_SEQ:
1202  $this->startElementModeSeq( $elm );
1203  break;
1204  case self::MODE_LANG:
1205  $this->startElementModeLang( $elm );
1206  break;
1207  case self::MODE_LI_LANG:
1208  $this->startElementModeLiLang( $elm, $attribs );
1209  break;
1210  case self::MODE_LI:
1211  $this->startElementModeLi( $elm, $attribs );
1212  break;
1213  case self::MODE_QDESC:
1214  $this->startElementModeQDesc( $elm );
1215  break;
1216  default:
1217  throw new MWException( 'StartElement in unknown mode: ' . $this->mode[0] );
1218  }
1219  }
1220 
1221  // @codingStandardsIgnoreStart Long line that cannot be broken
1237  // @codingStandardsIgnoreEnd
1238  private function doAttribs( $attribs ) {
1239  // first check for rdf:parseType attribute, as that can change
1240  // how the attributes are interperted.
1241 
1242  if ( isset( $attribs[self::NS_RDF . ' parseType'] )
1243  && $attribs[self::NS_RDF . ' parseType'] === 'Resource'
1244  && $this->mode[0] === self::MODE_SIMPLE
1245  ) {
1246  // this is equivalent to having an inner rdf:Description
1247  $this->mode[0] = self::MODE_QDESC;
1248  }
1249  foreach ( $attribs as $name => $val ) {
1250  if ( strpos( $name, ' ' ) === false ) {
1251  // This shouldn't happen, but so far some old software forgets namespace
1252  // on rdf:about.
1253  wfDebugLog( 'XMP', __METHOD__ . ' Encountered non-namespaced attribute: '
1254  . " $name=\"$val\". Skipping. " );
1255  continue;
1256  }
1257  list( $ns, $tag ) = explode( ' ', $name, 2 );
1258  if ( $ns === self::NS_RDF ) {
1259  if ( $tag === 'value' || $tag === 'resource' ) {
1260  // resource is for url.
1261  // value attribute is a weird way of just putting the contents.
1262  $this->char( $this->xmlParser, $val );
1263  }
1264  } elseif ( isset( $this->items[$ns][$tag] ) ) {
1265  if ( $this->mode[0] === self::MODE_SIMPLE ) {
1266  throw new MWException( __METHOD__
1267  . " $ns:$tag found as attribute where not allowed" );
1268  }
1269  $this->saveValue( $ns, $tag, $val );
1270  } else {
1271  wfDebugLog( 'XMP', __METHOD__ . " Ignoring unrecognized element <$ns:$tag>." );
1272  }
1273  }
1274  }
1275 
1287  private function saveValue( $ns, $tag, $val ) {
1288 
1289  $info =& $this->items[$ns][$tag];
1290  $finalName = isset( $info['map_name'] )
1291  ? $info['map_name'] : $tag;
1292  if ( isset( $info['validate'] ) ) {
1293  $validate = is_array( $info['validate'] ) ? $info['validate']
1294  : array( 'XMPValidate', $info['validate'] );
1295 
1296  if ( is_callable( $validate ) ) {
1297  call_user_func_array( $validate, array( $info, &$val, true ) );
1298  // the reasoning behind using &$val instead of using the return value
1299  // is to be consistent between here and validating structures.
1300  if ( is_null( $val ) ) {
1301  wfDebugLog( 'XMP', __METHOD__ . " <$ns:$tag> failed validation." );
1302 
1303  return;
1304  }
1305  } else {
1306  wfDebugLog( 'XMP', __METHOD__ . " Validation function for $finalName ("
1307  . $validate[0] . '::' . $validate[1] . '()) is not callable.' );
1308  }
1309  }
1310 
1311  if ( $this->ancestorStruct && $this->processingArray ) {
1312  // Aka both an array and a struct. ( self::MODE_BAGSTRUCT )
1313  $this->results['xmp-' . $info['map_group']][$this->ancestorStruct][][$finalName] = $val;
1314  } elseif ( $this->ancestorStruct ) {
1315  $this->results['xmp-' . $info['map_group']][$this->ancestorStruct][$finalName] = $val;
1316  } elseif ( $this->processingArray ) {
1317  if ( $this->itemLang === false ) {
1318  // normal array
1319  $this->results['xmp-' . $info['map_group']][$finalName][] = $val;
1320  } else {
1321  // lang array.
1322  $this->results['xmp-' . $info['map_group']][$finalName][$this->itemLang] = $val;
1323  }
1324  } else {
1325  $this->results['xmp-' . $info['map_group']][$finalName] = $val;
1326  }
1327  }
1328 }
XMPReader\endElement
endElement( $parser, $elm)
Handler for hitting a closing element.
Definition: XMP.php:718
$result
The index of the header message $result[1]=The index of the body text message $result[2 through n]=Parameters passed to body text message. Please note the header message cannot receive/use parameters. 'ImportHandleLogItemXMLTag':When parsing a XML tag in a log item. $reader:XMLReader object $logInfo:Array of information Return false to stop further processing of the tag 'ImportHandlePageXMLTag':When parsing a XML tag in a page. $reader:XMLReader object $pageInfo:Array of information Return false to stop further processing of the tag 'ImportHandleRevisionXMLTag':When parsing a XML tag in a page revision. $reader:XMLReader object $pageInfo:Array of page information $revisionInfo:Array of revision information Return false to stop further processing of the tag 'ImportHandleToplevelXMLTag':When parsing a top level XML tag. $reader:XMLReader object Return false to stop further processing of the tag 'ImportHandleUploadXMLTag':When parsing a XML tag in a file upload. $reader:XMLReader object $revisionInfo:Array of information Return false to stop further processing of the tag 'InfoAction':When building information to display on the action=info page. $context:IContextSource object & $pageInfo:Array of information 'InitializeArticleMaybeRedirect':MediaWiki check to see if title is a redirect. $title:Title object for the current page $request:WebRequest $ignoreRedirect:boolean to skip redirect check $target:Title/string of redirect target $article:Article object 'InterwikiLoadPrefix':When resolving if a given prefix is an interwiki or not. Return true without providing an interwiki to continue interwiki search. $prefix:interwiki prefix we are looking for. & $iwData:output array describing the interwiki with keys iw_url, iw_local, iw_trans and optionally iw_api and iw_wikiid. 'InternalParseBeforeSanitize':during Parser 's internalParse method just before the parser removes unwanted/dangerous HTML tags and after nowiki/noinclude/includeonly/onlyinclude and other processings. Ideal for syntax-extensions after template/parser function execution which respect nowiki and HTML-comments. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InternalParseBeforeLinks':during Parser 's internalParse method before links but after nowiki/noinclude/includeonly/onlyinclude and other processings. & $parser:Parser object & $text:string containing partially parsed text & $stripState:Parser 's internal StripState object 'InvalidateEmailComplete':Called after a user 's email has been invalidated successfully. $user:user(object) whose email is being invalidated 'IRCLineURL':When constructing the URL to use in an IRC notification. Callee may modify $url and $query, URL will be constructed as $url . $query & $url:URL to index.php & $query:Query string $rc:RecentChange object that triggered url generation 'IsFileCacheable':Override the result of Article::isFileCacheable()(if true) $article:article(object) being checked 'IsTrustedProxy':Override the result of wfIsTrustedProxy() $ip:IP being check $result:Change this value to override the result of wfIsTrustedProxy() 'IsUploadAllowedFromUrl':Override the result of UploadFromUrl::isAllowedUrl() $url:URL used to upload from & $allowed:Boolean indicating if uploading is allowed for given URL 'isValidEmailAddr':Override the result of User::isValidEmailAddr(), for instance to return false if the domain name doesn 't match your organization. $addr:The e-mail address entered by the user & $result:Set this and return false to override the internal checks 'isValidPassword':Override the result of User::isValidPassword() $password:The password entered by the user & $result:Set this and return false to override the internal checks $user:User the password is being validated for 'Language::getMessagesFileName':$code:The language code or the language we 're looking for a messages file for & $file:The messages file path, you can override this to change the location. 'LanguageGetNamespaces':Provide custom ordering for namespaces or remove namespaces. Do not use this hook to add namespaces. Use CanonicalNamespaces for that. & $namespaces:Array of namespaces indexed by their numbers 'LanguageGetMagic':DEPRECATED, use $magicWords in a file listed in $wgExtensionMessagesFiles instead. Use this to define synonyms of magic words depending of the language $magicExtensions:associative array of magic words synonyms $lang:language code(string) 'LanguageGetSpecialPageAliases':DEPRECATED, use $specialPageAliases in a file listed in $wgExtensionMessagesFiles instead. Use to define aliases of special pages names depending of the language $specialPageAliases:associative array of magic words synonyms $lang:language code(string) 'LanguageGetTranslatedLanguageNames':Provide translated language names. & $names:array of language code=> language name $code language of the preferred translations 'LanguageLinks':Manipulate a page 's language links. This is called in various places to allow extensions to define the effective language links for a page. $title:The page 's Title. & $links:Associative array mapping language codes to prefixed links of the form "language:title". & $linkFlags:Associative array mapping prefixed links to arrays of flags. Currently unused, but planned to provide support for marking individual language links in the UI, e.g. for featured articles. 'LinkBegin':Used when generating internal and interwiki links in Linker::link(), before processing starts. Return false to skip default processing and return $ret. See documentation for Linker::link() for details on the expected meanings of parameters. $skin:the Skin object $target:the Title that the link is pointing to & $html:the contents that the< a > tag should have(raw HTML) $result
Definition: hooks.txt:1528
XMPReader\MODE_BAGSTRUCT
const MODE_BAGSTRUCT
Definition: XMP.php:99
XMPReader\$ancestorStruct
bool string $ancestorStruct
The structure name when processing nested structures.
Definition: XMP.php:54
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
XMPReader\$parsable
int $parsable
Flag determining if the XMP is safe to parse **.
Definition: XMP.php:72
XMPReader\startElementModeSimple
startElementModeSimple( $elm, $attribs)
Handle an opening element when in MODE_SIMPLE.
Definition: XMP.php:882
XMPReader\startElementModeInitial
startElementModeInitial( $ns, $tag, $attribs)
Starting an element when in MODE_INITIAL This usually happens when we hit an element inside the outer...
Definition: XMP.php:941
XMPReader\endElementModeLi
endElementModeLi( $elm)
Hit a closing element in MODE_LI (either rdf:Seq, or rdf:Bag ) Add information about what type of ele...
Definition: XMP.php:653
XMPReader\$itemLang
bool string $itemLang
Used for lang alts only *.
Definition: XMP.php:64
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1087
wfSuppressWarnings
wfSuppressWarnings( $end=false)
Reference-counted warning suppression.
Definition: GlobalFunctions.php:2434
XMPReader\resetXMLParser
resetXMLParser()
Main use is if a single item has multiple xmp documents describing it.
Definition: XMP.php:131
XMPReader\MODE_LI
const MODE_LI
Definition: XMP.php:87
XMPReader\endElementModeSimple
endElementModeSimple( $elm)
Hit a closing element when in MODE_SIMPLE.
Definition: XMP.php:551
XMPReader\startElement
startElement( $parser, $elm, $attribs)
Hits an opening element.
Definition: XMP.php:1132
XMPReader\startElementModeLiLang
startElementModeLiLang( $elm, $attribs)
Opening element in MODE_LI_LANG.
Definition: XMP.php:1101
XMPReader\MODE_IGNORE
const MODE_IGNORE
Definition: XMP.php:86
XMPReader\$items
array $items
XMP item configuration array *.
Definition: XMP.php:50
XMPReader\MODE_INITIAL
const MODE_INITIAL
These are various mode constants.
Definition: XMP.php:85
XMPReader\endElementModeQDesc
endElementModeQDesc( $elm)
End element while in MODE_QDESC mostly when ending an element when we have a simple value that has qu...
Definition: XMP.php:692
XMPReader\MODE_LI_LANG
const MODE_LI_LANG
Definition: XMP.php:88
MWException
MediaWiki exception.
Definition: MWException.php:26
XMPReader\MODE_SEQ
const MODE_SEQ
Definition: XMP.php:95
wfRestoreWarnings
wfRestoreWarnings()
Restore error level to previous value.
Definition: GlobalFunctions.php:2464
XMPReader\MODE_SIMPLE
const MODE_SIMPLE
Definition: XMP.php:93
XMPReader\endElementModeIgnore
endElementModeIgnore( $elm)
When we hit a closing element in MODE_IGNORE Check to see if this is the element we started to ignore...
Definition: XMP.php:529
$parser
do that in ParserLimitReportFormat instead $parser
Definition: hooks.txt:1961
XMPReader\startElementModeSeq
startElementModeSeq( $elm)
Start element in MODE_SEQ (ordered array) this should always be <rdf:Seq>
Definition: XMP.php:829
wfRunHooks
wfRunHooks( $event, array $args=array(), $deprecatedVersion=null)
Call hook functions defined in $wgHooks.
Definition: GlobalFunctions.php:4066
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
XMPReader\MODE_LANG
const MODE_LANG
Definition: XMP.php:97
XMPReader\startElementModeLang
startElementModeLang( $elm)
Start element in MODE_LANG (language alternative) this should always be <rdf:Alt>
Definition: XMP.php:856
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
XMPReader\MODE_BAG
const MODE_BAG
Definition: XMP.php:96
XMPReader\isSupported
static isSupported()
Check if this instance supports using this class.
Definition: XMP.php:164
XMPReader\$charContent
bool string $charContent
Temporary holder for character data that appears in xmp doc.
Definition: XMP.php:56
XMPReader
Class for reading xmp data containing properties relevant to images, and spitting out an array that F...
Definition: XMP.php:49
$ok
$ok
Definition: UtfNormalTest.php:71
XMPReader\PARSABLE_BUFFERING
const PARSABLE_BUFFERING
Definition: XMP.php:107
XMPReader\$extendedXMPOffset
int $extendedXMPOffset
Definition: XMP.php:70
XMPReader\PARSABLE_OK
const PARSABLE_OK
Definition: XMP.php:106
$name
Allows to change the fields on the form that will be generated $name
Definition: hooks.txt:336
XMPReader\startElementModeStruct
startElementModeStruct( $ns, $tag, $attribs)
Hit an opening element when in a Struct (MODE_STRUCT) This is generally for fields of a compound prop...
Definition: XMP.php:1003
XMPReader\$curItem
array $curItem
Array to hold the current element (and previous element, and so on) *.
Definition: XMP.php:52
XMPReader\parse
parse( $content, $allOfIt=true, $reset=false)
Main function to call to parse XMP.
Definition: XMP.php:272
XMPReader\checkParseSafety
checkParseSafety( $content)
Check if a block of XML is safe to pass to xml_parse, i.e.
Definition: XMP.php:478
XMPReader\$charset
bool string $charset
Character set like 'UTF-8' *.
Definition: XMP.php:68
XMPReader\startElementModeLi
startElementModeLi( $elm, $attribs)
opening element in MODE_LI process elements of arrays.
Definition: XMP.php:1051
XMPReader\NS_RDF
const NS_RDF
Definition: XMP.php:101
XMPReader\doAttribs
doAttribs( $attribs)
Process attributes.
Definition: XMP.php:1225
XMPReader\endElementNested
endElementNested( $elm)
Hit a closing element in MODE_STRUCT, MODE_SEQ, MODE_BAG generally means we've finished processing a ...
Definition: XMP.php:586
XMPInfo\getItems
static getItems()
Get the items array.
Definition: XMPInfo.php:33
XMPReader\MODE_ALT
const MODE_ALT
Definition: XMP.php:98
XMPReader\MODE_STRUCT
const MODE_STRUCT
Definition: XMP.php:94
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
XMPReader\$results
array $results
Array to hold results *.
Definition: XMP.php:60
XMPReader\startElementModeQDesc
startElementModeQDesc( $elm)
Start an element when in MODE_QDESC.
Definition: XMP.php:919
XMPReader\MODE_QDESC
const MODE_QDESC
Definition: XMP.php:89
XMPReader\__destruct
__destruct()
Destroy the xml parser.
Definition: XMP.php:156
XMPReader\NS_XML
const NS_XML
Definition: XMP.php:102
XMPReader\PARSABLE_UNKNOWN
const PARSABLE_UNKNOWN
Definition: XMP.php:105
XMPReader\startElementModeBag
startElementModeBag( $elm)
Start element in MODE_BAG (unordered array) this should always be <rdf:Bag>
Definition: XMP.php:814
XMPReader\parseExtended
parseExtended( $content)
Entry point for XMPExtended blocks in jpeg files.
Definition: XMP.php:366
XMPReader\saveValue
saveValue( $ns, $tag, $val)
Given an extracted value, save it to results array.
Definition: XMP.php:1274
XMPReader\char
char( $parser, $data)
Character data handler Called whenever character data is found in the xmp document.
Definition: XMP.php:441
XMPReader\PARSABLE_NO
const PARSABLE_NO
Definition: XMP.php:108
XMPReader\__construct
__construct()
Constructor.
Definition: XMP.php:115
XMPReader\$xmlParser
resource $xmlParser
A resource handle for the XML parser *.
Definition: XMP.php:66
$error
usually copyright or history_copyright This message must be in HTML not wikitext $subpages will be ignored and the rest of subPageSubtitle() will run. 'SkinTemplateBuildNavUrlsNav_urlsAfterPermalink' whether MediaWiki currently thinks this is a CSS JS page Hooks may change this value to override the return value of Title::isCssOrJsPage(). 'TitleIsAlwaysKnown' whether MediaWiki currently thinks this page is known isMovable() always returns false. $title whether MediaWiki currently thinks this page is movable Hooks may change this value to override the return value of Title::isMovable(). 'TitleIsWikitextPage' whether MediaWiki currently thinks this is a wikitext page Hooks may change this value to override the return value of Title::isWikitextPage() 'TitleMove' use UploadVerification and UploadVerifyFile instead where the first element is the message key and the remaining elements are used as parameters to the message based on mime etc Preferred in most cases over UploadVerification object with all info about the upload string as detected by MediaWiki Handlers will typically only apply for specific mime types object & $error
Definition: hooks.txt:2584
XMPReader\$xmlParsableBuffer
string $xmlParsableBuffer
Buffer of XML to parse **.
Definition: XMP.php:74
$attribs
null means default in associative array with keys and values unescaped Should be merged with default with a value of false meaning to suppress the attribute in associative array with keys and values unescaped noclasses just before the function returns a value If you return an< a > element with HTML attributes $attribs and contents $html will be returned If you return $ret will be returned and may include noclasses after processing & $attribs
Definition: hooks.txt:1530
XMPReader\$mode
array $mode
Stores the state the xmpreader is in (see MODE_FOO constants) *.
Definition: XMP.php:58
XMPReader\getResults
getResults()
Get the result array.
Definition: XMP.php:174
XMPReader\$processingArray
bool $processingArray
If we're doing a seq or bag.
Definition: XMP.php:62
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
XMPReader\startElementModeIgnore
startElementModeIgnore( $elm)
Hit an opening element while in MODE_IGNORE.
Definition: XMP.php:800