CXXXV. XML, Analisadores

Introdução

XML (eXtensible Markup Language) é um formato de dados para intercâmbio de documentos na Web. Ele é um padrão definido pela The World Wide Web consortium (W3C). Informações sobre XML e tecnologias relacionadas pode, ser encontradas em http://www.w3.org/XML/.

Esta extensão do PHP implementa suporte para o James Clark's expat no PHP. Esta ferramenta permite que você analise, mas não valide, documentos XML. Ele suporta três source character encodings também fornecido pelo PHP: US-ASCII, ISO-8859-1 e UTF-8. UTF-16 não é suportado.

Esta extensão te permite criar analisadores XML e então definir manipuladores (handlers) para diferentes eventos XML. Cada analisador XML também tem alguns parâmetros que você pode ajustar.

Dependências

Esta extensão utiliza o expat, que pode ser encontrado em http://www.jclark.com/xml/expat.html. O makefile que vem com o expat, por definição não constrói uma biblioteca, você pode usar as regras de 'make' para isso:
libexpat.a: $(OBJS)
    ar -rc $@ $(OBJS)
    ranlib $@
Um pacote dos fontes RPM do expat podem ser encontrados em http://sourceforge.net/projects/expat/.

Instalação

Estas funções estão por definição habilitadas, usando o pacote expat library. Você pode desabilitar o suporte a XML com --disable-xml. Se você compilou o PHP como um módulo do Apache 1.3.9 ou mais novo, o PHP automaticamente utilizará o pacote expat library do Apache. Regularmente você não quer utilizar a configuração do pacote expat library do PHP --with-expat-dir=DIR, onde DIR apontaria para o diretório base de instalação do expat.

A versão para Windows do PHP tem suporte embutido para esta extensão. Você não precisa carregar nenhuma extensão adicional para utilizar essas funções.

Configurações em execução

Esta extensão não define nenhum parâmetro de configuração no php.ini.

Tipos Resource

xml

O recurso xml enquanto retornado por xml_parser_create() e xml_parser_create_ns() refere-se a uma instância do analisador xml para ser usada coms as funções fornecidas por esta extensão.

Constantes pré-definidas

As contantes abaixo são definidas por esta extensão e somente estarão disponíveis quando a extensão foi compilada com o PHP ou carregada dinamicamente durante a execução.

XML_ERROR_NONE (integer)

XML_ERROR_NO_MEMORY (integer)

XML_ERROR_SYNTAX (integer)

XML_ERROR_NO_ELEMENTS (integer)

XML_ERROR_INVALID_TOKEN (integer)

XML_ERROR_UNCLOSED_TOKEN (integer)

XML_ERROR_PARTIAL_CHAR (integer)

XML_ERROR_TAG_MISMATCH (integer)

XML_ERROR_DUPLICATE_ATTRIBUTE (integer)

XML_ERROR_JUNK_AFTER_DOC_ELEMENT (integer)

XML_ERROR_PARAM_ENTITY_REF (integer)

XML_ERROR_UNDEFINED_ENTITY (integer)

XML_ERROR_RECURSIVE_ENTITY_REF (integer)

XML_ERROR_ASYNC_ENTITY (integer)

XML_ERROR_BAD_CHAR_REF (integer)

XML_ERROR_BINARY_ENTITY_REF (integer)

XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF (integer)

XML_ERROR_MISPLACED_XML_PI (integer)

XML_ERROR_UNKNOWN_ENCODING (integer)

XML_ERROR_INCORRECT_ENCODING (integer)

XML_ERROR_UNCLOSED_CDATA_SECTION (integer)

XML_ERROR_EXTERNAL_ENTITY_HANDLING (integer)

XML_OPTION_CASE_FOLDING (integer)

XML_OPTION_TARGET_ENCODING (integer)

XML_OPTION_SKIP_TAGSTART (integer)

XML_OPTION_SKIP_WHITE (integer)

Manipuladores (handlers) de Evento

Os manipuladores (handlers) de eventos de XML definidos são:

Tabela 1. Manipuladores XML Suportados

