first commit

This commit is contained in:
root
2026-03-21 17:04:18 +08:00
commit 3c38481573
617 changed files with 65539 additions and 0 deletions

38
var/IXR/Base64.php Executable file
View File

@@ -0,0 +1,38 @@
<?php
namespace IXR;
/**
* IXR Base64编码
*
* @package IXR
*/
class Base64
{
/**
* 编码数据
*
* @var string
*/
private string $data;
/**
* 初始化数据
*
* @param string $data
*/
public function __construct(string $data)
{
$this->data = $data;
}
/**
* 获取XML数据
*
* @return string
*/
public function getXml()
{
return '<base64>' . base64_encode($this->data) . '</base64>';
}
}

199
var/IXR/Client.php Executable file
View File

@@ -0,0 +1,199 @@
<?php
namespace IXR;
use Typecho\Http\Client as HttpClient;
/**
* IXR客户端
* reload by typecho team(http://www.typecho.org)
*
* @package IXR
*/
class Client
{
/** 默认客户端 */
private const DEFAULT_USERAGENT = 'Typecho XML-RPC PHP Library';
/**
* 地址
*
* @var string
*/
private string $url;
/**
* 消息体
*
* @var Message
*/
private Message $message;
/**
* 调试开关
*
* @var boolean
*/
private bool $debug = false;
/**
* 请求前缀
*
* @var string|null
*/
private ?string $prefix;
/**
* @var Error
*/
private Error $error;
/**
* 客户端构造函数
*
* @param string $url 服务端地址
* @param string|null $prefix
* @return void
*/
public function __construct(
string $url,
?string $prefix = null
) {
$this->url = $url;
$this->prefix = $prefix;
}
/**
* 设置调试模式
* @deprecated
*/
public function setDebug()
{
$this->debug = true;
}
/**
* 执行请求
*
* @param string $method
* @param array $args
* @return bool
*/
private function rpcCall(string $method, array $args): bool
{
$request = new Request($method, $args);
$xml = $request->getXml();
$client = HttpClient::get();
if (!$client) {
$this->error = new Error(-32300, 'transport error - could not open socket');
return false;
}
try {
$client->setHeader('Content-Type', 'text/xml')
->setHeader('User-Agent', self::DEFAULT_USERAGENT)
->setData($xml)
->send($this->url);
} catch (HttpClient\Exception $e) {
$this->error = new Error(-32700, $e->getMessage());
return false;
}
$contents = $client->getResponseBody();
if ($this->debug) {
echo '<pre>' . htmlspecialchars($contents) . "\n</pre>\n\n";
}
// Now parse what we've got back
$this->message = new Message($contents);
if (!$this->message->parse()) {
// XML error
$this->error = new Error(-32700, 'parse error. not well formed');
return false;
}
// Is the message a fault?
if ($this->message->messageType == 'fault') {
$this->error = new Error($this->message->faultCode, $this->message->faultString);
return false;
}
// Message must be OK
return true;
}
/**
* 增加前缀
* <code>
* $rpc->metaWeblog->newPost();
* </code>
*
* @param string $prefix 前缀
* @return Client
*/
public function __get(string $prefix): Client
{
return new self($this->url, $this->prefix . $prefix . '.');
}
/**
* 增加魔术特性
* by 70
*
* @return mixed
* @throws Exception
*/
public function __call($method, $args)
{
$return = $this->rpcCall($this->prefix . $method, $args);
if ($return) {
return $this->getResponse();
} else {
throw new Exception($this->getErrorMessage(), $this->getErrorCode());
}
}
/**
* 获得返回值
*
* @return mixed
*/
public function getResponse()
{
// methodResponses can only have one param - return that
return $this->message->params[0];
}
/**
* 是否为错误
*
* @return bool
*/
public function isError(): bool
{
return isset($this->error);
}
/**
* 获取错误代码
*
* @return int
*/
private function getErrorCode(): int
{
return $this->error->code;
}
/**
* 获取错误消息
*
* @return string
*/
private function getErrorMessage(): string
{
return $this->error->message;
}
}

90
var/IXR/Date.php Executable file
View File

