PHP DomDocument, повторное использование XSLTProcessor, стабильно / безопасно?

Я использую функцию ниже, но не уверен, что она всегда безопасна … Разве? Нет DOM-памяти или «остаточного XSLT»?

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

На нем нет будущих «неожиданных побочных эффектов»?

PS: У меня есть некоторые странные ошибки с моей обработкой XSLT … Итак, отправляя одну (из многих других) гипотез здесь, чтобы проверить, нормально ли или ее следует избегать. Это более очевидно с XPath, см. Этот другой родственный вопрос .


Еще один способ, чтобы ПОЛУЧИТЬ БОЛЬШЕ листа обработки (который я использовал в своей библиотеке), заключается в повторном использовании импортированного 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); } } 

Чтобы понять проблему, важно понять, как XSLTProcessor хранит данные внутри и что происходит после вызова XSLTProcessor::importStylesheet . Код, реализующий этот класс, находится в \ext\xsl\xsltprocessor.c из исходного кода php.

Объяснение будет немного упрощено – написано в чистом php 'c'. То, что находится в объекте php – с точки зрения с просто функций, работает в глобальном контексте.

Веб должен понимать, как и что происходит с импортированными данными:

  1. XSLTProcessor :: importStylesheet принимает объект DOMDocument или SimpleXMLElement .
  2. Firts, что происходит с импортом (из 409 строк источников, docp – параметр importStylesheet )

     //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 потому что он использует копию таблицы стилей $. Но вы не можете обновить или обновить таблицу $ importStylesheet без вызова importStylesheet снова.

Что XSLTProcessor::transformToDoc ( php_xsl_apply_stylesheet – из строки 477 того же источника) и другие методы transform . Каждый из них выделяет их тип вывода (DOMDocument в случае XSLTProcessor::transformToDoc ) и использует внутренний объект sheetp (созданный в importStylesheet ) для преобразования данных.

Отредактировано после комментариев

  1. Некоторые слова об условиях «когда» и / или «почему» повторное использование недействительны. Повторное использование действительно и рекомендуется каждый раз, когда вам нужно сделать transformTo более одного раза. Если повторное использование, если stylesheep использует xsl: key в больших таблицах стилей из-за дополнительного обхода XML-узла.
  2. Кэширование только объекта XSLTProcessor не имеет никакого смысла – конструктор этого объекта ничего не выделяет. И XSLproc_reuse2 имеет смысл и должен использоваться. Вы выполняете кеширование порядка $ stylesheet и трассировки в использовании xsl: key . Не указатель, а весь объект и его внутренние элементы.

Пусть s some more words about how the работает transformToDoc`:

  1. DOMDocument новый объект DOMDocument .
  2. Если $ stylesheet имеет xsl: ключ, он делает копию вашего параметра $ doc.
  3. Делает tramsform с xsltNewTransformContext и xsltApplyStylesheetUser из libxslt .
  4. Вернуться DOMDocument

Где нет никакого кода штрафа в XSLTProcessor или libxslt который неправильно использует XSLTProcessor использование XSLTProcessor . Перед 0.7.2 xslcache я пытаюсь работать с этим проектом и настраивать его для работы с xslcache сайтом. Есть мой опыт. В то время мы используем XSLT в качестве механизма шаблона с большими XSLT-шаблонами (в раунде ~ 3-5 МБ сокращенного кода). В таких случаях кеширование importStylesheet имеет большой прирост производительности. Но нет никакой возможности кэшировать результаты transformToDoc – каждый раз, когда вы вызываете его, libxslt делает dom манипуляции с двумя подготовленными объектами в памяти и дает вам новый объект в качестве результата.

Использование static определяет глобальное состояние, которое по определению является «неустойчивым». Его можно изменить из любой точки программы. Используя объект, вы получаете локальное состояние (внутри экземпляра объекта). Я также предлагаю использовать массив. Таким образом, он может хранить несколько процессоров для разных файлов.

 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.