função do PHP para definir o manipulador(handler)Descrição do evento
xml_set_element_handler() Eventos elementares são emitidos toda vez que o analisador XML o início ou o fim das tags. Há manipuladores (handlers) separadores para tags de início e tags de fim.
xml_set_character_data_handler() Dados de caractere são aproximadamente todo o conteúdo de documentos XML, incluindo espaços em branco entre as tags. Note que o analisador XML não adiciona ou remove qualquer espaço em branco, ele está pronto para a aplicação se você decidir que espaços em branco são significativos.
xml_set_processing_instruction_handler() Programadores de PHP já estariam familiarizados com instruções de processo (PIs). <?php ?> é uma instrução de processo, onde php é chamada de o "PI target". O tratamento destes são application-specific, exceto que todos os PI targets iniciados com "XML" estão reservados.
xml_set_default_handler() O que não vai para outro manipulador vai para o manipulador padrão. Você conseguirá coisas como o XML e declarações do tipo de documento no manipulador padrão.
xml_set_unparsed_entity_decl_handler() Este manipulador será chamado por uma declaração de um entity não analisada (NDATA).
xml_set_notation_decl_handler() Este manipulador é chamado pela declaração de uma nota.
xml_set_external_entity_ref_handler() Este manipulador é chamado quando o analisador XML encontra uma referência para uma entity geral analizada externamente. Isto pode ser uma referência para um arquivo ou URL, por examplo. Veja Um exemplo de entity externa para uma demonstração.

Case Folding

As funções de elementos do manipulador podem conseguir os nomes dos elementos case-folded. Case-folding é definida pelo padrão XML como "um processo aplicado para uma sequência de caracateres, em que aqueles identificados como minúsculos são substituidos pelos seus maiúsculos equivalentes". Em outras palavras, quando vai pro XML, case-folding simplesmente significa mudar pra maiúsculas.

Por definição, todos os nomes de elementos que são passados para as funções de manipulador são case-folded. Este comportamento pode ser perguntado e controlado pelo analisador XML com o xml_parser_get_option() e as funções xml_parser_set_option(), respectivamente.

Error Codes

As seguintes constantes são definidas para erros no código XML (conforme retornado por xml_parse()):

XML_ERROR_NONE
XML_ERROR_NO_MEMORY
XML_ERROR_SYNTAX
XML_ERROR_NO_ELEMENTS
XML_ERROR_INVALID_TOKEN
XML_ERROR_UNCLOSED_TOKEN
XML_ERROR_PARTIAL_CHAR
XML_ERROR_TAG_MISMATCH
XML_ERROR_DUPLICATE_ATTRIBUTE
XML_ERROR_JUNK_AFTER_DOC_ELEMENT
XML_ERROR_PARAM_ENTITY_REF
XML_ERROR_UNDEFINED_ENTITY
XML_ERROR_RECURSIVE_ENTITY_REF
XML_ERROR_ASYNC_ENTITY
XML_ERROR_BAD_CHAR_REF
XML_ERROR_BINARY_ENTITY_REF
XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF
XML_ERROR_MISPLACED_XML_PI
XML_ERROR_UNKNOWN_ENCODING
XML_ERROR_INCORRECT_ENCODING
XML_ERROR_UNCLOSED_CDATA_SECTION
XML_ERROR_EXTERNAL_ENTITY_HANDLING

Codificação de Caracter

A Extensão XML do PHP suporta o caracter Unicode definido por character encodings (codificação de caracteres) diferentes. Há dois tipos de codificação de caracteres, source encoding e target encoding. A apresentação interna do PHP do documento é sempre codificada com UTF-8.

Source encoding é feita quando um documento XML é analisado. Em criando um analisador XML, um source encoding pode ser especificado (Esta codificação não poderá ser mudada após o tempo de vida do analisador XML). As codificações suportadas são ISO-8859-1, US-ASCII e UTF-8. O segundo são codificações single-byte, que significa que cada caractere é representado por um byte simples. UTF-8 pode codificar caracteres compostos por um número de bits variável (acima de 21) em um de seus 4 bytes. O source encoding padrão utilizado pelo PHP é ISO-8859-1.

Target encoding é feito quando o PHP passa dados para as funções do analisador XML. Quando um analisador XML é criado, o target encoding é definido igual ao source encoding, mas este pode ser mudado em qualquer ponto. O target encoding afetará dados de caracter tão bem como nome de tags e processando alvos da instrução.

Se o analisador XML encontra caracteres de fora da linha que seu source encoding é capaz de detalhar, ele retornará um erro.

Se PHP encontra caracteres no documento XML analisado que não podem ser detalhados selecionados com target encoding, os caracteres com problema serão "demoted". Atualmente, isto significa que tais caracteres serão substituidos por um sinal de interrogação.

Exemplos

Aqui estão alguns exemplos de scripts PHP analisando documentos XML.

Exemplo estruturado de elementos XML

Este primeiro exemplo mostra a estrutura de elementos iniciais num documento com distanciamento da margem.

Exemplo 1. Mostra estrutura do elemento XML

<?php
$file
= "data.xml";
$depth = array();

