1<?php 2/** 3 * Matomo - free/libre analytics platform 4 * 5 * @link https://matomo.org 6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later 7 * 8 */ 9namespace Piwik\DataTable\Renderer; 10 11use Piwik\Common; 12use Piwik\DataTable\Renderer; 13use Piwik\DataTable; 14 15/** 16 * JSON export. 17 * Works with recursive DataTable (when a row can be associated with a subDataTable). 18 * 19 */ 20class Json extends Renderer 21{ 22 /** 23 * Computes the dataTable output and returns the string/binary 24 * 25 * @return string 26 */ 27 public function render() 28 { 29 return $this->renderTable($this->table); 30 } 31 32 /** 33 * Computes the output for the given data table 34 * 35 * @param DataTable $table 36 * @return string 37 */ 38 protected function renderTable($table) 39 { 40 if (is_array($table)) { 41 $array = $table; 42 if (self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = true)) { 43 $array = array($array); 44 } 45 46 foreach ($array as $key => $tab) { 47 if ($tab instanceof DataTable\Map 48 || $tab instanceof DataTable 49 || $tab instanceof DataTable\Simple) { 50 $array[$key] = $this->convertDataTableToArray($tab); 51 52 if (!is_array($array[$key])) { 53 $array[$key] = array('value' => $array[$key]); 54 } 55 } 56 } 57 } else { 58 $array = $this->convertDataTableToArray($table); 59 } 60 61 if (!is_array($array)) { 62 $array = array('value' => $array); 63 } 64 65 // convert datatable column/metadata values 66 $this->convertDataTableColumnMetadataValues($array); 67 68 // decode all entities 69 $callback = function (&$value, $key) { 70 if (is_string($value)) { 71 $value = html_entity_decode($value, ENT_QUOTES, "UTF-8"); 72 }; 73 }; 74 array_walk_recursive($array, $callback); 75 76 // silence "Warning: json_encode(): Invalid UTF-8 sequence in argument" 77 $str = @json_encode($array); 78 79 if ($str === false 80 && json_last_error() === JSON_ERROR_UTF8 81 && $this->canMakeArrayUtf8()) { 82 $array = $this->makeArrayUtf8($array); 83 $str = json_encode($array); 84 } 85 86 return $str; 87 } 88 89 private function canMakeArrayUtf8() 90 { 91 return function_exists('mb_convert_encoding'); 92 } 93 94 private function makeArrayUtf8($array) 95 { 96 if (is_array($array)) { 97 foreach ($array as $key => $value) { 98 $array[$key] = self::makeArrayUtf8($value); 99 } 100 } elseif (is_string($array)) { 101 return mb_convert_encoding($array, 'UTF-8', 'auto'); 102 } 103 104 return $array; 105 } 106 107 public static function sendHeaderJSON() 108 { 109 Common::sendHeader('Content-Type: application/json; charset=utf-8'); 110 } 111 112 private function convertDataTableColumnMetadataValues(&$table) 113 { 114 if (empty($table)) { 115 return; 116 } 117 118 array_walk_recursive($table, function (&$value, $key) { 119 if ($value instanceof DataTable) { 120 $value = $this->convertDataTableToArray($value); 121 $this->convertDataTableColumnMetadataValues($value); 122 } 123 }); 124 } 125} 126