XPath包含()搜索完全匹配

Is it possible to search a DOMDocument object with fn:contains and return true on only an exact match for a word?

I have a text replacement snippet that I did not write myself that does internal link replacements for keywords. But as written it also replaces partial words instead of only the full word.

Here is the snippet:

$autolinks = $this->config->get('autolinks');
if (isset($autolinks) && (strpos($this->data['description'], 'iframe') == false) 
        && (strpos($this->data['description'], 'object') == false)):
    $xdescription = mb_convert_encoding(html_entity_decode($this->data['description'], ENT_COMPAT, "UTF-8"), 'HTML-ENTITIES', "UTF-8"); 
    libxml_use_internal_errors(true);
    $dom = new DOMDocument;             
    $dom->loadHTML('<div>'.$xdescription.'</div>');             
    libxml_use_internal_errors(false);
    $xpath = new DOMXPath($dom);
    foreach ($autolinks as $autolink):
        $keyword    = $autolink['keyword'];
        $xlink  = mb_convert_encoding(html_entity_decode($autolink['link'], ENT_COMPAT, "UTF-8"), 'HTML-ENTITIES', "UTF-8");
        $target     = $autolink['target'];
        $tooltip    = isset($autolink['tooltip']);                          
        $pTexts     = $xpath->query(
            sprintf('///text()[contains(., "%s")]', $keyword)
        );
        foreach ($pTexts as $pText):
            $this->parseText($pText, $keyword, $dom, $xlink, $target, $tooltip);
        endforeach;
    endforeach;
    $this->data['description'] = $dom->saveXML($dom->documentElement);
endif;

In example:

If my keyword is "massage" *massage*r is partially matched and converted to a link, when only the whole word massage should be converted, not massager.

This actually turned out to be incredibly simple, I just added a space onto the end of the $keyword variable so now it only returns true when the entire word is found.

foreach ($autolinks as $autolink):
    $keyword    = trim($autolink['keyword']) . ' ';
    $xlink      = mb_convert_encoding(html_entity_decode($autolink['link'], ENT_COMPAT, "UTF-8"), 'HTML-ENTITIES', "UTF-8");
    $target     = $autolink['target'];
    $tooltip    = isset($autolink['tooltip']);                          
    $pTexts     = $xpath->query(
        sprintf('///text()[contains(., "%s")]', $keyword)
    );
    foreach ($pTexts as $pText):
        $this->parseText($pText, $keyword, $dom, $xlink, $target, $tooltip);
    endforeach;
endforeach;

thank you to everyone who tried to help.

You should use fn:matches instead of fn:contains. This allows you to do matching with regular expressions. Then you can include word boundaries with \b.

sprintf('///text()[matches(., "\b%s\b")]', $keyword)

Note that this does not affect whatever your function parseText is doing. So while <Tagname>This is a sentence containing the word massager.</Tagname> will be unaffected, I make no guarantee what will happen to <Tagname>The massager give the customer a massage.</Tagname>. To make sure that this is handled properly your parsetext function will need to be modified. Possibly in a similar manner as above.

Note also that the modifications you might need to make to parsetext means that the above change becomes unecessary.

Text manipulation in XSLT 1.0 is very limited, but if you can't move to 2.0 (why not?) then translate() often comes to the rescue. Use translate() to replace all common punctuation characters by spaces, use concat() to add a space fore and aft, and then test for contains(' massage ') (note the spaces).

When matches(), ends-with() are not supported, you can use starts-with() and string-length() to get around.

Example:

[starts-with(.,'$var') and string-length(.)=string-length('$var')]

This is equivalent to matches().