Merge pull request #357 from acelaya/feature/phpstan0.11

Feature/phpstan0.11
This commit is contained in:
Alejandro Celaya 2019-02-17 10:19:14 +01:00 committed by GitHub
commit d9f11e190f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 82 additions and 27 deletions

View File

@ -1,5 +1,6 @@
tools: tools:
external_code_coverage: true external_code_coverage:
timeout: 600
checks: checks:
php: php:
code_rating: true code_rating: true

View File

@ -54,7 +54,7 @@
"doctrine/data-fixtures": "^1.3", "doctrine/data-fixtures": "^1.3",
"filp/whoops": "^2.0", "filp/whoops": "^2.0",
"infection/infection": "^0.12.2", "infection/infection": "^0.12.2",
"phpstan/phpstan": "^0.10.8", "phpstan/phpstan": "^0.11.2",
"phpunit/phpcov": "^6.0@dev || ^5.0", "phpunit/phpcov": "^6.0@dev || ^5.0",
"phpunit/phpunit": "^8.0 || ^7.5", "phpunit/phpunit": "^8.0 || ^7.5",
"roave/security-advisories": "dev-master", "roave/security-advisories": "dev-master",

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api; namespace Shlinkio\Shlink\CLI\Command\Api;
use InvalidArgumentException; use InvalidArgumentException;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputArgument;
@ -32,7 +33,7 @@ class DisableKeyCommand extends Command
->addArgument('apiKey', InputArgument::REQUIRED, 'The API key to disable'); ->addArgument('apiKey', InputArgument::REQUIRED, 'The API key to disable');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$apiKey = $input->getArgument('apiKey'); $apiKey = $input->getArgument('apiKey');
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
@ -40,8 +41,10 @@ class DisableKeyCommand extends Command
try { try {
$this->apiKeyService->disable($apiKey); $this->apiKeyService->disable($apiKey);
$io->success(sprintf('API key "%s" properly disabled', $apiKey)); $io->success(sprintf('API key "%s" properly disabled', $apiKey));
return ExitCodes::EXIT_SUCCESS;
} catch (InvalidArgumentException $e) { } catch (InvalidArgumentException $e) {
$io->error(sprintf('API key "%s" does not exist.', $apiKey)); $io->error(sprintf('API key "%s" does not exist.', $apiKey));
return ExitCodes::EXIT_FAILURE;
} }
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api; namespace Shlinkio\Shlink\CLI\Command\Api;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -38,11 +39,12 @@ class GenerateKeyCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$expirationDate = $input->getOption('expirationDate'); $expirationDate = $input->getOption('expirationDate');
$apiKey = $this->apiKeyService->create(isset($expirationDate) ? Chronos::parse($expirationDate) : null); $apiKey = $this->apiKeyService->create(isset($expirationDate) ? Chronos::parse($expirationDate) : null);
(new SymfonyStyle($input, $output))->success(sprintf('Generated API key: "%s"', $apiKey)); (new SymfonyStyle($input, $output))->success(sprintf('Generated API key: "%s"', $apiKey));
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Api; namespace Shlinkio\Shlink\CLI\Command\Api;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Console\ShlinkTable; use Shlinkio\Shlink\Common\Console\ShlinkTable;
use Shlinkio\Shlink\Rest\Entity\ApiKey; use Shlinkio\Shlink\Rest\Entity\ApiKey;
use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface; use Shlinkio\Shlink\Rest\Service\ApiKeyServiceInterface;
@ -44,7 +45,7 @@ class ListKeysCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$enabledOnly = $input->getOption('enabledOnly'); $enabledOnly = $input->getOption('enabledOnly');
@ -66,6 +67,7 @@ class ListKeysCommand extends Command
! $enabledOnly ? 'Is enabled' : null, ! $enabledOnly ? 'Is enabled' : null,
'Expiration date', 'Expiration date',
]), $rows); ]), $rows);
return ExitCodes::EXIT_SUCCESS;
} }
private function determineMessagePattern(ApiKey $apiKey): string private function determineMessagePattern(ApiKey $apiKey): string

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Config; namespace Shlinkio\Shlink\CLI\Command\Config;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Options\UrlShortenerOptions; use Shlinkio\Shlink\Core\Options\UrlShortenerOptions;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -28,9 +29,10 @@ class GenerateCharsetCommand extends Command
->setHelp('<fg=red;options=bold>This command is deprecated. Better leave shlink generate the charset.</>'); ->setHelp('<fg=red;options=bold>This command is deprecated. Better leave shlink generate the charset.</>');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$charSet = str_shuffle(UrlShortenerOptions::DEFAULT_CHARS); $charSet = str_shuffle(UrlShortenerOptions::DEFAULT_CHARS);
(new SymfonyStyle($input, $output))->success(sprintf('Character set: "%s"', $charSet)); (new SymfonyStyle($input, $output))->success(sprintf('Character set: "%s"', $charSet));
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Config; namespace Shlinkio\Shlink\CLI\Command\Config;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Util\StringUtilsTrait; use Shlinkio\Shlink\Common\Util\StringUtilsTrait;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -27,9 +28,10 @@ class GenerateSecretCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$secret = $this->generateRandomString(32); $secret = $this->generateRandomString(32);
(new SymfonyStyle($input, $output))->success(sprintf('Secret key: "%s"', $secret)); (new SymfonyStyle($input, $output))->success(sprintf('Secret key: "%s"', $secret));
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception; use Shlinkio\Shlink\Core\Exception;
use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlServiceInterface; use Shlinkio\Shlink\Core\Service\ShortUrl\DeleteShortUrlServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
@ -43,7 +44,7 @@ class DeleteShortUrlCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$shortCode = $input->getArgument('shortCode'); $shortCode = $input->getArgument('shortCode');
@ -51,14 +52,16 @@ class DeleteShortUrlCommand extends Command
try { try {
$this->runDelete($io, $shortCode, $ignoreThreshold); $this->runDelete($io, $shortCode, $ignoreThreshold);
return ExitCodes::EXIT_SUCCESS;
} catch (Exception\InvalidShortCodeException $e) { } catch (Exception\InvalidShortCodeException $e) {
$io->error(sprintf('Provided short code "%s" could not be found.', $shortCode)); $io->error(sprintf('Provided short code "%s" could not be found.', $shortCode));
return ExitCodes::EXIT_FAILURE;
} catch (Exception\DeleteShortUrlException $e) { } catch (Exception\DeleteShortUrlException $e) {
$this->retry($io, $shortCode, $e); return $this->retry($io, $shortCode, $e);
} }
} }
private function retry(SymfonyStyle $io, string $shortCode, Exception\DeleteShortUrlException $e): void private function retry(SymfonyStyle $io, string $shortCode, Exception\DeleteShortUrlException $e): int
{ {
$warningMsg = sprintf( $warningMsg = sprintf(
'It was not possible to delete the short URL with short code "%s" because it has more than %s visits.', 'It was not possible to delete the short URL with short code "%s" because it has more than %s visits.',
@ -73,6 +76,8 @@ class DeleteShortUrlCommand extends Command
} else { } else {
$io->warning('Short URL was not deleted.'); $io->warning('Short URL was not deleted.');
} }
return $forceDelete ? ExitCodes::EXIT_SUCCESS : ExitCodes::EXIT_WARNING;
} }
private function runDelete(SymfonyStyle $io, string $shortCode, bool $ignoreThreshold): void private function runDelete(SymfonyStyle $io, string $shortCode, bool $ignoreThreshold): void

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Exception\PreviewGenerationException; use Shlinkio\Shlink\Common\Exception\PreviewGenerationException;
use Shlinkio\Shlink\Common\Service\PreviewGeneratorInterface; use Shlinkio\Shlink\Common\Service\PreviewGeneratorInterface;
use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface; use Shlinkio\Shlink\Core\Service\ShortUrlServiceInterface;
@ -39,7 +40,7 @@ class GeneratePreviewCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$page = 1; $page = 1;
do { do {
@ -52,6 +53,7 @@ class GeneratePreviewCommand extends Command
} while ($page <= $shortUrls->count()); } while ($page <= $shortUrls->count());
(new SymfonyStyle($input, $output))->success('Finished processing all URLs'); (new SymfonyStyle($input, $output))->success('Finished processing all URLs');
return ExitCodes::EXIT_SUCCESS;
} }
private function processUrl($url, OutputInterface $output): void private function processUrl($url, OutputInterface $output): void

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception\InvalidUrlException; use Shlinkio\Shlink\Core\Exception\InvalidUrlException;
use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException; use Shlinkio\Shlink\Core\Exception\NonUniqueSlugException;
use Shlinkio\Shlink\Core\Model\ShortUrlMeta; use Shlinkio\Shlink\Core\Model\ShortUrlMeta;
@ -102,13 +103,13 @@ class GenerateShortUrlCommand extends Command
} }
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$longUrl = $input->getArgument('longUrl'); $longUrl = $input->getArgument('longUrl');
if (empty($longUrl)) { if (empty($longUrl)) {
$io->error('A URL was not provided!'); $io->error('A URL was not provided!');
return; return ExitCodes::EXIT_FAILURE;
} }
$explodeWithComma = curry('explode')(','); $explodeWithComma = curry('explode')(',');
@ -134,12 +135,15 @@ class GenerateShortUrlCommand extends Command
sprintf('Processed long URL: <info>%s</info>', $longUrl), sprintf('Processed long URL: <info>%s</info>', $longUrl),
sprintf('Generated short URL: <info>%s</info>', $shortUrl), sprintf('Generated short URL: <info>%s</info>', $shortUrl),
]); ]);
return ExitCodes::EXIT_SUCCESS;
} catch (InvalidUrlException $e) { } catch (InvalidUrlException $e) {
$io->error(sprintf('Provided URL "%s" is invalid. Try with a different one.', $longUrl)); $io->error(sprintf('Provided URL "%s" is invalid. Try with a different one.', $longUrl));
return ExitCodes::EXIT_FAILURE;
} catch (NonUniqueSlugException $e) { } catch (NonUniqueSlugException $e) {
$io->error( $io->error(
sprintf('Provided slug "%s" is already in use by another URL. Try with a different one.', $customSlug) sprintf('Provided slug "%s" is already in use by another URL. Try with a different one.', $customSlug)
); );
return ExitCodes::EXIT_FAILURE;
} }
} }

