Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/**
3
 * package.xml generation class, package.xml version 2.0
4
 *
5
 * PHP versions 4 and 5
6
 *
7
 * @category   pear
8
 * @package    PEAR
9
 * @author     Greg Beaver <cellog@php.net>
10
 * @author     Stephan Schmidt (original XML_Serializer code)
11
 * @copyright  1997-2009 The Authors
12
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
13
 * @version    CVS: $Id: v2.php 313023 2011-07-06 19:17:11Z dufuz $
14
 * @link       http://pear.php.net/package/PEAR
15
 * @since      File available since Release 1.4.0a1
16
 */
17
/**
18
 * file/dir manipulation routines
19
 */
20
require_once 'System.php';
21
require_once 'XML/Util.php';
22
 
23
/**
24
 * This class converts a PEAR_PackageFile_v2 object into any output format.
25
 *
26
 * Supported output formats include array, XML string (using S. Schmidt's
27
 * XML_Serializer, slightly customized)
28
 * @category   pear
29
 * @package    PEAR
30
 * @author     Greg Beaver <cellog@php.net>
31
 * @author     Stephan Schmidt (original XML_Serializer code)
32
 * @copyright  1997-2009 The Authors
33
 * @license    http://opensource.org/licenses/bsd-license.php New BSD License
34
 * @version    Release: 1.9.4
35
 * @link       http://pear.php.net/package/PEAR
36
 * @since      Class available since Release 1.4.0a1
37
 */
