Overview

Packages

  • Pear

Classes

  • Horde_Pear_Exception
  • Horde_Pear_Package_Contents_Ignore_Composite
  • Horde_Pear_Package_Contents_Ignore_Dot
  • Horde_Pear_Package_Contents_Ignore_Git
  • Horde_Pear_Package_Contents_Ignore_Hidden
  • Horde_Pear_Package_Contents_Ignore_Nothing
  • Horde_Pear_Package_Contents_Ignore_Patterns
  • Horde_Pear_Package_Contents_Include_All
  • Horde_Pear_Package_Contents_InstallAs_Horde
  • Horde_Pear_Package_Contents_InstallAs_HordeApplication
  • Horde_Pear_Package_Contents_InstallAs_HordeComponent
  • Horde_Pear_Package_Contents_InstallAs_HordeRole
  • Horde_Pear_Package_Contents_List
  • Horde_Pear_Package_Contents_Role_HordeApplication
  • Horde_Pear_Package_Contents_Role_HordeComponent
  • Horde_Pear_Package_Dependencies
  • Horde_Pear_Package_Task_UpdateContents
  • Horde_Pear_Package_Type_Horde
  • Horde_Pear_Package_Xml
  • Horde_Pear_Package_Xml_Contents
  • Horde_Pear_Package_Xml_Directory
  • Horde_Pear_Package_Xml_Element_Directory
  • Horde_Pear_Package_Xml_Element_File
  • Horde_Pear_Package_Xml_Factory
  • Horde_Pear_Registry
  • Horde_Pear_Remote
  • Horde_Pear_Rest
  • Horde_Pear_Rest_Dependencies
  • Horde_Pear_Rest_Package
  • Horde_Pear_Rest_PackageList
  • Horde_Pear_Rest_Release
  • Horde_Pear_Rest_Releases

