Как экранировать кавычки в выражениях XPath

Чтобы не было проблем с кавычками, надо использовать функцию из XPath concat(), где одинарные кавычки писать в двойных, а двойные в одинарных. Дебилизм, конечно, но я приципиально не хочу хранить языковые строки в базе (они у меня в xml).

А вот функция для PHP, которая искомую строку превращает в соответствующее выражение с concat, например, из строки I'm using " она сделает такое: concat("I","'","m using ",'"').

// Solves problem with ' and " in xpath expressions.
// As it seems, there is only one way to solve this:
// use concat() function from XPath, enclose the single quotes in double quotes,
// and the double quotes in single quotes.
//
// Example (from http://kushalm.com/the-perils-of-xpath-expressions-specifically-escaping-quotes):
// "books/book[@publisher = " + "concat('Single', "'", 'quote. Double', '"', 'quote.')]";
// looks for a publisher called Single'quote. Double"quote
function escape_xpath_subexpression($s)
{
    // our quotes:
    $specials = array('"', "'");

    // how they are appear in concat() function:
    $specials_replaced = array('\'"\'', '"\'"');

    // if no special characters, it's the simplest case
    $is_found = false;

    foreach ($specials as $sp)
        if (mb_strpos($s, $sp, 0, 'utf-8') !== false)
        {
            $is_found = true;
            break;
        }

    if (!$is_found)
        return '"'.$s.'"';

    // otherwise processing string in cool way
    $substs = array();
    foreach ($specials as $i => $sp)
        $substs[] = "##$i##";

    $s = str_replace($specials, $substs, $s);
    $substrings = preg_split('/(##\d+##)/', $s, -1, PREG_SPLIT_DELIM_CAPTURE);

    foreach ($substrings as $i => $substr)
    {
        $substr = str_replace($substs, $specials, $substr);

        if (($idx = array_search($substr, $specials)) === false)
            $substrings[$i] = '"'.$substr.'"';
        else
            $substrings[$i] = $specials_replaced[$idx];
    }

    return 'concat('.implode(',', $substrings).')';
}

XPath in PHP, insensitive search

Для поиска по данным в XML без учета регистра можно использовать встроенную в XPath функцию translate(). Но ее особо не попользуешь, так как надо вручную указывать заглавные символы, которые соответствуют встреченным строчным. Если все происходит в PHP, можно все сделать намного легче:

$q = 'cлово';
$q_upper = mb_strtoupper($q, 'utf-8');
$nodes = $xpath->query("//*[contains(translate(text(),'$q','$q_upper'), '$q_upper')]");