protected static function _tokenize($expression_src)
    {
        // Child selectors
        $expression = str_replace('>', '/', $expression_src);

        // IDs
        $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', "[@id='$1']", $expression);
        $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression);

        // arbitrary attribute strict equality
        $expression = preg_replace_callback(
            '|\[@?([a-z0-9_-]*)([~\*\^\$\|\!])?=([^\[]+)\]|i',
            static function ($matches) {
                $attr = strtolower($matches[1]);
                $type = $matches[2];
                $val = trim($matches[3], "'\" \t");

                $field = ($attr === '' ? 'text()' : '@' . $attr);

                $items = [];
                foreach (explode(strpos($val, '|') !== false ? '|' : '&', $val) as $word) {
                    if ($type === '') {
                        $items[] = "$field='$word'";
                    } elseif ($type === '~') {
                        $items[] = "contains(concat(' ', normalize-space($field), ' '), ' $word ')";
                    } elseif ($type === '|') {
                        $item = "$field='$word' or starts-with($field,'$word-')";
                        $items[] = $word === $val ? $item : "($item)";
                    } elseif ($type === '!') {
                        $item = "not($field) or $field!='$word'";
                        $items[] = $word === $val ? $item : "($item)";
                    } else {
                        $items[] = ['*' => 'contains', '^' => 'starts-with', '$' => 'ends-with'][$type] . "($field, '$word')";
                    }
                }
                return '[' . implode(strpos($val, '|') !== false ? ' or ' : ' and ', $items) . ']';
            },
            $expression
        );

        //attribute contains specified content
        $expression = preg_replace_callback(
            '|\[(!?)([a-z][a-z0-9_-\|&]*)\]|i',
            static function ($matches) {
                $val = $matches[2];
                $op = strpos($val, '|') !== false ? '|' : '&';

                $items = [];
                foreach (explode($op, $val) as $word) {
                    $items[] = '@' . $word;
                }

                $r = '[' . implode($op === '|' ? ' or ' : ' and ', $items) . ']';
                return $matches[1] === '!' ? "not($r)" : $r;
            },
            $expression
        );
        // Classes
        if (false === strpos($expression, '[@')) {
            $expression = preg_replace(
                '|\.([a-z][a-z0-9_-]*)|i',
                "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]",
                $expression
            );
        }

        /** ZF-9764 -- remove double asterisk */
        $expression = str_replace('**', '*', $expression);

        return $expression;
    }
protected static function _tokenize($expression_src)
    {
        // Child selectors
        $expression = str_replace('>', '/', $expression_src);

        // IDs
        $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', "[@id='$1']", $expression);
        $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression);

        // arbitrary attribute strict equality
        $expression = preg_replace_callback(
            '|\[@?([a-z0-9_-]*)([~\*\^\$\|\!])?=([^\[]+)\]|i',
            static function ($matches) {
                $attr = strtolower($matches[1]);
                $type = $matches[2];
                $val = trim($matches[3], "'\" \t");

                $field = ($attr === '' ? 'text()' : '@' . $attr);

                $items = [];
                foreach (explode(strpos($val, '|') !== false ? '|' : '&', $val) as $word) {
                    if ($type === '') {
                        $items[] = "$field='$word'";
                    } elseif ($type === '~') {
                        $items[] = "contains(concat(' ', normalize-space($field), ' '), ' $word ')";
                    } elseif ($type === '|') {
                        $item = "$field='$word' or starts-with($field,'$word-')";
                        $items[] = $word === $val ? $item : "($item)";
                    } elseif ($type === '!') {
                        $item = "not($field) or $field!='$word'";
                        $items[] = $word === $val ? $item : "($item)";
                    } else {
                        $items[] = ['*' => 'contains', '^' => 'starts-with', '$' => 'ends-with'][$type] . "($field, '$word')";
                    }
                }
                return '[' . implode(strpos($val, '|') !== false ? ' or ' : ' and ', $items) . ']';
            },
            $expression
        );

        //attribute contains specified content
        $expression = preg_replace_callback(
            '|\[(!?)([a-z][a-z0-9_-\|&]*)\]|i',
            static function ($matches) {
                $val = $matches[2];
                $op = strpos($val, '|') !== false ? '|' : '&';

                $items = [];
                foreach (explode($op, $val) as $word) {
                    $items[] = '@' . $word;
                }

                $r = '[' . implode($op === '|' ? ' or ' : ' and ', $items) . ']';
                return $matches[1] === '!' ? "not($r)" : $r;
            },
            $expression
        );
        // Classes
        if (false === strpos($expression, '[@')) {
            $expression = preg_replace(
                '|\.([a-z][a-z0-9_-]*)|i',
                "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]",
                $expression
            );
        }

        /** ZF-9764 -- remove double asterisk */
        $expression = str_replace('**', '*', $expression);

        return $expression;
    }
