fluxcms /inc/XML/db2xml.php

Language PHP Lines 551
MD5 Hash f3449c4d980eaffeb7fd0315fe2a5358
Repository https://github.com/chregu/fluxcms.git View Raw File View Project SPDX
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
<?php
// +----------------------------------------------------------------------+
// | PHP version 4.0                                                      |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997, 1998, 1999, 2000, 2001 The PHP Group             |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license,       |
// | that is bundled with this package in the file LICENSE, and is        |
// | available at through the world-wide-web at                           |
// | http://www.php.net/license/2_02.txt.                                 |
// | If you did not receive a copy of the PHP license and are unable to   |
// | obtain it through the world-wide-web, please send a note to          |
// | license@php.net so we can mail you a copy immediately.               |
// +----------------------------------------------------------------------+
// | Authors: Christian Stocker <chregu@phant.ch>                         |
// +----------------------------------------------------------------------+
//
// $Id$

/**
* This class takes a PEAR::DB-Result Object, a sql-query-string or an array
*  and returns a xml-representation of it.
*
* TODO
*   -encoding etc, options for header
*   -ERROR CHECKING
*
* Usage example
*
* include_once ("DB.php");
* include_once("XML/db2xml.php");
* $db = DB::connect("mysql://root@localhost/xmltest");
* $db2xml = new xml_db2xml();
* //the next one is only needed, if you need others than the default
* $db2xml->setEncoding("ISO-8859-1","UTF-8");
* $result = $db->query("select * from bands");
* $xmlstring = $db2xml->getXML($result);
*
* or
*
* include_once ("DB.php");
* include_once("XML/db2xml.php");
* $db2xml = new xml_db2xml("mysql://root@localhost/xmltest");
* $db2xml->Add("select * from bands");
* $xmlstring = $db2xml->getXML();
*
* More documentation and a tutorial/how-to can be found at
*   http://php.chregu.tv/sql2xml
*
*
* @author   Christian Stocker <chregu@liip.ch>
* @version  $Id$
* @package  XML_db2xml
*/
class XML_db2xml {
    /**
    * If joined-tables should be output nested.
    *  Means, if you have joined two or more queries, the later
    *   specified tables will be nested within the result of the former
    *   table.
    *   Works at the moment only with mysql automagically. For other RDBMS
    *   you have to provide your table-relations by hand (see user_tableinfo)
    *
    * @var  boolean
    * @see  $user_tableinfo, doSql2Xml(), doArray2Xml();
    */
    public $nested = True;

    /**
    * Name of the tag element for resultsets
    *
    * @var  string
    * @see  insertNewResult()
    */
    public $tagNameResult = 'result';

    /**
    *
    * @var   object PEAR::DB
    * @access private
    */
    public $db = Null;


    /**
    * This array is used to give the structure of your database to the class.
    *  It's especially useful, if you don't use mysql, since other RDBMS than
    *  mysql are not able at the moment to provide the right information about
    *  your database structure within the query. And if you have more than 2
    *  tables joined in the sql it's also not possible for mysql to find out
    *  your real relations.
    *  The parameters are the same as in fieldInfo from the PEAR::DB and some
    *   additional ones. Here they come:
    *  From PEAR::DB->fieldinfo:
    *
    *    $tableInfo[$i]['table']    : the table, which field #$i belongs to.
    *           for some rdbms/comples queries and with arrays, it's impossible
    *           to find out to which table the field actually belongs. You can
    *           specify it here more accurate. Or if you want, that one fields
    *           belongs to another table, than it actually says (yes, there's
    *           use for that, see the upcoming tutorial ...)
    *
    *    $tableInfo[$i]['name']     : the name of field #$i. if you want another
    *           name for the tag, than the query or your array provides, assign
    *           it here.
    *
    *   Additional info
    *     $tableInfo['parent_key'][$table]  : index of the parent key for $table.
    *           this is the field, where the programm looks for changes, if this
    *           field changes, it assumes, that we need a new 'rowset' in the
    *           parent table.
    *
    *     $tableInfo['parent_table'][$table]: name of the parent table for $table.
    *
    * @var      array
    * @access    private
    */
    public $user_tableInfo = array();

    /**
    * the encoding type, the input from the db has
    */
    public $encoding_from  = 'ISO-8859-1';

