diff --git a/src/Command/BuiltIn/DeployCommand.php b/src/Command/BuiltIn/DeployCommand.php index dd86e4d..4fe99e4 100644 --- a/src/Command/BuiltIn/DeployCommand.php +++ b/src/Command/BuiltIn/DeployCommand.php @@ -14,10 +14,10 @@ use Mage\Deploy\Strategy\StrategyInterface; use Mage\Runtime\Exception\RuntimeException; use Mage\Runtime\Runtime; use Mage\Task\ExecuteOnRollbackInterface; -use Mage\Task\AbstractTask; use Mage\Task\Exception\ErrorException; use Mage\Task\Exception\SkipException; use Mage\Task\TaskFactory; +use Mage\Task\TaskInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputOption; @@ -185,8 +185,17 @@ class DeployCommand extends AbstractCommand $succeededTasks = 0; foreach ($tasks as $taskName) { - /** @var AbstractTask $task */ - $task = $this->taskFactory->get($taskName); + $options = null; + + // Handle the options + if (is_array($taskName)) { + $options = $taskName; + $taskName = key($options); + $options = $options[$taskName]; + } + + /** @var TaskInterface $task */ + $task = $this->taskFactory->get($taskName, $options); $output->write(sprintf(' Running %s ... ', $task->getDescription())); $this->log(sprintf('Running task %s (%s)', $task->getDescription(), $task->getName())); diff --git a/src/Task/AbstractTask.php b/src/Task/AbstractTask.php index ee03134..c65afb5 100644 --- a/src/Task/AbstractTask.php +++ b/src/Task/AbstractTask.php @@ -17,7 +17,7 @@ use Mage\Runtime\Runtime; * * @author Andrés Montañez */ -abstract class AbstractTask +abstract class AbstractTask implements TaskInterface { /** * @var array Task custom options @@ -29,27 +29,6 @@ abstract class AbstractTask */ protected $runtime; - /** - * Get the Name/Code of the Task - * - * @return string - */ - abstract public function getName(); - - /** - * Get a short Description of the Task - * - * @return string - */ - abstract public function getDescription(); - - /** - * Executes the Command - * - * @return bool - */ - abstract public function execute(); - /** * Set additional Options for the Task * diff --git a/src/Task/TaskFactory.php b/src/Task/TaskFactory.php index 0a5950b..e1ae576 100644 --- a/src/Task/TaskFactory.php +++ b/src/Task/TaskFactory.php @@ -20,18 +20,21 @@ use ReflectionClass; * Task Factory * * @author Andrés Montañez + * @author Kamil Kuzminski */ class TaskFactory { /** + * Runtime * @var Runtime */ - protected $runtime; + private $runtime; /** - * @var array Registered Tasks + * Registered tasks + * @var TaskInterface[] */ - protected $registeredTasks = []; + private $tasks = []; /** * Constructor @@ -48,11 +51,29 @@ class TaskFactory * Add a Task * * @param AbstractTask $task + * + * @deprecated Deprecated since 3.0, to be removed in 4.0. + * Use the TaskFactory::addTask() instead. */ public function add(AbstractTask $task) { + @trigger_error( + 'Using TaskFactory::add() has been deprecated and will no longer work in 4.0. Use the TaskFactory::addTask() instead.', + E_USER_DEPRECATED + ); + $task->setRuntime($this->runtime); - $this->registeredTasks[$task->getName()] = $task; + $this->tasks[$task->getName()] = $task; + } + + /** + * Add a task + * + * @param TaskInterface $task + */ + public function addTask(TaskInterface $task) + { + $this->tasks[$task->getName()] = $task; } /** @@ -60,58 +81,101 @@ class TaskFactory * in that case the class will be instantiated * * @param string $name Name/Code or Class of the Task - * @return AbstractTask + * @param array $options + * + * @return TaskInterface + * * @throws RuntimeException */ - public function get($name) + public function get($name, array $options = null) { - $options = []; + // TODO: backwards compatibility, remove in 4.0 if (is_array($name)) { $options = $name; - list($name) = array_keys($name); + $name = key($options); $options = $options[$name]; } - if (array_key_exists($name, $this->registeredTasks)) { - /** @var AbstractTask $task */ - $task = $this->registeredTasks[$name]; - $task->setOptions($options); - return $task; - } elseif (class_exists($name)) { - $reflex = new ReflectionClass($name); - if ($reflex->isInstantiable()) { - $task = new $name(); - if ($task instanceof AbstractTask) { - $task->setOptions($options); - $this->add($task); - return $task; - } - } + if (array_key_exists($name, $this->tasks)) { + $task = $this->tasks[$name]; + } else { + $task = $this->createTask($name); } - throw new RuntimeException(sprintf('Invalid task name "%s"', $name)); + $task->setRuntime($this->runtime); + + if ($options !== null) { + $task->setOptions($options); + } + + return $task; } /** - * Load BuiltIn Tasks + * Load the built-in tasks + * + * @throws RuntimeException */ - protected function loadBuiltInTasks() + private function loadBuiltInTasks() { $finder = new Finder(); - $finder->files()->in(__DIR__ . '/BuiltIn')->name('*Task.php'); + $finder->files()->in(__DIR__.'/BuiltIn')->name('*Task.php'); /** @var SplFileInfo $file */ foreach ($finder as $file) { - $class = substr('\\Mage\\Task\\BuiltIn\\' . str_replace('/', '\\', $file->getRelativePathname()), 0, -4); - if (class_exists($class)) { - $reflex = new ReflectionClass($class); - if ($reflex->isInstantiable()) { - $task = new $class(); - if ($task instanceof AbstractTask) { - $this->add($task); - } - } + $class = substr('\\Mage\\Task\\BuiltIn\\'.str_replace('/', '\\', $file->getRelativePathname()), 0, -4); + $reflex = new ReflectionClass($class); + + // Some classes found in the folder can be abstract + if (!$reflex->isInstantiable()) { + continue; } + + $this->createTask($class); + } + } + + /** + * Create the task + * + * @param string $class + * + * @return TaskInterface + * + * @throws RuntimeException + */ + private function createTask($class) + { + $this->validateTask($class); + + /** @var TaskInterface $task */ + $task = new $class(); + + // Register the task + $this->addTask($task); + + return $task; + } + + /** + * Validate the task + * + * @param string $class + * + * @throws RuntimeException + */ + private function validateTask($class) + { + if (!class_exists($class)) { + throw new RuntimeException(sprintf('The class "%s" does not exist', $class)); + } + + $reflex = new ReflectionClass($class); + + if (!$reflex->implementsInterface(TaskInterface::class)) { + throw new RuntimeException( + sprintf('The class "%s" must implement the "%s" interface', $class, TaskInterface::class) + ); } } } diff --git a/src/Task/TaskInterface.php b/src/Task/TaskInterface.php new file mode 100644 index 0000000..301e6d3 --- /dev/null +++ b/src/Task/TaskInterface.php @@ -0,0 +1,60 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Mage\Task; + +use Mage\Runtime\Runtime; + +/** + * The task interface + * + * @author Kamil Kuzminski + */ +interface TaskInterface +{ + /** + * Get the Name/Code of the Task + * + * @return string + */ + public function getName(); + + /** + * Get a short Description of the Task + * + * @return string + */ + public function getDescription(); + + /** + * Executes the Command + * + * @return bool + */ + public function execute(); + + /** + * Set additional Options for the Task + * + * @param array $options Options + * + * @return TaskInterface + */ + public function setOptions($options = []); + + /** + * Set the Runtime instance + * + * @param Runtime $runtime + * + * @return TaskInterface + */ + public function setRuntime(Runtime $runtime); +} diff --git a/tests/Command/BuiltIn/DeployCommandMiscTasksTest.php b/tests/Command/BuiltIn/DeployCommandMiscTasksTest.php index ae13f8b..9869f21 100644 --- a/tests/Command/BuiltIn/DeployCommandMiscTasksTest.php +++ b/tests/Command/BuiltIn/DeployCommandMiscTasksTest.php @@ -122,7 +122,7 @@ class DeployCommandMiscTasksTest extends TestCase $tester->execute(['command' => $command->getName(), 'environment' => 'test']); $this->assertEquals(7, $tester->getStatusCode()); - $this->assertContains('Invalid task name "invalid/task"', $tester->getDisplay()); + $this->assertContains('The class "invalid/task" does not exist', $tester->getDisplay()); } public function testBrokenGitBranch() diff --git a/tests/Task/TaskFactoryTest.php b/tests/Task/TaskFactoryTest.php index 8972568..2428719 100644 --- a/tests/Task/TaskFactoryTest.php +++ b/tests/Task/TaskFactoryTest.php @@ -18,29 +18,31 @@ use PHPUnit_Framework_TestCase as TestCase; class TaskFactoryTest extends TestCase { - public function testNonInstantiable() + public function testClassNotExists() { - $runtime = new Runtime(); - $factory = new TaskFactory($runtime); - try { - $factory->get('Traversable'); + $this->getFactory()->get('Foobar'); } catch (Exception $exception) { $this->assertTrue($exception instanceof RuntimeException); - $this->assertEquals('Invalid task name "Traversable"', $exception->getMessage()); + $this->assertEquals('The class "Foobar" does not exist', $exception->getMessage()); } } - public function testNotExtendingAbstractTask() + public function testNotImplementingInterface() { - $runtime = new Runtime(); - $factory = new TaskFactory($runtime); - try { - $factory->get('stdClass'); + $this->getFactory()->get('stdClass'); } catch (Exception $exception) { $this->assertTrue($exception instanceof RuntimeException); - $this->assertEquals('Invalid task name "stdClass"', $exception->getMessage()); + $this->assertEquals( + 'The class "stdClass" must implement the "Mage\Task\TaskInterface" interface', + $exception->getMessage() + ); } } + + private function getFactory() + { + return new TaskFactory(new Runtime()); + } }