2 namespace Robo\Collection;
6 use Robo\Task\BaseTask;
7 use Robo\Contract\BuilderAwareInterface;
8 use Robo\Common\BuilderAwareTrait;
11 * Creates a task wrapper that converts any Callable into an
12 * object that will execute the callback once for each item in the
13 * provided collection.
15 * It is not necessary to use this class directly; Collection::addIterable
16 * will automatically create one when it is called.
18 class TaskForEach extends BaseTask implements NestedCollectionInterface, BuilderAwareInterface
20 use BuilderAwareTrait;
25 protected $functionStack = [];
30 protected $countingStack = [];
40 protected $context = [];
43 * @var array $iterable
45 protected $iterable = [];
48 * @var \Robo\Collection\NestedCollectionInterface
50 protected $parentCollection;
53 * @var array $iterable
55 public function __construct($iterable = [])
57 $this->setIterable($iterable);
61 * @param array $iterable
65 public function setIterable($iterable)
67 $this->iterable = $iterable;
73 * @param string $message
74 * @param array $context
78 public function iterationMessage($message, $context = [])
80 $this->message = $message;
81 $this->context = $context + ['name' => 'Progress'];
86 * @param int|string $key
89 protected function showIterationMessage($key, $value)
92 $context = ['key' => $key, 'value' => $value];
93 $context += $this->context;
94 $context += TaskInfo::getTaskContext($this);
95 $this->printTaskInfo($this->message, $context);
100 * @param callable $fn
104 public function withEachKeyValueCall(callable $fn)
106 $this->functionStack[] = $fn;
111 * @param callable $fn
113 * @return \Robo\Collection\TaskForEach
115 public function call(callable $fn)
117 return $this->withEachKeyValueCall(
118 function ($key, $value) use ($fn) {
119 return call_user_func($fn, $value);
125 * @param callable $fn
127 * @return \Robo\Collection\TaskForEach
129 public function withBuilder(callable $fn)
131 $this->countingStack[] =
132 function ($key, $value) use ($fn) {
133 // Create a new builder for every iteration
134 $builder = $this->collectionBuilder();
135 // The user function should build task operations using
136 // the $key / $value parameters; we will call run() on
137 // the builder thus constructed.
138 call_user_func($fn, $builder, $key, $value);
139 return $builder->getCollection()->progressIndicatorSteps();
141 return $this->withEachKeyValueCall(
142 function ($key, $value) use ($fn) {
143 // Create a new builder for every iteration
144 $builder = $this->collectionBuilder()
145 ->setParentCollection($this->parentCollection);
146 // The user function should build task operations using
147 // the $key / $value parameters; we will call run() on
148 // the builder thus constructed.
149 call_user_func($fn, $builder, $key, $value);
150 return $builder->run();
158 public function setParentCollection(NestedCollectionInterface $parentCollection)
160 $this->parentCollection = $parentCollection;
167 public function progressIndicatorSteps()
169 $multiplier = count($this->functionStack);
170 if (!empty($this->countingStack) && count($this->iterable)) {
171 $value = reset($this->iterable);
172 $key = key($this->iterable);
173 foreach ($this->countingStack as $fn) {
174 $multiplier += call_user_func($fn, $key, $value);
177 return count($this->iterable) * $multiplier;
183 public function run()
185 $finalResult = Result::success($this);
186 $this->startProgressIndicator();
187 foreach ($this->iterable as $key => $value) {
188 $this->showIterationMessage($key, $value);
190 foreach ($this->functionStack as $fn) {
191 $result = call_user_func($fn, $key, $value);
192 $this->advanceProgressIndicator();
193 if (!isset($result)) {
194 $result = Result::success($this);
196 // If the function returns a result, it must either return
197 // a \Robo\Result or an exit code. In the later case, we
198 // convert it to a \Robo\Result.
199 if (!$result instanceof Result) {
200 $result = new Result($this, $result);
202 if (!$result->wasSuccessful()) {
205 $finalResult = $result->merge($finalResult);
207 } catch (\Exception $e) {
208 return Result::fromException($result, $e);
211 $this->stopProgressIndicator();