Subversion Repositories Applications.gtt

Rev

Rev 94 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
94 jpm 1
<?php
2
/**
3
 * package.xml generation class, package.xml version 2.0
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * LICENSE: This source file is subject to version 3.0 of the PHP license
8
 * that is available through the world-wide-web at the following URI:
9
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
10
 * the PHP License and are unable to obtain it through the web, please
11
 * send a note to license@php.net so we can mail you a copy immediately.
12
 *
13
 * @category   pear
14
 * @package    PEAR
15
 * @author     Greg Beaver <cellog@php.net>
16
 * @author     Stephan Schmidt (original XML_Serializer code)
17
 * @copyright  1997-2006 The PHP Group
18
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
19
 * @version    CVS: $Id: v2.php,v 1.35 2006/03/25 21:09:08 cellog Exp $
20
 * @link       http://pear.php.net/package/PEAR
21
 * @since      File available since Release 1.4.0a1
22
 */
23
/**
24
 * file/dir manipulation routines
25
 */
26
require_once 'System.php';
27
/**
28
 * This class converts a PEAR_PackageFile_v2 object into any output format.
29
 *
30
 * Supported output formats include array, XML string (using S. Schmidt's
31
 * XML_Serializer, slightly customized)
32
 * @category   pear
33
 * @package    PEAR
34
 * @author     Greg Beaver <cellog@php.net>
35
 * @author     Stephan Schmidt (original XML_Serializer code)
36
 * @copyright  1997-2006 The PHP Group
37
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
38
 * @version    Release: 1.5.1
39
 * @link       http://pear.php.net/package/PEAR
40
 * @since      Class available since Release 1.4.0a1
41
 */
42
class PEAR_PackageFile_Generator_v2
43
{
44
   /**
45
    * default options for the serialization
46
    * @access private
47
    * @var array $_defaultOptions
48
    */
49
    var $_defaultOptions = array(
50
                         'indent'             => ' ',                    // string used for indentation
51
                         'linebreak'          => "\n",                  // string used for newlines
52
                         'typeHints'          => false,                 // automatically add type hin attributes
53
                         'addDecl'            => true,                 // add an XML declaration
54
                         'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
55
                         'classAsTagName'     => false,                 // use classname for objects in indexed arrays
56
                         'keyAttribute'       => '_originalKey',        // attribute where original key is stored
57
                         'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
58
                         'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
59
                         'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
60
                         'prependAttributes'  => '',                    // prepend string for attributes
61
                         'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
62
                         'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
63
                         'addDoctype'         => false,                 // add a doctype declaration
64
                         'doctype'            => null,                  // supply a string or an array with id and uri ({@see PEAR_PackageFile_Generator_v2_PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration()}
65
                         'rootName'           => 'package',                  // name of the root tag
66
                         'rootAttributes'     => array(
67
                             'version' => '2.0',
68
                             'xmlns' => 'http://pear.php.net/dtd/package-2.0',
69
                             'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
70
                             'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
71
                             'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
72
http://pear.php.net/dtd/tasks-1.0.xsd
73
http://pear.php.net/dtd/package-2.0
74
http://pear.php.net/dtd/package-2.0.xsd',
75
                         ),               // attributes of the root tag
76
                         'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
77
                         'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
78
                         'beautifyFilelist'   => false,
79
                         'encoding' => 'UTF-8',
80
                        );
81
 
82
   /**
83
    * options for the serialization
84
    * @access private
85
    * @var array $options
86
    */
87
    var $options = array();
88
 
89
   /**
90
    * current tag depth
91
    * @var integer $_tagDepth
92
    */
93
    var $_tagDepth = 0;
94
 
95
   /**
96
    * serilialized representation of the data
97
    * @var string $_serializedData
98
    */
99
    var $_serializedData = null;
100
    /**
101
     * @var PEAR_PackageFile_v2
102
     */
103
    var $_packagefile;
104
    /**
105
     * @param PEAR_PackageFile_v2
106
     */
107
    function PEAR_PackageFile_Generator_v2(&$packagefile)
108
    {
109
        $this->_packagefile = &$packagefile;
110
    }
111
 
112
    /**
113
     * @return string
114
     */
115
    function getPackagerVersion()
116
    {
117
        return '1.5.1';
118
    }
119
 
120
    /**
121
     * @param PEAR_Packager
122
     * @param bool generate a .tgz or a .tar
123
     * @param string|null temporary directory to package in
124
     */
125
    function toTgz(&$packager, $compress = true, $where = null)
126
    {
127
        $a = null;
128
        return $this->toTgz2($packager, $a, $compress, $where);
129
    }
130
 
131
    /**
132
     * Package up both a package.xml and package2.xml for the same release
133
     * @param PEAR_Packager
134
     * @param PEAR_PackageFile_v1
135
     * @param bool generate a .tgz or a .tar
136
     * @param string|null temporary directory to package in
137
     */
138
    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
139
    {
140
        require_once 'Archive/Tar.php';
141
        if (!$this->_packagefile->isEquivalent($pf1)) {
142
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
143
                basename($pf1->getPackageFile()) .
144
                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
145
                . '"');
146
        }
147
        if ($where === null) {
148
            if (!($where = System::mktemp(array('-d')))) {
149
                return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: mktemp failed');
150
            }
151
        } elseif (!@System::mkDir(array('-p', $where))) {
152
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' . $where . '" could' .
153
                ' not be created');
154
        }
155
        if (file_exists($where . DIRECTORY_SEPARATOR . 'package.xml') &&
156
              !is_file($where . DIRECTORY_SEPARATOR . 'package.xml')) {
157
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
158
                ' "' . $where . DIRECTORY_SEPARATOR . 'package.xml"');
159
        }
160
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
161
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
162
        }
163
        $ext = $compress ? '.tgz' : '.tar';
164
        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
165
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
166
        if (file_exists(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext) &&
167
              !is_file(getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext)) {
168
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
169
                getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext . '"');
170
        }
171
        if ($pkgfile = $this->_packagefile->getPackageFile()) {
172
            $pkgdir = dirname(realpath($pkgfile));
173
            $pkgfile = basename($pkgfile);
174
        } else {
175
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
176
                'be created from a real file');
177
        }
178
        // {{{ Create the package file list
179
        $filelist = array();
180
        $i = 0;
181
        $this->_packagefile->flattenFilelist();
182
        $contents = $this->_packagefile->getContents();
