DomQuery34.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  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_Test
  17. * @subpackage PHPUnit
  18. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. if (version_compare(PHPUnit_Runner_Version::id(), '4.0.0', '<')) {
  23. /** @see PHPUnit_Framework_Constraint */
  24. require_once 'PHPUnit/Framework/Constraint.php';
  25. }
  26. /** @see Zend_Dom_Query */
  27. require_once 'Zend/Dom/Query.php';
  28. /**
  29. * Zend_Dom_Query-based PHPUnit Constraint
  30. *
  31. * @uses PHPUnit_Framework_Constraint
  32. * @category Zend
  33. * @package Zend_Test
  34. * @subpackage PHPUnit
  35. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. */
  38. class Zend_Test_PHPUnit_Constraint_DomQuery extends PHPUnit_Framework_Constraint
  39. {
  40. /**#@+
  41. * Assertion type constants
  42. */
  43. const ASSERT_QUERY = 'assertQuery';
  44. const ASSERT_CONTENT_CONTAINS = 'assertQueryContentContains';
  45. const ASSERT_CONTENT_REGEX = 'assertQueryContentRegex';
  46. const ASSERT_CONTENT_COUNT = 'assertQueryCount';
  47. const ASSERT_CONTENT_COUNT_MIN= 'assertQueryCountMin';
  48. const ASSERT_CONTENT_COUNT_MAX= 'assertQueryCountMax';
  49. /**#@-*/
  50. /**
  51. * Current assertion type
  52. * @var string
  53. */
  54. protected $_assertType = null;
  55. /**
  56. * Available assertion types
  57. * @var array
  58. */
  59. protected $_assertTypes = array(
  60. self::ASSERT_QUERY,
  61. self::ASSERT_CONTENT_CONTAINS,
  62. self::ASSERT_CONTENT_REGEX,
  63. self::ASSERT_CONTENT_COUNT,
  64. self::ASSERT_CONTENT_COUNT_MIN,
  65. self::ASSERT_CONTENT_COUNT_MAX,
  66. );
  67. /**
  68. * Content being matched
  69. * @var string
  70. */
  71. protected $_content = null;
  72. /**
  73. * Whether or not assertion is negated
  74. * @var bool
  75. */
  76. protected $_negate = false;
  77. /**
  78. * CSS selector or XPath path to select against
  79. * @var string
  80. */
  81. protected $_path = null;
  82. /**
  83. * Whether or not to use XPath when querying
  84. * @var bool
  85. */
  86. protected $_useXpath = false;
  87. /**
  88. * XPath namespaces
  89. * @var array
  90. */
  91. protected $_xpathNamespaces = array();
  92. /**
  93. * Constructor; setup constraint state
  94. *
  95. * @param string $path CSS selector path
  96. * @return void
  97. */
  98. public function __construct($path)
  99. {
  100. $this->_path = $path;
  101. }
  102. /**
  103. * Indicate negative match
  104. *
  105. * @param bool $flag
  106. * @return void
  107. */
  108. public function setNegate($flag = true)
  109. {
  110. $this->_negate = $flag;
  111. }
  112. /**
  113. * Whether or not path is a straight XPath expression
  114. *
  115. * @param bool $flag
  116. * @return Zend_Test_PHPUnit_Constraint_DomQuery
  117. */
  118. public function setUseXpath($flag = true)
  119. {
  120. $this->_useXpath = (bool) $flag;
  121. return $this;
  122. }
  123. /**
  124. * Evaluate an object to see if it fits the constraints
  125. *
  126. * @param string $other String to examine
  127. * @param null|string Assertion type
  128. * @return bool
  129. */
  130. public function evaluate($other, $assertType = null)
  131. {
  132. if (strstr($assertType, 'Not')) {
  133. $this->setNegate(true);
  134. $assertType = str_replace('Not', '', $assertType);
  135. }
  136. if (strstr($assertType, 'Xpath')) {
  137. $this->setUseXpath(true);
  138. $assertType = str_replace('Xpath', 'Query', $assertType);
  139. }
  140. if (!in_array($assertType, $this->_assertTypes)) {
  141. require_once 'Zend/Test/PHPUnit/Constraint/Exception.php';
  142. throw new Zend_Test_PHPUnit_Constraint_Exception(sprintf('Invalid assertion type "%s" provided to %s constraint', $assertType, __CLASS__));
  143. }
  144. $this->_assertType = $assertType;
  145. $method = $this->_useXpath ? 'queryXpath' : 'query';
  146. $domQuery = new Zend_Dom_Query($other);
  147. $domQuery->registerXpathNamespaces($this->_xpathNamespaces);
  148. $result = $domQuery->$method($this->_path);
  149. $argv = func_get_args();
  150. $argc = func_num_args();
  151. switch ($assertType) {
  152. case self::ASSERT_CONTENT_CONTAINS:
  153. if (3 > $argc) {
  154. require_once 'Zend/Test/PHPUnit/Constraint/Exception.php';
  155. throw new Zend_Test_PHPUnit_Constraint_Exception('No content provided against which to match');
  156. }
  157. $this->_content = $content = $argv[2];
  158. return ($this->_negate)
  159. ? $this->_notMatchContent($result, $content)
  160. : $this->_matchContent($result, $content);
  161. case self::ASSERT_CONTENT_REGEX:
  162. if (3 > $argc) {
  163. require_once 'Zend/Test/PHPUnit/Constraint/Exception.php';
  164. throw new Zend_Test_PHPUnit_Constraint_Exception('No pattern provided against which to match');
  165. }
  166. $this->_content = $content = $argv[2];
  167. return ($this->_negate)
  168. ? $this->_notRegexContent($result, $content)
  169. : $this->_regexContent($result, $content);
  170. case self::ASSERT_CONTENT_COUNT:
  171. case self::ASSERT_CONTENT_COUNT_MIN:
  172. case self::ASSERT_CONTENT_COUNT_MAX:
  173. if (3 > $argc) {
  174. require_once 'Zend/Test/PHPUnit/Constraint/Exception.php';
  175. throw new Zend_Test_PHPUnit_Constraint_Exception('No count provided against which to compare');
  176. }
  177. $this->_content = $content = $argv[2];
  178. return $this->_countContent($result, $content, $assertType);
  179. case self::ASSERT_QUERY:
  180. default:
  181. if ($this->_negate) {
  182. return (0 == count($result));
  183. } else {
  184. return (0 != count($result));
  185. }
  186. }
  187. }
  188. /**
  189. * Report Failure
  190. *
  191. * @see PHPUnit_Framework_Constraint for implementation details
  192. * @param mixed $other CSS selector path
  193. * @param string $description
  194. * @param bool $not
  195. * @return void
  196. * @throws PHPUnit_Framework_ExpectationFailedException
  197. */
  198. public function fail($other, $description, $not = false)
  199. {
  200. require_once 'Zend/Test/PHPUnit/Constraint/Exception.php';
  201. switch ($this->_assertType) {
  202. case self::ASSERT_CONTENT_CONTAINS:
  203. $failure = 'Failed asserting node denoted by %s CONTAINS content "%s"';
  204. if ($this->_negate) {
  205. $failure = 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content "%s"';
  206. }
  207. $failure = sprintf($failure, $other, $this->_content);
  208. break;
  209. case self::ASSERT_CONTENT_REGEX:
  210. $failure = 'Failed asserting node denoted by %s CONTAINS content MATCHING "%s"';
  211. if ($this->_negate) {
  212. $failure = 'Failed asserting node DENOTED BY %s DOES NOT CONTAIN content MATCHING "%s"';
  213. }
  214. $failure = sprintf($failure, $other, $this->_content);
  215. break;
  216. case self::ASSERT_CONTENT_COUNT:
  217. $failure = 'Failed asserting node DENOTED BY %s OCCURS EXACTLY %d times';
  218. if ($this->_negate) {
  219. $failure = 'Failed asserting node DENOTED BY %s DOES NOT OCCUR EXACTLY %d times';
  220. }
  221. $failure = sprintf($failure, $other, $this->_content);
  222. break;
  223. case self::ASSERT_CONTENT_COUNT_MIN:
  224. $failure = 'Failed asserting node DENOTED BY %s OCCURS AT LEAST %d times';
  225. $failure = sprintf($failure, $other, $this->_content);
  226. break;
  227. case self::ASSERT_CONTENT_COUNT_MAX:
  228. $failure = 'Failed asserting node DENOTED BY %s OCCURS AT MOST %d times';
  229. $failure = sprintf($failure, $other, $this->_content);
  230. break;
  231. case self::ASSERT_QUERY:
  232. default:
  233. $failure = 'Failed asserting node DENOTED BY %s EXISTS';
  234. if ($this->_negate) {
  235. $failure = 'Failed asserting node DENOTED BY %s DOES NOT EXIST';
  236. }
  237. $failure = sprintf($failure, $other);
  238. break;
  239. }
  240. if (!empty($description)) {
  241. $failure = $description . "\n" . $failure;
  242. }
  243. throw new Zend_Test_PHPUnit_Constraint_Exception($failure);
  244. }
  245. /**
  246. * Complete implementation
  247. *
  248. * @return string
  249. */
  250. public function toString()
  251. {
  252. return '';
  253. }
  254. /**
  255. * Register XPath namespaces
  256. *
  257. * @param array $xpathNamespaces
  258. * @return void
  259. */
  260. public function registerXpathNamespaces($xpathNamespaces)
  261. {
  262. $this->_xpathNamespaces = $xpathNamespaces;
  263. }
  264. /**
  265. * Check to see if content is matched in selected nodes
  266. *
  267. * @param Zend_Dom_Query_Result $result
  268. * @param string $match Content to match
  269. * @return bool
  270. */
  271. protected function _matchContent($result, $match)
  272. {
  273. $match = (string) $match;
  274. if (0 == count($result)) {
  275. return false;
  276. }
  277. foreach ($result as $node) {
  278. $content = $this->_getNodeContent($node);
  279. if (strstr($content, $match)) {
  280. return true;
  281. }
  282. }
  283. return false;
  284. }
  285. /**
  286. * Check to see if content is NOT matched in selected nodes
  287. *
  288. * @param Zend_Dom_Query_Result $result
  289. * @param string $match
  290. * @return bool
  291. */
  292. protected function _notMatchContent($result, $match)
  293. {
  294. if (0 == count($result)) {
  295. return true;
  296. }
  297. foreach ($result as $node) {
  298. $content = $this->_getNodeContent($node);
  299. if (strstr($content, $match)) {
  300. return false;
  301. }
  302. }
  303. return true;
  304. }
  305. /**
  306. * Check to see if content is matched by regex in selected nodes
  307. *
  308. * @param Zend_Dom_Query_Result $result
  309. * @param string $pattern
  310. * @return bool
  311. */
  312. protected function _regexContent($result, $pattern)
  313. {
  314. if (0 == count($result)) {
  315. return false;
  316. }
  317. foreach ($result as $node) {
  318. $content = $this->_getNodeContent($node);
  319. if (preg_match($pattern, $content)) {
  320. return true;
  321. }
  322. }
  323. return false;
  324. }
  325. /**
  326. * Check to see if content is NOT matched by regex in selected nodes
  327. *
  328. * @param Zend_Dom_Query_Result $result
  329. * @param string $pattern
  330. * @return bool
  331. */
  332. protected function _notRegexContent($result, $pattern)
  333. {
  334. if (0 == count($result)) {
  335. return true;
  336. }
  337. foreach ($result as $node) {
  338. $content = $this->_getNodeContent($node);
  339. if (preg_match($pattern, $content)) {
  340. return false;
  341. }
  342. }
  343. return true;
  344. }
  345. /**
  346. * Determine if content count matches criteria
  347. *
  348. * @param Zend_Dom_Query_Result $result
  349. * @param int $test Value against which to test
  350. * @param string $type assertion type
  351. * @return boolean
  352. */
  353. protected function _countContent($result, $test, $type)
  354. {
  355. $count = count($result);
  356. switch ($type) {
  357. case self::ASSERT_CONTENT_COUNT:
  358. return ($this->_negate)
  359. ? ($test != $count)
  360. : ($test == $count);
  361. case self::ASSERT_CONTENT_COUNT_MIN:
  362. return ($count >= $test);
  363. case self::ASSERT_CONTENT_COUNT_MAX:
  364. return ($count <= $test);
  365. default:
  366. return false;
  367. }
  368. }
  369. /**
  370. * Get node content, minus node markup tags
  371. *
  372. * @param DOMNode $node
  373. * @return string
  374. */
  375. protected function _getNodeContent(DOMNode $node)
  376. {
  377. if ($node instanceof DOMAttr) {
  378. return $node->value;
  379. } else {
  380. $doc = $node->ownerDocument;
  381. $content = $doc->saveXML($node);
  382. $tag = $node->nodeName;
  383. $regex = '|</?' . $tag . '[^>]*>|';
  384. return preg_replace($regex, '', $content);
  385. }
  386. }
  387. }