0

I have the following XPath query that a kind user on SO helped me with:

$xpath->query(".//*[not(self::textarea or self::select or self::input) and contains(., '{{{')]/text()") as $node)

Its purpose is to replace certain placeholders with a value, and correctly catches occurences such as the below that should not be replaced:

<textarea id="testtextarea" name="testtextarea">{{{variable:test}}}</textarea>

And replaces correctly occurrences like this:

<div>{{{variable:test}}}</div>

Now I want to exclude elements that are of type <div> that contain the class name note-editable in that query, e.g., <div class="note-editable mayhaveanotherclasstoo">, in addition to textareas, selects or inputs.

I have tried:

$xpath->query(".//*[not(self::textarea or self::select or self::input) and not(contains(@class, 'note-editable')) and contains(., '{{{')]/text()") as $node)

and:

$xpath->query(".//*[not(self::textarea or self::select or self::input or contains(@class, 'note-editable')) and contains(., '{{{')]/text()") as $node)

I have followed the advice on some questions similar to this: PHP xpath contains class and does not contain class, and I do not get PHP errors, but the note-editable <div> tags are still having their placeholders replaced.

Any idea what's wrong with my attempted queries?

EDIT

Minimum reproducible DOM sample:

<div class="note-editing-area">
    <textarea class="note-codable"></textarea>
    <div class="note-editable panel-body" contenteditable="true" style="height: 350px;">{{{variable:system_url}}</div>
</div>

Code that does the replacement:

$dom = new DOMDocument();
libxml_use_internal_errors(true);
$dom->loadHTML($html);
$xpath = new DOMXpath($dom);
foreach ($xpath->query(".//*[not(self::textarea or self::select or self::input or self::div[contains(@class,'note-editable')]) and contains(., '{{{')]/text()") as $node) {
    $node->nodeValue = preg_replace_callback('~{{{([^:]+):([^}]+)}}}~', function($m) use ($placeholders) {
        return $placeholders[$m[1]][$m[2]] ?? '';
    },
    $node->nodeValue);
}
$html = $dom->saveHTML();
echo html_entity_decode($html);
Kobus Myburgh
  • 1,114
  • 1
  • 17
  • 46

1 Answers1

1

Use this below xpath.

.//*[not(self::textarea or self::select or self::input or self::div[contains(@class,'note-editable')]) and contains(., '{{{')]
supputuri
  • 13,644
  • 2
  • 21
  • 39
  • Unfortunately, that did not work. My question is updated with a minimum reproducible DOM fragment as well as my code that does the replacement. Thank you in advance for your help. – Kobus Myburgh Jul 30 '19 at 00:26
  • I figured out why it is not working -- the
    element is created via JS after the page is rendered. Without that requirement, your answer works. Therefore accepting as correct.
    – Kobus Myburgh Aug 01 '19 at 12:25