183
        if (isset($contents['bundledpackage'])) { // bundles of packages
184
            $contents = $contents['bundledpackage'];
185
            if (!isset($contents[0])) {
186
                $contents = array($contents);
187
            }
188
            $packageDir = $where;
189
            foreach ($contents as $i => $package) {
190
                $fname = $package;
191
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
192
                if (!file_exists($file)) {
193
                    return $packager->raiseError("File does not exist: $fname");
194
                }
195
                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
196
                System::mkdir(array('-p', dirname($tfile)));
197
                copy($file, $tfile);
198
                $filelist[$i++] = $tfile;
199
                $packager->log(2, "Adding package $fname");
200
            }
201
        } else { // normal packages
202
            $contents = $contents['dir']['file'];
203
            if (!isset($contents[0])) {
204
                $contents = array($contents);
205
            }
206
 
207
            $packageDir = $where;
208
            foreach ($contents as $i => $file) {
209
                $fname = $file['attribs']['name'];
210
                $atts = $file['attribs'];
211
                $orig = $file;
212
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
213
                if (!file_exists($file)) {
214
                    return $packager->raiseError("File does not exist: $fname");
215
                } else {
216
                    $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
217
                    unset($orig['attribs']);
218
                    if (count($orig)) { // file with tasks
219
                        // run any package-time tasks
220
                        $contents = file_get_contents($file);
221
                        foreach ($orig as $tag => $raw) {
222
                            $tag = str_replace($this->_packagefile->getTasksNs() . ':', '', $tag);
223
                            $task = "PEAR_Task_$tag";
224
                            $task = &new $task($this->_packagefile->_config,
225
                                $this->_packagefile->_logger,
226
                                PEAR_TASK_PACKAGE);
227
                            $task->init($raw, $atts, null);
228
                            $res = $task->startSession($this->_packagefile, $contents, $tfile);
229
                            if (!$res) {
230
                                continue; // skip this task
231
                            }
232
                            if (PEAR::isError($res)) {
233
                                return $res;
234
                            }
235
                            $contents = $res; // save changes
236
                            System::mkdir(array('-p', dirname($tfile)));
237
                            $wp = fopen($tfile, "wb");
238
                            fwrite($wp, $contents);
239
                            fclose($wp);
240
                        }
241
                    }
242
                    if (!file_exists($tfile)) {
243
                        System::mkdir(array('-p', dirname($tfile)));
244
                        copy($file, $tfile);
245
                    }
246
                    $filelist[$i++] = $tfile;
247
                    $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
248
                    $packager->log(2, "Adding file $fname");
249
                }
250
            }
251
        }
252
            // }}}
253
        if ($pf1 !== null) {
254
            $name = 'package2.xml';
255
        } else {
256
            $name = 'package.xml';
257
        }
258
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
259
        if ($packagexml) {
260
            $tar =& new Archive_Tar($dest_package, $compress);
261
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
262
            // ----- Creates with the package.xml file
263
            $ok = $tar->createModify(array($packagexml), '', $where);
264
            if (PEAR::isError($ok)) {
265
                return $packager->raiseError($ok);
266
            } elseif (!$ok) {
267
                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
268
                    ' failed');
269
            }
270
            // ----- Add the content of the package
271
            if (!$tar->addModify($filelist, $pkgver, $where)) {
272
                return $packager->raiseError(
273
                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
274
            }
275
            // add the package.xml version 1.0
276
            if ($pf1 !== null) {
277
                $pfgen = &$pf1->getDefaultGenerator();
278
                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING,
279
                    'package.xml', true);
280
                if (!$tar->addModify(array($packagexml1), '', $where)) {
281
                    return $packager->raiseError(
282
                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
283
                }
284
            }
285
            return $dest_package;
286
        }
287
    }
288
 
289
    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
290
    {
291
        if (!$this->_packagefile->validate($state)) {
292
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
293
                null, null, null, $this->_packagefile->getValidationWarnings());
294
        }
295
        if ($where === null) {
296
            if (!($where = System::mktemp(array('-d')))) {
297
                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
298
            }
299
        } elseif (!@System::mkDir(array('-p', $where))) {
300
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
301
                ' not be created');
302
        }
303
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
304
        $np = @fopen($newpkgfile, 'wb');
305
        if (!$np) {
306
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
307
               "$name as $newpkgfile");
308
        }
309
        fwrite($np, $this->toXml($state));
310
        fclose($np);
311
        return $newpkgfile;
312
    }
313
 
314
    function &toV2()
315
    {
316
        return $this->_packagefile;
317
    }
318
 
319
    /**
320
     * Return an XML document based on the package info (as returned
321
     * by the PEAR_Common::infoFrom* methods).
322
     *
323
     * @return string XML data
324
     */
325
    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
326
    {
327
        $this->_packagefile->setDate(date('Y-m-d'));
328
        $this->_packagefile->setTime(date('H:i:s'));
329
        if (!$this->_packagefile->validate($state)) {
330
            return false;
331
        }
332
        if (is_array($options)) {
333
            $this->options = array_merge($this->_defaultOptions, $options);
334
        } else {
335
            $this->options = $this->_defaultOptions;
336
        }
337
        $arr = $this->_packagefile->getArray();
338
        if (isset($arr['filelist'])) {
339
            unset($arr['filelist']);
340
        }
341
        if (isset($arr['_lastversion'])) {
342
            unset($arr['_lastversion']);
343
        }
344
        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
345
            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
346
            unset($arr['contents']['dir']['file']);
347
            if (isset($use['dir'])) {
348
                $arr['contents']['dir']['dir'] = $use['dir'];
349
            }
350
            if (isset($use['file'])) {
351
                $arr['contents']['dir']['file'] = $use['file'];
352
            }
353
            $this->options['beautifyFilelist'] = true;
354
        }
355
        $arr['attribs']['packagerversion'] = '1.5.1';
356
        if ($this->serialize($arr, $options)) {
357
            return $this->_serializedData . "\n";
358
        }
359
        return false;
360
    }
361
 
362
 
363
    function _recursiveXmlFilelist($list)
364
    {
365
        $dirs = array();
366
        if (isset($list['attribs'])) {
367
            $file = $list['attribs']['name'];
368
            unset($list['attribs']['name']);
369
            $attributes = $list['attribs'];
370
            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
371
        } else {
372
            foreach ($list as $a) {
373
                $file = $a['attribs']['name'];
374
                $attributes = $a['attribs'];
375
                unset($a['attribs']);
376
                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
377
            }
378
        }
379
        $this->_formatDir($dirs);
380
        $this->_deFormat($dirs);
381
        return $dirs;
382
    }
383
 
384
    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
