diff --git a/library/Mockery.php b/library/Mockery.php index 05a384b4e..44a5a4b8d 100644 --- a/library/Mockery.php +++ b/library/Mockery.php @@ -662,10 +662,22 @@ private static function objectToArray($object, $nesting = 3) return array('...'); } - return array( - 'class' => get_class($object), - 'properties' => self::extractInstancePublicProperties($object, $nesting) + $defaultFormatter = function ($object, $nesting) { + return array('properties' => self::extractInstancePublicProperties($object, $nesting)); + }; + + $class = get_class($object); + + $formatter = self::getConfiguration()->getObjectFormatter($class, $defaultFormatter); + + $array = array( + 'class' => $class, + 'identity' => '#' . md5(spl_object_hash($object)) ); + + $array = array_merge($array, $formatter($object, $nesting)); + + return $array; } /** @@ -685,7 +697,11 @@ private static function extractInstancePublicProperties($object, $nesting) foreach ($properties as $publicProperty) { if (!$publicProperty->isStatic()) { $name = $publicProperty->getName(); - $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting); + try { + $cleanedProperties[$name] = self::cleanupNesting($object->$name, $nesting); + } catch (\Exception $exception) { + $cleanedProperties[$name] = $exception->getMessage(); + } } } diff --git a/library/Mockery/Configuration.php b/library/Mockery/Configuration.php index 23e897cdd..e5272874a 100644 --- a/library/Mockery/Configuration.php +++ b/library/Mockery/Configuration.php @@ -67,6 +67,13 @@ public function __construct() $this->_quickDefinitionsConfiguration = new QuickDefinitionsConfiguration(); } + /** + * Custom object formatters + * + * @var array + */ + protected $_objectFormatters = array(); + /** * Set boolean to allow/prevent mocking of non-existent methods * @@ -211,4 +218,25 @@ public function reflectionCacheEnabled() { return $this->_reflectionCacheEnabled; } + + public function setObjectFormatter($class, $formatterCallback) + { + $this->_objectFormatters[$class] = $formatterCallback; + } + + public function getObjectFormatter($class, $defaultFormatter) + { + $parentClass = $class; + do { + $classes[] = $parentClass; + $parentClass = get_parent_class($parentClass); + } while ($parentClass); + $classesAndInterfaces = array_merge($classes, class_implements($class)); + foreach ($classesAndInterfaces as $type) { + if (isset($this->_objectFormatters[$type])) { + return $this->_objectFormatters[$type]; + } + } + return $defaultFormatter; + } } diff --git a/tests/Mockery/WithCustomFormatterExpectationTest.php b/tests/Mockery/WithCustomFormatterExpectationTest.php new file mode 100644 index 000000000..13c3a37e7 --- /dev/null +++ b/tests/Mockery/WithCustomFormatterExpectationTest.php @@ -0,0 +1,196 @@ +setObjectFormatter( + 'ClassWithCustomFormatter', + function ($object, $nesting) { + return array( + "formatter" => 'ClassWithCustomFormatter', + "properties" => array( + "stringProperty" => $object->stringProperty + ), + "getters" => array( + "gettedProperty" => $object->getArrayProperty() + ) + ); + } + ); + \Mockery::getConfiguration()->setObjectFormatter( + 'InterfaceWithCustomFormatter', + function ($object, $nesting) { + return array( + "formatter" => 'InterfaceWithCustomFormatter', + "properties" => array( + "stringProperty" => $object->stringProperty + ), + "getters" => array( + "gettedProperty" => $object->getArrayProperty() + ) + ); + } + ); + } + + /** + * @dataProvider getObjectFormatterDataProvider + */ + public function testGetObjectFormatter($object, $expected) + { + $defaultFormatter = function ($class, $nesting) { + return null; + }; + + $formatter = \Mockery::getConfiguration()->getObjectFormatter(get_class($object), $defaultFormatter); + $formatted = $formatter($object, 1); + + $this->assertEquals( + $expected, + $formatted ? $formatted['formatter'] : null + ); + } + + public function getObjectFormatterDataProvider() + { + return array( + array( + new \StdClass(), + null + ), + array( + new ClassWithoutCustomFormatter(), + null + ), + array( + new ClassWithCustomFormatter(), + 'ClassWithCustomFormatter' + ), + array( + new ClasschildOfWithCustomFormatter(), + 'ClassWithCustomFormatter' + ), + array( + new ClassImplementsWithCustomFormatter(), + 'InterfaceWithCustomFormatter' + ) + ); + } + + /** + * @dataProvider formatObjectsDataProvider + */ + public function testFormatObjects($obj, $shouldContains, $shouldNotContains) + { + $string = Mockery::formatObjects(array($obj)); + foreach ($shouldContains as $containString) { + $this->assertStringContainsString($containString, $string); + } + foreach ($shouldNotContains as $containString) { + $this->assertStringNotContainsString($containString, $string); + } + } + + public function formatObjectsDataProvider() + { + return array( + array( + new ClassWithoutCustomFormatter(), + array( + 'stringProperty', + 'numberProperty', + 'arrayProperty' + ), + array( + 'privateProperty' + ) + ), + array( + new ClassWithCustomFormatter(), + array( + 'stringProperty', + 'gettedProperty' + ), + array( + 'numberProperty', + 'privateProperty' + ) + ), + array( + new ClassImplementsWithCustomFormatter(), + array( + 'stringProperty', + 'gettedProperty' + ), + array( + 'numberProperty', + 'privateProperty' + ) + ), + ); + } +} + +class ClassWithoutCustomFormatter +{ + public $stringProperty = "a string"; + public $numberProperty = 123; + public $arrayProperty = array('a', 'nother', 'array'); + private $privateProperty = "private"; +} + +class ClassWithCustomFormatter +{ + public $stringProperty = "a string"; + public $numberProperty = 123; + private $arrayProperty = array('a', 'nother', 'array'); + private $privateProperty = "private"; + + public function getArrayProperty() + { + return $this->arrayProperty; + } +} + +class ClassChildOfWithCustomFormatter extends ClassWithCustomFormatter +{ +} + +interface InterfaceWithCustomFormatter +{ +} + +class ClassImplementsWithCustomFormatter implements InterfaceWithCustomFormatter +{ + public $stringProperty = "a string"; + public $numberProperty = 123; + private $privateProperty = "private"; + private $arrayProperty = array('a', 'nother', 'array'); + + public function getArrayProperty() + { + return $this->arrayProperty; + } +}