首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >扩展DOMNode,然后扩展DOMDocument,继承扩展的DOMNode

扩展DOMNode,然后扩展DOMDocument,继承扩展的DOMNode
EN

Stack Overflow用户
提问于 2013-06-26 18:27:04
回答 3查看 1.2K关注 0票数 1

哦,是的,标题很棒,我知道,我知道。对不起,不是以英语为母语的,标题可能没有反映这个问题,但我会尽我所能。

我正在开发一个相对较大的DOM库扩展。最近,我在考虑重写它以扩展标准库。然而,由于继承的原因,我遇到了一些问题。

DOM的核心元素之一是DOMNode,因此我首先对其进行了扩展:

代码语言:javascript
复制
<?php namespace DOMWorks;

use \DOMNode;

class Node extends DOMNode
{
    // methods...
}

然后,我开始尝试在默认情况下扩展DOMNode的DOMDocument上工作。

代码语言:javascript
复制
<?php namespace DOMWorks;

use \DOMDocument;

class Document extends DOMDocument
{
    // methods...
}

但是,这会丢失先前扩展的Node的轨迹。

如何从扩展DOMDocument扩展DOMNode并从中创建我自己的文档扩展?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-06-26 18:33:52

多重继承很有趣。

在进行了更多的尝试之后,我得出结论,仅仅使用继承是不可能以一种有意义的方式做到这一点的。要做到这一点,唯一实用的方法是通过修饰(即,将本机类包装在您自己的类中而不进行扩展,并使用魔术方法访问内部对象的属性)。显然这并不理想,它很快就开始变得混乱,你失去了真正的继承提供的明显好处,比如和instanceofis_a()等人玩得很好。

问题的根源在于,因为registerNodeClass() (参见下面的原始答案)是一个实例方法,所以它只能在DOMDocument的实例上调用-并且在创建实例时,继承已经确定-您不能在创建对象后更改对象的基类。

(据我所知)没有办法静态地将PHP设置为总是在new Document点基于扩展基类创建实例,这是为了让您的自定义文档继承您的自定义节点而需要执行的操作。你可能无论如何都不想要这样,因为它是隐藏的全局状态,可能会影响超出你的库的消费应用程序。

我仍然在玩一个有点丑陋的想法,涉及到一些特征来促进装饰器模式,但我还不确定我的想法是否真的可行,或者我是否应该简单地坐在角落里,来回摇摆,悄悄地自言自语。