385
    {
386
        if (!$tasks) {
387
            $tasks = array();
388
        }
389
        if ($dir == array() || $dir == array('.')) {
390
            $dirs['file'][basename($file)] = $tasks;
391
            $attributes['name'] = basename($file);
392
            $dirs['file'][basename($file)]['attribs'] = $attributes;
393
            return;
394
        }
395
        $curdir = array_shift($dir);
396
        if (!isset($dirs['dir'][$curdir])) {
397
            $dirs['dir'][$curdir] = array();
398
        }
399
        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
400
    }
401
 
402
    function _formatDir(&$dirs)
403
    {
404
        if (!count($dirs)) {
405
            return array();
406
        }
407
        $newdirs = array();
408
        if (isset($dirs['dir'])) {
409
            $newdirs['dir'] = $dirs['dir'];
410
        }
411
        if (isset($dirs['file'])) {
412
            $newdirs['file'] = $dirs['file'];
413
        }
414
        $dirs = $newdirs;
415
        if (isset($dirs['dir'])) {
416
            uksort($dirs['dir'], 'strnatcasecmp');
417
            foreach ($dirs['dir'] as $dir => $contents) {
418
                $this->_formatDir($dirs['dir'][$dir]);
419
            }
420
        }
421
        if (isset($dirs['file'])) {
422
            uksort($dirs['file'], 'strnatcasecmp');
423
        };
424
    }
425
 
426
    function _deFormat(&$dirs)
427
    {
428
        if (!count($dirs)) {
429
            return array();
430
        }
431
        $newdirs = array();
432
        if (isset($dirs['dir'])) {
433
            foreach ($dirs['dir'] as $dir => $contents) {
434
                $newdir = array();
435
                $newdir['attribs']['name'] = $dir;
436
                $this->_deFormat($contents);
437
                foreach ($contents as $tag => $val) {
438
                    $newdir[$tag] = $val;
439
                }
440
                $newdirs['dir'][] = $newdir;
441
            }
442
            if (count($newdirs['dir']) == 1) {
443
                $newdirs['dir'] = $newdirs['dir'][0];
444
            }
445
        }
446
        if (isset($dirs['file'])) {
447
            foreach ($dirs['file'] as $name => $file) {
448
                $newdirs['file'][] = $file;
449
            }
450
            if (count($newdirs['file']) == 1) {
451
                $newdirs['file'] = $newdirs['file'][0];
452
            }
453
        }
454
        $dirs = $newdirs;
455
    }
456
 
457
    /**
458
    * reset all options to default options
459
    *
460
    * @access   public
461
    * @see      setOption(), XML_Unserializer()
462
    */
463
    function resetOptions()
464
    {
465
        $this->options = $this->_defaultOptions;
466
    }
467
 
468
   /**
469
    * set an option
470
    *
471
    * You can use this method if you do not want to set all options in the constructor
472
    *
473
    * @access   public
474
    * @see      resetOption(), XML_Serializer()
475
    */
476
    function setOption($name, $value)
477
    {
478
        $this->options[$name] = $value;
479
    }
480
 
481
   /**
482
    * sets several options at once
483
    *
484
    * You can use this method if you do not want to set all options in the constructor
485
    *
486
    * @access   public
487
    * @see      resetOption(), XML_Unserializer(), setOption()
488
    */
489
    function setOptions($options)
490
    {
491
        $this->options = array_merge($this->options, $options);
492
    }
493
 
494
   /**
495
    * serialize data
496
    *
497
    * @access   public
498
    * @param    mixed    $data data to serialize
499
    * @return   boolean  true on success, pear error on failure
500
    */
501
    function serialize($data, $options = null)
502
    {
503
        // if options have been specified, use them instead
504
        // of the previously defined ones
505
        if (is_array($options)) {
506
            $optionsBak = $this->options;
507
            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
508
                $this->options = array_merge($this->_defaultOptions, $options);
509
            } else {
510
                $this->options = array_merge($this->options, $options);
511
            }
512
        }
513
        else {
514
            $optionsBak = null;
515
        }
516
 
517
        //  start depth is zero
518
        $this->_tagDepth = 0;
519
 
520
        $this->_serializedData = '';
521
        // serialize an array
522
        if (is_array($data)) {
523
            if (isset($this->options['rootName'])) {
524
                $tagName = $this->options['rootName'];
525
            } else {
526
                $tagName = 'array';
527
            }
528
 
529
            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
530
        }
531
 
532
        // add doctype declaration
533
        if ($this->options['addDoctype'] === true) {
534
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
535
                                   . $this->options['linebreak']
536
                                   . $this->_serializedData;
537
        }
538
 
539
        //  build xml declaration
540
        if ($this->options['addDecl']) {
541
            $atts = array();
542
            if (isset($this->options['encoding']) ) {
543
                $encoding = $this->options['encoding'];
544
            } else {
545
                $encoding = null;
546
            }
547
            $this->_serializedData = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration('1.0', $encoding)
548
                                   . $this->options['linebreak']
549
                                   . $this->_serializedData;
550
        }
551
 
552
 
553
		if ($optionsBak !== null) {
554
			$this->options = $optionsBak;
555
		}
556
 
557
        return  true;
558
    }
559
 
560
   /**
561
    * get the result of the serialization
562
    *
563
    * @access public
564
    * @return string serialized XML
565
    */
566
    function getSerializedData()
567
    {
568
        if ($this->_serializedData == null ) {
569
            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
570
        }
571
        return $this->_serializedData;
572
    }
573
 
574
   /**
575
    * serialize any value
576
    *
577
    * This method checks for the type of the value and calls the appropriate method
578
    *
579
    * @access private
580
    * @param  mixed     $value
581
    * @param  string    $tagName
582
    * @param  array     $attributes
583
    * @return string
584
    */
585
    function _serializeValue($value, $tagName = null, $attributes = array())
586
    {
587
        if (is_array($value)) {
588
            $xml = $this->_serializeArray($value, $tagName, $attributes);
589
        } elseif (is_object($value)) {
590
            $xml = $this->_serializeObject($value, $tagName);
591
        } else {
592
            $tag = array(
593
                          'qname'      => $tagName,
594
                          'attributes' => $attributes,
595
                          'content'    => $value
596
                        );
597
            $xml = $this->_createXMLTag($tag);
598
        }
599
        return $xml;
600
    }
601
 
602
   /**
603
    * serialize an array
604
    *
605
    * @access   private
606
    * @param    array   $array       array to serialize
607
    * @param    string  $tagName     name of the root tag
608
    * @param    array   $attributes  attributes for the root tag
609
    * @return   string  $string      serialized data
610
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::isValidName() to check, whether key has to be substituted
611
    */
