renderHeader(); return '' . "\n" . $this->renderTable($this->table); } /** * Computes the exception output and returns the string/binary * * @return string */ function renderException() { $this->renderHeader(); $exceptionMessage = $this->getExceptionMessage(); $return = '' . "\n" . "\n" . "\t\n" . ""; return $return; } /** * Converts the given data table to an array * * @param DataTable|DataTable/Map $table data table to convert * @return array */ protected function getArrayFromDataTable($table) { if (is_array($table)) { return $table; } $renderer = new Php(); $renderer->setRenderSubTables($this->isRenderSubtables()); $renderer->setSerialize(false); $renderer->setTable($table); $renderer->setHideIdSubDatableFromResponse($this->hideIdSubDatatable); return $renderer->flatRender(); } /** * Computes the output for the given data table * * @param DataTable|DataTable/Map $table * @param bool $returnOnlyDataTableXml * @param string $prefixLines * @return array|string * @throws Exception */ protected function renderTable($table, $returnOnlyDataTableXml = false, $prefixLines = '') { $array = $this->getArrayFromDataTable($table); if ($table instanceof Map) { $out = $this->renderDataTableMap($table, $array, $prefixLines); if ($returnOnlyDataTableXml) { return $out; } $out = "\n$out"; return $out; } // integer value of ZERO is a value we want to display if ($array != 0 && empty($array)) { if ($returnOnlyDataTableXml) { throw new Exception("Illegal state, what xml shall we return?"); } $out = ""; return $out; } if ($table instanceof Simple) { if (is_array($array)) { $out = $this->renderDataTableSimple($array); } else { $out = $array; } if ($returnOnlyDataTableXml) { return $out; } if (is_array($array)) { $out = "\n" . $out . ""; } else { $value = self::formatValueXml($out); if ($value === '') { $out = ""; } else { $out = "" . $value . ""; } } return $out; } if ($table instanceof DataTable) { $out = $this->renderDataTable($array); if ($returnOnlyDataTableXml) { return $out; } $out = "\n$out"; return $out; } if (is_array($array)) { $out = $this->renderArray($array, $prefixLines . "\t"); if ($returnOnlyDataTableXml) { return $out; } return "\n$out"; } } /** * Renders an array as XML. * * @param array $array The array to render. * @param string $prefixLines The string to prefix each line in the output. * @return string */ private function renderArray($array, $prefixLines) { $isAssociativeArray = Piwik::isAssociativeArray($array); // check if array contains arrays, and if not wrap the result in an extra element // (only check if this is the root renderArray call) // NOTE: this is for backwards compatibility. before, array's were added to a new DataTable. // if the array had arrays, they were added as multiple rows, otherwise it was treated as // one row. removing will change API output. $wrapInRow = $prefixLines === "\t" && self::shouldWrapArrayBeforeRendering($array, $wrapSingleValues = false, $isAssociativeArray); // render the array $result = ""; if ($wrapInRow) { $result .= "$prefixLines\n"; $prefixLines .= "\t"; } foreach ($array as $key => $value) { // based on the type of array & the key, determine how this node will look if ($isAssociativeArray) { $keyIsInvalidXmlElement = is_numeric($key) || is_numeric($key[0]); if ($keyIsInvalidXmlElement) { $prefix = ""; $suffix = ""; $emptyNode = ""; } else if (strpos($key, '=') !== false) { list($keyAttributeName, $key) = explode('=', $key, 2); $prefix = ""; $suffix = ""; $emptyNode = ""; } else { $prefix = "<$key>"; $suffix = ""; $emptyNode = "<$key />"; } } else { $prefix = ""; $suffix = ""; $emptyNode = ""; } // render the array item if (is_array($value)) { $result .= $prefixLines . $prefix . "\n"; $result .= $this->renderArray($value, $prefixLines . "\t"); $result .= $prefixLines . $suffix . "\n"; } else if ($value instanceof DataTable || $value instanceof Map ) { if ($value->getRowsCount() == 0) { $result .= $prefixLines . $emptyNode . "\n"; } else { $result .= $prefixLines . $prefix . "\n"; if ($value instanceof Map) { $result .= $this->renderDataTableMap($value, $this->getArrayFromDataTable($value), $prefixLines); } else if ($value instanceof Simple) { $result .= $this->renderDataTableSimple($this->getArrayFromDataTable($value), $prefixLines); } else { $result .= $this->renderDataTable($this->getArrayFromDataTable($value), $prefixLines); } $result .= $prefixLines . $suffix . "\n"; } } else { $xmlValue = self::formatValueXml($value); if (strlen($xmlValue) != 0) { $result .= $prefixLines . $prefix . $xmlValue . $suffix . "\n"; } else { $result .= $prefixLines . $emptyNode . "\n"; } } } if ($wrapInRow) { $result .= substr($prefixLines, 0, strlen($prefixLines) - 1) . "\n"; } return $result; } /** * Computes the output for the given data table array * * @param Map $table * @param array $array * @param string $prefixLines * @return string */ protected function renderDataTableMap($table, $array, $prefixLines = "") { // CASE 1 //array // 'day1' => string '14' (length=2) // 'day2' => string '6' (length=1) $firstTable = current($array); if (!is_array($firstTable)) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $valueAttribute => $value) { if (empty($value)) { $xml .= $prefixLines . "\t\n"; } elseif ($value instanceof Map) { $out = $this->renderTable($value, true); //TODO somehow this code is not tested, cover this case $xml .= "\t\n$out\n"; } else { $xml .= $prefixLines . "\t" . self::formatValueXml($value) . "\n"; } } return $xml; } $subTables = $table->getDataTables(); $firstTable = current($subTables); // CASE 2 //array // 'day1' => // array // 'nb_uniq_visitors' => string '18' // 'nb_visits' => string '101' // 'day2' => // array // 'nb_uniq_visitors' => string '28' // 'nb_visits' => string '11' if ($firstTable instanceof Simple) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $valueAttribute => $dataTableSimple) { if (count($dataTableSimple) == 0) { $xml .= $prefixLines . "\t\n"; } else { if (is_array($dataTableSimple)) { $dataTableSimple = "\n" . $this->renderDataTableSimple($dataTableSimple, $prefixLines . "\t") . $prefixLines . "\t"; } $xml .= $prefixLines . "\t" . $dataTableSimple . "\n"; } } return $xml; } // CASE 3 //array // 'day1' => // array // 0 => // array // 'label' => string 'phpmyvisites' // 'nb_uniq_visitors' => int 11 // 'nb_visits' => int 13 // 1 => // array // 'label' => string 'phpmyvisits' // 'nb_uniq_visitors' => int 2 // 'nb_visits' => int 2 // 'day2' => // array // 0 => // array // 'label' => string 'piwik' // 'nb_uniq_visitors' => int 121 // 'nb_visits' => int 130 // 1 => // array // 'label' => string 'piwik bis' // 'nb_uniq_visitors' => int 20 // 'nb_visits' => int 120 if ($firstTable instanceof DataTable) { $xml = ''; $nameDescriptionAttribute = $table->getKeyName(); foreach ($array as $keyName => $arrayForSingleDate) { $dataTableOut = $this->renderDataTable($arrayForSingleDate, $prefixLines . "\t"); if (empty($dataTableOut)) { $xml .= $prefixLines . "\t\n"; } else { $xml .= $prefixLines . "\t\n"; $xml .= $dataTableOut; $xml .= $prefixLines . "\t\n"; } } return $xml; } if ($firstTable instanceof Map) { $xml = ''; $tables = $table->getDataTables(); $nameDescriptionAttribute = $table->getKeyName(); foreach ($tables as $valueAttribute => $tableInArray) { $out = $this->renderTable($tableInArray, true, $prefixLines . "\t"); $xml .= $prefixLines . "\t\n" . $out . $prefixLines . "\t\n"; } return $xml; } return ''; } /** * Computes the output for the given data array * * @param array $array * @param string $prefixLine * @return string */ protected function renderDataTable($array, $prefixLine = "") { $out = ''; foreach ($array as $rowId => $row) { if (!is_array($row)) { $value = self::formatValueXml($row); if (strlen($value) == 0) { $out .= $prefixLine . "\t\t<$rowId />\n"; } else { $out .= $prefixLine . "\t\t<$rowId>" . $value . "\n"; } continue; } // Handing case idgoal=7, creating a new array for that one $rowAttribute = ''; if (strstr($rowId, '=') !== false) { $rowAttribute = explode('=', $rowId); $rowAttribute = " " . $rowAttribute[0] . "='" . $rowAttribute[1] . "'"; } $out .= $prefixLine . "\t"; if (count($row) === 1 && key($row) === 0 ) { $value = self::formatValueXml(current($row)); $out .= $prefixLine . $value; } else { $out .= "\n"; foreach ($row as $name => $value) { // handle the recursive dataTable case by XML outputting the recursive table if (is_array($value)) { $value = "\n" . $this->renderDataTable($value, $prefixLine . "\t\t"); $value .= $prefixLine . "\t\t"; } else { $value = self::formatValueXml($value); } if (strlen($value) == 0) { $out .= $prefixLine . "\t\t<$name />\n"; } else { $out .= $prefixLine . "\t\t<$name>" . $value . "\n"; } } $out .= "\t"; } $out .= $prefixLine . "\n"; } return $out; } /** * Computes the output for the given data array (representing a simple data table) * * @param $array * @param string $prefixLine * @return string */ protected function renderDataTableSimple($array, $prefixLine = "") { if (!is_array($array)) { $array = array('value' => $array); } $out = ''; foreach ($array as $keyName => $value) { $xmlValue = self::formatValueXml($value); if (strlen($xmlValue) == 0) { $out .= $prefixLine . "\t<$keyName />\n"; } else { $out .= $prefixLine . "\t<$keyName>" . $xmlValue . "\n"; } } 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'); } }