Refactoring

This commit is contained in:
gamonoid
2017-09-03 20:39:22 +02:00
parent af40881847
commit a7274d3cfd
5075 changed files with 238202 additions and 16291 deletions

View File

@@ -0,0 +1,46 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Robo\Collection\CollectionBuilder;
trait BuilderAwareTrait
{
/**
* @var \Robo\Collection\CollectionBuilder
*/
protected $builder;
/**
* @see \Robo\Contract\BuilderAwareInterface::setBuilder()
*
* @param \Robo\Collection\CollectionBuilder $builder
*
* @return $this
*/
public function setBuilder(CollectionBuilder $builder)
{
$this->builder = $builder;
return $this;
}
/**
* @see \Robo\Contract\BuilderAwareInterface::getBuilder()
*
* @return \Robo\Collection\CollectionBuilder
*/
public function getBuilder()
{
return $this->builder;
}
/**
* @return \Robo\Collection\CollectionBuilder
*/
protected function collectionBuilder()
{
return $this->getBuilder()->newBuilder();
}
}

View File

@@ -0,0 +1,111 @@
<?php
namespace Robo\Common;
use Symfony\Component\Process\ProcessUtils;
/**
* Use this to add arguments and options to the $arguments property.
*/
trait CommandArguments
{
/**
* @var string
*/
protected $arguments = '';
/**
* Pass argument to executable. Its value will be automatically escaped.
*
* @param string $arg
*
* @return $this
*/
public function arg($arg)
{
return $this->args($arg);
}
/**
* Pass methods parameters as arguments to executable. Argument values
* are automatically escaped.
*
* @param string|string[] $args
*
* @return $this
*/
public function args($args)
{
if (!is_array($args)) {
$args = func_get_args();
}
$this->arguments .= ' ' . implode(' ', array_map('static::escape', $args));
return $this;
}
/**
* Pass the provided string in its raw (as provided) form as an argument to executable.
*
* @param string $arg
*/
public function rawArg($arg)
{
$this->arguments .= " $arg";
}
/**
* Escape the provided value, unless it contains only alphanumeric
* plus a few other basic characters.
*
* @param string $value
*
* @return string
*/
public static function escape($value)
{
if (preg_match('/^[a-zA-Z0-9\/\.@~_-]+$/', $value)) {
return $value;
}
return ProcessUtils::escapeArgument($value);
}
/**
* Pass option to executable. Options are prefixed with `--` , value can be provided in second parameter.
* Option values are automatically escaped.
*
* @param string $option
* @param string $value
*
* @return $this
*/
public function option($option, $value = null)
{
if ($option !== null and strpos($option, '-') !== 0) {
$option = "--$option";
}
$this->arguments .= null == $option ? '' : " " . $option;
$this->arguments .= null == $value ? '' : " " . static::escape($value);
return $this;
}
/**
* Pass multiple options to executable. Value can be a string or array.
* Option values are automatically escaped.
*
* @param string $option
* @param string|array $value
*
* @return $this
*/
public function optionList($option, $value = array())
{
if (is_array($value)) {
foreach ($value as $item) {
$this->optionList($option, $item);
}
} else {
$this->option($option, $value);
}
return $this;
}
}

View File

@@ -0,0 +1,30 @@
<?php
namespace Robo\Common;
use Robo\Contract\CommandInterface;
use Robo\Exception\TaskException;
/**
* This task can receive commands from task implementing CommandInterface.
*/
trait CommandReceiver
{
/**
* @param string|\Robo\Contract\CommandInterface $command
*
* @return string
*
* @throws \Robo\Exception\TaskException
*/
protected function receiveCommand($command)
{
if (!is_object($command)) {
return $command;
}
if ($command instanceof CommandInterface) {
return $command->getCommand();
} else {
throw new TaskException($this, get_class($command) . " does not implement CommandInterface, so can't be passed into this task");
}
}
}

View File