38
class PEAR_PackageFile_Generator_v2
39
{
40
   /**
41
    * default options for the serialization
42
    * @access private
43
    * @var array $_defaultOptions
44
    */
45
    var $_defaultOptions = array(
46
        'indent'             => ' ',                    // string used for indentation
47
        'linebreak'          => "\n",                  // string used for newlines
48
        'typeHints'          => false,                 // automatically add type hin attributes
49
        'addDecl'            => true,                 // add an XML declaration
50
        'defaultTagName'     => 'XML_Serializer_Tag',  // tag used for indexed arrays or invalid names
51
        'classAsTagName'     => false,                 // use classname for objects in indexed arrays
52
        'keyAttribute'       => '_originalKey',        // attribute where original key is stored
53
        'typeAttribute'      => '_type',               // attribute for type (only if typeHints => true)
54
        'classAttribute'     => '_class',              // attribute for class of objects (only if typeHints => true)
55
        'scalarAsAttributes' => false,                 // scalar values (strings, ints,..) will be serialized as attribute
56
        'prependAttributes'  => '',                    // prepend string for attributes
57
        'indentAttributes'   => false,                 // indent the attributes, if set to '_auto', it will indent attributes so they all start at the same column
58
        'mode'               => 'simplexml',             // use 'simplexml' to use parent name as tagname if transforming an indexed array
59
        'addDoctype'         => false,                 // add a doctype declaration
60
        'doctype'            => null,                  // supply a string or an array with id and uri ({@see XML_Util::getDoctypeDeclaration()}
61
        'rootName'           => 'package',                  // name of the root tag
62
        'rootAttributes'     => array(
63
            'version' => '2.0',
64
            'xmlns' => 'http://pear.php.net/dtd/package-2.0',
65
            'xmlns:tasks' => 'http://pear.php.net/dtd/tasks-1.0',
66
            'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
67
            'xsi:schemaLocation' => 'http://pear.php.net/dtd/tasks-1.0
68
http://pear.php.net/dtd/tasks-1.0.xsd
69
http://pear.php.net/dtd/package-2.0
70
http://pear.php.net/dtd/package-2.0.xsd',
71
        ),               // attributes of the root tag
72
        'attributesArray'    => 'attribs',                  // all values in this key will be treated as attributes
73
        'contentName'        => '_content',                   // this value will be used directly as content, instead of creating a new tag, may only be used in conjuction with attributesArray
74
        'beautifyFilelist'   => false,
75
        'encoding' => 'UTF-8',
76
    );
77
 
78
   /**
79
    * options for the serialization
80
    * @access private
81
    * @var array $options
82
    */
83
    var $options = array();
84
 
85
   /**
86
    * current tag depth
87
    * @var integer $_tagDepth
88
    */
89
    var $_tagDepth = 0;
90
 
91
   /**
92
    * serilialized representation of the data
93
    * @var string $_serializedData
94
    */
95
    var $_serializedData = null;
96
    /**
97
     * @var PEAR_PackageFile_v2
98
     */
99
    var $_packagefile;
100
    /**
101
     * @param PEAR_PackageFile_v2
102
     */
103
    function PEAR_PackageFile_Generator_v2(&$packagefile)
104
    {
105
        $this->_packagefile = &$packagefile;
106
        if (isset($this->_packagefile->encoding)) {
107
            $this->_defaultOptions['encoding'] = $this->_packagefile->encoding;
108
        }
109
    }
110
 
111
    /**
112
     * @return string
113
     */
114
    function getPackagerVersion()
115
    {
116
        return '1.9.4';
117
    }
118
 
119
    /**
120
     * @param PEAR_Packager
121
     * @param bool generate a .tgz or a .tar
122
     * @param string|null temporary directory to package in
123
     */
124
    function toTgz(&$packager, $compress = true, $where = null)
125
    {
126
        $a = null;
127
        return $this->toTgz2($packager, $a, $compress, $where);
128
    }
129
 
130
    /**
131
     * Package up both a package.xml and package2.xml for the same release
132
     * @param PEAR_Packager
133
     * @param PEAR_PackageFile_v1
134
     * @param bool generate a .tgz or a .tar
135
     * @param string|null temporary directory to package in
136
     */
137
    function toTgz2(&$packager, &$pf1, $compress = true, $where = null)
138
    {
139
        require_once 'Archive/Tar.php';
140
        if (!$this->_packagefile->isEquivalent($pf1)) {
141
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: "' .
142
                basename($pf1->getPackageFile()) .
143
                '" is not equivalent to "' . basename($this->_packagefile->getPackageFile())
144
                . '"');
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
 
156
        $file = $where . DIRECTORY_SEPARATOR . 'package.xml';
157
        if (file_exists($file) && !is_file($file)) {
158
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: unable to save package.xml as' .
159
                ' "' . $file  .'"');
160
        }
161
 
162
        if (!$this->_packagefile->validate(PEAR_VALIDATE_PACKAGING)) {
163
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: invalid package.xml');
164
        }
165
 
166
        $ext = $compress ? '.tgz' : '.tar';
167
        $pkgver = $this->_packagefile->getPackage() . '-' . $this->_packagefile->getVersion();
168
        $dest_package = getcwd() . DIRECTORY_SEPARATOR . $pkgver . $ext;
169
        if (file_exists($dest_package) && !is_file($dest_package)) {
170
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: cannot create tgz file "' .
171
                $dest_package . '"');
172
        }
173
 
174
        $pkgfile = $this->_packagefile->getPackageFile();
175
        if (!$pkgfile) {
176
            return PEAR::raiseError('PEAR_Packagefile_v2::toTgz: package file object must ' .
177
                'be created from a real file');
178
        }
179
 
180
        $pkgdir  = dirname(realpath($pkgfile));
181
        $pkgfile = basename($pkgfile);
182
 
183
        // {{{ Create the package file list
184
        $filelist = array();
185
        $i = 0;
186
        $this->_packagefile->flattenFilelist();
187
        $contents = $this->_packagefile->getContents();
