Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
72.73% covered (warning)
72.73%
32 / 44
66.67% covered (warning)
66.67%
2 / 3
CRAP
0.00% covered (danger)
0.00%
0 / 1
CreateHashesCommand
72.73% covered (warning)
72.73%
32 / 44
66.67% covered (warning)
66.67%
2 / 3
19.56
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 configure
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
1
 execute
62.50% covered (warning)
62.50%
20 / 32
0.00% covered (danger)
0.00%
0 / 1
21.91
1<?php
2
3/**
4 * This command iterates recursively through the whole phpMyFAQ project and
5 * creates SHA-1 keys for all files
6 *
7 * This Source Code Form is subject to the terms of the Mozilla Public License,
8 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
9 * obtain one at https://mozilla.org/MPL/2.0/.
10 *
11 * @package   phpMyFAQ
12 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
13 * @copyright 2012-2026 phpMyFAQ Team
14 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
15 * @link      https://www.phpmyfaq.de
16 * @since     2012-04-11
17 */
18
19declare(strict_types=1);
20
21namespace phpMyFAQ\Command;
22
23use phpMyFAQ\System;
24use Symfony\Component\Console\Attribute\AsCommand;
25use Symfony\Component\Console\Command\Command;
26use Symfony\Component\Console\Input\InputInterface;
27use Symfony\Component\Console\Input\InputOption;
28use Symfony\Component\Console\Output\OutputInterface;
29use Symfony\Component\Console\Style\SymfonyStyle;
30use Symfony\Component\Filesystem\Filesystem;
31use Throwable;
32
33#[AsCommand(name: 'phpmyfaq:hashes:create', description: 'Generate phpMyFAQ file hashes')]
34class CreateHashesCommand extends Command
35{
36    public function __construct(
37        private readonly System $system,
38        private readonly Filesystem $filesystem,
39    ) {
40        parent::__construct();
41    }
42
43    protected function configure(): void
44    {
45        $this->addOption(
46            name: 'root',
47            shortcut: null,
48            mode: InputOption::VALUE_OPTIONAL,
49            description: 'Optional phpMyFAQ root directory if different from the current install',
50        )->addOption(
51            name: 'out',
52            shortcut: null,
53            mode: InputOption::VALUE_OPTIONAL,
54            description: 'Optional file to store the generated hashes as JSON',
55        );
56    }
57
58    protected function execute(InputInterface $input, OutputInterface $output): int
59    {
60        $symfonyStyle = new SymfonyStyle($input, $output);
61
62        $root = $input->getOption('root');
63        $rootDir = defined('PMF_ROOT_DIR') ? PMF_ROOT_DIR : null;
64        if (is_string($root) && '' !== $root) {
65            if (!is_dir($root)) {
66                $symfonyStyle->error(sprintf("The provided root directory '%s' does not exist.", $root));
67                return Command::FAILURE;
68            }
69
70            $rootDir = rtrim($root, characters: '/');
71        }
72
73        if (null !== $rootDir && !defined('PMF_ROOT_DIR')) {
74            define('PMF_ROOT_DIR', $rootDir);
75        }
76
77        if (!defined('PMF_ROOT_DIR')) {
78            $symfonyStyle->error(
79                'PMF_ROOT_DIR is not defined. Provide --root option or run inside a configured installation.',
80            );
81            return Command::FAILURE;
82        }
83
84        try {
85            $hashes = $this->system->createHashes();
86        } catch (Throwable $throwable) {
87            $symfonyStyle->error('Failed to create hashes: ' . $throwable->getMessage());
88            return Command::FAILURE;
89        }
90
91        $outputPath = $input->getOption('out');
92        if (is_string($outputPath) && '' !== $outputPath) {
93            $directory = dirname($outputPath);
94            if (!$this->filesystem->exists($directory)) {
95                $this->filesystem->mkdir($directory);
96            }
97
98            try {
99                $this->filesystem->dumpFile($outputPath, $hashes);
100            } catch (Throwable $throwable) {
101                $symfonyStyle->error('Unable to write hashes: ' . $throwable->getMessage());
102                return Command::FAILURE;
103            }
104
105            $symfonyStyle->success(sprintf('Hashes written to %s', $outputPath));
106            return Command::SUCCESS;
107        }
108
109        $output->writeln($hashes);
110        return Command::SUCCESS;
111    }
112}