home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / XML / sql2xml.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  27.4 KB  |  804 lines

  1. <?php
  2. /**
  3.  * XML::sql2xml
  4.  *
  5.  * PHP version 4
  6.  *
  7.  * @category   XML
  8.  * @package    XML_sql2xml
  9.  * @author     Christian Stocker <chregu@php.net>
  10.  * @copyright  2001 - 2008 Christian Stocker
  11.  * @license    BSD, revised
  12.  * @version    CVS: $Id: sql2xml.php,v 1.17 2008/03/24 15:51:51 dufuz Exp $
  13.  * @link       http://pear.php.net/package/XML_sql2xml
  14.  */
  15.  
  16. /**
  17. * This class takes a PEAR::DB-Result Object, a sql-query-string or an array
  18. *  and returns a xml-representation of it.
  19. *
  20. * TODO
  21. *   -encoding etc, options for header
  22. *   -ERROR CHECKING
  23. *
  24. * Usage example
  25. *
  26. * include_once ("DB.php");
  27. * include_once("XML/sql2xml.php");
  28. * $db = DB::connect("mysql://root@localhost/xmltest");
  29. * $sql2xml = new xml_sql2xml();
  30. * //the next one is only needed, if you need others than the default
  31. * $sql2xml->setEncoding("ISO-8859-1","UTF-8");
  32. * $result = $db->query("select * from bands");
  33. * $xmlstring = $sql2xml->getXML($result);
  34. *
  35. * or
  36. *
  37. * include_once ("DB.php");
  38. * include_once("XML/sql2xml.php");
  39. * $sql2xml = new xml_sql2xml("mysql://root@localhost/xmltest");
  40. * $sql2xml->Add("select * from bands");
  41. * $xmlstring = $sql2xml->getXML();
  42. *
  43. * More documentation and a tutorial/how-to can be found at
  44. *   http://php.chregu.tv/sql2xml
  45. *
  46. * @author   Christian Stocker <chregu@bitflux.ch>
  47. * @version  $Id: sql2xml.php,v 1.17 2008/03/24 15:51:51 dufuz Exp $
  48. * @package  XML
  49. */
  50. class XML_sql2xml
  51. {
  52.     /**
  53.     * If joined-tables should be output nested.
  54.     *  Means, if you have joined two or more queries, the later
  55.     *   specified tables will be nested within the result of the former
  56.     *   table.
  57.     *   Works at the moment only with mysql automagically. For other RDBMS
  58.     *   you have to provide your table-relations by hand (see user_tableinfo)
  59.     *
  60.     * @var  boolean
  61.     * @see  $user_tableinfo, doSql2Xml(), doArray2Xml();
  62.     */
  63.     var $nested = True;
  64.  
  65.     /**
  66.     * Name of the tag element for resultsets
  67.     *
  68.     * @var  string
  69.     * @see  insertNewResult()
  70.     */
  71.     var $tagNameResult = "result";
  72.  
  73.     /**
  74.     * Name of the tag element for rows
  75.     *
  76.     * @var  string
  77.     * @see  insertNewRow()
  78.     */
  79.     var $tagNameRow = "row";
  80.  
  81.     /**
  82.     *
  83.     * @var   object PEAR::DB
  84.     * @access private
  85.     */
  86.     var $db = Null;
  87.  
  88.     /**
  89.     * Options to be used in extended Classes (for example in sql2xml_ext).
  90.     * They are passed with SetOptions as an array (arrary("user_options" = array());
  91.     *  and can then be accessed with $this->user_options["bla"] from your
  92.     *  extended classes for additional features.
  93.     *  This array is not use in this base class, it's only for passing easy parameters
  94.     *  to extended classes.
  95.     *
  96.     * @var      array
  97.     */
  98.     var $user_options = array();
  99.  
  100.     /**
  101.     * The DomDocument Object to be used in the whole class
  102.     *
  103.     * @var      object  DomDocument
  104.     * @access    private
  105.     */
  106.     var $xmldoc;
  107.  
  108.     /**
  109.     * The Root of the domxml object
  110.     * I'm not sure, if we need this as a class variable....
  111.     * could be replaced by domxml_root($this->xmldoc);
  112.     *
  113.     * @var      object DomNode
  114.     * @access    private
  115.     */
  116.     var $xmlroot;
  117.  
  118.     /**
  119.     * This array is used to give the structure of your database to the class.
  120.     *  It's especially useful, if you don't use mysql, since other RDBMS than
  121.     *  mysql are not able at the moment to provide the right information about
  122.     *  your database structure within the query. And if you have more than 2
  123.     *  tables joined in the sql it's also not possible for mysql to find out
  124.     *  your real relations.
  125.     *  The parameters are the same as in fieldInfo from the PEAR::DB and some
  126.     *   additional ones. Here they come:
  127.     *  From PEAR::DB->fieldinfo:
  128.     *
  129.     *    $tableInfo[$i]["table"]    : the table, which field #$i belongs to.
  130.     *           for some rdbms/comples queries and with arrays, it's impossible
  131.     *           to find out to which table the field actually belongs. You can
  132.     *           specify it here more accurate. Or if you want, that one fields
  133.     *           belongs to another table, than it actually says (yes, there's
  134.     *           use for that, see the upcoming tutorial ...)
  135.     *
  136.     *    $tableInfo[$i]["name"]     : the name of field #$i. if you want another
  137.     *           name for the tag, than the query or your array provides, assign
  138.     *           it here.
  139.     *
  140.     *   Additional info
  141.     *     $tableInfo["parent_key"][$table]  : index of the parent key for $table.
  142.     *           this is the field, where the programm looks for changes, if this
  143.     *           field changes, it assumes, that we need a new "rowset" in the
  144.     *           parent table.
  145.     *
  146.     *     $tableInfo["parent_table"][$table]: name of the parent table for $table.
  147.     *
  148.     * @var      array
  149.     * @access    private
  150.     */
  151.     var $user_tableInfo = array();
  152.  
  153.     /**
  154.     * the encoding type, the input from the db has
  155.     */
  156.     var $encoding_from  = 'ISO-8859-1';
  157.  
  158.     /**
  159.     * the encoding type, the output in the xml should have
  160.     * (note that domxml at the moment only support UTF-8, or at least it looks like)
  161.     */
  162.     var $encoding_to = 'UTF-8';
  163.  
  164.     var $tagname = 'tagname';
  165.  
  166.     /**
  167.     * Constructor
  168.     * The Constructor can take a Pear::DB "data source name" (eg.
  169.     *  "mysql://user:passwd@localhost/dbname") and will then connect
  170.     *  to the DB, or a PEAR::DB object link, if you already connected
  171.     *  the db before.
  172.     "  If you provide nothing as $dsn, you only can later add stuff with
  173.     *   a pear::db-resultset or as an array. providing sql-strings will
  174.     *   not work.
  175.     * the $root param is used, if you want to provide another name for your
  176.     *  root-tag than "root". if you give an empty string (""), there will be no
  177.     *  root element created here, but only when you add a resultset/array/sql-string.
  178.     *  And the first tag of this result is used as the root tag.
  179.     *
  180.     * @param  mixed $dsn    PEAR::DB "data source name" or object DB object
  181.     * @param  string $root  the name of the xml-doc root element.
  182.     * @access   public
  183.     */
  184.     function XML_sql2xml($dsn = Null, $root = 'root')
  185.     {
  186.         // if it's a string, then it must be a dsn-identifier;
  187.         if (is_string($dsn)) {
  188.             include_once 'DB.php';
  189.             $this->db = DB::Connect($dsn);
  190.             if (DB::isError($this->db)) {
  191.                 print "The given dsn for XML_sql2xml was not valid in file ".__FILE__." at line ".__LINE__."<br>\n";
  192.                 return new DB_Error($this->db->code,PEAR_ERROR_DIE);
  193.             }
  194.  
  195.         } elseif (is_object($dsn) && DB::isError($dsn)) {
  196.             print "The given param for XML_sql2xml was not valid in file ".__FILE__." at line ".__LINE__."<br>\n";
  197.             return new DB_Error($dsn->code,PEAR_ERROR_DIE);
  198.         } elseif (strtolower(get_parent_class($dsn)) == 'db_common') {
  199.             // if parent class is db_common, then it's already a connected identifier
  200.             $this->db = $dsn;
  201.         }
  202.  
  203.         $this->xmldoc = domxml_new_xmldoc('1.0');
  204.  
  205.         //oehm, seems not to work, unfortunately.... does anybody know a solution?
  206.         $this->xmldoc->encoding = $this->encoding_to;
  207.  
  208.         if ($root) {
  209.             $this->xmlroot = $this->xmldoc->add_root($root);
  210.             //PHP 4.0.6 had $root->name as tagname, check for that here...
  211.             if (!isset($this->xmlroot->{$this->tagname})) {
  212.                 $this->tagname = 'name';
  213.             }
  214.         }
  215.  
  216.     }
  217.  
  218.     /**
  219.     * General method for adding new resultsets to the xml-object
  220.     *  Give a sql-query-string, a pear::db_result object or an array as
  221.     *  input parameter, and the method calls the appropriate method for this
  222.     *  input and adds this to $this->xmldoc
  223.     *
  224.     * @param    string sql-string, or object db_result, or array
  225.     * @param    mixed additional parameters for the following functions
  226.     * @access   public
  227.     * @see      addResult(), addSql(), addArray(), addXmlFile()
  228.     */
  229.     function add ($resultset, $params = null)
  230.     {
  231.         // if string, then it's a query, a xml-file or a xml-string...
  232.         if (is_string($resultset)) {
  233.             if (preg_match("/\.xml$/",$resultset)) {
  234.                 $this->AddXmlFile($resultset,$params);
  235.             } elseif (preg_match("/.*select.*from.*/i" ,  $resultset)) {
  236.                 $this->AddSql($resultset);
  237.             } else {
  238.                 $this->AddXmlString($resultset);
  239.             }
  240.         } elseif (is_array($resultset)) {
  241.             // if array, then it's an array...
  242.             $this->AddArray($resultset);
  243.         }
  244.  
  245.         if (strtolower(get_class($resultset)) == 'db_result') {
  246.             $this->AddResult($resultset);
  247.         }
  248.     }
  249.  
  250.     /**
  251.     * Adds the content of a xml-file to $this->xmldoc, on the same level
  252.     * as a normal resultset (mostly just below <root>)
  253.     *
  254.     * @param    string filename
  255.     * @param    mixed xpath  either a string with the xpath expression or an array with "xpath"=>xpath expression  and "root"=tag/subtag/etc, which are the tags to be inserted before the result
  256.     * @access   public
  257.     * @see      doXmlString2Xml()
  258.     */
  259.  
  260.     function addXmlFile($file, $xpath = null)
  261.     {
  262.         $content = file_get_contents($file);
  263.         $this->doXmlString2Xml($content, $xpath);
  264.     }
  265.  
  266.     /**
  267.     * Adds the content of a xml-string to $this->xmldoc, on the same level
  268.     * as a normal resultset (mostly just below <root>)
  269.     *
  270.     * @param    string xml
  271.     * @param    mixed xpath  either a string with the xpath expression or an array with "xpath"=>xpath expression  and "root"=tag/subtag/etc, which are the tags to be inserted before the result
  272.     * @access   public
  273.     * @see      doXmlString2Xml()
  274.     */
  275.     function addXmlString($string, $xpath = null)
  276.     {
  277.         $this->doXmlString2Xml($string, $xpath);
  278.     }
  279.  
  280.     /**
  281.     * Adds an additional pear::db_result resultset to $this->xmldoc
  282.     *
  283.     * @param    Object db_result result from a DB-query
  284.     * @see      doSql2Xml()
  285.     * @access   public
  286.     */
  287.     function addResult($result)
  288.     {
  289.         $this->doSql2Xml($result);
  290.     }
  291.  
  292.     /**
  293.     * Adds an aditional resultset generated from an sql-statement
  294.     *  to $this->xmldoc
  295.     *
  296.     * @param    string sql a string containing an sql-statement.
  297.     * @access   public
  298.     * @see      doSql2Xml()
  299.     */
  300.     function addSql($sql)
  301.     {
  302.         /* if there are {} expressions in the sql query, we assume it's an xpath expression to
  303.         *   be evaluated.
  304.         */
  305.  
  306.         if (preg_match_all ("/\{([^\}]+)\}/i",$sql,$matches)) {
  307.             foreach ($matches[1] as $match) {
  308.                 $sql = preg_replace("#\{".preg_quote($match)."\}#  ", $this->getXpathValue($match),$sql);
  309.             }
  310.         }
  311.         $result = $this->db->query($sql);
  312.  
  313.         //very strange
  314.         if (PEAR::isError($result->result)) {
  315.                  print "You have an SQL-Error:<br>".$result->result->userinfo;
  316.                  print "<br>";
  317.                 new DB_Error($result->result->code,PEAR_ERROR_DIE);
  318.         }
  319.  
  320.         $this->doSql2Xml($result);
  321.     }
  322.  
  323.     /**
  324.     * Adds an aditional resultset generated from an Array
  325.     *  to $this->xmldoc
  326.     * TODO: more explanation, how arrays are transferred
  327.     *
  328.     * @param    array multidimensional array.
  329.     * @access   public
  330.     * @see      doArray2Xml()
  331.     */
  332.     function addArray($array, $prefix = 'index_')
  333.     {
  334.         $parent_row = $this->insertNewResult($metadata);
  335.  
  336.         $array = $this->prepareArray($array, $prefix);
  337.         if (!$array) {
  338.             return false;
  339.         }
  340.  
  341.         $this->DoArray2Xml($array, $parent_row);
  342.     }
  343.  
  344.     /**
  345.      * Makes sure the given array has no integer indices
  346.      *
  347.      * @param   array multidimensional array.
  348.      * @param   prefix string to prefix integer indices
  349.      * @access  public
  350.      * @see     doArray()
  351.      */
  352.     function prepareArray($array, $prefix = 'index_')
  353.     {
  354.         if (!is_array($array)) {
  355.             return false;
  356.         }
  357.  
  358.         $array_new = array();
  359.         foreach($array as $key => $val) {
  360.             if (!is_string($key)) {
  361.                 $key = $prefix.$key;
  362.             }
  363.  
  364.             if (is_array($val)) {
  365.                 $array_new[$key] = $this->prepareArray($val);
  366.             } else {
  367.                 $array_new[$key] = $val;
  368.             }
  369.         }
  370.  
  371.         return $array_new;
  372.     }
  373.  
  374.     /**
  375.     * Returns an xml-string with a xml-representation of the resultsets.
  376.     *
  377.     * The resultset can be directly provided here, or if you need more than one
  378.     * in your xml, then you have to provide each of them with add() before you
  379.     * call getXML, but the last one can also be provided here.
  380.     *
  381.     * @param    mixed  $result result Object from a DB-query
  382.     * @return   string  xml
  383.     * @access   public
  384.     */
  385.     function getXML($result = null)
  386.     {
  387.         $xmldoc = $this->getXMLObject($result);
  388.         return $xmldoc->dumpmem();
  389.     }
  390.  
  391.     /**
  392.     * Returns an xml DomDocument Object with a xml-representation of the resultsets.
  393.     *
  394.     * The resultset can be directly provided here, or if you need more than one
  395.     * in your xml, then you have to provide each of them with add() before you
  396.     * call getXMLObject, but the last one can also be provided here.
  397.     *
  398.     * @param    mixed $result result Object from a DB-query
  399.     * @return   Object DomDocument
  400.     * @access   public
  401.     */
  402.     function getXMLObject($result = null)
  403.     {
  404.         if ($result) {
  405.             $this->add ($result);
  406.         }
  407.         return $this->xmldoc;
  408.     }
  409.  
  410.     /**
  411.     * For adding db_result-"trees" to $this->xmldoc
  412.     * @param    Object db_result
  413.     * @access   private
  414.     * @see      addResult(),addSql()
  415.     */
  416.     function doSql2Xml($result)
  417.     {
  418.         if (DB::IsError($result)) {
  419.             print "Error in file ".__FILE__." at line ".__LINE__."<br>\n";
  420.             print $result->userinfo."<br>\n";
  421.             new DB_Error($result->code, PEAR_ERROR_DIE);
  422.         }
  423.  
  424.         // BE CAREFUL: if you have fields with the same name in different tables, you will get errors
  425.         // later, since DB_FETCHMODE_ASSOC doesn't differentiate that stuff.
  426.         $this->LastResult = &$result;
  427.  
  428.         if (!$tableInfo = $result->tableInfo(false)) {
  429.             //emulate tableInfo. this can go away, if every db supports tableInfo
  430.             $fetchmode = DB_FETCHMODE_ASSOC;
  431.             $res = $result->FetchRow($fetchmode);
  432.             $this->nested = false;
  433.             $i = 0;
  434.  
  435.             while (list($key, $val) = each($res)) {
  436.                 $tableInfo[$i]['table']= $this->tagNameResult;
  437.                 $tableInfo[$i]['name'] = $key;
  438.                 $resFirstRow[$i] = $val;
  439.                 $i++;
  440.             }
  441.             $res  = $resFirstRow;
  442.             $fetchmode = DB_FETCHMODE_ORDERED;
  443.         } else {
  444.             $fetchmode = DB_FETCHMODE_ORDERED;
  445.         }
  446.  
  447.         // initialize db hierarchy...
  448.         $parenttable = 'root';
  449.         $tableInfo['parent_key']['root'] = 0;
  450.  
  451.         foreach ($tableInfo as $key => $value) {
  452.             if (is_int($key)) {
  453.                 // if the sql-query had a function the table starts with a # (only in mysql i think....), then give the field the name of the table before...
  454.                 if (preg_match ("/^#/", $value['table']) || strlen($value['table']) === 0) {
  455.                     $value['table'] = $tableInfo[($key - 1)]['table'];
  456.                     $tableInfo[$key]['table'] = $value['table'];
  457.                 }
  458.  
  459.                 if (!isset($tableInfo['parent_table'])
  460.                     || !isset($tableInfo['parent_table'][$value['table']])
  461.                     || is_null($tableInfo['parent_table'][$value['table']])
  462.                 ) {
  463.                     $tableInfo['parent_key'][$value['table']] = $key;
  464.                     $tableInfo['parent_table'][$value['table']] = $parenttable;
  465.                     $parenttable = $value['table'] ;
  466.                 }
  467.  
  468.             }
  469.             //if you need more tableInfo for later use you can write a function addTableInfo..
  470.             $this->addTableInfo($key, $value, $tableInfo);
  471.         }
  472.  
  473.         // end initialize
  474.  
  475.         // if user made some own tableInfo data, merge them here.
  476.         if ($this->user_tableInfo) {
  477.             $tableInfo = $this->array_merge_clobber($tableInfo,$this->user_tableInfo);
  478.         }
  479.         $parent['root'] = $this->insertNewResult($tableInfo);
  480.  
  481.         //initialize $resold to get rid of warning messages;
  482.         $resold[0] = "ThisValueIsImpossibleForTheFirstFieldInTheFirstRow";
  483.  
  484.         while ($res = $result->FetchRow($fetchmode)) {
  485.             while (list($key, $val) = each($res)) {
  486.                 if ($resold[$tableInfo['parent_key'][$tableInfo[$key]['table']]] != $res[$tableInfo['parent_key'][$tableInfo[$key]['table']]] || !$this->nested) {
  487.                     if ($tableInfo['parent_key'][$tableInfo[$key]['table']] == $key ) {
  488.                         if ($this->nested || $key == 0) {
  489.                             $parent[$tableInfo[$key]['table']] = $this->insertNewRow($parent[$tableInfo['parent_table'][$tableInfo[$key]['table']]], $res, $key, $tableInfo);
  490.                         } else {
  491.                             $parent[$tableInfo[$key]['table']] = $parent[$tableInfo['parent_table'][$tableInfo[$key]['table']]];
  492.                         }
  493.  
  494.                         //set all children entries to somethin stupid
  495.                         foreach ($tableInfo['parent_table'] as $pkey => $pvalue) {
  496.                             if ($pvalue == $tableInfo[$key]['table']) {
  497.                                 $resold[$tableInfo['parent_key'][$pkey]]= "ThisIsJustAPlaceHolder";
  498.                             }
  499.                         }
  500.  
  501.                     }
  502.  
  503.                     if ( $parent[$tableInfo[$key]['table']] != null) {
  504.                         $this->insertNewElement($parent[$tableInfo[$key]['table']], $res, $key, $tableInfo, $subrow);
  505.                     }
  506.  
  507.                 }
  508.             }
  509.  
  510.             $resold = $res;
  511.             unset($subrow);
  512.         }
  513.  
  514.         return $this->xmldoc;
  515.     }
  516.  
  517.     /**
  518.      * For adding whole arrays to $this->xmldoc
  519.      *
  520.      * @param    array
  521.      * @param    Object domNode
  522.      * @access   private
  523.      * @see      addArray()
  524.      */
  525.     function DoArray2Xml ($array, $parent)
  526.     {
  527.         while (list($key, $val) = each($array)) {
  528.             $tableInfo[$key]['table'] = $this->tagNameResult;
  529.             $tableInfo[$key]['name']  = $key;
  530.         }
  531.  
  532.         if ($this->user_tableInfo) {
  533.             $tableInfo = $this->array_merge_clobber($tableInfo,$this->user_tableInfo);
  534.         }
  535.  
  536.         foreach ($array as $key => $value) {
  537.             if (is_array($value)) {
  538.                 if (is_int($key)) {
  539.                     $valuenew = array_slice($value,0,1);
  540.                     $keynew   = array_keys($valuenew);
  541.                     $keynew   = $keynew[0];
  542.                 } else {
  543.                     $valuenew = $value;
  544.                     $keynew = $key;
  545.                 }
  546.  
  547.                 $rec2 = $this->insertNewRow($parent, $valuenew, $keynew, $tableInfo);
  548.                 $this->DoArray2Xml($value,$rec2);
  549.             } else {
  550.                 $this->insertNewElement($parent, $array, $key, $tableInfo, $subrow);
  551.             }
  552.         }
  553.     }
  554.  
  555.     /**
  556.      * This method sets the options for the class
  557.      *  One can only set variables, which are defined at the top of
  558.      *  of this class.
  559.      *
  560.      * @param    array   options to be passed to the class
  561.      * @param    boolean   if the old suboptions should be deleted
  562.      * @access   public
  563.      * @see      $nested,$user_options,$user_tableInfo
  564.      */
  565.     function setOptions($options, $delete = false)
  566.     {
  567.         //set options
  568.         if (is_array($options)) {
  569.             foreach ($options as $option => $value) {
  570.                if (isset($this->{$option})) {
  571.                     if (is_array($value) && ! $delete) {
  572.                         foreach ($value as $suboption => $subvalue) {
  573.                             $this->{$option}["$suboption"] = $subvalue;
  574.                         }
  575.                     } else {
  576.                         $this->$option = $value;
  577.                     }
  578.                 }
  579.             }
  580.         }
  581.     }
  582.  
  583.     // these are the functions, which are intended to be overriden in user classes
  584.  
  585.     /**
  586.      *
  587.      * @param    mixed
  588.      * @return   object  DomNode
  589.      * @access   private
  590.      */
  591.     function insertNewResult(&$metadata)
  592.     {
  593.         if ($this->xmlroot) {
  594.             return $this->xmlroot->new_child($this->tagNameResult, null);
  595.         }
  596.  
  597.         $this->xmlroot = $this->xmldoc->add_root($this->tagNameResult);
  598.         return $this->xmlroot;
  599.     }
  600.  
  601.     /**
  602.      *   to be written
  603.      *
  604.      * @param    object DomNode $parent_row
  605.      * @param    mixed $res
  606.      * @param    mixed $key
  607.      * @param    mixed &metadata
  608.      * @return   object DomNode
  609.      * @access private
  610.      */
  611.     function insertNewRow($parent_row, $res, $key, &$metadata)
  612.     {
  613.         return  $parent_row->new_child($this->tagNameRow, null);
  614.     }
  615.  
  616.     /**
  617.      *   to be written
  618.      *
  619.      * @param    object DomNode $parent
  620.      * @param    mixed $res
  621.      * @param    mixed $key
  622.      * @param    mixed &$metadata
  623.      * @param    mixed &$subrow
  624.      * @return   object DomNode
  625.      * @access private
  626.      */
  627.     function insertNewElement($parent, $res, $key, &$metadata, &$subrow)
  628.     {
  629.         return  $parent->new_child($metadata[$key]['name'], $this->xml_encode($res[$key]));
  630.     }
  631.  
  632.     /**
  633.      *   to be written
  634.      *
  635.      * @param    mixed $key
  636.      * @param    mixed $value
  637.      * @param    mixed &$metadata
  638.      * @access private
  639.      */
  640.     function addTableInfo($key, $value, &$metadata)
  641.     {
  642.     }
  643.  
  644.     // end functions, which are intended to be overriden in user classes
  645.  
  646.     // here come some helper functions...
  647.  
  648.     /**
  649.     * make utf8 out of the input data and escape & with & and "< " with "< "
  650.     * (we assume that when there's no space after < it's a tag, which we need in the xml)
  651.     *  I'm not sure, if this is the standard way, but it works for me.
  652.     *
  653.     * @param    string text to be utfed.
  654.     * @access private
  655.     */
  656.     function xml_encode ($text)
  657.     {
  658.         $replace = ereg_replace("&","&",ereg_replace("< ","< ",$text));
  659.         if (function_exists('iconv') && isset($this->encoding_from) && isset($this->encoding_to)) {
  660.             ini_set('track_errors', 1);
  661.             $text = iconv($this->encoding_from, $this->encoding_to, $replace);
  662.  
  663.             if (isset($text)) {
  664.                 return $text;
  665.             }
  666.  
  667.             if (isset($php_errormsg)) {
  668.                 $errormsg = "error: $php_errormsg";
  669.             } else {
  670.                 $errormsg = "undefined iconv error, turn on track_errors in php.ini to get more details";
  671.             }
  672.             return PEAR::raiseError($errormsg, null, PEAR_ERROR_DIE);
  673.         } else {
  674.             $text = utf8_encode($replace);
  675.         }
  676.  
  677.         return $text;
  678.     }
  679.  
  680.     //taken from kc@hireability.com at http://www.php.net/manual/en/function.array-merge-recursive.php
  681.     /**
  682.     * There seemed to be no built in function that would merge two arrays recursively and clobber
  683.     *   any existing key/value pairs. Array_Merge() is not recursive, and array_merge_recursive
  684.     *   seemed to give unsatisfactory results... it would append duplicate key/values.
  685.     *
  686.     *   So here's a cross between array_merge and array_merge_recursive
  687.     **/
  688.     /**
  689.     *
  690.     * @param    array first array to be merged
  691.     * @param    array second array to be merged
  692.     * @return   array merged array
  693.     * @access private
  694.     */
  695.     function array_merge_clobber($a1,$a2)
  696.     {
  697.         if (!is_array($a1) || !is_array($a2)) {
  698.             return false;
  699.         }
  700.         $newarray = $a1;
  701.         while (list($key, $val) = each($a2)) {
  702.             if (is_array($val) && is_array($newarray[$key])) {
  703.                 $newarray[$key] = $this->array_merge_clobber($newarray[$key], $val);
  704.             } else {
  705.                 $newarray[$key] = $val;
  706.             }
  707.         }
  708.  
  709.         return $newarray;
  710.     }
  711.  
  712.     /**
  713.     * Adds a xml string to $this->xmldoc.
  714.     * It's inserted on the same level as a "normal" resultset, means just as a children of <root>
  715.     * if a xpath expression is supplied, it takes that for selecting only part of the xml-file
  716.     *
  717.     * @param    string xml string
  718.     * @param    mixed xpath  either a string with the xpath expression or an array with
  719.     *                 "xpath"=>xpath expression  and "root"=tag/subtag/etc,
  720.     *                 which are the tags to be inserted before the result
  721.     * @access private
  722.     */
  723.     function doXmlString2Xml ($string,$xpath = null)
  724.     {
  725.         $MainXmlString = $this->xmldoc->dumpmem();
  726.         $string = preg_replace("/<\?xml.*\?>/","",$string);
  727.  
  728.         $MainXmlString = preg_replace("/<".$this->xmlroot->{$this->tagname}."\/>/","<".$this->xmlroot->{$this->tagname}."></".$this->xmlroot->{$this->tagname}.">",$MainXmlString);
  729.         $MainXmlString = preg_replace("/<\/".$this->xmlroot->{$this->tagname}.">/",$string."</".$this->xmlroot->{$this->tagname}.">",$MainXmlString);
  730.  
  731.         $this->xmldoc  = xmldoc($MainXmlString);
  732.         $this->xmlroot = $this->xmldoc->root();
  733.     }
  734.  
  735.     /**
  736.     * sets the encoding for the db2xml transformation
  737.     * @param    string $encoding_from encoding to transform from
  738.     * @param    string $encoding_to encoding to transform to
  739.     * @access public
  740.     */
  741.     function setEncoding($encoding_from = 'ISO-8859-1', $encoding_to = 'UTF-8')
  742.     {
  743.         $this->encoding_from = $encoding_from;
  744.         $this->encoding_to   = $encoding_to;
  745.     }
  746.  
  747.     /**
  748.     * @param array $parentTables parent to child relation
  749.     * @access public
  750.     */
  751.     function SetParentTables($parentTables)
  752.     {
  753.         foreach ($parentTables as $table => $parent) {
  754.             $table_info['parent_table'][$table] = $parent;
  755.         }
  756.         $this->SetOptions(array('user_tableInfo' => $table_info));
  757.     }
  758.  
  759.     /**
  760.     * returns the content of the first match of the xpath expression
  761.     *
  762.     * @param    string $expr xpath expression
  763.     * @return   mixed content of the evaluated xpath expression
  764.     * @access   public
  765.     */
  766.     function getXpathValue($expr)
  767.     {
  768.         $xpath = $this->xmldoc->xpath_new_context();
  769.         $xnode = xpath_eval($xpath, $expr);
  770.  
  771.         if (isset ($xnode->nodeset[0])) {
  772.             $firstnode = $xnode->nodeset[0];
  773.             $children  = $firstnode->children();
  774.             $value     = $children[0]->content;
  775.             return $value;
  776.         }
  777.  
  778.         return null;
  779.     }
  780.  
  781.     /**
  782.     * get the values as an array from the childtags from the first match of the xpath expression
  783.     *
  784.     * @param    string xpath expression
  785.     * @return   array with key->value of subtags
  786.     * @access   public
  787.     */
  788.     function getXpathChildValues($expr)
  789.     {
  790.         $xpath = $this->xmldoc->xpath_new_context();
  791.         $xnode = xpath_eval($xpath, $expr);
  792.  
  793.         if (isset($xnode->nodeset[0])) {
  794.             foreach ($xnode->nodeset[0]->children() as $child) {
  795.                 $children = $child->children();
  796.                 $value[$child->{$this->tagname}] = $children[0]->content;
  797.             }
  798.             return $value;
  799.         }
  800.  
  801.         return null;
  802.     }
  803. }
  804.