Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / vendor / consolidation / robo / src / Task / StackBasedTask.php
diff --git a/vendor/consolidation/robo/src/Task/StackBasedTask.php b/vendor/consolidation/robo/src/Task/StackBasedTask.php
new file mode 100644 (file)
index 0000000..91659f3
--- /dev/null
@@ -0,0 +1,236 @@
+<?php
+namespace Robo\Task;
+
+use Robo\Result;
+
+/**
+ * Extend StackBasedTask to create a Robo task that
+ * runs a sequence of commands.
+ *
+ * This is particularly useful for wrapping an existing
+ * object-oriented API.  Doing it this way requires
+ * less code than manually adding a method for each wrapped
+ * function in the delegate.  Additionally, wrapping the
+ * external class in a StackBasedTask creates a loosely-coupled
+ * interface -- i.e. if a new method is added to the delegate
+ * class, it is not necessary to update your wrapper, as the
+ * new functionality will be inherited.
+ *
+ * For example, you have:
+ *
+ *   $frobinator = new Frobinator($a, $b, $c)
+ *      ->friz()
+ *      ->fraz()
+ *      ->frob();
+ *
+ * We presume that the existing library throws an exception on error.
+ *
+ * You want:
+ *
+ *   $result = $this->taskFrobinator($a, $b, $c)
+ *      ->friz()
+ *      ->fraz()
+ *      ->frob()
+ *      ->run();
+ *
+ * Execution is deferred until run(), and a Robo\Result instance is
+ * returned. Additionally, using Robo will covert Exceptions
+ * into RoboResult objects.
+ *
+ * To create a new Robo task:
+ *
+ *  - Make a new class that extends StackBasedTask
+ *  - Give it a constructor that creates a new Frobinator
+ *  - Override getDelegate(), and return the Frobinator instance
+ *
+ * Finally, add your new class to loadTasks.php as usual,
+ * and you are all done.
+ *
+ * If you need to add any methods to your task that should run
+ * immediately (e.g. to set parameters used at run() time), just
+ * implement them in your derived class.
+ *
+ * If you need additional methods that should run deferred, just
+ * define them as 'protected function _foo()'.  Then, users may
+ * call $this->taskFrobinator()->foo() to get deferred execution
+ * of _foo().
+ */
+abstract class StackBasedTask extends BaseTask
+{
+    /**
+     * @var array
+     */
+    protected $stack = [];
+
+    /**
+     * @var bool
+     */
+    protected $stopOnFail = true;
+
+    /**
+     * @param bool $stop
+     *
+     * @return $this
+     */
+    public function stopOnFail($stop = true)
+    {
+        $this->stopOnFail = $stop;
+        return $this;
+    }
+
+    /**
+     * Derived classes should override the getDelegate() method, and
+     * return an instance of the API class being wrapped.  When this
+     * is done, any method of the delegate is available as a method of
+     * this class.  Calling one of the delegate's methods will defer
+     * execution until the run() method is called.
+     *
+     * @return null
+     */
+    protected function getDelegate()
+    {
+        return null;
+    }
+
+    /**
+     * Derived classes that have more than one delegate may override
+     * getCommandList to add as many delegate commands as desired to
+     * the list of potential functions that __call() tried to find.
+     *
+     * @param string $function
+     *
+     * @return array
+     */
+    protected function getDelegateCommandList($function)
+    {
+        return [[$this, "_$function"], [$this->getDelegate(), $function]];
+    }
+
+    /**
+     * Print progress about the commands being executed
+     *
+     * @param string $command
+     * @param string $action
+     */
+    protected function printTaskProgress($command, $action)
+    {
+        $this->printTaskInfo('{command} {action}', ['command' => "{$command[1]}", 'action' => json_encode($action, JSON_UNESCAPED_SLASHES)]);
+    }
+
+    /**
+     * Derived classes can override processResult to add more
+     * logic to result handling from functions. By default, it
+     * is assumed that if a function returns in int, then
+     * 0 == success, and any other value is the error code.
+     *
+     * @param int|\Robo\Result $function_result
+     *
+     * @return \Robo\Result
+     */
+    protected function processResult($function_result)
+    {
+        if (is_int($function_result)) {
+            if ($function_result) {
+                return Result::error($this, $function_result);
+            }
+        }
+        return Result::success($this);
+    }
+
+    /**
+     * Record a function to call later.
+     *
+     * @param string $command
+     * @param array $args
+     *
+     * @return $this
+     */
+    protected function addToCommandStack($command, $args)
+    {
+        $this->stack[] = array_merge([$command], $args);
+        return $this;
+    }
+
+    /**
+     * Any API function provided by the delegate that executes immediately
+     * may be handled by __call automatically.  These operations will all
+     * be deferred until this task's run() method is called.
+     *
+     * @param string $function
+     * @param array $args
+     *
+     * @return $this
+     */
+    public function __call($function, $args)
+    {
+        foreach ($this->getDelegateCommandList($function) as $command) {
+            if (method_exists($command[0], $command[1])) {
+                // Otherwise, we'll defer calling this function
+                // until run(), and return $this.
+                $this->addToCommandStack($command, $args);
+                return $this;
+            }
+        }
+
+        $message = "Method $function does not exist.\n";
+        throw new \BadMethodCallException($message);
+    }
+
+    /**
+     * @return int
+     */
+    public function progressIndicatorSteps()
+    {
+        // run() will call advanceProgressIndicator() once for each
+        // file, one after calling stopBuffering, and again after compression.
+        return count($this->stack);
+    }
+
+    /**
+     * Run all of the queued objects on the stack
+     *
+     * @return \Robo\Result
+     */
+    public function run()
+    {
+        $this->startProgressIndicator();
+        $result = Result::success($this);
+
+        foreach ($this->stack as $action) {
+            $command = array_shift($action);
+            $this->printTaskProgress($command, $action);
+            $this->advanceProgressIndicator();
+            // TODO: merge data from the result on this call
+            // with data from the result on the previous call?
+            // For now, the result always comes from the last function.
+            $result = $this->callTaskMethod($command, $action);
+            if ($this->stopOnFail && $result && !$result->wasSuccessful()) {
+                break;
+            }
+        }
+
+        $this->stopProgressIndicator();
+
+        // todo: add timing information to the result
+        return $result;
+    }
+
+    /**
+     * Execute one task method
+     *
+     * @param string $command
+     * @param string $action
+     *
+     * @return \Robo\Result
+     */
+    protected function callTaskMethod($command, $action)
+    {
+        try {
+            $function_result = call_user_func_array($command, $action);
+            return $this->processResult($function_result);
+        } catch (\Exception $e) {
+            $this->printTaskError($e->getMessage());
+            return Result::fromException($this, $e);
+        }
+    }
+}