188
        if (isset($contents['bundledpackage'])) { // bundles of packages
189
            $contents = $contents['bundledpackage'];
190
            if (!isset($contents[0])) {
191
                $contents = array($contents);
192
            }
193
 
194
            $packageDir = $where;
195
            foreach ($contents as $i => $package) {
196
                $fname = $package;
197
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
198
                if (!file_exists($file)) {
199
                    return $packager->raiseError("File does not exist: $fname");
200
                }
201
 
202
                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
203
                System::mkdir(array('-p', dirname($tfile)));
204
                copy($file, $tfile);
205
                $filelist[$i++] = $tfile;
206
                $packager->log(2, "Adding package $fname");
207
            }
208
        } else { // normal packages
209
            $contents = $contents['dir']['file'];
210
            if (!isset($contents[0])) {
211
                $contents = array($contents);
212
            }
213
 
214
            $packageDir = $where;
215
            foreach ($contents as $i => $file) {
216
                $fname = $file['attribs']['name'];
217
                $atts = $file['attribs'];
218
                $orig = $file;
219
                $file = $pkgdir . DIRECTORY_SEPARATOR . $fname;
220
                if (!file_exists($file)) {
221
                    return $packager->raiseError("File does not exist: $fname");
222
                }
223
 
224
                $origperms = fileperms($file);
225
                $tfile = $packageDir . DIRECTORY_SEPARATOR . $fname;
226
                unset($orig['attribs']);
227
                if (count($orig)) { // file with tasks
228
                    // run any package-time tasks
229
                    $contents = file_get_contents($file);
230
                    foreach ($orig as $tag => $raw) {
231
                        $tag = str_replace(
232
                            array($this->_packagefile->getTasksNs() . ':', '-'),
233
                            array('', '_'), $tag);
234
                        $task = "PEAR_Task_$tag";
235
                        $task = &new $task($this->_packagefile->_config,
236
                            $this->_packagefile->_logger,
237
                            PEAR_TASK_PACKAGE);
238
                        $task->init($raw, $atts, null);
239
                        $res = $task->startSession($this->_packagefile, $contents, $tfile);
240
                        if (!$res) {
241
                            continue; // skip this task
242
                        }
243
 
244
                        if (PEAR::isError($res)) {
245
                            return $res;
246
                        }
247
 
248
                        $contents = $res; // save changes
249
                        System::mkdir(array('-p', dirname($tfile)));
250
                        $wp = fopen($tfile, "wb");
251
                        fwrite($wp, $contents);
252
                        fclose($wp);
253
                    }
254
                }
255
 
256
                if (!file_exists($tfile)) {
257
                    System::mkdir(array('-p', dirname($tfile)));
258
                    copy($file, $tfile);
259
                }
260
 
261
                chmod($tfile, $origperms);
262
                $filelist[$i++] = $tfile;
263
                $this->_packagefile->setFileAttribute($fname, 'md5sum', md5_file($tfile), $i - 1);
264
                $packager->log(2, "Adding file $fname");
265
            }
266
        }
267
            // }}}
268
 
269
        $name       = $pf1 !== null ? 'package2.xml' : 'package.xml';
270
        $packagexml = $this->toPackageFile($where, PEAR_VALIDATE_PACKAGING, $name);
271
        if ($packagexml) {
272
            $tar =& new Archive_Tar($dest_package, $compress);
273
            $tar->setErrorHandling(PEAR_ERROR_RETURN); // XXX Don't print errors
274
            // ----- Creates with the package.xml file
275
            $ok = $tar->createModify(array($packagexml), '', $where);
276
            if (PEAR::isError($ok)) {
277
                return $packager->raiseError($ok);
278
            } elseif (!$ok) {
279
                return $packager->raiseError('PEAR_Packagefile_v2::toTgz(): adding ' . $name .
280
                    ' failed');
281
            }
282
 
283
            // ----- Add the content of the package
284
            if (!$tar->addModify($filelist, $pkgver, $where)) {
285
                return $packager->raiseError(
286
                    'PEAR_Packagefile_v2::toTgz(): tarball creation failed');
287
            }
288
 
289
            // add the package.xml version 1.0
290
            if ($pf1 !== null) {
291
                $pfgen = &$pf1->getDefaultGenerator();
292
                $packagexml1 = $pfgen->toPackageFile($where, PEAR_VALIDATE_PACKAGING, 'package.xml', true);
293
                if (!$tar->addModify(array($packagexml1), '', $where)) {
294
                    return $packager->raiseError(
295
                        'PEAR_Packagefile_v2::toTgz(): adding package.xml failed');
296
                }
297
            }
298
 
299
            return $dest_package;
300
        }
301
    }
302
 
303
    function toPackageFile($where = null, $state = PEAR_VALIDATE_NORMAL, $name = 'package.xml')
304
    {
305
        if (!$this->_packagefile->validate($state)) {
306
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: invalid package.xml',
307
                null, null, null, $this->_packagefile->getValidationWarnings());
308
        }