612
    function _serializeArray(&$array, $tagName = null, $attributes = array())
613
    {
614
        $_content = null;
615
 
616
        /**
617
         * check for special attributes
618
         */
619
        if ($this->options['attributesArray'] !== null) {
620
            if (isset($array[$this->options['attributesArray']])) {
621
                $attributes = $array[$this->options['attributesArray']];
622
                unset($array[$this->options['attributesArray']]);
623
            }
624
            /**
625
             * check for special content
626
             */
627
            if ($this->options['contentName'] !== null) {
628
                if (isset($array[$this->options['contentName']])) {
629
                    $_content = $array[$this->options['contentName']];
630
                    unset($array[$this->options['contentName']]);
631
                }
632
            }
633
        }
634
 
635
        /*
636
        * if mode is set to simpleXML, check whether
637
        * the array is associative or indexed
638
        */
639
        if (is_array($array) && $this->options['mode'] == 'simplexml') {
640
            $indexed = true;
641
            if (!count($array)) {
642
                $indexed = false;
643
            }
644
            foreach ($array as $key => $val) {
645
                if (!is_int($key)) {
646
                    $indexed = false;
647
                    break;
648
                }
649
            }
650
 
651
            if ($indexed && $this->options['mode'] == 'simplexml') {
652
                $string = '';
653
                foreach ($array as $key => $val) {
654
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
655
                        if (!isset($this->_curdir)) {
656
                            $this->_curdir = '';
657
                        }
658
                        $savedir = $this->_curdir;
659
                        if (isset($val['attribs'])) {
660
                            if ($val['attribs']['name'] == '/') {
661
                                $this->_curdir = '/';
662
                            } else {
663
                                if ($this->_curdir == '/') {
664
                                    $this->_curdir = '';
665
                                }
666
                                $this->_curdir .= '/' . $val['attribs']['name'];
667
                            }
668
                        }
669
                    }
670
                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
671
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
672
                        $string .= ' <!-- ' . $this->_curdir . ' -->';
673
                        if (empty($savedir)) {
674
                            unset($this->_curdir);
675
                        } else {
676
                            $this->_curdir = $savedir;
677
                        }
678
                    }
679
 
680
                    $string .= $this->options['linebreak'];
681
        			//	do indentation
682
                    if ($this->options['indent']!==null && $this->_tagDepth>0) {
683
                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
684
                    }
685
                }
686
                return rtrim($string);
687
            }
688
        }
689
 
690
		if ($this->options['scalarAsAttributes'] === true) {
691
	        foreach ($array as $key => $value) {
692
				if (is_scalar($value) && (PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key) === true)) {
693
					unset($array[$key]);
694
					$attributes[$this->options['prependAttributes'].$key] = $value;
695
				}
696
			}
697
		}
698
 
699
        // check for empty array => create empty tag
700
        if (empty($array)) {
701
            $tag = array(
702
                            'qname'      => $tagName,
703
                            'content'    => $_content,
704
                            'attributes' => $attributes
705
                        );
706
 
707
        } else {
708
            $this->_tagDepth++;
709
            $tmp = $this->options['linebreak'];
710
            foreach ($array as $key => $value) {
711
    			//	do indentation
712
                if ($this->options['indent']!==null && $this->_tagDepth>0) {
713
                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
714
                }
715
 
716
    			//	copy key
717
    			$origKey	=	$key;
718
    			//	key cannot be used as tagname => use default tag
719
                $valid = PEAR_PackageFile_Generator_v2_XML_Util::isValidName($key);
720
    	        if (PEAR::isError($valid)) {
721
    	            if ($this->options['classAsTagName'] && is_object($value)) {
722
    	                $key = get_class($value);
723
    	            } else {
724
            	        $key = $this->options['defaultTagName'];
725
    	            }
726
           	 	}
727
                $atts = array();
728
                if ($this->options['typeHints'] === true) {
729
                    $atts[$this->options['typeAttribute']] = gettype($value);
730
    				if ($key !== $origKey) {
731
    					$atts[$this->options['keyAttribute']] = (string)$origKey;
732
    				}
733
 
734
                }
735
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
736
                    if (!isset($this->_curdir)) {
737
                        $this->_curdir = '';
738
                    }
739
                    $savedir = $this->_curdir;
740
                    if (isset($value['attribs'])) {
741
                        if ($value['attribs']['name'] == '/') {
742
                            $this->_curdir = '/';
743
                        } else {
744
                            $this->_curdir .= '/' . $value['attribs']['name'];
745
                        }
746
                    }
747
                }
748
 
749
                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
750
                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
751
                }
752
                $tmp .= $this->_createXMLTag(array(
753
                                                    'qname'      => $key,
754
                                                    'attributes' => $atts,
755
                                                    'content'    => $value )
756
                                            );
757
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
758
                    if (isset($value['attribs'])) {
759
                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
760
                        if (empty($savedir)) {
761
                            unset($this->_curdir);
762
                        } else {
763
                            $this->_curdir = $savedir;
764
                        }
765
                    }
766
                }
767
                $tmp .= $this->options['linebreak'];
768
            }
769
 
770
            $this->_tagDepth--;
771
            if ($this->options['indent']!==null && $this->_tagDepth>0) {
772
                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
773
            }
774
 
775
    		if (trim($tmp) === '') {
776
    			$tmp = null;
777
    		}
778
 
779
            $tag = array(
780
                            'qname'      => $tagName,
781
                            'content'    => $tmp,
782
                            'attributes' => $attributes
783
                        );
784
        }
785
        if ($this->options['typeHints'] === true) {
786
            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
787
                $tag['attributes'][$this->options['typeAttribute']] = 'array';
788
            }
789
        }
790
 
791
        $string = $this->_createXMLTag($tag, false);
792
        return $string;
793
    }
794
 
795
   /**
796
    * create a tag from an array
797
    * this method awaits an array in the following format
798
    * array(
799
    *       'qname'        => $tagName,
800
    *       'attributes'   => array(),
801
    *       'content'      => $content,      // optional
802
    *       'namespace'    => $namespace     // optional
803
    *       'namespaceUri' => $namespaceUri  // optional
804
    *   )
805
    *
806
    * @access   private
807
    * @param    array   $tag tag definition
808
    * @param    boolean $replaceEntities whether to replace XML entities in content or not
809
    * @return   string  $string XML tag
810
    */
811
    function _createXMLTag( $tag, $replaceEntities = true )
