XML 3.0Hoy toca otra actualización de una función relativamente antigua.
El XML es un lenguaje de almacenamiento de datos principalmente que hoy en día se usa para casi todo, desde las API online, RSS o los principales sistemas de comunicación entre empresas o gobiernos hasta las mas simples galerías de imágenes flash o conversaciones de MSN Messenger utilizan este sistema para encapsular los datos de manera fácil para las maquinas y los humanos. Por esto es un sistema que no podemos dejar de lado en nuestras aplicaciones. Por esto me dispongo a sacar la tercera versión de la función xml().

Esta nueva actualización duplica la velocidad de la anterior ya que ha sido reescrita entera y el único error detectado es que no respeta los saltos de linea dentro de los valores de los archivos XML, algo que por el momento no he podido solucionar. Está optimizada para funcionar con archivos UTF-8 y como en las anteriores versiones es capaz de autodetectar si el dato introducido es una dirección de archivo (local o de la red), un string de datos XML o un array de datos XML.
En el caso de ser un texto XML o una dirección a un archivo la función dará como resultado un array con todo el archivo XML. Si por el contrario lo introducido es un array, dará como resultado el mismo en formato XML.

Para conseguir un resultado optimo en la conversión "array2xml" el array tiene que tener un formato especifico. Si el formato no es el correcto se producirá una perdida de datos en la conversión del mismo. Para evitar esto se recomienda realizar test comparativos entre los resultados.

La actualización a la versión 3.0 como se indica mas arriba solo tiene un parámetro de entrada. El resto de parámetros de la función son de uso interno para realizar las lecturas recursivas del array en la conversión a XML.

Aquí van unos cuantos ejemplos.

Ejemplo de conversion de archivo local a array, (se necesita tener un archivo con contenido XML que tenga como nombre sample.xml en el mismo directorio):

PHP:
  1. $xml = xml('sample.xml');
  2. print_r($xml);

Ejemplo de verificación simple del XML anterior (de xml a array y de ese mismo array a xml):

PHP:
  1. $xml = xml('sample.xml');
  2. $xml = xml($xml);
  3. print_r($xml);

Ejemplo de array a XML (se necesita tener un array xml almacenado en la variable $array):

PHP:
  1. $xml = xml($array);
  2. echo $xml;

Ejemplos de conversión de archivo remoto XML en array:

PHP:
  1. $file = 'http://www.w3schools.com/XML/note.xml';
  2. $xml = xml($file);
  3. print_r($xml);

PHP:
  1. $file = 'http://www.w3schools.com/XML/cd_catalog.xml';
  2. $xml = xml($file);
  3. print_r($xml);

PHP:
  1. $file = 'http://www.w3schools.com/XML/plant_catalog.xml';
  2. $xml = xml($file);
  3. print_r($xml);

PHP:
  1. $file = 'http://www.w3schools.com/XML/simple.xml';
  2. $xml = xml($file);
  3. print_r($xml);

y aquí os dejo el la función. Como podéis leer la parte de conversión de datos de XML a Array es una variación del código de Binny V. A. de xml2array() por lo que el error de los saltos de linea seguirán estando hasta que lo corrija o le meta mas mano yo.

