Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.71% covered (warning)
85.71%
24 / 28
88.89% covered (warning)
88.89%
8 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
DataMessageValue
85.71% covered (warning)
85.71%
24 / 28
88.89% covered (warning)
88.89%
8 / 9
15.66
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
 new
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getCode
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getData
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 dump
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
 isSameAs
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
20
 toJsonArray
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
1
 jsonClassHintFor
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 newFromJsonArray
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace Wikimedia\Message;
4
5use Wikimedia\JsonCodec\JsonCodecableTrait;
6
7/**
8 * Value object representing a message for i18n with alternative
9 * machine-readable data.
10 *
11 * This augments a MessageValue with an additional machine-readable code and
12 * structured data. The intended use is to facilitate error reporting in APIs.
13 *
14 * For example, a MessageValue reporting an "integer out of range" error might
15 * use one of three message keys, depending on whether there is a minimum, a
16 * maximum, or both. But an API would likely want to use one code for all three
17 * cases, and would likely want the endpoints represented along the lines of
18 * `[ 'min' => 1, 'max' => 10 ]` rather than
19 * `[ 0 => new ScalarParam( ParamType::TEXT, 1 ), 1 => new ScalarParam( ParamType::TEXT, 10 ) ]`.
20 *
21 * DataMessageValues are pure value objects and are newable and (de)serializable.
22 *
23 * @newable
24 */
25class DataMessageValue extends MessageValue {
26    use JsonCodecableTrait;
27
28    private string $code;
29    private ?array $data;
30
31    /**
32     * @stable to call
33     *
34     * @param string $key
35     * @param (MessageParam|MessageValue|string|int|float)[] $params
36     * @param string|null $code String representing the concept behind
37     *  this message.
38     * @param array|null $data Structured data representing the concept
39     *  behind this message.
40     */
41    public function __construct( string $key, array $params = [], ?string $code = null, ?array $data = null ) {
42        parent::__construct( $key, $params );
43
44        $this->code = $code ?? $key;
45        $this->data = $data;
46    }
47
48    /**
49     * Static constructor for easier chaining of `->params()` methods
50     *
51     * @param string $key
52     * @param (MessageParam|MessageValue|string|int|float)[] $params
53     * @param string|null $code
54     * @param array|null $data
55     *
56     * @return DataMessageValue
57     */
58    public static function new(
59        string $key,
60        array $params = [],
61        ?string $code = null,
62        ?array $data = null
63    ): DataMessageValue {
64        return new DataMessageValue( $key, $params, $code, $data );
65    }
66
67    /**
68     * Get the message code
69     */
70    public function getCode(): string {
71        return $this->code;
72    }
73
74    /**
75     * Get the message's structured data
76     * @return array|null
77     */
78    public function getData(): ?array {
79        return $this->data;
80    }
81
82    public function dump(): string {
83        $contents = '';
84        if ( $this->getParams() ) {
85            $contents = '<params>';
86            foreach ( $this->getParams() as $param ) {
87                $contents .= $param->dump();
88            }
89            $contents .= '</params>';
90        }
91
92        if ( $this->data !== null ) {
93            $contents .= '<data>' . htmlspecialchars( json_encode( $this->data ), ENT_NOQUOTES ) . '</data>';
94        }
95
96        return '<datamessage key="' . htmlspecialchars( $this->getKey() ) . '"'
97            . ' code="' . htmlspecialchars( $this->code ) . '">'
98            . $contents
99            . '</datamessage>';
100    }
101
102    public function isSameAs( MessageValue $mv ): bool {
103        return parent::isSameAs( $mv ) &&
104            $mv instanceof DataMessageValue &&
105            $this->code === $mv->code &&
106            $this->data === $mv->data;
107    }
108
109    public function toJsonArray(): array {
110        // WARNING: When changing how this class is serialized, follow the instructions
111        // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>!
112        return parent::toJsonArray() + [
113                'code' => $this->code,
114                'data' => $this->data,
115            ];
116    }
117
118    /** @inheritDoc */
119    public static function jsonClassHintFor( string $keyName ) {
120        // JsonStaticClassCodec invokes $className::jsonClassHintFor() so
121        // we need to have an explicit definition in DataMessageValue, we
122        // can't simply inherit the static method from MessageValue.
123        return parent::jsonClassHintFor( $keyName );
124    }
125
126    public static function newFromJsonArray( array $json ): DataMessageValue {
127        // WARNING: When changing how this class is serialized, follow the instructions
128        // at <https://www.mediawiki.org/wiki/Manual:Parser_cache/Serialization_compatibility>!
129        return new self( $json['key'], $json['params'], $json['code'], $json['data'] );
130    }
131}