309
 
310
        if ($where === null) {
311
            if (!($where = System::mktemp(array('-d')))) {
312
                return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: mktemp failed');
313
            }
314
        } elseif (!@System::mkDir(array('-p', $where))) {
315
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: "' . $where . '" could' .
316
                ' not be created');
317
        }
318
 
319
        $newpkgfile = $where . DIRECTORY_SEPARATOR . $name;
320
        $np = @fopen($newpkgfile, 'wb');
321
        if (!$np) {
322
            return PEAR::raiseError('PEAR_Packagefile_v2::toPackageFile: unable to save ' .
323
               "$name as $newpkgfile");
324
        }
325
        fwrite($np, $this->toXml($state));
326
        fclose($np);
327
        return $newpkgfile;
328
    }
329
 
330
    function &toV2()
331
    {
332
        return $this->_packagefile;
333
    }
334
 
335
    /**
336
     * Return an XML document based on the package info (as returned
337
     * by the PEAR_Common::infoFrom* methods).
338
     *
339
     * @return string XML data
340
     */
341
    function toXml($state = PEAR_VALIDATE_NORMAL, $options = array())
342
    {
343
        $this->_packagefile->setDate(date('Y-m-d'));
344
        $this->_packagefile->setTime(date('H:i:s'));
345
        if (!$this->_packagefile->validate($state)) {
346
            return false;
347
        }
348
 
349
        if (is_array($options)) {
350
            $this->options = array_merge($this->_defaultOptions, $options);
351
        } else {
352
            $this->options = $this->_defaultOptions;
353
        }
354
 
355
        $arr = $this->_packagefile->getArray();
356
        if (isset($arr['filelist'])) {
357
            unset($arr['filelist']);
358
        }
359
 
360
        if (isset($arr['_lastversion'])) {
361
            unset($arr['_lastversion']);
362
        }
363
 
364
        // Fix the notes a little bit
365
        if (isset($arr['notes'])) {
366
            // This trims out the indenting, needs fixing
367
            $arr['notes'] = "\n" . trim($arr['notes']) . "\n";
368
        }
369
 
370
        if (isset($arr['changelog']) && !empty($arr['changelog'])) {
371
            // Fix for inconsistency how the array is filled depending on the changelog release amount
372
            if (!isset($arr['changelog']['release'][0])) {
373
                $release = $arr['changelog']['release'];
374
                unset($arr['changelog']['release']);
375
 
376
                $arr['changelog']['release']    = array();
377
                $arr['changelog']['release'][0] = $release;
378
            }
379
 
380
            foreach (array_keys($arr['changelog']['release']) as $key) {
381
                $c =& $arr['changelog']['release'][$key];
382
                if (isset($c['notes'])) {
383
                    // This trims out the indenting, needs fixing
384
                    $c['notes'] = "\n" . trim($c['notes']) . "\n";
385
                }
386
            }
387
        }
388
 
389
        if ($state ^ PEAR_VALIDATE_PACKAGING && !isset($arr['bundle'])) {
390
            $use = $this->_recursiveXmlFilelist($arr['contents']['dir']['file']);
391
            unset($arr['contents']['dir']['file']);
392
            if (isset($use['dir'])) {
393
                $arr['contents']['dir']['dir'] = $use['dir'];
394
            }
395
            if (isset($use['file'])) {
396
                $arr['contents']['dir']['file'] = $use['file'];
397
            }
398
            $this->options['beautifyFilelist'] = true;
399
        }
400
 
401
        $arr['attribs']['packagerversion'] = '1.9.4';
402
        if ($this->serialize($arr, $options)) {
403
            return $this->_serializedData . "\n";
404
        }
405
 
406
        return false;
407
    }
408
 
409
 
410
    function _recursiveXmlFilelist($list)
411
    {
412
        $dirs = array();
413
        if (isset($list['attribs'])) {
414
            $file = $list['attribs']['name'];
415
            unset($list['attribs']['name']);
416
            $attributes = $list['attribs'];
417
            $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes);
418
        } else {
419
            foreach ($list as $a) {
420
                $file = $a['attribs']['name'];
421
                $attributes = $a['attribs'];
422
                unset($a['attribs']);
423
                $this->_addDir($dirs, explode('/', dirname($file)), $file, $attributes, $a);
424
            }
425
        }