PHP:
  1. function xml($datos='', $key='', $sangria = 0){
  2.     if($key || $sangria || is_array($datos)){
  3.         if($sangria == 0 ) $s[] = '<?xml version="1.0" encoding="ISO-8859-1"?'.'>';
  4.         $array = $datos;
  5.         $_sangria = str_pad('', $sangria, ' ');
  6.         foreach($array as $k => $v){
  7.             $_value = $v['value'];
  8.             $_attr  = $v['attr'];
  9.             unset($v['value'], $v['attr']);
  10.            
  11.             $_keys  = implode('',array_keys($array));
  12.             $__keys = (is_numeric($_keys) && $key!='') ? $key : $k;
  13.             if(is_array($_attr)){
  14.                 unset($_s);
  15.                 $_s = "\n";
  16.                 foreach($_attr as $namevar => $valuevar){   $_s .= $_sangria.' '.$namevar.'="'.$valuevar.'"'."\n"}
  17.                 $_s .= $_sangria;
  18.             }
  19.             if(count($v)>0){
  20.                 if((is_numeric($_keys) && $key!='')){
  21.                     $s[] = $_sangria.'<'.$__keys.$_s.'>';
  22.                     $s[] = str_pad('', $sangria-1, '    ').xml($v, $__keys, $sangria+1);
  23.                     $s[] = $_sangria.'</'.$__keys.'>'; //
  24.                 } else {
  25.                     $v_key = is_numeric(implode('',array_keys($v)));
  26.                     if( ($v_key==false) ){
  27.                         $s[] = $_sangria.'<'.$__keys.$_s.'>';
  28.                         $s[] = xml($v, $__keys, $sangria+1);
  29.                         $s[] = $_sangria.'</'.$__keys.'>'; //
  30.                     } else {
  31.                         $s[] = xml($v, $__keys, $sangria);
  32.                     }
  33.                 }
  34.             } else {
  35.                 $s[] = $_value!='' ? $_sangria.'<'.$__keys.$_s.'>'.$_value.'</'.$__keys.'>' : $_sangria.'<'.$__keys.$_s.' />';
  36.             }
  37.         }
  38.         return implode("\n", $s);
  39.     } elseif(is_string($datos) && $datos!='') {
  40.         $contents      = $datos;
  41.         $get_attributes = 1;
  42.         if(!$contents) return array('error' => array('value' =>  'no hay datos en la variable de entrada!'));
  43.         if($contents = utf8_decode(@file_get_contents($contents))){ $contents = utf8_decode($contents)}
  44.         if(!function_exists('xml_parser_create')) { return array( 'error' => array('value' =>  'funcion \')xml_parser_create()\' no encotrada!' ) )} //si la funcion no existe
  45.    
  46.         //Correccion de &
  47.         $ampersan_flag = microtime(true);
  48.         while(strpos($contents, $ampersan_flag)){ $ampersan_flag = microtime(true); }
  49.         $contents = str_replace ('&', $ampersan_flag, $contents);
  50.        
  51.         //Correccion de \n
  52.         $salto_flag = microtime(true);
  53.         while(strpos($contents, $salto_flag)){ $salto_flag = microtime(true); }
  54.        
  55.         //Obtencion de los datos XML
  56.         $parser = xml_parser_create();
  57.         xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, 1 );
  58.         #xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );
  59.         #xml_parser_set_option( $parser, XML_OPTION_TARGET_ENCODING, 'UTF-8' );
  60.         xml_parse_into_struct( $parser, $contents, $xml_values );
  61.         xml_parser_free( $parser );
  62.    
  63.         if(!$xml_values){   return array('error' => array('value' =>  'el archivo no contiene variables!'));   } //si el archivo esta vacio
  64.    
  65.         //Iniciamos los valores
  66.         $xml_array    = array();
  67.         $parents        = array();
  68.         $opened_tags    = array();
  69.         $arr            = array();
  70.        
  71.         $current = &$xml_array;
  72.        
  73.         // Go through the tags.
  74.         foreach($xml_values as $data) {
  75.             unset($attributes,$value);//Remove existing values, or there will be trouble
  76.             extract($data);//We could use the array by itself, but this cooler.
  77.    
  78.             $result = '';
  79.             if($get_attributes) {//The second argument of the function decides this.
  80.                 $result = array();
  81.                 $value  = str_replace($ampersan_flag, '&', $value); //
  82.                 if(isset($value)){ $result['value'] = $value; }
  83.    
  84.                 //Set the attributes too.
  85.                 if(isset($attributes)) {
  86.                     foreach($attributes as $attr => $val) {
  87.                         $val = str_replace ($ampersan_flag, '&', $val); //
  88.                         if($get_attributes == 1) $result['attr'][$attr] = $val; //Set all the attributes in a array called 'attr'
  89.                         /**  :TODO: should we change the key name to '_attr'? Someone may use the tagname 'attr'. Same goes for 'value' too */
  90.                     }
  91.                 }
  92.             } elseif(isset($value)) {
  93.                 $value = str_replace($ampersan_flag, '&', $value); //
  94.                 $result = $value;
  95.             }
  96.    
  97.             // Ordenado del array // // // // // //
  98.    
  99.             //See tag status and do the needed.
  100.             if($type == "open") {//The starting of the tag '<tag>'
  101.                 $parent[$level-1] = &$current;
  102.    
  103.                 if(!is_array($current) or (!in_array($tag, array_keys($current)))) { //Insert New tag
  104.                     $current[$tag] = $result;
  105.                     $current = &$current[$tag];
  106.                 } else { //There was another element with the same tag name
  107.                     if(isset($current[$tag][0])) {
  108.                         array_push($current[$tag], $result);
  109.                     } else {
  110.                         $current[$tag] = array($current[$tag],$result);
  111.                     }
  112.                     $last = count($current[$tag]) - 1;
  113.                     $current = &$current[$tag][$last];
  114.                 }
  115.    
  116.             } elseif($type == "complete") { //Tags that ends in 1 line '<tag />'
  117.                 //See if the key is already taken.
  118.                 if(!isset($current[$tag])) { //New Key
  119.                     $current[$tag] = $result;
  120.                 } else { //If taken, put all things inside a list(array)
  121.                     if((is_array($current[$tag]) and $get_attributes == 0)//If it is already an array...
  122.                             or (isset($current[$tag][0]) and is_array($current[$tag][0]) and $get_attributes == 1)) {
  123.                         array_push($current[$tag],$result); // ...push the new element into that array.
  124.                     } else { //If it is not an array...
  125.                         $current[$tag] = array($current[$tag],$result); //...Make it an array using using the existing value and the new value
  126.                     }
  127.                 }
  128.    
  129.             } elseif($type == 'close') {    $current = &$parent[$level-1]}//End of tag '</tag>'
  130.         }
  131.         return($xml_array);
  132.     } else {
  133.         return array('error' => array('value' =>  'Formato de valor en la entrada no valido.'));
  134.     }
  135. }

