├── LICENSE ├── composer.json └── src ├── Cache ├── Cleaners │ ├── ApcCleaner.php │ ├── ApcuCleaner.php │ ├── ICleaner.php │ ├── LocalFilesystemCleaner.php │ ├── MemcachedCleaner.php │ ├── NetteCachingStorageCleaner.php │ └── OpcodeCleaner.php └── Generators │ ├── DiContainersCacheGenerator.php │ ├── IGenerator.php │ └── LatteTemplatesCacheGenerator.php ├── Command ├── AdvancedCache │ ├── CacheCleanCommand.php │ └── CacheGenerateCommand.php ├── Cache │ └── CachePurgeCommand.php ├── Caching │ └── CachingClearCommand.php ├── DI │ └── DIPurgeCommand.php ├── Database │ ├── BackupCommand.php │ └── LoadCommand.php ├── Latte │ ├── LattePurgeCommand.php │ └── LatteWarmupCommand.php ├── Router │ └── RouterDumpCommand.php ├── Security │ └── SecurityPasswordCommand.php └── Utils │ └── UtilsRandomCommand.php ├── DI ├── AbstractCompilerExtension.php ├── AdvancedCacheConsoleExtension.php ├── CacheConsoleExtension.php ├── CachingConsoleExtension.php ├── ConsoleBridgesExtension.php ├── DIConsoleExtension.php ├── DatabaseConsoleExtension.php ├── LatteConsoleExtension.php ├── RouterConsoleExtension.php ├── SecurityConsoleExtension.php └── UtilsConsoleExtension.php ├── Exception ├── LogicalException.php └── RuntimeException.php └── Utils ├── Database.php ├── Files.php └── Utils.php /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Contributte 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "contributte/console-extra", 3 | "description": "Nette-based console commands for latte, DIC, security, utils and many others", 4 | "keywords": [ 5 | "nette", 6 | "symfony", 7 | "console" 8 | ], 9 | "type": "library", 10 | "license": "MIT", 11 | "homepage": "https://github.com/contributte/console-extra", 12 | "authors": [ 13 | { 14 | "name": "Milan Felix Šulc", 15 | "homepage": "https://f3l1x.io" 16 | } 17 | ], 18 | "require": { 19 | "php": ">=8.1", 20 | "nette/di": "^3.1.8", 21 | "symfony/console": "^6.4.2 || ^7.0.2" 22 | }, 23 | "require-dev": { 24 | "contributte/console": "~0.10", 25 | "latte/latte": "^3.0.12", 26 | "nette/application": "^3.1.14", 27 | "nette/bootstrap": "^3.2.1", 28 | "nette/caching": "^3.2.3", 29 | "nette/security": "^3.1.8", 30 | "contributte/qa": "^0.4", 31 | "contributte/tester": "^0.4", 32 | "contributte/phpstan": "^0.1", 33 | "mockery/mockery": "^1.5.0" 34 | }, 35 | "conflict": { 36 | "nette/schema": "<1.0.1" 37 | }, 38 | "suggest": { 39 | "contributte/console": "Symfony\\Console for Nette" 40 | }, 41 | "autoload": { 42 | "psr-4": { 43 | "Contributte\\Console\\Extra\\": "src" 44 | } 45 | }, 46 | "autoload-dev": { 47 | "psr-4": { 48 | "Tests\\": "tests" 49 | } 50 | }, 51 | "minimum-stability": "dev", 52 | "prefer-stable": true, 53 | "config": { 54 | "sort-packages": true, 55 | "allow-plugins": { 56 | "dealerdirect/phpcodesniffer-composer-installer": true 57 | } 58 | }, 59 | "extra": { 60 | "branch-alias": { 61 | "dev-master": "0.9.x-dev" 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/ApcCleaner.php: -------------------------------------------------------------------------------- 1 | writeln('Skipped APC cache cleaning, apc_clear_cache function is not available.'); 20 | 21 | return false; 22 | } 23 | 24 | $output->writeln('Cleaning APC cache'); 25 | 26 | $output->writeln('Cleaning APC system cache', OutputInterface::VERBOSITY_VERBOSE); 27 | apc_clear_cache(); 28 | 29 | $output->writeln('Cleaning APC user cache', OutputInterface::VERBOSITY_VERBOSE); 30 | apc_clear_cache('user'); 31 | 32 | $output->writeln('APC cache successfully cleaned.'); 33 | 34 | return true; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/ApcuCleaner.php: -------------------------------------------------------------------------------- 1 | writeln('Skipped APCu cache cleaning, apcu_clear_cache function is not available.'); 20 | 21 | return false; 22 | } 23 | 24 | $output->writeln('Cleaning APCu cache'); 25 | 26 | apcu_clear_cache(); 27 | 28 | $output->writeln('APCu cache successfully cleaned.'); 29 | 30 | return true; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/ICleaner.php: -------------------------------------------------------------------------------- 1 | directories = $directories; 25 | $this->ignored = $ignored; 26 | } 27 | 28 | public function getDescription(): string 29 | { 30 | return 'Local files'; 31 | } 32 | 33 | public function clean(InputInterface $input, OutputInterface $output): bool 34 | { 35 | if ($this->directories === []) { 36 | $output->writeln('Skipped local filesystem cache cleaning, no directories defined.'); 37 | 38 | return false; 39 | } 40 | 41 | $output->writeln('Cleaning local filesystem cache'); 42 | 43 | foreach ($this->directories as $directory) { 44 | $output->writeln(sprintf('Cleaning directory %s', $directory), OutputInterface::VERBOSITY_VERBOSE); 45 | Files::purge($directory, $this->ignored); 46 | } 47 | 48 | $output->writeln('Local filesystem cache successfully cleaned.'); 49 | 50 | return true; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/MemcachedCleaner.php: -------------------------------------------------------------------------------- 1 | memcaches = $memcaches; 22 | } 23 | 24 | public function getDescription(): string 25 | { 26 | return 'Memcached'; 27 | } 28 | 29 | public function clean(InputInterface $input, OutputInterface $output): bool 30 | { 31 | if ($this->memcaches === []) { 32 | $output->writeln('Skipped Memcache(d) cleaning, no Memcache(d) services defined.'); 33 | 34 | return false; 35 | } 36 | 37 | $output->writeln('Cleaning Memcache(d)'); 38 | 39 | foreach ($this->memcaches as $name => $memcache) { 40 | $output->writeln(sprintf('Cleaning Memcache(d) instance %s', (string) $name), OutputInterface::VERBOSITY_VERBOSE); 41 | $memcache->flush(); 42 | } 43 | 44 | $output->writeln('Memcache(d) successfully cleaned.'); 45 | 46 | return true; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/NetteCachingStorageCleaner.php: -------------------------------------------------------------------------------- 1 | storages = $storages; 22 | } 23 | 24 | public function getDescription(): string 25 | { 26 | return Storage::class; 27 | } 28 | 29 | public function clean(InputInterface $input, OutputInterface $output): bool 30 | { 31 | if ($this->storages === []) { 32 | $output->writeln(sprintf('Skipped %s cleaning, no IStorage services defined.', Storage::class)); 33 | 34 | return false; 35 | } 36 | 37 | $output->writeln(sprintf('Cleaning %s', Storage::class)); 38 | 39 | foreach ($this->storages as $name => $storage) { 40 | $output->writeln(sprintf('Cleaning storage instance %s', (string) $name), OutputInterface::VERBOSITY_VERBOSE); 41 | $storage->clean([ 42 | Cache::All => true, 43 | ]); 44 | } 45 | 46 | $output->writeln(sprintf('%s successfully cleaned.', Storage::class)); 47 | 48 | return true; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/Cache/Cleaners/OpcodeCleaner.php: -------------------------------------------------------------------------------- 1 | writeln('Skipped opcode cache cleaning, opcache_reset function is not available.'); 20 | 21 | return false; 22 | } 23 | 24 | $output->writeln('Cleaning opcode cache cache'); 25 | $success = @opcache_reset(); 26 | 27 | if ($success) { 28 | $output->writeln('opcode cache successfully cleaned.'); 29 | 30 | return true; 31 | } else { 32 | $output->writeln('opcode cache cannot be cleaned. It is probably restricted by "restrict_api" directive of OPcache API.'); 33 | 34 | return false; 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/Cache/Generators/DiContainersCacheGenerator.php: -------------------------------------------------------------------------------- 1 | config = $config; 23 | $this->configurator = $configurator; 24 | } 25 | 26 | public function getDescription(): string 27 | { 28 | return 'DI Containers cache'; 29 | } 30 | 31 | public function generate(InputInterface $input, OutputInterface $output): bool 32 | { 33 | if ($this->config === []) { 34 | $output->writeln('Containers generating skipped, no containers configuration defined.'); 35 | 36 | return false; 37 | } 38 | 39 | $output->writeln('Compiling DI containers'); 40 | 41 | /** @var mixed[] $parameters */ 42 | foreach ($this->config as $container => $parameters) { 43 | if (isset($parameters['debugMode'])) { // Nette BC 44 | $parameters['productionMode'] = !boolval($parameters['debugMode']); 45 | } 46 | 47 | $output->writeln(sprintf( 48 | 'Compiling container `%s`', 49 | (string) $container 50 | )); 51 | 52 | $configurator = clone $this->configurator; 53 | $configurator->addStaticParameters($parameters); 54 | $configurator->loadContainer(); 55 | } 56 | 57 | $output->writeln('All containers successfully generated.'); 58 | 59 | return true; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/Cache/Generators/IGenerator.php: -------------------------------------------------------------------------------- 1 | templateFactory = $templateFactory; 32 | $this->dirs = $dirs; 33 | $this->excludeDirs = $excludeDirs; 34 | $this->rootDir = $rootDir; 35 | } 36 | 37 | public function getDescription(): string 38 | { 39 | return 'Latte templates cache'; 40 | } 41 | 42 | public function generate(InputInterface $input, OutputInterface $output): bool 43 | { 44 | if ($this->dirs === []) { 45 | $output->writeln('Latte templates compilation skipped, no directories defined.'); 46 | 47 | return false; 48 | } 49 | 50 | $output->writeln('Compiling Latte templates'); 51 | 52 | /** @var Template $template */ 53 | $template = $this->templateFactory->createTemplate(); 54 | $latte = $template->getLatte(); 55 | 56 | $finder = Finder::findFiles('*.latte')->from($this->dirs); 57 | 58 | if ($this->excludeDirs !== []) { 59 | $finder->exclude($this->excludeDirs); 60 | } 61 | 62 | $successes = 0; 63 | $fails = 0; 64 | 65 | foreach ($finder as $path => $file) { 66 | $path = (string) realpath($path); 67 | $outputPath = $this->rootDir !== null && str_starts_with($path, $this->rootDir) 68 | ? substr($path, mb_strlen($this->rootDir)) 69 | : $path; 70 | 71 | $output->writeln(sprintf('Compiling %s', $outputPath), OutputInterface::VERBOSITY_VERBOSE); 72 | 73 | try { 74 | $latte->warmupCache($path); 75 | $successes++; 76 | } catch (Throwable $e) { 77 | $output->writeln(sprintf('Failed %s compilation.', $outputPath), OutputInterface::VERBOSITY_VERBOSE); 78 | $fails++; 79 | } 80 | } 81 | 82 | if ($fails !== 0) { 83 | $output->writeln(sprintf('%d templates compiled, compilation of %d files failed.', $successes, $fails)); 84 | 85 | return false; 86 | } else { 87 | $output->writeln('All templates successfully compiled.'); 88 | 89 | return true; 90 | } 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Command/AdvancedCache/CacheCleanCommand.php: -------------------------------------------------------------------------------- 1 | cleaners = $cleaners; 34 | } 35 | 36 | protected function configure(): void 37 | { 38 | $this->addOption('list', 'l', InputOption::VALUE_NONE, 'List all available cleaners'); 39 | $this->addOption('cleaner', 'c', InputOption::VALUE_REQUIRED, 'Use only one cleaner'); 40 | } 41 | 42 | protected function execute(InputInterface $input, OutputInterface $output): int 43 | { 44 | $style = new SymfonyStyle($input, $output); 45 | 46 | if ($input->getOption('list') === true) { 47 | $table = new Table($output); 48 | $table->setStyle('box'); 49 | $table->setHeaders(['Name', 'Description']); 50 | $rows = []; 51 | 52 | foreach ($this->cleaners as $name => $cleaner) { 53 | $rows[] = [$name, $cleaner->getDescription()]; 54 | } 55 | 56 | $table->setRows($rows); 57 | $table->render(); 58 | 59 | return 0; 60 | } 61 | 62 | if (($cleanerName = $input->getOption('cleaner')) !== null) { 63 | if (!is_string($cleanerName) || !isset($this->cleaners[$cleanerName])) { 64 | throw new LogicalException(sprintf('Cannot run undefined cleaner "%s"', Utils::stringify($cleanerName))); 65 | } 66 | 67 | $this->cleaners[$cleanerName]->clean($input, $output); 68 | 69 | return 0; 70 | } 71 | 72 | if ($this->cleaners === []) { 73 | $style->error('Cache cleaning skipped, no cleaners defined.'); 74 | 75 | return 0; 76 | } 77 | 78 | $stats = ['ok' => [], 'error' => []]; 79 | 80 | foreach ($this->cleaners as $name => $cleaner) { 81 | $success = $cleaner->clean($input, $output); 82 | 83 | if ($success) { 84 | $stats['ok'][] = $name; 85 | } else { 86 | $stats['error'][] = $name; 87 | } 88 | } 89 | 90 | if ($stats['error'] !== []) { 91 | $style->warning(sprintf( 92 | 'Cache cleaning done. %d success / %d errors. Cleaner%s "%s" failed.', 93 | count($stats['ok']), 94 | count($stats['error']), 95 | (count($stats['error']) === 1) ? '' : 's', 96 | implode('", "', $stats['error']) 97 | )); 98 | } else { 99 | $style->success('Cache successfully cleaned.'); 100 | } 101 | 102 | return 0; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/Command/AdvancedCache/CacheGenerateCommand.php: -------------------------------------------------------------------------------- 1 | generators = $generators; 34 | } 35 | 36 | protected function configure(): void 37 | { 38 | $this->addOption('list', 'l', InputOption::VALUE_NONE, 'List all available generators'); 39 | $this->addOption('generator', 'g', InputOption::VALUE_REQUIRED, 'Use only one generator'); 40 | } 41 | 42 | protected function execute(InputInterface $input, OutputInterface $output): int 43 | { 44 | //todo - vypsat co failnulo a co se podařilo 45 | $style = new SymfonyStyle($input, $output); 46 | 47 | if ($input->getOption('list') === true) { 48 | $table = new Table($output); 49 | $table->setStyle('box'); 50 | $table->setHeaders(['Name', 'Description']); 51 | $rows = []; 52 | 53 | foreach ($this->generators as $name => $generator) { 54 | $rows[] = [$name, $generator->getDescription()]; 55 | } 56 | 57 | $table->setRows($rows); 58 | $table->render(); 59 | 60 | return 0; 61 | } 62 | 63 | if (($generatorName = $input->getOption('generator')) !== null) { 64 | if (!is_string($generatorName) || !isset($this->generators[$generatorName])) { 65 | throw new LogicalException(sprintf('Cannot run undefined generator "%s"', Utils::stringify($generatorName))); 66 | } 67 | 68 | $this->generators[$generatorName]->generate($input, $output); 69 | 70 | return 0; 71 | } 72 | 73 | if ($this->generators === []) { 74 | $style->error('Cache generating skipped, no generators defined.'); 75 | 76 | return 0; 77 | } 78 | 79 | $stats = ['ok' => [], 'error' => []]; 80 | 81 | foreach ($this->generators as $name => $generator) { 82 | $success = $generator->generate($input, $output); 83 | 84 | if ($success) { 85 | $stats['ok'][] = $name; 86 | } else { 87 | $stats['error'][] = $name; 88 | } 89 | } 90 | 91 | if ($stats['error'] !== []) { 92 | $style->warning(sprintf( 93 | 'Cache generating done. %d success / %d errors. Generator%s "%s" failed.', 94 | count($stats['ok']), 95 | count($stats['error']), 96 | (count($stats['error']) === 1) ? '' : 's', 97 | implode('", "', $stats['error']) 98 | )); 99 | } else { 100 | $style->success('Cache successfully generated.'); 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/Command/Cache/CachePurgeCommand.php: -------------------------------------------------------------------------------- 1 | dirs = $dirs; 32 | } 33 | 34 | protected function configure(): void 35 | { 36 | $this->addOption('recreate', null, InputOption::VALUE_OPTIONAL, 'Recreate folders', false); 37 | } 38 | 39 | protected function execute(InputInterface $input, OutputInterface $output): int 40 | { 41 | $style = new SymfonyStyle($input, $output); 42 | $style->title('Cache Purge'); 43 | 44 | foreach ($this->dirs as $directory) { 45 | $style->text(sprintf('Purging: %s', $directory)); 46 | Files::purge($directory); 47 | 48 | if (Utils::boolenize($input->getOption('recreate'))) { 49 | $style->text(sprintf('Recreating: %s', $directory)); 50 | Files::mkdir($directory); 51 | } 52 | } 53 | 54 | $style->success(sprintf('Purging done. Total %d folders purged.', count($this->dirs))); 55 | 56 | return 0; 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/Command/Caching/CachingClearCommand.php: -------------------------------------------------------------------------------- 1 | storage = $storage; 29 | } 30 | 31 | protected function configure(): void 32 | { 33 | $this->addOption('all', null, InputOption::VALUE_OPTIONAL, 'Clear whole storage', false); 34 | $this->addOption('tag', 't', InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY, 'Clear by tag(s)', []); 35 | $this->addOption('priority', 'p', InputOption::VALUE_OPTIONAL, 'Clear by priority'); 36 | } 37 | 38 | protected function execute(InputInterface $input, OutputInterface $output): int 39 | { 40 | $style = new SymfonyStyle($input, $output); 41 | $style->title('Caching Clear'); 42 | 43 | if ($input->getOption('all') === null) { 44 | $this->storage->clean([Cache::All => true]); 45 | $style->success('Clearing whole storage done.'); 46 | } elseif ($input->getOption('tag') !== null) { 47 | $this->storage->clean([Cache::Tags => $input->getOption('tag')]); 48 | $style->listing((array) $input->getOption('tag')); 49 | $style->success('Clearing by tags done.'); 50 | } elseif ($input->getOption('priority') !== null) { 51 | $this->storage->clean([Cache::Priority => $input->getOption('priority')]); 52 | $style->comment(Utils::stringify($input->getOption('priority'))); 53 | $style->success('Clearing by priority done.'); 54 | } else { 55 | $style->warning('Specify clearing strategy.'); 56 | 57 | return 1; 58 | } 59 | 60 | return 0; 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /src/Command/DI/DIPurgeCommand.php: -------------------------------------------------------------------------------- 1 | dirs = $dirs; 30 | } 31 | 32 | protected function execute(InputInterface $input, OutputInterface $output): int 33 | { 34 | $style = new SymfonyStyle($input, $output); 35 | $style->title('DI Purge'); 36 | 37 | foreach ($this->dirs as $directory) { 38 | $style->text(sprintf('Purging: %s', $directory)); 39 | Files::purge($directory); 40 | } 41 | 42 | $style->success(sprintf('Purging done. Total %d folders purged.', count($this->dirs))); 43 | 44 | return 0; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Command/Database/BackupCommand.php: -------------------------------------------------------------------------------- 1 | backupPath = $backupPath; 28 | } 29 | 30 | protected function configure(): void 31 | { 32 | $this->addArgument('platform', InputArgument::REQUIRED, 'mysql|postgresql') 33 | ->addArgument('host', InputArgument::REQUIRED, 'SQL server IP') 34 | ->addArgument('port', InputArgument::REQUIRED, 'SQL server port') 35 | ->addArgument('username', InputArgument::REQUIRED, 'SQL server username') 36 | ->addArgument('password', InputArgument::REQUIRED, 'SQL server password') 37 | ->addArgument('database', InputArgument::REQUIRED, 'Database name') 38 | ->addArgument('path', InputArgument::OPTIONAL, 'where save backup file (backup is saved to path defined in configuration if not defined)') 39 | ->addArgument('filename', InputArgument::OPTIONAL, 'backup filename (generated automatically if not defined)') 40 | ->addOption('no-gzip', 'g', InputOption::VALUE_NONE, 'do not gzip result') 41 | ->addOption('bin-path', 'b', InputOption::VALUE_OPTIONAL, 'path to mysqldump or pg_dump binary'); 42 | } 43 | 44 | protected function isGzipEnabled(): bool 45 | { 46 | exec('which gzip > /dev/null', $retParams, $retVal); 47 | 48 | return $retVal === 0; 49 | } 50 | 51 | protected function execute(InputInterface $input, OutputInterface $output): int 52 | { 53 | // Gzip compression 54 | $gzip = !(bool) $input->getOption('no-gzip'); 55 | 56 | if (!$gzip) { 57 | $output->writeln('Gzip compression is disabled'); 58 | } elseif (!$this->isGzipEnabled()) { 59 | $output->writeln('Error: gzip binary not found, use "--no-gzip" option'); 60 | 61 | return 1; 62 | } 63 | 64 | // FileName 65 | /** @var string|null $filename */ 66 | $filename = $input->getArgument('filename'); 67 | /** @var string $database */ 68 | $database = $input->getArgument('database'); 69 | 70 | if ($filename === null || $filename === '') { 71 | $filename = $database . '.backup.' . date('d-m-Y-h-i') . (!$gzip ? '.sql' : '.gz'); 72 | } elseif ($gzip && !Database::isGz($filename)) { 73 | $output->writeln('Error: expected ".gz" filename extension'); 74 | 75 | return 1; 76 | } elseif (!$gzip && !Database::isSql($filename)) { 77 | $output->writeln('Error: expected ".sql" filename extension'); 78 | 79 | return 1; 80 | } 81 | 82 | // Path 83 | /** @var string|null $path */ 84 | $path = $input->getArgument('path'); 85 | 86 | if ($path === null || $path === '') { 87 | $path = $this->backupPath; 88 | } elseif (!is_dir($path)) { 89 | $output->writeln('Error: given path "' . $path . '" was not found'); 90 | 91 | return 1; 92 | } 93 | 94 | $path = rtrim($path, DIRECTORY_SEPARATOR); 95 | 96 | // Destination 97 | $backupDestination = escapeshellarg($path . DIRECTORY_SEPARATOR . $filename); 98 | 99 | // Normalize bin-path 100 | $binPath = Utils::stringify($input->getOption('bin-path')); 101 | $binPath = Database::normalizeBinPath($binPath, ['mysqldump', 'pg_dump']); 102 | 103 | // Create command 104 | /** @var string $platform */ 105 | $platform = $input->getArgument('platform'); 106 | /** @var string $port */ 107 | $port = $input->getArgument('port'); 108 | /** @var string $username */ 109 | $username = $input->getArgument('username'); 110 | /** @var string $password */ 111 | $password = $input->getArgument('password'); 112 | /** @var string $host */ 113 | $host = $input->getArgument('host'); 114 | 115 | if ($platform === Database::PLATFORM_MYSQL) { 116 | $port = $port !== '' ? '--port ' . $port : ''; 117 | $command = $binPath . sprintf( 118 | 'mysqldump --user %s --password=\'%s\' --host %s %s --opt %s', 119 | $username, 120 | $password, 121 | $host, 122 | $port, 123 | $database 124 | ); 125 | } elseif ($platform === Database::PLATFORM_POSTGRES) { 126 | $port = $port !== '' ? ':' . $port : ''; 127 | $command = $binPath . sprintf( 128 | 'pg_dump --dbname=postgresql://%s:%s@%s%s/%s --blobs --no-owner', 129 | $username, 130 | $password, 131 | $host, 132 | $port, 133 | $database 134 | ); 135 | } else { 136 | $output->writeln('Error: unknown database connection type'); 137 | 138 | return 1; 139 | } 140 | 141 | if ($gzip) { 142 | $command .= ' | gzip -c'; 143 | } 144 | 145 | $command .= ' > ' . $backupDestination; 146 | 147 | // Execute 148 | $output->writeln('Backing up database "' . $database . '"...'); 149 | exec($command, $retParams, $retVal); 150 | 151 | if ($retVal === 0) { 152 | if (file_exists($backupDestination)) { 153 | $output->writeln('Backup created, see "' . $backupDestination . '" for result'); 154 | 155 | if (filesize($backupDestination) < 50) { 156 | $output->writeln('Warning: created backup is empty'); 157 | } 158 | 159 | return 0; 160 | } 161 | 162 | $output->writeln('Error: backup was not created'); 163 | } 164 | 165 | return 1; 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /src/Command/Database/LoadCommand.php: -------------------------------------------------------------------------------- 1 | addArgument('platform', InputArgument::REQUIRED, 'mysql|postgresql') 24 | ->addArgument('host', InputArgument::REQUIRED, 'SQL server IP') 25 | ->addArgument('port', InputArgument::REQUIRED, 'SQL server port') 26 | ->addArgument('username', InputArgument::REQUIRED, 'SQL server username') 27 | ->addArgument('password', InputArgument::REQUIRED, 'SQL server password') 28 | ->addArgument('database', InputArgument::REQUIRED, 'Database name') 29 | ->addArgument('filename', InputArgument::REQUIRED, 'Full path to imported file') 30 | ->addOption('bin-path', 'b', InputOption::VALUE_OPTIONAL, 'Path to mysql or psql binary'); 31 | } 32 | 33 | protected function execute(InputInterface $input, OutputInterface $output): int 34 | { 35 | // Check given file 36 | /** @var string $filename */ 37 | $filename = $input->getArgument('filename'); 38 | 39 | if (!file_exists($filename)) { 40 | $output->writeln('Error: file "' . $filename . '" not found'); 41 | 42 | return 1; 43 | } 44 | 45 | // Setup gunzip 46 | if (Database::isSql($filename)) { 47 | $packed = false; 48 | } elseif (Database::isGz($filename)) { 49 | $packed = true; 50 | 51 | if (!$this->isGunzipEnabled()) { 52 | $output->writeln('Error: gunzip binary not found'); 53 | 54 | return 1; 55 | } 56 | } else { 57 | $output->writeln('Error: unsupported file format, expected .sql or .gz file'); 58 | 59 | return 1; 60 | } 61 | 62 | // Destination 63 | $filename = escapeshellarg($filename); 64 | 65 | // Normalize binPath 66 | $binPath = Utils::stringify($input->getOption('bin-path')); 67 | $binPath = Database::normalizeBinPath($binPath, ['mysql', 'psql']); 68 | 69 | // Create command 70 | /** @var string $platform */ 71 | $platform = $input->getArgument('platform'); 72 | /** @var string $port */ 73 | $port = $input->getArgument('port'); 74 | /** @var string $username */ 75 | $username = $input->getArgument('username'); 76 | /** @var string $password */ 77 | $password = $input->getArgument('password'); 78 | /** @var string $database */ 79 | $database = $input->getArgument('database'); 80 | /** @var string $host */ 81 | $host = $input->getArgument('host'); 82 | 83 | if ($platform === Database::PLATFORM_MYSQL) { 84 | $port = $port !== '' ? '--port ' . $port : ''; 85 | $command = $binPath . sprintf( 86 | 'mysql --user %s --password=\'%s\' --host %s %s %s', 87 | $username, 88 | $password, 89 | $host, 90 | $port, 91 | $database 92 | ); 93 | } elseif ($platform === Database::PLATFORM_POSTGRES) { 94 | $port = $port !== '' ? ':' . $port : ''; 95 | $command = $binPath . sprintf( 96 | 'psql --dbname=postgresql://%s:%s@%s%s/%s', 97 | $username, 98 | $password, 99 | $host, 100 | $port, 101 | $database 102 | ); 103 | } else { 104 | $output->writeln('Error: unknown database connection type'); 105 | 106 | return 1; 107 | } 108 | 109 | if ($packed) { 110 | $command = 'gunzip -c ' . $filename . ' | ' . $command; 111 | } else { 112 | $command .= ' < ' . $filename; 113 | } 114 | 115 | // Execute command 116 | $output->writeln('Importing data into database "' . $database . '"...'); 117 | exec($command, $retParams, $retVal); 118 | 119 | if ($retVal === 0) { 120 | $output->writeln('Import finished'); 121 | 122 | return 0; 123 | } 124 | 125 | return 1; 126 | } 127 | 128 | protected function isGunzipEnabled(): bool 129 | { 130 | exec('which gunzip > /dev/null', $retParams, $retVal); 131 | 132 | return $retVal === 0; 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/Command/Latte/LattePurgeCommand.php: -------------------------------------------------------------------------------- 1 | dirs = $dirs; 30 | } 31 | 32 | protected function execute(InputInterface $input, OutputInterface $output): int 33 | { 34 | $style = new SymfonyStyle($input, $output); 35 | $style->title('Latte Purge'); 36 | 37 | foreach ($this->dirs as $directory) { 38 | $style->text(sprintf('Purging: %s', $directory)); 39 | Files::purge($directory); 40 | } 41 | 42 | $style->success(sprintf('Purging done. Total %d folders purged.', count($this->dirs))); 43 | 44 | return 0; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Command/Latte/LatteWarmupCommand.php: -------------------------------------------------------------------------------- 1 | templateFactory = $templateFactory; 40 | $this->dirs = $dirs; 41 | $this->excludeDirs = $excludeDirs; 42 | } 43 | 44 | protected function execute(InputInterface $input, OutputInterface $output): int 45 | { 46 | $style = new SymfonyStyle($input, $output); 47 | $style->title('Latte Warmup'); 48 | 49 | /** @var Template $template */ 50 | $template = $this->templateFactory->createTemplate(); 51 | $latte = $template->getLatte(); 52 | 53 | $finder = Finder::findFiles('*.latte')->from($this->dirs); 54 | 55 | if ($this->excludeDirs !== []) { 56 | $finder->exclude($this->excludeDirs); 57 | } 58 | 59 | $stats = ['ok' => 0, 'error' => 0]; 60 | 61 | /** @var SplFileInfo $file */ 62 | foreach ($finder as $file) { 63 | try { 64 | $latte->warmupCache($file->getPathname()); 65 | $stats['ok']++; 66 | 67 | if ($output->isVerbose()) { 68 | $style->text(sprintf('Warmuping: %s', $file->getPathname())); 69 | } 70 | } catch (Throwable $e) { 71 | $stats['error']++; 72 | 73 | if ($output->isVerbose()) { 74 | $style->caution(sprintf("Warmuping error: %s\nError: %s", $file->getPathname(), $e->getMessage())); 75 | } 76 | } 77 | } 78 | 79 | if ($stats['error'] > 0) { 80 | $style->warning(sprintf( 81 | 'Warmup partial done. %d success / %d errors. Total %d files.', 82 | $stats['ok'], 83 | $stats['error'], 84 | $stats['ok'] + $stats['error'] 85 | )); 86 | } else { 87 | $style->success(sprintf('Warmup done. Total %d files.', $stats['ok'])); 88 | } 89 | 90 | return 0; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/Command/Router/RouterDumpCommand.php: -------------------------------------------------------------------------------- 1 | router = $router; 30 | } 31 | 32 | protected function execute(InputInterface $input, OutputInterface $output): int 33 | { 34 | $table = new Table($output); 35 | $table 36 | ->setHeaders(['Mask', 'Module', 'Defaults', 'Router']) 37 | ->setRows($this->createRows()); 38 | 39 | $table->render(); 40 | 41 | return 0; 42 | } 43 | 44 | /** 45 | * @return array 46 | */ 47 | protected function createRows(): array 48 | { 49 | return $this->analyse($this->router); 50 | } 51 | 52 | /** 53 | * @return array 54 | */ 55 | protected function analyse(Router $router, ?string $module = null): array 56 | { 57 | if ($router instanceof RouteList) { 58 | $routes = $this->analyseRouteList($router, $module); 59 | } elseif ($router instanceof Route) { 60 | $routes = [(array) $this->analyseRoute($router, $module)]; 61 | } else { 62 | throw new LogicalException(sprintf('Router "%s" is not supported', $router::class)); 63 | } 64 | 65 | return $routes; 66 | } 67 | 68 | /** 69 | * @return array 70 | */ 71 | protected function analyseRouteList(RouteList $router, ?string $module = null): array 72 | { 73 | $routes = []; 74 | 75 | foreach ($router->getRouters() as $subRouter) { 76 | if ($subRouter instanceof RouteList) { 77 | $routes = array_merge( 78 | $routes, 79 | $this->analyseRouteList($subRouter, $module . $router->getModule()) 80 | ); 81 | } elseif ($subRouter instanceof Route) { 82 | $routes = array_merge( 83 | $routes, 84 | [(array) $this->analyseRoute($subRouter, $module . $router->getModule())] 85 | ); 86 | } else { 87 | throw new LogicalException(sprintf('Router "%s" is not supported', $router::class)); 88 | } 89 | } 90 | 91 | return $routes; 92 | } 93 | 94 | /** 95 | * @return stdClass 96 | */ 97 | protected function analyseRoute(Route $router, ?string $module = null): object 98 | { 99 | return (object) [ 100 | 'mask' => $router->getMask(), 101 | 'module' => rtrim((string) $module, ':'), 102 | 'defaults' => $this->analyseDefaults($router->getDefaults()), 103 | 'class' => $router::class, 104 | ]; 105 | } 106 | 107 | /** 108 | * @param string[] $defaults 109 | */ 110 | protected function analyseDefaults(array $defaults): string 111 | { 112 | $primary = []; 113 | 114 | if (isset($defaults['presenter'])) { 115 | $primary[] = $defaults['presenter']; 116 | unset($defaults['presenter']); 117 | } 118 | 119 | if (isset($defaults['action'])) { 120 | $primary[] = $defaults['action']; 121 | unset($defaults['action']); 122 | } 123 | 124 | if (isset($defaults['id'])) { 125 | $primary[] = $defaults['id']; 126 | unset($defaults['id']); 127 | } 128 | 129 | $secondary = []; 130 | 131 | foreach ($defaults as $key => $value) { 132 | $secondary[] = sprintf('%s=>%s', $key, $value); 133 | } 134 | 135 | if ($secondary !== []) { 136 | return implode(':', $primary) . ' [' . implode(',', $secondary) . ']'; 137 | } 138 | 139 | return implode(':', $primary); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /src/Command/Security/SecurityPasswordCommand.php: -------------------------------------------------------------------------------- 1 | passwords = $passwords; 32 | } 33 | 34 | protected function configure(): void 35 | { 36 | $this->addArgument('password', InputArgument::OPTIONAL, 'Given password'); 37 | $this->addOption('count', 'c', InputOption::VALUE_OPTIONAL, '', '10'); 38 | } 39 | 40 | protected function execute(InputInterface $input, OutputInterface $output): int 41 | { 42 | $style = new SymfonyStyle($input, $output); 43 | $style->title('Security Password'); 44 | 45 | if ($input->getArgument('password') !== null) { 46 | // Generate one password 47 | $password = Utils::stringify($input->getArgument('password')); 48 | $style->comment('Password given'); 49 | $encrypted = $this->passwords->hash($password); 50 | $style->success(sprintf('Hashed password: %s', $encrypted)); 51 | 52 | return 0; 53 | } else { 54 | // Generate more passwords 55 | $table = new Table($output); 56 | $table->setHeaders(['ID', 'Generated random password']); 57 | $count = Utils::numerize($input->getOption('count')); 58 | 59 | for ($i = 1; $i <= $count; $i++) { 60 | $table->addRow([$i, $this->passwords->hash(sha1(Random::generate(50) . time() . random_bytes(20)))]); 61 | 62 | if ($i !== $count) { 63 | $table->addRow(new TableSeparator()); 64 | } 65 | } 66 | 67 | $table->render(); 68 | $style->success(sprintf('Total generated and hashed passwords %d.', $count)); 69 | 70 | return 0; 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/Command/Utils/UtilsRandomCommand.php: -------------------------------------------------------------------------------- 1 | addOption('count', 'c', InputOption::VALUE_OPTIONAL, '', '10'); 26 | $this->addOption('length', 'l', InputOption::VALUE_OPTIONAL, '', '50'); 27 | } 28 | 29 | protected function execute(InputInterface $input, OutputInterface $output): int 30 | { 31 | $style = new SymfonyStyle($input, $output); 32 | $style->title('Nette Random'); 33 | 34 | $table = new Table($output); 35 | $table->setHeaders(['ID', 'Generated strings']); 36 | 37 | $count = max(Utils::numerize($input->getOption('count')), 1); 38 | $length = max(Utils::numerize($input->getOption('length')), 1); 39 | for ($i = 1; $i <= $count; $i++) { 40 | $table->addRow([$i, Random::generate($length)]); 41 | 42 | if ($i !== $count) { 43 | $table->addRow(new TableSeparator()); 44 | } 45 | } 46 | 47 | $table->render(); 48 | $style->success(sprintf('Total generated strings %d.', $count)); 49 | 50 | return 0; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/DI/AbstractCompilerExtension.php: -------------------------------------------------------------------------------- 1 | cliMode = $cliMode; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/DI/AdvancedCacheConsoleExtension.php: -------------------------------------------------------------------------------- 1 | Expect::arrayOf( 18 | Expect::anyOf(Expect::string(), Expect::array(), Expect::type(Statement::class)) 19 | ), 20 | 'generators' => Expect::arrayOf( 21 | Expect::anyOf(Expect::string(), Expect::array(), Expect::type(Statement::class)) 22 | ), 23 | ]); 24 | } 25 | 26 | public function getConfigSchema(): Schema 27 | { 28 | return self::createSchema(); 29 | } 30 | 31 | public function loadConfiguration(): void 32 | { 33 | // Skip if isn't CLI 34 | if ($this->cliMode !== true) { 35 | return; 36 | } 37 | 38 | $builder = $this->getContainerBuilder(); 39 | $config = $this->getConfig(); 40 | 41 | // Register generators 42 | $generatorDefinitions = []; 43 | 44 | foreach ($config->generators as $generatorName => $generatorConfig) { 45 | $generatorDef = $builder->addDefinition($this->prefix('generator.' . $generatorName)) 46 | ->setFactory($generatorConfig) 47 | ->setAutowired(false); 48 | 49 | $generatorDefinitions[$generatorName] = $generatorDef; 50 | } 51 | 52 | $builder->addDefinition($this->prefix('generatorCommand')) 53 | ->setFactory(CacheGenerateCommand::class) 54 | ->setArguments([$generatorDefinitions]); 55 | 56 | // Register cleaners 57 | $cleanerDefinitions = []; 58 | 59 | foreach ($config->cleaners as $cleanerName => $cleanerConfig) { 60 | $cleanerDef = $builder->addDefinition($this->prefix('cleaner.' . $cleanerName)) 61 | ->setFactory($cleanerConfig) 62 | ->setAutowired(false); 63 | 64 | $cleanerDefinitions[$cleanerName] = $cleanerDef; 65 | } 66 | 67 | $builder->addDefinition($this->prefix('cleanCommand')) 68 | ->setFactory(CacheCleanCommand::class) 69 | ->setArguments([$cleanerDefinitions]); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/DI/CacheConsoleExtension.php: -------------------------------------------------------------------------------- 1 | Expect::listOf('string'), 17 | ]); 18 | } 19 | 20 | public function getConfigSchema(): Schema 21 | { 22 | return self::createSchema(); 23 | } 24 | 25 | public function loadConfiguration(): void 26 | { 27 | // Skip if isn't CLI 28 | if ($this->cliMode !== true) { 29 | return; 30 | } 31 | 32 | $builder = $this->getContainerBuilder(); 33 | $config = $this->getConfig(); 34 | 35 | // Default values cannot be in schema, arrays are merged by keys 36 | if ($config->purge === []) { 37 | $config->purge = Helpers::expand(['%tempDir%/cache'], $builder->parameters); 38 | } 39 | 40 | $builder->addDefinition($this->prefix('purge')) 41 | ->setFactory(CachePurgeCommand::class, [$config->purge]); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/DI/CachingConsoleExtension.php: -------------------------------------------------------------------------------- 1 | cliMode !== true) { 14 | return; 15 | } 16 | 17 | $builder = $this->getContainerBuilder(); 18 | 19 | $builder->addDefinition($this->prefix('clear')) 20 | ->setFactory(CachingClearCommand::class); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/DI/ConsoleBridgesExtension.php: -------------------------------------------------------------------------------- 1 | > */ 14 | private array $map = [ 15 | 'advancedCache' => AdvancedCacheConsoleExtension::class, 16 | 'cache' => CacheConsoleExtension::class, 17 | 'caching' => CachingConsoleExtension::class, 18 | 'di' => DIConsoleExtension::class, 19 | 'latte' => LatteConsoleExtension::class, 20 | 'router' => RouterConsoleExtension::class, 21 | 'security' => SecurityConsoleExtension::class, 22 | 'utils' => UtilsConsoleExtension::class, 23 | ]; 24 | 25 | /** @var CompilerExtension[] */ 26 | private array $passes = []; 27 | 28 | public function getConfigSchema(): Schema 29 | { 30 | $advancedCache = AdvancedCacheConsoleExtension::createSchema(); 31 | $cache = CacheConsoleExtension::createSchema(); 32 | $di = DIConsoleExtension::createSchema(); 33 | $latte = LatteConsoleExtension::createSchema(); 34 | 35 | return Expect::structure([ 36 | 'advancedCache' => Expect::anyOf(false, $advancedCache)->default($advancedCache), 37 | 'cache' => Expect::anyOf(false, $cache)->default($cache), 38 | 'caching' => Expect::anyOf(false), 39 | 'di' => Expect::anyOf(false, $di)->default($di), 40 | 'latte' => Expect::anyOf(false, $latte)->default($latte), 41 | 'router' => Expect::anyOf(false), 42 | 'security' => Expect::anyOf(false), 43 | 'utils' => Expect::anyOf(false), 44 | ])->castTo('array'); 45 | } 46 | 47 | public function loadConfiguration(): void 48 | { 49 | // Skip if isn't CLI 50 | if ($this->cliMode !== true) { 51 | return; 52 | } 53 | 54 | /** @var mixed[] $config */ 55 | $config = $this->config; 56 | 57 | /** @var false|array|object|null $bridgeConfig */ 58 | foreach ($config as $bridge => $bridgeConfig) { 59 | // Don't register sub extension 60 | 61 | if ($bridgeConfig === false) { 62 | continue; 63 | } 64 | 65 | /** @var CompilerExtension $pass */ 66 | $pass = new $this->map[$bridge]($this->cliMode); 67 | $pass->setCompiler($this->compiler, $this->prefix($bridge)); 68 | 69 | if ($bridgeConfig !== null) { 70 | $pass->setConfig($bridgeConfig); 71 | } 72 | 73 | $pass->loadConfiguration(); 74 | 75 | // Register sub extension a.k.a CompilerPass 76 | $this->passes[$bridge] = $pass; 77 | } 78 | } 79 | 80 | public function beforeCompile(): void 81 | { 82 | // Skip if isn't CLI 83 | if ($this->cliMode !== true) { 84 | return; 85 | } 86 | 87 | foreach ($this->passes as $pass) { 88 | $pass->beforeCompile(); 89 | } 90 | } 91 | 92 | public function afterCompile(ClassType $class): void 93 | { 94 | // Skip if isn't CLI 95 | if ($this->cliMode !== true) { 96 | return; 97 | } 98 | 99 | foreach ($this->passes as $pass) { 100 | $pass->afterCompile($class); 101 | } 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/DI/DIConsoleExtension.php: -------------------------------------------------------------------------------- 1 | Expect::listOf('string'), 17 | ]); 18 | } 19 | 20 | public function getConfigSchema(): Schema 21 | { 22 | return self::createSchema(); 23 | } 24 | 25 | public function loadConfiguration(): void 26 | { 27 | // Skip if isn't CLI 28 | if ($this->cliMode !== true) { 29 | return; 30 | } 31 | 32 | $builder = $this->getContainerBuilder(); 33 | $config = $this->getConfig(); 34 | 35 | // Default values cannot be in schema, arrays are merged by keys 36 | if ($config->purge === []) { 37 | $config->purge = Helpers::expand(['%tempDir%/cache/nette.configurator'], $builder->parameters); 38 | } 39 | 40 | $builder->addDefinition($this->prefix('purge')) 41 | ->setFactory(DIPurgeCommand::class, [$config->purge]); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/DI/DatabaseConsoleExtension.php: -------------------------------------------------------------------------------- 1 | Expect::string()->required(), 17 | 'consoleMode' => Expect::bool(false), 18 | ]); 19 | } 20 | 21 | public function loadConfiguration(): void 22 | { 23 | // Skip if isn't CLI 24 | if ($this->cliMode !== true) { 25 | return; 26 | } 27 | 28 | $builder = $this->getContainerBuilder(); 29 | $config = $this->getConfig(); 30 | 31 | $builder->addDefinition($this->prefix('backupCommand')) 32 | ->setFactory(BackupCommand::class, [$config->backupPath]) 33 | ->setAutowired(false); 34 | 35 | $builder->addDefinition($this->prefix('loadCommand')) 36 | ->setFactory(LoadCommand::class) 37 | ->setAutowired(false); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/DI/LatteConsoleExtension.php: -------------------------------------------------------------------------------- 1 | Expect::listOf('string'), 18 | 'warmupExclude' => Expect::listOf('string'), 19 | 'purge' => Expect::listOf('string'), 20 | ]); 21 | } 22 | 23 | public function getConfigSchema(): Schema 24 | { 25 | return self::createSchema(); 26 | } 27 | 28 | public function loadConfiguration(): void 29 | { 30 | // Skip if isn't CLI 31 | if ($this->cliMode !== true) { 32 | return; 33 | } 34 | 35 | $builder = $this->getContainerBuilder(); 36 | $config = $this->getConfig(); 37 | 38 | // Default values cannot be in schema, arrays are merged by keys 39 | if ($config->warmup === []) { 40 | $config->warmup = Helpers::expand(['%appDir%'], $builder->parameters); 41 | } 42 | 43 | if ($config->purge === []) { 44 | $config->purge = Helpers::expand(['%tempDir%/cache/latte'], $builder->parameters); 45 | } 46 | 47 | $builder->addDefinition($this->prefix('warmup')) 48 | ->setFactory(LatteWarmupCommand::class, [ 49 | 1 => $config->warmup, 50 | 2 => $config->warmupExclude, 51 | ]); 52 | 53 | $builder->addDefinition($this->prefix('purge')) 54 | ->setFactory(LattePurgeCommand::class, [$config->purge]); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/DI/RouterConsoleExtension.php: -------------------------------------------------------------------------------- 1 | cliMode !== true) { 14 | return; 15 | } 16 | 17 | $builder = $this->getContainerBuilder(); 18 | 19 | $builder->addDefinition($this->prefix('dump')) 20 | ->setFactory(RouterDumpCommand::class); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/DI/SecurityConsoleExtension.php: -------------------------------------------------------------------------------- 1 | cliMode !== true) { 14 | return; 15 | } 16 | 17 | $builder = $this->getContainerBuilder(); 18 | 19 | $builder->addDefinition($this->prefix('password')) 20 | ->setFactory(SecurityPasswordCommand::class); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/DI/UtilsConsoleExtension.php: -------------------------------------------------------------------------------- 1 | cliMode !== true) { 14 | return; 15 | } 16 | 17 | $builder = $this->getContainerBuilder(); 18 | 19 | $builder->addDefinition($this->prefix('random')) 20 | ->setFactory(UtilsRandomCommand::class); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/Exception/LogicalException.php: -------------------------------------------------------------------------------- 1 | realpath($path), $ignored); 36 | 37 | /** @var SplFileObject $entry */ 38 | foreach ($iterator as $entry) { 39 | if (!in_array(str_replace('\\', '/', (string) $entry->getRealPath()), $ignored, true)) { 40 | if ($entry->isDir()) { 41 | rmdir((string) $entry->getRealPath()); 42 | } else { 43 | unlink((string) $entry->getRealPath()); 44 | } 45 | } 46 | } 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/Utils/Utils.php: -------------------------------------------------------------------------------- 1 |