View File

@ -4,6 +4,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Cake\Chronos\Chronos; use Cake\Chronos\Chronos;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Console\ShlinkTable; use Shlinkio\Shlink\Common\Console\ShlinkTable;
use Shlinkio\Shlink\Common\Util\DateRange; use Shlinkio\Shlink\Common\Util\DateRange;
use Shlinkio\Shlink\Core\Entity\Visit; use Shlinkio\Shlink\Core\Entity\Visit;
@ -68,7 +69,7 @@ class GetVisitsCommand extends Command
} }
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$shortCode = $input->getArgument('shortCode'); $shortCode = $input->getArgument('shortCode');
$startDate = $this->getDateOption($input, 'startDate'); $startDate = $this->getDateOption($input, 'startDate');
@ -83,6 +84,7 @@ class GetVisitsCommand extends Command
return select_keys($rowData, ['referer', 'date', 'userAgent', 'country']); return select_keys($rowData, ['referer', 'date', 'userAgent', 'country']);
}, $visits); }, $visits);
ShlinkTable::fromOutput($output)->render(['Referer', 'Date', 'User agent', 'Country'], $rows); ShlinkTable::fromOutput($output)->render(['Referer', 'Date', 'User agent', 'Country'], $rows);
return ExitCodes::EXIT_SUCCESS;
} }
private function getDateOption(InputInterface $input, $key) private function getDateOption(InputInterface $input, $key)

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Console\ShlinkTable; use Shlinkio\Shlink\Common\Console\ShlinkTable;
use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter; use Shlinkio\Shlink\Common\Paginator\Adapter\PaginableRepositoryAdapter;
use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait; use Shlinkio\Shlink\Common\Paginator\Util\PaginatorUtilsTrait;
@ -74,7 +75,7 @@ class ListShortUrlsCommand extends Command
->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not'); ->addOption('showTags', null, InputOption::VALUE_NONE, 'Whether to display the tags or not');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$page = (int) $input->getOption('page'); $page = (int) $input->getOption('page');
@ -95,6 +96,7 @@ class ListShortUrlsCommand extends Command
$io->newLine(); $io->newLine();
$io->success('Short URLs properly listed'); $io->success('Short URLs properly listed');
return ExitCodes::EXIT_SUCCESS;
} }
private function renderPage( private function renderPage(

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\ShortUrl; namespace Shlinkio\Shlink\CLI\Command\ShortUrl;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException; use Shlinkio\Shlink\Core\Exception\InvalidShortCodeException;
use Shlinkio\Shlink\Core\Service\UrlShortenerInterface; use Shlinkio\Shlink\Core\Service\UrlShortenerInterface;
@ -50,7 +51,7 @@ class ResolveUrlCommand extends Command
} }
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$shortCode = $input->getArgument('shortCode'); $shortCode = $input->getArgument('shortCode');
@ -58,10 +59,13 @@ class ResolveUrlCommand extends Command
try { try {
$url = $this->urlShortener->shortCodeToUrl($shortCode); $url = $this->urlShortener->shortCodeToUrl($shortCode);
$output->writeln(sprintf('Long URL: <info>%s</info>', $url->getLongUrl())); $output->writeln(sprintf('Long URL: <info>%s</info>', $url->getLongUrl()));
return ExitCodes::EXIT_SUCCESS;
} catch (InvalidShortCodeException $e) { } catch (InvalidShortCodeException $e) {
$io->error(sprintf('Provided short code "%s" has an invalid format.', $shortCode)); $io->error(sprintf('Provided short code "%s" has an invalid format.', $shortCode));
return ExitCodes::EXIT_FAILURE;
} catch (EntityDoesNotExistException $e) { } catch (EntityDoesNotExistException $e) {
$io->error(sprintf('Provided short code "%s" could not be found.', $shortCode)); $io->error(sprintf('Provided short code "%s" could not be found.', $shortCode));
return ExitCodes::EXIT_FAILURE;
} }
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag; namespace Shlinkio\Shlink\CLI\Command\Tag;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface; use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -36,17 +37,18 @@ class CreateTagCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$tagNames = $input->getOption('name'); $tagNames = $input->getOption('name');
if (empty($tagNames)) { if (empty($tagNames)) {
$io->warning('You have to provide at least one tag name'); $io->warning('You have to provide at least one tag name');
return; return ExitCodes::EXIT_WARNING;
} }
$this->tagService->createTags($tagNames); $this->tagService->createTags($tagNames);
$io->success('Tags properly created'); $io->success('Tags properly created');
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag; namespace Shlinkio\Shlink\CLI\Command\Tag;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface; use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputInterface;
@ -36,17 +37,18 @@ class DeleteTagsCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$tagNames = $input->getOption('name'); $tagNames = $input->getOption('name');
if (empty($tagNames)) { if (empty($tagNames)) {
$io->warning('You have to provide at least one tag name'); $io->warning('You have to provide at least one tag name');
return; return ExitCodes::EXIT_WARNING;
} }
$this->tagService->deleteTags($tagNames); $this->tagService->deleteTags($tagNames);
$io->success('Tags properly deleted'); $io->success('Tags properly deleted');
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag; namespace Shlinkio\Shlink\CLI\Command\Tag;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Console\ShlinkTable; use Shlinkio\Shlink\Common\Console\ShlinkTable;
use Shlinkio\Shlink\Core\Entity\Tag; use Shlinkio\Shlink\Core\Entity\Tag;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface; use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
@ -31,9 +32,10 @@ class ListTagsCommand extends Command
->setDescription('Lists existing tags.'); ->setDescription('Lists existing tags.');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
ShlinkTable::fromOutput($output)->render(['Name'], $this->getTagsRows()); ShlinkTable::fromOutput($output)->render(['Name'], $this->getTagsRows());
return ExitCodes::EXIT_SUCCESS;
} }
private function getTagsRows(): array private function getTagsRows(): array

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Tag; namespace Shlinkio\Shlink\CLI\Command\Tag;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException; use Shlinkio\Shlink\Core\Exception\EntityDoesNotExistException;
use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface; use Shlinkio\Shlink\Core\Service\Tag\TagServiceInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
@ -34,7 +35,7 @@ class RenameTagCommand extends Command
->addArgument('newName', InputArgument::REQUIRED, 'New name of the tag.'); ->addArgument('newName', InputArgument::REQUIRED, 'New name of the tag.');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$oldName = $input->getArgument('oldName'); $oldName = $input->getArgument('oldName');
@ -43,8 +44,10 @@ class RenameTagCommand extends Command
try { try {
$this->tagService->renameTag($oldName, $newName); $this->tagService->renameTag($oldName, $newName);
$io->success('Tag properly renamed.'); $io->success('Tag properly renamed.');
return ExitCodes::EXIT_SUCCESS;
} catch (EntityDoesNotExistException $e) { } catch (EntityDoesNotExistException $e) {
$io->error(sprintf('A tag with name "%s" was not found', $oldName)); $io->error(sprintf('A tag with name "%s" was not found', $oldName));
return ExitCodes::EXIT_FAILURE;
} }
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Visit; namespace Shlinkio\Shlink\CLI\Command\Visit;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Exception\WrongIpException; use Shlinkio\Shlink\Common\Exception\WrongIpException;
use Shlinkio\Shlink\Common\IpGeolocation\IpLocationResolverInterface; use Shlinkio\Shlink\Common\IpGeolocation\IpLocationResolverInterface;
use Shlinkio\Shlink\Common\Util\IpAddress; use Shlinkio\Shlink\Common\Util\IpAddress;
@ -48,7 +49,7 @@ class ProcessVisitsCommand extends Command
->setDescription('Processes visits where location is not set yet'); ->setDescription('Processes visits where location is not set yet');
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$this->output = $output; $this->output = $output;
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
@ -56,7 +57,7 @@ class ProcessVisitsCommand extends Command
$lock = $this->locker->createLock(self::NAME); $lock = $this->locker->createLock(self::NAME);
if (! $lock->acquire()) { if (! $lock->acquire()) {
$io->warning(sprintf('There is already an instance of the "%s" command in execution', self::NAME)); $io->warning(sprintf('There is already an instance of the "%s" command in execution', self::NAME));
return; return ExitCodes::EXIT_WARNING;
} }
try { try {
@ -70,6 +71,7 @@ class ProcessVisitsCommand extends Command
$io->success('Finished processing all IPs'); $io->success('Finished processing all IPs');
} finally { } finally {
$lock->release(); $lock->release();
return ExitCodes::EXIT_SUCCESS;
} }
} }

