<?php
/*
  This code is part of FusionDirectory (http://www.fusiondirectory.org/)

  Copyright (C) 2003-2010  Cajus Pollmeier
  Copyright (C) 2011-2020  FusionDirectory

  This program is free software; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation; either version 2 of the License, or
  (at your option) any later version.

  This program is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with this program; if not, write to the Free Software
  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/

/*!
 * \file class_xml.inc
 * Source code for class xml
 */

/*!
 * \brief This class contains all the function needed to manage xml
 * files
 */
class xml
{
  /*!
   * \brief Transform a xml document to an array
   *
   * \param $contents Contents
   *
   * \param integer $get_attributes Initialized at 1
   *
   * \param string $priority Initialized at 'tag'
   */
  static function xml2array ($contents, $get_attributes = 1, $priority = 'tag')
  {
    if (!$contents) {
      return [];
    }

    if (!function_exists('xml_parser_create')) {
      trigger_error('xml_parser_create function does not exists');
      return [];
    }

    //Get the XML parser of PHP - PHP must have this module for the parser to work
    $parser = xml_parser_create('');
    xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, "UTF-8"); // http://minutillo.com/steve/weblog/2004/6/17/php-xml-and-character-encodings-a-tale-of-sadness-rage-and-data-loss
    xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0);
    xml_parser_set_option($parser, XML_OPTION_SKIP_WHITE, 1);
    xml_parse_into_struct($parser, trim($contents), $xml_values);
    xml_parser_free($parser);

    if (!$xml_values) {
      return;
    }

    // Initializations
    $xml_array    = [];

    // Reference
    $current = &$xml_array;

    // Go through the tags.
    // Multiple tags with same name will be turned into an array
    $repeated_tag_index = [];
    foreach ($xml_values as $data) {
      $result = [];
      $attributes_data = [];

      if (isset($data['value'])) {
        if ($priority == 'tag') {
          $result = $data['value'];
        } else {
          // Put the value in a assoc array if we are in the 'Attribute' mode
          $result['value'] = $data['value'];
        }
      }

      //Set the attributes too.
      if (isset($data['attributes']) and $get_attributes) {
        foreach ($data['attributes'] as $attr => $val) {
          if ($priority == 'tag') {
            $attributes_data[$attr] = $val;
          } else {
            // Set all the attributes in a array called 'attr'
            $result['attr'][$attr] = $val;
          }
        }
      }

      // See tag status and do the needed.
      if ($data['type'] == 'open') {
        // The starting of the tag '<tag>'
        $parent[$data['level'] - 1] = &$current;
        if (!is_array($current) or (!in_array($data['tag'], array_keys($current)))) {
          // Insert New tag
          $current[$data['tag']] = $result;
          if ($attributes_data) {
            $current[$data['tag']. '_attr'] = $attributes_data;
          }
          $repeated_tag_index[$data['tag'].'_'.$data['level']] = 1;

          $current = &$current[$data['tag']];
        } else {
          // There was another element with the same tag name

          if (isset($current[$data['tag']][0])) {
            // If there is a 0th element it is already an array
            $current[$data['tag']][$repeated_tag_index[$data['tag'].'_'.$data['level']]] = $result;
            $repeated_tag_index[$data['tag'].'_'.$data['level']]++;
          } else {
            // This section will make the value an array if multiple tags with the same name appear together
            // This will combine the existing item and the new item together to make an array
            $current[$data['tag']] = [$current[$data['tag']],$result];
            $repeated_tag_index[$data['tag'].'_'.$data['level']] = 2;

            if (isset($current[$data['tag'].'_attr'])) {
              // The attribute of the last(0th) tag must be moved as well
              $current[$data['tag']]['0_attr'] = $current[$data['tag'].'_attr'];
              unset($current[$data['tag'].'_attr']);
            }
          }
          $last_item_index  = $repeated_tag_index[$data['tag'].'_'.$data['level']] - 1;
          $current          = &$current[$data['tag']][$last_item_index];
        }
      } elseif ($data['type'] == "complete") {
        // Tags that ends in 1 line '<tag />'
        // See if the key is already taken.
        if (!isset($current[$data['tag']])) {
          // New Key
          $current[$data['tag']]                                = $result;
          $repeated_tag_index[$data['tag'].'_'.$data['level']]  = 1;
          if ($priority == 'tag' and $attributes_data) {
            $current[$data['tag']. '_attr'] = $attributes_data;
          }
        } else {
          // If taken, put all things inside a list(array)
          if (isset($current[$data['tag']][0]) and is_array($current[$data['tag']])) {
            // If it is already an array...
            // ...push the new element into that array.
            $current[$data['tag']][$repeated_tag_index[$data['tag'].'_'.$data['level']]] = $result;

            if ($priority == 'tag' and $get_attributes and $attributes_data) {
              $current[$data['tag']][$repeated_tag_index[$data['tag'].'_'.$data['level']] . '_attr'] = $attributes_data;
            }
            $repeated_tag_index[$data['tag'].'_'.$data['level']]++;
          } else { //If it is not an array...
            //...Make it an array using using the existing value and the new value
            $current[$data['tag']] = [$current[$data['tag']],$result];
            $repeated_tag_index[$data['tag'].'_'.$data['level']] = 1;
            if ($priority == 'tag' and $get_attributes) {
              if (isset($current[$data['tag'].'_attr'])) {
                // The attribute of the last(0th) tag must be moved as well
                $current[$data['tag']]['0_attr'] = $current[$data['tag'].'_attr'];
                unset($current[$data['tag'].'_attr']);
              }

              if ($attributes_data) {
                $current[$data['tag']][$repeated_tag_index[$data['tag'].'_'.$data['level']] . '_attr'] = $attributes_data;
              }
            }
            // 0 and 1 index is already taken
            $repeated_tag_index[$data['tag'].'_'.$data['level']]++;
          }
        }
      } elseif ($data['type'] == 'close') {
        // End of tag '</tag>'
        $current = &$parent[$data['level'] - 1];
      }
    }

    return $xml_array;
  }
}