DOMDocument-> gt; saveHTML () и urlencode с рекламным символом (@)

С помощью DOMDocument(), Я заменяю ссылки в $message и добавив некоторые вещи, такие как [@MERGEID], Когда я сохраняю изменения с $dom_document->saveHTML(), ссылки получают «своего рода» в кодировке URL. [@MERGEID] становится %5B@MERGEID%5D,

Позже в моем коде мне нужно заменить [@MERGEID] с удостоверением личности. Поэтому я ищу urlencode('[@MERGEID]') — тем не мение, urlencode() изменяет рекламу в символе (@) на% 40, а saveHTML () оставляет его в покое. Так что нет совпадений — '%5B@MERGEID%5D' != '%5B%40MERGEID%5D'

Теперь я знаю, может бежать str_replace('%40', '@', urlencode('[@MERGEID]')) чтобы получить то, что мне нужно, чтобы найти переменную слияния в $ message.

Мой вопрос, Какие спецификации RFC использует DOMDocument и почему они отличаются от urlencode или даже rawurlencode? Могу ли я что-нибудь сделать, чтобы сохранить str_replace?

Демо-код:

$message = '<a href="http://www.google.com?ref=abc" data-tag="thebottomlink">Google</a>';
$dom_document = new \DOMDocument();
libxml_use_internal_errors(true); //Supress content errors
$dom_document->loadHTML(mb_convert_encoding($message, 'HTML-ENTITIES', 'UTF-8'));
$elements = $dom_document->getElementsByTagName('a');
foreach($elements as $element) {
$link = $element->getAttribute('href'); //http://www.google.com?ref=abc
$tag = $element->getAttribute('data-tag'); //thebottomlink
if ($link) {
$newlink = 'http://www.example.com/click/[@MERGEID]?url=' . $link;
if ($tag) {
$newlink .= '&tag=' . $tag;
}
$element->setAttribute('href', $newlink);
}
}
$message = $dom_document->saveHTML();
$urlencodedmerge = urlencode('[@MERGEID]');
die($message . ' and url encoded version: ' . $urlencodedmerge);
//<a data-tag="thebottomlink" href="http://www.example.com/click/%5B@MERGEID%5D?url=http://www.google.com?ref=abc&amp;tag=thebottomlink">Google</a> and url encoded version: %5B%40MERGEID%5D

4

Решение

Я считаю, что эти две кодировки служат разным целям. urlencode() кодирует «строка, которая будет использоваться в части запроса URL «, в то время как $element->setAttribute('href', $newlink); кодирует полный URL-адрес для использования в качестве URL-адреса.

Например:

urlencode('http://www.google.com'); // -> http%3A%2F%2Fwww.google.com

Это удобно для кодирования часть запроса, но это не может быть использовано на <a href='...'>,

Тем не мение:

$element->setAttribute('href', $newlink); // -> http://www.google.com

будет правильно кодировать строку, чтобы она все еще могла использоваться в href, Причина, по которой он не может кодировать @ потому что он не может сказать, @ является частью запроса или частью userinfo или же email URL (например: mailto:[email protected] или же [email protected])


Решение

  1. Вместо того, чтобы использовать [@MERGEID], ты можешь использовать @@MERGEID@@, Затем вы замените его своим идентификатором позже. Это решение не требует от вас даже использования urlencode,

  2. Если вы настаиваете на использовании urlencode, вы можете просто использовать% 40 вместо @. Итак, ваш код будет таким $newlink = 'http://www.example.com/click/[%40MERGEID]?url=' . $link;

  3. Вы также можете сделать что-то вроде $newlink = 'http://www.example.com/click/' . urlencode('[@MERGEID]') . '?url=' . $link;

5

Другие решения

urlencode функция и rawurlencode в основном основаны на RFC 1738, Однако с 2005 года текущий RFC, используемый для стандарта URI, является RFC 3986,

С другой стороны, расширение DOM использует кодировку UTF-8, которая основана на RFC 3629 . Используйте utf8_encode () и utf8_decode () для работы с текстами в кодировке ISO-8859-1 или Iconv для других кодировок.

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

Вот функция для декодирования URL в соответствии с RFC 3986,

<?php
function myUrlEncode($string) {
$entities = array('%21', '%2A', '%27', '%28', '%29', '%3B', '%3A', '%40', '%26', '%3D', '%2B', '%24', '%2C', '%2F', '%3F', '%25', '%23', '%5B', '%5D');
$replacements = array('!', '*', "'", "(", ")", ";", ":", "@", "&", "=", "+", "$", ",", "/", "?", "%", "#", "[", "]");
return str_replace($entities, $replacements, urldecode($string));
}
?>

