Convertisseur xml <=> tableaux associatifs php

Contenu du snippet

Cette librairie de quelque lignes permet de convertir un contenu XML en tableau associatif php et inversement.
Le plus gros du code concerne le sens XML => Assoc car il faut parser les balises XML.
Ceux qui souhaitent éviter d'utiliser xml_parse() pour une raison ou une autre y trouverons leur compte.

Source / Exemple :


<?

# lib.assoc.xml.php: Assoc <=> XML Conversion
#
# Date:     August, 29 2004
# Author:   YARD Mathieu / FAYA Solutions
# URL:      http://www.faya.fr/
# Contact:  lib.assoc.xml@faya.fr
#

define(AX_NAME_SUBKEY,'__name__');
define(AX_CONTENT_SUBKEY,'__content__');
define(AX_DEFNAME, 'unknown');

###### PRIVATE FUNCTIONS ###############################################################

# Is next element an xml nod ? (or is it a leaf...)
function is_balise_next($buf,$os)
{
  $l=strlen($buf);
  $x = $buf[$os];  
  while((ord($x)<=32)&&($os<$l)) $os++;
  return($x=='<');  
} 

# Parse strings like keyword="value"
function read_kv(&$os, $buf)
{
  $sw = 0; 
  $lb = strlen($buf);
  
  while($os<$lb)
  {
    $x = $buf[$os];
    if(($x=='/')||($x==' ')||($x=='>')) break;
    
    if($x == '=') 
      $sw = 1; 
    else 
      $val[$sw] .= $x;
    
    $os++;
  }
  
  list($k,$v) = $val;
  
  # Conditional quotes extraction
  if(ereg("^[\'\"](.*)[\'\"]$",$v,$tbl)) $v = $tbl[1];

  return(array($k=>$v));
}

###### PUBLIC FUNCTIONS #############################################################

# Create a root XML Object
function ax_root($name = null, $content = null)
{
  $assoc = array();
  if($name) $assoc[AX_NAME_SUBKEY] = $name;
  if($content) $assoc[AX_CONTENT_SUBKEY] = $content;   
  return($assoc);
}

# Create an XML Object
function &ax_balise(&$root, $name = null, $content = null)
{
  $assoc = ax_root($name,$content);
  return($root[] = $assoc);
}

# Generate XML code from an assoc array.
function assoc2xml(&$xmlbuf, $assoc)
{
  if(!is_array($assoc)) return true;  // Nothing to do
  
  # Object name extraction
  foreach($assoc as $ref => $content)
  {
    if(is_numeric($ref))
      assoc2xml(&$sub,$content);
    else
    {
      if($ref==AX_NAME_SUBKEY)
        $name = $content;
      elseif($ref==AX_CONTENT_SUBKEY)
        $bc = $content; 
      else
      {
        $out .= ' '.$ref;
        if($content) $out .= '="'.addslashes($content).'"';
      }
    } 
  }
  
  # If there's some subnods :  
  if($sub)
    $out .= '>'.$sub.'</'.$name.'>';    
  # If there's a content :
  elseif($bc)
    $out .= '>'.htmlentities($bc).'</'.$name.'>';
  # Empty marker :
  else $out .= '/>';
  
  $xmlbuf .= '<'.$name.$out;
  return(true); 
}

# Parsing XML into an assoc array.
function xml2assoc(&$assoc,$xmlbuf,$more=array())
{
  $lenbuf=strlen($xmlbuf); 
  $os=0;
  while($os<$lenbuf)
  {
    $x = $xmlbuf[$os];
    if(ord($x)>32)
    {
      # A marker should be found 
      if(!xa_xbalise_read($os, $assoc, $xmlbuf, $more)) return false;
    }    
    else $os++;     
  }    
  return true;
}