@@ -0,0 +1,90 @@
<?php
namespace IXR;
/**
* IXR日期
*
* @package IXR
*/
class Date
{
private string $year;
private string $month;
private string $day;
private string $hour;
private string $minute;
private string $second;
private string $timezone;
/**
* @param int|string $time
*/
public function __construct($time)
{
// $time can be a PHP timestamp or an ISO one
if (is_numeric($time)) {
$this->parseTimestamp(intval($time));
} else {
$this->parseIso($time);
}
}
/**
* @param int $timestamp
*/
private function parseTimestamp(int $timestamp)
{
$this->year = gmdate('Y', $timestamp);
$this->month = gmdate('m', $timestamp);
$this->day = gmdate('d', $timestamp);
$this->hour = gmdate('H', $timestamp);
$this->minute = gmdate('i', $timestamp);
$this->second = gmdate('s', $timestamp);
$this->timezone = '';
}
/**
* @param string $iso
*/
private function parseIso(string $iso)
{
$this->year = substr($iso, 0, 4);
$this->month = substr($iso, 4, 2);
$this->day = substr($iso, 6, 2);
$this->hour = substr($iso, 9, 2);
$this->minute = substr($iso, 12, 2);
$this->second = substr($iso, 15, 2);
$this->timezone = substr($iso, 17);
}
/**
* @return string
*/
public function getIso(): string
{
return $this->year . $this->month . $this->day . 'T' . $this->hour . ':' . $this->minute . ':' . $this->second . $this->timezone;
}
/**
* @return string
*/
public function getXml(): string
{
return '<dateTime.iso8601>' . $this->getIso() . '</dateTime.iso8601>';
}
/**
* @return false|int
*/
public function getTimestamp()
{
return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year);
}
}

67
var/IXR/Error.php Executable file
View File

@@ -0,0 +1,67 @@
<?php
namespace IXR;
/**
* IXR错误
*
* @package IXR
*/
class Error
{
/**
* 错误代码
*
* @access public
* @var integer
*/
public int $code;
/**
* 错误消息
*
* @access public
* @var string|null
*/
public ?string $message;
/**
* 构造函数
*
* @param integer $code 错误代码
* @param string $message 错误消息
*/
public function __construct(int $code, string $message)
{
$this->code = $code;
$this->message = $message;
}
/**
* 获取xml
*
* @return string
*/
public function getXml(): string
{
return <<<EOD
<methodResponse>
<fault>
<value>
<struct>
<member>
<name>faultCode</name>
<value><int>{$this->code}</int></value>
</member>
<member>
<name>faultString</name>
<value><string>{$this->message}</string></value>
</member>
</struct>
</value>
</fault>
</methodResponse>
EOD;
}
}

12
var/IXR/Exception.php Executable file
View File

@@ -0,0 +1,12 @@
<?php
namespace IXR;
/**
* IXR异常类
*
* @package IXR
*/
class Exception extends \Exception
{
}

25
var/IXR/Hook.php Executable file
View File

@@ -0,0 +1,25 @@
<?php
namespace IXR;
use ReflectionMethod;
/**
* hook rpc call
*/
interface Hook
{
/**
* @param string $methodName
* @param ReflectionMethod $reflectionMethod
* @param array $parameters
* @return mixed
*/
public function beforeRpcCall(string $methodName, ReflectionMethod $reflectionMethod, array $parameters);
/**
* @param string $methodName
* @param mixed $result
*/
public function afterRpcCall(string $methodName, &$result): void;
}

218
var/IXR/Message.php Executable file
View File

