Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.80% covered (warning)
89.80%
44 / 49
75.00% covered (warning)
75.00%
3 / 4
CRAP
0.00% covered (danger)
0.00%
0 / 1
CopyStringCommand
89.80% covered (warning)
89.80%
44 / 49
75.00% covered (warning)
75.00%
3 / 4
11.13
0.00% covered (danger)
0.00%
0 / 1
 configure
100.00% covered (success)
100.00%
23 / 23
100.00% covered (success)
100.00%
1 / 1
1
 addLineToFile
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 applyReplaceRule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 execute
100.00% covered (success)
100.00%
20 / 20
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2
3/**
4 * Language command: copy string.
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2020.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Console
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development Wiki
28 */
29
30namespace VuFindConsole\Command\Language;
31
32use Symfony\Component\Console\Attribute\AsCommand;
33use Symfony\Component\Console\Input\InputArgument;
34use Symfony\Component\Console\Input\InputInterface;
35use Symfony\Component\Console\Input\InputOption;
36use Symfony\Component\Console\Output\OutputInterface;
37
38/**
39 * Language command: copy string.
40 *
41 * @category VuFind
42 * @package  Console
43 * @author   Demian Katz <demian.katz@villanova.edu>
44 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
45 * @link     https://vufind.org/wiki/development Wiki
46 */
47#[AsCommand(
48    name: 'language/copystring',
49    description: 'String copier'
50)]
51class CopyStringCommand extends AbstractCommand
52{
53    /**
54     * Configure the command.
55     *
56     * @return void
57     */
58    protected function configure()
59    {
60        $note = "(may include 'textdomain::' prefix)";
61        $this
62            ->setHelp('Copies one language string to another.')
63            ->addArgument(
64                'source',
65                InputArgument::REQUIRED,
66                'the source key to read ' . $note
67            )->addArgument(
68                'target',
69                InputArgument::REQUIRED,
70                'the target key to write ' . $note
71            )->addOption(
72                'replace',
73                null,
74                InputOption::VALUE_REQUIRED,
75                'string delimited by replaceDelimiter option, representing '
76                . "search-and-replace operation.\ne.g. textToReplace/replacementText"
77            )->addOption(
78                'replaceDelimiter',
79                null,
80                InputOption::VALUE_REQUIRED,
81                'delimiter used in replace option',
82                '/'
83            );
84    }
85
86    /**
87     * Add a line to a language file
88     *
89     * @param string $filename File to update
90     * @param string $key      Name of language key
91     * @param string $value    Value of translation
92     *
93     * @return void
94     */
95    protected function addLineToFile($filename, $key, $value)
96    {
97        $fHandle = fopen($filename, 'a');
98        if (!$fHandle) {
99            throw new \Exception('Cannot open ' . $filename . ' for writing.');
100        }
101        fwrite($fHandle, "\n$key = \"" . $value . "\"\n");
102        fclose($fHandle);
103    }
104
105    /**
106     * Apply a replacement rule, if necessary.
107     *
108     * @param string $text Text to transform
109     * @param array  $rule Replacement rule (empty for no change; [text to replace,
110     * replacement] array to apply a transformation)
111     *
112     * @return string
113     */
114    protected function applyReplaceRule(string $text, array $rule): string
115    {
116        return empty($rule) ? $text : str_replace($rule[0], $rule[1], $text);
117    }
118
119    /**
120     * Run the command.
121     *
122     * @param InputInterface  $input  Input object
123     * @param OutputInterface $output Output object
124     *
125     * @return int 0 for success
126     */
127    protected function execute(InputInterface $input, OutputInterface $output)
128    {
129        $source = $input->getArgument('source');
130        $target = $input->getArgument('target');
131        $replace = $input->getOption('replace');
132        $replaceDelimiter = $input->getOption('replaceDelimiter');
133        $replaceRule = empty($replace) ? [] : explode($replaceDelimiter, $replace);
134
135        [$sourceDomain, $sourceKey] = $this->extractTextDomain($source);
136        [$targetDomain, $targetKey] = $this->extractTextDomain($target);
137
138        if (
139            !($sourceDir = $this->getLangDir($output, $sourceDomain))
140            || !($targetDir = $this->getLangDir($output, $targetDomain, true))
141        ) {
142            return 1;
143        }
144
145        // First, collect the source values from the source text domain:
146        $sources = [];
147        $sourceCallback
148            = function ($full) use ($output, $replaceRule, $sourceKey, &$sources) {
149                $strings = $this->reader->getTextDomain($full, false);
150                if (!isset($strings[$sourceKey])) {
151                    $output->writeln('Source key not found.');
152                    return;
153                }
154                $sources[basename($full)] = $this->applyReplaceRule(
155                    $strings[$sourceKey],
156                    $replaceRule
157                );
158            };
159        $this->processDirectory($sourceDir, $sourceCallback, [$output, 'writeln']);
160
161        // Make sure that all target files exist:
162        $this->createMissingFiles($targetDir->path, array_keys($sources));
163
164        // Now copy the values to their destination:
165        $targetCallback = function ($full) use ($targetKey, $sources) {
166            if (isset($sources[basename($full)])) {
167                $this->addLineToFile($full, $targetKey, $sources[basename($full)]);
168                $this->normalizer->normalizeFile($full);
169            }
170        };
171        $this->processDirectory($targetDir, $targetCallback, [$output, 'writeln']);
172
173        return 0;
174    }
175}