3

In the foreach below, I need to parse the $node.TextContent for the keyword and wrap bold tags around it. Is this possible with DOM? How?

$myContent ="<h1>This word should not be replaced: TEST</h1>. But this one should be replaced: test";

$dom = new DOMDocument;
$dom->loadHTML(strtolower($myContent));
$xPath = new DOMXPath($dom);
foreach($xPath->query("//text()[contains(.,'test') and not(ancestor::h1)]") as $node)
    {
        /*need to do a replace on each occurrence of the word "test"
         in $node->textContent here so that it becomes <b>test</b>. How? */
    }

echo $dom->saveHTML should yield:

<h1>This word should not be replaced: TEST</h1>. 
But this one should be replaced: <b>test</b>"
Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431
Scott B
  • 38,833
  • 65
  • 160
  • 266
  • The output you want to achieve cannot be achieved with `$dom->saveHTML` as long as you `strtolower` the input fed to `DOM`. See my answer to your question here [how to do case insensitive XPath node-tests with PHP](http://stackoverflow.com/questions/4046022/parse-content-for-appearance-of-a-keyword-inside-h1-h2-and-h3-heading-tags) or a [pure XPath 1.0 solution here](http://stackoverflow.com/questions/3238989/case-insensitive-xpath-searching-in-php) – Gordon Nov 05 '10 at 23:01
  • Good question, +1. Seemy answer for a complete XSLT 1.0 solution. :) – Dimitre Novatchev Nov 05 '10 at 23:14

1 Answers1

1

Edit: As @LarsH has noticed, I hadn't paid attention to the requirement that the replacement should be in bold.

There are two easy ways to correct this:

.1. In the transformation replace:

  <xsl:value-of select="$pRep"/>

with

  <b><xsl:value-of select="$pRep"/></b>

.2. Pass as the value of the pReplacement parameter not just "ABC" but <b>ABC</b>

This XSLT 1.0 transformation:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:param name="pTarget" select="'test'"/>
 <xsl:param name="pReplacement" select="'ABC'"/>

 <xsl:variable name="vCaps"     select="'ABCDEFGHIJKLMNOPQRSTUVWXYZ'"/>
 <xsl:variable name="vLowecase" select="'abcdefghijklmnopqrstuvwxyz'"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
    <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="text()[not(ancestor::h1)]">
  <xsl:call-template name="replaceCI">
   <xsl:with-param name="pText" select="."/>
  </xsl:call-template>
 </xsl:template>

 <xsl:template name="replaceCI">
  <xsl:param name="pText"/>
  <xsl:param name="pTargetText" select="$pTarget"/>
  <xsl:param name="pRep" select="$pReplacement"/>

  <xsl:variable name="vLowerText"
       select="translate($pText, $vCaps, $vLowecase)"/>
  <xsl:choose>
   <xsl:when test=
   "not(contains($vLowerText, $pTargetText))">
     <xsl:value-of select="$pText"/>
   </xsl:when>
   <xsl:otherwise>
    <xsl:variable name="vOffset" select=
    "string-length(substring-before($vLowerText, $pTargetText))"/>

    <xsl:value-of select="substring($pText,1,$vOffset)"/>

    <xsl:value-of select="$pRep"/>

    <xsl:call-template name="replaceCI">
     <xsl:with-param name="pText" select=
     "substring($pText, $vOffset + string-length($pTargetText)+1)"/>
     <xsl:with-param name="pTargetText" select="$pTargetText"/>
     <xsl:with-param name="pRep" select="$pRep"/>
    </xsl:call-template>
   </xsl:otherwise>
  </xsl:choose>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document (corrected to be well-formed):

<html>
<h1>This word should not be replaced: TEST</h1>.
 But this one should be replaced: test
</html>

produces the wanted result:

<html>
<h1>This word should not be replaced: TEST</h1>.
 But this one should be replaced: ABC
</html>

Do note:

  1. This is a generic transformation that accepts as parameters the target and the replacement text.

  2. The replacement is case-incensitive, but we suppose that the target parameter is provided in lowercase.

  3. It is even much easier to solve this with XSLT 2.0.

Dimitre Novatchev
  • 240,661
  • 26
  • 293
  • 431