I'm trying to replace the word "custom" and replicate it with <span> custom </span>
.
With the str_replace ()
function it works but this also replaces it in the title attribute and I don't want this to happen because the span tag inside the title is an error.
How can I replace the word "custom" without touching the title attribute?
This is my code:
$oldText = "custom";
$newText = "<span>custom</span>";
$string = "<a href='#' title='Products custom'>Products custom</a>";
str_ireplace($oldText, $newText,$string);
This is just one example. The word custom can also be placed in the middle of a string or at the beginning...
Thanks
You'll probably have to use PHP's DOM parser to do that. Writting a regular expression to solve it will just not work for all cases.
I would start off with this Stackoverflow answer and then change it a bit to accomplish what you want to do. As you are replacing custom
by <span>custom</span>
you'll be creating a new DOM element. Replacing the text content won't work because <span>
will be escaped and replaced by <span>
.
So I would do this:
use preg_match_all() with a pattern such as /\bcustom\b/
to get all the offsets of the found items in the text:
// Search for the word, but delimited by word boundaries to
// avoid matching 'custom' in 'customization' or 'customer'.
$pattern = '/\b' . preg_quote($word_to_search) . '\b/';
if (preg_match_all($pattern, $child->wholeText, $matches, PREG_SET_ORDER | PREG_OFFSET_CAPTURE)) {
var_export($matches);
}
convert these offsets in bytes to offsets in chars (this is because UTF-8 can have chars of 1 or n bytes):
function char_offset($string, $byte_offset, $encoding = null)
{
$substr = substr($string, 0, $byte_offset);
return mb_strlen($substr, $encoding ?: mb_internal_encoding());
}
use DOMText::splitText() to split the text nodes into two text nodes with the offset in char unit.
create a <span>
element with DOMDocument::createElement()
$new_text = 'custom'; // or whatever.
$spanElement = $domNode->ownerDocument->createElement('span', $new_text);
insert this span element before the second text node with DOMNode::insertBefore()
correct the second text node to remove the custom
word at the beginning.
But if your case is always in a <a>
tag then you could have a go with something like this: https://regex101.com/r/ksPqxe/1
For the regex explanation, look at the description on the right column. You could remove the i
flag for case-insensitive, if needed. The s
flag is used so that the .
also matches new lines. I had to use the ungreedy search with .*?
instead of .*
. So in the end I used the U
for Ungreedy flag and then used .*
.
This solution will not handle the case of several custom
words in the link. But you'll probably only have it once. If you need that then use one regex to get the text content of the link and then a second one to replace all instances of custom
by <span>custom</span>
.
<?php
$pattern = '/(<a[^>]*>.*)\bcustom\b(.*<\/a>)/isU';
// Or without the ungreedy flag:
//$pattern = '/(<a[^>]*>.*?)\bcustom\b(.*?<\/a>)/is';
$substitution = '$1<span>custom</span>$2';
$inputs = [
"<a href='#' title='Products custom'>Products custom</a>",
'<a href="https://www.customer.com" title="customer" data-type="custom">Custom stuff</a>',
'<a href=\"https://www.customer.com\" title=\"customer"
data-type="custom">Customer stuff</a>',
'<a href="#">customize it!</a>',
];
$results = [];
foreach ($inputs as $input) {
$result = preg_replace($pattern, $substitution, $input);
$results[] = "$input\n$result\n";
}
print implode(str_repeat('-', 80) . "\n", $results);
Output:
<a href='#' title='Products custom'>Products custom</a>
<a href='#' title='Products custom'>Products <span>custom</span></a>
--------------------------------------------------------------------------------
<a href="https://www.customer.com" title="customer" data-type="custom">Custom stuff</a>
<a href="https://www.customer.com" title="customer" data-type="custom"><span>custom</span> stuff</a>
--------------------------------------------------------------------------------
<a href=\"https://www.customer.com\" title=\"customer"
data-type="custom">Customer stuff</a>
<a href=\"https://www.customer.com\" title=\"customer"
data-type="custom">Customer stuff</a>
--------------------------------------------------------------------------------
<a href="#">customize it!</a>
<a href="#">customize it!</a>