function
startElement($parser, $name, $attrs) {
    global
$depth;
    for (
$i = 0; $i < $depth[$parser]; $i++) {
        echo
"  ";
    }
    echo
"$name\n";
    
$depth[$parser]++;
}

function
endElement($parser, $name) {
    global
$depth;
    
$depth[$parser]--;
}

$xml_parser = xml_parser_create();
xml_set_element_handler($xml_parser, "startElement", "endElement");
if (!(
$fp = fopen($file, "r"))) {
    die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML error: %s at line %d",
                    
xml_error_string(xml_get_error_code($xml_parser)),
                    
xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);
?>

Exemplo arrumação de Tag XML

Exemplo 2. Arrumar XML to HTML

Este exemplo arruma tags do documento XML diretamente para tags HTML. Elementos não encontrados no "array de arrumação" são ignorados. É claro, este exemplo trabalhará apenas com um tipo de documento XML específico.

<?php
$file
= "data.xml";
$map_array = array(
    
"BOLD"     => "B",
    
"EMPHASIS" => "I",
    
"LITERAL"  => "TT"
);

function
startElement($parser, $name, $attrs) {
    global
$map_array;
    if (
$htmltag == $map_array[$name]) {
        echo
"<$htmltag>";
    }
}

function
endElement($parser, $name) {
    global
$map_array;
    if (
$htmltag == $map_array[$name]) {
        echo
"</$htmltag>";
    }
}

function
characterData($parser, $data) {
    echo
$data;
}

$xml_parser = xml_parser_create();
// use case-folding so we are sure to find the tag in $map_array
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, true);
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData");
if (!(
$fp = fopen($file, "r"))) {
    die(
"could not open XML input");
}

while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML error: %s at line %d",
                    
xml_error_string(xml_get_error_code($xml_parser)),
                    
xml_get_current_line_number($xml_parser)));
    }
}
xml_parser_free($xml_parser);
?>

Exemplo XML de Entity externa

Este exemplo destaca o código XML. Ele ilusta como utilizar um analisador de referência a uma entity externa para incluir e analisar outros documentos, tão bem como PIs (instruções de processo) podem ser processadas, e uma forma de determinar "trust" para PIs contendo códigos.

Documentos XML que podem ser utilizados para este exemplo são encontrados abaixo do exemplo (xmltest.xml e xmltest2.xml.)

Exemplo 3. Exemplo de Entity externa

<?php
$file
= "xmltest.xml";

function
trustedFile($file) {
    
// only trust local files owned by ourselves
    
if (!eregi("^([a-z]+)://", $file)
        &&
fileowner($file) == getmyuid()) {
            return
true;
    }
    return
false;
}

function
startElement($parser, $name, $attribs) {
    echo
"&lt;<font color=\"#0000cc\">$name</font>";
    if (
sizeof($attribs)) {
        while (list(
$k, $v) = each($attribs)) {
            echo
" <font color=\"#009900\">$k</font>=\"<font
                   color=
\"#990000\">$v</font>\"";
        }
    }
    echo
"&gt;";
}

function
endElement($parser, $name) {
    echo
"&lt;/<font color=\"#0000cc\">$name</font>&gt;";
}

function
characterData($parser, $data) {
    echo
"<b>$data</b>";
}

function
PIHandler($parser, $target, $data) {
    switch (
strtolower($target)) {
        case
"php":
            global
$parser_file;
            
// If the parsed document is "trusted", we say it is safe
            // to execute PHP code inside it.  If not, display the code
            // instead.
            
if (trustedFile($parser_file[$parser])) {
                eval(
$data);
            } else {
                
printf("Untrusted PHP code: <i>%s</i>",
                        
htmlspecialchars($data));
            }
            break;
    }
}

function
defaultHandler($parser, $data) {
    if (
substr($data, 0, 1) == "&" && substr($data, -1, 1) == ";") {
        
printf('<font color="#aa00aa">%s</font>',
                
htmlspecialchars($data));
    } else {
        
printf('<font size="-1">%s</font>',
                
htmlspecialchars($data));
    }
}

function
externalEntityRefHandler($parser, $openEntityNames, $base, $systemId,
                                  
$publicId) {
    if (
$systemId) {
        if (!list(
$parser, $fp) = new_xml_parser($systemId)) {
            
printf("Could not open entity %s at %s\n", $openEntityNames,
                   
$systemId);
            return
false;
        }
        while (
$data = fread($fp, 4096)) {
            if (!
xml_parse($parser, $data, feof($fp))) {
                
printf("XML error: %s at line %d while parsing entity %s\n",
                       
xml_error_string(xml_get_error_code($parser)),
                       
xml_get_current_line_number($parser), $openEntityNames);
                
xml_parser_free($parser);
                return
false;
            }
        }
        
xml_parser_free($parser);
        return
true;
    }
    return
false;
}