426
        $this->_formatDir($dirs);
427
        $this->_deFormat($dirs);
428
        return $dirs;
429
    }
430
 
431
    function _addDir(&$dirs, $dir, $file = null, $attributes = null, $tasks = null)
432
    {
433
        if (!$tasks) {
434
            $tasks = array();
435
        }
436
        if ($dir == array() || $dir == array('.')) {
437
            $dirs['file'][basename($file)] = $tasks;
438
            $attributes['name'] = basename($file);
439
            $dirs['file'][basename($file)]['attribs'] = $attributes;
440
            return;
441
        }
442
        $curdir = array_shift($dir);
443
        if (!isset($dirs['dir'][$curdir])) {
444
            $dirs['dir'][$curdir] = array();
445
        }
446
        $this->_addDir($dirs['dir'][$curdir], $dir, $file, $attributes, $tasks);
447
    }
448
 
449
    function _formatDir(&$dirs)
450
    {
451
        if (!count($dirs)) {
452
            return array();
453
        }
454
        $newdirs = array();
455
        if (isset($dirs['dir'])) {
456
            $newdirs['dir'] = $dirs['dir'];
457
        }
458
        if (isset($dirs['file'])) {
459
            $newdirs['file'] = $dirs['file'];
460
        }
461
        $dirs = $newdirs;
462
        if (isset($dirs['dir'])) {
463
            uksort($dirs['dir'], 'strnatcasecmp');
464
            foreach ($dirs['dir'] as $dir => $contents) {
465
                $this->_formatDir($dirs['dir'][$dir]);
466
            }
467
        }
468
        if (isset($dirs['file'])) {
469
            uksort($dirs['file'], 'strnatcasecmp');
470
        };
471
    }
472
 
473
    function _deFormat(&$dirs)
474
    {
475
        if (!count($dirs)) {
476
            return array();
477
        }
478
        $newdirs = array();
479
        if (isset($dirs['dir'])) {
480
            foreach ($dirs['dir'] as $dir => $contents) {
481
                $newdir = array();
482
                $newdir['attribs']['name'] = $dir;
483
                $this->_deFormat($contents);
484
                foreach ($contents as $tag => $val) {
485
                    $newdir[$tag] = $val;
486
                }
487
                $newdirs['dir'][] = $newdir;
488
            }
489
            if (count($newdirs['dir']) == 1) {
490
                $newdirs['dir'] = $newdirs['dir'][0];
491
            }
492
        }
493
        if (isset($dirs['file'])) {
494
            foreach ($dirs['file'] as $name => $file) {
495
                $newdirs['file'][] = $file;
496
            }
497
            if (count($newdirs['file']) == 1) {
498
                $newdirs['file'] = $newdirs['file'][0];
499
            }
500
        }
501
        $dirs = $newdirs;
502
    }
503
 
504
    /**
505
    * reset all options to default options
506
    *
507
    * @access   public
508
    * @see      setOption(), XML_Unserializer()
509
    */
510
    function resetOptions()
511
    {
512
        $this->options = $this->_defaultOptions;
513
    }
514
 
515
   /**
516
    * set an option
517
    *
518
    * You can use this method if you do not want to set all options in the constructor
519
    *
520
    * @access   public
521
    * @see      resetOption(), XML_Serializer()
522
    */
523
    function setOption($name, $value)
524
    {
525
        $this->options[$name] = $value;
526
    }
527
 
528
   /**
529
    * sets several options at once
530
    *
531
    * You can use this method if you do not want to set all options in the constructor
532
    *
533
    * @access   public
534
    * @see      resetOption(), XML_Unserializer(), setOption()
535
    */
536
    function setOptions($options)
537
    {
538
        $this->options = array_merge($this->options, $options);
539
    }
540
 
541
   /**
542
    * serialize data
543
    *
544
    * @access   public
545
    * @param    mixed    $data data to serialize
546
    * @return   boolean  true on success, pear error on failure
547
    */
