用其他 XML 来源扩展 SimpleXML class

extend the SimpleXML class with other XML sources

对于当前的项目,如果能够扩展 class SimpleXML 以访问 SimpleXML 可遍历的魔力,那就太好了。出于某种超出我的原因,构造函数是最终的。无论如何,我将如何扩展 class。

到目前为止我所做的是避免在子class上使用构造函数,而是为return创建一个静态方法,一个XML字符串可以在调用者中使用构造一个对象。不漂亮,因为它给调用者增加了一个应该隐藏的模式的负担,但它正在工作。有没有更优雅的方法来规避具有最终构造函数的阻碍设计选择?

namespace acme.com;
class SubXML extends \SimpleXML{
   static public createXML() {
        // here we return a magnificent XML string
        // probably from a DB or a REST interface
   }
}

$o = new \SubXML( \SubXML::createXML() );

SimpleXMLElement 上的构造函数不只是初始化一个 PHP 对象,而是调用一个外部库来初始化一些内部内存结构,然后在您遍历 XML 文档或片段。

一种支持扩展 class 的方法是将 class 的名称作为第二个参数传递给 simplexml_load_string or simplexml_load_file,它将用于 all 遍历元素和属性。例如:

class MyXML extends SimpleXMLElement {
    public function hello() { echo "Hello ", $this->getName(); }
}
$sx = simplexml_load_string('<foo><bar><baz>42</baz></bar></foo>', 'MyXML');
$sx->bar->baz->hello();

不清楚如果 MyXML 在这种情况下有一个自定义构造函数会发生什么:是否应该为每个子元素调用它?有什么论据?


撇开这一点不谈,您的实际要求似乎是拥有某种工厂,从某些来源(数据库,API)获取数据并创建某种对象。您的静态方法似乎是一个明智的方向,但可以 return 直接对象而不是字符串:

class SubXML extends SimpleXMLElement {
   public static function createXMLfromDB($db) {
        $xmlString = $db->query('Select XML from SpecialTable');
        return simplexml_load_string($xmlString, static::class);
   }
}

但是,我认为继承并不是首先表示这一点的特别好的方式 - 结果对象可能不需要知道它的数据来自哪里,它需要能够表示数据。因此,您的工厂可以是一个完全不同的 class,以更 test-friendly 的方式管理其依赖关系:

class MyXMLFromDBFactory {
    private MyDBWrapper $db;

    public function __construct(MyDBWrapper $db) {
        $this->db = $db;
    }

    public function createXML() {
        $xmlString = $db->query('Select XML from SpecialTable');
        return simplexml_load_string($xmlString);
    }
}

那你可以有一个单独的MyXMLFromAPIFactory,MyXMLFromFileOnAmazonS3Factory,等等,如果你愿意,这些都可以return和一样扩展 SimpleXMLElement class,甚至将 class 名称作为参数并将其传递给 simplexml_load_string.