function
new_xml_parser($file) {
    global
$parser_file;

    
$xml_parser = xml_parser_create();
    
xml_parser_set_option($xml_parser, XML_OPTION_CASE_FOLDING, 1);
    
xml_set_element_handler($xml_parser, "startElement", "endElement");
    
xml_set_character_data_handler($xml_parser, "characterData");
    
xml_set_processing_instruction_handler($xml_parser, "PIHandler");
    
xml_set_default_handler($xml_parser, "defaultHandler");
    
xml_set_external_entity_ref_handler($xml_parser, "externalEntityRefHandler");
    
    if (!(
$fp = @fopen($file, "r"))) {
        return
false;
    }
    if (!
is_array($parser_file)) {
        
settype($parser_file, "array");
    }
    
$parser_file[$xml_parser] = $file;
    return array(
$xml_parser, $fp);
}

if (!(list(
$xml_parser, $fp) = new_xml_parser($file))) {
    die(
"could not open XML input");
}

echo
"<pre>";
while (
$data = fread($fp, 4096)) {
    if (!
xml_parse($xml_parser, $data, feof($fp))) {
        die(
sprintf("XML error: %s at line %d\n",
                    
xml_error_string(xml_get_error_code($xml_parser)),
                    
xml_get_current_line_number($xml_parser)));
    }
}
echo
"</pre>";
echo
"parse complete\n";
xml_parser_free($xml_parser);

?>

Exemplo 4. xmltest.xml

<?xml version='1.0'?>
<!DOCTYPE chapter SYSTEM "/just/a/test.dtd" [
<!ENTITY plainEntity "FOO entity">
<!ENTITY systemEntity SYSTEM "xmltest2.xml">
]>
<chapter>
 <TITLE>Título &plainEntity;</TITLE>
 <para>
  <informaltable>
   <tgroup cols="3">
    <tbody>
     <row><entry>a1</entry><entry morerows="1">b1</entry><entry>c1</entry></row>
     <row><entry>a2</entry><entry>c2</entry></row>
     <row><entry>a3</entry><entry>b3</entry><entry>c3</entry></row>
    </tbody>
   </tgroup>
  </informaltable>
 </para>
 &systemEntity;
 <section id="about">
  <title>Sobre este documento</title>
  <para>
   <!-- this is a comment -->
   <?php echo 'Hi!  This is PHP version '.phpversion(); ?>
  </para>
 </section>
</chapter>

Este arquivo está incluido de xmltest.xml:

Exemplo 5. xmltest2.xml

<?xml version="1.0"?>
<!DOCTYPE foo [
<!ENTITY testEnt "test entity">
]>
<foo>
   <element attrib="value"/>
   &testEnt;
   <?php print "This is some more PHP code being executed."; ?>
</foo>

Índice
utf8_decode --  Converte uma string com caracteres ISO-8859-1 codificadas com UTF-8 para single-byte ISO-8859-1.
utf8_encode -- Codifica um string ISO-8859-1 para UTF-8
xml_error_string -- Obtém uma string de erro do analisador XML
xml_get_current_byte_index -- Obtém o índice do byte atual para um analisador XML
xml_get_current_column_number --  Obtém o número da coluna atual para um analisador XML
xml_get_current_line_number -- Obtém o número da linha para um analisador XML
xml_get_error_code -- Obtém um código de erro do analisador XML
xml_parse_into_struct -- Analisa dados XML dentro de uma estrutura de array
xml_parse -- Inicia a análise em um documento XML
xml_parser_create_ns --  Cria um analisador XML com suporte a namespace (uma estrutura do XML)
xml_parser_create -- cria um analisador XML
xml_parser_free -- Free an XML parser
xml_parser_get_option -- Get options from an XML parser
xml_parser_set_option -- Set options in an XML parser
xml_set_character_data_handler -- Set up character data handler
xml_set_default_handler -- Set up default handler
xml_set_element_handler -- Set up start and end element handlers
xml_set_end_namespace_decl_handler --  Set up end namespace declaration handler
xml_set_external_entity_ref_handler -- Set up external entity reference handler
xml_set_notation_decl_handler -- Set up notation declaration handler
xml_set_object -- Use XML Parser within an object
xml_set_processing_instruction_handler --  Set up processing instruction (PI) handler
xml_set_start_namespace_decl_handler --  Set up start namespace declaration handler
xml_set_unparsed_entity_decl_handler --  Set up unparsed entity declaration handler