#php #xml #xslt #cookies
#php #xml #xslt #файлы cookie
Вопрос:
Как я могу указать streamContext, созданному с stream_context_create()
помощью, использовать разные HTTP-заголовки в соответствии с доменным именем запрошенного URI?
Я использую XSL для преобразования XML на стороне сервера (PHP).
Для этого я сначала создаю контекст потока с некоторыми параметрами и применяю этот поток к libxml
:
$streamContextOptions = array(
'http' =>array(
'header'=>$clientHeadersString /* HTTP header */
)
);
$streamContext = stream_context_create($streamContextOptions);
libxml_set_streams_context($streamContext);
Затем я загружаю XML, XSL и выполняю преобразование
// Load the XML
$xmlDocument = new DOMDocument();
$xmlDocument->load($xmlURI);
// Load the XSL into
$xslDocument = new DOMDocument();
$xslDocument->load($xslURI);
$xslProcessor = new XSLTProcessor();
$xslProcessor->importStylesheet($xslDocument);
// Apply the XSL
$htmlDocument = $xslProcessor->transformToDoc($xmlDocument);
В XSL у меня есть несколько document()
вызовов, поэтому сервер PHP вызывает указанные URI, используя контекст потока. Но у меня внутри этих document()
вызовов разные домены (допустим, у меня есть www.foo.com и www.bar.com ).
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" encoding="utf-8" indent="yes"/>
<xsl:template match="/">
<xsl:value-of select="document('http://www.foo.com/with-user-agent.xml.php')/root"/>
<xsl:value-of select="document('http://www.bar.com/with-accept-language.php')/root"/>
</xsl:template>
</xsl:stylesheet>
Я хочу изменить HTTP-заголовки, используемые libxml при выполнении document()
вызовов внутри XSL, чтобы я мог отправлять [cookies user-agent] в foo.com , и [другие файлы cookie accept-language] для bar.com когда они запрашиваются внутри document()
вызова XSL.
Я не хочу отправлять заголовок «too many» в foo.com (иначе говоря, я не хочу посылать Accept-Language
в foo.com ) ни к bar.com , и я не хочу, чтобы печенье смешивалось (печенье отправляется на foo.com не должны быть отправлены в bar.com ).
Каким-то образом, как:
$streamContextOptions = array(
'http-for-foo' =>array(
'header'=>$clientHeadersStringForFoo /* HTTP header only when document() calls for foo.com*/
),
'http-for-bar' =>array(
'header'=>$clientHeadersStringForBar /* HTTP header only when document() calls for bar.com*/
)
);
Есть ли способ сообщить XSLTProcessor изменить параметры streamOptions в зависимости от домена, запрошенного document()
функцией?
Комментарии:
1. Разве истинная проблема вашего внешнего ресурса не зависит от заголовков запроса? Имхо ресурс (ы) должен быть статическим и кэшируемым.
2. @RolandFranssen Нет, потому что ресурсом XML могут быть данные профиля пользователя (имя пользователя, аватар, почта …). Таким образом, пользователь должен войти в систему, прежде чем сможет получить этот вид XML. Возвращаемый XML также может быть основан
Accept-Language
на данных локализации.
Ответ №1:
Вы не можете указать разные хосты для параметров контекста потока, потому что при потоковой операции ввода-вывода контекст был назначен — нет возможности libxml_set_streams_context
отличаться в зависимости от имени хоста (также для потоков в PHP это тоже невозможно. По крайней мере, AFAIK).
Поэтому вместо этого вам нужно назначить URI с соответствующими параметрами потока.
В вашем сценарии мне кажется, что это проще всего сделать после настройки DOM, но перед преобразованием, создав свой собственный загрузчик внешних объектов, который может различать параметры контекста на основе имени хоста:
...
$xslProcessor->importStylesheet($xslDocument);
$httpOptions = [
'timeout' => 1,
'header' => "User-Agent: Godzilla Gabba Gandalf Client 42.4 - Lord of the XSLT Weed Edition"
];
$httpOptionsByHost = [
'www.foo.com' => [
'header' => "X-Secret-Debug-Request-Flag: verbose-verbose-verbose"
],
'www.bar.com' => [
'header' => "User-Agent: 1' OR TRUE"
]
];
libxml_set_external_entity_loader(
function ($public, $system, $context) use ($httpOptions, $httpOptionsByHost) {
$url = new Net_URL2($system);
$url->normalize();
$host = $url->getHost();
$options['http'] = [];
if (isset($httpOptionsByHost[$host])) {
$options['http'] = $httpOptionsByHost[$host];
}
$options['http'] = $httpOptions;
$context = stream_context_create($options);
return fopen($url, 'r', false, $context);
}
);
// Apply the XSL
$htmlDocument = $xslProcessor->transformToDoc($xmlDocument);
...
Вы не указали XML в своем вопросе, и внешние URI не работают, поэтому я мог протестировать только частично, но, насколько я мог проверить, это должно сработать.
Возможно, вам потребуется больше отличаться от функции внешнего загрузчика объектов, поскольку у вас могут быть дополнительные другие загружаемые объекты, которые не должны обрабатываться одинаково. Но я предполагаю, что вы можете это понять.
Комментарии:
1.
libxml_set_external_entity_loader()
Работает ли дляdocument()
вызовов внутри XSL? Или я должен переключаться и использоватьentities
внутри XML вместоdocument()
внутри XSL?2. Это работает для
document()
вызовов внутри XSL, иначе я бы этого не предложил. В моем тесте я мог бы создать два разных контекста потока для двух разных имен хостов, которые у вас есть в двух разных URI, которые находятсяdocument()
внутри файла XSL.
Ответ №2:
Возможно, вы могли бы прокси-запрос? Т.е. document('//mydomain/?doc=origin-doc')
Таким образом, вы можете изменить запрос (например, с помощью cURL) в соответствии с вашими потребностями. Параметр doc можно использовать для условного изменения запроса.
Комментарии:
1. Дело в том, что я хочу иметь возможность обрабатывать XSL на стороне клиента: процесс на стороне сервера является резервным вариантом на случай, если браузер не поддерживает XSL. Итак, используя прокси, когда XSL анализируется на стороне клиента, браузер отправляет
mydomain
учетные данные (и прокси-серверotherDomain
) вместоotherDomain
прямого вызова с правильными учетными данными. Я знаю, что на стороне сервераmydomain
не будетotherDomain
учетных данных, но я хочуotherDomain
, чтобы меня называли без учетных данных, когда на стороне сервера, и с полными учетными данными, когда на стороне клиента.2. На самом деле, поскольку Chrome (webkit?) не принимает CORS для документов XML / XSL, я буду использовать такой способ и придерживаться локальных URI (с одним контекстом потока для всего XSL), загружаемых сервером, если мне нужно.