    /**
    * the encoding type, the output in the xml should have
    * (note that domxml at the moment only support UTF-8, or at least it looks like)
    */
    public $encoding_to = 'UTF-8';

    public $use_iconv = false;
    public $tagname = 'tagname';

    public $dsn = Null;

    static private $contentToXmlTranslation = false;
    
    const CONTENT_APPEND_FRAGMENT = 2;
    const CONTENT_APPEND_DOCUMENT = 1;
    const CONTENT_APPEND_PLAIN = 0;
    /**
    * Constructor
    * The Constructor can take a Pear::DB 'data source name' (eg.
    *  "mysql://user:passwd@localhost/dbname") and will then connect
    *  to the DB, or a PEAR::DB object link, if you already connected
    *  the db before.
    "  If you provide nothing as $dsn, you only can later add stuff with
    *   a pear::db-resultset or as an array. providing sql-strings will
    *   not work.
    * the $root param is used, if you want to provide another name for your
    *  root-tag than "root". if you give an empty string (""), there will be no
    *  root element created here, but only when you add a resultset/array/sql-string.
    *  And the first tag of this result is used as the root tag.
    *
    * @param  mixed $dsn    PEAR::DB "data source name" or object DB object
    * @param  string $root  the name of the xml-doc root element.
    * @access   public
    */
    function __construct ($dsn = Null,$root = 'root',$Format=Null,$InputClasses=array('Sql','Dbresult','Array','Http','File','XmlObject','String')) {

        if (is_null ($Format))
        {
            include_once('XML/db2xml/Format.php');
            $FormatClass = 'XML_db2xml_Format';
        }
        else {
            include_once("XML/db2xml/Format/$Format.php");
            $FormatClass = "XML_db2xml_Format_$Format";
        }
        $this->dsn = $dsn;
        
        if (is_object($dsn) && $dsn->isUtf8) {
            $this->encoding_from = "UTF-8";
            $this->use_iconv = true;
        }
        
        $this->Format = new $FormatClass($this);
        $this->InputClasses = $InputClasses;

        $this->Format->setXmlDoc( new DomDocument('1.0'));


        if ($root) {
            
            
            $this->Format->setXmlRoot( $this->Format->xmldoc->appendChild($this->Format->xmldoc->createElement($root)));
        }

    }

    /**
    * General method for adding new resultsets to the xml-object
    *  Give a sql-query-string, a pear::db_result object or an array as
    *  input parameter, and the method calls the appropriate method for this
    *  input and adds this to $this->xmldoc
    *
    * @param    string sql-string, or object db_result, or array
    * @param    mixed additional parameters for the following functions
    * @access   public
    * @see      addResult(), addSql(), addArray(), addXmlFile()
    */
    function add($resultset, $params = Null)
    {
        if (is_array($this->InputClasses ))
        {
            foreach($this->InputClasses as $TestClass)
            {
               include_once("XML/db2xml/Input/$TestClass.php");
               if (call_user_func("addTestBefore_$TestClass",$resultset))
               {

                 return $this->addWithInput($TestClass,$resultset,$params);

                }
            }
        }

    }

    function addWithInput($container,$resultset, $params = Null)
    {

       $class = "XML_db2xml_Input_$container";
       if (!isset($this->Input) || get_class($this->Input) != strtolower($class))
       {
        include_once("XML/db2xml/Input/$container.php");
        $this->Input= new $class ($this);
       }
       $this->Input->add($resultset,$params);
    }


    /**
    * Returns an xml-string with a xml-representation of the resultsets.
    *
    * The resultset can be directly provided here, or if you need more than one
    * in your xml, then you have to provide each of them with add() before you
    * call getXML, but the last one can also be provided here.
    *
    * @param    mixed  $result result Object from a DB-query
    * @return   string  xml
    * @access   public
    */
    function getXML($result = Null)
    {
        $xmldoc = $this->getXMLObject($result);
        return $xmldoc->saveXML();
    }

    /**
    * Returns an xml DomDocument Object with a xml-representation of the resultsets.
    *
    * The resultset can be directly provided here, or if you need more than one
    * in your xml, then you have to provide each of them with add() before you
    * call getXMLObject, but the last one can also be provided here.
    *
    * @param    mixed $result result Object from a DB-query
    * @return   Object DomDocument
    * @access   public
    */
    function getXMLObject($result = Null)
    {
        if ($result) {
            $this->add ($result);
        }
        return $this->Format->xmldoc;
    }


