2
0
forked from Wavyzz/dolibarr
Files
dolibarr-fork/htdocs/includes/restler/Format/XmlFormat.php
jfefe b503b16f07 NEW : add restler framework
First step to build REST API into Dolibarr
2015-05-01 15:42:05 +02:00

349 lines
13 KiB
PHP

<?php
namespace Luracast\Restler\Format;
use Luracast\Restler\Data\Object;
use Luracast\Restler\RestException;
use SimpleXMLElement;
use XMLWriter;
/**
* XML Markup Format for Restler Framework
*
* @category Framework
* @package Restler
* @subpackage format
* @author R.Arul Kumaran <arul@luracast.com>
* @copyright 2010 Luracast
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link http://luracast.com/products/restler/
* @version 3.0.0rc5
*/
class XmlFormat extends Format
{
const MIME = 'application/xml';
const EXTENSION = 'xml';
// ==================================================================
//
// Properties related to reading/parsing/decoding xml
//
// ------------------------------------------------------------------
public static $importSettingsFromXml = false;
public static $parseAttributes = true;
public static $parseNamespaces = true;
public static $parseTextNodeAsProperty = true;
// ==================================================================
//
// Properties related to writing/encoding xml
//
// ------------------------------------------------------------------
public static $useTextNodeProperty = true;
public static $useNamespaces = true;
public static $cdataNames = array();
// ==================================================================
//
// Common Properties
//
// ------------------------------------------------------------------
public static $attributeNames = array();
public static $textNodeName = 'text';
public static $namespaces = array();
public static $namespacedProperties = array();
/**
* Default name for the root node.
*
* @var string $rootNodeName
*/
public static $rootName = 'response';
public static $defaultTagName = 'item';
/**
* When you decode an XML its structure is copied to the static vars
* we can use this function to echo them out and then copy paste inside
* our service methods
*
* @return string PHP source code to reproduce the configuration
*/
public static function exportCurrentSettings()
{
$s = 'XmlFormat::$rootName = "' . (self::$rootName) . "\";\n";
$s .= 'XmlFormat::$attributeNames = ' .
(var_export(self::$attributeNames, true)) . ";\n";
$s .= 'XmlFormat::$defaultTagName = "' .
self::$defaultTagName . "\";\n";
$s .= 'XmlFormat::$parseAttributes = ' .
(self::$parseAttributes ? 'true' : 'false') . ";\n";
$s .= 'XmlFormat::$parseNamespaces = ' .
(self::$parseNamespaces ? 'true' : 'false') . ";\n";
if (self::$parseNamespaces) {
$s .= 'XmlFormat::$namespaces = ' .
(var_export(self::$namespaces, true)) . ";\n";
$s .= 'XmlFormat::$namespacedProperties = ' .
(var_export(self::$namespacedProperties, true)) . ";\n";
}
return $s;
}
public function encode($data, $humanReadable = false)
{
$data = Object::toArray($data);
$xml = new XMLWriter();
$xml->openMemory();
$xml->startDocument('1.0', $this->charset);
if ($humanReadable) {
$xml->setIndent(true);
$xml->setIndentString(' ');
}
static::$useNamespaces && isset(static::$namespacedProperties[static::$rootName])
?
$xml->startElementNs(
static::$namespacedProperties[static::$rootName],
static::$rootName,
static::$namespaces[static::$namespacedProperties[static::$rootName]]
)
:
$xml->startElement(static::$rootName);
if (static::$useNamespaces) {
foreach (static::$namespaces as $prefix => $ns) {
if (isset(static::$namespacedProperties[static::$rootName])
&& static::$namespacedProperties[static::$rootName] == $prefix
)
continue;
$prefix = 'xmlns' . (empty($prefix) ? '' : ':' . $prefix);
$xml->writeAttribute($prefix, $ns);
}
}
$this->write($xml, $data, static::$rootName);
$xml->endElement();
return $xml->outputMemory();
}
public function write(XMLWriter $xml, $data, $parent)
{
$text = array();
if (is_array($data)) {
if (static::$useTextNodeProperty && isset($data[static::$textNodeName])) {
$text [] = $data[static::$textNodeName];
unset($data[static::$textNodeName]);
}
$attributes = array_flip(static::$attributeNames);
//make sure we deal with attributes first
$temp = array();
foreach ($data as $key => $value) {
if (isset($attributes[$key])) {
$temp[$key] = $data[$key];
unset($data[$key]);
}
}
$data = array_merge($temp, $data);
foreach ($data as $key => $value) {
if (is_numeric($key)) {
if (!is_array($value)) {
$text [] = $value;
continue;
}
$key = static::$defaultTagName;
}
$useNS = static::$useNamespaces
&& !empty(static::$namespacedProperties[$key])
&& false === strpos($key, ':');
if (is_array($value)) {
if ($value == array_values($value)) {
//numeric array, create siblings
foreach ($value as $v) {
$useNS
? $xml->startElementNs(
static::$namespacedProperties[$key],
$key,
null
)
: $xml->startElement($key);
$this->write($xml, $v, $key);
$xml->endElement();
}
} else {
$useNS
? $xml->startElementNs(
static::$namespacedProperties[$key],
$key,
null
)
: $xml->startElement($key);
$this->write($xml, $value, $key);
$xml->endElement();
}
continue;
} elseif (is_bool($value)) {
$value = $value ? 'true' : 'false';
}
if (isset($attributes[$key])) {
$xml->writeAttribute($useNS ? static::$namespacedProperties[$key] . ':' . $key : $key, $value);
} else {
$useNS
?
$xml->startElementNs(
static::$namespacedProperties[$key],
$key,
null
)
: $xml->startElement($key);
$this->write($xml, $value, $key);
$xml->endElement();
}
}
} else {
$text [] = (string)$data;
}
if (!empty($text)) {
if (count($text) == 1) {
in_array($parent, static::$cdataNames)
? $xml->writeCdata(implode('', $text))
: $xml->text(implode('', $text));
} else {
foreach ($text as $t) {
$xml->writeElement(static::$textNodeName, $t);
}
}
}
}
public function decode($data)
{
try {
if ($data == '') {
return array();
}
libxml_use_internal_errors(true);
$xml = simplexml_load_string($data,
"SimpleXMLElement", LIBXML_NOBLANKS | LIBXML_NOCDATA | LIBXML_COMPACT);
if (false === $xml) {
$error = libxml_get_last_error();
throw new RestException(400, 'Malformed XML. '
. trim($error->message, "\r\n") . ' at line ' . $error->line);
}
libxml_clear_errors();
if (static::$importSettingsFromXml) {
static::$attributeNames = array();
static::$namespacedProperties = array();
static::$namespaces = array();
static::$rootName = $xml->getName();
$namespaces = $xml->getNamespaces();
if (count($namespaces)) {
$p = strpos($data, $xml->getName());
if ($p && $data{$p - 1} == ':') {
$s = strpos($data, '<') + 1;
$prefix = substr($data, $s, $p - $s - 1);
static::$namespacedProperties[static::$rootName] = $prefix;
}
}
}
$data = $this->read($xml);
if (count($data) == 1 && isset($data[static::$textNodeName]))
$data = $data[static::$textNodeName];
return $data;
} catch (\RuntimeException $e) {
throw new RestException(400,
"Error decoding request. " . $e->getMessage());
}
}
public function read(SimpleXMLElement $xml, $namespaces = null)
{
$r = array();
$text = (string)$xml;
if (static::$parseAttributes) {
$attributes = $xml->attributes();
foreach ($attributes as $key => $value) {
if (static::$importSettingsFromXml
&& !in_array($key, static::$attributeNames)
) {
static::$attributeNames[] = $key;
}
$r[$key] = static::setType((string)$value);
}
}
$children = $xml->children();
foreach ($children as $key => $value) {
if (isset($r[$key])) {
if (is_array($r[$key])) {
if ($r[$key] != array_values($r[$key]))
$r[$key] = array($r[$key]);
} else {
$r[$key] = array($r[$key]);
}
$r[$key][] = $this->read($value, $namespaces);
} else {
$r[$key] = $this->read($value);
}
}
if (static::$parseNamespaces) {
if (is_null($namespaces))
$namespaces = $xml->getDocNamespaces(true);
foreach ($namespaces as $prefix => $ns) {
static::$namespaces[$prefix] = $ns;
if (static::$parseAttributes) {
$attributes = $xml->attributes($ns);
foreach ($attributes as $key => $value) {
if (isset($r[$key])) {
$key = "{$prefix}:$key";
}
if (static::$importSettingsFromXml
&& !in_array($key, static::$attributeNames)
) {
static::$namespacedProperties[$key] = $prefix;
static::$attributeNames[] = $key;
}
$r[$key] = static::setType((string)$value);
}
}
$children = $xml->children($ns);
foreach ($children as $key => $value) {
if (static::$importSettingsFromXml)
static::$namespacedProperties[$key] = $prefix;
if (isset($r[$key])) {
if (is_array($r[$key])) {
if ($r[$key] != array_values($r[$key]))
$r[$key] = array($r[$key]);
} else {
$r[$key] = array($r[$key]);
}
$r[$key][] = $this->read($value, $namespaces);
} else {
$r[$key] = $this->read($value, $namespaces);
}
}
}
}
if (empty($text) && $text !== '0') {
if (empty($r)) return null;
} else {
empty($r)
? $r = static::setType($text)
: (
static::$parseTextNodeAsProperty
? $r[static::$textNodeName] = static::setType($text)
: $r[] = static::setType($text)
);
}
return $r;
}
public static function setType($value)
{
if (empty($value) && $value !== '0')
return null;
if ($value == 'true')
return true;
if ($value == 'false')
return true;
return $value;
}
}