Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
使用registerNodeClass扩展PHP中的DomeElement_Php_Design Patterns_Dom - Fatal编程技术网

使用registerNodeClass扩展PHP中的DomeElement

使用registerNodeClass扩展PHP中的DomeElement,php,design-patterns,dom,Php,Design Patterns,Dom,registerNodeClass非常适合在PHP中扩展各种基于DOMNode的DOM类,但我需要更深入一层 我创建了一个扩展DOMElement的extDOMElement。这在registerNodeClass中非常有效,但我希望有一些更像这样的功能: registerNodeClass(“doElement->nodeName='XYZ','extDOMXYZElement') 考虑以下XML文档,anives.XML: <animals> <dog name="

registerNodeClass
非常适合在PHP中扩展各种基于DOMNode的DOM类,但我需要更深入一层


我创建了一个扩展
DOMElement
extDOMElement
。这在
registerNodeClass
中非常有效,但我希望有一些更像这样的功能:
registerNodeClass(“doElement->nodeName='XYZ','extDOMXYZElement')

考虑以下XML文档,anives.XML:

<animals>
    <dog name="fido" />
    <dog name="lucky" />
    <cat name="scratchy" />
    <horse name="flicka" />
</animals>
输出: 狗叫了! 幸运的是狗会吠! 猫在叫! 弗利卡,马在呜呜叫

我不想把bark()、meow()和whinny()放到extdomement中,我想把它们分别放到extDOMDogElement、extdomcateelement和extdomhorselement中


我已经看过了这里的装饰和策略模式,但我不确定如何继续。当前的设置工作正常,但我更喜欢在
extdomeElement
中共享属性和方法,每个ElementName都有单独的类,这样我就可以将每个元素特定的方法和属性从主类中分离出来。

编辑您显示的代码,不扩展DOMElement不是更容易吗?只需将常规的家庭元素传递给你的处理策略,例如

class AnimalProcessor
{
    public function processAnimals(DOMDocument $dom) {
        foreach($dom->documentElement->childNodes as $animal) {
            $strategy = $animal->tagName . 'Strategy';
            $strategy = new $strategy($animal);
            $strategy->process();
        }
    }
}

$dom = new DOMDocument;
$dom->load('animals.xml');
$processor = new AnimalProcessor;
$processor->processAnimals($dom);

问题更新前的原始答案


不确定这是否是您想要的,但是如果您想要专门的domeElement,您可以简单地创建它们并直接使用它们,例如绕过
createElement
,这样您就不必注册
registerNodeClass

class DogElement extends DOMElement
{
    public function __construct($value) 
    {
        parent::__construct('dog', $value);
    }
}
class CatElement extends DOMElement
{
    public function __construct($value) 
    {
        parent::__construct('cat', $value);
    }
}
$dom = new DOMDocument;
$dom->loadXML('<animals/>');
$dom->documentElement->appendChild(new DogElement('Sparky'));
$dom->documentElement->appendChild(new CatElement('Tinky'));
echo $dom->saveXml();

我不能确切地指出这一点,但正如已经指出的那样,您正在尝试的是一种特定的方式。
无论如何根据实际节点(类型/contents/…),可以使用在extdoElement对象上公开不同的方法。为此,extdoElement对象可以存储一个助手对象,该对象根据元素的“类型”实例化,然后将方法调用委托给该助手对象。就我个人而言,我不太喜欢这样,因为这样做并不能使文档编制、测试和调试变得更容易。如果你觉得可行的话,我可以写一个完整的例子


这当然需要评论/文档…工作正在进行中,因为我现在没有时间

