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