812
    {
813
        if ($this->options['indentAttributes'] !== false) {
814
            $multiline = true;
815
            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);
816
 
817
            if ($this->options['indentAttributes'] == '_auto') {
818
                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
819
 
820
            } else {
821
                $indent .= $this->options['indentAttributes'];
822
            }
823
        } else {
824
            $multiline = false;
825
            $indent    = false;
826
        }
827
 
828
        if (is_array($tag['content'])) {
829
            if (empty($tag['content'])) {
830
                $tag['content'] =   '';
831
            }
832
        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
833
            $tag['content'] =   '';
834
        }
835
 
836
        if (is_scalar($tag['content']) || is_null($tag['content'])) {
837
            if ($this->options['encoding'] == 'UTF-8' &&
838
                  version_compare(phpversion(), '5.0.0', 'lt')) {
839
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML;
840
            } else {
841
                $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML;
842
            }
843
            $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak'], $encoding);
844
        } elseif (is_array($tag['content'])) {
845
            $tag    =   $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
846
        } elseif (is_object($tag['content'])) {
847
            $tag    =   $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
848
        } elseif (is_resource($tag['content'])) {
849
            settype($tag['content'], 'string');
850
            $tag    =   PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities);
851
        }
852
        return  $tag;
853
    }
854
}
855
 
856
// well, it's one way to do things without extra deps ...
857
/* vim: set expandtab tabstop=4 shiftwidth=4: */
858
// +----------------------------------------------------------------------+
859
// | PHP Version 4                                                        |
860
// +----------------------------------------------------------------------+
861
// | Copyright (c) 1997-2002 The PHP Group                                |
862
// +----------------------------------------------------------------------+
863
// | This source file is subject to version 2.0 of the PHP license,       |
864
// | that is bundled with this package in the file LICENSE, and is        |
865
// | available at through the world-wide-web at                           |
866
// | http://www.php.net/license/2_02.txt.                                 |
867
// | If you did not receive a copy of the PHP license and are unable to   |
868
// | obtain it through the world-wide-web, please send a note to          |
869
// | license@php.net so we can mail you a copy immediately.               |
870
// +----------------------------------------------------------------------+
871
// | Authors: Stephan Schmidt <schst@php-tools.net>                       |
872
// +----------------------------------------------------------------------+
873
//
874
//    $Id: v2.php,v 1.35 2006/03/25 21:09:08 cellog Exp $
875
 
876
/**
877
 * error code for invalid chars in XML name
878
 */
879
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS", 51);
880
 
881
/**
882
 * error code for invalid chars in XML name
883
 */
884
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START", 52);
885
 
886
/**
887
 * error code for non-scalar tag content
888
 */
889
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT", 60);
890
 
891
/**
892
 * error code for missing tag name
893
 */
894
define("PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME", 61);
895
 
896
/**
897
 * replace XML entities
898
 */
899
define("PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES", 1);
900
 
901
/**
902
 * embedd content in a CData Section
903
 */
904
define("PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION", 2);
905
 
906
/**
907
 * do not replace entitites
908
 */
909
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE", 0);
910
 
911
/**
912
 * replace all XML entitites
913
 * This setting will replace <, >, ", ' and &
914
 */
915
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML", 1);
916
 
917
/**
918
 * replace only required XML entitites
919
 * This setting will replace <, " and &
920
 */
921
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED", 2);
922
 
923
/**
924
 * replace HTML entitites
925
 * @link    http://www.php.net/htmlentities
926
 */
927
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML", 3);
928
 
929
/**
930
 * replace all XML entitites, and encode from ISO-8859-1 to UTF-8
931
 * This setting will replace <, >, ", ' and &
932
 */
933
define("PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML", 4);
934
 
935
/**
936
 * utility class for working with XML documents
937
 *
938
 * customized version of XML_Util 0.6.0
939
 *
940
 * @category XML
941
 * @package  PEAR
942
 * @version  0.6.0
943
 * @author   Stephan Schmidt <schst@php.net>
944
 * @author   Gregory Beaver <cellog@php.net>
945
 */
