DomQuery34.php 13 KB

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