548
    function serialize($data, $options = null)
549
    {
550
        // if options have been specified, use them instead
551
        // of the previously defined ones
552
        if (is_array($options)) {
553
            $optionsBak = $this->options;
554
            if (isset($options['overrideOptions']) && $options['overrideOptions'] == true) {
555
                $this->options = array_merge($this->_defaultOptions, $options);
556
            } else {
557
                $this->options = array_merge($this->options, $options);
558
            }
559
        } else {
560
            $optionsBak = null;
561
        }
562
 
563
        //  start depth is zero
564
        $this->_tagDepth = 0;
565
        $this->_serializedData = '';
566
        // serialize an array
567
        if (is_array($data)) {
568
            $tagName = isset($this->options['rootName']) ? $this->options['rootName'] : 'array';
569
            $this->_serializedData .= $this->_serializeArray($data, $tagName, $this->options['rootAttributes']);
570
        }
571
 
572
        // add doctype declaration
573
        if ($this->options['addDoctype'] === true) {
574
            $this->_serializedData = XML_Util::getDoctypeDeclaration($tagName, $this->options['doctype'])
575
                                   . $this->options['linebreak']
576
                                   . $this->_serializedData;
577
        }
578
 
579
        //  build xml declaration
580
        if ($this->options['addDecl']) {
581
            $atts = array();
582
            $encoding = isset($this->options['encoding']) ? $this->options['encoding'] : null;
583
            $this->_serializedData = XML_Util::getXMLDeclaration('1.0', $encoding)
584
                                   . $this->options['linebreak']
585
                                   . $this->_serializedData;
586
        }
587
 
588
 
589
        if ($optionsBak !== null) {
590
            $this->options = $optionsBak;
591
        }
592
 
593
        return  true;
594
    }
595
 
596
   /**
597
    * get the result of the serialization
598
    *
599
    * @access public
600
    * @return string serialized XML
601
    */
602
    function getSerializedData()
603
    {
604
        if ($this->_serializedData === null) {
605
            return  $this->raiseError('No serialized data available. Use XML_Serializer::serialize() first.', XML_SERIALIZER_ERROR_NO_SERIALIZATION);
606
        }
607
        return $this->_serializedData;
608
    }
609
 
610
   /**
611
    * serialize any value
612
    *
613
    * This method checks for the type of the value and calls the appropriate method
614
    *
615
    * @access private
616
    * @param  mixed     $value
617
    * @param  string    $tagName
618
    * @param  array     $attributes
619
    * @return string
620
    */
621
    function _serializeValue($value, $tagName = null, $attributes = array())
622
    {
623
        if (is_array($value)) {
624
            $xml = $this->_serializeArray($value, $tagName, $attributes);
625
        } elseif (is_object($value)) {
626
            $xml = $this->_serializeObject($value, $tagName);
627
        } else {
628
            $tag = array(
629
                          'qname'      => $tagName,
630
                          'attributes' => $attributes,
631
                          'content'    => $value
632
                        );
633
            $xml = $this->_createXMLTag($tag);
634
        }
635
        return $xml;
636
    }
637
 
638
   /**
639
    * serialize an array
640
    *
641
    * @access   private
642
    * @param    array   $array       array to serialize
643
    * @param    string  $tagName     name of the root tag
644
    * @param    array   $attributes  attributes for the root tag
645
    * @return   string  $string      serialized data
646
    * @uses     XML_Util::isValidName() to check, whether key has to be substituted
647
    */
648
    function _serializeArray(&$array, $tagName = null, $attributes = array())