946
class PEAR_PackageFile_Generator_v2_XML_Util {
947
 
948
   /**
949
    * return API version
950
    *
951
    * @access   public
952
    * @static
953
    * @return   string  $version API version
954
    */
955
    function apiVersion()
956
    {
957
        return "0.6";
958
    }
959
 
960
   /**
961
    * replace XML entities
962
    *
963
    * With the optional second parameter, you may select, which
964
    * entities should be replaced.
965
    *
966
    * <code>
967
    * require_once 'XML/Util.php';
968
    *
969
    * // replace XML entites:
970
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities("This string contains < & >.");
971
    * </code>
972
    *
973
    * @access   public
974
    * @static
975
    * @param    string  string where XML special chars should be replaced
976
    * @param    integer setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
977
    * @return   string  string with replaced chars
978
    */
979
    function replaceEntities($string, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
980
    {
981
        switch ($replaceEntities) {
982
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_UTF8_XML:
983
                return strtr(utf8_encode($string),array(
984
                                          '&'  => '&amp;',
985
                                          '>'  => '&gt;',
986
                                          '<'  => '&lt;',
987
                                          '"'  => '&quot;',
988
                                          '\'' => '&apos;' ));
989
                break;
990
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML:
991
                return strtr($string,array(
992
                                          '&'  => '&amp;',
993
                                          '>'  => '&gt;',
994
                                          '<'  => '&lt;',
995
                                          '"'  => '&quot;',
996
                                          '\'' => '&apos;' ));
997
                break;
998
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED:
999
                return strtr($string,array(
1000
                                          '&'  => '&amp;',
1001
                                          '<'  => '&lt;',
1002
                                          '"'  => '&quot;' ));
1003
                break;
1004
            case PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML:
1005
                return htmlspecialchars($string);
1006
                break;
1007
        }
1008
        return $string;
1009
    }
1010
 
1011
   /**
1012
    * build an xml declaration
1013
    *
1014
    * <code>
1015
    * require_once 'XML/Util.php';
1016
    *
1017
    * // get an XML declaration:
1018
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getXMLDeclaration("1.0", "UTF-8", true);
1019
    * </code>
1020
    *
1021
    * @access   public
1022
    * @static
1023
    * @param    string  $version     xml version
1024
    * @param    string  $encoding    character encoding
1025
    * @param    boolean $standAlone  document is standalone (or not)
1026
    * @return   string  $decl xml declaration
1027
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the XML declaration
1028
    */
1029
    function getXMLDeclaration($version = "1.0", $encoding = null, $standalone = null)
1030
    {
1031
        $attributes = array(
1032
                            "version" => $version,
1033
                           );
1034
        // add encoding
1035
        if ($encoding !== null) {
1036
            $attributes["encoding"] = $encoding;
1037
        }
1038
        // add standalone, if specified
1039
        if ($standalone !== null) {
1040
            $attributes["standalone"] = $standalone ? "yes" : "no";
1041
        }
1042
 
1043
        return sprintf("<?xml%s?>", PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, false));
1044
    }
1045
 
1046
   /**
1047
    * build a document type declaration
1048
    *
1049
    * <code>
1050
    * require_once 'XML/Util.php';
1051
    *
1052
    * // get a doctype declaration:
1053
    * $xmlDecl = PEAR_PackageFile_Generator_v2_XML_Util::getDocTypeDeclaration("rootTag","myDocType.dtd");
1054
    * </code>
1055
    *
1056
    * @access   public
1057
    * @static
1058
    * @param    string  $root         name of the root tag
1059
    * @param    string  $uri          uri of the doctype definition (or array with uri and public id)
1060
    * @param    string  $internalDtd  internal dtd entries
1061
    * @return   string  $decl         doctype declaration
1062
    * @since    0.2
1063
    */
1064
    function getDocTypeDeclaration($root, $uri = null, $internalDtd = null)
1065
    {
1066
        if (is_array($uri)) {
1067
            $ref = sprintf( ' PUBLIC "%s" "%s"', $uri["id"], $uri["uri"] );
1068
        } elseif (!empty($uri)) {
1069
            $ref = sprintf( ' SYSTEM "%s"', $uri );
1070
        } else {
1071
            $ref = "";
1072
        }
1073
 
1074
        if (empty($internalDtd)) {
1075
            return sprintf("<!DOCTYPE %s%s>", $root, $ref);
1076
        } else {
1077
            return sprintf("<!DOCTYPE %s%s [\n%s\n]>", $root, $ref, $internalDtd);
1078
        }
1079
    }
1080
 
1081
   /**
1082
    * create string representation of an attribute list
1083
    *
1084
    * <code>
1085
    * require_once 'XML/Util.php';
1086
    *
1087
    * // build an attribute string
1088
    * $att = array(
1089
    *              "foo"   =>  "bar",
1090
    *              "argh"  =>  "tomato"
1091
    *            );
1092
    *
1093
    * $attList = PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($att);
1094
    * </code>
1095
    *
1096
    * @access   public
1097
    * @static
1098
    * @param    array         $attributes        attribute array
1099
    * @param    boolean|array $sort              sort attribute list alphabetically, may also be an assoc array containing the keys 'sort', 'multiline', 'indent', 'linebreak' and 'entities'
1100
    * @param    boolean       $multiline         use linebreaks, if more than one attribute is given
1101
    * @param    string        $indent            string used for indentation of multiline attributes
1102
    * @param    string        $linebreak         string used for linebreaks of multiline attributes
1103
    * @param    integer       $entities          setting for entities in attribute values (one of PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML_REQUIRED, PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_HTML)
1104
    * @return   string                           string representation of the attributes
1105
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities() to replace XML entities in attribute values
1106
    * @todo     allow sort also to be an options array
1107
    */
1108
    function attributesToString($attributes, $sort = true, $multiline = false, $indent = '    ', $linebreak = "\n", $entities = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
1109
    {
1110
        /**
1111
         * second parameter may be an array
1112
         */
1113
        if (is_array($sort)) {
1114
            if (isset($sort['multiline'])) {
1115
                $multiline = $sort['multiline'];
1116
            }
1117
            if (isset($sort['indent'])) {
1118
                $indent = $sort['indent'];
1119
            }
1120
            if (isset($sort['linebreak'])) {
1121
                $multiline = $sort['linebreak'];
1122
            }
1123
            if (isset($sort['entities'])) {
1124
                $entities = $sort['entities'];
1125
            }
1126
            if (isset($sort['sort'])) {
1127
                $sort = $sort['sort'];
1128
            } else {
1129
                $sort = true;
1130
            }
1131
        }
1132
        $string = '';
1133
        if (is_array($attributes) && !empty($attributes)) {
1134
            if ($sort) {
1135
                ksort($attributes);
1136
            }
1137
            if( !$multiline || count($attributes) == 1) {
1138
                foreach ($attributes as $key => $value) {
1139
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
1140
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
1141
                    }
1142
                    $string .= ' '.$key.'="'.$value.'"';
1143
                }
1144
            } else {
1145
                $first = true;
1146
                foreach ($attributes as $key => $value) {
1147
                    if ($entities != PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_NONE) {
1148
                        $value = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($value, $entities);
1149
                    }
1150
                    if ($first) {
1151
                        $string .= " ".$key.'="'.$value.'"';
1152
                        $first = false;
1153
                    } else {
1154
                        $string .= $linebreak.$indent.$key.'="'.$value.'"';
1155
                    }
1156
                }
1157
            }
1158
        }
1159
        return $string;
1160
    }
1161
 
1162
   /**
1163
    * create a tag
1164
    *
1165
    * This method will call PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray(), which
1166
    * is more flexible.
1167
    *
1168
    * <code>
1169
    * require_once 'XML/Util.php';
1170
    *
1171
    * // create an XML tag:
1172
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createTag("myNs:myTag", array("foo" => "bar"), "This is inside the tag", "http://www.w3c.org/myNs#");
1173
    * </code>
1174
    *
1175
    * @access   public
1176
    * @static
1177
    * @param    string  $qname             qualified tagname (including namespace)
1178
    * @param    array   $attributes        array containg attributes
1179
    * @param    mixed   $content
1180
    * @param    string  $namespaceUri      URI of the namespace
1181
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
1182
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
1183
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
1184
    * @param    string  $linebreak         string used for linebreaks
1185
    * @param    string  $encoding          encoding that should be used to translate content
1186
    * @return   string  $string            XML tag
1187
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray()
1188
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray() to create the tag
1189
    */
1190
    function createTag($qname, $attributes = array(), $content = null, $namespaceUri = null, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
1191
    {
1192
        $tag = array(
1193
                     "qname"      => $qname,
1194
                     "attributes" => $attributes
1195
                    );
1196
 
1197
        // add tag content
1198
        if ($content !== null) {
1199
            $tag["content"] = $content;
1200
        }
1201
 
1202
        // add namespace Uri
1203
        if ($namespaceUri !== null) {
1204
            $tag["namespaceUri"] = $namespaceUri;
1205
        }
1206
 
1207
        return PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $linebreak, $encoding);
1208
    }
1209
 
1210
   /**
1211
    * create a tag from an array
1212
    * this method awaits an array in the following format
1213
    * <pre>
1214
    * array(
1215
    *  "qname"        => $qname         // qualified name of the tag
1216
    *  "namespace"    => $namespace     // namespace prefix (optional, if qname is specified or no namespace)
1217
    *  "localpart"    => $localpart,    // local part of the tagname (optional, if qname is specified)
1218
    *  "attributes"   => array(),       // array containing all attributes (optional)
1219
    *  "content"      => $content,      // tag content (optional)
1220
    *  "namespaceUri" => $namespaceUri  // namespaceUri for the given namespace (optional)
1221
    *   )
1222
    * </pre>
1223
    *
1224
    * <code>
1225
    * require_once 'XML/Util.php';
1226
    *
1227
    * $tag = array(
1228
    *           "qname"        => "foo:bar",
1229
    *           "namespaceUri" => "http://foo.com",
1230
    *           "attributes"   => array( "key" => "value", "argh" => "fruit&vegetable" ),
1231
    *           "content"      => "I'm inside the tag",
1232
    *            );
1233
    * // creating a tag with qualified name and namespaceUri
1234
    * $string = PEAR_PackageFile_Generator_v2_XML_Util::createTagFromArray($tag);
1235
    * </code>
1236
    *
1237
    * @access   public
1238
    * @static
1239
    * @param    array   $tag               tag definition
1240
    * @param    integer $replaceEntities   whether to replace XML special chars in content, embedd it in a CData section or none of both
1241
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
1242
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
1243
    * @param    string  $linebreak         string used for linebreaks
1244
    * @return   string  $string            XML tag
1245
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createTag()
1246
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::attributesToString() to serialize the attributes of the tag
1247
    * @uses     PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName() to get local part and namespace of a qualified name
1248
    */
1249
    function createTagFromArray($tag, $replaceEntities = PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES, $multiline = false, $indent = "_auto", $linebreak = "\n", $encoding = PEAR_PackageFile_Generator_v2_XML_Util_ENTITIES_XML)
1250
    {
1251
        if (isset($tag["content"]) && !is_scalar($tag["content"])) {
1252
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "Supplied non-scalar value as tag content", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NON_SCALAR_CONTENT );
1253
        }
1254
 
1255
        if (!isset($tag['qname']) && !isset($tag['localPart'])) {
1256
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( 'You must either supply a qualified name (qname) or local tag name (localPart).', PEAR_PackageFile_Generator_v2_XML_Util_ERROR_NO_TAG_NAME );
1257
        }
1258
 
1259
        // if no attributes hav been set, use empty attributes
1260
        if (!isset($tag["attributes"]) || !is_array($tag["attributes"])) {
1261
            $tag["attributes"] = array();
1262
        }
1263
 
1264
        // qualified name is not given
1265
        if (!isset($tag["qname"])) {
1266
            // check for namespace
1267
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
1268
                $tag["qname"] = $tag["namespace"].":".$tag["localPart"];
1269
            } else {
1270
                $tag["qname"] = $tag["localPart"];
1271
            }
1272
        // namespace URI is set, but no namespace
1273
        } elseif (isset($tag["namespaceUri"]) && !isset($tag["namespace"])) {
1274
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($tag["qname"]);
1275
            $tag["localPart"] = $parts["localPart"];
1276
            if (isset($parts["namespace"])) {
1277
                $tag["namespace"] = $parts["namespace"];
1278
            }
1279
        }