@@ -0,0 +1,218 @@
<?php
namespace IXR;
/**
* IXR消息
*
* @package IXR
*/
class Message
{
/**
* @var string
*/
public string $message;
/**
* @var string
*/
public string $messageType; // methodCall / methodResponse / fault
public int $faultCode;
public string $faultString;
/**
* @var string
*/
public string $methodName;
/**
* @var array
*/
public array $params = [];
// Current variable stacks
private array $arrayStructs = []; // The stack used to keep track of the current array/struct
private array $arrayStructsTypes = []; // Stack keeping track of if things are structs or array
private array $currentStructName = []; // A stack as well
private string $currentTagContents = '';
/**
* @param string $message
*/
public function __construct(string $message)
{
$this->message = $message;
}
/**
* @return bool
*/
public function parse(): bool
{
// first remove the XML declaration
$this->message = preg_replace('/<\?xml(.*)?\?' . '>/', '', $this->message);
if (trim($this->message) == '') {
return false;
}
// remove the DOCTYPE, avoid using a regexp, so we can save memory
$count = 0;
while (true) {
// Fail if there is an endless loop
if ($count >= 10) {
return false;
}
$pos = strpos($this->message, '<!DOCTYPE');
if ($pos !== false) {
$this->message = substr($this->message, 0, $pos)
. substr($this->message, strpos($this->message, '>', $pos) + 1);
$count ++;
} else {
break;
}
}
$parser = xml_parser_create();
// Set XML parser to take the case of tags in to account
xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, false);
// Set XML parser callback functions
xml_set_object($parser, $this);
xml_set_element_handler($parser, [$this, 'tagOpen'], [$this, 'tagClose']);
xml_set_character_data_handler($parser, [$this, 'cdata']);
if (!xml_parse($parser, $this->message)) {
/* die(sprintf('XML error: %s at line %d',
xml_error_string(xml_get_error_code($this->parser)),
xml_get_current_line_number($this->parser))); */
return false;
}
xml_parser_free($parser);
// Grab the error messages, if any
if ($this->messageType == 'fault') {
$this->faultCode = intval($this->params[0]['faultCode']);
$this->faultString = $this->params[0]['faultString'];
}
return true;
}
/**
* @param $parser
* @param string $tag
* @param $attr
*/
private function tagOpen($parser, string $tag, $attr)
{
switch ($tag) {
case 'methodCall':
case 'methodResponse':
case 'fault':
$this->messageType = $tag;
break;
/* Deal with stacks of arrays and structs */
case 'data': // data is to all intents and puposes more interesting than array
$this->arrayStructsTypes[] = 'array';
$this->arrayStructs[] = [];
break;
case 'struct':
$this->arrayStructsTypes[] = 'struct';
$this->arrayStructs[] = [];
break;
}
}
/**
* @param $parser
* @param string $cdata
*/
private function cdata($parser, string $cdata)
{
$this->currentTagContents .= $cdata;
}
/**
* @param $parser
* @param string $tag
*/
private function tagClose($parser, string $tag)
{
switch ($tag) {
case 'int':
case 'i4':
$value = (int) trim($this->currentTagContents);
$this->currentTagContents = '';
break;
case 'double':
$value = (double) trim($this->currentTagContents);
$this->currentTagContents = '';
break;
case 'string':
$value = trim($this->currentTagContents);
$this->currentTagContents = '';
break;
case 'dateTime.iso8601':
$value = new Date(trim($this->currentTagContents));
// $value = $iso->getTimestamp();
$this->currentTagContents = '';
break;
case 'value':
// "If no type is indicated, the type is string."
if (trim($this->currentTagContents) != '') {
$value = $this->currentTagContents;
$this->currentTagContents = '';
}
break;
case 'boolean':
$value = (bool) trim($this->currentTagContents);
$this->currentTagContents = '';
break;
case 'base64':
$value = base64_decode($this->currentTagContents);
$this->currentTagContents = '';
break;
/* Deal with stacks of arrays and structs */
case 'data':
case 'struct':
$value = array_pop($this->arrayStructs);
array_pop($this->arrayStructsTypes);
break;
case 'member':
array_pop($this->currentStructName);
break;
case 'name':
$this->currentStructName[] = trim($this->currentTagContents);
$this->currentTagContents = '';
break;
case 'methodName':
$this->methodName = trim($this->currentTagContents);
$this->currentTagContents = '';
break;
}
if (isset($value)) {
/*
if (!is_array($value) && !is_object($value)) {
$value = trim($value);
}
*/
if (count($this->arrayStructs) > 0) {
// Add value to struct or array
if ($this->arrayStructsTypes[count($this->arrayStructsTypes) - 1] == 'struct') {
// Add to struct
$this->arrayStructs[count($this->arrayStructs) - 1]
[$this->currentStructName[count($this->currentStructName) - 1]] = $value;
} else {
// Add to array
$this->arrayStructs[count($this->arrayStructs) - 1][] = $value;
}
} else {
// Just add as a paramater
$this->params[] = $value;
}
}
}
}

120
var/IXR/Pingback.php Executable file
View File

