Хранилища Subversion ant

Редакция

К новейшей редакции | Содержимое файла | Последнее изменение | Открыть журнал | RSS

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