1280
 
1281
        if (isset($tag["namespaceUri"]) && !empty($tag["namespaceUri"])) {
1282
            // is a namespace given
1283
            if (isset($tag["namespace"]) && !empty($tag["namespace"])) {
1284
                $tag["attributes"]["xmlns:".$tag["namespace"]] = $tag["namespaceUri"];
1285
            } else {
1286
                // define this Uri as the default namespace
1287
                $tag["attributes"]["xmlns"] = $tag["namespaceUri"];
1288
            }
1289
        }
1290
 
1291
        // check for multiline attributes
1292
        if ($multiline === true) {
1293
            if ($indent === "_auto") {
1294
                $indent = str_repeat(" ", (strlen($tag["qname"])+2));
1295
            }
1296
        }
1297
 
1298
        // create attribute list
1299
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($tag["attributes"], true, $multiline, $indent, $linebreak );
1300
        if (!isset($tag["content"]) || (string)$tag["content"] == '') {
1301
            $tag    =   sprintf("<%s%s />", $tag["qname"], $attList);
1302
        } else {
1303
            if ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_REPLACE_ENTITIES) {
1304
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::replaceEntities($tag["content"], $encoding);
1305
            } elseif ($replaceEntities == PEAR_PackageFile_Generator_v2_XML_Util_CDATA_SECTION) {
1306
                $tag["content"] = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection($tag["content"]);
1307
            }
1308
            $tag    =   sprintf("<%s%s>%s</%s>", $tag["qname"], $attList, $tag["content"], $tag["qname"] );
1309
        }
1310
        return  $tag;
1311
    }
1312
 
1313
   /**
1314
    * create a start element
1315
    *
1316
    * <code>
1317
    * require_once 'XML/Util.php';
1318
    *
1319
    * // create an XML start element:
1320
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createStartElement("myNs:myTag", array("foo" => "bar") ,"http://www.w3c.org/myNs#");
1321
    * </code>
1322
    *
1323
    * @access   public
1324
    * @static
1325
    * @param    string  $qname             qualified tagname (including namespace)
1326
    * @param    array   $attributes        array containg attributes
1327
    * @param    string  $namespaceUri      URI of the namespace
1328
    * @param    boolean $multiline         whether to create a multiline tag where each attribute gets written to a single line
1329
    * @param    string  $indent            string used to indent attributes (_auto indents attributes so they start at the same column)
1330
    * @param    string  $linebreak         string used for linebreaks
1331
    * @return   string  $string            XML start element
1332
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createEndElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
1333
    */
1334
    function createStartElement($qname, $attributes = array(), $namespaceUri = null, $multiline = false, $indent = '_auto', $linebreak = "\n")