@@ -0,0 +1,120 @@
<?php
namespace IXR;
use Typecho\Common;
use Typecho\Http\Client as HttpClient;
use Typecho\Http\Client\Exception as HttpException;
/**
* fetch pingback
*/
class Pingback
{
/**
* @var string
*/
private string $html;
/**
* @var string
*/
private string $target;
/**
* @param string $url
* @param string $target
* @throws Exception
*/
public function __construct(string $url, string $target)
{
$client = HttpClient::get();
$this->target = $target;
if (!isset($client)) {
throw new Exception('No available http client', 50);
}
try {
$client->setTimeout(5)
->send($url);
} catch (HttpException $e) {
throw new Exception('Pingback http error', 50);
}
if ($client->getResponseStatus() != 200) {
throw new Exception('Pingback wrong http status', 50);
}
$response = $client->getResponseBody();
$encoding = 'UTF-8';
$contentType = $client->getResponseHeader('Content-Type');
if (!empty($contentType) && preg_match("/charset=([_a-z0-9-]+)/i", $contentType, $matches)) {
$encoding = strtoupper($matches[1]);
} elseif (preg_match("/<meta\s+charset=\"([_a-z0-9-]+)\"/i", $response, $matches)) {
$encoding = strtoupper($matches[1]);
}
$this->html = $encoding == 'UTF-8' ? $response : mb_convert_encoding($response, 'UTF-8', $encoding);
if (
!$client->getResponseHeader('X-Pingback') &&
!preg_match_all("/<link[^>]*rel=[\"']pingback[\"'][^>]+href=[\"']([^\"']*)[\"'][^>]*>/i", $this->html)
) {
throw new Exception("Source server doesn't support pingback", 50);
}
}
/**
* get title
*
* @return string
*/
public function getTitle(): string
{
if (preg_match("/<title>([^<]*?)<\/title>/is", $this->html, $matchTitle)) {
return Common::subStr(Common::removeXSS(trim(strip_tags($matchTitle[1]))), 0, 150, '...');
}
return parse_url($this->target, PHP_URL_HOST);
}
/**
* get content
*
* @return string
* @throws Exception
*/
public function getContent(): string
{
/** 干掉html tag只留下<a>*/
$text = Common::stripTags($this->html, '<a href="">');
/** 此处将$target quote,留着后面用*/
$pregLink = preg_quote($this->target);
/** 找出含有target链接的最长的一行作为$finalText*/
$finalText = null;
$lines = explode("\n", $text);
foreach ($lines as $line) {
$line = trim($line);
if (null != $line) {
if (preg_match("|<a[^>]*href=[\"']{$pregLink}[\"'][^>]*>(.*?)</a>|", $line)) {
if (strlen($line) > strlen($finalText)) {
/** <a>也要干掉,*/
$finalText = Common::stripTags($line);
break;
}
}
}
}
if (!isset($finalText)) {
throw new Exception("Source page doesn't have target url", 50);
}
return '[...]' . Common::subStr($finalText, 0, 200, '') . '[...]';
}
}

55
var/IXR/Request.php Executable file
View File

@@ -0,0 +1,55 @@
<?php
namespace IXR;
/**
* IXR请求体
*
* @package IXR
*/
class Request
{
/**
* @var string
*/
private string $xml;
/**
* @param string $method
* @param array $args
*/
public function __construct(string $method, array $args)
{
$this->xml = <<<EOD
<?xml version="1.0"?>
<methodCall>
<methodName>{$method}</methodName>
<params>
EOD;
foreach ($args as $arg) {
$this->xml .= '<param><value>';
$v = new Value($arg);
$this->xml .= $v->getXml();
$this->xml .= "</value></param>\n";
}
$this->xml .= '</params></methodCall>';
}
/**
* @return int
*/
public function getLength(): int
{
return strlen($this->xml);
}
/**
* @return string
*/
public function getXml(): string
{
return $this->xml;
}
}

337
var/IXR/Server.php Executable file
View File