@@ -0,0 +1,77 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Robo\Config;
trait ConfigAwareTrait
{
/**
* @var \Robo\Config
*/
protected $config;
/**
* Set the config management object.
*
* @param \Robo\Config $config
*
* @return $this
*
* @see \Robo\Contract\ConfigAwareInterface::setConfig()
*/
public function setConfig(Config $config)
{
$this->config = $config;
return $this;
}
/**
* Get the config management object.
*
* @return \Robo\Config
*
* @see \Robo\Contract\ConfigAwareInterface::getConfig()
*/
public function getConfig()
{
return $this->config;
}
/**
* @param string $key
*
* @return string
*/
private static function getClassKey($key)
{
return sprintf('%s.%s', get_called_class(), $key);
}
/**
* @param string $key
* @param mixed $value
*
* @deprecated
*/
public static function configure($key, $value)
{
Robo::config()->set(static::getClassKey($key), $value);
}
/**
* @param string $key
* @param mixed|null $default
*
* @return mixed|null
*/
protected function getConfigValue($key, $default = null)
{
if (!$this->getConfig()) {
return $default;
}
return $this->getConfig()->get(static::getClassKey($key), $default);
}
}

View File

@@ -0,0 +1,45 @@
<?php
namespace Robo\Common;
/**
* Simplifies generating of configuration chanined methods.
* You can only define configuration properties and use magic methods to set them.
* Methods will be named the same way as properties.
* * Boolean properties are switched on/off if no values is provided.
* * Array properties can accept non-array values, in this case value will be appended to array.
* You should also define phpdoc for methods.
*/
trait DynamicParams
{
/**
* @param string $property
* @param array $args
*
* @return $this
*/
public function __call($property, $args)
{
if (!property_exists($this, $property)) {
throw new \RuntimeException("Property $property in task ".get_class($this).' does not exists');
}
// toggle boolean values
if (!isset($args[0]) and (is_bool($this->$property))) {
$this->$property = !$this->$property;
return $this;
}
// append item to array
if (is_array($this->$property)) {
if (is_array($args[0])) {
$this->$property = $args[0];
} else {
array_push($this->$property, $args[0]);
}
return $this;
}
$this->$property = $args[0];
return $this;
}
}

View File

@@ -0,0 +1,197 @@
<?php
namespace Robo\Common;
use Robo\Result;
use Symfony\Component\Process\ExecutableFinder;
use Symfony\Component\Process\Process;
/**
* This task is supposed to be executed as shell command.
* You can specify working directory and if output is printed.
*/
trait ExecCommand
{
/**
* @var bool
*/
protected $isPrinted = true;
/**
* @var string
*/
protected $workingDirectory;
/**
* @var \Robo\Common\TimeKeeper
*/
protected $execTimer;
/**
* @return \Robo\Common\TimeKeeper
*/
protected function getExecTimer()
{
if (!isset($this->execTimer)) {
$this->execTimer = new TimeKeeper();
}
return $this->execTimer;
}
/**
* Is command printing its output to screen
*
* @return bool
*/
public function getPrinted()
{
return $this->isPrinted;
}
/**
* Changes working directory of command
*
* @param string $dir
*
* @return $this
*/
public function dir($dir)
{
$this->workingDirectory = $dir;
return $this;
}
/**
* Should command output be printed
*
* @param bool $arg
*
* @return $this
*/
public function printed($arg)
{
if (is_bool($arg)) {
$this->isPrinted = $arg;
}
return $this;
}
/**
* Look for a "{$cmd}.phar" in the current working
* directory; return a string to exec it if it is
* found. Otherwise, look for an executable command
* of the same name via findExecutable.
*
* @param string $cmd
*
* @return bool|string
*/
protected function findExecutablePhar($cmd)
{
if (file_exists("{$cmd}.phar")) {
return "php {$cmd}.phar";
}
return $this->findExecutable($cmd);
}
/**
* Return the best path to the executable program
* with the provided name. Favor vendor/bin in the
* current project. If not found there, use
* whatever is on the $PATH.
*
* @param string $cmd
*
* @return bool|string
*/
protected function findExecutable($cmd)
{
$pathToCmd = $this->searchForExecutable($cmd);
if ($pathToCmd) {
return $this->useCallOnWindows($pathToCmd);
}
return false;
}
/**
* @param string $cmd
*
* @return string
*/
private function searchForExecutable($cmd)
{
$projectBin = $this->findProjectBin();
$localComposerInstallation = $projectBin . DIRECTORY_SEPARATOR . $cmd;
if (file_exists($localComposerInstallation)) {
return $localComposerInstallation;
}
$finder = new ExecutableFinder();
return $finder->find($cmd, null, []);
}
/**
* @return bool|string
*/
protected function findProjectBin()
{
$candidates = [ __DIR__ . '/../../vendor/bin', __DIR__ . '/../../bin' ];
// If this project is inside a vendor directory, give highest priority
// to that directory.
$vendorDirContainingUs = realpath(__DIR__ . '/../../../..');
if (is_dir($vendorDirContainingUs) && (basename($vendorDirContainingUs) == 'vendor')) {
array_unshift($candidates, $vendorDirContainingUs . '/bin');
}
foreach ($candidates as $dir) {
if (is_dir("$dir")) {
return realpath($dir);
}
}
return false;
}
/**
* Wrap Windows executables in 'call' per 7a88757d
*
* @param string $cmd
*
* @return string
*/
protected function useCallOnWindows($cmd)
{
if (defined('PHP_WINDOWS_VERSION_BUILD')) {
if (file_exists("{$cmd}.bat")) {
$cmd = "{$cmd}.bat";
}
return "call $cmd";
}
return $cmd;
}
/**
* @param string $command
*
* @return \Robo\Result
*/
protected function executeCommand($command)
{
$process = new Process($command);
$process->setTimeout(null);
if ($this->workingDirectory) {
$process->setWorkingDirectory($this->workingDirectory);
}
$this->getExecTimer()->start();
if ($this->isPrinted) {
$process->run(function ($type, $buffer) {
print $buffer;
});
} else {
$process->run();
}
$this->getExecTimer()->stop();
return new Result($this, $process->getExitCode(), $process->getOutput(), ['time' => $this->getExecTimer()->elapsed()]);
}
}

