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';
137 if ( !function_exists(
'xml_parser_create_ns' ) ) {
139 throw new RuntimeException(
'XMP support requires XML Parser' );
164 if ( $this->xmlParser ) {
165 xml_parser_free( $this->xmlParser );
166 $this->xmlParser =
null;
177 $this->xmlParser = xml_parser_create_ns(
'UTF-8',
' ' );
178 xml_parser_set_option( $this->xmlParser, XML_OPTION_CASE_FOLDING, 0 );
179 xml_parser_set_option( $this->xmlParser, XML_OPTION_SKIP_WHITE, 1 );
181 xml_set_element_handler( $this->xmlParser,
182 [ $this,
'startElement' ],
183 [ $this,
'endElement' ] );
185 xml_set_character_data_handler( $this->xmlParser, [ $this,
'char' ] );
188 $this->xmlParsableBuffer =
'';
196 return function_exists(
'xml_parser_create_ns' ) && class_exists(
'XMLReader' );
216 if ( isset( $data[
'xmp-special'][
'AuthorsPosition'] )
217 && is_string( $data[
'xmp-special'][
'AuthorsPosition'] )
218 && isset( $data[
'xmp-general'][
'Artist'][0] )
225 $data[
'xmp-general'][
'Artist'][0] =
226 $data[
'xmp-special'][
'AuthorsPosition'] .
', '
227 . $data[
'xmp-general'][
'Artist'][0];
234 if ( isset( $data[
'xmp-special'][
'LocationShown'][0] )
235 && is_array( $data[
'xmp-special'][
'LocationShown'][0] )
239 foreach ( $data[
'xmp-special'][
'LocationShown'] as $loc ) {
240 if ( !is_array( $loc ) ) {
244 foreach ( $loc as $field => $val ) {
245 $data[
'xmp-general'][$field .
'Dest'][] = $val;
249 if ( isset( $data[
'xmp-special'][
'LocationCreated'][0] )
250 && is_array( $data[
'xmp-special'][
'LocationCreated'][0] )
254 foreach ( $data[
'xmp-special'][
'LocationCreated'] as $loc ) {
255 if ( !is_array( $loc ) ) {
259 foreach ( $loc as $field => $val ) {
260 $data[
'xmp-general'][$field .
'Created'][] = $val;
267 unset( $data[
'xmp-special'] );
270 if ( isset( $data[
'xmp-exif'][
'GPSAltitudeRef'] )
271 && isset( $data[
'xmp-exif'][
'GPSAltitude'] )
275 list( $nom, $denom ) = explode(
'/', $data[
'xmp-exif'][
'GPSAltitude'] );
276 $data[
'xmp-exif'][
'GPSAltitude'] = $nom / $denom;
278 if ( $data[
'xmp-exif'][
'GPSAltitudeRef'] ==
'1' ) {
279 $data[
'xmp-exif'][
'GPSAltitude'] *= -1;
281 unset( $data[
'xmp-exif'][
'GPSAltitudeRef'] );
299 public function parse( $content, $allOfIt =
true ) {
300 if ( !$this->xmlParser ) {
307 if ( !$this->charset ) {
309 if ( preg_match(
'/\xEF\xBB\xBF|\xFE\xFF|\x00\x00\xFE\xFF|\xFF\xFE\x00\x00|\xFF\xFE/',
314 $this->charset =
'UTF-16BE';
317 $this->charset =
'UTF-16LE';
319 case "\x00\x00\xFE\xFF":
320 $this->charset =
'UTF-32BE';
322 case "\xFF\xFE\x00\x00":
323 $this->charset =
'UTF-32LE';
326 $this->charset =
'UTF-8';
330 throw new RuntimeException(
"Invalid BOM" );
334 $this->charset =
'UTF-8';
337 if ( $this->charset !==
'UTF-8' ) {
339 MediaWiki\suppressWarnings();
340 $content = iconv( $this->charset,
'UTF-8//IGNORE', $content );
341 MediaWiki\restoreWarnings();
346 if ( $this->parsable !== self::PARSABLE_OK ) {
347 if ( $this->parsable === self::PARSABLE_NO ) {
348 throw new RuntimeException(
'Unsafe doctype declaration in XML.' );
351 $content = $this->xmlParsableBuffer . $content;
353 if ( !$allOfIt && $this->parsable !== self::PARSABLE_NO ) {
359 'Unsafe doctype declaration in XML.' :
360 'No root element found in XML.';
361 throw new RuntimeException( $msg );
365 $ok = xml_parse( $this->xmlParser, $content, $allOfIt );
367 $code = xml_get_error_code( $this->xmlParser );
368 $error = xml_error_string(
$code );
369 $line = xml_get_current_line_number( $this->xmlParser );
370 $col = xml_get_current_column_number( $this->xmlParser );
371 $offset = xml_get_current_byte_index( $this->xmlParser );
373 $this->logger->warning(
374 '{method} : Error reading XMP content: {error} ' .
375 '(line: {line} column: {column} byte offset: {offset})',
377 'method' => __METHOD__,
378 'error_code' =>
$code,
383 'content' => $content,
389 }
catch ( Exception
$e ) {
390 $this->logger->warning(
391 '{method} Exception caught while parsing: ' .
$e->getMessage(),
393 'method' => __METHOD__,
395 'content' => $content,
418 $guid = substr( $content, 0, 32 );
419 if ( !isset( $this->results[
'xmp-special'][
'HasExtendedXMP'] )
420 || $this->results[
'xmp-special'][
'HasExtendedXMP'] !== $guid
422 $this->logger->info( __METHOD__ .
423 " Ignoring XMPExtended block due to wrong guid (guid= '$guid')" );
427 $len = unpack(
'Nlength/Noffset', substr( $content, 32, 8 ) );
430 $len[
'length'] < 4 ||
431 $len[
'offset'] < 0 ||
432 $len[
'offset'] > $len[
'length']
435 __METHOD__ .
'Error reading extended XMP block, invalid length or offset.'
451 if ( $len[
'offset'] !== $this->extendedXMPOffset ) {
452 $this->logger->info( __METHOD__ .
'Ignoring XMPExtended block due to wrong order. (Offset was '
453 . $len[
'offset'] .
' but expected ' . $this->extendedXMPOffset .
')' );
458 if ( $len[
'offset'] === 0 ) {
464 $this->extendedXMPOffset += $len[
'length'];
466 $actualContent = substr( $content, 40 );
468 if ( $this->extendedXMPOffset === strlen( $actualContent ) ) {
474 $this->logger->debug( __METHOD__ .
'Parsing a XMPExtended block' );
476 return $this->
parse( $actualContent, $atEnd );
496 $data = trim( $data );
497 if ( trim( $data ) ===
"" ) {
501 if ( !isset( $this->mode[0] ) ) {
502 throw new RuntimeException(
'Unexpected character data before first rdf:Description element' );
505 if ( $this->mode[0] === self::MODE_IGNORE ) {
509 if ( $this->mode[0] !== self::MODE_SIMPLE
510 && $this->mode[0] !== self::MODE_QDESC
512 throw new RuntimeException(
'character data where not expected. (mode ' . $this->mode[0] .
')' );
516 if ( $this->charContent ===
false ) {
517 $this->charContent = $data;
519 $this->charContent .= $data;
532 $reader =
new XMLReader();
538 'data://text/plain,' . urlencode( $content ),
540 LIBXML_NOERROR | LIBXML_NOWARNING | LIBXML_NONET
543 $oldDisable = libxml_disable_entity_loader(
true );
545 $reset =
new ScopedCallback(
546 'libxml_disable_entity_loader',
549 $reader->setParserProperty( XMLReader::SUBST_ENTITIES,
false );
553 MediaWiki\suppressWarnings();
554 while ( $reader->read() ) {
555 if ( $reader->nodeType === XMLReader::ELEMENT ) {
561 if ( $reader->nodeType === XMLReader::DOC_TYPE ) {
567 MediaWiki\restoreWarnings();
569 if ( !is_null( $result ) ) {
576 $this->xmlParsableBuffer = $content;
587 if ( $this->curItem[0] === $elm ) {
588 array_shift( $this->curItem );
589 array_shift( $this->mode );
609 if ( $this->charContent !==
false ) {
610 if ( $this->processingArray ) {
613 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
615 list( $ns, $tag ) = explode(
' ', $elm, 2 );
617 $this->
saveValue( $ns, $tag, $this->charContent );
619 $this->charContent =
false;
621 array_shift( $this->curItem );
622 array_shift( $this->mode );
646 if ( $this->curItem[0] !== $elm
647 && !( $elm === self::NS_RDF .
' Description'
648 && $this->mode[0] === self::MODE_STRUCT )
650 throw new RuntimeException(
"nesting mismatch. got a </$elm> but expected a </" .
651 $this->curItem[0] .
'>' );
655 list( $ns, $tag ) = explode(
' ', $elm, 2 );
656 if ( isset( $this->items[$ns][$tag][
'validate'] ) ) {
657 $info =& $this->items[$ns][$tag];
658 $finalName = isset( $info[
'map_name'] )
659 ? $info[
'map_name'] : $tag;
661 if ( is_array( $info[
'validate'] ) ) {
662 $validate = $info[
'validate'];
665 $validate = [ $validator, $info[
'validate'] ];
668 if ( !isset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] ) ) {
670 $this->logger->debug( __METHOD__ .
" <$ns:$tag> has no valid members." );
671 } elseif ( is_callable( $validate ) ) {
672 $val =& $this->results[
'xmp-' . $info[
'map_group']][$finalName];
673 call_user_func_array( $validate, [ $info, &$val,
false ] );
674 if ( is_null( $val ) ) {
677 $this->logger->info( __METHOD__ .
" <$ns:$tag> failed validation." );
678 unset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] );
681 $this->logger->warning( __METHOD__ .
" Validation function for $finalName ("
682 . $validate[0] .
'::' . $validate[1] .
'()) is not callable.' );
686 array_shift( $this->curItem );
687 array_shift( $this->mode );
688 $this->ancestorStruct =
false;
689 $this->processingArray =
false;
690 $this->itemLang =
false;
713 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
714 $info = $this->items[$ns][$tag];
715 $finalName = isset( $info[
'map_name'] )
716 ? $info[
'map_name'] : $tag;
718 array_shift( $this->mode );
720 if ( !isset( $this->results[
'xmp-' . $info[
'map_group']][$finalName] ) ) {
721 $this->logger->debug( __METHOD__ .
" Empty compund element $finalName." );
726 if ( $elm === self::NS_RDF .
' Seq' ) {
727 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'ol';
728 } elseif ( $elm === self::NS_RDF .
' Bag' ) {
729 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'ul';
730 } elseif ( $elm === self::NS_RDF .
' Alt' ) {
732 if ( $info[
'mode'] === self::MODE_LANG ) {
733 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
'_type'] =
'lang';
736 throw new RuntimeException(
737 __METHOD__ .
" expected </rdf:seq> or </rdf:bag> but instead got $elm."
753 if ( $elm === self::NS_RDF .
' value' ) {
754 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
755 $this->
saveValue( $ns, $tag, $this->charContent );
759 array_shift( $this->mode );
760 array_shift( $this->curItem );
778 if ( $elm === ( self::NS_RDF .
' RDF' )
779 || $elm ===
'adobe:ns:meta/ xmpmeta'
780 || $elm ===
'adobe:ns:meta/ xapmeta'
786 if ( $elm === self::NS_RDF .
' type' ) {
789 $this->logger->info( __METHOD__ .
' encountered <rdf:type>' );
792 if ( strpos( $elm,
' ' ) ===
false ) {
797 $this->logger->info( __METHOD__ .
" Encountered </$elm> which has no namespace. Skipping." );
802 if ( count( $this->mode[0] ) === 0 ) {
805 throw new RuntimeException(
'Encountered end element with no mode' );
808 if ( count( $this->curItem ) == 0 && $this->mode[0] !== self::MODE_INITIAL ) {
811 throw new RuntimeException(
"Hit end element </$elm> but no curItem" );
814 switch ( $this->mode[0] ) {
829 if ( $elm === self::NS_RDF .
' Description' ) {
830 array_shift( $this->mode );
832 throw new RuntimeException(
'Element ended unexpectedly while in MODE_INITIAL' );
843 $this->logger->warning( __METHOD__ .
" no mode (elm = $elm)" );
860 if ( $elm === $this->curItem[0] ) {
861 array_unshift( $this->curItem, $elm );
862 array_unshift( $this->mode, self::MODE_IGNORE );
874 if ( $elm === self::NS_RDF .
' Bag' ) {
875 array_unshift( $this->mode, self::MODE_LI );
877 throw new RuntimeException(
"Expected <rdf:Bag> but got $elm." );
889 if ( $elm === self::NS_RDF .
' Seq' ) {
890 array_unshift( $this->mode, self::MODE_LI );
891 } elseif ( $elm === self::NS_RDF .
' Bag' ) {
893 $this->logger->info( __METHOD__ .
' Expected an rdf:Seq, but got an rdf:Bag. Pretending'
894 .
' it is a Seq, since some buggy software is known to screw this up.' );
895 array_unshift( $this->mode, self::MODE_LI );
897 throw new RuntimeException(
"Expected <rdf:Seq> but got $elm." );
916 if ( $elm === self::NS_RDF .
' Alt' ) {
917 array_unshift( $this->mode, self::MODE_LI_LANG );
919 throw new RuntimeException(
"Expected <rdf:Seq> but got $elm." );
942 if ( $elm === self::NS_RDF .
' Description' ) {
944 array_unshift( $this->mode, self::MODE_QDESC );
945 array_unshift( $this->curItem, $this->curItem[0] );
947 if ( isset(
$attribs[self::NS_RDF .
' value'] ) ) {
948 list( $ns, $tag ) = explode(
' ', $this->curItem[0], 2 );
951 } elseif ( $elm === self::NS_RDF .
' value' ) {
953 throw new RuntimeException( __METHOD__ .
' Encountered <rdf:value> where it was unexpected.' );
956 $this->logger->info( __METHOD__ .
957 " Encountered element <$elm> where only expecting character data as value of " .
959 array_unshift( $this->mode, self::MODE_IGNORE );
960 array_unshift( $this->curItem, $elm );
978 if ( $elm === self::NS_RDF .
' value' ) {
982 array_unshift( $this->mode, self::MODE_IGNORE );
983 array_unshift( $this->curItem, $elm );
1000 if ( $ns !== self::NS_RDF ) {
1001 if ( isset( $this->items[$ns][$tag] ) ) {
1002 if ( isset( $this->items[$ns][$tag][
'structPart'] ) ) {
1008 $this->logger->warning(
"Encountered <$ns:$tag> outside"
1009 .
" of its expected parent. Ignoring." );
1011 array_unshift( $this->mode, self::MODE_IGNORE );
1012 array_unshift( $this->curItem, $ns .
' ' . $tag );
1016 $mode = $this->items[$ns][$tag][
'mode'];
1017 array_unshift( $this->mode,
$mode );
1018 array_unshift( $this->curItem, $ns .
' ' . $tag );
1019 if (
$mode === self::MODE_STRUCT ) {
1020 $this->ancestorStruct = isset( $this->items[$ns][$tag][
'map_name'] )
1021 ? $this->items[$ns][$tag][
'map_name'] : $tag;
1023 if ( $this->charContent !==
false ) {
1026 throw new RuntimeException(
'tag nested in non-whitespace characters.' );
1030 $this->logger->debug( __METHOD__ .
" Ignoring unrecognized element <$ns:$tag>." );
1031 array_unshift( $this->mode, self::MODE_IGNORE );
1032 array_unshift( $this->curItem, $ns .
' ' . $tag );
1061 if ( $ns !== self::NS_RDF ) {
1062 if ( isset( $this->items[$ns][$tag] ) ) {
1063 if ( isset( $this->items[$ns][$this->ancestorStruct][
'children'] )
1064 && !isset( $this->items[$ns][$this->ancestorStruct][
'children'][$tag] )
1068 throw new RuntimeException(
" <$tag> appeared nested in <" . $this->ancestorStruct
1069 .
"> where it is not allowed." );
1071 array_unshift( $this->mode, $this->items[$ns][$tag][
'mode'] );
1072 array_unshift( $this->curItem, $ns .
' ' . $tag );
1073 if ( $this->charContent !==
false ) {
1076 throw new RuntimeException(
"tag <$tag> nested in non-whitespace characters (" .
1077 $this->charContent .
")." );
1080 array_unshift( $this->mode, self::MODE_IGNORE );
1081 array_unshift( $this->curItem, $ns .
' ' . $tag );
1087 if ( $ns === self::NS_RDF && $tag ===
'Description' ) {
1089 array_unshift( $this->mode, self::MODE_STRUCT );
1090 array_unshift( $this->curItem, $this->curItem[0] );
1108 if ( ( $elm ) !== self::NS_RDF .
' li' ) {
1109 throw new RuntimeException(
"<rdf:li> expected but got $elm." );
1112 if ( !isset( $this->mode[1] ) ) {
1115 throw new RuntimeException(
'In mode Li, but no 2xPrevious mode!' );
1118 if ( $this->mode[1] === self::MODE_BAGSTRUCT ) {
1120 array_unshift( $this->mode, self::MODE_STRUCT );
1121 array_unshift( $this->curItem, $elm );
1122 $this->processingArray =
true;
1124 if ( !isset( $this->curItem[1] ) ) {
1126 throw new RuntimeException(
'Can not find parent of BAGSTRUCT.' );
1128 list( $curNS, $curTag ) = explode(
' ', $this->curItem[1] );
1129 $this->ancestorStruct = isset( $this->items[$curNS][$curTag][
'map_name'] )
1130 ? $this->items[$curNS][$curTag][
'map_name'] : $curTag;
1135 array_unshift( $this->mode, self::MODE_SIMPLE );
1138 array_unshift( $this->curItem, $this->curItem[0] );
1139 $this->processingArray =
true;
1158 if ( $elm !== self::NS_RDF .
' li' ) {
1159 throw new RuntimeException( __METHOD__ .
" <rdf:li> expected but got $elm." );
1161 if ( !isset(
$attribs[self::NS_XML .
' lang'] )
1162 || !preg_match(
'/^[-A-Za-z0-9]{2,}$/D',
$attribs[self::NS_XML .
' lang'] )
1164 throw new RuntimeException( __METHOD__
1165 .
" <rdf:li> did not contain, or has invalid xml:lang attribute in lang alternative" );
1169 $this->itemLang = strtolower(
$attribs[self::NS_XML .
' lang'] );
1173 array_unshift( $this->curItem, $this->curItem[0] );
1174 array_unshift( $this->mode, self::MODE_SIMPLE );
1175 $this->processingArray =
true;
1189 if ( $elm === self::NS_RDF .
' RDF'
1190 || $elm ===
'adobe:ns:meta/ xmpmeta'
1191 || $elm ===
'adobe:ns:meta/ xapmeta'
1195 } elseif ( $elm === self::NS_RDF .
' Description' ) {
1196 if ( count( $this->mode ) === 0 ) {
1198 array_unshift( $this->mode, self::MODE_INITIAL );
1200 } elseif ( $elm === self::NS_RDF .
' type' ) {
1207 $this->logger->info( __METHOD__ .
' Encountered <rdf:type> which isn\'t currently supported' );
1210 if ( strpos( $elm,
' ' ) ===
false ) {
1212 $this->logger->info( __METHOD__ .
" Encountered <$elm> which has no namespace. Skipping." );
1217 list( $ns, $tag ) = explode(
' ', $elm, 2 );
1219 if ( count( $this->mode ) === 0 ) {
1221 throw new RuntimeException(
'Error extracting XMP, '
1222 .
"encountered <$elm> with no mode" );
1225 switch ( $this->mode[0] ) {
1258 throw new RuntimeException(
'StartElement in unknown mode: ' . $this->mode[0] );
1283 if ( isset(
$attribs[self::NS_RDF .
' parseType'] )
1284 &&
$attribs[self::NS_RDF .
' parseType'] ===
'Resource'
1285 && $this->mode[0] === self::MODE_SIMPLE
1290 foreach (
$attribs as $name => $val ) {
1291 if ( strpos( $name,
' ' ) ===
false ) {
1294 $this->logger->info( __METHOD__ .
' Encountered non-namespaced attribute: '
1295 .
" $name=\"$val\". Skipping. " );
1298 list( $ns, $tag ) = explode(
' ', $name, 2 );
1299 if ( $ns === self::NS_RDF ) {
1300 if ( $tag ===
'value' || $tag ===
'resource' ) {
1303 $this->
char( $this->xmlParser, $val );
1305 } elseif ( isset( $this->items[$ns][$tag] ) ) {
1306 if ( $this->mode[0] === self::MODE_SIMPLE ) {
1307 throw new RuntimeException( __METHOD__
1308 .
" $ns:$tag found as attribute where not allowed" );
1312 $this->logger->debug( __METHOD__ .
" Ignoring unrecognized element <$ns:$tag>." );
1329 $info =& $this->items[$ns][$tag];
1330 $finalName = isset( $info[
'map_name'] )
1331 ? $info[
'map_name'] : $tag;
1332 if ( isset( $info[
'validate'] ) ) {
1333 if ( is_array( $info[
'validate'] ) ) {
1334 $validate = $info[
'validate'];
1337 $validate = [ $validator, $info[
'validate'] ];
1340 if ( is_callable( $validate ) ) {
1341 call_user_func_array( $validate, [ $info, &$val,
true ] );
1344 if ( is_null( $val ) ) {
1345 $this->logger->info( __METHOD__ .
" <$ns:$tag> failed validation." );
1350 $this->logger->warning( __METHOD__ .
" Validation function for $finalName ("
1351 . $validate[0] .
'::' . $validate[1] .
'()) is not callable.' );
1355 if ( $this->ancestorStruct && $this->processingArray ) {
1358 } elseif ( $this->ancestorStruct ) {
1360 } elseif ( $this->processingArray ) {
1361 if ( $this->itemLang ===
false ) {
1363 $this->results[
'xmp-' . $info[
'map_group']][$finalName][] = $val;
1366 $this->results[
'xmp-' . $info[
'map_group']][$finalName][
$this->itemLang] = $val;
1369 $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.
__construct(LoggerInterface $logger=null)
Primary job is to initialize the XMLParser.
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...
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