@@ -0,0 +1,337 @@
<?php
namespace IXR;
use Typecho\Widget\Exception as WidgetException;
/**
* IXR服务器
*
* @package IXR
*/
class Server
{
/**
* 回调函数
*
* @var array
*/
private array $callbacks;
/**
* 默认参数
*
* @var array
*/
private array $capabilities;
/**
* @var Hook
*/
private Hook $hook;
/**
* 构造函数
*
* @param array $callbacks 回调函数
*/
public function __construct(array $callbacks = [])
{
$this->setCapabilities();
$this->callbacks = $callbacks;
$this->setCallbacks();
}
/**
* 获取默认参数
*
* @access public
* @return array
*/
public function getCapabilities(): array
{
return $this->capabilities;
}
/**
* 列出所有方法
*
* @access public
* @return array
*/
public function listMethods(): array
{
// Returns a list of methods - uses array_reverse to ensure user defined
// methods are listed before server defined methods
return array_reverse(array_keys($this->callbacks));
}
/**
* 一次处理多个请求
*
* @param array $methodcalls
* @return array
*/
public function multiCall(array $methodcalls): array
{
// See http://www.xmlrpc.com/discuss/msgReader$1208
$return = [];
foreach ($methodcalls as $call) {
$method = $call['methodName'];
$params = $call['params'];
if ($method == 'system.multicall') {
$result = new Error(-32600, 'Recursive calls to system.multicall are forbidden');
} else {
$result = $this->call($method, $params);
}
if (is_a($result, 'Error')) {
$return[] = [
'faultCode' => $result->code,
'faultString' => $result->message
];
} else {
$return[] = [$result];
}
}
return $return;
}
/**
* @param string $methodName
* @return string|Error
*/
public function methodHelp(string $methodName)
{
if (!$this->hasMethod($methodName)) {
return new Error(-32601, 'server error. requested method ' . $methodName . ' does not exist.');
}
[$object, $method] = $this->callbacks[$methodName];
try {
$ref = new \ReflectionMethod($object, $method);
$doc = $ref->getDocComment();
return $doc ?: '';
} catch (\ReflectionException $e) {
return '';
}
}
/**
* @param Hook $hook
*/
public function setHook(Hook $hook)
{
$this->hook = $hook;
}
/**
* 呼叫内部方法
*
* @param string $methodName 方法名
* @param array $args 参数
* @return mixed
*/
private function call(string $methodName, array $args)
{
if (!$this->hasMethod($methodName)) {
return new Error(-32601, 'server error. requested method ' . $methodName . ' does not exist.');
}
$method = $this->callbacks[$methodName];
if (!is_callable($method)) {
return new Error(
-32601,
'server error. requested class method "' . $methodName . '" does not exist.'
);
}
[$object, $objectMethod] = $method;
try {
$ref = new \ReflectionMethod($object, $objectMethod);
$requiredArgs = $ref->getNumberOfRequiredParameters();
if (count($args) < $requiredArgs) {
return new Error(
-32602,
'server error. requested class method "' . $methodName . '" require ' . $requiredArgs . ' params.'
);
}
foreach ($ref->getParameters() as $key => $parameter) {
if ($parameter->hasType() && !settype($args[$key], $parameter->getType()->getName())) {
return new Error(
-32602,
'server error. requested class method "'
. $methodName . '" ' . $key . ' param has wrong type.'
);
}
}
if (isset($this->hook)) {
$result = $this->hook->beforeRpcCall($methodName, $ref, $args);
if (isset($result)) {
return $result;
}
}
$result = call_user_func_array($method, $args);
if (isset($this->hook)) {
$this->hook->afterRpcCall($methodName, $result);
}
return $result;
} catch (\ReflectionException $e) {
return new Error(
-32601,
'server error. requested class method "' . $methodName . '" does not exist.'
);
} catch (Exception $e) {
return new Error(
$e->getCode(),
$e->getMessage()
);
} catch (WidgetException $e) {
return new Error(
-32001,
$e->getMessage()
);
} catch (\Exception $e) {
return new Error(
-32001,
'server error. requested class method "' . $methodName . '" failed.'
);
}
}
/**
* 抛出错误
*
* @access private
* @param integer|Error $error 错误代码
* @param string|null $message 错误消息
* @return void
*/
private function error($error, ?string $message = null)
{
// Accepts either an error object or an error code and message
if (!$error instanceof Error) {
$error = new Error($error, $message);
}
$this->output($error->getXml());
}
/**
* 输出xml
*
* @access private
* @param string $xml 输出xml
*/
private function output(string $xml)
{
$xml = '<?xml version="1.0"?>' . "\n" . $xml;
$length = strlen($xml);
header('Connection: close');
header('Content-Length: ' . $length);
header('Content-Type: text/xml');
header('Date: ' . date('r'));
echo $xml;
exit;
}
/**
* 是否存在方法
*
* @access private
* @param string $method 方法名
* @return bool
*/
private function hasMethod(string $method): bool
{
return in_array($method, array_keys($this->callbacks));
}
/**
* 设置默认参数
*
* @access public
* @return void
*/
private function setCapabilities()
{
// Initialises capabilities array
$this->capabilities = [
'xmlrpc' => [
'specUrl' => 'http://www.xmlrpc.com/spec',
'specVersion' => 1
],
'faults_interop' => [
'specUrl' => 'http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php',
'specVersion' => 20010516
],
'system.multicall' => [
'specUrl' => 'http://www.xmlrpc.com/discuss/msgReader$1208',
'specVersion' => 1
],
];
}
/**
* 设置默认方法
*
* @access private
* @return void
*/
private function setCallbacks()
{
$this->callbacks['system.getCapabilities'] = [$this, 'getCapabilities'];
$this->callbacks['system.listMethods'] = [$this, 'listMethods'];
$this->callbacks['system.multicall'] = [$this, 'multiCall'];
$this->callbacks['system.methodHelp'] = [$this, 'methodHelp'];
}
/**
* 服务入口
*/
public function serve()
{
$message = new Message(file_get_contents('php://input') ?: '');
if (!$message->parse()) {
$this->error(-32700, 'parse error. not well formed');
} elseif ($message->messageType != 'methodCall') {
$this->error(-32600, 'server error. invalid xml-rpc. not conforming to spec. Request must be a methodCall');
}
$result = $this->call($message->methodName, $message->params);
// Is the result an error?
if ($result instanceof Error) {
$this->error($result);
}
// Encode the result
$r = new Value($result);
$resultXml = $r->getXml();
// Create the XML
$xml = <<<EOD
<methodResponse>
<params>
<param>
<value>
$resultXml
</value>
</param>
</params>
</methodResponse>
EOD;
// Send it
$this->output($xml);
}
}