    /**
    * This method sets the options for the class
    *  One can only set variables, which are defined at the top of
    *  of this class.
    *
    * @param    array   options to be passed to the class
    * @param    boolean   if the old suboptions should be deleted
    * @access   public
    * @see      $nested,$user_options,$user_tableInfo
    */

    function setOptions($options,$delete = False) {

        //this first if is for compatibility reasons
        // better use setUserOptions or $this->Format->setOptions
        if (isset($options['user_options']))
        {
            $this->Format->SetOptions($options);
        }


        if (is_array($options))
        {
            foreach ($options as $option => $value)
            {
               if (isset($this->{$option}))
                {
                    if (is_array($value) && ! $delete)
                    {
                        foreach ($value as $suboption => $subvalue)
                        {
                            $this->{$option}["$suboption"] = $subvalue;
                        }
                    }
                    else
                    {
                          $this->$option = $value;
                    }
                }
            }
        }
    }

    function setFormatOptions($options,$delete = False) {
        $FormatOptions['user_options'] = $options;
        $this->Format->SetOptions($FormatOptions);
    }

    // here come some helper functions...

    /**
    * make utf8 out of the input data and escape & with &amp; and "< " with "&lt; "
    * (we assume that when there's no space after < it's a tag, which we need in the xml)
    *  I'm not sure, if this is the standard way, but it works for me.
    *
    * @param    string text to be utfed.
    * @access private
    */
    function xml_encode ($text, $stringReplace = null)
    {
        if ($this->use_iconv)
        {
            // Notice: iconv(): Unknown error (0) in /home/bitlib2/php/XML/db2xml.php on line 327
// strange messaages as in 4.3.0-dev, turn them off
             $text = @iconv($this->encoding_from,$this->encoding_to,preg_replace('/\&amp;([#a-z0-9A-Z]+);/','&$1;',str_replace('&','&amp;',$text)));
             if ($stringReplace) {
                $text = str_replace($stringReplace[0],$stringReplace[1],$text);
            }
             if (! isset($text) )
             {
                if (isset($php_errormsg))
                {
                    $errormsg = "error: $php_errormsg";
                }
                else
                {
                    $errormsg = 'undefined iconv error, turn on track_errors in php.ini to get more details';
                }
                return PEAR::raiseError($errormsg,Null,PEAR_ERROR_DIE);
             }
             else {
                return $text;
             }
        }
        else
        {
            $ret = utf8_encode(preg_replace('/\&amp;([#a-z0-9A-Z]+);/','&$1;',str_replace('&','&amp;',$text)));
            if ($stringReplace) {
                $ret = str_replace($stringReplace[0],$stringReplace[1],$ret);
            }
            return $ret;
        }
        return $text;
    }

    //taken from kc@hireability.com at http://www.php.net/manual/en/function.array-merge-recursive.php
    /**
    * There seemed to be no built in function that would merge two arrays recursively and clobber
    *   any existing key/value pairs. Array_Merge() is not recursive, and array_merge_recursive
    *   seemed to give unsatisfactory results... it would append duplicate key/values.
    *
    *   So here's a cross between array_merge and array_merge_recursive
    **/
    /**
    *
    * @param    array first array to be merged
    * @param    array second array to be merged
    * @return   array merged array
    * @access private
    */
    function array_merge_clobber($a1,$a2)
    {
        if(!is_array($a1) || !is_array($a2)) return false;
        $newarray = $a1;
        while (list($key, $val) = each($a2))
        {
            if (is_array($val) && is_array($newarray[$key]))
            {
                $newarray[$key] = $this->array_merge_clobber($newarray[$key], $val);
            }
            else
            {
                $newarray[$key] = $val;
            }
        }
        return $newarray;
    }


