【问题标题】:PHP DomDocument, reuse of XSLTProcessor, it is stable/secure?PHP DomDocument,重用XSLTProcessor,稳定/安全?
【发布时间】:2013-11-22 05:58:56
【问题描述】:

我正在使用下面的功能,但不确定它是否总是安全的……是吗? 那里没有 DOM 内存或“残余 XSLT”?

   function XSLproc_reuse($domXsl) {
      static $XSLproc=NULL;
      if (!$XSLproc)
           $XSLproc = new XSLTProcessor();
      return $XSLproc->importStylesheet($domXsl); // STABLE?
   }

未来没有“意外的副作用”吗?

PS:我的 XSLT 处理存在一些奇怪的错误......所以,在这里发布一个(许多其他的)假设,以检查是否可以或必须避免。这个is more evident with XPath, see this other related question


另一种重用处理表(我在我的库中使用的)的另一种方法是重用导入的 XSLT:

   function XSLproc_reuse2($nameOrDomXsl='', $domXsl=NULL) {
      static $XSLproc=NULL;
      static $name='';

      if (!$XSLproc)
                $XSLproc = new XSLTProcessor();
      // else reune of the already initialized $XSLproc.

      if (is_object($nameOrDomXsl))
                return $XSLproc->importStylesheet($nameOrDomXsl); // STABLE?
      elseif ($nameOrDomXsl==$name);
                return $XSLproc;  // imported in the last call, STABLE?
      else { // recording for future reuse:
                $name = $nameOrDomXsl;
                return $XSLproc->importStylesheet($domXsl);
      }
   }

【问题讨论】:

  • 我不确定是否有人能理解这个问题。
  • 嗯...是的,谢谢,我编辑了。更好?

标签: php domdocument


【解决方案1】:

要了解问题,了解 XSLTProcessor 如何在内部存储数据以及调用 XSLTProcessor::importStylesheet 后会发生什么很重要。实现该类的代码位于\ext\xsl\xsltprocessor.c from php 源代码中。

解释将不得不简化一些 - 是用纯 php 'c' 编写的。 php 对象中的内容 - 就 с 而言,只是函数在全局上下文中运行。

Web 需要了解导入数据的方式和情况:

  1. XSLTProcessor::importStylesheet 接受 DOMDocumentSimpleXMLElement 的对象。
  2. 导入时发生的第一件事(来自 409 行来源,docpimportStylesheet 参数)

    //php_libxml_import_node is (in the \ext\libxml\libxml.c) just get
    //the real `xmlNodePtr` XMLlib2  object pointer by php object pointer.
    nodep = php_libxml_import_node(docp TSRMLS_CC);
    if (nodep) {
        doc = nodep->doc;
    }
    if (doc == NULL) {
        php_error(E_WARNING, "Invalid Document");
        RETURN_FALSE;
    }
    
    //Next lines is an original comments and call of `xmlCopyDoc` which makes copy
    // of your stylesheet. The main lines in my answer.
    
    /* libxslt uses _private, so we must copy the imported 
    stylesheet document otherwise the node proxies will be a mess */
    newdoc = xmlCopyDoc(doc, 1);
    ....
    //Here we create internal stylesheet object with libxslt function.
    

    sheetp = xsltParseStylesheetDoc(newdoc);
    ...

    //And some lines later store them to internal variables for this
    //XSLTProcessor class instance. 
    php_xsl_set_object(id, sheetp TSRMLS_CC); 
    
  3. importStylesheet 之后,您可以对您的$stylesheet 对象做任何您想做的事情——它不会影响XSLTProcessor 的工作,因为它使用了$stylesheet 的副本。但是如果不再次致电importStylesheet,您将无法刷新或更新您的 $stylesheet。

关于 XSLTProcessor::transformToDocphp_xsl_apply_stylesheet - 来自同一来源的 477 行)和其他 transform 方法是什么。他们每个人都分配他们的输出类型(在XSLTProcessor::transformToDoc的情况下是DOMDocument)并使用内部sheetp对象(在importStylesheet中创建)来转换数据。

在 cmets 之后编辑

  1. 关于“何时”和/或“为什么”重用条件的一些词语无效。每次您需要多次重复使用有效且建议transformTo。如果 stylesheep 在大样式表上使用 xsl:key,则应该重用,因为需要额外的 XML 节点遍历。
  2. 仅缓存 XSLTProcessor 对象没有任何意义 - 此对象的构造函数不分配任何内容。并且XSLproc_reuse2 有意义,应该使用。在xsl:key 使用中缓存$stylesheet 复制和遍历的过程。不是指针,而是所有对象及其内部结构。

