diff --git a/src/PHPSpec2/Console/Application.php b/src/PHPSpec2/Console/Application.php index 8216716..bdc3971 100644 --- a/src/PHPSpec2/Console/Application.php +++ b/src/PHPSpec2/Console/Application.php @@ -177,6 +177,10 @@ public function __construct($version) return new Listener\ClassNotFoundListener($c('io')); }); + $c->extend('event_dispatcher.listeners', function($c) { + return new Listener\FactoryMethodNotFoundListener($c('io')); + }); + $c->extend('event_dispatcher.listeners', function($c) { return new Listener\MethodNotFoundListener($c('io')); }); diff --git a/src/PHPSpec2/Exception/FactoryMethodNotFoundException.php b/src/PHPSpec2/Exception/FactoryMethodNotFoundException.php new file mode 100644 index 0000000..fe5d904 --- /dev/null +++ b/src/PHPSpec2/Exception/FactoryMethodNotFoundException.php @@ -0,0 +1,34 @@ +subject = $subject; + $this->method = $method; + $this->arguments = $arguments; + } + + public function getSubject() + { + return $this->subject; + } + + public function getMethod() + { + return $this->method; + } + + public function getArguments() + { + return $this->arguments; + } +} \ No newline at end of file diff --git a/src/PHPSpec2/Listener/FactoryMethodNotFoundListener.php b/src/PHPSpec2/Listener/FactoryMethodNotFoundListener.php new file mode 100644 index 0000000..aec7bc7 --- /dev/null +++ b/src/PHPSpec2/Listener/FactoryMethodNotFoundListener.php @@ -0,0 +1,70 @@ +io = $io; + } + + public static function getSubscribedEvents() + { + return array('afterExample' => 'afterExample'); + } + + public function afterExample(ExampleEvent $event) + { + $exception = $event->getException(); + if (null !== $exception && $exception instanceof FactoryMethodNotFoundException) { + if (null === $ioTemp = $this->io->cutTemp()) { + if ("\n" !== $this->io->getLastWrittenMessage()) { + $this->io->writeln(); + } + } + $shortcut = $exception->getSubject().'::'.$exception->getMethod(); + if (in_array($shortcut, $this->proposedMethods)) { + return; + } + $this->proposedMethods[] = $shortcut; + + if ($this->io->askConfirmation('Do you want me to create this factory method for you?')) { + $class = new \ReflectionClass($exception->getSubject()); + $method = $exception->getMethod(); + + $content = file_get_contents($class->getFileName()); + $content = preg_replace( + '/}[ \n]*$/', $this->getMethodContentFor($method) ."\n}\n", $content + ); + + file_put_contents($class->getFileName(), $content); + + $this->io->writeln(sprintf( + "\nFactory Method %s::%s() has been created.", + $class->getName(), $method + ), 6); + } + + $this->io->writeln(); + if (null !== $ioTemp) { + $this->io->writeTemp($ioTemp); + } + } + } + + protected function getMethodContentFor($method) + { + $template = file_get_contents(__DIR__.'/../Resources/templates/factorymethod.php'); + + return rtrim(strtr($template, array('%method%' => $method))); + } +} diff --git a/src/PHPSpec2/Prophet/ObjectProphet.php b/src/PHPSpec2/Prophet/ObjectProphet.php index a3ce357..b8d54e5 100644 --- a/src/PHPSpec2/Prophet/ObjectProphet.php +++ b/src/PHPSpec2/Prophet/ObjectProphet.php @@ -25,7 +25,11 @@ class ObjectProphet implements ArrayAccess, ProphetInterface { + /** + * @var \PHPSpec2\Subject\LazySubjectInterface + */ private $subject; + private $matchers; private $unwrapper; private $presenter; @@ -74,6 +78,17 @@ public function beConstructedWith() $this->subject->setConstructorArguments($this->unwrapper->unwrapAll(func_get_args())); } + /** + * Set a Factory to instantiate the object + * + * @param $factory string|Callable Either a static factory method on the class under spec, or a callable to a factory + * method, such as array('Class', 'method') + */ + public function beConstructedThrough($factory) + { + $this->subject->setFactory($factory); + } + public function should($name = null, array $arguments = array()) { if (null === $name) { diff --git a/src/PHPSpec2/Resources/templates/factorymethod.php b/src/PHPSpec2/Resources/templates/factorymethod.php new file mode 100644 index 0000000..0cb8651 --- /dev/null +++ b/src/PHPSpec2/Resources/templates/factorymethod.php @@ -0,0 +1,6 @@ + + public static function %method%() + { + // TODO: implement + return new static(); + } diff --git a/src/PHPSpec2/Subject/LazyObject.php b/src/PHPSpec2/Subject/LazyObject.php index 1ae65d1..e121920 100644 --- a/src/PHPSpec2/Subject/LazyObject.php +++ b/src/PHPSpec2/Subject/LazyObject.php @@ -6,6 +6,7 @@ use PHPSpec2\Exception\Exception; use PHPSpec2\Exception\ClassNotFoundException; +use PHPSpec2\Exception\FactoryMethodNotFoundException; use PHPSpec2\Formatter\Presenter\PresenterInterface; class LazyObject implements LazySubjectInterface @@ -14,6 +15,7 @@ class LazyObject implements LazySubjectInterface private $arguments; private $presenter; private $instance; + private $factory = '__construct'; public function __construct($classname = null, array $arguments = array(), PresenterInterface $presenter) @@ -62,12 +64,25 @@ public function getInstance() ), $this->classname); } - $reflection = new ReflectionClass($this->classname); - - if (!empty($this->arguments)) { - return $this->instance = $reflection->newInstanceArgs($this->arguments); + if ($this->factory == '__construct') { + $reflection = new ReflectionClass($this->classname); + $this->instance = $reflection->newInstanceArgs($this->arguments); + } elseif (is_string($this->factory)) { + if (!method_exists($this->classname, $this->factory)) { + throw new FactoryMethodNotFoundException(sprintf( + 'Method %s::%s does not exists.', $this->presenter->presentString($this->classname), $this->presenter->presentString($this->factory) + ), $this->classname, $this->factory); + } + $this->instance = call_user_func_array(array($this->classname, $this->factory), $this->arguments); + } elseif (is_callable($this->factory)) { + $this->instance = call_user_func_array($this->factory, $this->arguments); } - return $this->instance = $reflection->newInstance(); + return $this->instance; + } + + public function setFactory($factory) + { + $this->factory = $factory; } }