Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Console] Lazy commands, separate IO concerns, add forced resolve to command, and prittify and detail output #981

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 2 additions & 9 deletions .travis.yml
Expand Up @@ -14,22 +14,15 @@ env:

matrix:
include:
- php: hhvm-stable
sudo: required
dist: trusty
env: SYMFONY_VERSION=3.0.x-dev
- php: 7.0
env: SYMFONY_VERSION=3.0.x-dev
- php: 7.1
env: SYMFONY_VERSION=3.1.x-dev
- php: 7.1
env: SYMFONY_VERSION=3.2.x-dev
- php: 7.1
env: SYMFONY_VERSION=3.3.x-dev
- php: 7.1
env: SYMFONY_VERSION=3.4.x-dev
- php: 7.1
env: SYMFONY_VERSION=dev-master
allow_failures:
- php: hhvm-stable
- env: SYMFONY_VERSION=dev-master
fast_finish: true

Expand Down
151 changes: 151 additions & 0 deletions Command/CacheCommandTrait.php
@@ -0,0 +1,151 @@
<?php

/*
* This file is part of the `liip/LiipImagineBundle` project.
*
* (c) https://github.com/liip/LiipImagineBundle/graphs/contributors
*
* For the full copyright and license information, please view the LICENSE.md
* file that was distributed with this source code.
*/

namespace Liip\ImagineBundle\Command;

use Imagine\Exception\RuntimeException;
use Liip\ImagineBundle\Component\Console\Style\ImagineStyle;
use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @internal
*/
trait CacheCommandTrait
{
/**
* @var CacheManager
*/
private $cacheManager;

/**
* @var FilterManager
*/
private $filterManager;

/**
* @var ImagineStyle
*/
private $io;

/**
* @var bool
*/
private $outputMachineReadable;

/**
* @var int
*/
private $failures = 0;

/**
* @param InputInterface $input
* @param OutputInterface $output
*/
private function setupOutputStyle(InputInterface $input, OutputInterface $output): void
{
$this->outputMachineReadable = $input->getOption('as-script');
$this->io = new ImagineStyle($input, $output, $this->outputMachineReadable ? false : !$input->getOption('no-colors'));
}

/**
* @param InputInterface $input
*
* @return array[]
*/
private function resolveInputFiltersAndPaths(InputInterface $input): array
{
return [
$input->getArgument('path'),
$this->normalizeFilterList($input->getOption('filter')),
];
}

/**
* @param string[] $filters
*
* @return string[]
*/
private function normalizeFilterList(array $filters): array
{
if (0 < count($filters)) {
return $filters;
}

if (0 < count($filters = array_keys((array) $this->filterManager->getFilterConfiguration()->all()))) {
return $filters;
}

throw new RuntimeException('No filters have been defined in the active configuration!');
}

private function outputCommandHeader(): void
{
if (!$this->outputMachineReadable) {
$this->io->title($this->getName(), 'liip/imagine-bundle');
}
}

/**
* @param string[] $images
* @param string[] $filters
*/
private function outputCommandResult(array $images, array $filters): void
{
if (!$this->outputMachineReadable) {
$wordPluralizer = function (int $count, string $singular) {
return 1 === $count ? $singular : sprintf('%ss', $singular);
};
$rootTextFormat = 'Completed %d %s (%d %s, %d %s)';
$imagePathsSize = count($images);
$filterSetsSize = count($filters);
$allActionsSize = ($filterSetsSize * $imagePathsSize) - $this->failures;
$allActionsWord = $wordPluralizer($allActionsSize, 'operation');

switch (substr(__CLASS__, -18, 6)) {
case 'Remove':
$allActionsWord = $wordPluralizer($allActionsSize, 'removal');
break;

case 'esolve':
$allActionsWord = $wordPluralizer($allActionsSize, 'resolution');
break;
}

$rootTextOutput = vsprintf($rootTextFormat, [
$allActionsSize,
$allActionsWord,
$imagePathsSize,
$wordPluralizer($imagePathsSize, 'image'),
$filterSetsSize,
$wordPluralizer($filterSetsSize, 'filter'),
]);

if ($this->failures) {
$this->io->critBlock(sprintf('%s %%s', $rootTextOutput), [
sprintf('[encountered %d failures]', $this->failures)
]);
} else {
$this->io->okayBlock($rootTextOutput);
}
}
}

/**
* @return int
*/
private function getResultCode(): int
{
return 0 === $this->failures ? 0 : 255;
}
}
110 changes: 82 additions & 28 deletions Command/RemoveCacheCommand.php
Expand Up @@ -12,58 +12,112 @@
namespace Liip\ImagineBundle\Command;