    /**
    * sets the encoding for the db2xml transformation
    * @param    string $encoding_from encoding to transform from
    * @param    string $encoding_to encoding to transform to
    * @access public
    */
    function setEncoding ($encoding_from = 'ISO-8859-1', $encoding_to ='UTF-8')
    {
        if ($encoding_from == 'ISO-8859-1' && $encoding_to ='UTF-8') {
            $this->use_iconv = false;
        } else if (function_exists('iconv') && isset($this->encoding_from) && isset($this->encoding_to)) {
            $this->use_iconv = true;
            ini_set('track_errors',1);
        } 
        $this->encoding_from = $encoding_from;
        $this->encoding_to = $encoding_to;
    }
    /**
    * @param array $parentTables parent to child relation
    * @access public
    */

    function SetParentTables($parentTables)
    {
        foreach ($parentTables as $table => $parent)
        {
            $table_info['parent_table'][$table]=$parent;
        }
        $this->SetOptions(array('user_tableInfo'=>$table_info));
    }


    /**
    * returns the content of the first match of the xpath expression
    *
    * @param    string $expr xpath expression
    * @return   mixed content of the evaluated xpath expression
    * @access   public
    */

    function getXpathValue ($expr)
    {

        $xpth = new DOMXPath($this->Format->xmldoc);
        $xnode = $xpth->query($expr);

        if ($xnode->item(0))
        {
            $firstnode = $xnode->item(0);

            $children = $firstnode->firstChild;
            $value = $children->nodeValue;
            return $value;
        }

        else
        {
            return Null;
        }
    }

    /**
    * get the values as an array from the childtags from the first match of the xpath expression
    *
    * @param    string xpath expression
    * @return   array with key->value of subtags
    * @access   public
    */

    function getXpathChildValues ($expr)
    {
        $xpth = new DOMXPath($this->Format->xmldoc);
        $xnode = $xpth->query($expr);

        if ($xnode->item(0))
        {
            foreach ($xnode->item(0)->childNodes as $child)
            {
                $children = $child->childNodes;
                if($children) {
                    $value[$child->nodeName] = $children->item(0)->nodeValue;
                }
            }
            return $value;
        }
        else
        {
            return Null;
        }
    }

    function setNested ($nested)
    {
        $this->nested = $nested;
    }
    
    public function setContentIsXml($bool) {
        if ($bool) {
            // the CONTENT_APPEND_FRAGMENT should be faster than the one with DOCUMENT
            // please apply the patch  from
            // http://svn.bitflux.ch/repos/public/misc/dompatches/documentfragment_appendXML.patch
            // if you're using sth around PHP 5.0.0 
            if (version_compare(phpversion(),"5.0.99",">") || is_callable(array('domdocumentfragment','appendXML'))) {
                self::$contentToXmlTranslation =  XML_db2xml::CONTENT_APPEND_FRAGMENT;
            } else {
                self::$contentToXmlTranslation =  XML_db2xml::CONTENT_APPEND_DOCUMENT;
            }
        } else {
                self::$contentToXmlTranslation =  XML_db2xml::CONTENT_APPEND_PLAIN;
        }
    }
        
    
    static function newChild($parent, $newChild, $content) {
        if ($parent->nodeType == XML_DOCUMENT_NODE) {
            $doc = $parent;
        } else {
            $doc = $parent->ownerDocument;
        }
        if ( self::$contentToXmlTranslation && (strpos($content,"<") !== false || preg_match("/\&\S;/", $content) )) {
            if (self::$contentToXmlTranslation == XML_db2xml::CONTENT_APPEND_FRAGMENT) {
                $f = $doc->createDocumentFragment();
                $f->appendXML("<$newChild>".$content."</$newChild>");
                return $parent->appendChild($f);
                
            } else { //self::$contentToXmlTranslation == CONTENT_APPEND_DOCUMENT in this case, no need to check
                //insert that code here
                $dom = new DomDocument();
                $dom->loadXML("<$newChild>".$content."</$newChild>");
                $newnode = $parent->appendChild($doc->importNode($dom->documentElement,true));
                unset ($dom);
                return $newnode;
            }
        }
        return $parent->appendChild( $doc->createElement($newChild,$content));
        
    }
    
    static function addRoot($doc,$root) {
        
        if ($doc->documentElement && $doc->documentElement->nodeName == $root) {
            return $doc->documentElement;
        }
        $root = $doc->createElement($root);
        if ($doc->documentElement) {   
            return $doc->replaceChild($root,$doc->documentElement);
        } else {
            return $doc->appendChild($root);
        }
    }
}
?>
Back to Top