649
    {
650
        $_content = null;
651
 
652
        /**
653
         * check for special attributes
654
         */
655
        if ($this->options['attributesArray'] !== null) {
656
            if (isset($array[$this->options['attributesArray']])) {
657
                $attributes = $array[$this->options['attributesArray']];
658
                unset($array[$this->options['attributesArray']]);
659
            }
660
            /**
661
             * check for special content
662
             */
663
            if ($this->options['contentName'] !== null) {
664
                if (isset($array[$this->options['contentName']])) {
665
                    $_content = $array[$this->options['contentName']];
666
                    unset($array[$this->options['contentName']]);
667
                }
668
            }
669
        }
670
 
671
        /*
672
        * if mode is set to simpleXML, check whether
673
        * the array is associative or indexed
674
        */
675
        if (is_array($array) && $this->options['mode'] == 'simplexml') {
676
            $indexed = true;
677
            if (!count($array)) {
678
                $indexed = false;
679
            }
680
            foreach ($array as $key => $val) {
681
                if (!is_int($key)) {
682
                    $indexed = false;
683
                    break;
684
                }
685
            }
686
 
687
            if ($indexed && $this->options['mode'] == 'simplexml') {
688
                $string = '';
689
                foreach ($array as $key => $val) {
690
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
691
                        if (!isset($this->_curdir)) {
692
                            $this->_curdir = '';
693
                        }
694
                        $savedir = $this->_curdir;
695
                        if (isset($val['attribs'])) {
696
                            if ($val['attribs']['name'] == '/') {
697
                                $this->_curdir = '/';
698
                            } else {
699
                                if ($this->_curdir == '/') {
700
                                    $this->_curdir = '';
701
                                }
702
                                $this->_curdir .= '/' . $val['attribs']['name'];
703
                            }
704
                        }
705
                    }
706
                    $string .= $this->_serializeValue( $val, $tagName, $attributes);
707
                    if ($this->options['beautifyFilelist'] && $tagName == 'dir') {
708
                        $string .= ' <!-- ' . $this->_curdir . ' -->';
709
                        if (empty($savedir)) {
710
                            unset($this->_curdir);
711
                        } else {
712
                            $this->_curdir = $savedir;
713
                        }
714
                    }
715
 
716
                    $string .= $this->options['linebreak'];
717
                    // do indentation
718
                    if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
719
                        $string .= str_repeat($this->options['indent'], $this->_tagDepth);
720
                    }
721
                }
722
                return rtrim($string);
723
            }
724
        }
725
 
726
        if ($this->options['scalarAsAttributes'] === true) {
727
            foreach ($array as $key => $value) {
728
                if (is_scalar($value) && (XML_Util::isValidName($key) === true)) {
729
                    unset($array[$key]);
730
                    $attributes[$this->options['prependAttributes'].$key] = $value;
731
                }
732
            }
733
        }
734
 
735
        // check for empty array => create empty tag
736
        if (empty($array)) {
737
            $tag = array(
738
                            'qname'      => $tagName,
739
                            'content'    => $_content,
740
                            'attributes' => $attributes
741
                        );
742
 
743
        } else {
744
            $this->_tagDepth++;
745
            $tmp = $this->options['linebreak'];
746
            foreach ($array as $key => $value) {
747
                // do indentation
748
                if ($this->options['indent'] !== null && $this->_tagDepth > 0) {
749
                    $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
750
                }
751
 
752
                // copy key
753
                $origKey = $key;
754
                // key cannot be used as tagname => use default tag
755
                $valid = XML_Util::isValidName($key);
756
                if (PEAR::isError($valid)) {
757
                    if ($this->options['classAsTagName'] && is_object($value)) {
758
                        $key = get_class($value);
759
                    } else {
760
                        $key = $this->options['defaultTagName'];
761
                    }
762
                }
763
                $atts = array();
764
                if ($this->options['typeHints'] === true) {
765
                    $atts[$this->options['typeAttribute']] = gettype($value);
766
                    if ($key !== $origKey) {
767
                        $atts[$this->options['keyAttribute']] = (string)$origKey;
768
                    }
769
 
770
                }
771
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
772
                    if (!isset($this->_curdir)) {
773
                        $this->_curdir = '';
774
                    }
775
                    $savedir = $this->_curdir;
776
                    if (isset($value['attribs'])) {
777
                        if ($value['attribs']['name'] == '/') {
778
                            $this->_curdir = '/';
779
                        } else {
780
                            $this->_curdir .= '/' . $value['attribs']['name'];
781
                        }
782
                    }
783
                }
784
 
785
                if (is_string($value) && $value && ($value{strlen($value) - 1} == "\n")) {
786
                    $value .= str_repeat($this->options['indent'], $this->_tagDepth);
787
                }
788
                $tmp .= $this->_createXMLTag(array(
789
                                                    'qname'      => $key,
790
                                                    'attributes' => $atts,
791
                                                    'content'    => $value )
792
                                            );
793
                if ($this->options['beautifyFilelist'] && $key == 'dir') {
794
                    if (isset($value['attribs'])) {
795
                        $tmp .= ' <!-- ' . $this->_curdir . ' -->';
796
                        if (empty($savedir)) {
797
                            unset($this->_curdir);
798
                        } else {
799
                            $this->_curdir = $savedir;
800
                        }
801
                    }
802
                }
803
                $tmp .= $this->options['linebreak'];
804
            }