use Liip\ImagineBundle\Imagine\Cache\CacheManager;
use Symfony\Bundle\FrameworkBundle\Command\ContainerAwareCommand;
use Liip\ImagineBundle\Imagine\Filter\FilterManager;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class RemoveCacheCommand extends ContainerAwareCommand
class RemoveCacheCommand extends Command
{
use CacheCommandTrait;

/**
* @param CacheManager $cacheManager
* @param FilterManager $filterManager
*/
public function __construct(CacheManager $cacheManager, FilterManager $filterManager)
{
parent::__construct();

$this->cacheManager = $cacheManager;
$this->filterManager = $filterManager;
}

protected function configure()
{
$this
->setName('liip:imagine:cache:remove')
->setDescription('Remove cache for given paths and set of filters.')
->addArgument('paths', InputArgument::OPTIONAL | InputArgument::IS_ARRAY, 'Image paths')
->addOption(
'filters',
'f',
InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
'Filters list'
)
->setAliases(['imagine:del'])
->setDescription('Remove cache entries for given paths and filters.')
->addArgument('path', InputArgument::REQUIRED | InputArgument::IS_ARRAY,
'Image file path(s) to run resolution on.')
->addOption('filter', 'f', InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY,
'Filter(s) to use for image remove; if none explicitly passed, use all filters.')
->addOption('no-colors', 'C', InputOption::VALUE_NONE,
'Write only un-styled text output; remove any colors, styling, etc.')
->addOption('as-script', 'S', InputOption::VALUE_NONE,
'Write only machine-readable output; silenced verbose reporting and implies --no-colors.')
->setHelp(<<<'EOF'
The <info>%command.name%</info> command removes cache by specified parameters.
The <comment>%command.name%</comment> command removes the passed image(s) cache entry for the
resolved filter(s), outputting results using the following basic format:
<info>image.ext[filter] (removed|skipped|failure)[: (image-path|exception-message)]</>

Paths should be separated by spaces:
<info>php app/console %command.name% path1 path2</info>
All cache for a given `paths` will be lost.
<comment># bin/console %command.name% --filter=thumb1 foo.ext bar.ext</comment>
Remove cache for <options=bold>both</> <comment>foo.ext</comment> and <comment>bar.ext</comment> images for <options=bold>one</> filter (<comment>thumb1</comment>), outputting:
<info>- foo.ext[thumb1] removed</>
<info>- bar.ext[thumb1] removed</>

If you use --filters parameter:
<info>php app/console %command.name% --filters=thumb1 --filters=thumb2</info>
All cache for a given filters will be lost.
<comment># bin/console %command.name% --filter=thumb1 --filter=thumb3 foo.ext</comment>
Remove cache for <comment>foo.ext</comment> image using <options=bold>two</> filters (<comment>thumb1</comment> and <comment>thumb3</comment>), outputting:
<info>- foo.ext[thumb1] removed</>
<info>- foo.ext[thumb3] removed</>

You can combine these parameters:
<info>php app/console %command.name% path1 path2 --filters=thumb1 --filters=thumb2</info>
<comment># bin/console %command.name% foo.ext</comment>
Remove cache for <comment>foo.ext</comment> image using <options=bold>all</> filters (as none were specified), outputting:
<info>- foo.ext[thumb1] removed</>
<info>- foo.ext[thumb2] removed</>
<info>- foo.ext[thumb3] removed</>

<info>php app/console %command.name%</info>
Cache for all paths and filters will be lost when executing this command without parameters.
EOF
);
}

/**
* @param InputInterface $input
* @param OutputInterface $output
*
* @return int
*/
protected function execute(InputInterface $input, OutputInterface $output)
{
$paths = $input->getArgument('paths');
$filters = $input->getOption('filters');
$this->setupOutputStyle($input, $output);
$this->outputCommandHeader();

list($images, $filters) = $this->resolveInputFiltersAndPaths($input);

foreach ($images as $i) {
foreach ($filters as $f) {
$this->runCacheImageRemove($i, $f);
}
}

$this->outputCommandResult($images, $filters);

return $this->getResultCode();
}

if (empty($filters)) {
$filters = null;
/**
* @param string $image
* @param string $filter
*/
private function runCacheImageRemove(string $image, string $filter): void
{
if (!$this->outputMachineReadable) {
$this->io->text(' - ');
}

/* @var CacheManager cacheManager */
$cacheManager = $this->getContainer()->get('liip_imagine.cache.manager');
$this->io->group($image, $filter, 'blue');
$this->io->space();

if ($this->cacheManager->isStored($image, $filter)) {
$this->cacheManager->remove($image, $filter);
$this->io->status('removed', 'green');
} else {
$this->io->status('skipped', 'yellow');
}

$cacheManager->remove($paths, $filters);
$this->io->newline();
}
}