View File

@ -3,6 +3,7 @@ declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Command\Visit; namespace Shlinkio\Shlink\CLI\Command\Visit;
use Shlinkio\Shlink\CLI\Util\ExitCodes;
use Shlinkio\Shlink\Common\Exception\RuntimeException; use Shlinkio\Shlink\Common\Exception\RuntimeException;
use Shlinkio\Shlink\Common\IpGeolocation\GeoLite2\DbUpdaterInterface; use Shlinkio\Shlink\Common\IpGeolocation\GeoLite2\DbUpdaterInterface;
use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Command\Command;
@ -35,7 +36,7 @@ class UpdateDbCommand extends Command
); );
} }
protected function execute(InputInterface $input, OutputInterface $output): void protected function execute(InputInterface $input, OutputInterface $output): ?int
{ {
$io = new SymfonyStyle($input, $output); $io = new SymfonyStyle($input, $output);
$progressBar = new ProgressBar($output); $progressBar = new ProgressBar($output);
@ -51,6 +52,7 @@ class UpdateDbCommand extends Command
$io->writeln(''); $io->writeln('');
$io->success('GeoLite2 database properly updated'); $io->success('GeoLite2 database properly updated');
return ExitCodes::EXIT_SUCCESS;
} catch (RuntimeException $e) { } catch (RuntimeException $e) {
$progressBar->finish(); $progressBar->finish();
$io->writeln(''); $io->writeln('');
@ -59,6 +61,7 @@ class UpdateDbCommand extends Command
if ($io->isVerbose()) { if ($io->isVerbose()) {
$this->getApplication()->renderException($e, $output); $this->getApplication()->renderException($e, $output);
} }
return ExitCodes::EXIT_FAILURE;
} }
} }
} }

