Completed
Branch master (939199)
by
unknown
39:35
created

includes/content/JsonContent.php (2 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * JSON Content Model
4
 *
5
 * @file
6
 *
7
 * @author Ori Livneh <[email protected]>
8
 * @author Kunal Mehta <[email protected]>
9
 */
10
11
/**
12
 * Represents the content of a JSON content.
13
 * @since 1.24
14
 */
15
class JsonContent extends TextContent {
16
17
	/**
18
	 * @since 1.25
19
	 * @var Status
20
	 */
21
	protected $jsonParse;
22
23
	/**
24
	 * @param string $text JSON
25
	 */
26
	public function __construct( $text, $modelId = CONTENT_MODEL_JSON ) {
27
		parent::__construct( $text, $modelId );
28
	}
29
30
	/**
31
	 * Decodes the JSON into a PHP associative array.
32
	 *
33
	 * @deprecated since 1.25 Use getData instead.
34
	 * @return array|null
35
	 */
36
	public function getJsonData() {
37
		wfDeprecated( __METHOD__, '1.25' );
38
		return FormatJson::decode( $this->getNativeData(), true );
39
	}
40
41
	/**
42
	 * Decodes the JSON string.
43
	 *
44
	 * Note that this parses it without casting objects to associative arrays.
45
	 * Objects and arrays are kept as distinguishable types in the PHP values.
46
	 *
47
	 * @return Status
48
	 */
49
	public function getData() {
50
		if ( $this->jsonParse === null ) {
51
			$this->jsonParse = FormatJson::parse( $this->getNativeData() );
52
		}
53
		return $this->jsonParse;
54
	}
55
56
	/**
57
	 * @return bool Whether content is valid.
58
	 */
59
	public function isValid() {
60
		return $this->getData()->isGood();
61
	}
62
63
	/**
64
	 * Pretty-print JSON.
65
	 *
66
	 * If called before validation, it may return JSON "null".
67
	 *
68
	 * @return string
69
	 */
70
	public function beautifyJSON() {
71
		return FormatJson::encode( $this->getData()->getValue(), true, FormatJson::UTF8_OK );
72
	}
73
74
	/**
75
	 * Beautifies JSON prior to save.
76
	 *
77
	 * @param Title $title Title
78
	 * @param User $user User
79
	 * @param ParserOptions $popts
80
	 * @return JsonContent
81
	 */
82
	public function preSaveTransform( Title $title, User $user, ParserOptions $popts ) {
83
		// FIXME: WikiPage::doEditContent invokes PST before validation. As such, native data
84
		// may be invalid (though PST result is discarded later in that case).
85
		if ( !$this->isValid() ) {
86
			return $this;
87
		}
88
89
		return new static( self::normalizeLineEndings( $this->beautifyJSON() ) );
90
	}
91
92
	/**
93
	 * Set the HTML and add the appropriate styles.
94
	 *
95
	 * @param Title $title
96
	 * @param int $revId
97
	 * @param ParserOptions $options
98
	 * @param bool $generateHtml
99
	 * @param ParserOutput $output
100
	 */
101
	protected function fillParserOutput( Title $title, $revId,
102
		ParserOptions $options, $generateHtml, ParserOutput &$output
103
	) {
104
		// FIXME: WikiPage::doEditContent generates parser output before validation.
105
		// As such, native data may be invalid (though output is discarded later in that case).
106
		if ( $generateHtml && $this->isValid() ) {
107
			$output->setText( $this->rootValueTable( $this->getData()->getValue() ) );
108
			$output->addModuleStyles( 'mediawiki.content.json' );
109
		} else {
110
			$output->setText( '' );
111
		}
112
	}
113
114
	/**
115
	 * Construct HTML table representation of any JSON value.
116
	 *
117
	 * See also valueCell, which is similar.
118
	 *
119
	 * @param mixed $val
120
	 * @return string HTML.
121
	 */
122 View Code Duplication
	protected function rootValueTable( $val ) {
123
		if ( is_object( $val ) ) {
124
			return $this->objectTable( $val );
125
		}
126
127
		if ( is_array( $val ) ) {
128
			// Wrap arrays in another array so that they're visually boxed in a container.
129
			// Otherwise they are visually indistinguishable from a single value.
130
			return $this->arrayTable( [ $val ] );
131
		}
132
133
		return Html::rawElement( 'table', [ 'class' => 'mw-json mw-json-single-value' ],
134
			Html::rawElement( 'tbody', [],
135
				Html::rawElement( 'tr', [],
136
					Html::element( 'td', [], $this->primitiveValue( $val ) )
0 ignored issues
show
It seems like $this->primitiveValue($val) targeting JsonContent::primitiveValue() can also be of type false; however, Html::element() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
137
				)
138
			)
139
		);
140
	}
141
142
	/**
143
	 * Create HTML table representing a JSON object.
144
	 *
145
	 * @param stdClass $mapping
146
	 * @return string HTML
147
	 */
148 View Code Duplication
	protected function objectTable( $mapping ) {
149
		$rows = [];
150
		$empty = true;
151
152
		foreach ( $mapping as $key => $val ) {
153
			$rows[] = $this->objectRow( $key, $val );
154
			$empty = false;
155
		}
156
		if ( $empty ) {
157
			$rows[] = Html::rawElement( 'tr', [],
158
				Html::element( 'td', [ 'class' => 'mw-json-empty' ],
159
					wfMessage( 'content-json-empty-object' )->text()
160
				)
161
			);
162
		}
163
		return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
164
			Html::rawElement( 'tbody', [], implode( '', $rows ) )
165
		);
166
	}
167
168
	/**
169
	 * Create HTML table row representing one object property.
170
	 *
171
	 * @param string $key
172
	 * @param mixed $val
173
	 * @return string HTML.
174
	 */
175
	protected function objectRow( $key, $val ) {
176
		$th = Html::element( 'th', [], $key );
177
		$td = $this->valueCell( $val );
178
		return Html::rawElement( 'tr', [], $th . $td );
179
	}
180
181
	/**
182
	 * Create HTML table representing a JSON array.
183
	 *
184
	 * @param array $mapping
185
	 * @return string HTML
186
	 */
187 View Code Duplication
	protected function arrayTable( $mapping ) {
188
		$rows = [];
189
		$empty = true;
190
191
		foreach ( $mapping as $val ) {
192
			$rows[] = $this->arrayRow( $val );
193
			$empty = false;
194
		}
195
		if ( $empty ) {
196
			$rows[] = Html::rawElement( 'tr', [],
197
				Html::element( 'td', [ 'class' => 'mw-json-empty' ],
198
					wfMessage( 'content-json-empty-array' )->text()
199
				)
200
			);
201
		}
202
		return Html::rawElement( 'table', [ 'class' => 'mw-json' ],
203
			Html::rawElement( 'tbody', [], implode( "\n", $rows ) )
204
		);
205
	}
206
207
	/**
208
	 * Create HTML table row representing the value in an array.
209
	 *
210
	 * @param mixed $val
211
	 * @return string HTML.
212
	 */
213
	protected function arrayRow( $val ) {
214
		$td = $this->valueCell( $val );
215
		return Html::rawElement( 'tr', [], $td );
216
	}
217
218
	/**
219
	 * Construct HTML table cell representing any JSON value.
220
	 *
221
	 * @param mixed $val
222
	 * @return string HTML.
223
	 */
224 View Code Duplication
	protected function valueCell( $val ) {
225
		if ( is_object( $val ) ) {
226
			return Html::rawElement( 'td', [], $this->objectTable( $val ) );
227
		}
228
229
		if ( is_array( $val ) ) {
230
			return Html::rawElement( 'td', [], $this->arrayTable( $val ) );
231
		}
232
233
		return Html::element( 'td', [ 'class' => 'value' ], $this->primitiveValue( $val ) );
0 ignored issues
show
It seems like $this->primitiveValue($val) targeting JsonContent::primitiveValue() can also be of type false; however, Html::element() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
234
	}
235
236
	/**
237
	 * Construct text representing a JSON primitive value.
238
	 *
239
	 * @param mixed $val
240
	 * @return string Text.
241
	 */
242
	protected function primitiveValue( $val ) {
243
		if ( is_string( $val ) ) {
244
			// Don't FormatJson::encode for strings since we want quotes
245
			// and new lines to render visually instead of escaped.
246
			return '"' . $val . '"';
247
		}
248
		return FormatJson::encode( $val );
249
	}
250
}
251