PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.2.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.2.0
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
visualizer / classes / Visualizer / Source / Json.php

Json.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 3.2.0, at classes/Visualizer/Source/Json.php

411 lines 11.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 // +----------------------------------------------------------------------+
4 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
5 // +----------------------------------------------------------------------+
6 // | This program is free software; you can redistribute it and/or modify |
7 // | it under the terms of the GNU General Public License, version 2, as |
8 // | published by the Free Software Foundation. |
9 // | |
10 // | This program is distributed in the hope that it will be useful, |
11 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 // | GNU General Public License for more details. |
14 // | |
15 // | You should have received a copy of the GNU General Public License |
16 // | along with this program; if not, write to the Free Software |
17 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
18 // | MA 02110-1301 USA |
19 // +----------------------------------------------------------------------+
20 // | Author: Eugene Manuilov <eugene@manuilov.org> |
21 // +----------------------------------------------------------------------+
22 /**
23 * Source manager for JSON URLs.
24 *
25 * @category Visualizer
26 * @package Source
27 *
28 * @since 1.0.0
29 */
30 class Visualizer_Source_Json extends Visualizer_Source {
31
32 /**
33 * The url to the data.
34 *
35 * @since 1.0.0
36 *
37 * @access protected
38 * @var string
39 */
40 protected $_url;
41
42 /**
43 * The root to the data.
44 *
45 * @since 1.0.0
46 *
47 * @access protected
48 * @var string
49 */
50 protected $_root;
51
52 /**
53 * The paging element.
54 *
55 * @since 1.0.0
56 *
57 * @access protected
58 * @var string
59 */
60 protected $_paging;
61
62 /**
63 * The array that contains the definition of the data.
64 *
65 * @since 1.0.0
66 *
67 * @access protected
68 * @var array
69 */
70 protected $_args;
71
72 const TAG_SEPARATOR = '>';
73 const TAG_SEPARATOR_VIEW = ' &#x27A4; ';
74
75 /**
76 * Constructor.
77 *
78 * @since 1.0.0
79 *
80 * @access public
81 * @param array $params The array that contains the definition of the data.
82 */
83 public function __construct( $params = null ) {
84 $this->_args = $params;
85 if ( isset( $this->_args['url'] ) ) {
86 $this->_url = trim( $this->_args['url'] );
87 }
88 if ( isset( $this->_args['root'] ) ) {
89 $this->_root = trim( $this->_args['root'] );
90 }
91 if ( isset( $this->_args['paging'] ) ) {
92 $this->_paging = trim( $this->_args['paging'] );
93 }
94
95 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Constructor called for params = %s', print_r( $params, true ) ), 'debug', __FILE__, __LINE__ );
96 }
97
98 /**
99 * Get the root elements for JSON-endpoint.
100 *
101 * @since ?
102 *
103 * @access public
104 */
105 public function fetchRoots() {
106 $roots = apply_filters( 'visualizer_json_get_root_elements', false, $this->_url );
107 if ( false !== $roots ) {
108 return $roots;
109 }
110 return $this->getRootElements( 'root', '', array(), $this->getJSON() );
111 }
112
113 /**
114 * Get the name of the elements that are likely to contain the paginated URLs.
115 *
116 * @since ?
117 *
118 * @access public
119 */
120 public function getPaginationElements() {
121 $pages = array();
122 $root = explode( self::TAG_SEPARATOR, $this->_root );
123 // the pagination element is usually one level above the root element we are going to use
124 // e.g. if the data is in root > results, the pagination element (let's call it "next") would be root > next and not root > results > next.
125 array_pop( $root );
126
127 // base of the next element.
128 $base = implode( self::TAG_SEPARATOR, $root );
129 // get rid of the first element as that is the faux root element indicator
130 array_shift( $root );
131
132 $array = $this->getJSON();
133 if ( is_null( $array ) ) {
134 return $pages;
135 }
136
137 $leaf = $array;
138 if ( ! empty( $root ) ) {
139 foreach ( $root as $tag ) {
140 if ( array_key_exists( $tag, $leaf ) ) {
141 $leaf = $array[ $tag ];
142 } else {
143 // if the tag does not exist, we assume it is present in the 0th element of the current array.
144 $leaf = $leaf[0][ $tag ];
145 }
146 }
147 }
148
149 $paging = array();
150 foreach ( $leaf as $key => $value ) {
151 // the paging element's value will most probably contain the url of the feed.
152 if ( is_string( $value ) && 0 === stripos( $value, $this->_url ) ) {
153 $paging[] = $key;
154 }
155 }
156
157 foreach ( array_filter( array_unique( $paging ) ) as $page ) {
158 $pages[] = $base . self::TAG_SEPARATOR . $page;
159 }
160 return $pages;
161 }
162
163 /**
164 * Parse the JSON-endpoint from the chosen root as the base.
165 *
166 * @since ?
167 *
168 * @access public
169 */
170 public function parse() {
171 $url = $this->_url;
172 $data = array();
173 $page = 1;
174
175 while ( ! is_null( $url ) && $page++ < apply_filters( 'visualizer_json_fetch_pages', 5, $this->_url ) ) {
176 $array = $this->getJSON( $url );
177 if ( is_null( $array ) ) {
178 return $data;
179 }
180
181 $root = explode( self::TAG_SEPARATOR, $this->_root );
182 // get rid of the first element as that is the faux root element indicator
183 array_shift( $root );
184 $leaf = $array;
185 foreach ( $root as $tag ) {
186 if ( array_key_exists( $tag, $leaf ) ) {
187 $leaf = $leaf[ $tag ];
188 } else {
189 // if the tag does not exist, we assume it is present in the 0th element of the current array.
190 // TODO: we may want to change this to a filter later.
191 $leaf = $leaf[0][ $tag ];
192 }
193 }
194
195 // now that we have got the final array we need to operate on, we will use this as the collection of series.
196 // lets check if the series is a flat-series e.g. https://api.exchangeratesapi.io/latest
197 // in this, all values of `$leaf` would be a string, not an array.
198 $values = array_values( $leaf );
199 if ( ! is_array( $values[0] ) ) {
200 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Looks like a flat-series = %s', print_r( $leaf, true ) ), 'debug', __FILE__, __LINE__ );
201 $inner_data = array();
202 foreach ( $leaf as $datum => $value ) {
203 $inner_data[ $datum ] = $value;
204 }
205 $data[] = $inner_data;
206 } else {
207 // we will filter out all elements of this array that have array as a value.
208 foreach ( $leaf as $datum ) {
209 $inner_data = array();
210 foreach ( $datum as $key => $value ) {
211 if ( is_array( $value ) ) {
212 continue;
213 }
214 $inner_data[ $key ] = $value;
215 }
216 // if we want to exclude entire rows on the basis of some data/key.
217 if ( apply_filters( 'visualizer_json_include_row', true, $inner_data, $this->_root, $this->_url ) ) {
218 $data[] = $inner_data;
219 }
220 }
221 }
222
223 $url = $this->getNextPage( $array );
224 }
225
226 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Parsed data endpoint %s with rooot %s is %s = ', $this->_url, $this->_root, print_r( $data, true ) ), 'debug', __FILE__, __LINE__ );
227
228 return $data;
229 }
230
231 /**
232 * Get the next page URL.
233 *
234 * @since ?
235 *
236 * @access private
237 */
238 private function getNextPage( $array ) {
239 if ( empty( $this->_paging ) ) {
240 return null;
241 }
242
243 $root = explode( self::TAG_SEPARATOR, $this->_paging );
244 // get rid of the first element as that is the faux root element indicator
245 array_shift( $root );
246 $leaf = $array;
247 foreach ( $root as $tag ) {
248 if ( array_key_exists( $tag, $leaf ) ) {
249 $leaf = $array[ $tag ];
250 } else {
251 // if the tag does not exist, we assume it is present in the 0th element of the current array.
252 // TODO: we may want to change this to a filter later.
253 $leaf = $leaf[0][ $tag ];
254 }
255 }
256
257 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Got next page as %s for paging element %s', $leaf, $this->_paging ), 'debug', __FILE__, __LINE__ );
258
259 return $leaf;
260 }
261
262 /**
263 * Get the root elements for JSON-endpoint.
264 *
265 * @since ?
266 *
267 * @access private
268 */
269 private function getRootElements( $parent, $now, $root, $array ) {
270 if ( is_null( $array ) ) {
271 return array();
272 }
273
274 foreach ( $array as $key => $value ) {
275 if ( is_array( $value ) && ! empty( $value ) ) {
276 if ( ! is_numeric( $key ) ) {
277 $now = sprintf( '%s%s%s', $parent, self::TAG_SEPARATOR, $key );
278 $root[] = $now;
279 } else {
280 $now = $parent;
281 }
282 $root = $this->getRootElements( $now, $key, $root, $value );
283 }
284 }
285 $roots = array_filter( array_unique( $root ) );
286 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Roots found for %s = ', $this->_url, print_r( $roots, true ) ), 'debug', __FILE__, __LINE__ );
287 return $roots;
288 }
289
290 /**
291 * Get the JSON for the JSON-endpoint.
292 *
293 * @since ?
294 *
295 * @access private
296 */
297 private function getJSON( $url = null ) {
298 if ( is_null( $url ) ) {
299 $url = $this->_url;
300 }
301 // allow hooks to use any other args such as method=POST.
302 $response = wp_remote_request( $url, apply_filters( 'visualizer_json_args', array( 'method' => 'GET' ), $url ) );
303 if ( is_wp_error( $response ) ) {
304 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while fetching JSON endpoint %s = ', $url, print_r( $response, true ) ), 'error', __FILE__, __LINE__ );
305 return null;
306 }
307
308 $array = json_decode( wp_remote_retrieve_body( $response ), true );
309
310 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'JSON array for the endpoint is %s = ', print_r( $array, true ) ), 'debug', __FILE__, __LINE__ );
311
312 return $array;
313 }
314
315
316 /**
317 * Fetches series information. This is fetched only through the UI and not while refreshing the chart data.
318 *
319 * @since 1.0.0
320 *
321 * @access private
322 */
323 private function _fetchSeries() {
324 $params = $this->_args;
325 $headers = array_filter( $params['header'] );
326 $types = array_filter( $params['type'] );
327 $header_row = $type_row = array();
328 if ( $headers ) {
329 foreach ( $headers as $header ) {
330 if ( ! empty( $types[ $header ] ) ) {
331 $this->_series[] = array(
332 'label' => $header,
333 'type' => $types[ $header ],
334 );
335 }
336 }
337 }
338
339 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Series found for %s = ', $this->_url, print_r( $this->_series, true ) ), 'debug', __FILE__, __LINE__ );
340
341 return true;
342 }
343
344 /**
345 * Fetches data information.
346 *
347 * @since 1.0.0
348 *
349 * @access private
350 */
351 private function _fetchData() {
352 $params = $this->_args;
353
354 $headers = wp_list_pluck( $this->_series, 'label' );
355 $data = $this->parse();
356 foreach ( $data as $line ) {
357 $data_row = array();
358 foreach ( $line as $header => $value ) {
359 if ( in_array( $header, $headers, true ) ) {
360 $data_row[] = $value;
361 }
362 }
363 $this->_data[] = $this->_normalizeData( $data_row );
364 }
365
366 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Data found for %s = ', $this->_url, print_r( $this->_data, true ) ), 'debug', __FILE__, __LINE__ );
367
368 return true;
369 }
370
371 /**
372 * Fetches information from source, parses it and builds series and data arrays.
373 *
374 * @since 1.0.0
375 *
376 * @access public
377 * @return boolean TRUE on success, otherwise FALSE.
378 */
379 public function fetch() {
380 $this->_fetchSeries();
381 $this->_fetchData();
382 return true;
383 }
384
385 /**
386 * Refresh the data for the provided series.
387 *
388 * @since ?
389 *
390 * @access public
391 */
392 public function refresh( $series ) {
393 $this->_series = $series;
394 $this->_fetchData();
395 return true;
396 }
397
398 /**
399 * Returns source name.
400 *
401 * @since 1.0.0
402 *
403 * @access public
404 * @return string The name of source.
405 */
406 public function getSourceName() {
407 return __CLASS__;
408 }
409
410 }
411