Em primeiro lugar, tenho que admitir que adoro os padrões de informática. Se todos seguissem os padrões da indústria, a Internet seria um meio melhor. O uso de formatos padronizados de troca de dados viabiliza modelos de computação abertos e independentes de plataforma. É por isso que sou um entusiasta de XML.
Felizmente, minha linguagem de script favorita não apenas oferece suporte a XML, mas também o oferece cada vez mais. O PHP me permite publicar rapidamente documentos XML na Internet, coletar informações estatísticas sobre documentos XML e converter documentos XML em outros formatos. Por exemplo, costumo usar os recursos de processamento XML do PHP para gerenciar artigos e livros que escrevo em XML.
Neste artigo, discutirei qualquer uso do analisador Expat integrado do PHP para processar documentos XML. Através de exemplos, demonstrarei o método de processamento do Expat. Ao mesmo tempo, o exemplo pode mostrar como:
Criar sua própria função de processamento
Convertendo documentos XML em suas próprias estruturas de dados PHP
Introdução
O analisador do Expat XML, também chamado de processador XML, permite que os programas acessem a estrutura e o conteúdo dos documentos XML. Expat é um analisador XML para a linguagem de script PHP. Também é usado em outros projetos, como Mozilla, Apache e Perl.
O que é um analisador baseado em eventos?
Existem dois tipos básicos de analisadores XML:
Analisadores baseados em árvore: convertem documentos XML em estruturas de árvore. Este tipo de analisador analisa o artigo inteiro enquanto fornece uma API para acessar cada elemento da árvore resultante. Seu padrão comum é DOM (Document Object Model).
Analisador baseado em eventos: trate documentos XML como uma série de eventos. Quando ocorre um evento especial, o analisador chamará a função fornecida pelo desenvolvedor para tratá-lo.
O analisador baseado em eventos tem uma visão do documento XML focada nos dados, o que significa que ele se concentra na parte de dados do documento XML, e não em sua estrutura. Esses analisadores processam o documento do início ao fim e relatam eventos como - início do elemento, fim do elemento, início dos dados do recurso, etc. - para o aplicativo por meio de funções de retorno de chamada. A seguir está um exemplo de documento XML para "Hello-World":
<greeting>
Olá mundo
</greeting>
O analisador baseado em eventos reportará três eventos:
elemento inicial: saudação
O início do item CDATA, o valor é: Hello World
Elemento final: saudação
Ao contrário dos analisadores baseados em árvore, os analisadores baseados em eventos não produzem uma estrutura que descreva o documento. Nos itens CDATA, o analisador baseado em eventos não permitirá obter as informações de saudação do elemento pai.
No entanto, proporciona um acesso de nível inferior, o que permite uma melhor utilização dos recursos e um acesso mais rápido. Dessa forma, não há necessidade de colocar todo o documento na memória; na verdade, o documento inteiro pode até ser maior que o valor real da memória;
Expat é um analisador baseado em eventos. Claro, se você usar o Expat, ele também poderá gerar uma estrutura de árvore nativa completa em PHP, se necessário.
O exemplo Hello-World acima inclui o formato XML completo. Mas é inválido porque não há DTD (Definição de Tipo de Documento) associado a ele nem um DTD incorporado.
Para Expat, isso não faz diferença: Expat é um analisador que não verifica a validade e, portanto, ignora qualquer DTD associado ao documento. Deve-se notar, entretanto, que o documento ainda precisa ser totalmente formatado, caso contrário, o Expat (como outros analisadores compatíveis com XML) irá parar com uma mensagem de erro.
Como um analisador que não verifica a validade, a velocidade e a leveza do Exapt o tornam adequado para aplicações de Internet.
Compilando Expat
Expat pode ser compilado na versão PHP3.0.6 (ou superior). A partir do Apache 1.3.9, o Expat foi incluído como parte do Apache. Em sistemas Unix, você pode compilá-lo em PHP configurando o PHP com a opção -with-xml.
Se você compilar o PHP como um módulo do Apache, o Expat será incluído como parte do Apache por padrão. No Windows, você deve carregar a biblioteca de vínculo dinâmico XML.
Exemplos de XML: XMLstats
Uma maneira de aprender sobre as funções do Expat é através de exemplos. O exemplo que discutiremos é o uso do Expat para coletar estatísticas sobre documentos XML.
Para cada elemento do documento, serão geradas as seguintes informações:
o número de vezes que o elemento é utilizado no documento
A quantidade de dados de caracteres neste elemento
o elemento pai do elemento
elementos filhos do elemento
Nota: Para fins de demonstração, usamos PHP para gerar uma estrutura para salvar o elemento pai e o elemento filho do elemento.
preparada
para gerar a instância do analisador XML é xml_parser_create(). Esta instância será usada para todas as funções futuras. Essa ideia é muito semelhante à tag de conexão da função MySQL em PHP. Antes de analisar o documento, os analisadores baseados em eventos geralmente exigem que você registre uma função de retorno de chamada - a ser chamada quando ocorrer um evento específico. Expat não tem eventos de exceção. Ele define os seguintes sete eventos possíveis:
objeto Análise XML função descrição
elemento xml_set_element_handler()
dados de caracteres de início e fim do elemento xml_set_character_data_handler() dados de início de caracteres
entidade externa xml_set_external_entity_ref_handler() entidade externa
Entidade externa não analisada xml_set_unparsed_entity_decl_handler ( ) A ocorrência de uma
instrução de processamento de entidade externa não resolvida xml_set_processing_instruction_handler() A ocorrência de uma
declaração de notação de instrução de processamento xml_set_notation_decl_handler() A ocorrência de uma declaração de notação
default xml_set_default_handler() Outros eventos sem uma função de manipulador especificada
Todas as funções de retorno de chamada devem usar uma instância do analisador como seu primeiro parâmetro (existem outros parâmetros além).
Para o script de amostra no final deste artigo. O que você precisa observar é que ele usa funções de processamento de elementos e funções de processamento de dados de caracteres. A função manipuladora de retorno de chamada do elemento é registrada por meio de xml_set_element_handler().
Esta função leva três parâmetros:
uma instância do analisador
O nome da função de retorno de chamada que trata o elemento inicial
O nome da função de retorno de chamada que trata o elemento de fechamento
A função de retorno de chamada deve existir quando a análise do documento XML começar. Eles devem ser definidos de forma consistente com os protótipos descritos no manual do PHP.
Por exemplo, Expat passa três argumentos para a função manipuladora do elemento inicial. No exemplo de script, ele é definido da seguinte forma:
function start_element($parser, $name, $attrs)
O primeiro parâmetro é o identificador do analisador, o segundo parâmetro é o nome do elemento inicial e o terceiro parâmetro contém todos os atributos e valores da matriz do elemento.
Assim que você começar a analisar o documento XML, o Expat chamará sua função start_element() e passará os parâmetros sempre que encontrar o elemento inicial.
A opção Case Folding do XML
usa a função xml_parser_set_option() para desativar a opção Case Folding. Esta opção está ativada por padrão, fazendo com que os nomes dos elementos passados para as funções do manipulador sejam convertidos automaticamente para letras maiúsculas. Mas o XML diferencia maiúsculas de minúsculas (portanto, maiúsculas e minúsculas são muito importantes para documentos XML estatísticos). Para nosso exemplo, a opção de dobrar a caixa deve estar desativada.
Analisando o documento
Depois de concluir todos os preparativos, agora o script pode finalmente analisar o documento XML:
Xml_parse_from_file(), uma função personalizada, abre o arquivo especificado no parâmetro e o analisa em tamanho de 4kb
xml_parse(), assim como xml_parse_from_file(), retornará false quando ocorrer um erro, ou seja, quando o documento XML não estiver totalmente formatado.
Você pode usar a função xml_get_error_code() para obter o código numérico do último erro. Passe este código numérico para a função xml_error_string() para obter as informações de texto do erro.
Produz o número da linha atual do XML, facilitando a depuração.
Durante o processo de análise, a função de retorno de chamada é chamada.
Descrevendo a estrutura do documento
Ao analisar um documento, a questão que precisa ser abordada com o Expat é: como manter uma descrição básica da estrutura do documento?
Conforme mencionado anteriormente, o próprio analisador baseado em eventos não produz nenhuma informação estrutural.
No entanto, a estrutura da tag é um recurso importante do XML. Por exemplo, a sequência de elementos <book><title> significa algo diferente de <figure><title>. Dito isso, qualquer autor lhe dirá que títulos de livros e títulos de imagens não têm nada a ver um com o outro, embora ambos usem o termo “título”. Portanto, para processar XML de forma eficiente com um analisador baseado em eventos, você deve usar suas próprias pilhas ou listas para manter informações estruturais sobre o documento.
Para espelhar a estrutura do documento, o script precisa conhecer pelo menos o elemento pai do elemento atual. Isso não é possível com a API do Exapt. Ela apenas relata eventos do elemento atual sem qualquer informação contextual. Portanto, você precisa construir sua própria estrutura de pilha.
O exemplo de script usa uma estrutura de pilha FILO (primeiro a entrar, último a sair). Através de um array, a pilha salvará todos os elementos iniciais. Para a função de processamento de elemento inicial, o elemento atual será colocado no topo da pilha pela função array_push(). Da mesma forma, a função de processamento do elemento final remove o elemento superior por meio de array_pop().
Para a sequência <book><title></title></book>, a pilha é preenchida da seguinte forma:
elemento inicial book: atribui "book" ao primeiro elemento da pilha ($stack[0]).
Título do elemento inicial: Atribua "título" ao topo da pilha ($stack[1]).
Título do elemento final: Remova o elemento superior da pilha ($stack[1]).
Título do elemento final: Remova o elemento superior da pilha ($stack[0]).
PHP3.0 implementa o exemplo controlando manualmente o aninhamento de elementos através de uma variável $profundidade. Isso faz com que o script pareça mais complexo. PHP4.0 usa as funções array_pop() e array_push() para tornar o script mais conciso.
Coletando dados
Para coletar informações sobre cada elemento, o script precisa lembrar os eventos de cada elemento. Salve todos os diferentes elementos do documento usando uma variável de array global $elements. Os itens do array são instâncias da classe do elemento e possuem 4 propriedades (variáveis da classe)
$count - o número de vezes que o elemento foi encontrado no documento
$chars – Número de bytes de eventos de caracteres no elemento
$parents - elemento pai
$childs - elementos filhos
Como você pode ver, salvar instâncias de classe em um array é muito fácil.
Nota: Uma característica do PHP é que você pode percorrer toda a estrutura da classe através de um loop while(list() = each()), assim como você percorre todo o array correspondente. Todas as variáveis de classe (e nomes de métodos quando você usa PHP3.0) são exibidas como strings.
Quando um elemento é encontrado, precisamos incrementar seu contador correspondente para saber quantas vezes ele aparece no documento. O elemento count no item $elements correspondente também é incrementado em um.
Também precisamos informar ao elemento pai que o elemento atual é seu elemento filho. Portanto, o nome do elemento atual será adicionado ao item na matriz $childs do elemento pai. Finalmente, o elemento atual deve lembrar quem é seu pai. Portanto, o elemento pai é adicionado ao item na matriz $parents do elemento atual.
Exibindo estatísticas
O código restante percorre o array $elements e seus subarrays para exibir suas estatísticas. Este é o loop aninhado mais simples. Embora produza os resultados corretos, o código não é conciso nem possui nenhuma habilidade especial. É apenas um loop que você pode usar todos os dias para concluir seu trabalho.
Os exemplos de script são projetados para serem invocados a partir da linha de comando através da abordagem CGI do PHP. Portanto, o formato de saída do resultado estatístico é o formato de texto. Se quiser usar o script na Internet, você precisará modificar a função de saída para gerar o formato HTML.
Resumo
Exapt é um analisador XML para PHP. Por ser um analisador baseado em eventos, não produz uma descrição estrutural do documento. Mas, ao fornecer acesso de baixo nível, permite uma melhor utilização dos recursos e um acesso mais rápido.
Como um analisador que não verifica a validade, o Expat ignora DTDs anexados a documentos XML, mas irá parar com uma mensagem de erro se o documento não estiver bem formado.
Fornece manipuladores de eventos para processar documentos
Crie suas próprias estruturas de eventos, como pilhas e árvores, para aproveitar as vantagens da marcação de informações estruturadas em XML.
Novos programas XML aparecem todos os dias, e o suporte do PHP para XML está sendo constantemente fortalecido (por exemplo, foi adicionado suporte para o analisador XML baseado em DOM LibXML).
Com PHP e Expat, você pode se preparar para os próximos padrões que são válidos, abertos e independentes de plataforma.
Exemplo
<?
/********************************************** ***********************************
* Nome: exemplo de análise XML: estatísticas de informações de documentos XML
* descrever
* Este exemplo usa o analisador Expat do PHP para coletar e contar informações do documento XML (por exemplo: o número de ocorrências de cada elemento, elementos pai e elementos filho
* Arquivo XML como parâmetro./xmlstats_PHP4.php3 test.xml
* $Requires: Requisitos para Expat: Expat PHP4.0 é compilado no modo CGI
************************************************** * ***************************/
// O primeiro parâmetro é o arquivo XML
$file = $argv[1];
// Inicialização de variáveis
$elementos = $pilha = array();
$total_elements = $total_chars = 0;
//Classe básica de elementos
elemento de classe
{
var $contagem = 0;
var $ caracteres = 0;
var $pais = array();
var $crianças = array();
}
//Função para analisar arquivos XML
função xml_parse_from_file($parser, $file)
{
if(!file_exists($arquivo))
{
die("Não foi possível encontrar o arquivo "$arquivo".");
}
if(!($fp = @fopen($arquivo, "r")))
{
die("Não foi possível abrir o arquivo "$arquivo".");
}
while($dados = fread($fp, 4096))
{
if(!xml_parse($parser, $data, feof($fp)))
{
retornar(falso);
}
}
fclose($fp)
;
}
// Função de resultado de saída (formato de caixa)
função print_box($título, $valor)
{
printf("n+%'-60s+n", "");
printf("|%20s", "$título:");
printf("%14s", $valor);
printf("%26s|n", "");
printf("+%'-60s+n", "");
}
// Função de resultado de saída (formato de linha)
função print_line($título, $valor)
{
printf("%20s", "$título:");
printf("%15sn", $valor);
}
//Função de classificação
função meu_sort($a, $b)
{
return(is_object($a) && is_object($b) ? $b->count - $a->count: 0);
}
função start_element($parser, $name, $attrs)
{
global $elements, $stack;
// O elemento já está no array global $elements?
if(!isset($elementos[$nome]))
{
// Não - adiciona uma instância de classe de um elemento
$elemento = novo elemento;
$elementos[$nome] = $elemento;
}
//Incrementa o contador deste elemento em um
$elements[$name]->count++;
// Existe um elemento pai?
if(isset($stack[count($stack)-1]))
{
// Sim – atribua o elemento pai a $last_element
$last_element = $stack[count($stack)-1];
// Se o array do elemento pai do elemento atual estiver vazio, inicialize-o como 0
if(!isset($elementos[$nome]->pais[$último_elemento]))
{
$elements[$name]->parents[$last_element] = 0;
}
//Incrementa o contador do elemento pai deste elemento em um
$elements[$name]->parents[$last_element]++;
// Se o array do elemento filho do elemento pai do elemento atual estiver vazio, ele será inicializado com 0
if(!isset($elements[$last_element]-> filho[$ nome]))
{
$elements[$last_element]->childs[$name] = 0;
}
// Adiciona um ao contador do elemento filho do elemento pai do elemento.
$elements[$last_element]->childs[$nome]++;
}
//Adiciona o elemento atual à pilha
array_push($pilha, $nome);
}
função stop_element($parser, $name)
{
global $stack;
// Remove o elemento superior da pilha
array_pop($pilha);
}
função char_data($parser, $data)
{
global $elements, $stack, $profundidade
// Aumenta o número de caracteres do elemento atual
;
$elements[$stack][count($stack)-1]]->chars += strlen(trim($data));
}
// Gera instância do analisador
$parser = xml_parser_create();
// Definir função de processamento
xml_set_element_handler($parser, "start_element", "stop_element");
xml_set_character_data_handler($parser, "char_data");
XML_OPTION_CASE_FOLDING
, 0);
$ret = xml_parse_from_file($parser, $file);
if(!$ret)
{
die(sprintf("Erro XML: %s na linha %d",
xml_error_string(xml_get_error_code($parser)),
xml_get_current_line_number($parser)));
}
// Libera o analisador
xml_parser_free($parser);
// Libera o elemento auxiliar
unset($elementos["elemento_atual"]);
unset($elements["last_element"]);
// Classifica de acordo com o número de elementos
uasort($elements, "my_sort");
// Percorre $elements para coletar informações do elemento
while(lista($nome, $elemento) = cada($elementos))
{
print_box("Nome do elemento", $nome);
print_line("Contagem de elementos", $elemento->contagem);
print_line("Contagem de caracteres", $element->chars);
printf("n%20sn", "* Elementos pai")
;
while(lista($chave, $valor) = cada($elemento->pais))
{
print_line($chave, $valor);
}
if(contagem($elemento->pais) == 0)
{
printf("%35sn", "[elemento raiz]");
}
// Faz um loop pelo filho deste elemento e gera o resultado
printf("n%20sn", "* Elementos filhos");
while(lista($chave, $valor) = cada($elemento->filhos))
{
print_line($chave, $valor);
}
if(contagem($elemento->filhos) == 0)
{
printf("%35sn", "[sem filhos]");
}
$total_elements += $element->contagem;
$total_chars += $element->chars;
}
//resultado final
print_box("Total de elementos", $total_elements);
print_box("Total de caracteres", $total_chars);
?>