diff options
author | Thomas Steur <thomas.steur@googlemail.com> | 2014-08-04 00:44:33 +0400 |
---|---|---|
committer | Thomas Steur <thomas.steur@googlemail.com> | 2014-08-06 11:02:56 +0400 |
commit | 49b24c2e0cfbc1839b80d95f67f04f4420d8630f (patch) | |
tree | 5f3beef4c2fb07e5c141d358287f3a7972243a25 /core | |
parent | 6da08c8c9dfa30d6925a40fe75110c10a3e7ada9 (diff) |
refs #5896 started to move rendering code from response builder in other classes which also allows plugins to define their own render formats
Diffstat (limited to 'core')
-rw-r--r-- | core/API/ApiRenderer.php | 126 | ||||
-rw-r--r-- | core/API/ResponseBuilder.php | 336 | ||||
-rw-r--r-- | core/DataTable/Renderer.php | 46 | ||||
-rw-r--r-- | core/DataTable/Renderer/Console.php | 13 | ||||
-rw-r--r-- | core/DataTable/Renderer/Csv.php | 15 | ||||
-rw-r--r-- | core/DataTable/Renderer/Html.php | 25 | ||||
-rw-r--r-- | core/DataTable/Renderer/Json.php | 27 | ||||
-rw-r--r-- | core/DataTable/Renderer/Php.php | 22 | ||||
-rw-r--r-- | core/DataTable/Renderer/Rss.php | 21 | ||||
-rw-r--r-- | core/DataTable/Renderer/Xml.php | 31 | ||||
-rw-r--r-- | core/Piwik.php | 17 |
11 files changed, 202 insertions, 477 deletions
diff --git a/core/API/ApiRenderer.php b/core/API/ApiRenderer.php new file mode 100644 index 0000000000..ada1e3cd5d --- /dev/null +++ b/core/API/ApiRenderer.php @@ -0,0 +1,126 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + * + */ +namespace Piwik\API; + +use Piwik\Common; +use Piwik\DataTable\Renderer; +use Piwik\DataTable; +use Exception; +use Piwik\Piwik; +use Piwik\Plugin; + +/** + * API renderer + */ +abstract class ApiRenderer +{ + protected $request; + + final public function __construct($request) + { + $this->request = $request; + $this->init(); + } + + protected function init() {} + + abstract public function sendHeader(); + + public function renderSuccess($message) + { + return 'Success:' . $message; + } + + public function renderException($message, \Exception $exception) + { + return $message; + } + + public function renderScalar($scalar) + { + $dataTable = new DataTable\Simple(); + $dataTable->addRowsFromArray(array($scalar)); + return $this->renderDataTable($dataTable); + } + + public function renderDataTable($dataTable) + { + $renderer = $this->buildDataTableRenderer($dataTable); + return $renderer->render(); + } + + public function renderArray($array) + { + $renderer = $this->buildDataTableRenderer($array); + return $renderer->render(); + } + + public function renderObject($object) + { + $exception = new Exception('The API cannot handle this data structure.'); + return $this->renderException($exception->getMessage(), $exception); + } + + public function renderResource($resource) + { + $exception = new Exception('The API cannot handle this data structure.'); + return $this->renderException($exception->getMessage(), $exception); + } + + /** + * @param $dataTable + * @return Renderer + */ + protected function buildDataTableRenderer($dataTable) + { + $format = self::getFormatFromClass(get_class($this)); + $renderer = Renderer::factory($format); + $renderer->setTable($dataTable); + $renderer->setRenderSubTables(Common::getRequestVar('expanded', false, 'int', $this->request)); + $renderer->setHideIdSubDatableFromResponse(Common::getRequestVar('hideIdSubDatable', false, 'int', $this->request)); + + return $renderer; + } + + /** + * @param string $format + * @param array $request + * @return ApiRenderer + * @throws Exception + */ + public static function factory($format, $request) + { + $formatToCheck = '\\' . ucfirst(strtolower($format)); + + $rendererClassnames = Plugin\Manager::getInstance()->findMultipleComponents('Renderer', 'Piwik\\API\\ApiRenderer'); + + foreach ($rendererClassnames as $klassName) { + if (Common::stringEndsWith($klassName, $formatToCheck)) { + return new $klassName($request); + } + } + + $availableRenderers = array(); + foreach ($rendererClassnames as $rendererClassname) { + $availableRenderers[] = self::getFormatFromClass($rendererClassname); + } + + $availableRenderers = implode(', ', $availableRenderers); + @header('Content-Type: text/plain; charset=utf-8'); + throw new Exception(Piwik::translate('General_ExceptionInvalidRendererFormat', array($format, $availableRenderers))); + } + + private static function getFormatFromClass($klassname) + { + $klass = explode('\\', $klassname); + + return strtolower(end($klass)); + } + +} diff --git a/core/API/ResponseBuilder.php b/core/API/ResponseBuilder.php index 54c6cc664d..4eb0f9a1c8 100644 --- a/core/API/ResponseBuilder.php +++ b/core/API/ResponseBuilder.php @@ -13,17 +13,18 @@ use Piwik\API\DataTableManipulator\Flattener; use Piwik\API\DataTableManipulator\LabelFilter; use Piwik\API\DataTableManipulator\ReportTotalsCalculator; use Piwik\Common; -use Piwik\DataTable\Renderer\Json; use Piwik\DataTable\Renderer; -use Piwik\DataTable\Simple; +use Piwik\DataTable\DataTableInterface; use Piwik\DataTable; +use Piwik\Piwik; /** */ class ResponseBuilder { - private $request = null; private $outputFormat = null; + private $apiRenderer = null; + private $request = null; private $apiModule = false; private $apiMethod = false; @@ -34,8 +35,9 @@ class ResponseBuilder */ public function __construct($outputFormat, $request = array()) { - $this->request = $request; $this->outputFormat = $outputFormat; + $this->request = $request; + $this->apiRenderer = ApiRenderer::factory($outputFormat, $request); } /** @@ -70,61 +72,21 @@ class ResponseBuilder $this->apiModule = $apiModule; $this->apiMethod = $apiMethod; - if($this->outputFormat == 'original') { - @header('Content-Type: text/plain; charset=utf-8'); - } - return $this->renderValue($value); - } - - /** - * Returns an error $message in the requested $format - * - * @param Exception $e - * @throws Exception - * @return string - */ - public function getResponseException(Exception $e) - { - $format = strtolower($this->outputFormat); - - if ($format == 'original') { - throw $e; - } + $this->apiRenderer->sendHeader(); - try { - $renderer = Renderer::factory($format); - } catch (Exception $exceptionRenderer) { - return "Error: " . $e->getMessage() . " and: " . $exceptionRenderer->getMessage(); - } - - $e = $this->decorateExceptionWithDebugTrace($e); - - $renderer->setException($e); - - if ($format == 'php') { - $renderer->setSerialize($this->caseRendererPHPSerialize()); - } - - return $renderer->renderException(); - } - - /** - * @param $value - * @return string - */ - protected function renderValue($value) - { // when null or void is returned from the api call, we handle it as a successful operation if (!isset($value)) { - return $this->handleSuccess(); + if (ob_get_contents()) { + return null; + } + + return $this->apiRenderer->renderSuccess('ok'); } // If the returned value is an object DataTable we // apply the set of generic filters if asked in the URL // and we render the DataTable according to the format specified in the URL - if ($value instanceof DataTable - || $value instanceof DataTable\Map - ) { + if ($value instanceof DataTableInterface) { return $this->handleDataTable($value); } @@ -137,26 +99,38 @@ class ResponseBuilder return $this->handleArray($value); } - // original data structure requested, we return without process - if ($this->outputFormat == 'original') { - return $value; + if (is_object($value)) { + return $this->apiRenderer->renderObject($value); } - if (is_object($value) - || is_resource($value) - ) { - return $this->getResponseException(new Exception('The API cannot handle this data structure.')); + if (is_resource($value)) { + return $this->apiRenderer->renderResource($value); } - // bool // integer // float // serialized object - return $this->handleScalar($value); + return $this->apiRenderer->renderScalar($value); + } + + /** + * Returns an error $message in the requested $format + * + * @param Exception $e + * @throws Exception + * @return string + */ + public function getResponseException(Exception $e) + { + $e = $this->decorateExceptionWithDebugTrace($e); + $message = $this->formatExceptionMessage($e); + + $this->apiRenderer->sendHeader(); + return $this->apiRenderer->renderException($message, $e); } /** * @param Exception $e * @return Exception */ - protected function decorateExceptionWithDebugTrace(Exception $e) + private function decorateExceptionWithDebugTrace(Exception $e) { // If we are in tests, show full backtrace if (defined('PIWIK_PATH_TEST_TO_ROOT')) { @@ -165,137 +139,25 @@ class ResponseBuilder } else { $message = $e->getMessage() . "\n \n --> To temporarily debug this error further, set const PIWIK_PRINT_ERROR_BACKTRACE=true; in index.php"; } - return new Exception($message); - } - return $e; - } - /** - * Returns true if the user requested to serialize the output data (&serialize=1 in the request) - * - * @param mixed $defaultSerializeValue Default value in case the user hasn't specified a value - * @return bool - */ - protected function caseRendererPHPSerialize($defaultSerializeValue = 1) - { - $serialize = Common::getRequestVar('serialize', $defaultSerializeValue, 'int', $this->request); - if ($serialize) { - return true; - } - return false; - } - - /** - * Apply the specified renderer to the DataTable - * - * @param DataTable|array $dataTable - * @return string - */ - protected function getRenderedDataTable($dataTable) - { - $format = strtolower($this->outputFormat); - - // if asked for original dataStructure - if ($format == 'original') { - // by default "original" data is not serialized - if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) { - $dataTable = serialize($dataTable); - } - return $dataTable; - } - - $method = Common::getRequestVar('method', '', 'string', $this->request); - - $renderer = Renderer::factory($format); - $renderer->setTable($dataTable); - $renderer->setRenderSubTables(Common::getRequestVar('expanded', false, 'int', $this->request)); - $renderer->setHideIdSubDatableFromResponse(Common::getRequestVar('hideIdSubDatable', false, 'int', $this->request)); - - if ($format == 'php') { - $renderer->setSerialize($this->caseRendererPHPSerialize()); - $renderer->setPrettyDisplay(Common::getRequestVar('prettyDisplay', false, 'int', $this->request)); - } else if ($format == 'html') { - $renderer->setTableId($this->request['method']); - } else if ($format == 'csv' || $format == 'tsv') { - $renderer->setConvertToUnicode(Common::getRequestVar('convertToUnicode', true, 'int', $this->request)); - } - - // prepare translation of column names - if ($format == 'html' || $format == 'csv' || $format == 'tsv' || $format = 'rss') { - $renderer->setApiMethod($method); - $renderer->setIdSite(Common::getRequestVar('idSite', false, 'int', $this->request)); - $renderer->setTranslateColumnNames(Common::getRequestVar('translateColumnNames', false, 'int', $this->request)); + return new Exception($message); } - return $renderer->render(); + return $e; } - /** - * Returns a success $message in the requested $format - * - * @param string $message - * @return string - */ - protected function handleSuccess($message = 'ok') + private function formatExceptionMessage(Exception $exception) { - // return a success message only if no content has already been buffered, useful when APIs return raw text or html content to the browser - if (!ob_get_contents()) { - switch ($this->outputFormat) { - case 'xml': - @header("Content-Type: text/xml;charset=utf-8"); - $return = - "<?xml version=\"1.0\" encoding=\"utf-8\" ?>\n" . - "<result>\n" . - "\t<success message=\"" . $message . "\" />\n" . - "</result>"; - break; - case 'json': - @header("Content-Type: application/json"); - $return = '{"result":"success", "message":"' . $message . '"}'; - break; - case 'php': - $return = array('result' => 'success', 'message' => $message); - if ($this->caseRendererPHPSerialize()) { - $return = serialize($return); - } - break; - - case 'csv': - @header("Content-Type: application/vnd.ms-excel"); - @header("Content-Disposition: attachment; filename=piwik-report-export.csv"); - $return = "message\n" . $message; - break; - - default: - $return = 'Success:' . $message; - break; - } - return $return; + $message = $exception->getMessage(); + if (\Piwik_ShouldPrintBackTraceWithMessage()) { + $message .= "\n" . $exception->getTraceAsString(); } - } - /** - * Converts the given scalar to an data table - * - * @param mixed $scalar - * @return string - */ - protected function handleScalar($scalar) - { - $dataTable = new Simple(); - $dataTable->addRowsFromArray(array($scalar)); - return $this->getRenderedDataTable($dataTable); + return Renderer::formatValueXml($message); } - /** - * Handles the given data table - * - * @param DataTable $datatable - * @return string - */ protected function handleDataTable($datatable) { - // process request $label = $this->getLabelFromRequest($this->request); // if requested, flatten nested tables @@ -345,120 +207,28 @@ class ResponseBuilder $filter = new LabelFilter($this->apiModule, $this->apiMethod, $this->request); $datatable = $filter->filter($label, $datatable, $addLabelIndex); } - return $this->getRenderedDataTable($datatable); + + return $this->apiRenderer->renderDataTable($datatable); } - /** - * Converts the given simple array to a data table - * - * @param array $array - * @return string - */ protected function handleArray($array) { - if ($this->outputFormat == 'original') { - // we handle the serialization. Because some php array have a very special structure that - // couldn't be converted with the automatic DataTable->addRowsFromSimpleArray - // the user may want to request the original PHP data structure serialized by the API - // in case he has to setup serialize=1 in the URL - if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) { - return serialize($array); - } - return $array; - } - - $multiDimensional = $this->handleMultiDimensionalArray($array); - if ($multiDimensional !== false) { - return $multiDimensional; + $firstArray = null; + $firstKey = null; + if (!empty($array)) { + $firstArray = reset($array); + $firstKey = key($array); } - $isAssoc = ($array !== array_values($array)); + $isAssoc = !empty($firstArray) && is_numeric($firstKey) && is_array($firstArray) && !Piwik::isMultiDimensionalArray($array) && count(array_filter(array_keys($firstArray), 'is_string')); if ($isAssoc) { - $dataTable = new DataTable(); - $dataTable->addRowsFromSimpleArray($array); - - return $this->handleDataTable($dataTable); - } - - return $this->getRenderedDataTable($array); - } + // $dataTable = DataTable::makeFromSimpleArray($array); - /** - * Is this a multi dimensional array? - * Multi dim arrays are not supported by the Datatable renderer. - * We manually render these. - * - * array( - * array( - * 1, - * 2 => array( 1, - * 2 - * ) - * ), - * array( 2, - * 3 - * ) - * ); - * - * @param array $array - * @return string|bool false if it isn't a multidim array - */ - protected function handleMultiDimensionalArray($array) - { - $first = reset($array); - foreach ($array as $first) { - if (is_array($first)) { - foreach ($first as $value) { - // Yes, this is a multi dim array - if (is_array($value)) { - switch ($this->outputFormat) { - case 'json': - @header("Content-Type: application/json"); - return self::convertMultiDimensionalArrayToJson($array); - break; - - case 'php': - if ($this->caseRendererPHPSerialize($defaultSerialize = 0)) { - return serialize($array); - } - return $array; - - case 'xml': - @header("Content-Type: text/xml;charset=utf-8"); - return $this->getRenderedDataTable($array); - default: - break; - } - } - } - } + // return $this->handleDataTable($dataTable); } - return false; - } - /** - * Render a multidimensional array to Json - * Handle DataTable|Set elements in the first dimension only, following case does not work: - * array( - * array( - * DataTable, - * 2 => array( - * 1, - * 2 - * ), - * ), - * ); - * - * @param array $array can contain scalar, arrays, DataTable and Set - * @return string - */ - public static function convertMultiDimensionalArrayToJson($array) - { - $jsonRenderer = new Json(); - $jsonRenderer->setTable($array); - $renderedReport = $jsonRenderer->render(); - return $renderedReport; + return $this->apiRenderer->renderArray($array); } /** diff --git a/core/DataTable/Renderer.php b/core/DataTable/Renderer.php index 679d261f3f..ddcde91cf3 100644 --- a/core/DataTable/Renderer.php +++ b/core/DataTable/Renderer.php @@ -111,22 +111,6 @@ abstract class Renderer abstract public function render(); /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - abstract public function renderException(); - - protected function getExceptionMessage() - { - $message = $this->exception->getMessage(); - if (\Piwik_ShouldPrintBackTraceWithMessage()) { - $message .= "\n" . $this->exception->getTraceAsString(); - } - return self::renderHtmlEntities($message); - } - - /** * @see render() * @return string */ @@ -144,29 +128,14 @@ abstract class Renderer public function setTable($table) { if (!is_array($table) - && !($table instanceof DataTable) - && !($table instanceof DataTable\Map) + && !($table instanceof DataTableInterface) ) { - throw new Exception("DataTable renderers renderer accepts only DataTable and Map instances, and arrays."); + throw new Exception("DataTable renderers renderer accepts only DataTable, Simple and Map instances, and arrays."); } $this->table = $table; } /** - * Set the Exception to be rendered - * - * @param Exception $exception to be rendered - * @throws Exception - */ - public function setException($exception) - { - if (!($exception instanceof Exception)) { - throw new Exception("The exception renderer accepts only an Exception object."); - } - $this->exception = $exception; - } - - /** * @var array */ protected static $availableRenderers = array('xml', @@ -209,17 +178,6 @@ abstract class Renderer } /** - * Returns $rawData after all applicable characters have been converted to HTML entities. - * - * @param String $rawData data to be converted - * @return String - */ - protected static function renderHtmlEntities($rawData) - { - return self::formatValueXml($rawData); - } - - /** * Format a value to xml * * @param string|number|bool $value value to format diff --git a/core/DataTable/Renderer/Console.php b/core/DataTable/Renderer/Console.php index b9d8cc33a7..88161165fc 100644 --- a/core/DataTable/Renderer/Console.php +++ b/core/DataTable/Renderer/Console.php @@ -31,23 +31,10 @@ class Console extends Renderer */ public function render() { - $this->renderHeader(); return $this->renderTable($this->table); } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - public function renderException() - { - $this->renderHeader(); - $exceptionMessage = $this->getExceptionMessage(); - return 'Error: ' . $exceptionMessage; - } - - /** * Sets the prefix to be used * * @param string $str new prefix diff --git a/core/DataTable/Renderer/Csv.php b/core/DataTable/Renderer/Csv.php index cf28d3ca6c..ae7d2acbb3 100644 --- a/core/DataTable/Renderer/Csv.php +++ b/core/DataTable/Renderer/Csv.php @@ -93,18 +93,6 @@ class Csv extends Renderer } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - function renderException() - { - @header('Content-Type: text/html; charset=utf-8'); - $exceptionMessage = $this->getExceptionMessage(); - return 'Error: ' . $exceptionMessage; - } - - /** * Enables / Disables unicode converting * * @param $bool @@ -353,8 +341,7 @@ class Csv extends Renderer } // silent fail otherwise unit tests fail - @header('Content-Type: application/vnd.ms-excel'); - @header('Content-Disposition: attachment; filename="' . $fileName . '"'); + @header('Content-Disposition: attachment; filename="' . $fileName . '"', true); ProxyHttp::overrideCacheControlHeaders(); } diff --git a/core/DataTable/Renderer/Html.php b/core/DataTable/Renderer/Html.php index eb130ec05b..e193ebfdc5 100644 --- a/core/DataTable/Renderer/Html.php +++ b/core/DataTable/Renderer/Html.php @@ -29,27 +29,18 @@ class Html extends Renderer * * @param string $id */ - function setTableId($id) + public function setTableId($id) { $this->tableId = str_replace('.', '_', $id); } /** - * Output HTTP Content-Type header - */ - protected function renderHeader() - { - @header('Content-Type: text/html; charset=utf-8'); - } - - /** * Computes the dataTable output and returns the string/binary * * @return string */ - function render() + public function render() { - $this->renderHeader(); $this->tableStructure = array(); $this->allColumns = array(); $this->i = 0; @@ -58,18 +49,6 @@ class Html extends Renderer } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - function renderException() - { - $this->renderHeader(); - $exceptionMessage = $this->getExceptionMessage(); - return nl2br($exceptionMessage); - } - - /** * Computes the output for the given data table * * @param DataTable $table diff --git a/core/DataTable/Renderer/Json.php b/core/DataTable/Renderer/Json.php index ae1bf3ef05..adfd499b8f 100644 --- a/core/DataTable/Renderer/Json.php +++ b/core/DataTable/Renderer/Json.php @@ -27,28 +27,10 @@ class Json extends Renderer */ public function render() { - $this->renderHeader(); return $this->renderTable($this->table); } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - function renderException() - { - $this->renderHeader(); - - $exceptionMessage = $this->getExceptionMessage(); - $exceptionMessage = str_replace(array("\r\n", "\n"), "", $exceptionMessage); - - $result = json_encode(array('result' => 'error', 'message' => $exceptionMessage)); - - return $this->jsonpWrap($result); - } - - /** * Computes the output for the given data table * * @param DataTable $table @@ -112,15 +94,6 @@ class Json extends Renderer return $str; } - /** - * Sends the http header for json file - */ - protected function renderHeader() - { - self::sendHeaderJSON(); - ProxyHttp::overrideCacheControlHeaders(); - } - public static function sendHeaderJSON() { @header('Content-Type: application/json; charset=utf-8'); diff --git a/core/DataTable/Renderer/Php.php b/core/DataTable/Renderer/Php.php index a1475f1969..56360b939c 100644 --- a/core/DataTable/Renderer/Php.php +++ b/core/DataTable/Renderer/Php.php @@ -71,8 +71,6 @@ class Php extends Renderer */ public function render($dataTable = null) { - $this->renderHeader(); - if (is_null($dataTable)) { $dataTable = $this->table; } @@ -88,26 +86,6 @@ class Php extends Renderer } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - public function renderException() - { - $this->renderHeader(); - - $exceptionMessage = $this->getExceptionMessage(); - - $return = array('result' => 'error', 'message' => $exceptionMessage); - - if ($this->serialize) { - $return = serialize($return); - } - - return $return; - } - - /** * Produces a flat php array from the DataTable, putting "columns" and "metadata" on the same level. * * For example, when a originalRender() would be diff --git a/core/DataTable/Renderer/Rss.php b/core/DataTable/Renderer/Rss.php index 691f1f25b9..8adda661be 100644 --- a/core/DataTable/Renderer/Rss.php +++ b/core/DataTable/Renderer/Rss.php @@ -31,23 +31,10 @@ class Rss extends Renderer */ function render() { - $this->renderHeader(); return $this->renderTable($this->table); } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - function renderException() - { - header('Content-type: text/plain'); - $exceptionMessage = $this->getExceptionMessage(); - return 'Error: ' . $exceptionMessage; - } - - /** * Computes the output for the given data table * * @param DataTable $table @@ -101,14 +88,6 @@ class Rss extends Renderer } /** - * Sends the xml file http header - */ - protected function renderHeader() - { - @header('Content-Type: text/xml; charset=utf-8'); - } - - /** * Returns the RSS file footer * * @return string diff --git a/core/DataTable/Renderer/Xml.php b/core/DataTable/Renderer/Xml.php index a18e475d04..9eb7db914b 100644 --- a/core/DataTable/Renderer/Xml.php +++ b/core/DataTable/Renderer/Xml.php @@ -30,32 +30,12 @@ class Xml extends Renderer * * @return string */ - function render() + public function render() { - $this->renderHeader(); return '<?xml version="1.0" encoding="utf-8" ?>' . "\n" . $this->renderTable($this->table); } /** - * Computes the exception output and returns the string/binary - * - * @return string - */ - function renderException() - { - $this->renderHeader(); - - $exceptionMessage = $this->getExceptionMessage(); - - $return = '<?xml version="1.0" encoding="utf-8" ?>' . "\n" . - "<result>\n" . - "\t<error message=\"" . $exceptionMessage . "\" />\n" . - "</result>"; - - return $return; - } - - /** * Converts the given data table to an array * * @param DataTable|DataTable/Map $table data table to convert @@ -430,13 +410,4 @@ class Xml extends Renderer } return $out; } - - /** - * Sends the XML headers - */ - protected function renderHeader() - { - // silent fail because otherwise it throws an exception in the unit tests - @header('Content-Type: text/xml; charset=utf-8'); - } } diff --git a/core/Piwik.php b/core/Piwik.php index 4c4f6fba74..befe249923 100644 --- a/core/Piwik.php +++ b/core/Piwik.php @@ -742,6 +742,23 @@ class Piwik return false; } + public static function isMultiDimensionalArray($array) + { + $first = reset($array); + foreach ($array as $first) { + if (is_array($first)) { + foreach ($first as $value) { + // Yes, this is a multi dim array + if (is_array($value)) { + return true; + } + } + } + } + + return false; + } + /** * Returns the class name of an object without its namespace. * |