PHP5: применение метода из расширенного класса к объекту из исходного (родительского) класса - PullRequest
0 голосов
/ 06 апреля 2010

Я пытаюсь расширить два собственных класса PHP5 (DOMDocument и DOMNode), чтобы реализовать 2 метода (selectNodes и selectSingleNode), чтобы упростить запросы XPath. Я думал, что это будет довольно просто, но я застрял в проблеме, которая, я думаю, является проблемой для начинающих ООП.

class nDOMDocument extends DOMDocument {
    public function selectNodes($xpath){
    $oxpath = new DOMXPath($this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

Затем я попытался расширить DOMNode для реализации тех же методов, чтобы я мог выполнять запрос XPath непосредственно на узле:

class nDOMNode extends DOMNode {
    public function selectNodes($xpath){
    $oxpath = new DOMXPath($this->ownerDocument,$this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

Теперь, если я выполню следующий код (для произвольного XMLDocument):

$xmlDoc = new nDOMDocument;
$xmlDoc->loadXML(...some XML...);
$node1 = $xmlDoc->selectSingleNode("//item[@id=2]");
$node2 = $node1->selectSingleNode("firstname");

Третья строка работает и возвращает объект DOMNode $ node1. Однако четвертая строка не работает, потому что метод selectSingleNode принадлежит классу nDOMNode, а не DOMNode. Итак, мой вопрос: есть ли способ в какой-то момент "преобразовать" возвращенный объект DOMNode в объект nDOMNode? Я чувствую, что упускаю какой-то важный момент, и буду очень признателен за вашу помощь.

(Извините, это переформулировка моего вопроса Расширение DOMDocument и DOMNode: проблема с возвращаемым объектом )

Ответы [ 5 ]

2 голосов
/ 07 апреля 2010

Вы можете «сообщить» DOMDocument через DOMDocument :: registerNodeClass () , какой класс он должен использовать вместо классов по умолчанию при создании экземпляра объекта для определенного типа узла. Э.Г.

$doc->registerNodeClass('DOMElement', 'Foo');

каждый раз, когда dom-объект "нормально" создает экземпляр DOMElement, теперь он создает экземпляр Foo.

$doc = new nDOMDocument;
$doc->loadxml('<a><b><c>foo</c></b></a>');

$a = $doc->selectSingleNode('//a');
$c = $a->selectSingleNode('//c');

echo 'content: ', $c->textContent;

class nDOMElement extends DOMElement {
  public function selectNodes($xpath){
    $oxpath = new DOMXPath($this->ownerDocument);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}


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

  public function selectNodes($xpath){
    $oxpath = new DOMXPath($this);
    return $oxpath->query($xpath);
  }
  public function selectSingleNode($xpath){
    return $this->selectNodes($xpath)->item(0);
  }
}

отпечатки content: foo.

Но вы не можете просто установить registerNodeClass('DOMNode', 'MyDOMNode') и ожидать, что DOMElement отныне будет наследовать от MyDOMNode вместо DOMNode. То есть Вы должны зарегистрировать все типы узлов, которые хотите перезаписать.

1 голос
/ 06 апреля 2010

Я думаю, вам будет лучше, если вы будете украшать существующую библиотеку объектами-обертками, а не напрямую создавать их подклассы.Как вы уже видите, вы не можете легко добавить вспомогательные методы к DOMDocument и заставить его возвращать ваши подклассы.

В частности, проблема здесь заключается в DOMXPath :: item () не знает, чтобы вернуть nDOMNode.

. Если вы попытаетесь сделать что-то вроде следующего, произойдет сбой (многие свойства выглядят как только для чтения ):

class nDOMDocument extends DOMDocument {

    ...

    public function selectSingleNode($xPath) {
        $node = $this->selectNodes($xPath)->item(0);
        $myNode = new nDOMNode;
        $myNode->set_name($node->get_name()); // fail?
        $myNode->set_content($node->get_content());
        // set namespace
        // can you set the owner document? etc

        return $myNode;
    }

}

Если вместо этого вы оберните объект, вы можете быстро открыть существующий интерфейс DOMNode, используя магические методы __get() и __call() , включить ваши дополнительные функции и выбрать возврат ваших собственных завернутых / пользовательскихклассы для достижения вашей первоначальной цели.

Пример:

class nDOMNode {

    protected $_node;

    public function __construct(DOMNode $node) {
        $this->_node = $node;
    }

    // your methods

}

class nDOMDocument {

    protected $_doc;

    public function __construct(DOMDocument $doc) {
        $this->_doc = $doc;
    }

    ...

    public function selectNodes($xPath){
        $oxPath = new DOMXPath($this->_doc->ownerDocument, $this->_doc);
        return $oxPath->query($xPath);
    }

    public function selectSingleNode($xPath) {
        return new nDOMNode($this->selectNodes($xPath)->item(0));
    }

}

$doc = new nDOMDocument(new DOMDocument);
$doc->loadXML('<xml>');
$node = $doc->selectSingleNode("//item[@id=2]"); // returns nDOMNode

Надеюсь, это поможет.

1 голос
/ 06 апреля 2010

Одним из последствий объектно-ориентированного подхода является то, что наследование является улицей с односторонним движением. То есть, нет никакого способа, чтобы родитель знал о методах детей. Таким образом, хотя вы можете выполнять итерацию по коллекции объектов, вы можете только надежно вызывать методы родительского класса. У PHP действительно есть способы проверить, существует ли метод во время выполнения, и это может быть полезно, но также может быстро стать ужасным.

Это означает, что когда вы расширяете встроенный класс, вам необходимо полностью расширить его, чтобы ваше приложение никогда не работало с экземплярами встроенного класса - только с вашей версией. В вашем случае вам нужно расширить свои расширенные классы, чтобы переопределить все методы родителя, которые возвращают тип класса родителя:

public myClass extends foo {
// override all methods that would normally return foo objects so that they
// return myClass objects.
}
1 голос
/ 06 апреля 2010

Вам нужно будет кодировать nDOMDocument :: selectSingleNode (), чтобы вернуть объект nDOMNode. Там нет волшебного преобразования, которое может произойти.

Я говорю, что вы должны продолжить свой эксперимент, поскольку на этом пути вы выучите несколько хороших уроков ООП (хотя и трудных). В этом нет ничего плохого.

0 голосов
/ 06 апреля 2010

Я думаю, что вам, возможно, придется пройти долгий путь и установить свойства указания, указывающие вниз, вверх, влево и вправо, что-то вроде DOM для JavaScript. Они обязательно будут назначены при создании структуры.

...