web/lib/Zend/Dom/Query/Css2Xpath.php
changeset 64 162c1de6545a
parent 19 1c2f13fd785c
child 68 ecaf28ffe26e
equal deleted inserted replaced
63:5b37998e522e 64:162c1de6545a
       
     1 <?php
       
     2 /**
       
     3  * Zend Framework
       
     4  *
       
     5  * LICENSE
       
     6  *
       
     7  * This source file is subject to the new BSD license that is bundled
       
     8  * with this package in the file LICENSE.txt.
       
     9  * It is also available through the world-wide-web at this URL:
       
    10  * http://framework.zend.com/license/new-bsd
       
    11  * If you did not receive a copy of the license and are unable to
       
    12  * obtain it through the world-wide-web, please send an email
       
    13  * to license@zend.com so we can send you a copy immediately.
       
    14  *
       
    15  * @category   Zend
       
    16  * @package    Zend_Dom
       
    17  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    18  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    19  */
       
    20 
       
    21 /**
       
    22  * Transform CSS selectors to XPath
       
    23  *
       
    24  * @package    Zend_Dom
       
    25  * @subpackage Query
       
    26  * @copyright  Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
       
    27  * @license    http://framework.zend.com/license/new-bsd     New BSD License
       
    28  * @version    $Id: Css2Xpath.php 22044 2010-04-28 19:58:29Z matthew $
       
    29  */
       
    30 class Zend_Dom_Query_Css2Xpath
       
    31 {
       
    32     /**
       
    33      * Transform CSS expression to XPath
       
    34      *
       
    35      * @param  string $path
       
    36      * @return string
       
    37      */
       
    38     public static function transform($path)
       
    39     {
       
    40         $path = (string) $path;
       
    41         if (strstr($path, ',')) {
       
    42             $paths       = explode(',', $path);
       
    43             $expressions = array();
       
    44             foreach ($paths as $path) {
       
    45                 $xpath = self::transform(trim($path));
       
    46                 if (is_string($xpath)) {
       
    47                     $expressions[] = $xpath;
       
    48                 } elseif (is_array($xpath)) {
       
    49                     $expressions = array_merge($expressions, $xpath);
       
    50                 }
       
    51             }
       
    52             return implode('|', $expressions);
       
    53         }
       
    54 
       
    55         $paths    = array('//');
       
    56         $path     = preg_replace('|\s+>\s+|', '>', $path);
       
    57         $segments = preg_split('/\s+/', $path);
       
    58         foreach ($segments as $key => $segment) {
       
    59             $pathSegment = self::_tokenize($segment);
       
    60             if (0 == $key) {
       
    61                 if (0 === strpos($pathSegment, '[contains(')) {
       
    62                     $paths[0] .= '*' . ltrim($pathSegment, '*');
       
    63                 } else {
       
    64                     $paths[0] .= $pathSegment;
       
    65                 }
       
    66                 continue;
       
    67             }
       
    68             if (0 === strpos($pathSegment, '[contains(')) {
       
    69                 foreach ($paths as $key => $xpath) {
       
    70                     $paths[$key] .= '//*' . ltrim($pathSegment, '*');
       
    71                     $paths[]      = $xpath . $pathSegment;
       
    72                 }
       
    73             } else {
       
    74                 foreach ($paths as $key => $xpath) {
       
    75                     $paths[$key] .= '//' . $pathSegment;
       
    76                 }
       
    77             }
       
    78         }
       
    79 
       
    80         if (1 == count($paths)) {
       
    81             return $paths[0];
       
    82         }
       
    83         return implode('|', $paths);
       
    84     }
       
    85 
       
    86     /**
       
    87      * Tokenize CSS expressions to XPath
       
    88      *
       
    89      * @param  string $expression
       
    90      * @return string
       
    91      */
       
    92     protected static function _tokenize($expression)
       
    93     {
       
    94         // Child selectors
       
    95         $expression = str_replace('>', '/', $expression);
       
    96 
       
    97         // IDs
       
    98         $expression = preg_replace('|#([a-z][a-z0-9_-]*)|i', '[@id=\'$1\']', $expression);
       
    99         $expression = preg_replace('|(?<![a-z0-9_-])(\[@id=)|i', '*$1', $expression);
       
   100 
       
   101         // arbitrary attribute strict equality
       
   102         $expression = preg_replace_callback(
       
   103             '|\[([a-z0-9_-]+)=[\'"]([^\'"]+)[\'"]\]|i',
       
   104             array(__CLASS__, '_createEqualityExpression'),
       
   105             $expression
       
   106         );
       
   107 
       
   108         // arbitrary attribute contains full word
       
   109         $expression = preg_replace_callback(
       
   110             '|\[([a-z0-9_-]+)~=[\'"]([^\'"]+)[\'"]\]|i',
       
   111             array(__CLASS__, '_normalizeSpaceAttribute'),
       
   112             $expression
       
   113         );
       
   114 
       
   115         // arbitrary attribute contains specified content
       
   116         $expression = preg_replace_callback(
       
   117             '|\[([a-z0-9_-]+)\*=[\'"]([^\'"]+)[\'"]\]|i',
       
   118             array(__CLASS__, '_createContainsExpression'),
       
   119             $expression
       
   120         );
       
   121 
       
   122         // Classes
       
   123         $expression = preg_replace(
       
   124             '|\.([a-z][a-z0-9_-]*)|i', 
       
   125             "[contains(concat(' ', normalize-space(@class), ' '), ' \$1 ')]", 
       
   126             $expression
       
   127         );
       
   128 
       
   129         /** ZF-9764 -- remove double asterix */
       
   130         $expression = str_replace('**', '*', $expression);
       
   131 
       
   132         return $expression;
       
   133     }
       
   134 
       
   135     /**
       
   136      * Callback for creating equality expressions
       
   137      * 
       
   138      * @param  array $matches 
       
   139      * @return string
       
   140      */
       
   141     protected static function _createEqualityExpression($matches)
       
   142     {
       
   143         return '[@' . strtolower($matches[1]) . "='" . $matches[2] . "']";
       
   144     }
       
   145 
       
   146     /**
       
   147      * Callback for creating expressions to match one or more attribute values
       
   148      * 
       
   149      * @param  array $matches 
       
   150      * @return string
       
   151      */
       
   152     protected static function _normalizeSpaceAttribute($matches)
       
   153     {
       
   154         return "[contains(concat(' ', normalize-space(@" . strtolower($matches[1]) . "), ' '), ' " 
       
   155              . $matches[2] . " ')]";
       
   156     }
       
   157 
       
   158     /**
       
   159      * Callback for creating a strict "contains" expression
       
   160      * 
       
   161      * @param  array $matches 
       
   162      * @return string
       
   163      */
       
   164     protected static function _createContainsExpression($matches)
       
   165     {
       
   166         return "[contains(@" . strtolower($matches[1]) . ", '" 
       
   167              . $matches[2] . "')]";
       
   168     }
       
   169 }