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 | } |
---|