Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
8.62% covered (danger)
8.62%
5 / 58
36.36% covered (danger)
36.36%
4 / 11
CRAP
0.00% covered (danger)
0.00%
0 / 1
JsonContent
8.62% covered (danger)
8.62%
5 / 58
36.36% covered (danger)
36.36%
4 / 11
325.21
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 getData
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 isValid
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 beautifyJSON
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 rootValueTable
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
12
 objectTable
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 objectRow
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 arrayTable
0.00% covered (danger)
0.00%
0 / 14
0.00% covered (danger)
0.00%
0 / 1
12
 arrayRow
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 valueCell
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 primitiveValue
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * JSON Content Model
4 *
5 * @file
6 *
7 * @author Ori Livneh <ori@wikimedia.org>
8 * @author Kunal Mehta <legoktm@gmail.com>
9 */
10
11use MediaWiki\Html\Html;
12use MediaWiki\Status\Status;
13
14/**
15 * JSON text content that can be viewed and edit directly by users.
16 *
17 * @since 1.24
18 * @newable
19 * @stable to extend
20 * @ingroup Content
21 */
22class JsonContent extends TextContent {
23
24    /**
25     * @since 1.25
26     * @var Status
27     */
28    protected $jsonParse;
29
30    /**
31     * @param string $text JSON
32     * @param string $modelId
33     * @stable to call
34     */
35    public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
36        parent::__construct( $text, $modelId );
37    }
38
39    /**
40     * Decodes the JSON string.
41     *
42     * Note that this parses it without casting objects to associative arrays.
43     * Objects and arrays are kept as distinguishable types in the PHP values.
44     *
45     * @return Status
46     */
47    public function getData() {
48        $this->jsonParse ??= FormatJson::parse( $this->getText() );
49        return $this->jsonParse;
50    }
51
52    /**
53     * @return bool Whether content is valid.
54     */
55    public function isValid() {
56        return $this->getData()->isGood();
57    }
58
59    /**
60     * Pretty-print JSON.
61     *
62     * If called before validation, it may return JSON "null".
63     *
64     * @return string
65     */
66    public function beautifyJSON() {
67        return FormatJson::encode( $this->getData()->getValue(), "\t", FormatJson::UTF8_OK );
68    }
69
70    /**
71     * Construct HTML table representation of any JSON value.
72     *
73     * See also valueCell, which is similar.
74     *
75     * @param mixed $val
76     * @return string HTML.
77     */
78    public function rootValueTable( $val ) {
79        if ( is_object( $val ) ) {
80            return $this->objectTable( $val );
81        }
82
83        if ( is_array( $val ) ) {
84            // Wrap arrays in another array so that they're visually boxed in a container.
85            // Otherwise they are visually indistinguishable from a single value.
86            return $this->arrayTable( [ $val ] );
87        }
88
89        return Html::rawElement( 'table', [ 'class' => 'mw-json mw-json-single-value' ],
90            Html::rawElement( 'tbody', [],
91                Html::rawElement( 'tr', [],
92                    Html::element( 'td', [], $this->primitiveValue( $val ) )
93                )
94            )
95        );
96    }
97
98    /**
99     * Create HTML table representing a JSON object.
100     *
101     * @param stdClass $mapping
102     * @return string HTML
103     */
104    protected function objectTable( $mapping ) {
105        $rows = [];
106        $empty = true;
107
108        foreach ( $mapping as $key => $val ) {
109            $rows[] = $this->objectRow( $key, $val );
110            $empty = false;
111        }
112        if ( $empty ) {
113            $rows[] = Html::rawElement( 'tr', [],
114                Html::element( 'td', [ 'class' => 'mw-json-empty' ],
115                    wfMessage( 'content-json-empty-object' )->text()
116                )
117            );
118        }
119        return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
120            Html::rawElement( 'tbody', [], implode( '', $rows ) )
121        );
122    }
123
124    /**
125     * Create HTML table row representing one object property.
126     *
127     * @param string $key
128     * @param mixed $val
129     * @return string HTML.
130     */
131    protected function objectRow( $key, $val ) {
132        $thContent = Html::element( 'span', [], $key );
133        $th = Html::rawElement( 'th', [], $thContent );
134        $td = $this->valueCell( $val );
135        return Html::rawElement( 'tr', [], $th . $td );
136    }
137
138    /**
139     * Create HTML table representing a JSON array.
140     *
141     * @param array $mapping
142     * @return string HTML
143     */
144    protected function arrayTable( $mapping ) {
145        $rows = [];
146        $empty = true;
147
148        foreach ( $mapping as $val ) {
149            $rows[] = $this->arrayRow( $val );
150            $empty = false;
151        }
152        if ( $empty ) {
153            $rows[] = Html::rawElement( 'tr', [],
154                Html::element( 'td', [ 'class' => 'mw-json-empty' ],
155                    wfMessage( 'content-json-empty-array' )->text()
156                )
157            );
158        }
159        return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
160            Html::rawElement( 'tbody', [], implode( "\n", $rows ) )
161        );
162    }
163
164    /**
165     * Create HTML table row representing the value in an array.
166     *
167     * @param mixed $val
168     * @return string HTML.
169     */
170    protected function arrayRow( $val ) {
171        $td = $this->valueCell( $val );
172        return Html::rawElement( 'tr', [], $td );
173    }
174
175    /**
176     * Construct HTML table cell representing any JSON value.
177     *
178     * @param mixed $val
179     * @return string HTML.
180     */
181    protected function valueCell( $val ) {
182        if ( is_object( $val ) ) {
183            return Html::rawElement( 'td', [], $this->objectTable( $val ) );
184        }
185
186        if ( is_array( $val ) ) {
187            return Html::rawElement( 'td', [], $this->arrayTable( $val ) );
188        }
189
190        return Html::element( 'td', [ 'class' => 'mw-json-value' ], $this->primitiveValue( $val ) );
191    }
192
193    /**
194     * Construct text representing a JSON primitive value.
195     *
196     * @param mixed $val
197     * @return string Text.
198     */
199    protected function primitiveValue( $val ) {
200        if ( is_string( $val ) ) {
201            // Don't FormatJson::encode for strings since we want quotes
202            // and new lines to render visually instead of escaped.
203            return '"' . $val . '"';
204        }
205        return FormatJson::encode( $val );
206    }
207}