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

/vendor/mockery/mockery/library/Mockery/Generator/MockConfiguration.php

https://gitlab.com/jed-lagunday/laratuts
PHP | 460 lines | 298 code | 77 blank | 85 comment | 39 complexity | cd1d8242400f4827dec21fedfcf00922 MD5 | raw file
  1. <?php
  2. namespace Mockery\Generator;
  3. /**
  4. * This class describes the configuration of mocks and hides away some of the
  5. * reflection implementation
  6. */
  7. class MockConfiguration
  8. {
  9. protected static $mockCounter = 0;
  10. /**
  11. * A class that we'd like to mock
  12. */
  13. protected $targetClass;
  14. protected $targetClassName;
  15. /**
  16. * A number of interfaces we'd like to mock, keyed by name to attempt to
  17. * keep unique
  18. */
  19. protected $targetInterfaces = array();
  20. protected $targetInterfaceNames = array();
  21. /**
  22. * An object we'd like our mock to proxy to
  23. */
  24. protected $targetObject;
  25. /**
  26. * The class name we'd like to use for a generated mock
  27. */
  28. protected $name;
  29. /**
  30. * Methods that should specifically not be mocked
  31. *
  32. * This is currently populated with stuff we don't know how to deal with,
  33. * should really be somewhere else
  34. */
  35. protected $blackListedMethods = array();
  36. /**
  37. * If not empty, only these methods will be mocked
  38. */
  39. protected $whiteListedMethods = array();
  40. /**
  41. * An instance mock is where we override the original class before it's
  42. * autoloaded
  43. */
  44. protected $instanceMock = false;
  45. /**
  46. * Param overrides
  47. */
  48. protected $parameterOverrides = array();
  49. /**
  50. * Instance cache of all methods
  51. */
  52. protected $allMethods;
  53. public function __construct(array $targets = array(), array $blackListedMethods = array(), array $whiteListedMethods = array(), $name = null, $instanceMock = false, array $parameterOverrides = array())
  54. {
  55. $this->addTargets($targets);
  56. $this->blackListedMethods = $blackListedMethods;
  57. $this->whiteListedMethods = $whiteListedMethods;
  58. $this->name = $name;
  59. $this->instanceMock = $instanceMock;
  60. $this->parameterOverrides = $parameterOverrides;
  61. }
  62. /**
  63. * Attempt to create a hash of the configuration, in order to allow caching
  64. *
  65. * @TODO workout if this will work
  66. *
  67. * @return string
  68. */
  69. public function getHash()
  70. {
  71. $vars = array(
  72. 'targetClassName' => $this->targetClassName,
  73. 'targetInterfaceNames' => $this->targetInterfaceNames,
  74. 'name' => $this->name,
  75. 'blackListedMethods' => $this->blackListedMethods,
  76. 'whiteListedMethod' => $this->whiteListedMethods,
  77. 'instanceMock' => $this->instanceMock,
  78. 'parameterOverrides' => $this->parameterOverrides,
  79. );
  80. return md5(serialize($vars));
  81. }
  82. /**
  83. * Gets a list of methods from the classes, interfaces and objects and
  84. * filters them appropriately. Lot's of filtering going on, perhaps we could
  85. * have filter classes to iterate through
  86. */
  87. public function getMethodsToMock()
  88. {
  89. $methods = $this->getAllMethods();
  90. foreach ($methods as $key => $method) {
  91. if ($method->isFinal()) {
  92. unset($methods[$key]);
  93. }
  94. }
  95. /**
  96. * Whitelist trumps everything else
  97. */
  98. if (count($this->getWhiteListedMethods())) {
  99. $whitelist = array_map('strtolower', $this->getWhiteListedMethods());
  100. $methods = array_filter($methods, function ($method) use ($whitelist) {
  101. return $method->isAbstract() || in_array(strtolower($method->getName()), $whitelist);
  102. });
  103. return $methods;
  104. }
  105. /**
  106. * Remove blacklisted methods
  107. */
  108. if (count($this->getBlackListedMethods())) {
  109. $blacklist = array_map('strtolower', $this->getBlackListedMethods());
  110. $methods = array_filter($methods, function ($method) use ($blacklist) {
  111. return !in_array(strtolower($method->getName()), $blacklist);
  112. });
  113. }
  114. /**
  115. * Internal objects can not be instantiated with newInstanceArgs and if
  116. * they implement Serializable, unserialize will have to be called. As
  117. * such, we can't mock it and will need a pass to add a dummy
  118. * implementation
  119. */
  120. if ($this->getTargetClass()
  121. && $this->getTargetClass()->implementsInterface("Serializable")
  122. && $this->getTargetClass()->hasInternalAncestor()) {
  123. $methods = array_filter($methods, function ($method) {
  124. return $method->getName() !== "unserialize";
  125. });
  126. }
  127. return array_values($methods);
  128. }
  129. /**
  130. * We declare the __call method to handle undefined stuff, if the class
  131. * we're mocking has also defined it, we need to comply with their interface
  132. */
  133. public function requiresCallTypeHintRemoval()
  134. {
  135. foreach ($this->getAllMethods() as $method) {
  136. if ("__call" === $method->getName()) {
  137. $params = $method->getParameters();
  138. return !$params[1]->isArray();
  139. }
  140. }
  141. return false;
  142. }
  143. /**
  144. * We declare the __callStatic method to handle undefined stuff, if the class
  145. * we're mocking has also defined it, we need to comply with their interface
  146. */
  147. public function requiresCallStaticTypeHintRemoval()
  148. {
  149. foreach ($this->getAllMethods() as $method) {
  150. if ("__callStatic" === $method->getName()) {
  151. $params = $method->getParameters();
  152. return !$params[1]->isArray();
  153. }
  154. }
  155. return false;
  156. }
  157. public function rename($className)
  158. {
  159. $targets = array();
  160. if ($this->targetClassName) {
  161. $targets[] = $this->targetClassName;
  162. }
  163. if ($this->targetInterfaceNames) {
  164. $targets = array_merge($targets, $this->targetInterfaceNames);
  165. }
  166. if ($this->targetObject) {
  167. $targets[] = $this->targetObject;
  168. }
  169. return new self(
  170. $targets,
  171. $this->blackListedMethods,
  172. $this->whiteListedMethods,
  173. $className,
  174. $this->instanceMock,
  175. $this->parameterOverrides
  176. );
  177. }
  178. protected function addTarget($target)
  179. {
  180. if (is_object($target)) {
  181. $this->setTargetObject($target);
  182. $this->setTargetClassName(get_class($target));
  183. return $this;
  184. }
  185. if ($target[0] !== "\\") {
  186. $target = "\\" . $target;
  187. }
  188. if (class_exists($target)) {
  189. $this->setTargetClassName($target);
  190. return $this;
  191. }
  192. if (interface_exists($target)) {
  193. $this->addTargetInterfaceName($target);
  194. return $this;
  195. }
  196. /**
  197. * Default is to set as class, or interface if class already set
  198. *
  199. * Don't like this condition, can't remember what the default
  200. * targetClass is for
  201. */
  202. if ($this->getTargetClassName()) {
  203. $this->addTargetInterfaceName($target);
  204. return $this;
  205. }
  206. $this->setTargetClassName($target);
  207. }
  208. protected function addTargets($interfaces)
  209. {
  210. foreach ($interfaces as $interface) {
  211. $this->addTarget($interface);
  212. }
  213. }
  214. public function getTargetClassName()
  215. {
  216. return $this->targetClassName;
  217. }
  218. public function getTargetClass()
  219. {
  220. if ($this->targetClass) {
  221. return $this->targetClass;
  222. }
  223. if (!$this->targetClassName) {
  224. return null;
  225. }
  226. if (class_exists($this->targetClassName)) {
  227. $dtc = DefinedTargetClass::factory($this->targetClassName);
  228. if ($this->getTargetObject() == false && $dtc->isFinal()) {
  229. throw new \Mockery\Exception(
  230. 'The class ' . $this->targetClassName . ' is marked final and its methods'
  231. . ' cannot be replaced. Classes marked final can be passed in'
  232. . ' to \Mockery::mock() as instantiated objects to create a'
  233. . ' partial mock, but only if the mock is not subject to type'
  234. . ' hinting checks.'
  235. );
  236. }
  237. $this->targetClass = $dtc;
  238. } else {
  239. $this->targetClass = new UndefinedTargetClass($this->targetClassName);
  240. }
  241. return $this->targetClass;
  242. }
  243. public function getTargetInterfaces()
  244. {
  245. if (!empty($this->targetInterfaces)) {
  246. return $this->targetInterfaces;
  247. }
  248. foreach ($this->targetInterfaceNames as $targetInterface) {
  249. if (!interface_exists($targetInterface)) {
  250. $this->targetInterfaces[] = new UndefinedTargetClass($targetInterface);
  251. return;
  252. }
  253. $dtc = DefinedTargetClass::factory($targetInterface);
  254. $extendedInterfaces = array_keys($dtc->getInterfaces());
  255. $extendedInterfaces[] = $targetInterface;
  256. $traversableFound = false;
  257. $iteratorShiftedToFront = false;
  258. foreach ($extendedInterfaces as $interface) {
  259. if (!$traversableFound && preg_match("/^\\?Iterator(|Aggregate)$/i", $interface)) {
  260. break;
  261. }
  262. if (preg_match("/^\\\\?IteratorAggregate$/i", $interface)) {
  263. $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
  264. $iteratorShiftedToFront = true;
  265. } elseif (preg_match("/^\\\\?Iterator$/i", $interface)) {
  266. $this->targetInterfaces[] = DefinedTargetClass::factory("\\Iterator");
  267. $iteratorShiftedToFront = true;
  268. } elseif (preg_match("/^\\\\?Traversable$/i", $interface)) {
  269. $traversableFound = true;
  270. }
  271. }
  272. if ($traversableFound && !$iteratorShiftedToFront) {
  273. $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
  274. }
  275. /**
  276. * We never straight up implement Traversable
  277. */
  278. if (!preg_match("/^\\\\?Traversable$/i", $targetInterface)) {
  279. $this->targetInterfaces[] = $dtc;
  280. }
  281. }
  282. $this->targetInterfaces = array_unique($this->targetInterfaces); // just in case
  283. return $this->targetInterfaces;
  284. }
  285. public function getTargetObject()
  286. {
  287. return $this->targetObject;
  288. }
  289. public function getName()
  290. {
  291. return $this->name;
  292. }
  293. /**
  294. * Generate a suitable name based on the config
  295. */
  296. public function generateName()
  297. {
  298. $name = 'Mockery_' . static::$mockCounter++;
  299. if ($this->getTargetObject()) {
  300. $name .= "_" . str_replace("\\", "_", get_class($this->getTargetObject()));
  301. }
  302. if ($this->getTargetClass()) {
  303. $name .= "_" . str_replace("\\", "_", $this->getTargetClass()->getName());
  304. }
  305. if ($this->getTargetInterfaces()) {
  306. $name .= array_reduce($this->getTargetInterfaces(), function ($tmpname, $i) {
  307. $tmpname .= '_' . str_replace("\\", "_", $i->getName());
  308. return $tmpname;
  309. }, '');
  310. }
  311. return $name;
  312. }
  313. public function getShortName()
  314. {
  315. $parts = explode("\\", $this->getName());
  316. return array_pop($parts);
  317. }
  318. public function getNamespaceName()
  319. {
  320. $parts = explode("\\", $this->getName());
  321. array_pop($parts);
  322. if (count($parts)) {
  323. return implode("\\", $parts);
  324. }
  325. return "";
  326. }
  327. public function getBlackListedMethods()
  328. {
  329. return $this->blackListedMethods;
  330. }
  331. public function getWhiteListedMethods()
  332. {
  333. return $this->whiteListedMethods;
  334. }
  335. public function isInstanceMock()
  336. {
  337. return $this->instanceMock;
  338. }
  339. public function getParameterOverrides()
  340. {
  341. return $this->parameterOverrides;
  342. }
  343. protected function setTargetClassName($targetClassName)
  344. {
  345. $this->targetClassName = $targetClassName;
  346. }
  347. protected function getAllMethods()
  348. {
  349. if ($this->allMethods) {
  350. return $this->allMethods;
  351. }
  352. $classes = $this->getTargetInterfaces();
  353. if ($this->getTargetClass()) {
  354. $classes[] = $this->getTargetClass();
  355. }
  356. $methods = array();
  357. foreach ($classes as $class) {
  358. $methods = array_merge($methods, $class->getMethods());
  359. }
  360. $names = array();
  361. $methods = array_filter($methods, function ($method) use (&$names) {
  362. if (in_array($method->getName(), $names)) {
  363. return false;
  364. }
  365. $names[] = $method->getName();
  366. return true;
  367. });
  368. return $this->allMethods = $methods;
  369. }
  370. /**
  371. * If we attempt to implement Traversable, we must ensure we are also
  372. * implementing either Iterator or IteratorAggregate, and that whichever one
  373. * it is comes before Traversable in the list of implements.
  374. */
  375. protected function addTargetInterfaceName($targetInterface)
  376. {
  377. $this->targetInterfaceNames[] = $targetInterface;
  378. }
  379. protected function setTargetObject($object)
  380. {
  381. $this->targetObject = $object;
  382. }
  383. }