s some more words about how thetransformToDoc` 工作:

  1. 分配新的DOMDocument 对象。
  2. 如果 $stylesheet 有 xsl:key 它会复制您的 $doc 参数。
  3. 使用来自libxsltxsltNewTransformContextxsltApplyStylesheetUser 制作tramsform。
  4. 返回DOMDocument

XSLTProcessorlibxslt 中没有任何惩罚代码导致 XSLTProcessor 重复使用错误。在xslcache 的 0.7.2 之前,我尝试使用该项目并将其调整为与外部站点一起使用。有我的经验。那时我们使用 XSLT 作为带有大型 XSLT 模板的模板引擎(大约 3-5mb 的缩小代码)。在这种情况下,importStylesheet 的缓存具有很大的性能提升。但是没有任何机会缓存 transformToDoc 结果 - 每次调用它时,libxslt 都会对内存中的两个准备好的对象进行 dom 操作,并为您提供新的对象作为结果。

【讨论】:

  • 非常感谢,您真的了解它的工作原理!您能否添加一些行“以解释'何时'和/或'为什么'重用无效的条件”(请参阅​​我的赏金文本)?我知道我的XSLproc_reuse(),总是重做importStylesheet,是稳定的;我的XSLproc_reuse2() 很危险,因为正如您所说“如果不再次调用 importStylesheet 就无法刷新或更新您的 $stylesheet”。
  • 另一点:我的函数XSLproc_reuse() 是通过缓存来提高性能...但它只缓存一个指针(?),没有实用程序(除了做 importStylesheet 的快捷方式)...是吗?
  • OPS... 我的目标是在transformToDoc() 中重用XSLT,那么问题是什么? XSLT 不会改变(!),只有输入/输出转换......那么,为什么(理论上)我在重用时会不稳定?
  • 感谢您的编辑笔记!关于您的断言,“XSLproc_reuse2 有意义并且应该使用”,它可能是错误的:我观察到不“刷新”时的不稳定性(请参阅相关问题)...作为专家,也许您可​​以回答 a similar bounty here
  • 彼得,我已经为您的类似问题添加了答案。
【解决方案2】:

使用静态定义全局状态,即定义为“不稳定”。它可以从程序中的任何位置进行更改。使用对象可以获得本地状态(在对象实例内)。我也建议使用数组。因此它可以为不同的文件存储多个处理器。

class XsltFactory {

  private $_processors = array();

  public function get($file) {
    if (!isset($this->_processors[$file])) {
      $xslDom = new DOMDocument();
      $xslDom->load($file);
      $xslProc = new XSLTProcessor();
      $xslProc->importStylesheet($xslDom);
      return $this->_processors[$file] = $xslProc;
    }
    return $this->_processors[$file];
  }
}

$xsltFactory = new XsltFactory();
var_dump(
  htmlspecialchars(
    $xsltFactory->get($template)->transformToDoc($xmlDom)->saveXml()
  )
);

提高性能的更好解决方案是xslcache。它在进程内缓存$xslt->importStyleSheet($filename) 的结果。如果进程被重用,那么编译的xsl也是。

【讨论】:

  • 你好托马斯,谢谢!各有一点……“静态的定义是不稳定的”:我不明白,PHP(甚至C)会发生什么,是一种不稳定的语言吗?你有我必须研究的错误报告的链接吗?
  • 关于使用标量(我的说明性XSLproc_reuse2())或数组(您的xsltFactory 类),我认为这不会影响“不稳定讨论”,所以,我喜欢你的类XsltFactory (我将使用它!),但我更喜欢先讨论 XSLproc_reuse2 周围的概念......问题:你对 return $XSLproc 的看法是稳定的(如果不是,问题和解决方法在哪里)?
  • 问题不在于 XsltProcessor 实例。就像 misterion 所描述的那样,它本身就是封装的。如果您使用静态来存储状态,这将是一个全局状态,来自应用程序中任何位置的调用都可以修改全局状态。这是一个概念问题,而不是语言问题。你应该非常小心地使用 static 并尽可能避免它。
猜你喜欢
  • 2013-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-01-07
  • 1970-01-01
  • 2021-05-07
相关资源
最近更新 更多