protected static function _tokenize ( $ expression_src ) { // Child selectors $ expression = str_replace ( '>' , '/' , $ expression_src ) ; // IDs $ expression = preg_replace ( '|#([a-z][a-z0-9_-]*)|i' , "[@id='$1']" , $ expression ) ; $ expression = preg_replace ( '|(?<![a-z0-9_-])(\[@id=)|i' , '*$1' , $ expression ) ; // arbitrary attribute strict equality $ expression = preg_replace_callback ( '|\[@?([a-z0-9_-]*)([~\*\^\$\|\!])?=([^\[]+)\]|i' , static function ( $ matches ) { $ attr = strtolower ( $ matches [ 1 ] ) ; $ type = $ matches [ 2 ] ; $ val = trim ( $ matches [ 3 ] , "'\" \t" ) ; $ field = ( $ attr === '' ? 'text()' : '@' . $ attr ) ; $ items = [ ] ; foreach ( explode ( strpos ( $ val , '|' ) !== false ? '|' : '&' , $ val ) as $ word ) { if ( $ type === '' ) { $ items [ ] = "$field='$word'" ; } elseif ( $ type === '~' ) { $ items [ ] = "contains(concat(' ', normalize-space($field), ' '), ' $word ')" ; } elseif ( $ type === '|' ) { $ item = "$field='$word' or starts-with($field,'$word-')" ; $ items [ ] = $ word === $ val ? $ item : "($item)" ; } elseif ( $ type === '!' ) { $ item = "not($field) or $field!='$word'" ; $ items [ ] = $ word === $ val ? $ item : "($item)" ; } else { $ items [ ] = [ '*' => 'contains' , '^' => 'starts-with' , '$' => 'ends-with' ] [ $ type ] . "($field, '$word')" ; } } return '[' . implode ( strpos ( $ val , '|' ) !== false ? ' or ' : ' and ' , $ items ) . ']' ; } , $ expression ) ; //attribute contains specified content $ expression = preg_replace_callback ( '|\[(!?)([a-z][a-z0-9_-\|&]*)\]|i' , static function ( $ matches ) { $ val = $ matches [ 2 ] ; $ op = strpos ( $ val , '|' ) !== false ? '|' : '&' ; $ items = [ ] ; foreach ( explode ( $ op , $ val ) as $ word ) { $ items [ ] = '@' . $ word ; } $ r = '[' . implode ( $ op === '|' ? ' or ' : ' and ' , $ items ) . ']' ; return $ matches [ 1 ] === '!' ? "not($r)" : $ r ; } , $ expression ) ; // Classes if ( false === strpos ( $ expression , '[@' ) ) { $ expression = preg_replace ( '|\.([a-z][a-z0-9_-]*)|i' , "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]" , $ expression ) ; } /** ZF-9764 -- remove double asterisk */ $ expression = str_replace ( '**' , '*' , $ expression ) ; return $ expression ; }
Tokenize CSS expressions to XPath

@param string $expression_src

@return string
Tokenize CSS expressions to XPath