PageRenderTime 30ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpunit/phpunit/src/Util/Test.php

https://gitlab.com/ealexis.t/trends
PHP | 1084 lines | 860 code | 74 blank | 150 comment | 74 complexity | 2b2a46da1f564bcd6689286c46cb6aa4 MD5 | raw file
  1. <?php
  2. /*
  3. * This file is part of PHPUnit.
  4. *
  5. * (c) Sebastian Bergmann <sebastian@phpunit.de>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. if (!function_exists('trait_exists')) {
  11. function trait_exists($traitname, $autoload = true)
  12. {
  13. return false;
  14. }
  15. }
  16. /**
  17. * Test helpers.
  18. *
  19. * @since Class available since Release 3.0.0
  20. */
  21. class PHPUnit_Util_Test
  22. {
  23. const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/';
  24. const REGEX_TEST_WITH = '/@testWith\s+/';
  25. const REGEX_EXPECTED_EXCEPTION = '(@expectedException\s+([:.\w\\\\x7f-\xff]+)(?:[\t ]+(\S*))?(?:[\t ]+(\S*))?\s*$)m';
  26. const REGEX_REQUIRES_VERSION = '/@requires\s+(?P<name>PHP(?:Unit)?)\s+(?P<value>[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m';
  27. const REGEX_REQUIRES_OS = '/@requires\s+OS\s+(?P<value>.+?)[ \t]*\r?$/m';
  28. const REGEX_REQUIRES = '/@requires\s+(?P<name>function|extension)\s+(?P<value>([^ ]+?))[ \t]*\r?$/m';
  29. const UNKNOWN = -1;
  30. const SMALL = 0;
  31. const MEDIUM = 1;
  32. const LARGE = 2;
  33. private static $annotationCache = array();
  34. private static $hookMethods = array();
  35. /**
  36. * @param PHPUnit_Framework_Test $test
  37. * @param bool $asString
  38. *
  39. * @return mixed
  40. */
  41. public static function describe(PHPUnit_Framework_Test $test, $asString = true)
  42. {
  43. if ($asString) {
  44. if ($test instanceof PHPUnit_Framework_SelfDescribing) {
  45. return $test->toString();
  46. } else {
  47. return get_class($test);
  48. }
  49. } else {
  50. if ($test instanceof PHPUnit_Framework_TestCase) {
  51. return array(
  52. get_class($test), $test->getName()
  53. );
  54. } elseif ($test instanceof PHPUnit_Framework_SelfDescribing) {
  55. return array('', $test->toString());
  56. } else {
  57. return array('', get_class($test));
  58. }
  59. }
  60. }
  61. /**
  62. * @param string $className
  63. * @param string $methodName
  64. *
  65. * @return array|bool
  66. *
  67. * @throws PHPUnit_Framework_CodeCoverageException
  68. *
  69. * @since Method available since Release 4.0.0
  70. */
  71. public static function getLinesToBeCovered($className, $methodName)
  72. {
  73. $annotations = self::parseTestMethodAnnotations(
  74. $className,
  75. $methodName
  76. );
  77. if (isset($annotations['class']['coversNothing']) || isset($annotations['method']['coversNothing'])) {
  78. return false;
  79. }
  80. return self::getLinesToBeCoveredOrUsed($className, $methodName, 'covers');
  81. }
  82. /**
  83. * Returns lines of code specified with the @uses annotation.
  84. *
  85. * @param string $className
  86. * @param string $methodName
  87. *
  88. * @return array
  89. *
  90. * @since Method available since Release 4.0.0
  91. */
  92. public static function getLinesToBeUsed($className, $methodName)
  93. {
  94. return self::getLinesToBeCoveredOrUsed($className, $methodName, 'uses');
  95. }
  96. /**
  97. * @param string $className
  98. * @param string $methodName
  99. * @param string $mode
  100. *
  101. * @return array
  102. *
  103. * @throws PHPUnit_Framework_CodeCoverageException
  104. *
  105. * @since Method available since Release 4.2.0
  106. */
  107. private static function getLinesToBeCoveredOrUsed($className, $methodName, $mode)
  108. {
  109. $annotations = self::parseTestMethodAnnotations(
  110. $className,
  111. $methodName
  112. );
  113. $classShortcut = null;
  114. if (!empty($annotations['class'][$mode . 'DefaultClass'])) {
  115. if (count($annotations['class'][$mode . 'DefaultClass']) > 1) {
  116. throw new PHPUnit_Framework_CodeCoverageException(
  117. sprintf(
  118. 'More than one @%sClass annotation in class or interface "%s".',
  119. $mode,
  120. $className
  121. )
  122. );
  123. }
  124. $classShortcut = $annotations['class'][$mode . 'DefaultClass'][0];
  125. }
  126. $list = array();
  127. if (isset($annotations['class'][$mode])) {
  128. $list = $annotations['class'][$mode];
  129. }
  130. if (isset($annotations['method'][$mode])) {
  131. $list = array_merge($list, $annotations['method'][$mode]);
  132. }
  133. $codeList = array();
  134. foreach (array_unique($list) as $element) {
  135. if ($classShortcut && strncmp($element, '::', 2) === 0) {
  136. $element = $classShortcut . $element;
  137. }
  138. $element = preg_replace('/[\s()]+$/', '', $element);
  139. $element = explode(' ', $element);
  140. $element = $element[0];
  141. $codeList = array_merge(
  142. $codeList,
  143. self::resolveElementToReflectionObjects($element)
  144. );
  145. }
  146. return self::resolveReflectionObjectsToLines($codeList);
  147. }
  148. /**
  149. * Returns the requirements for a test.
  150. *
  151. * @param string $className
  152. * @param string $methodName
  153. *
  154. * @return array
  155. *
  156. * @since Method available since Release 3.6.0
  157. */
  158. public static function getRequirements($className, $methodName)
  159. {
  160. $reflector = new ReflectionClass($className);
  161. $docComment = $reflector->getDocComment();
  162. $reflector = new ReflectionMethod($className, $methodName);
  163. $docComment .= "\n" . $reflector->getDocComment();
  164. $requires = array();
  165. if ($count = preg_match_all(self::REGEX_REQUIRES_OS, $docComment, $matches)) {
  166. $requires['OS'] = sprintf(
  167. '/%s/i',
  168. addcslashes($matches['value'][$count - 1], '/')
  169. );
  170. }
  171. if ($count = preg_match_all(self::REGEX_REQUIRES_VERSION, $docComment, $matches)) {
  172. for ($i = 0; $i < $count; $i++) {
  173. $requires[$matches['name'][$i]] = $matches['value'][$i];
  174. }
  175. }
  176. // https://bugs.php.net/bug.php?id=63055
  177. $matches = array();
  178. if ($count = preg_match_all(self::REGEX_REQUIRES, $docComment, $matches)) {
  179. for ($i = 0; $i < $count; $i++) {
  180. $name = $matches['name'][$i] . 's';
  181. if (!isset($requires[$name])) {
  182. $requires[$name] = array();
  183. }
  184. $requires[$name][] = $matches['value'][$i];
  185. }
  186. }
  187. return $requires;
  188. }
  189. /**
  190. * Returns the missing requirements for a test.
  191. *
  192. * @param string $className
  193. * @param string $methodName
  194. *
  195. * @return array
  196. *
  197. * @since Method available since Release 4.3.0
  198. */
  199. public static function getMissingRequirements($className, $methodName)
  200. {
  201. $required = static::getRequirements($className, $methodName);
  202. $missing = array();
  203. if (!empty($required['PHP']) && version_compare(PHP_VERSION, $required['PHP'], '<')) {
  204. $missing[] = sprintf('PHP %s (or later) is required.', $required['PHP']);
  205. }
  206. if (!empty($required['PHPUnit'])) {
  207. $phpunitVersion = PHPUnit_Runner_Version::id();
  208. if (version_compare($phpunitVersion, $required['PHPUnit'], '<')) {
  209. $missing[] = sprintf('PHPUnit %s (or later) is required.', $required['PHPUnit']);
  210. }
  211. }
  212. if (!empty($required['OS']) && !preg_match($required['OS'], PHP_OS)) {
  213. $missing[] = sprintf('Operating system matching %s is required.', $required['OS']);
  214. }
  215. if (!empty($required['functions'])) {
  216. foreach ($required['functions'] as $function) {
  217. $pieces = explode('::', $function);
  218. if (2 === count($pieces) && method_exists($pieces[0], $pieces[1])) {
  219. continue;
  220. }
  221. if (function_exists($function)) {
  222. continue;
  223. }
  224. $missing[] = sprintf('Function %s is required.', $function);
  225. }
  226. }
  227. if (!empty($required['extensions'])) {
  228. foreach ($required['extensions'] as $extension) {
  229. if (!extension_loaded($extension)) {
  230. $missing[] = sprintf('Extension %s is required.', $extension);
  231. }
  232. }
  233. }
  234. return $missing;
  235. }
  236. /**
  237. * Returns the expected exception for a test.
  238. *
  239. * @param string $className
  240. * @param string $methodName
  241. *
  242. * @return array
  243. *
  244. * @since Method available since Release 3.3.6
  245. */
  246. public static function getExpectedException($className, $methodName)
  247. {
  248. $reflector = new ReflectionMethod($className, $methodName);
  249. $docComment = $reflector->getDocComment();
  250. $docComment = substr($docComment, 3, -2);
  251. if (preg_match(self::REGEX_EXPECTED_EXCEPTION, $docComment, $matches)) {
  252. $annotations = self::parseTestMethodAnnotations(
  253. $className,
  254. $methodName
  255. );
  256. $class = $matches[1];
  257. $code = null;
  258. $message = '';
  259. $messageRegExp = '';
  260. if (isset($matches[2])) {
  261. $message = trim($matches[2]);
  262. } elseif (isset($annotations['method']['expectedExceptionMessage'])) {
  263. $message = self::parseAnnotationContent(
  264. $annotations['method']['expectedExceptionMessage'][0]
  265. );
  266. }
  267. if (isset($annotations['method']['expectedExceptionMessageRegExp'])) {
  268. $messageRegExp = self::parseAnnotationContent(
  269. $annotations['method']['expectedExceptionMessageRegExp'][0]
  270. );
  271. }
  272. if (isset($matches[3])) {
  273. $code = $matches[3];
  274. } elseif (isset($annotations['method']['expectedExceptionCode'])) {
  275. $code = self::parseAnnotationContent(
  276. $annotations['method']['expectedExceptionCode'][0]
  277. );
  278. }
  279. if (is_numeric($code)) {
  280. $code = (int) $code;
  281. } elseif (is_string($code) && defined($code)) {
  282. $code = (int) constant($code);
  283. }
  284. return array(
  285. 'class' => $class, 'code' => $code, 'message' => $message, 'message_regex' => $messageRegExp
  286. );
  287. }
  288. return false;
  289. }
  290. /**
  291. * Parse annotation content to use constant/class constant values
  292. *
  293. * Constants are specified using a starting '@'. For example: @ClassName::CONST_NAME
  294. *
  295. * If the constant is not found the string is used as is to ensure maximum BC.
  296. *
  297. * @param string $message
  298. *
  299. * @return string
  300. */
  301. private static function parseAnnotationContent($message)
  302. {
  303. if (strpos($message, '::') !== false && count(explode('::', $message) == 2)) {
  304. if (defined($message)) {
  305. $message = constant($message);
  306. }
  307. }
  308. return $message;
  309. }
  310. /**
  311. * Returns the provided data for a method.
  312. *
  313. * @param string $className
  314. * @param string $methodName
  315. *
  316. * @return array|Iterator when a data provider is specified and exists
  317. * null when no data provider is specified
  318. *
  319. * @throws PHPUnit_Framework_Exception
  320. *
  321. * @since Method available since Release 3.2.0
  322. */
  323. public static function getProvidedData($className, $methodName)
  324. {
  325. $reflector = new ReflectionMethod($className, $methodName);
  326. $docComment = $reflector->getDocComment();
  327. $data = null;
  328. if ($dataProviderData = self::getDataFromDataProviderAnnotation($docComment, $className, $methodName)) {
  329. $data = $dataProviderData;
  330. }
  331. if ($testWithData = self::getDataFromTestWithAnnotation($docComment)) {
  332. $data = $testWithData;
  333. }
  334. if ($data !== null) {
  335. if (is_object($data)) {
  336. $data = iterator_to_array($data);
  337. }
  338. foreach ($data as $key => $value) {
  339. if (!is_array($value)) {
  340. throw new PHPUnit_Framework_Exception(
  341. sprintf(
  342. 'Data set %s is invalid.',
  343. is_int($key) ? '#' . $key : '"' . $key . '"'
  344. )
  345. );
  346. }
  347. }
  348. }
  349. return $data;
  350. }
  351. /**
  352. * Returns the provided data for a method.
  353. *
  354. * @param string $docComment
  355. * @param string $className
  356. * @param string $methodName
  357. *
  358. * @return array|Iterator when a data provider is specified and exists
  359. * null when no data provider is specified
  360. *
  361. * @throws PHPUnit_Framework_Exception
  362. */
  363. private static function getDataFromDataProviderAnnotation($docComment, $className, $methodName)
  364. {
  365. if (preg_match(self::REGEX_DATA_PROVIDER, $docComment, $matches)) {
  366. $dataProviderMethodNameNamespace = explode('\\', $matches[1]);
  367. $leaf = explode('::', array_pop($dataProviderMethodNameNamespace));
  368. $dataProviderMethodName = array_pop($leaf);
  369. if (!empty($dataProviderMethodNameNamespace)) {
  370. $dataProviderMethodNameNamespace = implode('\\', $dataProviderMethodNameNamespace) . '\\';
  371. } else {
  372. $dataProviderMethodNameNamespace = '';
  373. }
  374. if (!empty($leaf)) {
  375. $dataProviderClassName = $dataProviderMethodNameNamespace . array_pop($leaf);
  376. } else {
  377. $dataProviderClassName = $className;
  378. }
  379. $dataProviderClass = new ReflectionClass($dataProviderClassName);
  380. $dataProviderMethod = $dataProviderClass->getMethod(
  381. $dataProviderMethodName
  382. );
  383. if ($dataProviderMethod->isStatic()) {
  384. $object = null;
  385. } else {
  386. $object = $dataProviderClass->newInstance();
  387. }
  388. if ($dataProviderMethod->getNumberOfParameters() == 0) {
  389. $data = $dataProviderMethod->invoke($object);
  390. } else {
  391. $data = $dataProviderMethod->invoke($object, $methodName);
  392. }
  393. return $data;
  394. }
  395. }
  396. /**
  397. * @param string $docComment full docComment string
  398. *
  399. * @return array when @testWith annotation is defined
  400. * null when @testWith annotation is omitted
  401. *
  402. * @throws PHPUnit_Framework_Exception when @testWith annotation is defined but cannot be parsed
  403. */
  404. public static function getDataFromTestWithAnnotation($docComment)
  405. {
  406. $docComment = self::cleanUpMultiLineAnnotation($docComment);
  407. if (preg_match(self::REGEX_TEST_WITH, $docComment, $matches, PREG_OFFSET_CAPTURE)) {
  408. $offset = strlen($matches[0][0]) + $matches[0][1];
  409. $annotationContent = substr($docComment, $offset);
  410. $data = array();
  411. foreach (explode("\n", $annotationContent) as $candidateRow) {
  412. $candidateRow = trim($candidateRow);
  413. $dataSet = json_decode($candidateRow, true);
  414. if (json_last_error() != JSON_ERROR_NONE) {
  415. break;
  416. }
  417. $data[] = $dataSet;
  418. }
  419. if (!$data) {
  420. throw new PHPUnit_Framework_Exception('The dataset for the @testWith annotation cannot be parsed.');
  421. }
  422. return $data;
  423. }
  424. }
  425. private static function cleanUpMultiLineAnnotation($docComment)
  426. {
  427. //removing initial ' * ' for docComment
  428. $docComment = preg_replace('/' . '\n' . '\s*' . '\*' . '\s?' . '/', "\n", $docComment);
  429. $docComment = substr($docComment, 0, -1);
  430. $docComment = rtrim($docComment, "\n");
  431. return $docComment;
  432. }
  433. /**
  434. * @param string $className
  435. * @param string $methodName
  436. *
  437. * @return array
  438. *
  439. * @throws ReflectionException
  440. *
  441. * @since Method available since Release 3.4.0
  442. */
  443. public static function parseTestMethodAnnotations($className, $methodName = '')
  444. {
  445. if (!isset(self::$annotationCache[$className])) {
  446. $class = new ReflectionClass($className);
  447. self::$annotationCache[$className] = self::parseAnnotations($class->getDocComment());
  448. }
  449. if (!empty($methodName) && !isset(self::$annotationCache[$className . '::' . $methodName])) {
  450. try {
  451. $method = new ReflectionMethod($className, $methodName);
  452. $annotations = self::parseAnnotations($method->getDocComment());
  453. } catch (ReflectionException $e) {
  454. $annotations = array();
  455. }
  456. self::$annotationCache[$className . '::' . $methodName] = $annotations;
  457. }
  458. return array(
  459. 'class' => self::$annotationCache[$className],
  460. 'method' => !empty($methodName) ? self::$annotationCache[$className . '::' . $methodName] : array()
  461. );
  462. }
  463. /**
  464. * @param string $docblock
  465. *
  466. * @return array
  467. *
  468. * @since Method available since Release 3.4.0
  469. */
  470. private static function parseAnnotations($docblock)
  471. {
  472. $annotations = array();
  473. // Strip away the docblock header and footer to ease parsing of one line annotations
  474. $docblock = substr($docblock, 3, -2);
  475. if (preg_match_all('/@(?P<name>[A-Za-z_-]+)(?:[ \t]+(?P<value>.*?))?[ \t]*\r?$/m', $docblock, $matches)) {
  476. $numMatches = count($matches[0]);
  477. for ($i = 0; $i < $numMatches; ++$i) {
  478. $annotations[$matches['name'][$i]][] = $matches['value'][$i];
  479. }
  480. }
  481. return $annotations;
  482. }
  483. /**
  484. * Returns the backup settings for a test.
  485. *
  486. * @param string $className
  487. * @param string $methodName
  488. *
  489. * @return array
  490. *
  491. * @since Method available since Release 3.4.0
  492. */
  493. public static function getBackupSettings($className, $methodName)
  494. {
  495. return array(
  496. 'backupGlobals' => self::getBooleanAnnotationSetting(
  497. $className,
  498. $methodName,
  499. 'backupGlobals'
  500. ),
  501. 'backupStaticAttributes' => self::getBooleanAnnotationSetting(
  502. $className,
  503. $methodName,
  504. 'backupStaticAttributes'
  505. )
  506. );
  507. }
  508. /**
  509. * Returns the dependencies for a test class or method.
  510. *
  511. * @param string $className
  512. * @param string $methodName
  513. *
  514. * @return array
  515. *
  516. * @since Method available since Release 3.4.0
  517. */
  518. public static function getDependencies($className, $methodName)
  519. {
  520. $annotations = self::parseTestMethodAnnotations(
  521. $className,
  522. $methodName
  523. );
  524. $dependencies = array();
  525. if (isset($annotations['class']['depends'])) {
  526. $dependencies = $annotations['class']['depends'];
  527. }
  528. if (isset($annotations['method']['depends'])) {
  529. $dependencies = array_merge(
  530. $dependencies,
  531. $annotations['method']['depends']
  532. );
  533. }
  534. return array_unique($dependencies);
  535. }
  536. /**
  537. * Returns the error handler settings for a test.
  538. *
  539. * @param string $className
  540. * @param string $methodName
  541. *
  542. * @return bool
  543. *
  544. * @since Method available since Release 3.4.0
  545. */
  546. public static function getErrorHandlerSettings($className, $methodName)
  547. {
  548. return self::getBooleanAnnotationSetting(
  549. $className,
  550. $methodName,
  551. 'errorHandler'
  552. );
  553. }
  554. /**
  555. * Returns the groups for a test class or method.
  556. *
  557. * @param string $className
  558. * @param string $methodName
  559. *
  560. * @return array
  561. *
  562. * @since Method available since Release 3.2.0
  563. */
  564. public static function getGroups($className, $methodName = '')
  565. {
  566. $annotations = self::parseTestMethodAnnotations(
  567. $className,
  568. $methodName
  569. );
  570. $groups = array();
  571. if (isset($annotations['method']['author'])) {
  572. $groups = $annotations['method']['author'];
  573. } elseif (isset($annotations['class']['author'])) {
  574. $groups = $annotations['class']['author'];
  575. }
  576. if (isset($annotations['class']['group'])) {
  577. $groups = array_merge($groups, $annotations['class']['group']);
  578. }
  579. if (isset($annotations['method']['group'])) {
  580. $groups = array_merge($groups, $annotations['method']['group']);
  581. }
  582. if (isset($annotations['class']['ticket'])) {
  583. $groups = array_merge($groups, $annotations['class']['ticket']);
  584. }
  585. if (isset($annotations['method']['ticket'])) {
  586. $groups = array_merge($groups, $annotations['method']['ticket']);
  587. }
  588. foreach (array('method', 'class') as $element) {
  589. foreach (array('small', 'medium', 'large') as $size) {
  590. if (isset($annotations[$element][$size])) {
  591. $groups[] = $size;
  592. break 2;
  593. }
  594. if (isset($annotations[$element][$size])) {
  595. $groups[] = $size;
  596. break 2;
  597. }
  598. }
  599. }
  600. return array_unique($groups);
  601. }
  602. /**
  603. * Returns the size of the test.
  604. *
  605. * @param string $className
  606. * @param string $methodName
  607. *
  608. * @return int
  609. *
  610. * @since Method available since Release 3.6.0
  611. */
  612. public static function getSize($className, $methodName)
  613. {
  614. $groups = array_flip(self::getGroups($className, $methodName));
  615. $size = self::UNKNOWN;
  616. $class = new ReflectionClass($className);
  617. if (isset($groups['large']) ||
  618. (class_exists('PHPUnit_Extensions_Database_TestCase', false) &&
  619. $class->isSubclassOf('PHPUnit_Extensions_Database_TestCase')) ||
  620. (class_exists('PHPUnit_Extensions_SeleniumTestCase', false) &&
  621. $class->isSubclassOf('PHPUnit_Extensions_SeleniumTestCase'))) {
  622. $size = self::LARGE;
  623. } elseif (isset($groups['medium'])) {
  624. $size = self::MEDIUM;
  625. } elseif (isset($groups['small'])) {
  626. $size = self::SMALL;
  627. }
  628. return $size;
  629. }
  630. /**
  631. * Returns the tickets for a test class or method.
  632. *
  633. * @param string $className
  634. * @param string $methodName
  635. *
  636. * @return array
  637. *
  638. * @since Method available since Release 3.4.0
  639. */
  640. public static function getTickets($className, $methodName)
  641. {
  642. $annotations = self::parseTestMethodAnnotations(
  643. $className,
  644. $methodName
  645. );
  646. $tickets = array();
  647. if (isset($annotations['class']['ticket'])) {
  648. $tickets = $annotations['class']['ticket'];
  649. }
  650. if (isset($annotations['method']['ticket'])) {
  651. $tickets = array_merge($tickets, $annotations['method']['ticket']);
  652. }
  653. return array_unique($tickets);
  654. }
  655. /**
  656. * Returns the process isolation settings for a test.
  657. *
  658. * @param string $className
  659. * @param string $methodName
  660. *
  661. * @return bool
  662. *
  663. * @since Method available since Release 3.4.1
  664. */
  665. public static function getProcessIsolationSettings($className, $methodName)
  666. {
  667. $annotations = self::parseTestMethodAnnotations(
  668. $className,
  669. $methodName
  670. );
  671. if (isset($annotations['class']['runTestsInSeparateProcesses']) ||
  672. isset($annotations['method']['runInSeparateProcess'])) {
  673. return true;
  674. } else {
  675. return false;
  676. }
  677. }
  678. /**
  679. * Returns the preserve global state settings for a test.
  680. *
  681. * @param string $className
  682. * @param string $methodName
  683. *
  684. * @return bool
  685. *
  686. * @since Method available since Release 3.4.0
  687. */
  688. public static function getPreserveGlobalStateSettings($className, $methodName)
  689. {
  690. return self::getBooleanAnnotationSetting(
  691. $className,
  692. $methodName,
  693. 'preserveGlobalState'
  694. );
  695. }
  696. /**
  697. * @param string $className
  698. *
  699. * @return array
  700. *
  701. * @since Method available since Release 4.0.8
  702. */
  703. public static function getHookMethods($className)
  704. {
  705. if (!class_exists($className, false)) {
  706. return self::emptyHookMethodsArray();
  707. }
  708. if (!isset(self::$hookMethods[$className])) {
  709. self::$hookMethods[$className] = self::emptyHookMethodsArray();
  710. try {
  711. $class = new ReflectionClass($className);
  712. foreach ($class->getMethods() as $method) {
  713. if (self::isBeforeClassMethod($method)) {
  714. self::$hookMethods[$className]['beforeClass'][] = $method->getName();
  715. }
  716. if (self::isBeforeMethod($method)) {
  717. self::$hookMethods[$className]['before'][] = $method->getName();
  718. }
  719. if (self::isAfterMethod($method)) {
  720. self::$hookMethods[$className]['after'][] = $method->getName();
  721. }
  722. if (self::isAfterClassMethod($method)) {
  723. self::$hookMethods[$className]['afterClass'][] = $method->getName();
  724. }
  725. }
  726. } catch (ReflectionException $e) {
  727. }
  728. }
  729. return self::$hookMethods[$className];
  730. }
  731. /**
  732. * @return array
  733. *
  734. * @since Method available since Release 4.0.9
  735. */
  736. private static function emptyHookMethodsArray()
  737. {
  738. return array(
  739. 'beforeClass' => array('setUpBeforeClass'),
  740. 'before' => array('setUp'),
  741. 'after' => array('tearDown'),
  742. 'afterClass' => array('tearDownAfterClass')
  743. );
  744. }
  745. /**
  746. * @param string $className
  747. * @param string $methodName
  748. * @param string $settingName
  749. *
  750. * @return bool
  751. *
  752. * @since Method available since Release 3.4.0
  753. */
  754. private static function getBooleanAnnotationSetting($className, $methodName, $settingName)
  755. {
  756. $annotations = self::parseTestMethodAnnotations(
  757. $className,
  758. $methodName
  759. );
  760. $result = null;
  761. if (isset($annotations['class'][$settingName])) {
  762. if ($annotations['class'][$settingName][0] == 'enabled') {
  763. $result = true;
  764. } elseif ($annotations['class'][$settingName][0] == 'disabled') {
  765. $result = false;
  766. }
  767. }
  768. if (isset($annotations['method'][$settingName])) {
  769. if ($annotations['method'][$settingName][0] == 'enabled') {
  770. $result = true;
  771. } elseif ($annotations['method'][$settingName][0] == 'disabled') {
  772. $result = false;
  773. }
  774. }
  775. return $result;
  776. }
  777. /**
  778. * @param string $element
  779. *
  780. * @return array
  781. *
  782. * @throws PHPUnit_Framework_InvalidCoversTargetException
  783. *
  784. * @since Method available since Release 4.0.0
  785. */
  786. private static function resolveElementToReflectionObjects($element)
  787. {
  788. $codeToCoverList = array();
  789. if (strpos($element, '\\') !== false && function_exists($element)) {
  790. $codeToCoverList[] = new ReflectionFunction($element);
  791. } elseif (strpos($element, '::') !== false) {
  792. list($className, $methodName) = explode('::', $element);
  793. if (isset($methodName[0]) && $methodName[0] == '<') {
  794. $classes = array($className);
  795. foreach ($classes as $className) {
  796. if (!class_exists($className) &&
  797. !interface_exists($className)) {
  798. throw new PHPUnit_Framework_InvalidCoversTargetException(
  799. sprintf(
  800. 'Trying to @cover or @use not existing class or ' .
  801. 'interface "%s".',
  802. $className
  803. )
  804. );
  805. }
  806. $class = new ReflectionClass($className);
  807. $methods = $class->getMethods();
  808. $inverse = isset($methodName[1]) && $methodName[1] == '!';
  809. if (strpos($methodName, 'protected')) {
  810. $visibility = 'isProtected';
  811. } elseif (strpos($methodName, 'private')) {
  812. $visibility = 'isPrivate';
  813. } elseif (strpos($methodName, 'public')) {
  814. $visibility = 'isPublic';
  815. }
  816. foreach ($methods as $method) {
  817. if ($inverse && !$method->$visibility()) {
  818. $codeToCoverList[] = $method;
  819. } elseif (!$inverse && $method->$visibility()) {
  820. $codeToCoverList[] = $method;
  821. }
  822. }
  823. }
  824. } else {
  825. $classes = array($className);
  826. foreach ($classes as $className) {
  827. if ($className == '' && function_exists($methodName)) {
  828. $codeToCoverList[] = new ReflectionFunction(
  829. $methodName
  830. );
  831. } else {
  832. if (!((class_exists($className) ||
  833. interface_exists($className) ||
  834. trait_exists($className)) &&
  835. method_exists($className, $methodName))) {
  836. throw new PHPUnit_Framework_InvalidCoversTargetException(
  837. sprintf(
  838. 'Trying to @cover or @use not existing method "%s::%s".',
  839. $className,
  840. $methodName
  841. )
  842. );
  843. }
  844. $codeToCoverList[] = new ReflectionMethod(
  845. $className,
  846. $methodName
  847. );
  848. }
  849. }
  850. }
  851. } else {
  852. $extended = false;
  853. if (strpos($element, '<extended>') !== false) {
  854. $element = str_replace('<extended>', '', $element);
  855. $extended = true;
  856. }
  857. $classes = array($element);
  858. if ($extended) {
  859. $classes = array_merge(
  860. $classes,
  861. class_implements($element),
  862. class_parents($element)
  863. );
  864. }
  865. foreach ($classes as $className) {
  866. if (!class_exists($className) &&
  867. !interface_exists($className) &&
  868. !trait_exists($className)) {
  869. throw new PHPUnit_Framework_InvalidCoversTargetException(
  870. sprintf(
  871. 'Trying to @cover or @use not existing class or ' .
  872. 'interface "%s".',
  873. $className
  874. )
  875. );
  876. }
  877. $codeToCoverList[] = new ReflectionClass($className);
  878. }
  879. }
  880. return $codeToCoverList;
  881. }
  882. /**
  883. * @param array $reflectors
  884. *
  885. * @return array
  886. */
  887. private static function resolveReflectionObjectsToLines(array $reflectors)
  888. {
  889. $result = array();
  890. foreach ($reflectors as $reflector) {
  891. $filename = $reflector->getFileName();
  892. if (!isset($result[$filename])) {
  893. $result[$filename] = array();
  894. }
  895. $result[$filename] = array_unique(
  896. array_merge(
  897. $result[$filename],
  898. range($reflector->getStartLine(), $reflector->getEndLine())
  899. )
  900. );
  901. }
  902. return $result;
  903. }
  904. /**
  905. * @param ReflectionMethod $method
  906. *
  907. * @return bool
  908. *
  909. * @since Method available since Release 4.0.8
  910. */
  911. private static function isBeforeClassMethod(ReflectionMethod $method)
  912. {
  913. return $method->isStatic() && strpos($method->getDocComment(), '@beforeClass') !== false;
  914. }
  915. /**
  916. * @param ReflectionMethod $method
  917. *
  918. * @return bool
  919. *
  920. * @since Method available since Release 4.0.8
  921. */
  922. private static function isBeforeMethod(ReflectionMethod $method)
  923. {
  924. return preg_match('/@before\b/', $method->getDocComment());
  925. }
  926. /**
  927. * @param ReflectionMethod $method
  928. *
  929. * @return bool
  930. *
  931. * @since Method available since Release 4.0.8
  932. */
  933. private static function isAfterClassMethod(ReflectionMethod $method)
  934. {
  935. return $method->isStatic() && strpos($method->getDocComment(), '@afterClass') !== false;
  936. }
  937. /**
  938. * @param ReflectionMethod $method
  939. *
  940. * @return bool
  941. *
  942. * @since Method available since Release 4.0.8
  943. */
  944. private static function isAfterMethod(ReflectionMethod $method)
  945. {
  946. return preg_match('/@after\b/', $method->getDocComment());
  947. }
  948. }