1: <?php
2:
3: namespace Mockery\Generator;
4:
5: 6: 7: 8:
9: class MockConfiguration
10: {
11: protected static $mockCounter = 0;
12:
13: 14: 15:
16: protected $targetClass;
17: protected $targetClassName;
18:
19: 20: 21: 22:
23: protected $targetInterfaces = array();
24: protected $targetInterfaceNames = array();
25:
26: 27: 28:
29: protected $targetObject;
30:
31: 32: 33:
34: protected $name;
35:
36: 37: 38: 39: 40: 41:
42: protected $blackListedMethods = array();
43:
44: 45: 46:
47: protected $whiteListedMethods = array();
48:
49: 50: 51: 52:
53: protected $instanceMock = false;
54:
55: 56: 57:
58: protected $parameterOverrides = array();
59:
60: 61: 62:
63: protected $allMethods;
64:
65: public function __construct(array $targets = array(), array $blackListedMethods = array(), array $whiteListedMethods = array(), $name = null, $instanceMock = false, array $parameterOverrides = array())
66: {
67: $this->addTargets($targets);
68: $this->blackListedMethods = $blackListedMethods;
69: $this->whiteListedMethods = $whiteListedMethods;
70: $this->name = $name;
71: $this->instanceMock = $instanceMock;
72: $this->parameterOverrides = $parameterOverrides;
73: }
74:
75: 76: 77: 78: 79: 80: 81:
82: public function getHash()
83: {
84: $vars = array(
85: 'targetClassName' => $this->targetClassName,
86: 'targetInterfaceNames' => $this->targetInterfaceNames,
87: 'name' => $this->name,
88: 'blackListedMethods' => $this->blackListedMethods,
89: 'whiteListedMethod' => $this->whiteListedMethods,
90: 'instanceMock' => $this->instanceMock,
91: 'parameterOverrides' => $this->parameterOverrides,
92: );
93:
94: return md5(serialize($vars));
95: }
96:
97: 98: 99: 100: 101:
102: public function getMethodsToMock()
103: {
104: $methods = $this->getAllMethods();
105:
106: foreach ($methods as $key => $method) {
107: if ($method->isFinal()) {
108: unset($methods[$key]);
109: }
110: }
111:
112: 113: 114:
115: if (count($this->getWhiteListedMethods())) {
116: $whitelist = array_map('strtolower', $this->getWhiteListedMethods());
117: $methods = array_filter($methods, function ($method) use ($whitelist) {
118: return $method->isAbstract() || in_array(strtolower($method->getName()), $whitelist);
119: });
120:
121: return $methods;
122: }
123:
124: 125: 126:
127: if (count($this->getBlackListedMethods())) {
128: $blacklist = array_map('strtolower', $this->getBlackListedMethods());
129: $methods = array_filter($methods, function ($method) use ($blacklist) {
130: return !in_array(strtolower($method->getName()), $blacklist);
131: });
132: }
133:
134: 135: 136: 137: 138: 139:
140: if ($this->getTargetClass()
141: && $this->getTargetClass()->implementsInterface("Serializable")
142: && $this->getTargetClass()->hasInternalAncestor()) {
143:
144: $methods = array_filter($methods, function ($method) {
145: return $method->getName() !== "unserialize";
146: });
147: }
148:
149: return array_values($methods);
150: }
151:
152: 153: 154: 155:
156: public function requiresCallTypeHintRemoval()
157: {
158: foreach ($this->getAllMethods() as $method) {
159: if ("__call" === $method->getName()) {
160: $params = $method->getParameters();
161: return !$params[1]->isArray();
162: }
163: }
164:
165: return false;
166: }
167:
168: 169: 170: 171:
172: public function requiresCallStaticTypeHintRemoval()
173: {
174: foreach ($this->getAllMethods() as $method) {
175: if ("__callStatic" === $method->getName()) {
176: $params = $method->getParameters();
177: return !$params[1]->isArray();
178: }
179: }
180:
181: return false;
182: }
183:
184: public function rename($className)
185: {
186: $targets = array();
187:
188: if ($this->targetClassName) {
189: $targets[] = $this->targetClassName;
190: }
191:
192: if ($this->targetInterfaceNames) {
193: $targets = array_merge($targets, $this->targetInterfaceNames);
194: }
195:
196: if ($this->targetObject) {
197: $targets[] = $this->targetObject;
198: }
199:
200: return new self(
201: $targets,
202: $this->blackListedMethods,
203: $this->whiteListedMethods,
204: $className,
205: $this->instanceMock,
206: $this->parameterOverrides
207: );
208: }
209:
210: protected function addTarget($target)
211: {
212: if (is_object($target)) {
213: $this->setTargetObject($target);
214: $this->setTargetClassName(get_class($target));
215: return $this;
216: }
217:
218: if ($target[0] !== "\\") {
219: $target = "\\" . $target;
220: }
221:
222: if (class_exists($target)) {
223: $this->setTargetClassName($target);
224: return $this;
225: }
226:
227: if (interface_exists($target)) {
228: $this->addTargetInterfaceName($target);
229: return $this;
230: }
231:
232: 233: 234: 235: 236: 237:
238: if ($this->getTargetClassName()) {
239: $this->addTargetInterfaceName($target);
240: return $this;
241: }
242:
243: $this->setTargetClassName($target);
244: }
245:
246: protected function addTargets($interfaces)
247: {
248: foreach ($interfaces as $interface) {
249: $this->addTarget($interface);
250: }
251: }
252:
253: public function getTargetClassName()
254: {
255: return $this->targetClassName;
256: }
257:
258: public function getTargetClass()
259: {
260: if ($this->targetClass) {
261: return $this->targetClass;
262: }
263:
264: if (!$this->targetClassName) {
265: return null;
266: }
267:
268: if (class_exists($this->targetClassName)) {
269: $dtc = DefinedTargetClass::factory($this->targetClassName);
270:
271: if ($this->getTargetObject() == false && $dtc->isFinal()) {
272: throw new \Mockery\Exception(
273: 'The class ' . $this->targetClassName . ' is marked final and its methods'
274: . ' cannot be replaced. Classes marked final can be passed in'
275: . ' to \Mockery::mock() as instantiated objects to create a'
276: . ' partial mock, but only if the mock is not subject to type'
277: . ' hinting checks.'
278: );
279: }
280:
281: $this->targetClass = $dtc;
282:
283: } else {
284: $this->targetClass = new UndefinedTargetClass($this->targetClassName);
285: }
286:
287: return $this->targetClass;
288: }
289:
290: public function getTargetInterfaces()
291: {
292: if (!empty($this->targetInterfaces)) {
293: return $this->targetInterfaces;
294: }
295:
296: foreach ($this->targetInterfaceNames as $targetInterface) {
297: if (!interface_exists($targetInterface)) {
298: $this->targetInterfaces[] = new UndefinedTargetClass($targetInterface);
299: return;
300: }
301:
302: $dtc = DefinedTargetClass::factory($targetInterface);
303: $extendedInterfaces = array_keys($dtc->getInterfaces());
304: $extendedInterfaces[] = $targetInterface;
305:
306: $traversableFound = false;
307: $iteratorShiftedToFront = false;
308: foreach ($extendedInterfaces as $interface) {
309:
310: if (!$traversableFound && preg_match("/^\\?Iterator(|Aggregate)$/i", $interface)) {
311: break;
312: }
313:
314: if (preg_match("/^\\\\?IteratorAggregate$/i", $interface)) {
315: $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
316: $iteratorShiftedToFront = true;
317: } elseif (preg_match("/^\\\\?Iterator$/i", $interface)) {
318: $this->targetInterfaces[] = DefinedTargetClass::factory("\\Iterator");
319: $iteratorShiftedToFront = true;
320: } elseif (preg_match("/^\\\\?Traversable$/i", $interface)) {
321: $traversableFound = true;
322: }
323: }
324:
325: if ($traversableFound && !$iteratorShiftedToFront) {
326: $this->targetInterfaces[] = DefinedTargetClass::factory("\\IteratorAggregate");
327: }
328:
329: 330: 331:
332: if (!preg_match("/^\\\\?Traversable$/i", $targetInterface)) {
333: $this->targetInterfaces[] = $dtc;
334: }
335:
336: }
337: $this->targetInterfaces = array_unique($this->targetInterfaces);
338: return $this->targetInterfaces;
339: }
340:
341: public function getTargetObject()
342: {
343: return $this->targetObject;
344: }
345:
346: public function getName()
347: {
348: return $this->name;
349: }
350:
351: 352: 353:
354: public function generateName()
355: {
356: $name = 'Mockery_' . static::$mockCounter++;
357:
358: if ($this->getTargetObject()) {
359: $name .= "_" . str_replace("\\", "_", get_class($this->getTargetObject()));
360: }
361:
362: if ($this->getTargetClass()) {
363: $name .= "_" . str_replace("\\", "_", $this->getTargetClass()->getName());
364: }
365:
366: if ($this->getTargetInterfaces()) {
367: $name .= array_reduce($this->getTargetInterfaces(), function ($tmpname, $i) {
368: $tmpname .= '_' . str_replace("\\", "_", $i->getName());
369: return $tmpname;
370: }, '');
371: }
372:
373: return $name;
374: }
375:
376: public function getShortName()
377: {
378: $parts = explode("\\", $this->getName());
379: return array_pop($parts);
380: }
381:
382: public function getNamespaceName()
383: {
384: $parts = explode("\\", $this->getName());
385: array_pop($parts);
386:
387: if (count($parts)) {
388: return implode("\\", $parts);
389: }
390:
391: return "";
392: }
393:
394: public function getBlackListedMethods()
395: {
396: return $this->blackListedMethods;
397: }
398:
399: public function getWhiteListedMethods()
400: {
401: return $this->whiteListedMethods;
402: }
403:
404: public function isInstanceMock()
405: {
406: return $this->instanceMock;
407: }
408:
409: public function getParameterOverrides()
410: {
411: return $this->parameterOverrides;
412: }
413:
414: protected function setTargetClassName($targetClassName)
415: {
416: $this->targetClassName = $targetClassName;
417: }
418:
419: protected function getAllMethods()
420: {
421: if ($this->allMethods) {
422: return $this->allMethods;
423: }
424:
425: $classes = $this->getTargetInterfaces();
426:
427: if ($this->getTargetClass()) {
428: $classes[] = $this->getTargetClass();
429: }
430:
431: $methods = array();
432: foreach ($classes as $class) {
433: $methods = array_merge($methods, $class->getMethods());
434: }
435:
436: $names = array();
437: $methods = array_filter($methods, function ($method) use (&$names) {
438: if (in_array($method->getName(), $names)) {
439: return false;
440: }
441:
442: $names[] = $method->getName();
443: return true;
444: });
445:
446: return $this->allMethods = $methods;
447: }
448:
449: 450: 451: 452: 453:
454: protected function addTargetInterfaceName($targetInterface)
455: {
456: $this->targetInterfaceNames[] = $targetInterface;
457: }
458:
459:
460: protected function setTargetObject($object)
461: {
462: $this->targetObject = $object;
463: }
464:
465: }
466: