1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: use Mockery\ExpectationInterface;
22: use Mockery\Generator\CachingGenerator;
23: use Mockery\Generator\Generator;
24: use Mockery\Generator\MockConfigurationBuilder;
25: use Mockery\Generator\StringManipulationGenerator;
26: use Mockery\Generator\StringManipulation\Pass\CallTypeHintPass;
27: use Mockery\Generator\StringManipulation\Pass\ClassNamePass;
28: use Mockery\Generator\StringManipulation\Pass\ClassPass;
29: use Mockery\Generator\StringManipulation\Pass\InstanceMockPass;
30: use Mockery\Generator\StringManipulation\Pass\InterfacePass;
31: use Mockery\Generator\StringManipulation\Pass\MethodDefinitionPass;
32: use Mockery\Generator\StringManipulation\Pass\RemoveBuiltinMethodsThatAreFinalPass;
33: use Mockery\Generator\StringManipulation\Pass\RemoveUnserializeForInternalSerializableClassesPass;
34: use Mockery\Loader\EvalLoader;
35: use Mockery\Loader\Loader;
36:
37: class Mockery
38: {
39: const BLOCKS = 'Mockery_Forward_Blocks';
40:
41: 42: 43: 44: 45:
46: protected static $_container = null;
47:
48: 49: 50: 51: 52:
53: protected static $_config = null;
54:
55: 56: 57:
58: protected static $_generator;
59:
60: 61: 62:
63: protected static $_loader;
64:
65: 66: 67: 68: 69:
70: public static function mock()
71: {
72: $args = func_get_args();
73:
74: return call_user_func_array(array(self::getContainer(), 'mock'), $args);
75: }
76:
77: 78: 79:
80: public static function spy()
81: {
82: $args = func_get_args();
83: return call_user_func_array(array(self::getContainer(), 'mock'), $args)->shouldIgnoreMissing();
84: }
85:
86: 87: 88:
89: public static function instanceMock()
90: {
91: $args = func_get_args();
92:
93: return call_user_func_array(array(self::getContainer(), 'mock'), $args);
94: }
95:
96: 97: 98: 99: 100:
101: public static function namedMock()
102: {
103: $args = func_get_args();
104: $name = array_shift($args);
105:
106: $builder = new MockConfigurationBuilder();
107: $builder->setName($name);
108:
109: array_unshift($args, $builder);
110:
111: return call_user_func_array(array(self::getContainer(), 'mock'), $args);
112: }
113:
114: 115: 116: 117: 118: 119: 120:
121: public static function self()
122: {
123: if (is_null(self::$_container)) {
124: throw new \LogicException('You have not declared any mocks yet');
125: }
126:
127: return self::$_container->self();
128: }
129:
130: 131: 132: 133: 134: 135:
136: public static function close()
137: {
138: if (is_null(self::$_container)) return;
139:
140: self::$_container->mockery_teardown();
141: self::$_container->mockery_close();
142: self::$_container = null;
143: }
144:
145: 146: 147: 148: 149: 150: 151:
152: public static function fetchMock($name)
153: {
154: return self::$_container->fetchMock($name);
155: }
156:
157: 158: 159:
160: public static function getContainer()
161: {
162: if (is_null(self::$_container)) {
163: self::$_container = new Mockery\Container(self::getGenerator(), self::getLoader());
164: }
165:
166: return self::$_container;
167: }
168:
169: 170: 171:
172: public static function setGenerator(Generator $generator)
173: {
174: self::$_generator = $generator;
175: }
176:
177: public static function getGenerator()
178: {
179: if (is_null(self::$_generator)) {
180: self::$_generator = self::getDefaultGenerator();
181: }
182:
183: return self::$_generator;
184: }
185:
186: public static function getDefaultGenerator()
187: {
188: $generator = new StringManipulationGenerator(array(
189: new CallTypeHintPass(),
190: new ClassPass(),
191: new ClassNamePass(),
192: new InstanceMockPass(),
193: new InterfacePass(),
194: new MethodDefinitionPass(),
195: new RemoveUnserializeForInternalSerializableClassesPass(),
196: new RemoveBuiltinMethodsThatAreFinalPass(),
197: ));
198:
199: return new CachingGenerator($generator);
200: }
201:
202: 203: 204:
205: public static function setLoader(Loader $loader)
206: {
207: self::$_loader = $loader;
208: }
209:
210: 211: 212:
213: public static function getLoader()
214: {
215: if (is_null(self::$_loader)) {
216: self::$_loader = self::getDefaultLoader();
217: }
218:
219: return self::$_loader;
220: }
221:
222: 223: 224:
225: public static function getDefaultLoader()
226: {
227: return new EvalLoader();
228: }
229:
230: 231: 232: 233: 234: 235: 236:
237: public static function setContainer(Mockery\Container $container)
238: {
239: return self::$_container = $container;
240: }
241:
242: 243: 244:
245: public static function resetContainer()
246: {
247: self::$_container = null;
248: }
249:
250: 251: 252: 253: 254:
255: public static function any()
256: {
257: return new \Mockery\Matcher\Any();
258: }
259:
260: 261: 262: 263: 264: 265: 266:
267: public static function type($expected)
268: {
269: return new \Mockery\Matcher\Type($expected);
270: }
271:
272: 273: 274: 275: 276:
277: public static function ducktype()
278: {
279: return new \Mockery\Matcher\Ducktype(func_get_args());
280: }
281:
282: 283: 284: 285: 286: 287: 288:
289: public static function subset(array $part)
290: {
291: return new \Mockery\Matcher\Subset($part);
292: }
293:
294: 295: 296: 297: 298:
299: public static function contains()
300: {
301: return new \Mockery\Matcher\Contains(func_get_args());
302: }
303:
304: 305: 306: 307: 308: 309: 310:
311: public static function hasKey($key)
312: {
313: return new \Mockery\Matcher\HasKey($key);
314: }
315:
316: 317: 318: 319: 320: 321: 322:
323: public static function hasValue($val)
324: {
325: return new \Mockery\Matcher\HasValue($val);
326: }
327:
328: 329: 330: 331: 332: 333: 334:
335: public static function on($closure)
336: {
337: return new \Mockery\Matcher\Closure($closure);
338: }
339:
340: 341: 342: 343: 344: 345: 346:
347: public static function mustBe($expected)
348: {
349: return new \Mockery\Matcher\MustBe($expected);
350: }
351:
352: 353: 354: 355: 356: 357: 358:
359: public static function not($expected)
360: {
361: return new \Mockery\Matcher\Not($expected);
362: }
363:
364: 365: 366: 367: 368:
369: public static function anyOf()
370: {
371: return new \Mockery\Matcher\AnyOf(func_get_args());
372: }
373:
374: 375: 376: 377: 378:
379: public static function notAnyOf()
380: {
381: return new \Mockery\Matcher\NotAnyOf(func_get_args());
382: }
383:
384: 385: 386:
387: public static function getConfiguration()
388: {
389: if (is_null(self::$_config)) {
390: self::$_config = new \Mockery\Configuration();
391: }
392:
393: return self::$_config;
394: }
395:
396: 397: 398: 399: 400: 401: 402: 403:
404: public static function formatArgs($method, array $arguments = null)
405: {
406: if (is_null($arguments)) {
407: return $method . '()';
408: }
409:
410: $formattedArguments = array();
411: foreach ($arguments as $argument) {
412: $formattedArguments[] = self::formatArgument($argument);
413: }
414:
415: return $method . '(' . implode(', ', $formattedArguments) . ')';
416: }
417:
418: private static function formatArgument($argument, $depth = 0)
419: {
420: if (is_object($argument)) {
421: return 'object(' . get_class($argument) . ')';
422: }
423:
424: if (is_int($argument) || is_float($argument)) {
425: return $argument;
426: }
427:
428: if (is_array($argument)) {
429: if ($depth === 1) {
430: $argument = 'array(...)';
431: } else {
432: $sample = array();
433: foreach ($argument as $key => $value) {
434: $sample[$key] = self::formatArgument($value, $depth + 1);
435: }
436: $argument = preg_replace("{\s}", '', var_export($sample, true));
437: }
438:
439: return ((strlen($argument) > 1000) ? substr($argument, 0, 1000).'...)' : $argument);
440: }
441:
442: if (is_bool($argument)) {
443: return $argument ? 'true' : 'false';
444: }
445:
446: if (is_resource($argument)) {
447: return 'resource(...)';
448: }
449:
450: $argument = (string) $argument;
451:
452: return $depth === 0 ? '"' . $argument . '"' : $argument;
453: }
454:
455: 456: 457: 458: 459: 460: 461:
462: public static function formatObjects(array $objects = null)
463: {
464: static $formatting;
465:
466: if ($formatting) {
467: return '[Recursion]';
468: }
469:
470: if (is_null($objects)) {
471: return '';
472: }
473:
474: $objects = array_filter($objects, 'is_object');
475: if (empty($objects)) {
476: return '';
477: }
478:
479: $formatting = true;
480: $parts = array();
481:
482: foreach($objects as $object) {
483: $parts[get_class($object)] = self::objectToArray($object);
484: }
485:
486: $formatting = false;
487:
488: return 'Objects: ( ' . var_export($parts, true) . ')';
489: }
490:
491: 492: 493: 494: 495: 496: 497: 498:
499: private static function objectToArray($object, $nesting = 3)
500: {
501: if ($nesting == 0) {
502: return array('...');
503: }
504:
505: return array(
506: 'class' => get_class($object),
507: 'properties' => self::extractInstancePublicProperties($object, $nesting),
508: 'getters' => self::extractGetters($object, $nesting)
509: );
510: }
511:
512: 513: 514: 515: 516: 517: 518: 519:
520: private static function extractInstancePublicProperties($object, $nesting)
521: {
522: $reflection = new \ReflectionClass(get_class($object));
523: $properties = $reflection->getProperties(\ReflectionProperty::IS_PUBLIC & ~ \ReflectionProperty::IS_STATIC);
524: $cleanedProperties = array();
525:
526: foreach ($properties as $publicProperty) {
527: $name = $publicProperty->getName();
528: $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting);
529: }
530:
531: return $cleanedProperties;
532: }
533:
534: 535: 536: 537: 538: 539: 540: 541:
542: private static function extractGetters($object, $nesting)
543: {
544: $reflection = new \ReflectionClass(get_class($object));
545: $publicMethods = $reflection->getMethods(\ReflectionProperty::IS_PUBLIC & ~ \ReflectionProperty::IS_STATIC);
546: $getters = array();
547:
548: foreach ($publicMethods as $publicMethod) {
549: $name = $publicMethod->getName();
550: $numberOfParameters = $publicMethod->getNumberOfParameters();
551:
552: if ((substr($name, 0, 3) !== 'get' && substr($name, 0, 2) !== 'is') || $numberOfParameters != 0) {
553: continue;
554: }
555:
556: try {
557: $getters[$name] = self::cleanupNesting($object->$name(), $nesting);
558: } catch(\Exception $e) {
559: $getters[$name] = '!! ' . get_class($e) . ': ' . $e->getMessage() . ' !!';
560: }
561: }
562:
563: return $getters;
564: }
565:
566: private static function cleanupNesting($argument, $nesting)
567: {
568: if (is_object($argument)) {
569: $object = self::objectToArray($argument, $nesting - 1);
570: $object['class'] = get_class($argument);
571:
572: return $object;
573: }
574:
575: if (is_array($argument)) {
576: return self::cleanupArray($argument, $nesting - 1);
577: }
578:
579: return $argument;
580: }
581:
582: private static function cleanupArray($argument, $nesting = 3)
583: {
584: if ($nesting == 0) {
585: return '...';
586: }
587:
588: foreach ($argument as $key => $value) {
589: if (is_array($value)) {
590: $argument[$key] = self::cleanupArray($value, $nesting - 1);
591: } elseif (is_object($value)) {
592: $argument[$key] = self::objectToArray($value, $nesting - 1);
593: }
594: }
595:
596: return $argument;
597: }
598:
599: 600: 601: 602: 603: 604: 605: 606: 607:
608: public static function parseShouldReturnArgs(\Mockery\MockInterface $mock, $args, $add)
609: {
610: $composite = new \Mockery\CompositeExpectation();
611:
612: foreach ($args as $arg) {
613: if (is_array($arg)) {
614: foreach($arg as $k => $v) {
615: $expectation = self::buildDemeterChain($mock, $k, $add)->andReturn($v);
616: $composite->add($expectation);
617: }
618: } elseif (is_string($arg)) {
619: $expectation = self::buildDemeterChain($mock, $arg, $add);
620: $composite->add($expectation);
621: }
622: }
623:
624: return $composite;
625: }
626:
627: 628: 629: 630: 631: 632: 633: 634: 635: 636:
637: protected static function buildDemeterChain(\Mockery\MockInterface $mock, $arg, $add)
638: {
639:
640: $container = $mock->mockery_getContainer();
641: $methodNames = explode('->', $arg);
642: reset($methodNames);
643:
644: if (!\Mockery::getConfiguration()->mockingNonExistentMethodsAllowed()
645: && !$mock->mockery_isAnonymous()
646: && !in_array(current($methodNames), $mock->mockery_getMockableMethods())
647: ) {
648: throw new \Mockery\Exception(
649: 'Mockery\'s configuration currently forbids mocking the method '
650: . current($methodNames) . ' as it does not exist on the class or object '
651: . 'being mocked'
652: );
653: }
654:
655:
656: $expectations = null;
657:
658:
659: $nextExp = function ($method) use ($add) {
660: return $add($method);
661: };
662:
663: while (true) {
664: $method = array_shift($methodNames);
665: $expectations = $mock->mockery_getExpectationsFor($method);
666:
667: if (is_null($expectations) || self::noMoreElementsInChain($methodNames)) {
668: $expectations = $nextExp($method);
669: if (self::noMoreElementsInChain($methodNames)) {
670: break;
671: }
672:
673: $mock = self::getNewDemeterMock($container, $method, $expectations);
674: } else {
675: $demeterMockKey = $container->getKeyOfDemeterMockFor($method);
676: if ($demeterMockKey) {
677: $mock = self::getExistingDemeterMock($container, $demeterMockKey);
678: }
679: }
680:
681: $nextExp = function ($n) use ($mock) {
682: return $mock->shouldReceive($n);
683: };
684: }
685:
686: return $expectations;
687: }
688:
689: 690: 691: 692: 693: 694: 695:
696: private static function getNewDemeterMock(Mockery\Container $container,
697: $method,
698: Mockery\ExpectationInterface $exp
699: ) {
700: $mock = $container->mock('demeter_' . $method);
701: $exp->andReturn($mock);
702:
703: return $mock;
704: }
705:
706: 707: 708: 709: 710: 711:
712: private static function getExistingDemeterMock(Mockery\Container $container, $demeterMockKey)
713: {
714: $mocks = $container->getMocks();
715: $mock = $mocks[$demeterMockKey];
716:
717: return $mock;
718: }
719:
720: 721: 722: 723: 724:
725: private static function noMoreElementsInChain(array $methodNames)
726: {
727: return empty($methodNames);
728: }
729: }
730: