PageRenderTime 53ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/protected/extensions/doctrine/vendors/Symfony/Component/CssSelector/Node/FunctionNode.php

https://bitbucket.org/NordLabs/yiidoctrine
PHP | 295 lines | 148 code | 41 blank | 106 comment | 43 complexity | 11f25a29085bb491dd19e61be025c90d MD5 | raw file
Possible License(s): BSD-2-Clause, LGPL-2.1, BSD-3-Clause
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\CssSelector\Node;
  11. use Symfony\Component\CssSelector\Exception\ParseException;
  12. use Symfony\Component\CssSelector\XPathExpr;
  13. /**
  14. * FunctionNode represents a "selector:name(expr)" node.
  15. *
  16. * This component is a port of the Python lxml library,
  17. * which is copyright Infrae and distributed under the BSD license.
  18. *
  19. * @author Fabien Potencier <fabien@symfony.com>
  20. */
  21. class FunctionNode implements NodeInterface
  22. {
  23. static protected $unsupported = array('target', 'lang', 'enabled', 'disabled');
  24. protected $selector;
  25. protected $type;
  26. protected $name;
  27. protected $expr;
  28. /**
  29. * Constructor.
  30. *
  31. * @param NodeInterface $selector The XPath expression
  32. * @param string $type
  33. * @param string $name
  34. * @param XPathExpr $expr
  35. */
  36. public function __construct($selector, $type, $name, $expr)
  37. {
  38. $this->selector = $selector;
  39. $this->type = $type;
  40. $this->name = $name;
  41. $this->expr = $expr;
  42. }
  43. /**
  44. * {@inheritDoc}
  45. */
  46. public function __toString()
  47. {
  48. return sprintf('%s[%s%s%s(%s)]', __CLASS__, $this->selector, $this->type, $this->name, $this->expr);
  49. }
  50. /**
  51. * {@inheritDoc}
  52. * @throws ParseException When unsupported or unknown pseudo-class is found
  53. */
  54. public function toXpath()
  55. {
  56. $selPath = $this->selector->toXpath();
  57. if (in_array($this->name, self::$unsupported)) {
  58. throw new ParseException(sprintf('The pseudo-class %s is not supported', $this->name));
  59. }
  60. $method = '_xpath_'.str_replace('-', '_', $this->name);
  61. if (!method_exists($this, $method)) {
  62. throw new ParseException(sprintf('The pseudo-class %s is unknown', $this->name));
  63. }
  64. return $this->$method($selPath, $this->expr);
  65. }
  66. /**
  67. * undocumented function
  68. *
  69. * @param XPathExpr $xpath
  70. * @param mixed $expr
  71. * @param Boolean $last
  72. * @param Boolean $addNameTest
  73. *
  74. * @return XPathExpr
  75. */
  76. protected function _xpath_nth_child($xpath, $expr, $last = false, $addNameTest = true)
  77. {
  78. list($a, $b) = $this->parseSeries($expr);
  79. if (!$a && !$b && !$last) {
  80. // a=0 means nothing is returned...
  81. $xpath->addCondition('false() and position() = 0');
  82. return $xpath;
  83. }
  84. if ($addNameTest) {
  85. $xpath->addNameTest();
  86. }
  87. $xpath->addStarPrefix();
  88. if ($a == 0) {
  89. if ($last) {
  90. $b = sprintf('last() - %s', $b);
  91. }
  92. $xpath->addCondition(sprintf('position() = %s', $b));
  93. return $xpath;
  94. }
  95. if ($last) {
  96. // FIXME: I'm not sure if this is right
  97. $a = -$a;
  98. $b = -$b;
  99. }
  100. if ($b > 0) {
  101. $bNeg = -$b;
  102. } else {
  103. $bNeg = sprintf('+%s', -$b);
  104. }
  105. if ($a != 1) {
  106. $expr = array(sprintf('(position() %s) mod %s = 0', $bNeg, $a));
  107. } else {
  108. $expr = array();
  109. }
  110. if ($b >= 0) {
  111. $expr[] = sprintf('position() >= %s', $b);
  112. } elseif ($b < 0 && $last) {
  113. $expr[] = sprintf('position() < (last() %s)', $b);
  114. }
  115. $expr = implode($expr, ' and ');
  116. if ($expr) {
  117. $xpath->addCondition($expr);
  118. }
  119. return $xpath;
  120. /* FIXME: handle an+b, odd, even
  121. an+b means every-a, plus b, e.g., 2n+1 means odd
  122. 0n+b means b
  123. n+0 means a=1, i.e., all elements
  124. an means every a elements, i.e., 2n means even
  125. -n means -1n
  126. -1n+6 means elements 6 and previous */
  127. }
  128. /**
  129. * undocumented function
  130. *
  131. * @param XPathExpr $xpath
  132. * @param XPathExpr $expr
  133. *
  134. * @return XPathExpr
  135. */
  136. protected function _xpath_nth_last_child($xpath, $expr)
  137. {
  138. return $this->_xpath_nth_child($xpath, $expr, true);
  139. }
  140. /**
  141. * undocumented function
  142. *
  143. * @param XPathExpr $xpath
  144. * @param XPathExpr $expr
  145. *
  146. * @return XPathExpr
  147. */
  148. protected function _xpath_nth_of_type($xpath, $expr)
  149. {
  150. if ($xpath->getElement() == '*') {
  151. throw new ParseException('*:nth-of-type() is not implemented');
  152. }
  153. return $this->_xpath_nth_child($xpath, $expr, false, false);
  154. }
  155. /**
  156. * undocumented function
  157. *
  158. * @param XPathExpr $xpath
  159. * @param XPathExpr $expr
  160. *
  161. * @return XPathExpr
  162. */
  163. protected function _xpath_nth_last_of_type($xpath, $expr)
  164. {
  165. return $this->_xpath_nth_child($xpath, $expr, true, false);
  166. }
  167. /**
  168. * undocumented function
  169. *
  170. * @param XPathExpr $xpath
  171. * @param XPathExpr $expr
  172. *
  173. * @return XPathExpr
  174. */
  175. protected function _xpath_contains($xpath, $expr)
  176. {
  177. // text content, minus tags, must contain expr
  178. if ($expr instanceof ElementNode) {
  179. $expr = $expr->formatElement();
  180. }
  181. // FIXME: lower-case is only available with XPath 2
  182. //$xpath->addCondition(sprintf('contains(lower-case(string(.)), %s)', XPathExpr::xpathLiteral(strtolower($expr))));
  183. $xpath->addCondition(sprintf('contains(string(.), %s)', XPathExpr::xpathLiteral($expr)));
  184. // FIXME: Currently case insensitive matching doesn't seem to be happening
  185. return $xpath;
  186. }
  187. /**
  188. * undocumented function
  189. *
  190. * @param XPathExpr $xpath
  191. * @param XPathExpr $expr
  192. *
  193. * @return XPathExpr
  194. */
  195. protected function _xpath_not($xpath, $expr)
  196. {
  197. // everything for which not expr applies
  198. $expr = $expr->toXpath();
  199. $cond = $expr->getCondition();
  200. // FIXME: should I do something about element_path?
  201. $xpath->addCondition(sprintf('not(%s)', $cond));
  202. return $xpath;
  203. }
  204. /**
  205. * Parses things like '1n+2', or 'an+b' generally, returning (a, b)
  206. *
  207. * @param mixed $s
  208. *
  209. * @return array
  210. */
  211. protected function parseSeries($s)
  212. {
  213. if ($s instanceof ElementNode) {
  214. $s = $s->formatElement();
  215. }
  216. if (!$s || '*' == $s) {
  217. // Happens when there's nothing, which the CSS parser thinks of as *
  218. return array(0, 0);
  219. }
  220. if (is_string($s)) {
  221. // Happens when you just get a number
  222. return array(0, $s);
  223. }
  224. if ('odd' == $s) {
  225. return array(2, 1);
  226. }
  227. if ('even' == $s) {
  228. return array(2, 0);
  229. }
  230. if ('n' == $s) {
  231. return array(1, 0);
  232. }
  233. if (false === strpos($s, 'n')) {
  234. // Just a b
  235. return array(0, intval((string) $s));
  236. }
  237. list($a, $b) = explode('n', $s);
  238. if (!$a) {
  239. $a = 1;
  240. } elseif ('-' == $a || '+' == $a) {
  241. $a = intval($a.'1');
  242. } else {
  243. $a = intval($a);
  244. }
  245. if (!$b) {
  246. $b = 0;
  247. } elseif ('-' == $b || '+' == $b) {
  248. $b = intval($b.'1');
  249. } else {
  250. $b = intval($b);
  251. }
  252. return array($a, $b);
  253. }
  254. }