2019-04-18 17:12
浏览 342


I'm trying to create a search function allowing partial matching by song title or genre using Xpath.

This is my XML file:

<?xml version="1.0" encoding="UTF-8"?>
    <songtitle>I Say a Little Prayer</songtitle>
    <artist>Aretha Franklin</artist>
    <songtitle>Every Breath You Take</songtitle>
    <artist>The Police</artist>
    <songtitle>London Calling</songtitle>
    <artist>The Clash</artist>

and this is my search function so far:

function searchSong($words){
    global $xml;

        foreach($words as $word){
            //$query = "//playlist/item[contains(songtitle/genre, '{$word}')]";
            $query = "//playlist/item[(songtitle[contains('{$word}')]) and (genre[contains('{$word}')])]";
            $result = $xml->xpath($query);


Calling the function searchSong(array("take", "soul")) should return the second and first song from XML file, but the array is always empty.

图片转代码服务由CSDN问答提供 功能建议



 &lt;?xml version =“1.0”encoding =“UTF  -8“?&gt; 
&lt; item&gt; 
&lt; songid&gt; USAT29902236&lt; / songid&gt; 
&lt; songtitle&gt;我说小祷告&lt; / songtitle&gt; 
&lt; artist&gt; Aretha  Franklin&lt; / artist&gt; 
&lt; genre&gt; Soul&lt; / genre&gt; 
&lt; link&gt;< / link&gt; \  n&lt; releaseyear&gt; 1968&lt; / releaseyear&gt; 
&lt; / item&gt; 
&lt; item&gt; 
&lt; songid&gt; GBAAM8300001&lt; / songid&gt; 
&lt; songtitle&gt;您的每次呼吸&lt; / songtitle&gt; 
  &lt; artist&gt; The Police&lt; / artist&gt; 
&lt; genre&gt; Pop / Rock&lt; / genre&gt; 
&lt; link&gt;  / B000008JI6&lt; / link&gt; 
&lt; releaseyear&gt; 1  983&lt; / releaseyear&gt; 
&lt; / item&gt; 
&lt; item&gt; 
&lt; songid&gt; GBBBN7902002&lt; / songid&gt; 
&lt; songtitle&gt; London Calling&lt; / songtitle&gt; 
&lt; artist&gt; The Clash&lt;  / artist&gt; 
&lt; genre&gt; Post-punk&lt; / genre&gt; 
&lt; link&gt;< / link&gt; 
&lt; releaseyear&gt  ; 1979&lt; / releaseyear&gt; 
&lt; / item&gt; 
&lt; / playlist&gt; 

这是我迄今为止的搜索功能: \ n

  function searchSong($ words){
 global $ xml; 
 if(!empty($ words)){
 foreach(  $ words as $ word){
 // $ query =“// playlist / item [contains(songtitle / genre,'{$ word}')]”; 
 $ query =“// playlist / item [(  songtitle [contains('{$ word}')])和(genre [contains('{$ word}')])]“; 
 $ result = $ xml-&gt; xpath($ query); 
 print_r($ result); 

调用函数 searchSong(array(“take”) “,”soul“))应该从XML文件中返回第二首和第一首歌曲,但数组总是空的。

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

2条回答 默认 最新

  • dpp34603 2019-04-18 17:33

    A few errors here: use of and instead of or, assuming searches are case-insensitive, and passing incorrect number of parameters to contains. The last would have triggered PHP warnings if you were looking for them. Also, you're only ever returning the last item you search for.

    Case insensitive searches in XPath 1.0 (which is all PHP supports) are a huge pain to do:

    $result = $xml->query(
        "//playlist/item[(songtitle[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{$word}')]) or (genre[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{$word}')])]"

    This assumes you've taken your search terms and converted them to lower-case already. For example:

    function searchSong($xpath, ...$words)
        $return = [];
        foreach($words as $word) {
            $word = strtolower($word);
            $q = "//playlist/item[(songtitle[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{$word}')]) or (genre[contains(translate(., 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), '{$word}')])]";
            $result = $xpath->query($q);
            foreach($result as $node) {
                $return[] = $node;
        return $return;
    打赏 评论
  • douzi1986 2019-04-20 20:11

    In DOM you have another option, you can register PHP functions and use them in Xpath expressions.

    So write a function that does the matching logic:

    function contentContains($nodes, ...$needles) {
        // ICUs transliterator is really convenient, 
        // lets get one for lowercase and replacing umlauts 
        $transliterator = \Transliterator::create('Any-Lower; Latin-ASCII');
        foreach ($nodes as $node) {
            $haystack = $transliterator->transliterate($node->nodeValue);
            foreach ($needles as $needle) {
                if (FALSE !== strpos($haystack, $needle)) {
                    return TRUE;
        return FALSE;

    Now you can register it on an DOMXpath instance:

    $document = new DOMDocument();
    $xpath = new DOMXpath($document);
    $xpath->registerNamespace("php", "");
    $expression = "//item[
        php:function('contentContains', songtitle, 'take', 'soul') or
        php:function('contentContains', genre, 'take', 'soul') 
    $result = [];
    foreach ($xpath->evaluate($expression) as $node) {
        // read values as strings
        $result[] = [
            'title' => $xpath->evaluate('string(songtitle)', $node),
            'gerne' => $xpath->evaluate('string(genre)', $node),
            // ...
    打赏 评论

相关推荐 更多相似问题