Que lo disfrutéis!
Actualización: Para quitar el error de los saltos de linea solo hay que comentar la linea 58 (xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 ); por #xml_parser_set_option( $parser, XML_OPTION_SKIP_WHITE, 1 );)

A petición del publico añado un nuevo ejemplo que ilustre mas "claramente" como usar la función.. usare el 1º ejemplo de lectura de xml externo de este articulo.

PHP:
  1. $file = 'http://www.w3schools.com/XML/note.xml';
  2. $xml = xml($file);
  3.  
  4. //muesta los valores de las ramas con un salto de linea al fina
  5. echo $xml['NOTE']['TO']['value']."\n";
  6. echo $xml['NOTE']['FFROM']['value']."\n";
  7. echo $xml['NOTE']['HEADING']['value']."\n";
  8. echo $xml['NOTE']['BODDY']['value']."\n";
  9.  
  10. //hace lo mismo que antes pero usando $note para referirnos a la rama 'NOTE'
  11. $note = $xml['NOTE'];
  12. echo $note['TO']['value']."\n";
  13. echo $note['FFROM']['value']."\n";
  14. echo $note['HEADING']['value']."\n";
  15. echo $note['BODDY']['value']."\n";
  16.  
  17. //forma para recorrer una sola rama de forma automática
  18. //en este caso mostrando las ramas parientes y sus valores en una frase
  19. //(se podría recorrer el array desde el principio en vez de desde NOTE, pero
  20. // al tener un solo valor es innecesario hacer dos veces el foreach)
  21. foreach($xml['NOTE'] as $key => $valor){
  22.     echo 'El nombre de la rama es '.$key.' y su valor es '.$valor['value']."\n";
  23. }



Para mas aclaraciones... comentarios ;)