24use Psr\Log\LoggerAwareInterface;
25use Psr\Log\LoggerInterface;
26use Psr\Log\NullLogger;
27use Wikimedia\ScopedCallback;
118 const NS_RDF =
'http://www.w3.org/1999/02/22-rdf-syntax-ns#';
119 const NS_XML =
'http://www.w3.org/XML/1998/namespace';
143 if ( !function_exists(
'xml_parser_create_ns' ) ) {
145 throw new RuntimeException(
'XMP support requires XML Parser' );
171 if ( $this->xmlParser ) {
172 xml_parser_free( $this->xmlParser );
173 $this->xmlParser =
null;
184 $this->xmlParser = xml_parser_create_ns(
'UTF-8',
' ' );
185 xml_parser_set_option( $this->xmlParser, XML_OPTION_CASE_FOLDING, 0 );
186 xml_parser_set_option( $this->xmlParser, XML_OPTION_SKIP_WHITE, 1 );
188 xml_set_element_handler( $this->xmlParser,
189 [ $this,
'startElement' ],
190 [ $this,
'endElement' ] );
192 xml_set_character_data_handler( $this->xmlParser, [ $this,
'char' ] );
195 $this->xmlParsableBuffer =
'';
203 return function_exists(
'xml_parser_create_ns' ) && class_exists(
'XMLReader' );
223 if ( isset( $data[
'xmp-special'][
'AuthorsPosition'] )
224 && is_string( $data[
'xmp-special'][
'AuthorsPosition'] )
225 && isset( $data[
'xmp-general'][
'Artist'][0] )
232 $data[
'xmp-general'][
'Artist'][0] =
233 $data[
'xmp-special'][
'AuthorsPosition'] .
', '
234 . $data[
'xmp-general'][
'Artist'][0];
241 if ( isset( $data[
'xmp-special'][
'LocationShown'][0] )
242 && is_array( $data[
'xmp-special'][
'LocationShown'][0] )
246 foreach ( $data[
'xmp-special'][
'LocationShown'] as $loc ) {
247 if ( !is_array( $loc ) ) {
251 foreach ( $loc as $field => $val ) {
252 $data[
'xmp-general'][$field .
'Dest'][] = $val;
256 if ( isset( $data[
'xmp-special'][
'LocationCreated'][0] )
257 && is_array( $data[
'xmp-special'][
'LocationCreated'][0] )
261 foreach ( $data[
'xmp-special'][
'LocationCreated'] as $loc ) {
262 if ( !is_array( $loc ) ) {
266 foreach ( $loc as $field => $val ) {
267 $data[
'xmp-general'][$field .
'Created'][] = $val;
274 unset( $data[
'xmp-special'] );
277 if ( isset( $data[
'xmp-exif'][
'GPSAltitudeRef'] )
278 && isset( $data[
'xmp-exif'][
'GPSAltitude'] )
282 list( $nom, $denom ) = explode(
'/', $data[
'xmp-exif'][
'GPSAltitude'] );
283 $data[
'xmp-exif'][
'GPSAltitude'] = $nom / $denom;
285 if ( $data[
'xmp-exif'][
'GPSAltitudeRef'] ==
'1' ) {
286 $data[
'xmp-exif'][
'GPSAltitude'] *= -1;
288 unset( $data[
'xmp-exif'][
'GPSAltitudeRef'] );
306 public function parse( $content, $allOfIt =
true ) {
307 if ( !$this->xmlParser ) {
314 if ( !$this->charset ) {
316 if ( preg_match(
'/\xEF\xBB\xBF|\xFE\xFF|\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\xFF\xFE/',
321 $this->charset =
'UTF-16BE';
324 $this->charset =
'UTF-16LE';
326 case "\x00\x00\xFE\xFF":
327 $this->charset =
'UTF-32BE';
329 case "\xFF\xFE\x00\x00":
330 $this->charset =
'UTF-32LE';
333 $this->charset =
'UTF-8';
337 throw new RuntimeException(
"Invalid BOM" );
341 $this->charset =
'UTF-8';
344 if ( $this->charset !==
'UTF-8' ) {
346 Wikimedia\suppressWarnings();
347 $content = iconv( $this->charset,
'UTF-8//IGNORE', $content );
348 Wikimedia\restoreWarnings();
353 if ( $this->parsable !== self::PARSABLE_OK ) {
354 if ( $this->parsable === self::PARSABLE_NO ) {
355 throw new RuntimeException(
'Unsafe doctype declaration in XML.' );
358 $content = $this->xmlParsableBuffer . $content;
360 if ( !$allOfIt && $this->parsable !== self::PARSABLE_NO ) {
366 'Unsafe doctype declaration in XML.' :
367 'No root element found in XML.';
368 throw new RuntimeException( $msg );
372 $ok = xml_parse( $this->xmlParser, $content, $allOfIt );
374 $code = xml_get_error_code( $this->xmlParser );
375 $error = xml_error_string(
$code );
376 $line = xml_get_current_line_number( $this->xmlParser );
377 $col = xml_get_current_column_number( $this->xmlParser );
378 $offset = xml_get_current_byte_index( $this->xmlParser );
381 '{method} : Error reading XMP content: {error} ' .
382 '(file: {file}, line: {line} column: {column} ' .
383 'byte offset: {offset})',
385 'method' => __METHOD__,
386 'error_code' =>
$code,
388 'file' => $this->filename,
392 'content' => $content,
398 }
catch ( Exception
$e ) {
399 $this->logger->warning(
400 '{method} {exception}',
402 'method' => __METHOD__,
404 'file' => $this->filename,
405 'content' => $content,
428 $guid = substr( $content, 0, 32 );
429 if ( !isset( $this->results[
'xmp-special'][
'HasExtendedXMP'] )
430 || $this->results[
'xmp-special'][
'HasExtendedXMP'] !== $guid
432 $this->logger->info( __METHOD__ .
433 " Ignoring XMPExtended block due to wrong guid (guid= '{guid}')",
436 'file' => $this->filename,
442 $len = unpack(
'Nlength/Noffset', substr( $content, 32, 8 ) );
445 $len[
'length'] < 4 ||
446 $len[
'offset'] < 0 ||
447 $len[
'offset'] > $len[
'length']
450 __METHOD__ .
'Error reading extended XMP block, invalid length or offset.',
451 [
'file' => $this->filename ]
467 if ( $len[
'offset'] !== $this->extendedXMPOffset ) {
468 $this->logger->info( __METHOD__ .
'Ignoring XMPExtended block due to wrong order. (Offset was '
469 . $len[
'offset'] .
' but expected ' . $this->extendedXMPOffset .
')',
470 [
'file' => $this->filename ]
476 if ( $len[
'offset'] === 0 ) {
482 $this->extendedXMPOffset += $len[
'length'];
484 $actualContent = substr( $content, 40 );
486 if ( $this->extendedXMPOffset === strlen( $actualContent ) ) {
492 $this->logger->debug(
493 __METHOD__ .
'Parsing a XMPExtended block',
494 [
'file' => $this->filename ]
497 return $this->
parse( $actualContent, $atEnd );
517 $data = trim( $data );
518 if ( trim( $data ) ===
"" ) {
522 if ( !isset( $this->mode[0] ) ) {
523 throw new RuntimeException(
'Unexpected character data before first rdf:Description element' );
526 if ( $this->mode[0] === self::MODE_IGNORE ) {
530 if ( $this->mode[0] !== self::MODE_SIMPLE
531 && $this->mode[0] !== self::MODE_QDESC
533 throw new RuntimeException(
'character data where not expected. (mode ' . $this->mode[0] .
')' );
537 if ( $this->charContent ===
false ) {
538 $this->charContent = $data;
540 $this->charContent .= $data;
553 $reader =
new XMLReader();
559 'data://text/plain,' . urlencode( $content ),
561 LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET
564 $oldDisable = libxml_disable_entity_loader(
true );
566 $reset =
new ScopedCallback(
567 'libxml_disable_entity_loader',
570 $reader->setParserProperty( XMLReader::SUBST_ENTITIES,
false );
574 Wikimedia\suppressWarnings();
575 while ( $reader->read() ) {
576 if ( $reader->nodeType === XMLReader::ELEMENT ) {
582 if ( $reader->nodeType === XMLReader::DOC_TYPE ) {
588 Wikimedia\restoreWarnings();
590 if ( !is_null( $result ) ) {
597 $this->xmlParsableBuffer = $content;
608 if ( $this->curItem[0] === $elm ) {
609 array_shift( $this->curItem );
610 array_shift( $this->mode );
630 if ( $this->charContent !==
false ) {
631 if ( $this->processingArray ) {
634 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
636 list( $ns, $tag ) = explode(
' ', $elm, 2 );
638 $this->
saveValue( $ns, $tag, $this->charContent );
640 $this->charContent =
false;
642 array_shift( $this->curItem );
643 array_shift( $this->mode );
667 if ( $this->curItem[0] !== $elm
668 && !( $elm === self::NS_RDF .
' Description'
669 && $this->mode[0] === self::MODE_STRUCT )
671 throw new RuntimeException(
"nesting mismatch. got a </$elm> but expected a </" .
672 $this->curItem[0] .
'>' );
676 list( $ns, $tag ) = explode(
' ', $elm, 2 );
677 if ( isset( $this->items[$ns][$tag][
'validate'] ) ) {
678 $info =& $this->items[$ns][$tag];
679 $finalName = isset( $info[
'map_name'] )
680 ? $info[
'map_name'] : $tag;
682 if ( is_array( $info[
'validate'] ) ) {
683 $validate = $info[
'validate'];
686 $validate = [ $validator, $info[
'validate'] ];
689 if ( !isset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] ) ) {
691 $this->logger->debug(
692 __METHOD__ .
" <$ns:$tag> has no valid members.",
693 [
'file' => $this->filename ]
695 } elseif ( is_callable( $validate ) ) {
696 $val =& $this->results[
'xmp-' . $info[
'map_group']][$finalName];
697 call_user_func_array( $validate, [ $info, &$val,
false ] );
698 if ( is_null( $val ) ) {
702 __METHOD__ .
" <$ns:$tag> failed validation.",
703 [
'file' => $this->filename ]
705 unset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] );
708 $this->logger->warning(
709 __METHOD__ .
" Validation function for $finalName (" .
710 $validate[0] .
'::' . $validate[1] .
'()) is not callable.',
711 [
'file' => $this->filename ]
716 array_shift( $this->curItem );
717 array_shift( $this->mode );
718 $this->ancestorStruct =
false;
719 $this->processingArray =
false;
720 $this->itemLang =
false;
743 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
744 $info = $this->items[$ns][$tag];
745 $finalName = isset( $info[
'map_name'] )
746 ? $info[
'map_name'] : $tag;
748 array_shift( $this->mode );
750 if ( !isset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] ) ) {
751 $this->logger->debug(
752 __METHOD__ .
" Empty compund element $finalName.",
753 [
'file' => $this->filename ]
759 if ( $elm === self::NS_RDF .
' Seq' ) {
760 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'ol';
761 } elseif ( $elm === self::NS_RDF .
' Bag' ) {
762 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'ul';
763 } elseif ( $elm === self::NS_RDF .
' Alt' ) {
765 if ( $info[
'mode'] === self::MODE_LANG ) {
766 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'lang';
769 throw new RuntimeException(
770 __METHOD__ .
" expected </rdf:seq> or </rdf:bag> but instead got $elm."
786 if ( $elm === self::NS_RDF .
' value' ) {
787 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
788 $this->
saveValue( $ns, $tag, $this->charContent );
792 array_shift( $this->mode );
793 array_shift( $this->curItem );
811 if ( $elm === ( self::NS_RDF .
' RDF' )
812 || $elm ===
'adobe:ns:meta/ xmpmeta'
813 || $elm ===
'adobe:ns:meta/ xapmeta'
819 if ( $elm === self::NS_RDF .
' type' ) {
823 __METHOD__ .
' encountered <rdf:type>',
824 [
'file' => $this->filename ]
828 if ( strpos( $elm,
' ' ) ===
false ) {
834 __METHOD__ .
" Encountered </$elm> which has no namespace. Skipping.",
835 [
'file' => $this->filename ]
841 if ( count( $this->mode ) === 0 ) {
844 throw new RuntimeException(
'Encountered end element with no mode' );
847 if ( count( $this->curItem ) == 0 && $this->mode[0] !== self::MODE_INITIAL ) {
850 throw new RuntimeException(
"Hit end element </$elm> but no curItem" );
853 switch ( $this->mode[0] ) {
868 if ( $elm === self::NS_RDF .
' Description' ) {
869 array_shift( $this->mode );
871 throw new RuntimeException(
'Element ended unexpectedly while in MODE_INITIAL' );
883 __METHOD__ .
" no mode (elm = $elm)",
884 [
'file' => $this->filename ]
902 if ( $elm === $this->curItem[0] ) {
903 array_unshift( $this->curItem, $elm );
904 array_unshift( $this->mode, self::MODE_IGNORE );
916 if ( $elm === self::NS_RDF .
' Bag' ) {
917 array_unshift( $this->mode, self::MODE_LI );
919 throw new RuntimeException(
"Expected <rdf:Bag> but got $elm." );
931 if ( $elm === self::NS_RDF .
' Seq' ) {
932 array_unshift( $this->mode, self::MODE_LI );
933 } elseif ( $elm === self::NS_RDF .
' Bag' ) {
936 __METHOD__ .
' Expected an rdf:Seq, but got an rdf:Bag. Pretending' .
937 ' it is a Seq, since some buggy software is known to screw this up.',
938 [
'file' => $this->filename ]
940 array_unshift( $this->mode, self::MODE_LI );
942 throw new RuntimeException(
"Expected <rdf:Seq> but got $elm." );
961 if ( $elm === self::NS_RDF .
' Alt' ) {
962 array_unshift( $this->mode, self::MODE_LI_LANG );
964 throw new RuntimeException(
"Expected <rdf:Seq> but got $elm." );
987 if ( $elm === self::NS_RDF .
' Description' ) {
989 array_unshift( $this->mode, self::MODE_QDESC );
990 array_unshift( $this->curItem, $this->curItem[0] );
992 if ( isset(
$attribs[self::NS_RDF .
' value'] ) ) {
993 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
996 } elseif ( $elm === self::NS_RDF .
' value' ) {
998 throw new RuntimeException( __METHOD__ .
' Encountered <rdf:value> where it was unexpected.' );
1001 $this->logger->info( __METHOD__ .
1002 " Encountered element <{element}> where only expecting character data as value of {curitem}",
1005 'curitem' => $this->curItem[0],
1006 'file' => $this->filename,
1009 array_unshift( $this->mode, self::MODE_IGNORE );
1010 array_unshift( $this->curItem, $elm );
1028 if ( $elm === self::NS_RDF .
' value' ) {
1032 array_unshift( $this->mode, self::MODE_IGNORE );
1033 array_unshift( $this->curItem, $elm );
1050 if ( $ns !== self::NS_RDF ) {
1051 if ( isset( $this->items[$ns][$tag] ) ) {
1052 if ( isset( $this->items[$ns][$tag][
'structPart'] ) ) {
1058 $this->logger->info(
1059 'Encountered <{element}> outside of its expected parent. Ignoring.',
1060 [
'element' =>
"$ns:$tag",
'file' => $this->filename ]
1063 array_unshift( $this->mode, self::MODE_IGNORE );
1064 array_unshift( $this->curItem, $ns .
' ' . $tag );
1068 $mode = $this->items[$ns][$tag][
'mode'];
1069 array_unshift( $this->mode,
$mode );
1070 array_unshift( $this->curItem, $ns .
' ' . $tag );
1071 if (
$mode === self::MODE_STRUCT ) {
1072 $this->ancestorStruct = isset( $this->items[$ns][$tag][
'map_name'] )
1073 ? $this->items[$ns][$tag][
'map_name'] : $tag;
1075 if ( $this->charContent !==
false ) {
1078 throw new RuntimeException(
'tag nested in non-whitespace characters.' );
1082 $this->logger->debug( __METHOD__ .
' Ignoring unrecognized element <{element}>.',
1083 [
'element' =>
"$ns:$tag",
'file' => $this->filename ] );
1084 array_unshift( $this->mode, self::MODE_IGNORE );
1085 array_unshift( $this->curItem, $ns .
' ' . $tag );
1114 if ( $ns !== self::NS_RDF ) {
1115 if ( isset( $this->items[$ns][$tag] ) ) {
1116 if ( isset( $this->items[$ns][$this->ancestorStruct][
'children'] )
1117 && !isset( $this->items[$ns][$this->ancestorStruct][
'children'][$tag] )
1121 throw new RuntimeException(
" <$tag> appeared nested in <" . $this->ancestorStruct
1122 .
"> where it is not allowed." );
1124 array_unshift( $this->mode, $this->items[$ns][$tag][
'mode'] );
1125 array_unshift( $this->curItem, $ns .
' ' . $tag );
1126 if ( $this->charContent !==
false ) {
1129 throw new RuntimeException(
"tag <$tag> nested in non-whitespace characters (" .
1130 $this->charContent .
")." );
1133 array_unshift( $this->mode, self::MODE_IGNORE );
1134 array_unshift( $this->curItem, $ns .
' ' . $tag );
1140 if ( $ns === self::NS_RDF && $tag ===
'Description' ) {
1142 array_unshift( $this->mode, self::MODE_STRUCT );
1143 array_unshift( $this->curItem, $this->curItem[0] );
1161 if ( ( $elm ) !== self::NS_RDF .
' li' ) {
1162 throw new RuntimeException(
"<rdf:li> expected but got $elm." );
1165 if ( !isset( $this->mode[1] ) ) {
1168 throw new RuntimeException(
'In mode Li, but no 2xPrevious mode!' );
1171 if ( $this->mode[1] === self::MODE_BAGSTRUCT ) {
1173 array_unshift( $this->mode, self::MODE_STRUCT );
1174 array_unshift( $this->curItem, $elm );
1175 $this->processingArray =
true;
1177 if ( !isset( $this->curItem[1] ) ) {
1179 throw new RuntimeException(
'Can not find parent of BAGSTRUCT.' );
1181 list( $curNS, $curTag ) = explode(
' ', $this->curItem[1] );
1182 $this->ancestorStruct = isset( $this->items[$curNS][$curTag][
'map_name'] )
1183 ? $this->items[$curNS][$curTag][
'map_name'] : $curTag;
1188 array_unshift( $this->mode, self::MODE_SIMPLE );
1191 array_unshift( $this->curItem, $this->curItem[0] );
1192 $this->processingArray =
true;
1211 if ( $elm !== self::NS_RDF .
' li' ) {
1212 throw new RuntimeException( __METHOD__ .
" <rdf:li> expected but got $elm." );
1214 if ( !isset(
$attribs[self::NS_XML .
' lang'] )
1215 || !preg_match(
'/^[-A-Za-z0-9]{2,}$/D',
$attribs[self::NS_XML .
' lang'] )
1217 throw new RuntimeException( __METHOD__
1218 .
" <rdf:li> did not contain, or has invalid xml:lang attribute in lang alternative" );
1222 $this->itemLang = strtolower(
$attribs[self::NS_XML .
' lang'] );
1226 array_unshift( $this->curItem, $this->curItem[0] );
1227 array_unshift( $this->mode, self::MODE_SIMPLE );
1228 $this->processingArray =
true;
1242 if ( $elm === self::NS_RDF .
' RDF'
1243 || $elm ===
'adobe:ns:meta/ xmpmeta'
1244 || $elm ===
'adobe:ns:meta/ xapmeta'
1248 } elseif ( $elm === self::NS_RDF .
' Description' ) {
1249 if ( count( $this->mode ) === 0 ) {
1251 array_unshift( $this->mode, self::MODE_INITIAL );
1253 } elseif ( $elm === self::NS_RDF .
' type' ) {
1260 $this->logger->info(
1261 __METHOD__ .
' Encountered <rdf:type> which isn\'t currently supported',
1262 [
'file' => $this->filename ]
1266 if ( strpos( $elm,
' ' ) ===
false ) {
1268 $this->logger->info(
1269 __METHOD__ .
" Encountered <$elm> which has no namespace. Skipping.",
1270 [
'file' => $this->filename ]
1276 list( $ns, $tag ) = explode(
' ', $elm, 2 );
1278 if ( count( $this->mode ) === 0 ) {
1280 throw new RuntimeException(
'Error extracting XMP, '
1281 .
"encountered <$elm> with no mode" );
1284 switch ( $this->mode[0] ) {
1317 throw new RuntimeException(
'StartElement in unknown mode: ' . $this->mode[0] );
1342 if ( isset(
$attribs[self::NS_RDF .
' parseType'] )
1343 &&
$attribs[self::NS_RDF .
' parseType'] ===
'Resource'
1344 && $this->mode[0] === self::MODE_SIMPLE
1349 foreach (
$attribs as $name => $val ) {
1350 if ( strpos( $name,
' ' ) ===
false ) {
1353 $this->logger->info(
1354 __METHOD__ .
' Encountered non-namespaced attribute: ' .
1355 " $name=\"$val\". Skipping. ",
1356 [
'file' => $this->filename ]
1360 list( $ns, $tag ) = explode(
' ', $name, 2 );
1361 if ( $ns === self::NS_RDF ) {
1362 if ( $tag ===
'value' || $tag ===
'resource' ) {
1365 $this->
char( $this->xmlParser, $val );
1367 } elseif ( isset( $this->items[$ns][$tag] ) ) {
1368 if ( $this->mode[0] === self::MODE_SIMPLE ) {
1369 throw new RuntimeException( __METHOD__
1370 .
" $ns:$tag found as attribute where not allowed" );
1374 $this->logger->debug(
1375 __METHOD__ .
" Ignoring unrecognized element <$ns:$tag>.",
1376 [
'file' => $this->filename ]
1394 $info =& $this->items[$ns][$tag];
1395 $finalName = isset( $info[
'map_name'] )
1396 ? $info[
'map_name'] : $tag;
1397 if ( isset( $info[
'validate'] ) ) {
1398 if ( is_array( $info[
'validate'] ) ) {
1399 $validate = $info[
'validate'];
1402 $validate = [ $validator, $info[
'validate'] ];
1405 if ( is_callable( $validate ) ) {
1406 call_user_func_array( $validate, [ $info, &$val,
true ] );
1409 if ( is_null( $val ) ) {
1410 $this->logger->info(
1411 __METHOD__ .
" <$ns:$tag> failed validation.",
1412 [
'file' => $this->filename ]
1418 $this->logger->warning(
1419 __METHOD__ .
" Validation function for $finalName (" .
1420 $validate[0] .
'::' . $validate[1] .
'()) is not callable.',
1421 [
'file' => $this->filename ]
1426 if ( $this->ancestorStruct && $this->processingArray ) {
1429 } elseif ( $this->ancestorStruct ) {
1431 } elseif ( $this->processingArray ) {
1432 if ( $this->itemLang ===
false ) {
1434 $this->results[
'xmp-' . $info[
'map_group']][$finalName][] = $val;
1437 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
$this->itemLang] = $val;
1440 $this->results[
'xmp-' . $info[
'map_group']][$finalName] = $val;
static getItems()
Get the items array.
Class for reading xmp data containing properties relevant to images, and spitting out an array that F...
bool string $charContent
Temporary holder for character data that appears in xmp doc.
bool string $itemLang
Used for lang alts only.
destroyXMLParser()
free the XML parser.
endElementModeIgnore( $elm)
When we hit a closing element in MODE_IGNORE Check to see if this is the element we started to ignore...
char( $parser, $data)
Character data handler Called whenever character data is found in the xmp document.
setLogger(LoggerInterface $logger)
getResults()
Get the result array.
startElementModeLang( $elm)
Start element in MODE_LANG (language alternative) this should always be <rdf:Alt>
array $results
Array to hold results.
bool string $charset
Character set like 'UTF-8'.
doAttribs( $attribs)
Process attributes.
parseExtended( $content)
Entry point for XMPExtended blocks in jpeg files.
endElementModeSimple( $elm)
Hit a closing element when in MODE_SIMPLE.
array $curItem
Array to hold the current element (and previous element, and so on)
startElementModeStruct( $ns, $tag, $attribs)
Hit an opening element when in a Struct (MODE_STRUCT) This is generally for fields of a compound prop...
static isSupported()
Check if this instance supports using this class.
parse( $content, $allOfIt=true)
Main function to call to parse XMP.
array $mode
Stores the state the xmpreader is in (see MODE_FOO constants)
int $parsable
Flag determining if the XMP is safe to parse.
resetXMLParser()
Main use is if a single item has multiple xmp documents describing it.
startElement( $parser, $elm, $attribs)
Hits an opening element.
startElementModeBag( $elm)
Start element in MODE_BAG (unordered array) this should always be <rdf:Bag>
const MODE_INITIAL
These are various mode constants.
startElementModeQDesc( $elm)
Start an element when in MODE_QDESC.
startElementModeInitial( $ns, $tag, $attribs)
Starting an element when in MODE_INITIAL This usually happens when we hit an element inside the outer...
saveValue( $ns, $tag, $val)
Given an extracted value, save it to results array.
endElement( $parser, $elm)
Handler for hitting a closing element.
startElementModeSeq( $elm)
Start element in MODE_SEQ (ordered array) this should always be <rdf:Seq>
string $xmlParsableBuffer
Buffer of XML to parse.
endElementModeLi( $elm)
Hit a closing element in MODE_LI (either rdf:Seq, or rdf:Bag ) Add information about what type of ele...
__construct(LoggerInterface $logger=null, $filename='unknown')
Primary job is to initialize the XMLParser.
array $items
XMP item configuration array.
startElementModeSimple( $elm, $attribs)
Handle an opening element when in MODE_SIMPLE.
resource $xmlParser
A resource handle for the XML parser.
endElementNested( $elm)
Hit a closing element in MODE_STRUCT, MODE_SEQ, MODE_BAG generally means we've finished processing a ...
startElementModeIgnore( $elm)
Hit an opening element while in MODE_IGNORE.
startElementModeLi( $elm, $attribs)
opening element in MODE_LI process elements of arrays.
bool $processingArray
If we're doing a seq or bag.
bool string $ancestorStruct
The structure name when processing nested structures.
endElementModeQDesc( $elm)
End element while in MODE_QDESC mostly when ending an element when we have a simple value that has qu...
checkParseSafety( $content)
Check if a block of XML is safe to pass to xml_parse, i.e.
startElementModeLiLang( $elm, $attribs)
Opening element in MODE_LI_LANG.
This contains some static methods for validating XMP properties.
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
namespace being checked & $result
do that in ParserLimitReportFormat instead $parser
this hook is for auditing only or null if authentication failed before getting that far or null if we can t even determine that probably a stub it is not rendered in wiki pages or galleries in category pages allow injecting custom HTML after the section Any uses of the hook need to handle escaping see BaseTemplate::getToolbox and BaseTemplate::makeListItem for details on the format of individual items inside of this array or by returning and letting standard HTTP rendering take place modifiable or by returning false and taking over the output modifiable & $code
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
returning false will NOT prevent logging $e