Test Failed
Push — master ( dc0637...2e721f )
by Magnar Ovedal
03:15
created

Pspell   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 97
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 32
dl 0
loc 97
rs 10
c 0
b 0
f 0
wmc 11

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getWordsToCheck() 0 9 2
A errorHandler() 0 3 1
A __construct() 0 4 1
A fromLocale() 0 18 3
A contains() 0 18 4
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Stadly\PasswordPolice\WordList;
6
7
use ErrorException;
8
use InvalidArgumentException;
9
use RuntimeException;
10
use Stadly\PasswordPolice\CaseConverter\CaseConverterInterface;
11
12
final class Pspell implements WordListInterface
13
{
14
    /**
15
     * @var int Pspell dictionary.
16
     */
17
    private $pspell;
18
19
    /**
20
     * @var CaseConverterInterface[] Case converters.
21
     */
22
    private $caseConverters;
23
24
    /**
25
     * Pspell dictionaries are case-sensitive.
26
     * Specify case converters if tests should also be performed for the word converted to other cases.
27
     *
28
     * @param int $pspell Pspell dictionary link, as generated by `pspell_new` and friends.
29
     * @param CaseConverterInterface... $caseConverters Case converters.
0 ignored issues
show
Documentation Bug introduced by
The doc comment CaseConverterInterface... at position 0 could not be parsed: Unknown type name 'CaseConverterInterface...' at position 0 in CaseConverterInterface....
Loading history...
30
     */
31
    public function __construct(int $pspell, CaseConverterInterface... $caseConverters)
32
    {
33
        $this->pspell = $pspell;
34
        $this->caseConverters = $caseConverters;
35
    }
36
37
    /**
38
     * Pspell dictionaries are case-sensitive.
39
     * Specify case converters if tests should also be performed for the word converted to other cases.
40
     *
41
     * @param string $locale Locale of the pspell dictionary to load. For example `en-US` or `de`.
42
     * @param CaseConverterInterface... $caseConverters Case converters.
0 ignored issues
show
Documentation Bug introduced by
The doc comment CaseConverterInterface... at position 0 could not be parsed: Unknown type name 'CaseConverterInterface...' at position 0 in CaseConverterInterface....
Loading history...
43
     * @throws RuntimeException If the pspell dictionary could not be loaded.
44
     * @return self Pspell word list.
45
     */
46
    public static function fromLocale(string $locale, CaseConverterInterface... $caseConverters): self
47
    {
48
        if (preg_match('{^[a-z]{2}(?:[-_][A-Z]{2})?$}', $locale) !== 1) {
49
            throw new InvalidArgumentException(sprintf('%s is not a valid locale.', $locale));
50
        }
51
52
        set_error_handler([self::class, 'errorHandler']);
53
        try {
54
            $pspell = pspell_new($locale);
55
        } catch (ErrorException $exception) {
56
            throw new RuntimeException('An error occurred while loading the word list.', /*code*/0, $exception);
57
        } finally {
58
            restore_error_handler();
59
        }
60
61
        assert($pspell !== false);
62
63
        return new self($pspell, ...$caseConverters);
64
    }
65
66
    /**
67
     * {@inheritDoc}
68
     */
69
    public function contains(string $word): bool
70
    {
71
        foreach ($this->getWordsToCheck($word) as $wordVariant) {
72
            set_error_handler([self::class, 'errorHandler']);
73
            try {
74
                $check = pspell_check($this->pspell, $wordVariant);
75
            } catch (ErrorException $exception) {
76
                throw new RuntimeException('An error occurred while using the word list.', /*code*/0, $exception);
77
            } finally {
78
                restore_error_handler();
79
            }
80
81
            if ($check) {
82
                return true;
83
            }
84
        }
85
86
        return false;
87
    }
88
89
    /**
90
     * @return string[] Variants of the word to check.
91
     */
92
    private function getWordsToCheck(string $word): array
93
    {
94
        $words = [$word];
95
96
        foreach ($this->caseConverters as $caseConverter) {
97
            $words[] = $caseConverter->convert($word);
98
        }
99
100
        return array_unique($words);
101
    }
102
103
    /**
104
     * @throws ErrorException Error converted to an exception.
105
     */
106
    private static function errorHandler(int $severity, string $message, string $filename, int $line): void
107
    {
108
        throw new ErrorException($message, /*code*/0, $severity, $filename, $line);
109
    }
110
}
111