PHP Fiddle.


Обновить:

Поскольку UTF8 был использован для кодирования $message:

$dom_document->loadHTML(mb_convert_encoding($message, 'HTML-ENTITIES', 'UTF-8'))

использование urldecode($message) при возврате URL без процентов.

die(urldecode($message) . ' and url encoded version: ' . $urlencodedmerge);
3

Коренная причина вашей проблемы было очень хорошо объяснено с технической точки зрения.

Однако, на мой взгляд, в вашем подходе есть концептуальный недостаток, и это созданный ситуация, которую вы сейчас пытаетесь исправить.

Обрабатывая ваш вход $message через объект DomDocument вы перешли на более высокий уровень абстракции. Неправильно манипулировать как уникальной простой строкой чем-то, что было «продвинуто» в поток HTML.

Вместо того, чтобы пытаться воспроизвести поведение DomDocument, используйте саму библиотеку, чтобы найти, извлечь и заменить значения, представляющие интерес:

$token = 'blah blah [@MERGEID]';
$message = '<a id="' . $token . '" href="' . $token . '"></a>';

$dom = new DOMDocument();
$dom->loadHTML($message);
echo $dom->saveHTML(); // now we have an abstract HTML document

// extract a raw value
$rawstring = $dom->getElementsByTagName('a')->item(0)->getAttribute('href');
// do the low-level fiddling
$newstring = str_replace($token, 'replaced', $rawstring);
// push the new value back into the abstract black box.
$dom->getElementsByTagName('a')->item(0)->setAttribute('href', $newstring);

// less code written, but works all the time
$rawstring = $dom->getElementsByTagName('a')->item(0)->getAttribute('id');
$newstring = str_replace($token, 'replaced', $rawstring);
$dom->getElementsByTagName('a')->item(0)->setAttribute('id', $newstring);

echo $dom->saveHTML();

Как показано выше, сегодня мы пытаемся решить проблему, когда ваш токен находится внутри href, но однажды мы можем захотеть найти и заменить тег в другом месте документа. Чтобы учесть этот случай, не беспокойтесь о том, чтобы ваш низкоуровневый код учитывал HTML.

(альтернативным вариантом будет не загружать DomDocument, пока не будут выполнены все низкоуровневые замены, но я предполагаю, что это не практично)


Полное доказательство концепции:

function searchAndReplace(DOMNode $node, $search, $replace) {
if($node->hasAttributes()) {
foreach ($node->attributes as $attribute) {
$input = $attribute->nodeValue;
$output = str_replace($search, $replace, $input);
$attribute->nodeValue = $output;
}
}

if(!$node instanceof DOMElement) { // this test needs double-checking
$input = $node->nodeValue;
$output = str_replace($search, $replace, $input);
$node->nodeValue = $output;
}

if($node->hasChildNodes()) {
foreach ($node->childNodes as $child) {
searchAndReplace($child, $search, $replace);
}
}
}

$token = '<>&;[@MERGEID]';
$message = '<a/>';

$dom = new DOMDocument();
$dom->loadHTML($message);

$dom->getElementsByTagName('a')->item(0)->setAttribute('id', "foo$token");
$dom->getElementsByTagName('a')->item(0)->setAttribute('href', "http://foo@$token");
$textNode = new DOMText("foo$token");
$dom->getElementsByTagName('a')->item(0)->appendchild($textNode);

echo $dom->saveHTML();

searchAndReplace($dom, $token, '*replaced*');

echo $dom->saveHTML();
2

Если вы используете saveXML() это не испортит кодировку saveHTML() делает:

PHP

//your code...
$message = $dom_document->saveXML();

РЕДАКТИРОВАТЬ: также удалите тег XML:

//this will add an xml tag, so just remove it
$message=preg_replace("/\<\?xml(.*?)\?\>/","",$message);

echo $message;

Выход

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><a href="http://www.example.com/click/[@MERGEID]?url=http://www.google.com?ref=abc&amp;tag=thebottomlink" data-tag="thebottomlink">Google</a></body></html>

Обратите внимание, что оба по-прежнему правильно конвертировать & в &amp;

0

Разве не имеет смысла просто урленкодировать оригинал [@mergeid], кроме того, чтобы сохранить его в первую очередь? Ваш поиск должен соответствовать без str_replace?

$newlink = 'http://www.example.com/click/'.urlencode('[@MERGEID]').'?url=' . $link;

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

0
По вопросам рекламы [email protected]