Passed
Branch master (78c061)
by Alexander
08:14
created

MessageSource::getFilePath()   B

Complexity

Conditions 7
Paths 3

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 7

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 7
eloc 7
c 2
b 0
f 0
nc 3
nop 3
dl 0
loc 13
ccs 8
cts 8
cp 1
crap 7
rs 8.8333
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Translator\Message\Php;
6
7
use InvalidArgumentException;
8
use RuntimeException;
9
use Yiisoft\Translator\MessageReaderInterface;
10
use Yiisoft\Translator\MessageWriterInterface;
11
12
use function array_key_exists;
13
use function is_array;
14
use function is_string;
15
16
final class MessageSource implements MessageReaderInterface, MessageWriterInterface
17
{
18
    private string $path;
19
20
    /**
21
     * @psalm-var array<string, array<string, array<string, string>>>
22
     */
23
    private array $messages = [];
24
25 14
    public function __construct(string $path)
26
    {
27 14
        $this->path = $path;
28 14
    }
29
30 6
    public function getMessage(string $id, string $category, string $locale, array $parameters = []): ?string
31
    {
32 6
        if (!isset($this->messages[$category][$locale])) {
33 6
            $this->read($category, $locale);
34
        }
35
36 5
        return $this->messages[$category][$locale][$id] ?? null;
37
    }
38
39 2
    public function getMessages(string $category, string $locale): array
40
    {
41 2
        if (!isset($this->messages[$category][$locale])) {
42 2
            $this->read($category, $locale);
43
        }
44
45 2
        $messages = $this->messages[$category][$locale] ?? [];
46 2
        foreach ($messages as &$message) {
47 2
            $message = ['message' => $message];
48
        }
49
        /** @psalm-var array<string, array<string, string>> $messages */
50
51 2
        return $messages;
52
    }
53
54
    /**
55
     * @psalm-param array<string, array<string, string>> $messages
56
     */
57 12
    public function write(string $category, string $locale, array $messages): void
58
    {
59 12
        $content = $this->generateMessagesFileContent($messages);
60
61 9
        $path = $this->getFilePath($category, $locale, true);
62
63 8
        if (file_put_contents($path, $content, LOCK_EX) === false) {
64 1
            throw new RuntimeException('Can not write to ' . $path);
65
        }
66 8
    }
67
68 11
    private function getFilePath(string $category, string $locale, bool $withCreateDir = false): string
69
    {
70 11
        if ($locale !== '' && !preg_match('/^[a-z_-]+$/i', $locale)) {
71 1
            throw new InvalidArgumentException(sprintf('Invalid locale code: "%s".', $locale));
72
        }
73
74 10
        $filePath = $this->path . DIRECTORY_SEPARATOR . $locale;
75 10
        if ($withCreateDir && !file_exists($filePath) && !mkdir($filePath, 0775, true) && !is_dir($filePath)) {
76 1
            throw new RuntimeException(sprintf('Directory "%s" was not created', $filePath));
77
        }
78 9
        $filePath .= DIRECTORY_SEPARATOR . $category . '.php';
79
80 9
        return $filePath;
81
    }
82
83 8
    private function read(string $category, string $locale): void
84
    {
85 8
        $path = $this->getFilePath($category, $locale);
86
87 7
        if (is_file($path)) {
88 6
            $messages = include $path;
89
90 6
            if (!is_array($messages)) {
91 6
                throw new RuntimeException('Invalid file format: ' . $path);
92
            }
93
            /** @psalm-var array<string, string> $messages */
94
        } else {
95 1
            $messages = [];
96
        }
97
98 7
        $this->messages[$category][$locale] = $messages;
99 7
    }
100
101
    /**
102
     * @psalm-param array<string, array<string, string>> $messages
103
     */
104 12
    private function generateMessagesFileContent(array $messages): string
105
    {
106 12
        $content = "<?php\nreturn ";
107 12
        if (empty($messages)) {
108 3
            $content .= '[]';
109
        } else {
110 9
            $content .= $this->messagesToCode($messages);
111 6
            $content .= ';';
112
        }
113 9
        $content .= "\n";
114
115 9
        return $content;
116
    }
117
118
    /**
119
     * @psalm-param array<string, array<string, string>> $messages
120
     */
121 9
    private function messagesToCode(array $messages): string
122
    {
123 9
        $code = '[';
124 9
        foreach ($messages as $messageId => $messageData) {
125 9
            if (!array_key_exists('message', $messageData)) {
126 1
                throw new InvalidArgumentException("Message is not valid for ID \"$messageId\". \"message\" key is missing.");
127
            }
128
129
            /** @psalm-suppress DocblockTypeContradiction */
130 8
            if (!is_string($messageData['message'])) {
131 1
                throw new InvalidArgumentException("Message is not a string for ID \"$messageId\".");
132
            }
133
134 7
            if (array_key_exists('comment', $messageData)) {
135
                /** @psalm-suppress DocblockTypeContradiction */
136 5
                if (!is_string($messageData['comment'])) {
137 1
                    throw new InvalidArgumentException("Message comment is not a string for ID \"$messageId\".");
138
                }
139
140 4
                $code .= "\n" . '    /* ' . $messageData['comment'] . ' */';
141
            }
142
143 6
            $code .= "\n" . '    ';
144 6
            $code .= var_export($messageId, true);
145 6
            $code .= ' => ';
146 6
            $code .= var_export($messageData['message'], true);
147 6
            $code .= ',';
148
        }
149 6
        $code .= "\n" . ']';
150 6
        return $code;
151
    }
152
}
153