PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

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

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