1335
    {
1336
        // if no attributes hav been set, use empty attributes
1337
        if (!isset($attributes) || !is_array($attributes)) {
1338
            $attributes = array();
1339
        }
1340
 
1341
        if ($namespaceUri != null) {
1342
            $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName($qname);
1343
        }
1344
 
1345
        // check for multiline attributes
1346
        if ($multiline === true) {
1347
            if ($indent === "_auto") {
1348
                $indent = str_repeat(" ", (strlen($qname)+2));
1349
            }
1350
        }
1351
 
1352
        if ($namespaceUri != null) {
1353
            // is a namespace given
1354
            if (isset($parts["namespace"]) && !empty($parts["namespace"])) {
1355
                $attributes["xmlns:".$parts["namespace"]] = $namespaceUri;
1356
            } else {
1357
                // define this Uri as the default namespace
1358
                $attributes["xmlns"] = $namespaceUri;
1359
            }
1360
        }
1361
 
1362
        // create attribute list
1363
        $attList    =   PEAR_PackageFile_Generator_v2_XML_Util::attributesToString($attributes, true, $multiline, $indent, $linebreak);
1364
        $element    =   sprintf("<%s%s>", $qname, $attList);
1365
        return  $element;
1366
    }
1367
 
1368
   /**
1369
    * create an end element
1370
    *
1371
    * <code>
1372
    * require_once 'XML/Util.php';
1373
    *
1374
    * // create an XML start element:
1375
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createEndElement("myNs:myTag");
1376
    * </code>
1377
    *
1378
    * @access   public
1379
    * @static
1380
    * @param    string  $qname             qualified tagname (including namespace)
1381
    * @return   string  $string            XML end element
1382
    * @see      PEAR_PackageFile_Generator_v2_XML_Util::createStartElement(), PEAR_PackageFile_Generator_v2_XML_Util::createTag()
1383
    */
1384
    function createEndElement($qname)
1385
    {
1386
        $element    =   sprintf("</%s>", $qname);
1387
        return  $element;
1388
    }
1389
 
1390
   /**
1391
    * create an XML comment
1392
    *
1393
    * <code>
1394
    * require_once 'XML/Util.php';
1395
    *
1396
    * // create an XML start element:
1397
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createComment("I am a comment");
1398
    * </code>
1399
    *
1400
    * @access   public
1401
    * @static
1402
    * @param    string  $content           content of the comment
1403
    * @return   string  $comment           XML comment
1404
    */
1405
    function createComment($content)
1406
    {
1407
        $comment    =   sprintf("<!-- %s -->", $content);
1408
        return  $comment;
1409
    }
1410
 
1411
   /**
1412
    * create a CData section
1413
    *
1414
    * <code>
1415
    * require_once 'XML/Util.php';
1416
    *
1417
    * // create a CData section
1418
    * $tag = PEAR_PackageFile_Generator_v2_XML_Util::createCDataSection("I am content.");
1419
    * </code>
1420
    *
1421
    * @access   public
1422
    * @static
1423
    * @param    string  $data              data of the CData section
1424
    * @return   string  $string            CData section with content
1425
    */
1426
    function createCDataSection($data)
1427
    {
1428
        return  sprintf("<![CDATA[%s]]>", $data);
1429
    }
1430
 
1431
   /**
1432
    * split qualified name and return namespace and local part
1433
    *
1434
    * <code>
1435
    * require_once 'XML/Util.php';
1436
    *
1437
    * // split qualified tag
1438
    * $parts = PEAR_PackageFile_Generator_v2_XML_Util::splitQualifiedName("xslt:stylesheet");
1439
    * </code>
1440
    * the returned array will contain two elements:
1441
    * <pre>
1442
    * array(
1443
    *       "namespace" => "xslt",
1444
    *       "localPart" => "stylesheet"
1445
    *      );
1446
    * </pre>
1447
    *
1448
    * @access public
1449
    * @static
1450
    * @param  string    $qname      qualified tag name
1451
    * @param  string    $defaultNs  default namespace (optional)
1452
    * @return array     $parts      array containing namespace and local part
1453
    */
1454
    function splitQualifiedName($qname, $defaultNs = null)
1455
    {
1456
        if (strstr($qname, ':')) {
1457
            $tmp = explode(":", $qname);
1458
            return array(
1459
                          "namespace" => $tmp[0],
1460
                          "localPart" => $tmp[1]
1461
                        );
1462
        }
1463
        return array(
1464
                      "namespace" => $defaultNs,
1465
                      "localPart" => $qname
1466
                    );
1467
    }
1468
 
1469
   /**
1470
    * check, whether string is valid XML name
1471
    *
1472
    * <p>XML names are used for tagname, attribute names and various
1473
    * other, lesser known entities.</p>
1474
    * <p>An XML name may only consist of alphanumeric characters,
1475
    * dashes, undescores and periods, and has to start with a letter
1476
    * or an underscore.
1477
    * </p>
1478
    *
1479
    * <code>
1480
    * require_once 'XML/Util.php';
1481
    *
1482
    * // verify tag name
1483
    * $result = PEAR_PackageFile_Generator_v2_XML_Util::isValidName("invalidTag?");
1484
    * if (PEAR_PackageFile_Generator_v2_XML_Util::isError($result)) {
1485
    *    print "Invalid XML name: " . $result->getMessage();
1486
    * }
1487
    * </code>
1488
    *
1489
    * @access  public
1490
    * @static
1491
    * @param   string  $string string that should be checked
1492
    * @return  mixed   $valid  true, if string is a valid XML name, PEAR error otherwise
1493
    * @todo    support for other charsets
1494
    */
1495
    function isValidName($string)
1496
    {
1497
        // check for invalid chars
1498
        if (!preg_match("/^[[:alnum:]_\-.]$/", $string{0})) {
1499
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only start with letter or underscore", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_START );
1500
        }
1501
 
1502
        // check for invalid chars
1503
        if (!preg_match("/^([a-zA-Z_]([a-zA-Z0-9_\-\.]*)?:)?[a-zA-Z_]([a-zA-Z0-9_\-\.]+)?$/", $string)) {
1504
            return PEAR_PackageFile_Generator_v2_XML_Util::raiseError( "XML names may only contain alphanumeric chars, period, hyphen, colon and underscores", PEAR_PackageFile_Generator_v2_XML_Util_ERROR_INVALID_CHARS );
1505
         }
1506
        // XML name is valid
1507
        return true;
1508
    }
1509
 
1510
   /**
1511
    * replacement for PEAR_PackageFile_Generator_v2_XML_Util::raiseError
1512
    *
1513
    * Avoids the necessity to always require
1514
    * PEAR.php
1515
    *
1516
    * @access   public
1517
    * @param    string      error message
1518
    * @param    integer     error code
1519
    * @return   object PEAR_Error
1520
    */
1521
    function raiseError($msg, $code)
1522
    {
1523
        require_once 'PEAR.php';
1524
        return PEAR::raiseError($msg, $code);
1525
    }
1526
}
1527
?>