Failed Conditions
Push — master ( 01d6ae...9ca6d9 )
by Philippe
534:14 queued 469:10
created

Ispell::getSupportedLanguages()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 40
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 26
nc 3
nop 0
dl 0
loc 40
rs 9.1928
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace PhpSpellcheck\Spellchecker;
6
7
use PhpSpellcheck\Exception\ProcessHasErrorOutputException;
8
use PhpSpellcheck\Exception\RuntimeException;
9
use PhpSpellcheck\Utils\CommandLine;
10
use PhpSpellcheck\Utils\IspellOutputParser;
11
use PhpSpellcheck\Utils\ProcessRunner;
12
use Symfony\Component\Process\Process;
13
use Webmozart\Assert\Assert;
14
15
class Ispell implements SpellcheckerInterface
16
{
17
    /**
18
     * @var string[]|null
19
     */
20
    private $supportedLanguages;
21
22
    /**
23
     * @var CommandLine
24
     */
25
    private $ispellCommandLine;
26
27
    /**
28
     * @var null|CommandLine
29
     */
30
    private $shellEntryPoint;
31
32 5
    public function __construct(CommandLine $ispellCommandLine, ?CommandLine $shellEntryPoint = null)
33
    {
34 5
        $this->ispellCommandLine = $ispellCommandLine;
35 5
        $this->shellEntryPoint = $shellEntryPoint;
36 5
    }
37
38
    /**
39
     * {@inheritDoc}
40
     */
41 3
    public function check(string $text, array $languages = [], array $context = [], ?string $encoding = null): iterable
42
    {
43 3
        Assert::greaterThan($languages, 1, 'Ispell spellchecker doesn\'t support multiple languages check');
44
45 3
        $cmd = $this->ispellCommandLine->addArg('-a');
46
47 3
        if (!empty($languages)) {
48 2
            $cmd = $cmd->addArgs(['-d', implode(',', $languages)]);
49
        }
50
51 3
        $process = new Process($cmd->getArgs());
52
53
        // Add prefix characters putting Ispell's type of spellcheckers in terse-mode,
54
        // ignoring correct words and thus speeding execution
55 3
        $process->setInput('!' . PHP_EOL . $text . PHP_EOL . '%');
56
57 3
        $output = ProcessRunner::run($process)->getOutput();
58
59 3
        if ($process->getErrorOutput() !== '') {
60 1
            throw new ProcessHasErrorOutputException($process->getErrorOutput(), $text, $process->getCommandLine());
61
        }
62
63 2
        return IspellOutputParser::parseMisspellings($output, $context);
64
    }
65
66
    public function getCommandLine(): CommandLine
67
    {
68
        return $this->ispellCommandLine;
69
    }
70
71
    /**
72
     * {@inheritDoc}
73
     */
74 2
    public function getSupportedLanguages(): iterable
75
    {
76
        if ($this->supportedLanguages === null) {
77
            $shellEntryPoint = $this->shellEntryPoint ?? new CommandLine([]);
78 2
            $whichCommand = clone $shellEntryPoint;
79 2
            $process = new Process(
80 2
                $whichCommand
81 2
                    ->addArg('which')
82
                    ->addArg('ispell')
83 2
                    ->getArgs()
84 2
            );
85 2
            $process->mustRun();
86
            $binaryPath = trim($process->getOutput());
87 2
88 2
            $lsCommand = clone $shellEntryPoint;
89
            $process = new Process(
90 2
                $lsCommand
91 2
                    ->addArg('ls')
92
                    ->addArg(\dirname($binaryPath, 2) . '/lib/ispell')
93 2
                    ->getArgs()
94 2
            );
95 2
            $process->mustRun();
96
97 2
            $listOfFiles = trim($process->getOutput());
98
99 2
            $this->supportedLanguages = [];
100
            foreach (explode(PHP_EOL, $listOfFiles) as $file) {
101 2
                if (strpos($file, '.aff', -4) === false) {
102 2
                    continue;
103 2
                }
104 1
105
                $this->supportedLanguages[] = \Safe\substr($file, 0, -4);
106
            }
107 2
108
            if (empty($this->supportedLanguages)) {
109
                throw new RuntimeException('Ispell doesn\'t have any directory or none could have been found');
110 2
            }
111
        }
112
113
        return $this->supportedLanguages;
114
    }
115 2
116
    public static function create(?string $ispellCommandLineAsString): self
117
    {
118 1
        return new self(new CommandLine($ispellCommandLineAsString ?? 'ispell'));
119
    }
120
}
121