View File

@@ -0,0 +1,12 @@
<?php
namespace Robo\Common;
/**
* This task specifies exactly one shell command.
* It can take additional arguments and options as config parameters.
*/
trait ExecOneCommand
{
use ExecCommand;
use CommandArguments;
}

View File

@@ -0,0 +1,176 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Symfony\Component\Console\Helper\QuestionHelper;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\ConfirmationQuestion;
use Symfony\Component\Console\Question\Question;
use Symfony\Component\Console\Style\SymfonyStyle;
trait IO
{
use InputAwareTrait;
use OutputAwareTrait;
/**
* @var \Symfony\Component\Console\Style\SymfonyStyle
*/
protected $io;
/**
* Provide access to SymfonyStyle object.
*
* @return SymfonyStyle
*
* @see http://symfony.com/blog/new-in-symfony-2-8-console-style-guide
*/
protected function io()
{
if (!$this->io) {
$this->io = new SymfonyStyle($this->input(), $this->output());
}
return $this->io;
}
/**
* @param string $nonDecorated
* @param string $decorated
*
* @return string
*/
protected function decorationCharacter($nonDecorated, $decorated)
{
if (!$this->output()->isDecorated() || (strncasecmp(PHP_OS, 'WIN', 3) == 0)) {
return $nonDecorated;
}
return $decorated;
}
/**
* @param string $text
*/
protected function say($text)
{
$char = $this->decorationCharacter('>', '➜');
$this->writeln("$char $text");
}
/**
* @param string $text
* @param int $length
* @param string $color
*/
protected function yell($text, $length = 40, $color = 'green')
{
$char = $this->decorationCharacter(' ', '➜');
$format = "$char <fg=white;bg=$color;options=bold>%s</fg=white;bg=$color;options=bold>";
$this->formattedOutput($text, $length, $format);
}
/**
* @param string $text
* @param int $length
* @param string $format
*/
private function formattedOutput($text, $length, $format)
{
$lines = explode("\n", trim($text, "\n"));
$maxLineLength = array_reduce(array_map('strlen', $lines), 'max');
$length = max($length, $maxLineLength);
$len = $length + 2;
$space = str_repeat(' ', $len);
$this->writeln(sprintf($format, $space));
foreach ($lines as $line) {
$line = str_pad($line, $length, ' ', STR_PAD_BOTH);
$this->writeln(sprintf($format, " $line "));
}
$this->writeln(sprintf($format, $space));
}
/**
* @param string $question
* @param bool $hideAnswer
*
* @return string
*/
protected function ask($question, $hideAnswer = false)
{
if ($hideAnswer) {
return $this->askHidden($question);
}
return $this->doAsk(new Question($this->formatQuestion($question)));
}
/**
* @param string $question
*
* @return string
*/
protected function askHidden($question)
{
$question = new Question($this->formatQuestion($question));
$question->setHidden(true);
return $this->doAsk($question);
}
/**
* @param string $question
* @param string $default
*
* @return string
*/
protected function askDefault($question, $default)
{
return $this->doAsk(new Question($this->formatQuestion("$question [$default]"), $default));
}
/**
* @param string $question
*
* @return string
*/
protected function confirm($question)
{
return $this->doAsk(new ConfirmationQuestion($this->formatQuestion($question . ' (y/n)'), false));
}
/**
* @param \Symfony\Component\Console\Question\Question $question
*
* @return string
*/
private function doAsk(Question $question)
{
return $this->getDialog()->ask($this->input(), $this->output(), $question);
}
/**
* @param string $message
*
* @return string
*/
private function formatQuestion($message)
{
return "<question>? $message</question> ";
}
/**
* @return \Symfony\Component\Console\Helper\QuestionHelper
*/
protected function getDialog()
{
return new QuestionHelper();
}
/**
* @param $text
*/
private function writeln($text)
{
$this->output()->writeln($text);
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Robo\Common;
use Robo\Contract\InflectionInterface;
trait InflectionTrait
{
/**
* Ask the provided parent class to inject all of the dependencies
* that it has and we need.
*
* @param \Robo\Contract\InflectionInterface $parent
*
* @return $this
*/
public function inflect(InflectionInterface $parent)
{
$parent->injectDependencies($this);
return $this;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Robo\Common;
use Symfony\Component\Console\Input\ArgvInput;
use Symfony\Component\Console\Input\InputInterface;
trait InputAwareTrait
{
/**
* @var \Symfony\Component\Console\Input\InputInterface
*/
protected $input;
/**
* @param \Symfony\Component\Console\Input\InputInterface $input
*
* @return $this
*
* @see \Symfony\Component\Console\Input\InputAwareInterface::setInput()
*/
public function setInput(InputInterface $input)
{
$this->input = $input;
return $this;
}
/**
* @return \Symfony\Component\Console\Input\InputInterface
*/
protected function input()
{
if (!isset($this->input)) {
$this->setInput(new ArgvInput());
}
return $this->input;
}
/**
* Backwards compatibility.
*
* @return \Symfony\Component\Console\Input\InputInterface
*
* @deprecated
*/
protected function getInput()
{
return $this->input();
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Robo\Common;
use Symfony\Component\Console\Output\NullOutput;
use Symfony\Component\Console\Output\OutputInterface;
trait OutputAwareTrait
{
/**
* @var \Symfony\Component\Console\Output\OutputInterface
*/
protected $output;
/**
* @param \Symfony\Component\Console\Output\OutputInterface $output
*
* @return $this
*
* @see \Robo\Contract\OutputAwareInterface::setOutput()
*/
public function setOutput(OutputInterface $output)
{
$this->output = $output;
return $this;
}
/**
* @return \Symfony\Component\Console\Output\OutputInterface
*/
protected function output()
{
if (!isset($this->output)) {
$this->setOutput(new NullOutput());
}
return $this->output;
}
/**
* Backwards compatibility
*
* @return \Symfony\Component\Console\Output\OutputInterface
*
* @deprecated
*/
protected function getOutput()
{
return $this->output();
}
}

View File

@@ -0,0 +1,201 @@
<?php
namespace Robo\Common;
/**
* Wrapper around \Symfony\Component\Console\Helper\ProgressBar
*/
class ProgressIndicator
{
use Timer;
/**
* @var \Symfony\Component\Console\Helper\ProgressBar
*/
protected $progressBar;
/**
* @var \Symfony\Component\Console\Output\OutputInterface
*/
protected $output;
/**
* @var bool
*/
protected $progressIndicatorRunning = false;
/**
* @var int
*/
protected $autoDisplayInterval = 0;
/**
* @var int
*/
protected $cachedSteps = 0;
/**
* @var int
*/
protected $totalSteps = 0;
/**
* @var bool
*/
protected $progressBarDisplayed = false;
/**
* @var \Robo\Contract\TaskInterface
*/
protected $owner;
/**
* @param \Symfony\Component\Console\Helper\ProgressBar $progressBar
* @param \Symfony\Component\Console\Output\OutputInterface $output
*/
public function __construct($progressBar, \Symfony\Component\Console\Output\OutputInterface $output)
{
$this->progressBar = $progressBar;
$this->output = $output;
}
/**
* @param int $interval
*/
public function setProgressBarAutoDisplayInterval($interval)
{
if ($this->progressIndicatorRunning) {
return;
}
$this->autoDisplayInterval = $interval;
}
/**
* @return bool
*/
public function hideProgressIndicator()
{
$result = $this->progressBarDisplayed;
if ($this->progressIndicatorRunning && $this->progressBarDisplayed) {
$this->progressBar->clear();
// Hack: progress indicator does not reset cursor to beginning of line on 'clear'
$this->output->write("\x0D");
$this->progressBarDisplayed = false;
}
return $result;
}
public function showProgressIndicator()
{
if ($this->progressIndicatorRunning && !$this->progressBarDisplayed && isset($this->progressBar)) {
$this->progressBar->display();
$this->progressBarDisplayed = true;
$this->advanceProgressIndicatorCachedSteps();
}
}
/**
* @param bool $visible
*/
public function restoreProgressIndicator($visible)
{
if ($visible) {
$this->showProgressIndicator();
}
}
/**
* @param int $totalSteps
* @param \Robo\Contract\TaskInterface $owner
*/
public function startProgressIndicator($totalSteps, $owner)
{
if (!isset($this->progressBar)) {
return;
}
$this->progressIndicatorRunning = true;
if (!isset($this->owner)) {
$this->owner = $owner;
$this->startTimer();
$this->totalSteps = $totalSteps;
$this->autoShowProgressIndicator();
}
}
public function autoShowProgressIndicator()
{
if (($this->autoDisplayInterval < 0) || !isset($this->progressBar) || !$this->output->isDecorated()) {
return;
}
if ($this->autoDisplayInterval <= $this->getExecutionTime()) {
$this->autoDisplayInterval = -1;
$this->progressBar->start($this->totalSteps);
$this->showProgressIndicator();
}
}
/**
* @return bool
*/
public function inProgress()
{
return $this->progressIndicatorRunning;
}
/**
* @param \Robo\Contract\TaskInterface $owner
*/
public function stopProgressIndicator($owner)
{
if ($this->progressIndicatorRunning && ($this->owner === $owner)) {
$this->cleanup();
}
}
protected function cleanup()
{
$this->progressIndicatorRunning = false;
$this->owner = null;
if ($this->progressBarDisplayed) {
$this->progressBar->finish();
// Hack: progress indicator does not always finish cleanly
$this->output->writeln('');
$this->progressBarDisplayed = false;
}
$this->stopTimer();
}
/**
* Erase progress indicator and ensure it never returns. Used
* only during error handlers.
*/
public function disableProgressIndicator()
{
$this->cleanup();
// ProgressIndicator is shared, so this permanently removes
// the program's ability to display progress bars.
$this->progressBar = null;
}
/**
* @param int $steps
*/
public function advanceProgressIndicator($steps = 1)
{
$this->cachedSteps += $steps;
if ($this->progressIndicatorRunning) {
$this->autoShowProgressIndicator();
// We only want to call `advance` if the progress bar is visible,
// because it always displays itself when it is advanced.
if ($this->progressBarDisplayed) {
return $this->advanceProgressIndicatorCachedSteps();
}
}
}
protected function advanceProgressIndicatorCachedSteps()
{
$this->progressBar->advance($this->cachedSteps);
$this->cachedSteps = 0;
}
}

View File

@@ -0,0 +1,124 @@
<?php
namespace Robo\Common;
trait ProgressIndicatorAwareTrait
{
use Timer;
/**
* @var null|\Robo\Common\ProgressIndicator
*/
protected $progressIndicator;
/**
* @return int
*/
public function progressIndicatorSteps()
{
return 0;
}
/**
* @param null|\Robo\Common\ProgressIndicator $progressIndicator
*/
public function setProgressIndicator($progressIndicator)
{
$this->progressIndicator = $progressIndicator;
}
/**
* @return null|bool
*/
protected function hideProgressIndicator()
{
if (!$this->progressIndicator) {
return;
}
return $this->progressIndicator->hideProgressIndicator();
}
protected function showProgressIndicator()
{
if (!$this->progressIndicator) {
return;
}
$this->progressIndicator->showProgressIndicator();
}
/**
* @param bool $visible
*/
protected function restoreProgressIndicator($visible)
{
if (!$this->progressIndicator) {
return;
}
$this->progressIndicator->restoreProgressIndicator($visible);
}
/**
* @return int
*/
protected function getTotalExecutionTime()
{
if (!$this->progressIndicator) {
return 0;
}
return $this->progressIndicator->getExecutionTime();
}
protected function startProgressIndicator()
{
$this->startTimer();
if (!$this->progressIndicator) {
return;
}
$totalSteps = $this->progressIndicatorSteps();
$this->progressIndicator->startProgressIndicator($totalSteps, $this);
}
/**
* @return bool
*/
protected function inProgress()
{
if (!$this->progressIndicator) {
return false;
}
return $this->progressIndicator->inProgress();
}
protected function stopProgressIndicator()
{
$this->stopTimer();
if (!$this->progressIndicator) {
return;
}
$this->progressIndicator->stopProgressIndicator($this);
}
protected function disableProgressIndicator()
{
$this->stopTimer();
if (!$this->progressIndicator) {
return;
}
$this->progressIndicator->disableProgressIndicator();
}
protected function detatchProgressIndicator()
{
$this->setProgressIndicator(null);
}
/**
* @param int $steps
*/
protected function advanceProgressIndicator($steps = 1)
{
if (!$this->progressIndicator) {
return;
}
$this->progressIndicator->advanceProgressIndicator($steps);
}
}

View File

@@ -0,0 +1,116 @@
<?php
namespace Robo\Common;
trait ResourceExistenceChecker
{
/**
* Checks if the given input is a file or folder.
*
* @param string|string[] $resources
* @param string $type "file", "dir", "fileAndDir"
*
* @return bool True if no errors were encountered otherwise false.
*/
protected function checkResources($resources, $type = 'fileAndDir')
{
if (!in_array($type, ['file', 'dir', 'fileAndDir'])) {
throw new \InvalidArgumentException(sprintf('Invalid resource check of type "%s" used!', $type));
}
if (is_string($resources)) {
$resources = [$resources];
}
$success = true;
foreach ($resources as $resource) {
$glob = glob($resource);
if ($glob === false) {
$this->printTaskError(sprintf('Invalid glob "%s"!', $resource), $this);
$success = false;
continue;
}
foreach ($glob as $resource) {
if (!$this->checkResource($resource, $type)) {
$success = false;
}
}
}
return $success;
}
/**
* Checks a single resource, file or directory.
*
* It will print an error as well on the console.
*
* @param string $resource File or folder.
* @param string $type "file", "dir", "fileAndDir"
*
* @return bool
*/
protected function checkResource($resource, $type)
{
switch ($type) {
case 'file':
if (!$this->isFile($resource)) {
$this->printTaskError(sprintf('File "%s" does not exist!', $resource), $this);
return false;
}
return true;
case 'dir':
if (!$this->isDir($resource)) {
$this->printTaskError(sprintf('Directory "%s" does not exist!', $resource), $this);
return false;
}
return true;
case 'fileAndDir':
if (!$this->isDir($resource) && !$this->isFile($resource)) {
$this->printTaskError(sprintf('File or directory "%s" does not exist!', $resource), $this);
return false;
}
return true;
}
}
/**
* Convenience method to check the often uses "source => target" file / folder arrays.
*
* @param string|array $resources
*/
protected function checkSourceAndTargetResource($resources)
{
if (is_string($resources)) {
$resources = [$resources];
}
$sources = [];
$targets = [];
foreach ($resources as $source => $target) {
$sources[] = $source;
$target[] = $target;
}
$this->checkResources($sources);
$this->checkResources($targets);
}
/**
* Wrapper method around phps is_dir()
*
* @param string $directory
*
* @return bool
*/
protected function isDir($directory)
{
return is_dir($directory);
}
/**
* Wrapper method around phps file_exists()
*
* @param string $file
*
* @return bool
*/
protected function isFile($file)
{
return file_exists($file);
}
}

View File

@@ -0,0 +1,235 @@
<?php
namespace Robo\Common;
use Robo\Robo;
use Robo\TaskInfo;
use Consolidation\Log\ConsoleLogLevel;
use Robo\Common\ConfigAwareTrait;
use Psr\Log\LoggerAwareTrait;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use Robo\Contract\ProgressIndicatorAwareInterface;
/**
* Task input/output methods. TaskIO is 'used' in BaseTask, so any
* task that extends this class has access to all of the methods here.
* printTaskInfo, printTaskSuccess, and printTaskError are the three
* primary output methods that tasks are encouraged to use. Tasks should
* avoid using the IO trait output methods.
*/
trait TaskIO
{
use LoggerAwareTrait;
use ConfigAwareTrait;
/**
* @return mixed|null|\Psr\Log\LoggerInterface
*/
public function logger()
{
// $this->logger should always be set in Robo core tasks.
if ($this->logger) {
return $this->logger;
}
// TODO: Remove call to Robo::logger() once maintaining backwards
// compatibility with legacy external Robo tasks is no longer desired.
if (!Robo::logger()) {
return null;
}
static $gaveDeprecationWarning = false;
if (!$gaveDeprecationWarning) {
trigger_error('No logger set for ' . get_class($this) . '. Use $this->task(Foo::class) rather than new Foo() in loadTasks to ensure the builder can initialize task the task, or use $this->collectionBuilder()->taskFoo() if creating one task from within another.', E_USER_DEPRECATED);
$gaveDeprecationWarning = true;
}
return Robo::logger();
}
/**
* Print information about a task in progress.
*
* With the Symfony Console logger, NOTICE is displayed at VERBOSITY_VERBOSE
* and INFO is displayed at VERBOSITY_VERY_VERBOSE.
*
* Robo overrides the default such that NOTICE is displayed at
* VERBOSITY_NORMAL and INFO is displayed at VERBOSITY_VERBOSE.
*
* n.b. We should probably have printTaskNotice for our ordinary
* output, and use printTaskInfo for less interesting messages.
*
* @param string $text
* @param null|array $context
*/
protected function printTaskInfo($text, $context = null)
{
// The 'note' style is used for both 'notice' and 'info' log levels;
// However, 'notice' is printed at VERBOSITY_NORMAL, whereas 'info'
// is only printed at VERBOSITY_VERBOSE.
$this->printTaskOutput(LogLevel::NOTICE, $text, $this->getTaskContext($context));
}
/**
* Provide notification that some part of the task succeeded.
*
* With the Symfony Console logger, success messages are remapped to NOTICE,
* and displayed in VERBOSITY_VERBOSE. When used with the Robo logger,
* success messages are displayed at VERBOSITY_NORMAL.
*
* @param string $text
* @param null|array $context
*/
protected function printTaskSuccess($text, $context = null)
{
// Not all loggers will recognize ConsoleLogLevel::SUCCESS.
// We therefore log as LogLevel::NOTICE, and apply a '_level'
// override in the context so that this message will be
// logged as SUCCESS if that log level is recognized.
$context['_level'] = ConsoleLogLevel::SUCCESS;
$this->printTaskOutput(LogLevel::NOTICE, $text, $this->getTaskContext($context));
}
/**
* Provide notification that there is something wrong, but
* execution can continue.
*
* Warning messages are displayed at VERBOSITY_NORMAL.
*
* @param string $text
* @param null|array $context
*/
protected function printTaskWarning($text, $context = null)
{
$this->printTaskOutput(LogLevel::WARNING, $text, $this->getTaskContext($context));
}
/**
* Provide notification that some operation in the task failed,
* and the task cannot continue.
*
* Error messages are displayed at VERBOSITY_NORMAL.
*
* @param string $text
* @param null|array $context
*/
protected function printTaskError($text, $context = null)
{
$this->printTaskOutput(LogLevel::ERROR, $text, $this->getTaskContext($context));
}
/**
* Provide debugging notification. These messages are only
* displayed if the log level is VERBOSITY_DEBUG.
*
* @param string$text
* @param null|array $context
*/
protected function printTaskDebug($text, $context = null)
{
$this->printTaskOutput(LogLevel::DEBUG, $text, $this->getTaskContext($context));
}
/**
* @param string $level
* One of the \Psr\Log\LogLevel constant
* @param string $text
* @param null|array $context
*/
protected function printTaskOutput($level, $text, $context)
{
$logger = $this->logger();
if (!$logger) {
return;
}
// Hide the progress indicator, if it is visible.
$inProgress = $this->hideTaskProgress();
$logger->log($level, $text, $this->getTaskContext($context));
// After we have printed our log message, redraw the progress indicator.
$this->showTaskProgress($inProgress);
}
/**
* @return bool
*/
protected function hideTaskProgress()
{
$inProgress = false;
if ($this instanceof ProgressIndicatorAwareInterface) {
$inProgress = $this->inProgress();
}
// If a progress indicator is running on this task, then we mush
// hide it before we print anything, or its display will be overwritten.
if ($inProgress) {
$inProgress = $this->hideProgressIndicator();
}
return $inProgress;
}
/**
* @param $inProgress
*/
protected function showTaskProgress($inProgress)
{
if ($inProgress) {
$this->restoreProgressIndicator($inProgress);
}
}
/**
* Format a quantity of bytes.
*
* @param int $size
* @param int $precision
*
* @return string
*/
protected function formatBytes($size, $precision = 2)
{
if ($size === 0) {
return 0;
}
$base = log($size, 1024);
$suffixes = array('', 'k', 'M', 'G', 'T');
return round(pow(1024, $base - floor($base)), $precision) . $suffixes[floor($base)];
}
/**
* Get the formatted task name for use in task output.
* This is placed in the task context under 'name', and
* used as the log label by Robo\Common\RoboLogStyle,
* which is inserted at the head of log messages by
* Robo\Common\CustomLogStyle::formatMessage().
*
* @param null|object $task
*
* @return string
*/
protected function getPrintedTaskName($task = null)
{
if (!$task) {
$task = $this;
}
return TaskInfo::formatTaskName($task);
}
/**
* @param null|array $context
*
* @return array with context information
*/
protected function getTaskContext($context = null)
{
if (!$context) {
$context = [];
}
if (!is_array($context)) {
$context = ['task' => $context];
}
if (!array_key_exists('task', $context)) {
$context['task'] = $this;
}
return $context + TaskInfo::getTaskContext($context['task']);
}
}

View File

@@ -0,0 +1,69 @@
<?php
namespace Robo\Common;
class TimeKeeper
{
const MINUTE = 60;
const HOUR = 3600;
const DAY = 86400;
/**
* @var float
*/
protected $startedAt;
/**
* @var float
*/
protected $finishedAt;
public function start()
{
if ($this->startedAt) {
return;
}
// Get time in seconds as a float, accurate to the microsecond.
$this->startedAt = microtime(true);
}
public function stop()
{
$this->finishedAt = microtime(true);
}
/**
* @return float|null
*/
public function elapsed()
{
$finished = $this->finishedAt ? $this->finishedAt : microtime(true);
if ($finished - $this->startedAt <= 0) {
return null;
}
return $finished - $this->startedAt;
}
/**
* Format a duration into a human-readable time
*
* @param float $duration Duration in seconds, with fractional component
*
* @return string
*/
public static function formatDuration($duration)
{
if ($duration >= self::DAY * 2) {
return gmdate('z \d\a\y\s H:i:s', $duration);
}
if ($duration > self::DAY) {
return gmdate('\1 \d\a\y H:i:s', $duration);
}
if ($duration > self::HOUR) {
return gmdate("H:i:s", $duration);
}
if ($duration > self::MINUTE) {
return gmdate("i:s", $duration);
}
return round($duration, 3).'s';
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Robo\Common;
trait Timer
{
/**
* @var \Robo\Common\TimeKeeper
*/
protected $timer;
protected function startTimer()
{
if (!isset($this->timer)) {
$this->timer = new TimeKeeper();
}
$this->timer->start();
}
protected function stopTimer()
{
if (!isset($this->timer)) {
return;
}
$this->timer->stop();
}
/**
* @return float|null
*/
protected function getExecutionTime()
{
if (!isset($this->timer)) {
return null;
}
return $this->timer->elapsed();
}
}