<?php
$doc = new MyDOMDocument('1.0', 'iso-8859-1');
$doc->loadxml('<animals>
  <Foo name="fido" />
  <Bar name="lucky" />
  <Foo name="scratchy" />
  <Ham name="flicka" />
  <Egg name="donald" />
</animals>');
$xpath = new DOMXPath($doc);
foreach( $xpath->query('//Foo') as $e ) {
  echo $e->name(), ': ', $e->foo(), "\n";
}
echo "----\n";
foreach( $xpath->query('//Bar') as $e ) {
  echo $e->name(), ': ', $e->bar(), "\n";
}
echo "====\n";
echo $doc->savexml();


class MyDOMElement extends DOMElement {
  protected $helper;

  public function getHelper() {
    // lazy loading and caching the helper object
    // since lookup/instantiation can be costly
    if ( is_null($this->helper) ) {
      $this->helper = $this->resolveHelper();
    }
    return $this->helper;
  }

  public function isType($t) {
    return $this->getHelper() instanceof $t;
  }

  public function __call($name, $args) {
    $helper = $this->getHelper();
    if ( !method_exists($helper, $name) ) {
      var_dump($name, $args, $helper);
      throw new Exception('yaddayadda');
    }
    return call_user_func_array( array($this->helper, $name), $args);
  }

  public function releaseHelper() {
    // you might want to consider something like this
    // to help php with the circular references
    // ...or maybe not, haven't tested the impact circual references have on php's gc
    $this->helper = null;
  }

  protected function resolveHelper() {
    // this is hardcored only for brevity's sake
    // add any kind of lookup/factory/... you like
    $rv = null;
    switch( $this->tagName ) {
      case 'Foo':
      case 'Bar':
        $cn = "DOMHelper".$this->tagName;
        return new $cn($this);
      default:
        return new DOMHelper($this);
        break;
    }
  }
}

class MyDOMDocument extends DOMDocument {
  public function __construct($version=null,$encoding=null) {
    parent::__construct($version,$encoding);
    $this->registerNodeClass('DOMElement', 'MyDOMElement');
  }
}

class DOMHelper {
  protected $node;
  public function __construct(DOMNode $node) {
    $this->node = $node;
  }
  public function name() { return $this->node->getAttribute("name"); }
}

class DOMHelperFoo extends DOMHelper {
  public function foo() {
    echo 'foo';
    $this->node->appendChild(  $this->node->ownerDocument->createElement('action', 'something'));
  }
}

class DOMHelperBar extends DOMHelper {
  public function bar() {
    echo 'bar';
    $this->node->setAttribute('done', '1');
  }
}
query('//Foo')作为$e){
echo$e->name(),“:”,$e->foo(),“\n”;
}
回显“---\n”;
foreach($xpath->query('//Bar')作为$e){
回显$e->name(),“:”,$e->bar(),“\n”;
}
回显“==\n”;
echo$doc->savexml();
类MyDomeElement扩展了DomeElement{
受保护的$helper;
公共函数getHelper(){
//延迟加载和缓存辅助对象
//因为查找/实例化可能代价高昂
如果(为空($this->helper)){
$this->helper=$this->resolvehlper();
}
返回$this->helper;
}
公共函数isType($t){
返回$t的$this->getHelper()实例;
}
公共函数调用($name,$args){
$helper=$this->getHelper();
如果(!方法_存在($helper,$name)){
变量转储($name、$args、$helper);
抛出新异常('yaddayada');
}
返回call\u user\u func\u数组(数组($this->helper,$name),$args);
}
公共函数releaseheloper(){
你可能想考虑这样的事情。
//帮助php使用循环引用
//…或者可能不是,还没有测试循环引用对php的gc的影响
$this->helper=null;
}
受保护的函数resolveHelper(){
//这只是为了简洁起见而精雕细琢的
//添加任何您喜欢的查找/工厂/…类型
$rv=null;
开关($this->tagName){
案例“Foo”:
“酒吧”一案:
$cn=“DOMHelper”。$this->tagName;
返回新的$cn($this);
违约:
返回新的DOMHelper($this);
打破
}
}
}
类MyDOMDocument扩展了DOMDocument{
公共函数构造($version=null,$encoding=null){
父项::__构造($version,$encoding);
$this->registerNodeClass('DomeElement','MyDomeElement');
}
}
类DOMHelper{
受保护的$节点;
公共函数构造(DOMNode$node){
$this->node=$node;
}
公共函数名(){return$this->node->getAttribute(“name”);}
}
类DOMHelperFoo扩展了DOMHelper{
公共功能foo(){
回声“foo”;
$this->node->appendChild($this->node->ownerDocument->createElement('action','something');
}
}
类DOMHelperBar扩展了DOMHelper{
公共功能条(){
回声“条”;
$this->node->setAttribute('done','1');
}
}
印刷品

fido: foo
scratchy: foo
----
lucky: bar
====
<?xml version="1.0"?>
<animals>
  <Foo name="fido"><action>something</action></Foo>
  <Bar name="lucky" done="1"/>
  <Foo name="scratchy"><action>something</action></Foo>
  <Ham name="flicka"/>
  <Egg name="donald"/>
</animals>
fido:foo
抓痒:福
----
幸运儿:酒吧
====
某物
某物

我也有同样的问题。我的解决方案是基于XMLReader扩展编写自己的解析器。由此产生的AdvancedParser类对我来说工作得非常好。可以为每个元素名称注册一个单独的元素类。通过扩展AdvancedParser类并覆盖getClassForElement()方法,还可以基于元素名动态计算所需类的名称

/**
 * Specialized Xml parser with element based class registry.
 * 
 * This class uses the XMLReader extension for document parsing and creates
 * a DOM tree with individual DOMElement subclasses for each element type.
 * 
 * @author       Andreas Traber < a.traber (at) rivo-systems (dot) com >
 * 
 * @since        April 21, 2012
 * @package      XML
 */
class AdvancedParser
{
    /**
     * Map with registered classes.
     * @var array
     */
    protected $_elementClasses = array();

    /**
     * Default class for unknown elements.
     * @var string
     */
    protected $_defaultElementClass = 'DOMElement';

    /**
     * The reader for Xml parsing.
     * @var XMLReader
     */
    protected $_reader;

    /**
     * The document object.
     * @var DOMDocument
     */
    protected $_document;

    /**
     * The current parsing element.
     * @var DOMElement
     */
    protected $_currentElement;

    /**
     * Gets the fallback class for unknown elements.
     * 
     * @return string
     */
    public function getDefaultElementClass()
    {
        return $this->_defaultElementClass;
    }

    /**
     * Sets the fallback class for unknown elements.
     * 
     * @param string $class
     * @return void
     * @throws Exception $class is not a subclass of DOMElement.
     */
    public function setDefaultElementClass($class)
    {
        switch (true) {
            case $class === null:
                $this->_defaultElementClass = 'DOMElement';
                break;

            case !$class instanceof DOMElement:
                throw new Exception($class.' must be a subclass of DOMElement');

            default:
                $this->_defaultElementClass = $class;
        }
    }

    /**
     * Registers the class for a specified element name.
     * 
     * @param string $elementName.
     * @param string $class.
     * @return void
     * @throws Exception $class is not a subclass of DOMElement.
     */
    public function registerElementClass($elementName, $class)
    {
        switch (true) {
            case $class === null:
                unset($this->_elementClasses[$elementName]);
                break;

            case !$class instanceof DOMElement:
                throw new Exception($class.' must be a subclass of DOMElement');

            default:
                $this->_elementClasses[$elementName] = $class;
        }
    }

    /**
     * Gets the class for a given element name.
     * 
     * @param string $elementName
     * @return string
     */
    public function getClassForElement($elementName)
    {
        return $this->_elementClasses[$elementName]
            ? $this->_elementClasses[$elementName]
            : $this->_defaultElementClass;
    }

    /**
     * Parse Xml Data from string.
     * 
     * @see XMLReader::XML()
     * 
     * @param string $source String containing the XML to be parsed.
     * @param string $encoding The document encoding or NULL.
     * @param string $options A bitmask of the LIBXML_* constants.
     * @return DOMDocument The created DOM tree.
     */
    public function parseString($source, $encoding = null, $options = 0)
    {
        $this->_reader = new XMLReader();
        $this->_reader->XML($source, $encoding, $options);
        return $this->_parse();
    }

    /**
     * Parse Xml Data from file.
     * 
     * @see XMLReader::open()
     * 
     * @param string $uri URI pointing to the document.
     * @param string $encoding The document encoding or NULL.
     * @param string $options A bitmask of the LIBXML_* constants.
     * @return DOMDocument The created DOM tree.
     */
    public function parseFile($uri, $encoding = null, $options = 0)
    {
        $this->_reader = new XMLReader();
        $this->_reader->open($uri, $encoding, $options);
        return $this->_parse();
    }

    /**
     * The parser.
     * 
     * @return DOMDocument The created DOM tree.
     */
    protected function _parse()
    {
        $this->_document = new DOMDocument('1.0', 'utf-8');
        $this->_document->_elements = array(); // keep references to elements
        $this->_currentElement = $this->_document;
        while ($this->_reader->read()) {
            switch ($this->_reader->nodeType) {
                case XMLReader::ELEMENT:
                    $this->_reader->isEmptyElement
                        ? $this->_addElement()
                        : $this->_currentElement = $this->_addElement();
                    break;

                case XMLReader::END_ELEMENT:
                    $this->_currentElement = $this->_currentElement->parentNode;
                    break;

                case XMLReader::CDATA:
                    $this->_currentElement->appendChild(
                        $this->_document->createCDATASection($this->_reader->value)
                    );
                    break;

                case XMLReader::TEXT:
                case XMLReader::SIGNIFICANT_WHITESPACE:
                    $this->_currentElement->appendChild(
                        $this->_document->createTextNode($this->_reader->value)
                    );
                    break;

                case XMLReader::COMMENT:
                    $this->_currentElement->appendChild(
                        $this->_document->createComment($this->_reader->value)
                    );
                    break;
            }
        }
        $this->_reader->close();
        return $this->_document;
    }

    /**
     * Adds the current element into the DOM tree.
     * 
     * @return DOMElement The added element.
     */
    protected function _addElement()
    {
        $element = $this->_createElement();

        // It's important to keep a reference to each element.
        // Elements without any reference were destroyed by the
        // garbage collection and loses their type.
        $this->_document->_elements[] = $element;

        $this->_currentElement->appendChild($element);
        $this->_addAttributes($element);
        return $element;
    }

    /**
     * Creates a new element.
     * 
     * @return DOMElement The created element.
     */
    protected function _createElement()
    {
        $class = $this->getClassForElement($this->_reader->localName);
        return new $class(
            $this->_reader->name,
            $this->_reader->value,
            $this->_reader->namespaceURI
        );
    }

    /**
     * Adds the current attributes to an $element.
     * 
     * @param DOMElement $element
     * @return void
     */
    protected function _addAttributes(DOMElement $element)
    {
        while ($this->_reader->moveToNextAttribute()) {
            $this->_reader->prefix && ($uri = $this->_reader->lookupNamespace($this->_reader->prefix))
                ? $element->setAttributeNS($uri, $this->_reader->name, $this->_reader->value)
                : $element->setAttribute($this->_reader->name, $this->_reader->value);
        }
    }
}
/**
*具有基于元素的类注册表的专用Xml解析器。
* 
*此类使用XMLReader扩展进行文档解析并创建
*一个DOM树,每个元素类型都有单独的DomeElement子类。
* 
*@author Andreas Traber
* 
*@自2012年4月21日起
*@package-XML
*/
类高级语法分析器
{
/**
*映射已注册的类。
*@var数组
*/
受保护的$_elementClasses=array();
/**
*未知元素的默认类。
*@var字符串
*/
受保护的$\u defaultElementClass='DomeElement';
/**
*用于Xml解析的读取器。
*@var XMLReader
*/
受保护的$u读取器;
/**
*文档对象。
*@var DOMDocument
*/
受保护的$u文件;
/**
*狗
fido: foo
scratchy: foo
----
lucky: bar
====
<?xml version="1.0"?>
<animals>
  <Foo name="fido"><action>something</action></Foo>
  <Bar name="lucky" done="1"/>
  <Foo name="scratchy"><action>something</action></Foo>
  <Ham name="flicka"/>
  <Egg name="donald"/>
</animals>
/**
 * Specialized Xml parser with element based class registry.
 * 
 * This class uses the XMLReader extension for document parsing and creates
 * a DOM tree with individual DOMElement subclasses for each element type.
 * 
 * @author       Andreas Traber < a.traber (at) rivo-systems (dot) com >
 * 
 * @since        April 21, 2012
 * @package      XML
 */
class AdvancedParser
{
    /**
     * Map with registered classes.
     * @var array
     */
    protected $_elementClasses = array();

    /**
     * Default class for unknown elements.
     * @var string
     */
    protected $_defaultElementClass = 'DOMElement';

    /**
     * The reader for Xml parsing.
     * @var XMLReader
     */
    protected $_reader;

    /**
     * The document object.
     * @var DOMDocument
     */
    protected $_document;

    /**
     * The current parsing element.
     * @var DOMElement
     */
    protected $_currentElement;

    /**
     * Gets the fallback class for unknown elements.
     * 
     * @return string
     */
    public function getDefaultElementClass()
    {
        return $this->_defaultElementClass;
    }

    /**
     * Sets the fallback class for unknown elements.
     * 
     * @param string $class
     * @return void
     * @throws Exception $class is not a subclass of DOMElement.
     */
    public function setDefaultElementClass($class)
    {
        switch (true) {
            case $class === null:
                $this->_defaultElementClass = 'DOMElement';
                break;

            case !$class instanceof DOMElement:
                throw new Exception($class.' must be a subclass of DOMElement');

            default:
                $this->_defaultElementClass = $class;
        }
    }

    /**
     * Registers the class for a specified element name.
     * 
     * @param string $elementName.
     * @param string $class.
     * @return void
     * @throws Exception $class is not a subclass of DOMElement.
     */
    public function registerElementClass($elementName, $class)
    {
        switch (true) {
            case $class === null:
                unset($this->_elementClasses[$elementName]);
                break;

            case !$class instanceof DOMElement:
                throw new Exception($class.' must be a subclass of DOMElement');

            default:
                $this->_elementClasses[$elementName] = $class;
        }
    }

    /**
     * Gets the class for a given element name.
     * 
     * @param string $elementName
     * @return string
     */
    public function getClassForElement($elementName)
    {
        return $this->_elementClasses[$elementName]
            ? $this->_elementClasses[$elementName]
            : $this->_defaultElementClass;
    }

    /**
     * Parse Xml Data from string.
     * 
     * @see XMLReader::XML()
     * 
     * @param string $source String containing the XML to be parsed.
     * @param string $encoding The document encoding or NULL.
     * @param string $options A bitmask of the LIBXML_* constants.
     * @return DOMDocument The created DOM tree.
     */
    public function parseString($source, $encoding = null, $options = 0)
    {
        $this->_reader = new XMLReader();
        $this->_reader->XML($source, $encoding, $options);
        return $this->_parse();
    }

    /**
     * Parse Xml Data from file.
     * 
     * @see XMLReader::open()
     * 
     * @param string $uri URI pointing to the document.
     * @param string $encoding The document encoding or NULL.
     * @param string $options A bitmask of the LIBXML_* constants.
     * @return DOMDocument The created DOM tree.
     */
    public function parseFile($uri, $encoding = null, $options = 0)
    {
        $this->_reader = new XMLReader();
        $this->_reader->open($uri, $encoding, $options);
        return $this->_parse();
    }

    /**
     * The parser.
     * 
     * @return DOMDocument The created DOM tree.
     */
    protected function _parse()
    {
        $this->_document = new DOMDocument('1.0', 'utf-8');
        $this->_document->_elements = array(); // keep references to elements
        $this->_currentElement = $this->_document;
        while ($this->_reader->read()) {
            switch ($this->_reader->nodeType) {
                case XMLReader::ELEMENT:
                    $this->_reader->isEmptyElement
                        ? $this->_addElement()
                        : $this->_currentElement = $this->_addElement();
                    break;

                case XMLReader::END_ELEMENT:
                    $this->_currentElement = $this->_currentElement->parentNode;
                    break;

                case XMLReader::CDATA:
                    $this->_currentElement->appendChild(
                        $this->_document->createCDATASection($this->_reader->value)
                    );
                    break;

                case XMLReader::TEXT:
                case XMLReader::SIGNIFICANT_WHITESPACE:
                    $this->_currentElement->appendChild(
                        $this->_document->createTextNode($this->_reader->value)
                    );
                    break;

                case XMLReader::COMMENT:
                    $this->_currentElement->appendChild(
                        $this->_document->createComment($this->_reader->value)
                    );
                    break;
            }
        }
        $this->_reader->close();
        return $this->_document;
    }

    /**
     * Adds the current element into the DOM tree.
     * 
     * @return DOMElement The added element.
     */
    protected function _addElement()
    {
        $element = $this->_createElement();

        // It's important to keep a reference to each element.
        // Elements without any reference were destroyed by the
        // garbage collection and loses their type.
        $this->_document->_elements[] = $element;

        $this->_currentElement->appendChild($element);
        $this->_addAttributes($element);
        return $element;
    }

    /**
     * Creates a new element.
     * 
     * @return DOMElement The created element.
     */
    protected function _createElement()
    {
        $class = $this->getClassForElement($this->_reader->localName);
        return new $class(
            $this->_reader->name,
            $this->_reader->value,
            $this->_reader->namespaceURI
        );
    }

    /**
     * Adds the current attributes to an $element.
     * 
     * @param DOMElement $element
     * @return void
     */
    protected function _addAttributes(DOMElement $element)
    {
        while ($this->_reader->moveToNextAttribute()) {
            $this->_reader->prefix && ($uri = $this->_reader->lookupNamespace($this->_reader->prefix))
                ? $element->setAttributeNS($uri, $this->_reader->name, $this->_reader->value)
                : $element->setAttribute($this->_reader->name, $this->_reader->value);
        }
    }
}