PageRenderTime 44ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/mockery/mockery/library/Mockery/Container.php

https://gitlab.com/btkm/correct_fb
PHP | 524 lines | 313 code | 58 blank | 153 comment | 45 complexity | 898e8b6f315b6c12f1e2445fec6f9655 MD5 | raw file
  1. <?php
  2. /**
  3. * Mockery
  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://github.com/padraic/mockery/blob/master/LICENSE
  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 padraic@php.net so we can send you a copy immediately.
  14. *
  15. * @category Mockery
  16. * @package Mockery
  17. * @copyright Copyright (c) 2010-2014 Pádraic Brady (http://blog.astrumfutura.com)
  18. * @license http://github.com/padraic/mockery/blob/master/LICENSE New BSD License
  19. */
  20. namespace Mockery;
  21. use Mockery\Generator\Generator;
  22. use Mockery\Generator\MockConfigurationBuilder;
  23. use Mockery\Loader\Loader as LoaderInterface;
  24. class Container
  25. {
  26. const BLOCKS = \Mockery::BLOCKS;
  27. /**
  28. * Store of mock objects
  29. *
  30. * @var array
  31. */
  32. protected $_mocks = array();
  33. /**
  34. * Order number of allocation
  35. *
  36. * @var int
  37. */
  38. protected $_allocatedOrder = 0;
  39. /**
  40. * Current ordered number
  41. *
  42. * @var int
  43. */
  44. protected $_currentOrder = 0;
  45. /**
  46. * Ordered groups
  47. *
  48. * @var array
  49. */
  50. protected $_groups = array();
  51. /**
  52. * @var Generator\Generator
  53. */
  54. protected $_generator;
  55. /**
  56. * @var LoaderInterface
  57. */
  58. protected $_loader;
  59. /**
  60. * @var array
  61. */
  62. protected $_namedMocks = array();
  63. public function __construct(Generator $generator = null, LoaderInterface $loader = null)
  64. {
  65. $this->_generator = $generator ?: \Mockery::getDefaultGenerator();
  66. $this->_loader = $loader ?: \Mockery::getDefaultLoader();
  67. }
  68. /**
  69. * Generates a new mock object for this container
  70. *
  71. * I apologies in advance for this. A God Method just fits the API which
  72. * doesn't require differentiating between classes, interfaces, abstracts,
  73. * names or partials - just so long as it's something that can be mocked.
  74. * I'll refactor it one day so it's easier to follow.
  75. *
  76. * @throws Exception\RuntimeException
  77. * @throws Exception
  78. * @return \Mockery\Mock
  79. */
  80. public function mock()
  81. {
  82. $expectationClosure = null;
  83. $quickdefs = array();
  84. $constructorArgs = null;
  85. $blocks = array();
  86. $args = func_get_args();
  87. if (count($args) > 1) {
  88. $finalArg = end($args);
  89. reset($args);
  90. if (is_callable($finalArg) && is_object($finalArg)) {
  91. $expectationClosure = array_pop($args);
  92. }
  93. }
  94. $builder = new MockConfigurationBuilder();
  95. foreach ($args as $k => $arg) {
  96. if ($arg instanceof MockConfigurationBuilder) {
  97. $builder = $arg;
  98. unset($args[$k]);
  99. }
  100. }
  101. reset($args);
  102. $builder->setParameterOverrides(\Mockery::getConfiguration()->getInternalClassMethodParamMaps());
  103. while (count($args) > 0) {
  104. $arg = current($args);
  105. // check for multiple interfaces
  106. if (is_string($arg) && strpos($arg, ',') && !strpos($arg, ']')) {
  107. $interfaces = explode(',', str_replace(' ', '', $arg));
  108. foreach ($interfaces as $i) {
  109. if (!interface_exists($i, true) && !class_exists($i, true)) {
  110. throw new \Mockery\Exception(
  111. 'Class name follows the format for defining multiple'
  112. . ' interfaces, however one or more of the interfaces'
  113. . ' do not exist or are not included, or the base class'
  114. . ' (which you may omit from the mock definition) does not exist'
  115. );
  116. }
  117. }
  118. $builder->addTargets($interfaces);
  119. array_shift($args);
  120. continue;
  121. } elseif (is_string($arg) && substr($arg, 0, 6) == 'alias:') {
  122. $name = array_shift($args);
  123. $name = str_replace('alias:', '', $name);
  124. $builder->addTarget('stdClass');
  125. $builder->setName($name);
  126. continue;
  127. } elseif (is_string($arg) && substr($arg, 0, 9) == 'overload:') {
  128. $name = array_shift($args);
  129. $name = str_replace('overload:', '', $name);
  130. $builder->setInstanceMock(true);
  131. $builder->addTarget('stdClass');
  132. $builder->setName($name);
  133. continue;
  134. } elseif (is_string($arg) && substr($arg, strlen($arg)-1, 1) == ']') {
  135. $parts = explode('[', $arg);
  136. if (!class_exists($parts[0], true) && !interface_exists($parts[0], true)) {
  137. throw new \Mockery\Exception('Can only create a partial mock from'
  138. . ' an existing class or interface');
  139. }
  140. $class = $parts[0];
  141. $parts[1] = str_replace(' ', '', $parts[1]);
  142. $partialMethods = explode(',', strtolower(rtrim($parts[1], ']')));
  143. $builder->addTarget($class);
  144. $builder->setWhiteListedMethods($partialMethods);
  145. array_shift($args);
  146. continue;
  147. } elseif (is_string($arg) && (class_exists($arg, true) || interface_exists($arg, true))) {
  148. $class = array_shift($args);
  149. $builder->addTarget($class);
  150. continue;
  151. } elseif (is_string($arg)) {
  152. $class = array_shift($args);
  153. $builder->addTarget($class);
  154. continue;
  155. } elseif (is_object($arg)) {
  156. $partial = array_shift($args);
  157. $builder->addTarget($partial);
  158. continue;
  159. } elseif (is_array($arg) && !empty($arg) && array_keys($arg) !== range(0, count($arg) - 1)) {
  160. // if associative array
  161. if (array_key_exists(self::BLOCKS, $arg)) {
  162. $blocks = $arg[self::BLOCKS];
  163. }
  164. unset($arg[self::BLOCKS]);
  165. $quickdefs = array_shift($args);
  166. continue;
  167. } elseif (is_array($arg)) {
  168. $constructorArgs = array_shift($args);
  169. continue;
  170. }
  171. throw new \Mockery\Exception(
  172. 'Unable to parse arguments sent to '
  173. . get_class($this) . '::mock()'
  174. );
  175. }
  176. $builder->addBlackListedMethods($blocks);
  177. if (!is_null($constructorArgs)) {
  178. $builder->addBlackListedMethod("__construct"); // we need to pass through
  179. }
  180. if (!empty($partialMethods) && $constructorArgs === null) {
  181. $constructorArgs = array();
  182. }
  183. $config = $builder->getMockConfiguration();
  184. $this->checkForNamedMockClashes($config);
  185. $def = $this->getGenerator()->generate($config);
  186. if (class_exists($def->getClassName(), $attemptAutoload = false)) {
  187. $rfc = new \ReflectionClass($def->getClassName());
  188. if (!$rfc->implementsInterface("Mockery\MockInterface")) {
  189. throw new \Mockery\Exception\RuntimeException("Could not load mock {$def->getClassName()}, class already exists");
  190. }
  191. }
  192. $this->getLoader()->load($def);
  193. $mock = $this->_getInstance($def->getClassName(), $constructorArgs);
  194. $mock->mockery_init($this, $config->getTargetObject());
  195. if (!empty($quickdefs)) {
  196. $mock->shouldReceive($quickdefs)->byDefault();
  197. }
  198. if (!empty($expectationClosure)) {
  199. $expectationClosure($mock);
  200. }
  201. $this->rememberMock($mock);
  202. return $mock;
  203. }
  204. public function instanceMock()
  205. {
  206. }
  207. public function getLoader()
  208. {
  209. return $this->_loader;
  210. }
  211. public function getGenerator()
  212. {
  213. return $this->_generator;
  214. }
  215. /**
  216. * @param string $method
  217. * @return string|null
  218. */
  219. public function getKeyOfDemeterMockFor($method)
  220. {
  221. $keys = array_keys($this->_mocks);
  222. $match = preg_grep("/__demeter_{$method}$/", $keys);
  223. if (count($match) == 1) {
  224. $res = array_values($match);
  225. if (count($res) > 0) {
  226. return $res[0];
  227. }
  228. }
  229. return null;
  230. }
  231. /**
  232. * @return array
  233. */
  234. public function getMocks()
  235. {
  236. return $this->_mocks;
  237. }
  238. /**
  239. * Tear down tasks for this container
  240. *
  241. * @throws \Exception
  242. * @return void
  243. */
  244. public function mockery_teardown()
  245. {
  246. try {
  247. $this->mockery_verify();
  248. } catch (\Exception $e) {
  249. $this->mockery_close();
  250. throw $e;
  251. }
  252. }
  253. /**
  254. * Verify the container mocks
  255. *
  256. * @return void
  257. */
  258. public function mockery_verify()
  259. {
  260. foreach ($this->_mocks as $mock) {
  261. $mock->mockery_verify();
  262. }
  263. }
  264. /**
  265. * Reset the container to its original state
  266. *
  267. * @return void
  268. */
  269. public function mockery_close()
  270. {
  271. foreach ($this->_mocks as $mock) {
  272. $mock->mockery_teardown();
  273. }
  274. $this->_mocks = array();
  275. }
  276. /**
  277. * Fetch the next available allocation order number
  278. *
  279. * @return int
  280. */
  281. public function mockery_allocateOrder()
  282. {
  283. $this->_allocatedOrder += 1;
  284. return $this->_allocatedOrder;
  285. }
  286. /**
  287. * Set ordering for a group
  288. *
  289. * @param mixed $group
  290. * @param int $order
  291. */
  292. public function mockery_setGroup($group, $order)
  293. {
  294. $this->_groups[$group] = $order;
  295. }
  296. /**
  297. * Fetch array of ordered groups
  298. *
  299. * @return array
  300. */
  301. public function mockery_getGroups()
  302. {
  303. return $this->_groups;
  304. }
  305. /**
  306. * Set current ordered number
  307. *
  308. * @param int $order
  309. * @return int The current order number that was set
  310. */
  311. public function mockery_setCurrentOrder($order)
  312. {
  313. $this->_currentOrder = $order;
  314. return $this->_currentOrder;
  315. }
  316. /**
  317. * Get current ordered number
  318. *
  319. * @return int
  320. */
  321. public function mockery_getCurrentOrder()
  322. {
  323. return $this->_currentOrder;
  324. }
  325. /**
  326. * Validate the current mock's ordering
  327. *
  328. * @param string $method
  329. * @param int $order
  330. * @throws \Mockery\Exception
  331. * @return void
  332. */
  333. public function mockery_validateOrder($method, $order, \Mockery\MockInterface $mock)
  334. {
  335. if ($order < $this->_currentOrder) {
  336. $exception = new \Mockery\Exception\InvalidOrderException(
  337. 'Method ' . $method . ' called out of order: expected order '
  338. . $order . ', was ' . $this->_currentOrder
  339. );
  340. $exception->setMock($mock)
  341. ->setMethodName($method)
  342. ->setExpectedOrder($order)
  343. ->setActualOrder($this->_currentOrder);
  344. throw $exception;
  345. }
  346. $this->mockery_setCurrentOrder($order);
  347. }
  348. /**
  349. * Gets the count of expectations on the mocks
  350. *
  351. * @return int
  352. */
  353. public function mockery_getExpectationCount()
  354. {
  355. $count = 0;
  356. foreach ($this->_mocks as $mock) {
  357. $count += $mock->mockery_getExpectationCount();
  358. }
  359. return $count;
  360. }
  361. /**
  362. * Store a mock and set its container reference
  363. *
  364. * @param \Mockery\Mock
  365. * @return \Mockery\Mock
  366. */
  367. public function rememberMock(\Mockery\MockInterface $mock)
  368. {
  369. if (!isset($this->_mocks[get_class($mock)])) {
  370. $this->_mocks[get_class($mock)] = $mock;
  371. } else {
  372. /**
  373. * This condition triggers for an instance mock where origin mock
  374. * is already remembered
  375. */
  376. $this->_mocks[] = $mock;
  377. }
  378. return $mock;
  379. }
  380. /**
  381. * Retrieve the last remembered mock object, which is the same as saying
  382. * retrieve the current mock being programmed where you have yet to call
  383. * mock() to change it - thus why the method name is "self" since it will be
  384. * be used during the programming of the same mock.
  385. *
  386. * @return \Mockery\Mock
  387. */
  388. public function self()
  389. {
  390. $mocks = array_values($this->_mocks);
  391. $index = count($mocks) - 1;
  392. return $mocks[$index];
  393. }
  394. /**
  395. * Return a specific remembered mock according to the array index it
  396. * was stored to in this container instance
  397. *
  398. * @return \Mockery\Mock
  399. */
  400. public function fetchMock($reference)
  401. {
  402. if (isset($this->_mocks[$reference])) {
  403. return $this->_mocks[$reference];
  404. }
  405. }
  406. protected function _getInstance($mockName, $constructorArgs = null)
  407. {
  408. if ($constructorArgs !== null) {
  409. $r = new \ReflectionClass($mockName);
  410. return $r->newInstanceArgs($constructorArgs);
  411. }
  412. try {
  413. $instantiator = new Instantiator;
  414. $instance = $instantiator->instantiate($mockName);
  415. } catch (\Exception $ex) {
  416. $internalMockName = $mockName . '_Internal';
  417. if (!class_exists($internalMockName)) {
  418. eval("class $internalMockName extends $mockName {" .
  419. 'public function __construct() {}' .
  420. '}');
  421. }
  422. $instance = new $internalMockName();
  423. }
  424. return $instance;
  425. }
  426. /**
  427. * Takes a class name and declares it
  428. *
  429. * @param string $fqcn
  430. */
  431. public function declareClass($fqcn)
  432. {
  433. if (false !== strpos($fqcn, '/')) {
  434. throw new \Mockery\Exception(
  435. 'Class name contains a forward slash instead of backslash needed '
  436. . 'when employing namespaces'
  437. );
  438. }
  439. if (false !== strpos($fqcn, "\\")) {
  440. $parts = array_filter(explode("\\", $fqcn), function ($part) {
  441. return $part !== "";
  442. });
  443. $cl = array_pop($parts);
  444. $ns = implode("\\", $parts);
  445. eval(" namespace $ns { class $cl {} }");
  446. } else {
  447. eval(" class $fqcn {} ");
  448. }
  449. }
  450. protected function checkForNamedMockClashes($config)
  451. {
  452. $name = $config->getName();
  453. if (!$name) {
  454. return;
  455. }
  456. $hash = $config->getHash();
  457. if (isset($this->_namedMocks[$name])) {
  458. if ($hash !== $this->_namedMocks[$name]) {
  459. throw new \Mockery\Exception(
  460. "The mock named '$name' has been already defined with a different mock configuration"
  461. );
  462. }
  463. }
  464. $this->_namedMocks[$name] = $hash;
  465. }
  466. }