# XML marker parsing
//function xa_xbalise_read(&$os, &$assoc, $xmlbuf, $more = array())
function xa_xbalise_read(&$os, &$assoc, $xmlbuf, $more = array("strict"=>true))
{
  if(($x = $xmlbuf[$os++]) != '<') return false;
        
  $xbufsz = strlen($xmlbuf);

  # Read of marker name  
  while($os < $xbufsz)
  {
    $x = $xmlbuf[$os];
    if(($x == ' ')||($x == '/')||($x == '>')) break;
    $name .= $x;
    $os++;
  }
  
  $assoc[AX_NAME_SUBKEY] = $name;

  # Read of marker parameter (xxxxx="yyyyy")
  while($os<$xbufsz)
  {
    $x = $xmlbuf[$os];
    
    if($x=='/') 
      if($xmlbuf[$os+1]=='>') { $no_close=true; $os+=2; break; } # End of leaf marker
    if($x=='>') { $os++; break; }   # End of nod marker

    # Read unique assignement
    if(ord($x)>32) 
    {
      list($key,$value) = each(read_kv(&$os, $xmlbuf));
      if($key) $assoc[$key] = $value;
    }          
    else $os++;
  }
  

  # Spaces jumping
  while($os<$xbufsz) 
  {
    $x=$xmlbuf[$os];
    if(ord($x)>32) break; 
    $os++;
  }
  
  if($no_close) return true;
  
  # Marker content extraction
  $content = substr($xmlbuf,$os);

  # Ending marker detection
  $closing = '</'.$name.'>'; $lc = strlen($closing);

  if(false === ($endpos = strpos($content,$closing)))
  {
    if($more['strict']) return false;  # end of marker not found !
    $endpos =  strlen($content);
  }
  $content = substr($content,0,$endpos);
  $startpos = $os;

  # Content or submarker is coming next?
  if(is_balise_next($content,0))
  {
    while(($os-$startpos) < $endpos)
    {
      $tmp=array();
      if(!xa_xbalise_read(&$os,&$tmp,$xmlbuf,$more)) return false; # Erroneous submarker !
      $assoc[] = $tmp;
    }
  }
  else
  {
    $assoc[AX_CONTENT_SUBKEY] = $content;
    $os += $endpos;
  }

  # If can't find ending marker
  if(($xbufsz - $os - $lc)<0) return($more['strict']?false:true);
  
  $os += $lc;
 
  return true;
}

###### TESTING ################################################################

# This part demonstrates how to use this library.

function printr ( $var, $do_not_echo = false )
{
   ob_start();
   print_r($var);
   $code =  htmlentities(ob_get_contents());
   ob_clean();
   if ( !$do_not_echo )
   {
       echo "<pre>$code</pre>";
   }
   return $code;
}

function xa_test()
{
  # 1. CONSTRUCT A SIMPLE PHP XML ASSOC ARRAY
  $root = ax_root('animals');         # XML root marker

  $dogs = ax_balise($root, "dogs"); # XML child
  $dogs['kinds'] = 2;                 # XML marker property
  $dogs['size'] = "big";

  $labrador = ax_balise($dogs, "labrador");
  $labrador[retreiver] = ''; 

  $chihuahua = ax_balise($dogs, "chihuahua", "charly");

  $cats = ax_balise($root, "cats");

  $persian = ax_balise($cats, "persian");
  $siamese = ax_balise($cats, "siamese");

  $root['kinds'] = 'dogs,cats';

  $staff = ax_balise($dogs, "staff");
  $staff[weight] = 40;
  
  # 2. PHP XML ASSOC ARRAY => XML BUFFER

  printr($root);
  echo "<hr>";
  
  assoc2xml($xmlbuf,$root);
  
  echo("XML: ".htmlentities($xmlbuf)."<br/>");

  # 3. XML BUFFER => PHP XML ASSOC ARRAY (check)

  echo "<hr>";
  
  xml2assoc($assoc,$xmlbuf);

  echo("Check: ".(($assoc == $root)?'ok':'non')."<br/>");  

  printr($root);

}

Conclusion :


Tout le fichier est commenté en anglais et divisé en 3 parties: fonctions privées, fonctions publiques et routine de test.
La section routine de test est prévue pour vous faire une démonstration de l'utilisation de la librairie.

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.