MessageFormatter   A
last analyzed

Complexity

Total Complexity 21

Size/Duplication

Total Lines 163
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 2
Bugs 0 Features 0
Metric Value
wmc 21
c 2
b 0
f 0
lcom 1
cbo 3
dl 0
loc 163
ccs 61
cts 61
cp 1
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
C __construct() 0 36 7
A addMessageFormatter() 0 10 2
A getFormattedMessageFromId() 0 4 1
B format() 0 26 6
A getMessageTemplate() 0 8 3
A getLocator() 0 4 1
A messageForIdExists() 0 4 1
1
<?php
2
/**
3
 * MessageFormatter.php
4
 *
5
 * MIT LICENSE
6
 *
7
 * LICENSE: This source file is subject to the MIT license.
8
 * A copy of the licenses text was distributed alongside this
9
 * file (usually the repository or package root). The text can also
10
 * be obtained through one of the following sources:
11
 * * http://opensource.org/licenses/MIT
12
 * * https://github.com/suralc/pvra/blob/master/LICENSE
13
 *
14
 * @author     suralc <[email protected]>
15
 * @license    http://opensource.org/licenses/MIT  MIT
16
 */
17
namespace Pvra\Result;
18
19
20
/**
21
 * Class MessageFormatter
22
 *
23
 * @package Pvra\Result
24
 */
25
class MessageFormatter
26
{
27
    use CallbackChainHelperTrait;
28
29
    const CALLBACK_POSITION_PREPEND = 1,
30
        CALLBACK_POSITION_APPEND = 2;
31
32
    const FORMAT_KEY_TARGET_ID = 'targetId',
33
        FORMAT_KEY_LINE = 'line',
34
        FORMAT_KEY_VERSION = 'version',
35
        FORMAT_KEY_REASON_ID = 'reasonId',
36
        FORMAT_KEY_REASON_NAME = 'reasonName';
37
38
    /**
39
     * @var MessageLocator
40
     */
41
    private $locator;
42
    /**
43
     * @var bool
44
     */
45
    private $throwOnMissingTemplate = false;
46
    /**
47
     * @var array
48
     */
49
    private $messageFormatters = [];
50
51
    /**
52
     * @param array|MessageLocator $locator
53
     * @param bool $addDefaultFormatter
54
     * @param bool $addDefaultExclusiveMissingMessageLocatorHandler
55
     * @param bool $throwOnMissingTemplate
56
     */
57 74
    public function __construct(
58
        $locator = null,
59
        $addDefaultFormatter = true,
60
        $addDefaultExclusiveMissingMessageLocatorHandler = false,
61
        $throwOnMissingTemplate = false
62
    ) {
63 74
        $this->throwOnMissingTemplate = $throwOnMissingTemplate;
64 74
        if ($locator instanceof MessageLocator) {
65 58
            $this->locator = $locator;
66 45
        } elseif ($locator === null) {
67 12
            $this->locator = MessageLocator::fromPhpFile(__DIR__ . '/../../data/default_messages.php');
68 10
        } elseif (is_array($locator)) {
69 2
            $this->locator = MessageLocator::fromArray($locator);
70 1
        } else {
71 2
            throw new \InvalidArgumentException('The $locator parameter needs to be an instance of ResultMessageLocator, null or an array containing messages');
72
        }
73
74 72
        if ($addDefaultFormatter) {
75
            $this->addMessageFormatter(function ($id, $format) {
76 8
                return sprintf('%s in :%s:::%s:', $format, MessageFormatter::FORMAT_KEY_TARGET_ID,
77 8
                    MessageFormatter::FORMAT_KEY_LINE);
78 18
            });
79 9
        }
80
81 72
        if ($addDefaultExclusiveMissingMessageLocatorHandler) {
82 28
            $this->getLocator()->addMissingMessageHandler(function ($id, MessageLocator $locator) {
83 6
                $locator->terminateCallbackChain();
84 6
                $msg = 'Message for id "%s" %s could not be found.';
85 6
                $desc = '';
86 6
                if (($name = Reason::getReasonNameFromValue((int)$id)) !== 'UNKNOWN') {
87 6
                    $desc = '[' . $name . ']';
88 3
                }
89 6
                return sprintf($msg, $id, $desc);
90 28
            }, MessageLocator::CALLBACK_POSITION_PREPEND);
91 14
        }
92 72
    }
93
94
    /**
95
     * @param callable $transformer A callback with the following signature:
96
     * ```
97
     * function(int|string $msgId, string $template, MessageFormatter $f, array $data) : string
98
     * ```
99
     * @param int $position
100
     * @return $this
101
     */
102 20
    public function addMessageFormatter(callable $transformer, $position = self::CALLBACK_POSITION_APPEND)
103
    {
104 20
        if ($position === self::CALLBACK_POSITION_PREPEND) {
105 2
            array_unshift($this->messageFormatters, $transformer);
106 1
        } else {
107 20
            array_push($this->messageFormatters, $transformer);
108
        }
109
110 20
        return $this;
111
    }
112
113
    /**
114
     * @param $msgId
115
     * @param array $data
116
     * @return mixed
117
     * @throws \Exception
118
     */
119 54
    public function getFormattedMessageFromId($msgId, array $data = [])
120
    {
121 54
        return $this->format(['id' => $msgId, 'template' => $this->getMessageTemplate($msgId)], $data);
122
    }
123
124
    /**
125
     * @param $messageInfo
126
     * @param array $data
127
     * @param bool $runUserFormatters
128
     * @return mixed
129
     */
130 56
    public function format($messageInfo, array $data = [], $runUserFormatters = true)
131
    {
132 56
        if (is_string($messageInfo)) {
133 2
            $messageInfo = ['template' => $messageInfo, 'id' => 'unknown_message_id'];
134 1
        }
135 56
        $data += ['id' => $messageInfo['id']];
136 56
        if ($runUserFormatters) {
137 56
            $this->inCallbackChain(true);
138
            /** @var callable $formatter */
139 56
            foreach ($this->messageFormatters as $formatter) {
140 10
                if ($this->isCallbackChainToBeTerminated()) {
141 2
                    break;
142
                }
143 10
                $messageInfo['template'] = $formatter($messageInfo['id'], $messageInfo['template'], $this, $data);
144 28
            }
145 56
            $this->markCallbackChainTerminated();
146 28
        }
147
148 56
        foreach ($data as $name => $value) {
149
            // str_replace(array, array) would be better
150
            // but this appears to be faster than iterating over the $data array and manipulating the keys
151 56
            $messageInfo['template'] = str_replace(':' . $name . ':', $value, $messageInfo['template']);
152 28
        }
153
154 56
        return $messageInfo['template'];
155
    }
156
157
    /**
158
     * @param $msgId
159
     * @return null|string
160
     * @throws \Exception
161
     */
162 58
    public function getMessageTemplate($msgId)
163
    {
164 58
        if ($this->throwOnMissingTemplate && !$this->getLocator()->messageExists($msgId)) {
165 2
            throw new \Exception(sprintf('Could not find message for id: "%s"', $msgId));
166
        } else {
167 56
            return $this->getLocator()->getMessage($msgId);
168
        }
169
    }
170
171
    /**
172
     * @return \Pvra\Result\MessageLocator
173
     */
174 62
    public function getLocator()
175
    {
176 62
        return $this->locator;
177
    }
178
179
    /**
180
     * @param $msgId
181
     * @return bool
182
     */
183 2
    public function messageForIdExists($msgId)
184
    {
185 2
        return $this->getLocator()->messageExists($msgId);
186
    }
187
}
188