Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
JCContent
0.00% covered (danger)
0.00%
0 / 37
0.00% covered (danger)
0.00%
0 / 14
420
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 getData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getSafeData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getRawData
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getStatus
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isValid
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 isEmpty
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
 isCountable
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
6
 isValidJson
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 thorough
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 validate
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 parse
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
12
 getView
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 createDefaultView
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3namespace JsonConfig;
4
5use FormatJson;
6use MediaWiki\Status\Status;
7use stdClass;
8
9/**
10 * Represents the content of a JSON Json Config article.
11 * @file
12 * @ingroup Extensions
13 * @ingroup JsonConfig
14 *
15 * @author Yuri Astrakhan <yurik@wikimedia.org>,
16 *   based on Ori Livneh <ori@wikimedia.org> extension schema
17 */
18class JCContent extends \TextContent {
19    /** @var mixed */
20    private $rawData = null;
21    /** @var stdClass */
22    protected $data = null;
23    /** @var Status */
24    private $status;
25    /** @var bool */
26    private $thorough;
27    /** @var bool */
28    private $stripComments;
29    /** @var JCContentView|null contains an instance of the view class */
30    private $view = null;
31
32    /**
33     * @param string|null $text Json configuration. If null, default content will be inserted instead
34     * @param string $modelId
35     * @param bool $thorough True if extra validation should be performed
36     */
37    public function __construct( $text, $modelId, $thorough ) {
38        $this->stripComments = $text !== null;
39        $text ??= $this->getView( $modelId )->getDefault( $modelId );
40        parent::__construct( $text, $modelId );
41        $this->thorough = $thorough;
42        $this->status = new Status();
43        $this->parse();
44    }
45
46    /**
47     * Get validated data
48     * @return stdClass
49     */
50    public function getData() {
51        return $this->data;
52    }
53
54    /**
55     * Returns data after sanitization, suitable for third-party use
56     *
57     * @param stdClass $data
58     * @return stdClass
59     */
60    public function getSafeData( $data ) {
61        return $data;
62    }
63
64    /**
65     * Returns JSON object as resulted from parsing initial text,
66     * before any validation/modifications took place
67     * @return mixed
68     */
69    public function getRawData() {
70        return $this->rawData;
71    }
72
73    /**
74     * Get content status object
75     * @return Status
76     */
77    public function getStatus() {
78        return $this->status;
79    }
80
81    /**
82     * @return bool False if this configuration has parsing or validation errors
83     */
84    public function isValid() {
85        return $this->status->isGood();
86    }
87
88    public function isEmpty() {
89        $text = trim( $this->getNativeData() );
90        return $text === '' || $text === '{}';
91    }
92
93    /**
94     * Determines whether this content should be considered a "page" for statistics
95     * In our case, just making sure it's not empty or a redirect
96     * @param bool|null $hasLinks
97     * @return bool
98     */
99    public function isCountable( $hasLinks = null ) {
100        return !$this->isEmpty() && !$this->isRedirect();
101    }
102
103    /**
104     * Returns true if the text is in JSON format.
105     * @return bool
106     */
107    public function isValidJson() {
108        return $this->rawData !== null;
109    }
110
111    /**
112     * @return bool true if thorough validation may be needed -
113     *   e.g. rendering HTML or saving new value
114     */
115    public function thorough() {
116        return $this->thorough;
117    }
118
119    /**
120     * Override this method to perform additional data validation
121     * @param mixed $data
122     * @return stdClass
123     */
124    public function validate( $data ) {
125        return $data;
126    }
127
128    /**
129     * Perform initial json parsing and validation
130     */
131    private function parse() {
132        $rawText = $this->getNativeData();
133        $parseOpts = FormatJson::TRY_FIXING;
134        if ( $this->stripComments ) {
135            $parseOpts += FormatJson::STRIP_COMMENTS;
136        }
137        $status = FormatJson::parse( $rawText, $parseOpts );
138        if ( !$status->isOK() ) {
139            $this->status = $status;
140            return;
141        }
142        $data = $status->getValue();
143        // @fixme: HACK - need a deep clone of the data
144        // @fixme: but doing (object)(array)$data will re-encode empty [] as {}
145        // @performance: re-encoding is likely faster than stripping comments in PHP twice
146        $this->rawData = FormatJson::decode(
147            FormatJson::encode( $data, false, FormatJson::ALL_OK ), true
148        );
149        $this->data = $this->validate( $data );
150    }
151
152    /**
153     * Get a view object for this content object
154     * @internal Only public for JCContentHandler
155     *
156     * @param string $modelId is required here because parent ctor might not have ran yet
157     * @return JCContentView
158     */
159    public function getView( $modelId ) {
160        global $wgJsonConfigModels;
161        if ( !$this->view ) {
162            $configModels = \ExtensionRegistry::getInstance()->getAttribute( 'JsonConfigModels' )
163                + $wgJsonConfigModels;
164            $class = $configModels[$modelId]['view'] ?? null;
165            $this->view = $class ? new $class() : $this->createDefaultView();
166        }
167        return $this->view;
168    }
169
170    /**
171     * In case view is not associated with the model for this class, this function will instantiate
172     * a default. Override may instantiate a more appropriate view
173     * @return JCContentView
174     */
175    protected function createDefaultView() {
176        return new JCDefaultContentView();
177    }
178}