805
 
806
            $this->_tagDepth--;
807
            if ($this->options['indent']!==null && $this->_tagDepth>0) {
808
                $tmp .= str_repeat($this->options['indent'], $this->_tagDepth);
809
            }
810
 
811
            if (trim($tmp) === '') {
812
                $tmp = null;
813
            }
814
 
815
            $tag = array(
816
                'qname'      => $tagName,
817
                'content'    => $tmp,
818
                'attributes' => $attributes
819
            );
820
        }
821
        if ($this->options['typeHints'] === true) {
822
            if (!isset($tag['attributes'][$this->options['typeAttribute']])) {
823
                $tag['attributes'][$this->options['typeAttribute']] = 'array';
824
            }
825
        }
826
 
827
        $string = $this->_createXMLTag($tag, false);
828
        return $string;
829
    }
830
 
831
   /**
832
    * create a tag from an array
833
    * this method awaits an array in the following format
834
    * array(
835
    *       'qname'        => $tagName,
836
    *       'attributes'   => array(),
837
    *       'content'      => $content,      // optional
838
    *       'namespace'    => $namespace     // optional
839
    *       'namespaceUri' => $namespaceUri  // optional
840
    *   )
841
    *
842
    * @access   private
843
    * @param    array   $tag tag definition
844
    * @param    boolean $replaceEntities whether to replace XML entities in content or not
845
    * @return   string  $string XML tag
846
    */
847
    function _createXMLTag($tag, $replaceEntities = true)
848
    {
849
        if ($this->options['indentAttributes'] !== false) {
850
            $multiline = true;
851
            $indent    = str_repeat($this->options['indent'], $this->_tagDepth);
852
 
853
            if ($this->options['indentAttributes'] == '_auto') {
854
                $indent .= str_repeat(' ', (strlen($tag['qname'])+2));
855
 
856
            } else {
857
                $indent .= $this->options['indentAttributes'];
858
            }
859
        } else {
860
            $indent = $multiline = false;
861
        }
862
 
863
        if (is_array($tag['content'])) {
864
            if (empty($tag['content'])) {
865
                $tag['content'] = '';
866
            }
867
        } elseif(is_scalar($tag['content']) && (string)$tag['content'] == '') {
868
            $tag['content'] = '';
869
        }
870
 
871
        if (is_scalar($tag['content']) || is_null($tag['content'])) {
872
            if ($this->options['encoding'] == 'UTF-8' &&
873
                  version_compare(phpversion(), '5.0.0', 'lt')
874
            ) {
875
                $tag['content'] = utf8_encode($tag['content']);
876
            }
877
 
878
            if ($replaceEntities === true) {
879
                $replaceEntities = XML_UTIL_ENTITIES_XML;
880
            }
881
 
882
            $tag = XML_Util::createTagFromArray($tag, $replaceEntities, $multiline, $indent, $this->options['linebreak']);
883
        } elseif (is_array($tag['content'])) {
884
            $tag = $this->_serializeArray($tag['content'], $tag['qname'], $tag['attributes']);
885
        } elseif (is_object($tag['content'])) {
886
            $tag = $this->_serializeObject($tag['content'], $tag['qname'], $tag['attributes']);
887
        } elseif (is_resource($tag['content'])) {
888
            settype($tag['content'], 'string');
889
            $tag = XML_Util::createTagFromArray($tag, $replaceEntities);
890
        }
891
        return  $tag;
892
    }
893
}