Interfaces

  • Horde_Pear_Package_Contents
  • Horde_Pear_Package_Contents_Ignore
  • Horde_Pear_Package_Contents_Include
  • Horde_Pear_Package_Contents_InstallAs
  • Horde_Pear_Package_Contents_Role
  • Horde_Pear_Package_Task
  • Horde_Pear_Package_Type
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Handles package.xml files.
  4:  *
  5:  * PHP version 5
  6:  *
  7:  * @category Horde
  8:  * @package  Pear
  9:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 10:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 11:  * @link     http://pear.horde.org/index.php?package=Pear
 12:  */
 13: 
 14: /**
 15:  * Handles package.xml files.
 16:  *
 17:  * Copyright 2011-2012 Horde LLC (http://www.horde.org/)
 18:  *
 19:  * See the enclosed file COPYING for license information (LGPL). If you
 20:  * did not receive this file, see http://www.horde.org/licenses/lgpl21.
 21:  *
 22:  * @category Horde
 23:  * @package  Pear
 24:  * @author   Gunnar Wrobel <wrobel@pardus.de>
 25:  * @license  http://www.horde.org/licenses/lgpl21 LGPL 2.1
 26:  * @link     http://pear.horde.org/index.php?package=Pear
 27:  */
 28: class Horde_Pear_Package_Xml
 29: {
 30:     /** The package.xml namespace */
 31:     const XMLNAMESPACE = 'http://pear.php.net/dtd/package-2.0';
 32: 
 33:     /**
 34:      * The parsed XML.
 35:      *
 36:      * @var DOMDocument
 37:      */
 38:     private $_xml;
 39: 
 40:     /**
 41:      * The path to the XML file.
 42:      *
 43:      * @var string
 44:      */
 45:     private $_path;
 46: 
 47:     /**
 48:      * The XPath query handler.
 49:      *
 50:      * @var DOMXpath
 51:      */
 52:     private $_xpath;
 53: 
 54:     /**
 55:      * The XPath namespace prefix (if necessary).
 56:      *
 57:      * @var string
 58:      */
 59:     private $_namespace_prefix = '';
 60: 
 61:     /**
 62:      * The factory for required instances.
 63:      *
 64:      * @var Horde_Pear_Package_Xml_Factory
 65:      */
 66:     private $_factory;
 67: 
 68:     /**
 69:      * Constructor.
 70:      *
 71:      * @param resource|string $xml The package.xml as stream or path.
 72:      */
 73:     public function __construct($xml, $factory = null)
 74:     {
 75:         if (is_resource($xml)) {
 76:             rewind($xml);
 77:         } else {
 78:             $this->_path = $xml;
 79:             $xml = fopen($xml, 'r');
 80:         }
 81:         $this->_xml = new DOMDocument('1.0', 'UTF-8');
 82:         $this->_xml->loadXML(stream_get_contents($xml));
 83:         $rootNamespace = $this->_xml->lookupNamespaceUri($this->_xml->namespaceURI);
 84:         $this->_xpath = new DOMXpath($this->_xml);
 85:         if ($rootNamespace !== null) {
 86:             $this->_xpath->registerNamespace('p', $rootNamespace);
 87:             $this->_namespace_prefix = 'p:';
 88:         }
 89:         if ($factory === null) {
 90:             $this->_factory = new Horde_Pear_Package_Xml_Factory();
 91:         } else {
 92:             $this->_factory = $factory;
 93:         }
 94:     }
 95: 
 96:     /**
 97:      * Return the list of contents.
 98:      *
 99:      * @return Horde_Pear_Package_Contents_List The contents.
100:      */
101:     public function getContent($type = 'horde', $path = null)
102:     {
103:         if ($path === null) {
104:             if ($this->_path === null) {
105:                 throw new Horde_Pear_Exception('The path has not been provided!');
106:             }
107:             $path = $this->_path;
108:         }
109:         if (!is_object($type)) {
110:             if ($type == 'horde' || !class_exists($type)) {
111:                 $type = 'Horde_Pear_Package_Type_' . ucfirst($type);
112:             }
113:             $type = new $type(dirname($this->_path));
114:         }
115:         return new Horde_Pear_Package_Contents_List($type);
116:     }
117: 
118:     /**
119:      * Return the package name.
120:      *
121:      * @return string The name of the package.
122:      */
123:     public function getName()
124:     {
125:         return $this->getNodeText('/p:package/p:name');
126:     }
127: 
128:     /**
129:      * Return the package channel.
130:      *
131:      * @return string The channel of the package.
132:      */
133:     public function getChannel()
134:     {
135:         return $this->getNodeText('/p:package/p:channel');
136:     }
137: 
138:     /**
139:      * Return the package summary.
140:      *
141:      * @return string The summary of the package.
142:      */
143:     public function getSummary()
144:     {
145:         return $this->getNodeText('/p:package/p:summary');
146:     }
147: 
148:     /**
149:      * Return the package description.
150:      *
151:      * @return string The description of the package.
152:      */
153:     public function getDescription()
154:     {
155:         return $this->getNodeText('/p:package/p:description');
156:     }
157: 
158:     /**
159:      * Return the package version.
160:      *
161:      * @return string The version of the package.
162:      */
163:     public function getVersion()
164:     {
165:         return $this->getNodeText('/p:package/p:version/p:release');
166:     }
167: 
168:     /**
169:      * Return the release date.
170:      *
171:      * @return string The date for the current release.
172:      */
173:     public function getDate()
174:     {
175:         return $this->getNodeText('/p:package/p:date');
176:     }
177: 
178:     /**
179:      * Return the package notes.
180:      *
181:      * @return string The notes for the current release.
182:      */
183:     public function getNotes()
184:     {
185:         return $this->getNodeText('/p:package/p:notes');
186:     }
187: 
188:     /**
189:      * Return the stability of the release or api.
190:      *
191:      * @param string $key "release" or "api"
192:      *
193:      * @return string The stability.
194:      */
195:     public function getState($key = 'release')
196:     {
197:         if (in_array($key, array('release', 'api'))) {
198:             return $this->getNodeText('/p:package/p:stability/p:' . $key);
199:         }
200:         throw new Horde_Pear_Exception(sprintf('Unsupported state "%s"!', $key));
201:     }
202: 
203:     /**
204:      * Return the package dependencies.
205:      *
206:      * @return array The package dependencies.
207:      */
208:     public function getDependencies()
209:     {
210:         $result = array();
211:         $this->_completeDependencies(
212:             $this->findNode('/p:package/p:dependencies/p:required'),
213:             $result,
214:             'no'
215:         );
216:         $this->_completeDependencies(
217:             $this->findNode('/p:package/p:dependencies/p:optional'),
218:             $result,
219:             'yes'
220:         );
221:         return $result;
222:     }
223: 
224:     /**
225:      * Complete the dependency information.
226:      *
227:      * @param DOMNode $parent   The parent node ("required" or "optional").
228:      * @param array   &$result  The result array.
229:      * @param string  $optional Optional dependency or not?
230:      *
231:      * @return NULL
232:      */
233:     private function _completeDependencies($parent, &$result, $optional)
234:     {
235:         if ($parent === false) {
236:             return;
237:         }
238:         foreach ($parent->childNodes as $dep) {
239:             if ($dep->nodeType == XML_TEXT_NODE) {
240:                 continue;
241:             }
242:             $input = array();
243:             $this->_dependencyInputValue($input, 'min', $dep);
244:             $this->_dependencyInputValue($input, 'max', $dep);
245:             $this->_dependencyInputValue($input, 'name', $dep);
246:             $this->_dependencyInputValue($input, 'channel', $dep);
247:             $this->_dependencyInputValue($input, 'conflicts', $dep);
248:             Horde_Pear_Package_Dependencies::addDependency(
249:                 $input, $dep->nodeName, $optional, $result
250:             );
251:         }
252:     }
253: 
254:     /**
255:      * Generate one element of the input data.
256:      *
257:      * @param array   &$input The input array.
258:      * @param string  $name   Value name.
259:      * @param DOMNode $node   The dependency node.
260:      *
261:      * @return NULL
262:      */
263:     private function _dependencyInputValue(&$input, $name, $node)
264:     {
265:         if (($result = $this->getNodeTextRelativeTo('./p:' . $name, $node)) !== false) {
266:             $input[$name] = $result;
267:         }
268:     }
269: 
270:     /**
271:      * Return the license name.
272:      *
273:      * @return string The name of the license.
274:      */
275:     public function getLicense()
276:     {
277:         return $this->getNodeText('/p:package/p:license');
278:     }
279: 
280:     /**
281:      * Return the URL to the license information.
282:      *
283:      * @return string The license URI.
284:      */
285:     public function getLicenseLocation()
286:     {
287:         $node = $this->findNode('/p:package/p:license');
288:         if (empty($node)) {
289:             return '';
290:         }
291:         return $node->getAttribute('uri');
292:     }
293: 
294:     /**
295:      * Return the package lead developers.
296:      *
297:      * @return string The package lead developers.
298:      */
299:     public function getLeads()
300:     {
301:         $result = array();
302:         foreach ($this->findNodes('/p:package/p:lead') as $lead) {
303:             $result[] = array(
304:                 'name' => $this->getNodeTextRelativeTo('./p:name', $lead),
305:                 'user' => $this->getNodeTextRelativeTo('./p:user', $lead),
306:                 'email' => $this->getNodeTextRelativeTo('./p:email', $lead),
307:                 'active' => $this->getNodeTextRelativeTo('./p:active', $lead),
308:             );
309:         }
310:         return $result;
311:     }
312: 
313:     /**
314:      * Catch undefined method calls and try to run them as task.
315:      *
316:      * @param string $name      The method/task name.
317:      * @param array  $arguments The arguments for the call.
318:      *
319:      * @return mixed
320:      */
321:     public function __call($name, $arguments)
322:     {
323:         if (substr($name, 0, 6) == 'create') {
324:             return $this->_factory->create(substr($name, 6), $arguments);
325:         } else {
326:             array_unshift($arguments, $this);
327:             $this->_factory->createTask($name, $arguments)->run();
328:         }
329:     }
330: 
331:     /**
332:      * Mark the package as being release and set the timestamps to now.
333:      *
334:      * @return NULL
335:      */
336:     public function timestamp()
337:     {
338:         $this->replaceTextNode('/p:package/p:date', date('Y-m-d'));
339:         $this->replaceTextNode('/p:package/p:time', date('H:i:s'));
340: 
341:         $release = $this->_requireCurrentRelease();
342: 
343:         $this->replaceTextNodeRelativeTo('./p:date', $release, date('Y-m-d'));
344:     }
345: 
346:     /**
347:      * Synchronizes the current version information with the release information
348:      * in the changelog.
349:      *
350:      * @return NULL
351:      */
352:     public function syncCurrentVersion()
353:     {
354:         $date = $this->getNodeText('/p:package/p:date');
355:         $notes = $this->getNodeText('/p:package/p:notes');
356:         $api = $this->getNodeText('/p:package/p:version/p:api');
357:         $stability_api = $this->getNodeText('/p:package/p:stability/p:api');
358:         $stability_release = $this->getNodeText(
359:             '/p:package/p:stability/p:release'
360:         );
361: 
362:         $release = $this->_requireCurrentRelease();
363: 
364:         $this->replaceTextNodeRelativeTo('./p:date', $release, $date);
365:         $this->replaceTextNodeRelativeTo(
366:             './p:notes', $release, $notes . '  '
367:         );
368:         $this->replaceTextNodeRelativeTo(
369:             './p:license',
370:             $release,
371:             $this->getLicense(),
372:             array('uri' => $this->getLicenseLocation())
373:         );
374:         $version_node = $this->findNodeRelativeTo(
375:             './p:version', $release
376:         );
377:         $this->replaceTextNodeRelativeTo(
378:             './p:api', $version_node, $api
379:         );
380:         $stability_node = $this->findNodeRelativeTo(
381:             './p:stability', $release
382:         );
383:         $this->replaceTextNodeRelativeTo(
384:             './p:api', $stability_node, $stability_api
385:         );
386:         $this->replaceTextNodeRelativeTo(
387:             './p:release', $stability_node, $stability_release
388:         );
389:     }
390: 
391:     /**
392:      * Add a new note to the package.xml
393:      *
394:      * @param string $note The note text.
395:      *
396:      * @return NULL
397:      */
398:     public function addNote($note)
399:     {
400:         $notes = trim($this->getNodeText('/p:package/p:notes'));
401:         if ($notes != '*') {
402:             $new_notes = "\n* " . $note . "\n" . $notes . "\n ";
403:         } else {
404:             $new_notes = "\n* " . $note . "\n ";
405:         }
406:         $this->replaceTextNode('/p:package/p:notes', $new_notes);
407: 
408:         $release = $this->_fetchCurrentRelease();
409:         if ($release !== null) {
410:             $this->replaceTextNodeRelativeTo(
411:                 './p:notes', $release, $new_notes . '  '
412:             );
413:         }
414:     }
415: 
416:     /**
417:      * Set the version in the package.xml
418:      *
419:      * @param string $rel_version The new release version number.
420:      * @param string $api_version The new api version number.
421:      *
422:      * @return NULL
423:      */
424:     public function setVersion($rel_version = null, $api_version = null)
425:     {
426:         $release = $this->findNodeRelativeTo(
427:             './p:version',
428:             $this->_requireCurrentRelease()
429:         );
430:         $version = $this->findNode('/p:package/p:version');
431:         if ($rel_version !== null) {
432:             $this->replaceTextNodeRelativeTo(
433:                 './p:release', $version, $rel_version
434:             );
435:             $this->replaceTextNodeRelativeTo(
436:                 './p:release', $release, $rel_version
437:             );
438:         }
439:         if ($api_version !== null) {
440:             $this->replaceTextNodeRelativeTo(
441:                 './p:api', $version, $api_version
442:             );
443:             $this->replaceTextNodeRelativeTo(
444:                 './p:api', $release, $api_version
445:             );
446:         }
447:     }
448: 
449:     /**
450:      * Add the next version to the package.xml
451:      *
452:      * @param string $version           The new version number.
453:      * @param string $initial_note      The text for the initial note.
454:      * @param string $stability_api     The API stability for the next release.
455:      * @param string $stability_release The stability for the next release.
456:      *
457:      * @return NULL
458:      */
459:     public function addNextVersion($version, $initial_note,
460:                                    $stability_api = null,
461:                                    $stability_release = null)
462:     {
463:         $notes = "\n* " . $initial_note . "\n ";
464:         $api = $this->getNodeText('/p:package/p:version/p:api');
465:         if ($stability_api === null) {
466:             $stability_api = $this->getNodeText('/p:package/p:stability/p:api');
467:         }
468:         if ($stability_release === null) {
469:             $stability_release = $this->getNodeText(
470:                 '/p:package/p:stability/p:release'
471:             );
472:         }
473:         $version_node = $this->findNode('/p:package/p:version');
474:         $this->replaceTextNodeRelativeTo(
475:             './p:release', $version_node, $version
476:         );
477:         $this->replaceTextNode('/p:package/p:notes', $notes);
478:         $this->replaceTextNode('/p:package/p:date', date('Y-m-d'));
479:         $this->replaceTextNode('/p:package/p:time', date('H:i:s'));
480: 
481:         $changelog = $this->findNode('/p:package/p:changelog');
482:         $this->_insertWhiteSpace($changelog, ' ');
483: 
484:         $release = $this->_xml->createElementNS(self::XMLNAMESPACE, 'release');
485:         $this->_appendVersion($release, $version, $api, "\n   ");
486:         $this->_appendStability($release, $stability_release, $stability_api, "\n   ");
487:         $this->_appendChild($release, 'date', date('Y-m-d'), "\n   ");
488:         $this->_appendLicense(
489:             $release,
490:             $this->getLicense(),
491:             $this->getLicenseLocation(),
492:             "\n   "
493:         );
494:         $this->_appendChild($release, 'notes', $notes . '  ', "\n   ");
495:         $this->_insertWhiteSpace($release, "\n  ");
496:         $changelog->appendChild($release);
497:         $this->_insertWhiteSpace($changelog, "\n ");
498:     }
499: 
500:     /**
501:      * Append version information.
502:      *
503:      * @param DOMNode $parent  The parent DOMNode.
504:      * @param string  $version The version.
505:      * @param string  $api     The api version.
506:      * @param string  $ws      Additional white space that should be inserted.
507:      *
508:      * @return NULL
509:      */
510:     private function _appendVersion($parent, $version, $api, $ws = '')
511:     {
512:         $this->_insertWhiteSpace($parent, $ws);
513:         $node = $this->_xml->createElementNS(self::XMLNAMESPACE, 'version');
514:         $this->_appendChild($node, 'release', $version, "\n    ");
515:         $this->_appendChild($node, 'api', $api, "\n    ");
516:         $parent->appendChild($node);
517:     }
518: 
519:     /**
520:      * Append stability information.
521:      *
522:      * @param DOMNode $parent  The parent DOMNode.
523:      * @param string  $release The release stability.
524:      * @param string  $api     The api stability.
525:      * @param string  $ws      Additional white space that should be inserted.
526:      *
527:      * @return NULL
528:      */
529:     private function _appendStability($parent, $release, $api, $ws = null)
530:     {
531:         $this->_insertWhiteSpace($parent, $ws);
532:         $node = $this->_xml->createElementNS(self::XMLNAMESPACE, 'stability');
533:         $this->_appendChild($node, 'release', $release, "\n    ");
534:         $this->_appendChild($node, 'api', $api, "\n    ");
535:         $parent->appendChild($node);
536:     }
537: 
538:     /**
539:      * Append license information.
540:      *
541:      * @param DOMNode $parent  The parent DOMNode.
542:      * @param string  $license The license name.
543:      * @param string  $uri     The license URI.
544:      * @param string  $ws      Additional white space that should be inserted.
545:      *
546:      * @return NULL
547:      */
548:     private function _appendLicense($parent, $license, $uri, $ws = null)
549:     {
550:         $this->_insertWhiteSpace($parent, $ws);
551:         $new_node = $this->_xml->createElementNS(
552:             self::XMLNAMESPACE, 'license'
553:         );
554:         $text = $this->_xml->createTextNode($license);
555:         $new_node->appendChild($text);
556:         $new_node->setAttribute('uri', $uri);
557:         $parent->appendChild($new_node);
558:     }
559: 
560:     /**
561:      * Fetch the node holding the current release information in the changelog
562:      * and fail if there is no such node.
563:      *
564:      * @return DOMElement|NULL The release node.
565:      *
566:      * @throws Horde_Pear_Exception If the node does not exist.
567:      */
568:     private function _requireCurrentRelease()
569:     {
570:         $release = $this->_fetchCurrentRelease();
571:         if ($release === null) {
572:             throw new Horde_Pear_Exception('No release in the changelog matches the current version!');
573:         }
574:         return $release;
575:     }
576: 
577:     /**
578:      * Fetch the node holding the current release information in the changelog.
579:      *
580:      * @return DOMElement|NULL The release node or empty if no such node was found.
581:      */
582:     private function _fetchCurrentRelease()
583:     {
584:         $version = $this->getNodeText('/p:package/p:version/p:release');
585:         foreach ($this->findNodes('/p:package/p:changelog/p:release') as $release) {
586:             if ($this->getNodeTextRelativeTo('./p:version/p:release', $release) == $version) {
587:                 return $release;
588:             }
589:         }
590:     }
591: 
592:     /**
593:      * Return the complete package.xml as string.
594:      *
595:      * @return string The package.xml content.
596:      */
597:     public function __toString()
598:     {
599:         $result = $this->_xml->saveXML();
600:         $result = preg_replace(
601:             '#<package (.*) (packagerversion="[.0-9]*" version="2.0")#',
602:             '<package \2 \1',
603:             $result
604:         );
605:         $result = preg_replace("#'#", '&apos;', $result);
606:         return preg_replace('#"/>#', '" />', $result);
607:     }
608: 
609:     /**
610:      * Return a single named node matching the given XPath query.
611:      *
612:      * @param string $query The query.
613:      *
614:      * @return DOMNode|false The named DOMNode or empty if no node was found.
615:      */
616:     public function findNode($query)
617:     {
618:         return $this->_findSingleNode($this->findNodes($query));
619:     }
620: 
621:     /**
622:      * Return a single named node below the given context matching the given
623:      * XPath query.
624:      *
625:      * @param string  $query   The query.
626:      * @param DOMNode $context Search below this node.
627:      *
628:      * @return DOMNode|false The named DOMNode or empty if no node was found.
629:      */
630:     public function findNodeRelativeTo($query, DOMNode $context)
631:     {
632:         return $this->_findSingleNode(
633:             $this->findNodesRelativeTo($query, $context)
634:         );
635:     }
636: 
637:     /**
638:      * Return a single node for the result set.
639:      *
640:      * @param DOMNodeList $result The query result.
641:      *
642:      * @return DOMNode|false The DOMNode or empty if no node was found.
643:      */
644:     private function _findSingleNode($result)
645:     {
646:         if ($result->length) {
647:             return $result->item(0);
648:         }
649:         return false;
650:     }
651: 
652:     /**
653:      * Return all nodes matching the given XPath query.
654:      *
655:      * @param string $query The query.
656:      *
657:      * @return DOMNodeList The list of DOMNodes.
658:      */
659:     public function findNodes($query)
660:     {
661:         $query = preg_replace('#/p:#', '/' . $this->_namespace_prefix, $query);
662:         return $this->_xpath->query($query);
663:     }
664: 
665:     /**
666:      * Return all nodes matching the given XPath query.
667:      *
668:      * @param string $query The query.
669:      *
670:      * @return DOMNodeList The list of DOMNodes.
671:      */
672:     public function findNodesRelativeTo($query, $context)
673:     {
674:         $query = preg_replace('#/p:#', '/' . $this->_namespace_prefix, $query);
675:         return $this->_xpath->query($query, $context);
676:     }
677: 
678:     /**
679:      * Return the content of a single named node matching the given XPath query.
680:      *
681:      * @param string $path The node path.
682:      *
683:      * @return string|false The node content as string or empty if no node was
684:      *                      found.
685:      */
686:     public function getNodeText($path)
687:     {
688:         if ($node = $this->findNode($path)) {
689:             return $node->textContent;
690:         }
691:         return false;
692:     }
693: 
694:     /**
695:      * Return the content of a single named node below the given context
696:      * and matching the given XPath query.
697:      *
698:      * @param string  $path    The node path.
699:      * @param DOMNode $context Search below this node.
700:      *
701:      * @return string|false The node content as string or empty if no node was
702:      *                      found.
703:      */
704:     public function getNodeTextRelativeTo($path, DOMNode $context)
705:     {
706:         if ($node = $this->findNodeRelativeTo($path, $context)) {
707:             return $node->textContent;
708:         }
709:         return false;
710:     }
711: 
712:     /**
713:      * Replace a specific text node
714:      *
715:      * @param string $path  The XPath query pointing to the node.
716:      * @param string $value The new text value.
717:      *
718:      * @return DOMNodeList The list of DOMNodes.
719:      */
720:     public function replaceTextNode($path, $value)
721:     {
722:         if ($node = $this->findNode($path)) {
723:             $this->_xml->documentElement->replaceChild(
724:                 $this->_replacementNode($node, $value), $node
725:             );
726:         }
727:     }
728: 
729:     /**
730:      * Replace a specific text node
731:      *
732:      * @param string  $path      The XPath query pointing to the node.
733:      * @param DOMNode $context   Search below this node.
734:      * @param string  $value     The new text value.
735:      * @param array   $attribues Attributes to add to the node.
736:      *
737:      * @return DOMNodeList The list of DOMNodes.
738:      */
739:     public function replaceTextNodeRelativeTo($path, DOMNode $context, $value,
740:                                               $attributes = array())
741:     {
742:         if ($node = $this->findNodeRelativeTo($path, $context)) {
743:             $new_node = $this->_replacementNode($node, $value);
744:             foreach ($attributes as $name => $value) {
745:                 $new_node->setAttribute($name, $value);
746:             }
747:             $context->replaceChild($new_node, $node);
748:         }
749:     }
750: 
751:     /**
752:      * Generate a replacement node.
753:      *
754:      * @param DOMNode $old_node The old DOMNode to be replaced.
755:      * @param string  $value    The new text value.
756:      *
757:      * @return DOMNode The new DOMNode.
758:      */
759:     private function _replacementNode($old_node, $value)
760:     {
761:         $new_node = $this->_xml->createElementNS(
762:             self::XMLNAMESPACE, $old_node->tagName
763:         );
764:         $text = $this->_xml->createTextNode($value);
765:         $new_node->appendChild($text);
766:         return $new_node;
767:     }
768: 
769:     /**
770:      * Append a new child.
771:      *
772:      * @param DOMNode $parent The parent DOMNode.
773:      * @param string  $name   The tag name of the new node.
774:      * @param string  $value  The text content of the new node.
775:      * @param string  $ws     Additional white space that should be inserted.
776:      *
777:      * @return NULL
778:      */
779:     private function _appendChild($parent, $name, $value, $ws = '')
780:     {
781:         $this->_insertWhiteSpace($parent, $ws);
782:         $new_node = $this->_xml->createElementNS(
783:             self::XMLNAMESPACE, $name
784:         );
785:         $text = $this->_xml->createTextNode($value);
786:         $new_node->appendChild($text);
787:         $parent->appendChild($new_node);
788:     }
789: 
790:     /**
791:      * Insert some white space.
792:      *
793:      * @param DOMNode $parent The parent DOMNode.
794:      * @param string  $ws     Additional white space that should be inserted.
795:      *
796:      * @return DOMNode The inserted white space node.
797:      */
798:     public function _insertWhiteSpace($parent, $ws)
799:     {
800:         $ws_node = $this->_xml->createTextNode($ws);
801:         $parent->appendChild($ws_node);
802:         return $ws_node;
803:     }
804: 
805: 
806:     public function insert($elements, $point)
807:     {
808:         if (!is_array($elements)) {
809:             $elements = array($elements);
810:         }
811:         $node = null;
812:         foreach ($elements as $key => $element) {
813:             if (is_string($element)) {
814:                 $element = $this->createText($element);
815:             } else if (is_array($element)) {
816:                 $node = $element = $this->createNode($key, $element);
817:             }
818:             $point->parentNode->insertBefore($element, $point);
819:         }
820:         return $node;
821:     }
822: 
823:     public function append($elements, $parent)
824:     {
825:         if (!is_array($elements)) {
826:             $elements = array($elements);
827:         }
828:         $node = null;
829:         foreach ($elements as $key => $element) {
830:             if (is_string($element)) {
831:                 $element = $this->createText($element);
832:             } else if (is_array($element)) {
833:                 $node = $element = $this->createNode($key, $element);
834:             }
835:             $parent->appendChild($element);
836:         }
837:         return $node;
838:     }
839: 
840:     public function createText($text)
841:     {
842:         return $this->_xml->createTextNode($text);
843:     }
844: 
845:     public function createComment($comment)
846:     {
847:         return $this->_xml->createComment($comment);
848:     }
849: 
850:     public function createNode($name, $attributes = array())
851:     {
852:         $node = $this->_xml->createElementNS(self::XMLNAMESPACE, $name);
853:         foreach ($attributes as $key => $value) {
854:             $node->setAttribute($key, $value);
855:         }
856:         return $node;
857:     }
858: 
859:     public function removeWhitespace($node)
860:     {
861:         if ($node) {
862:             $ws = trim($node->textContent);
863:             if (empty($ws)) {
864:                 $node->parentNode->removeChild($node);
865:             }
866:         }
867:     }
868: 
869:     public function removeComment($node, $comment)
870:     {
871:         if ($node) {
872:             $current = trim($node->textContent);
873:             if ($current == $comment) {
874:                 $node->parentNode->removeChild($node);
875:             }
876:         }
877:     }
878: }
API documentation generated by ApiGen