--- /dev/null
+<?php
+
+namespace Robo\Task\File;
+
+use Robo\Result;
+use Robo\Task\BaseTask;
+
+/**
+ * Writes to file.
+ *
+ * ``` php
+ * <?php
+ * $this->taskWriteToFile('blogpost.md')
+ * ->line('-----')
+ * ->line(date('Y-m-d').' '.$title)
+ * ->line('----')
+ * ->run();
+ * ?>
+ * ```
+ */
+class Write extends BaseTask
+{
+ /**
+ * @var array
+ */
+ protected $stack = [];
+
+ /**
+ * @var string
+ */
+ protected $filename;
+
+ /**
+ * @var bool
+ */
+ protected $append = false;
+
+ /**
+ * @var null|string
+ */
+ protected $originalContents = null;
+
+ /**
+ * @param string $filename
+ */
+ public function __construct($filename)
+ {
+ $this->filename = $filename;
+ }
+
+ /**
+ * @param string $filename
+ *
+ * @return $this
+ */
+ public function filename($filename)
+ {
+ $this->filename = $filename;
+ return $this;
+ }
+
+ /**
+ * @param bool $append
+ *
+ * @return $this
+ */
+ public function append($append = true)
+ {
+ $this->append = $append;
+ return $this;
+ }
+
+ /**
+ * add a line.
+ *
+ * @param string $line
+ *
+ * @return $this The current instance
+ */
+ public function line($line)
+ {
+ $this->text($line . "\n");
+ return $this;
+ }
+
+ /**
+ * add more lines.
+ *
+ * @param array $lines
+ *
+ * @return $this The current instance
+ */
+ public function lines(array $lines)
+ {
+ $this->text(implode("\n", $lines) . "\n");
+ return $this;
+ }
+
+ /**
+ * add a text.
+ *
+ * @param string $text
+ *
+ * @return $this The current instance
+ */
+ public function text($text)
+ {
+ $this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
+ return $this;
+ }
+
+ /**
+ * add a text from a file.
+ *
+ * Note that the file is read in the run() method of this task.
+ * To load text from the current state of a file (e.g. one that may
+ * be deleted or altered by other tasks prior the execution of this one),
+ * use:
+ * $task->text(file_get_contents($filename));
+ *
+ * @param string $filename
+ *
+ * @return $this The current instance
+ */
+ public function textFromFile($filename)
+ {
+ $this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
+ return $this;
+ }
+
+ /**
+ * substitute a placeholder with value, placeholder must be enclosed by `{}`.
+ *
+ * @param string $name
+ * @param string $val
+ *
+ * @return $this The current instance
+ */
+ public function place($name, $val)
+ {
+ $this->replace('{'.$name.'}', $val);
+
+ return $this;
+ }
+
+ /**
+ * replace any string with value.
+ *
+ * @param string $string
+ * @param string $replacement
+ *
+ * @return $this The current instance
+ */
+ public function replace($string, $replacement)
+ {
+ $this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
+ return $this;
+ }
+
+ /**
+ * replace any string with value using regular expression.
+ *
+ * @param string $pattern
+ * @param string $replacement
+ *
+ * @return $this The current instance
+ */
+ public function regexReplace($pattern, $replacement)
+ {
+ $this->stack[] = array_merge([__FUNCTION__ . 'Collect'], func_get_args());
+ return $this;
+ }
+
+ /**
+ * Append the provided text to the end of the buffer if the provided
+ * regex pattern matches any text already in the buffer.
+ *
+ * @param string $pattern
+ * @param string $text
+ *
+ * @return $this
+ */
+ public function appendIfMatches($pattern, $text)
+ {
+ $this->stack[] = array_merge(['appendIfMatchesCollect'], [$pattern, $text, true]);
+ return $this;
+ }
+
+ /**
+ * Append the provided text to the end of the buffer unless the provided
+ * regex pattern matches any text already in the buffer.
+ *
+ * @param string $pattern
+ * @param string $text
+ *
+ * @return $this
+ */
+ public function appendUnlessMatches($pattern, $text)
+ {
+ $this->stack[] = array_merge(['appendIfMatchesCollect'], [$pattern, $text, false]);
+ return $this;
+ }
+
+ /**
+ * @param $contents string
+ * @param $filename string
+ *
+ * @return string
+ */
+ protected function textFromFileCollect($contents, $filename)
+ {
+ if (file_exists($filename)) {
+ $contents .= file_get_contents($filename);
+ }
+ return $contents;
+ }
+
+ /**
+ * @param string|string[] $contents
+ * @param string|string[] $string
+ * @param string|string[] $replacement
+ *
+ * @return string|string[]
+ */
+ protected function replaceCollect($contents, $string, $replacement)
+ {
+ return str_replace($string, $replacement, $contents);
+ }
+
+ /**
+ * @param string|string[] $contents
+ * @param string|string[] $pattern
+ * @param string|string[] $replacement
+ *
+ * @return string|string[]
+ */
+ protected function regexReplaceCollect($contents, $pattern, $replacement)
+ {
+ return preg_replace($pattern, $replacement, $contents);
+ }
+
+ /**
+ * @param string $contents
+ * @param string $text
+ *
+ * @return string
+ */
+ protected function textCollect($contents, $text)
+ {
+ return $contents . $text;
+ }
+
+ /**
+ * @param string $contents
+ * @param string $pattern
+ * @param string $text
+ * @param bool $shouldMatch
+ *
+ * @return string
+ */
+ protected function appendIfMatchesCollect($contents, $pattern, $text, $shouldMatch)
+ {
+ if (preg_match($pattern, $contents) == $shouldMatch) {
+ $contents .= $text;
+ }
+ return $contents;
+ }
+
+ /**
+ * @return string
+ */
+ public function originalContents()
+ {
+ if (!isset($this->originalContents)) {
+ $this->originalContents = '';
+ if (file_exists($this->filename)) {
+ $this->originalContents = file_get_contents($this->filename);
+ }
+ }
+ return $this->originalContents;
+ }
+
+ /**
+ * @return bool
+ */
+ public function wouldChange()
+ {
+ return $this->originalContents() != $this->getContentsToWrite();
+ }
+
+ /**
+ * @return string
+ */
+ protected function getContentsToWrite()
+ {
+ $contents = "";
+ if ($this->append) {
+ $contents = $this->originalContents();
+ }
+ foreach ($this->stack as $action) {
+ $command = array_shift($action);
+ if (method_exists($this, $command)) {
+ array_unshift($action, $contents);
+ $contents = call_user_func_array([$this, $command], $action);
+ }
+ }
+ return $contents;
+ }
+
+ /**
+ * {@inheritdoc}
+ */
+ public function run()
+ {
+ $this->printTaskInfo("Writing to {filename}.", ['filename' => $this->filename]);
+ $contents = $this->getContentsToWrite();
+ if (!file_exists(dirname($this->filename))) {
+ mkdir(dirname($this->filename), 0777, true);
+ }
+ $res = file_put_contents($this->filename, $contents);
+ if ($res === false) {
+ return Result::error($this, "File {$this->filename} couldn't be created");
+ }
+
+ return Result::success($this);
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath()
+ {
+ return $this->filename;
+ }
+}