如果我想出更有用/更具体的东西,或者一般不太像脑残的东西,我可能会在某个时候对此进行扩展。下面的第一句话让我想哭:-(

原始答案:

幸运的是,PHP开发人员预见到了这种可能性,并以一种(合理的)明智的方式满足了您的需求:

DOMDocument::registerNodeClass()

您可以在扩展document类的构造函数中将其构建到lib中(这样消费者就不必这么做了):

代码语言:javascript
复制
<?php

namespace DOMWorks;

use \DOMDocument;

class Document extends DOMDocument
{
    public function __construct($version = null, $encoding = null)
    {
        $this->registerNodeClass('DOMNode', __NAMESPACE__ . '\Node');
    }

    // methods...
}
票数 4
EN

Stack Overflow用户

发布于 2013-06-26 20:24:38

如何扩展DOMNode,从扩展DOMDocument到创建自己的文档扩展?

在PHP中,我们没有多重继承。这意味着,在PHP中不可能实现您想要做的事情。原因是DOMDocument从DOMNode扩展而来( DOMElement、DOMAttr、DOMText等也是如此)。所以这个继承路径已经完成了(DaveRandom解释了这个更长的,可能用词更好的,如果你想知道的话,我的意思是相同的)。

PHP5.4让情况变得更好,因为你可以将所有子类型之间共享的代码(例如,你本应放入DOMNode中的代码)放入traits中。

然后,您创建的每个子类型都可以使用这些特征(稍后会看到一个示例)。

如果你还想让它们都成为你的DOMNode类型,你也可以定义一个空的接口,然后用你的所有子类型来实现。

下面是一个典型的scraper库中的该技术的示例:

代码语言:javascript
复制
class ScraperDoc extends DOMDocument implements ScraperNodeType
{
    use ScraperNodeTrait;

    ...

如图所示,它实现了一个接口(ScraperNodeType)和一个特征(ScraperNodeTrait)。

所以这里有一个接口:

代码语言:javascript
复制
/**
 * Empty Interface for typing reasons (instanceof operator and traits
 * work not well, this interface cures that limitation a bit)
 */
interface ScraperNodeType
{
}

还有特征;如果你是特征的新手,这里有一些示例代码,一个单方法特征,它为实现特征的所有节点提供字符串上下文(只是为了给出一个概念,它是原始库的缩写):

代码语言:javascript
复制
Trait ScraperNodeTrait
{
    public function __toString()
    {
        /* @var $this DOMNode  */
        return trim($this->textContent);
    }
}

这不像Ruby中的特征/混合那样流畅,但在PHP中尽可能接近(到目前为止,使用非动态代码)。

这仍然不能解决创建自己的层次结构的所有问题,但我认为你应该了解这种技术(特征+空接口)。

这是一个继承图,显示了顶部的DOMNode,然后是PHP DOM扩展中的扩展类型,然后是这些扩展的用户端扩展,以及它们与特征(下图左)和接口(下图右)的关系。

右边的cluser与迭代器和simplexml相关,这不是这个答案的一部分,所以不是直接感兴趣的。尽管它表明,例如,你不能在PHP中重载DOMNodeList。使用SimpleXML可能会有一些疯狂的举动,这就是为什么该库也将其作为整体的一部分。

然后在左下角你会发现一个对Net_URL2的引用,这是迄今为止最好的PHP类。这个库是从它扩展而来的,所以它有自己的URL类型,并且外部库至少被分层到代码库中。

基于DOMDocument继承图(full-size)的

抓取库示例

我希望这对你有所帮助,同时也能给你一些启发。上一次我回答了一个关于扩展DOMDocument的问题是关于DOMDocument是一个模型而不是你的模型的现象:

票数 2
EN

Stack Overflow用户

发布于 2014-11-08 05:36:15

对特征使用“代理模式”怎么样?这个想法是声明“特征”内的公共方法,以便扩展和注册的节点类即使不是扩展的DOMNode…的派生/子类也可以访问

以下是PHP.net上发布的一小段代码:

代码语言:javascript
复制
    namespace my;

    trait tNode
    {    // We need the magic method __get in order to add properties such as DOMNode->parentElement
        public function __get($name)
        {    if(property_exists($this, $name)){return $this->$name;}
            if(method_exists($this, $name)){return $this->$name();}
            throw new \ErrorException('my\\Node property \''.(string) $name.'\' not found…', 42, E_USER_WARNING);
        }

        // parentElement property definition
        private function parentElement()
        {    if($this->parentNode === null){return null;}
            if($this->parentNode->nodeType === XML_ELEMENT_NODE){return $this->parentNode;}
            return $this->parentNode->parentElement();
        }

        // JavaScript equivalent
        public function isEqualNode(\DOMNode $node){return $this->isSameNode($node);}
        public function compareDocumentPosition(\DOMNode $otherNode)
        {    if($this->ownerDocument !== $otherNode->ownerDocument){return DOCUMENT_POSITION_DISCONNECTED;}
            $c = strcmp($this->getNodePath(), $otherNode->getNodePath());
            if($c === 0){return 0;}
            else if($c < 0){return DOCUMENT_POSITION_FOLLOWING | ($c < -1 ? DOCUMENT_POSITION_CONTAINED_BY : 0);}
            return DOCUMENT_POSITION_PRECEDING | ($c > 1 ? DOCUMENT_POSITION_CONTAINS : 0);
        }
        public function contains(\DOMNode $otherNode){return ($this->compareDocumentPosition($otherNode) >= DOCUMENT_POSITION_CONTAINED_BY);}
    }

    class Document extends \DomDocument
    {    public function __construct($version=null, $encoding=null)
        {    parent::__construct($version, $encoding);
            $this->registerNodeClass('DOMNode', 'my\Node');
            $this->registerNodeClass('DOMElement', 'my\Element');
            $this->registerNodeClass('DOMDocument', 'my\Document');
            /* [...] */
        }
    }

    class Element extends \DOMElement
    {    use tNode;
        /* [...] */
    }

    class Node extends \DOMNode
    {    use tNode;
        /* [...] */
    }
票数 -1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17317575

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档