View File

@ -0,0 +1,11 @@
<?php
declare(strict_types=1);
namespace Shlinkio\Shlink\CLI\Util;
final class ExitCodes
{
public const EXIT_SUCCESS = 0;
public const EXIT_FAILURE = -1;
public const EXIT_WARNING = 1;
}

View File

@ -25,7 +25,6 @@ class ShortUrlDataTransformer implements DataTransformerInterface
*/ */
public function transform($value): array public function transform($value): array
{ {
$dateCreated = $value->getDateCreated();
$longUrl = $value->getLongUrl(); $longUrl = $value->getLongUrl();
$shortCode = $value->getShortCode(); $shortCode = $value->getShortCode();
@ -33,7 +32,7 @@ class ShortUrlDataTransformer implements DataTransformerInterface
'shortCode' => $shortCode, 'shortCode' => $shortCode,
'shortUrl' => $this->buildShortUrl($this->domainConfig, $shortCode), 'shortUrl' => $this->buildShortUrl($this->domainConfig, $shortCode),
'longUrl' => $longUrl, 'longUrl' => $longUrl,
'dateCreated' => $dateCreated !== null ? $dateCreated->toAtomString() : null, 'dateCreated' => $value->getDateCreated()->toAtomString(),
'visitsCount' => $value->getVisitsCount(), 'visitsCount' => $value->getVisitsCount(),
'tags' => invoke($value->getTags(), '__toString'), 'tags' => invoke($value->getTags(), '__toString'),