123
var/IXR/Value.php Executable file
View File

@@ -0,0 +1,123 @@
<?php
namespace IXR;
/**
* IXR值
*
* @package IXR
*/
class Value
{
private $data;
private ?string $type;
/**
* @param mixed $data
* @param string|null $type
*/
public function __construct($data, ?string $type = null)
{
$this->data = $data;
if (!$type) {
$type = $this->calculateType();
}
$this->type = $type;
if ($type == 'struct') {
/* Turn all the values in the array in to new IXR_Value objects */
foreach ($this->data as $key => $value) {
$this->data[$key] = new Value($value);
}
}
if ($type == 'array') {
for ($i = 0, $j = count($this->data); $i < $j; $i++) {
$this->data[$i] = new Value($this->data[$i]);
}
}
}
public function getXml(): string
{
/* Return XML for this value */
switch ($this->type) {
case 'boolean':
return '<boolean>' . (($this->data) ? '1' : '0') . '</boolean>';
case 'int':
return '<int>' . $this->data . '</int>';
case 'double':
return '<double>' . $this->data . '</double>';
case 'string':
return '<string>' . htmlspecialchars($this->data) . '</string>';
case 'array':
$return = '<array><data>' . "\n";
foreach ($this->data as $item) {
$return .= ' <value>' . $item->getXml() . "</value>\n";
}
$return .= '</data></array>';
return $return;
case 'struct':
$return = '<struct>' . "\n";
foreach ($this->data as $name => $value) {
$return .= " <member><name>$name</name><value>";
$return .= $value->getXml() . "</value></member>\n";
}
$return .= '</struct>';
return $return;
case 'date':
case 'base64':
return $this->data->getXml();
}
return false;
}
/**
* @return string
*/
private function calculateType(): string
{
if ($this->data === true || $this->data === false) {
return 'boolean';
}
if (is_integer($this->data)) {
return 'int';
}
if (is_double($this->data)) {
return 'double';
}
// Deal with IXR object types base64 and date
if (is_object($this->data) && is_a($this->data, 'IXR_Date')) {
return 'date';
}
if (is_object($this->data) && is_a($this->data, 'IXR_Base64')) {
return 'base64';
}
// If it is a normal PHP object convert it in to a struct
if (is_object($this->data)) {
$this->data = get_object_vars($this->data);
return 'struct';
}
if (!is_array($this->data)) {
return 'string';
}
/* We have an array - is it an array or a struct ? */
if ($this->isStruct($this->data)) {
return 'struct';
} else {
return 'array';
}
}
private function isStruct($array): bool
{
/* Nasty function to check if an array is a struct or not */
$expected = 0;
foreach ($array as $key => $value) {
if ((string)$key != (string)$expected) {
return true;
}
$expected++;
}
return false;
}
}