File indexing completed on 2024-12-22 05:36:40

0001 <?php
0002 
0003 /**
0004  * Zend Framework
0005  *
0006  * LICENSE
0007  *
0008  * This source file is subject to the new BSD license that is bundled
0009  * with this package in the file LICENSE.txt.
0010  * It is also available through the world-wide-web at this URL:
0011  * http://framework.zend.com/license/new-bsd
0012  * If you did not receive a copy of the license and are unable to
0013  * obtain it through the world-wide-web, please send an email
0014  * to license@zend.com so we can send you a copy immediately.
0015  *
0016  * @category   Zend
0017  * @package    Zend_Feed
0018  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0019  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0020  * @version    $Id$
0021  */
0022 
0023 
0024 /**
0025  * Wraps a DOMElement allowing for SimpleXML-like access to attributes.
0026  *
0027  * @category   Zend
0028  * @package    Zend_Feed
0029  * @copyright  Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
0030  * @license    http://framework.zend.com/license/new-bsd     New BSD License
0031  */
0032 class Zend_Feed_Element implements ArrayAccess
0033 {
0034 
0035     /**
0036      * @var DOMElement
0037      */
0038     protected $_element;
0039 
0040     /**
0041      * @var string Character encoding to utilize
0042      */
0043     protected $_encoding = 'UTF-8';
0044 
0045     /**
0046      * @var Zend_Feed_Element
0047      */
0048     protected $_parentElement;
0049 
0050     /**
0051      * @var boolean
0052      */
0053     protected $_appended = true;
0054 
0055 
0056     /**
0057      * Zend_Feed_Element constructor.
0058      *
0059      * @param  DOMElement $element The DOM element we're encapsulating.
0060      * @return void
0061      */
0062     public function __construct($element = null)
0063     {
0064         $this->_element = $element;
0065     }
0066 
0067 
0068     /**
0069      * Get a DOM representation of the element
0070      *
0071      * Returns the underlying DOM object, which can then be
0072      * manipulated with full DOM methods.
0073      *
0074      * @return DOMDocument
0075      */
0076     public function getDOM()
0077     {
0078         return $this->_element;
0079     }
0080 
0081 
0082     /**
0083      * Update the object from a DOM element
0084      *
0085      * Take a DOMElement object, which may be originally from a call
0086      * to getDOM() or may be custom created, and use it as the
0087      * DOM tree for this Zend_Feed_Element.
0088      *
0089      * @param  DOMElement $element
0090      * @return void
0091      */
0092     public function setDOM(DOMElement $element)
0093     {
0094         $this->_element = $this->_element->ownerDocument->importNode($element, true);
0095     }
0096 
0097     /**
0098      * Set the parent element of this object to another
0099      * Zend_Feed_Element.
0100      *
0101      * @param  Zend_Feed_Element $element
0102      * @return void
0103      */
0104     public function setParent(Zend_Feed_Element $element)
0105     {
0106         $this->_parentElement = $element;
0107         $this->_appended = false;
0108     }
0109 
0110 
0111     /**
0112      * Appends this element to its parent if necessary.
0113      *
0114      * @return void
0115      */
0116     protected function ensureAppended()
0117     {
0118         if (!$this->_appended) {
0119             $this->_parentElement->getDOM()->appendChild($this->_element);
0120             $this->_appended = true;
0121             $this->_parentElement->ensureAppended();
0122         }
0123     }
0124 
0125 
0126     /**
0127      * Get an XML string representation of this element
0128      *
0129      * Returns a string of this element's XML, including the XML
0130      * prologue.
0131      *
0132      * @return string
0133      */
0134     public function saveXml()
0135     {
0136         // Return a complete document including XML prologue.
0137         $doc = new DOMDocument($this->_element->ownerDocument->version,
0138                                $this->_element->ownerDocument->actualEncoding);
0139         $doc->appendChild($doc->importNode($this->_element, true));
0140         return $doc->saveXML();
0141     }
0142 
0143 
0144     /**
0145      * Get the XML for only this element
0146      *
0147      * Returns a string of this element's XML without prologue.
0148      *
0149      * @return string
0150      */
0151     public function saveXmlFragment()
0152     {
0153         return $this->_element->ownerDocument->saveXML($this->_element);
0154     }
0155 
0156     /**
0157      * Get encoding
0158      *
0159      * @return string
0160      */
0161     public function getEncoding()
0162     {
0163         return $this->_encoding;
0164     }
0165 
0166     /**
0167      * Set encoding
0168      *
0169      * @param  string $value Encoding to use
0170      * @return Zend_Feed_Element
0171      */
0172     public function setEncoding($value)
0173     {
0174         $this->_encoding = (string) $value;
0175         return $this;
0176     }
0177 
0178     /**
0179      * Map variable access onto the underlying entry representation.
0180      *
0181      * Get-style access returns a Zend_Feed_Element representing the
0182      * child element accessed. To get string values, use method syntax
0183      * with the __call() overriding.
0184      *
0185      * @param  string $var The property to access.
0186      * @return mixed
0187      */
0188     public function __get($var)
0189     {
0190         $nodes = $this->_children($var);
0191         $length = count($nodes);
0192 
0193         if ($length == 1) {
0194             return new Zend_Feed_Element($nodes[0]);
0195         } elseif ($length > 1) {
0196             return array_map(create_function('$e', 'return new Zend_Feed_Element($e);'), $nodes);
0197         } else {
0198             // When creating anonymous nodes for __set chaining, don't
0199             // call appendChild() on them. Instead we pass the current
0200             // element to them as an extra reference; the child is
0201             // then responsible for appending itself when it is
0202             // actually set. This way "if ($foo->bar)" doesn't create
0203             // a phantom "bar" element in our tree.
0204             if (strpos($var, ':') !== false) {
0205                 list($ns, $elt) = explode(':', $var, 2);
0206                 $node = $this->_element->ownerDocument->createElementNS(Zend_Feed::lookupNamespace($ns), $elt);
0207             } else {
0208                 $node = $this->_element->ownerDocument->createElement($var);
0209             }
0210             $node = new self($node);
0211             $node->setParent($this);
0212             return $node;
0213         }
0214     }
0215 
0216 
0217     /**
0218      * Map variable sets onto the underlying entry representation.
0219      *
0220      * @param  string $var The property to change.
0221      * @param  string $val The property's new value.
0222      * @return void
0223      * @throws Zend_Feed_Exception
0224      */
0225     public function __set($var, $val)
0226     {
0227         $this->ensureAppended();
0228 
0229         $nodes = $this->_children($var);
0230         if (!$nodes) {
0231             if (strpos($var, ':') !== false) {
0232                 list($ns, $elt) = explode(':', $var, 2);
0233                 $node = $this->_element->ownerDocument->createElementNS(Zend_Feed::lookupNamespace($ns),
0234                     $var, htmlspecialchars($val, ENT_NOQUOTES, $this->getEncoding()));
0235                 $this->_element->appendChild($node);
0236             } else {
0237                 $node = $this->_element->ownerDocument->createElement($var,
0238                     htmlspecialchars($val, ENT_NOQUOTES, $this->getEncoding()));
0239                 $this->_element->appendChild($node);
0240             }
0241         } elseif (count($nodes) > 1) {
0242             /**
0243              * @see Zend_Feed_Exception
0244              */
0245             // require_once 'Zend/Feed/Exception.php';
0246             throw new Zend_Feed_Exception('Cannot set the value of multiple tags simultaneously.');
0247         } else {
0248             $nodes[0]->nodeValue = $val;
0249         }
0250     }
0251 
0252 
0253     /**
0254      * Map isset calls onto the underlying entry representation.
0255      *
0256      * @param  string $var
0257      * @return boolean
0258      */
0259     public function __isset($var)
0260     {
0261         // Look for access of the form {ns:var}. We don't use
0262         // _children() here because we can break out of the loop
0263         // immediately once we find something.
0264         if (strpos($var, ':') !== false) {
0265             list($ns, $elt) = explode(':', $var, 2);
0266             foreach ($this->_element->childNodes as $child) {
0267                 if ($child->localName == $elt && $child->prefix == $ns) {
0268                     return true;
0269                 }
0270             }
0271         } else {
0272             foreach ($this->_element->childNodes as $child) {
0273                 if ($child->localName == $var) {
0274                     return true;
0275                 }
0276             }
0277         }
0278     }
0279 
0280 
0281     /**
0282      * Get the value of an element with method syntax.
0283      *
0284      * Map method calls to get the string value of the requested
0285      * element. If there are multiple elements that match, this will
0286      * return an array of those objects.
0287      *
0288      * @param  string $var    The element to get the string value of.
0289      * @param  mixed  $unused This parameter is not used.
0290      * @return mixed The node's value, null, or an array of nodes.
0291      */
0292     public function __call($var, $unused)
0293     {
0294         $nodes = $this->_children($var);
0295 
0296         if (!$nodes) {
0297             return null;
0298         } elseif (count($nodes) > 1) {
0299             return $nodes;
0300         } else {
0301             return $nodes[0]->nodeValue;
0302         }
0303     }
0304 
0305 
0306     /**
0307      * Remove all children matching $var.
0308      *
0309      * @param  string $var
0310      * @return void
0311      */
0312     public function __unset($var)
0313     {
0314         $nodes = $this->_children($var);
0315         foreach ($nodes as $node) {
0316             $parent = $node->parentNode;
0317             $parent->removeChild($node);
0318         }
0319     }
0320 
0321 
0322     /**
0323      * Returns the nodeValue of this element when this object is used
0324      * in a string context.
0325      *
0326      * @return string
0327      */
0328     public function __toString()
0329     {
0330         return $this->_element->nodeValue;
0331     }
0332 
0333 
0334     /**
0335      * Finds children with tagnames matching $var
0336      *
0337      * Similar to SimpleXML's children() method.
0338      *
0339      * @param  string $var Tagname to match, can be either namespace:tagName or just tagName.
0340      * @return array
0341      */
0342     protected function _children($var)
0343     {
0344         $found = array();
0345 
0346         // Look for access of the form {ns:var}.
0347         if (strpos($var, ':') !== false) {
0348             list($ns, $elt) = explode(':', $var, 2);
0349             foreach ($this->_element->childNodes as $child) {
0350                 if ($child->localName == $elt && $child->prefix == $ns) {
0351                     $found[] = $child;
0352                 }
0353             }
0354         } else {
0355             foreach ($this->_element->childNodes as $child) {
0356                 if ($child->localName == $var) {
0357                     $found[] = $child;
0358                 }
0359             }
0360         }
0361 
0362         return $found;
0363     }
0364 
0365 
0366     /**
0367      * Required by the ArrayAccess interface.
0368      *
0369      * @param  string $offset
0370      * @return boolean
0371      */
0372     public function offsetExists($offset)
0373     {
0374         if (strpos($offset, ':') !== false) {
0375             list($ns, $attr) = explode(':', $offset, 2);
0376             return $this->_element->hasAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
0377         } else {
0378             return $this->_element->hasAttribute($offset);
0379         }
0380     }
0381 
0382 
0383     /**
0384      * Required by the ArrayAccess interface.
0385      *
0386      * @param  string $offset
0387      * @return string
0388      */
0389     public function offsetGet($offset)
0390     {
0391         if (strpos($offset, ':') !== false) {
0392             list($ns, $attr) = explode(':', $offset, 2);
0393             return $this->_element->getAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
0394         } else {
0395             return $this->_element->getAttribute($offset);
0396         }
0397     }
0398 
0399 
0400     /**
0401      * Required by the ArrayAccess interface.
0402      *
0403      * @param  string $offset
0404      * @param  string $value
0405      * @return string
0406      */
0407     public function offsetSet($offset, $value)
0408     {
0409         $this->ensureAppended();
0410 
0411         if (strpos($offset, ':') !== false) {
0412             list($ns, $attr) = explode(':', $offset, 2);
0413             // DOMElement::setAttributeNS() requires $qualifiedName to have a prefix
0414             return $this->_element->setAttributeNS(Zend_Feed::lookupNamespace($ns), $offset, $value);
0415         } else {
0416             return $this->_element->setAttribute($offset, $value);
0417         }
0418     }
0419 
0420 
0421     /**
0422      * Required by the ArrayAccess interface.
0423      *
0424      * @param  string $offset
0425      * @return boolean
0426      */
0427     public function offsetUnset($offset)
0428     {
0429         if (strpos($offset, ':') !== false) {
0430             list($ns, $attr) = explode(':', $offset, 2);
0431             return $this->_element->removeAttributeNS(Zend_Feed::lookupNamespace($ns), $attr);
0432         } else {
0433             return $this->_element->removeAttribute($offset);
0434         }
0435     }
0436 
0437 }