switch to newboos dedicated api + idletime check
This commit is contained in:
		
				
					committed by
					
						 Steffen Schröder
						Steffen Schröder
					
				
			
			
				
	
			
			
			
						parent
						
							0aed71b067
						
					
				
				
					commit
					85bc053fb3
				
			| @@ -20,19 +20,19 @@ class Connection | ||||
|  | ||||
| 	/** | ||||
| 	 * XML-RPC client instance | ||||
| 	 * @var Xmlrpc\ClientMulticall | ||||
| 	 * @var Xmlrpc\GbxRemote | ||||
| 	 */ | ||||
| 	protected $xmlrpcClient; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| 	 * @param int $timeout | ||||
| 	 * @param int $timeout (in ms) | ||||
| 	 * @param string $user | ||||
| 	 * @param string $password | ||||
| 	 * @return Connection | ||||
| 	 */ | ||||
| 	static function factory($host = '127.0.0.1', $port = 5000, $timeout = 5, $user = 'SuperAdmin', $password = 'SuperAdmin') | ||||
| 	static function factory($host = '127.0.0.1', $port = 5000, $timeout = 50, $user = 'SuperAdmin', $password = 'SuperAdmin') | ||||
| 	{ | ||||
| 		$key = $host.':'.$port; | ||||
| 		if(!isset(self::$instances[$key])) | ||||
| @@ -56,6 +56,24 @@ class Connection | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Change client timeouts | ||||
| 	 * @param int $read read timeout (in ms), null or 0 to leave unchanged | ||||
| 	 * @param int $write write timeout (in ms), null or 0 to leave unchanged | ||||
| 	 */ | ||||
| 	function setTimeouts($read=null, $write=null) | ||||
| 	{ | ||||
| 		$this->xmlrpcClient->setTimeouts($read, $write); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return int Network idle time in seconds | ||||
| 	 */ | ||||
| 	function getIdleTime() | ||||
| 	{ | ||||
| 		return $this->xmlrpcClient->getIdleTime(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| @@ -65,7 +83,7 @@ class Connection | ||||
| 	 */ | ||||
| 	protected function __construct($host, $port, $timeout, $user, $password) | ||||
| 	{ | ||||
| 		$this->xmlrpcClient = new Xmlrpc\ClientMulticall($host, $port, $timeout); | ||||
| 		$this->xmlrpcClient = new Xmlrpc\GbxRemote($host, $port, array('open' => $timeout)); | ||||
| 		$this->authenticate($user, $password); | ||||
| 		$this->setApiVersion('2013-04-16'); | ||||
| 	} | ||||
| @@ -86,9 +104,7 @@ class Connection | ||||
| 	 */ | ||||
| 	function executeCallbacks() | ||||
| 	{ | ||||
| 		$this->xmlrpcClient->readCallbacks(); | ||||
| 		$calls = $this->xmlrpcClient->getCallbackResponses(); | ||||
| 		return $calls; | ||||
| 		return $this->xmlrpcClient->getCallbacks(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -97,7 +113,7 @@ class Connection | ||||
| 	 */ | ||||
| 	function executeMulticall() | ||||
| 	{ | ||||
| 		$this->xmlrpcClient->multiqueryIgnoreResult(); | ||||
| 		$this->xmlrpcClient->multiquery(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -114,9 +130,7 @@ class Connection | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			array_unshift($params, $methodName); | ||||
| 			call_user_func_array(array($this->xmlrpcClient, 'query'), $params); | ||||
| 			return $this->xmlrpcClient->getResponse(); | ||||
| 			return $this->xmlrpcClient->query($methodName, $params); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| @@ -227,9 +241,9 @@ class Connection | ||||
| 			throw new InvalidArgumentException('vote->cmdParam = '.print_r($vote->cmdParam, true)); | ||||
| 		} | ||||
|  | ||||
| 		$tmpCmd = new Xmlrpc\Request($vote->cmdName, $vote->cmdParam); | ||||
| 		$tmpCmd = Xmlrpc\Request::encode($vote->cmdName, $vote->cmdName); | ||||
|  | ||||
| 		return $this->execute(ucfirst(__FUNCTION__).'Ex', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); | ||||
| 		return $this->execute(ucfirst(__FUNCTION__).'Ex', array($tmpCmd, $ratio, $timeout, $voters), $multicall); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -264,9 +278,9 @@ class Connection | ||||
| 			throw new InvalidArgumentException('voters = '.print_r($voters, true)); | ||||
| 		} | ||||
|  | ||||
| 		$tmpCmd = new Xmlrpc\Request('Kick', array($login)); | ||||
| 		$tmpCmd = Xmlrpc\Request::encode('Kick', array($login)); | ||||
|  | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -301,9 +315,9 @@ class Connection | ||||
| 			throw new InvalidArgumentException('voters = '.print_r($voters, true)); | ||||
| 		} | ||||
|  | ||||
| 		$tmpCmd = new Xmlrpc\Request('Ban', array($login)); | ||||
| 		$tmpCmd = Xmlrpc\Request::encode('Ban', array($login)); | ||||
|  | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -333,9 +347,9 @@ class Connection | ||||
| 			throw new InvalidArgumentException('voters = '.print_r($voters, true)); | ||||
| 		} | ||||
|  | ||||
| 		$tmpCmd = new Xmlrpc\Request('RestartMap', array()); | ||||
| 		$tmpCmd = Xmlrpc\Request::encode('RestartMap', array()); | ||||
|  | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -365,9 +379,9 @@ class Connection | ||||
| 			throw new InvalidArgumentException('voters = '.print_r($voters, true)); | ||||
| 		} | ||||
|  | ||||
| 		$tmpCmd = new Xmlrpc\Request('NextMap', array()); | ||||
| 		$tmpCmd = Xmlrpc\Request::encode('NextMap', array()); | ||||
|  | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd->getXml(), $ratio, $timeout, $voters), $multicall); | ||||
| 		return $this->execute('CallVoteEx', array($tmpCmd, $ratio, $timeout, $voters), $multicall); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -832,11 +846,11 @@ class Connection | ||||
| 	{ | ||||
| 		return $this->execute(ucfirst(__FUNCTION__)); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Opens a link in the client with the specified players.  | ||||
| 	 * The parameters are the login of the client to whom the link to open is sent, the link url, and the 'LinkType'  | ||||
| 	 * (0 in the external browser, 1 in the internal manialink browser).  | ||||
| 	 * Opens a link in the client with the specified players. | ||||
| 	 * The parameters are the login of the client to whom the link to open is sent, the link url, and the 'LinkType' | ||||
| 	 * (0 in the external browser, 1 in the internal manialink browser). | ||||
| 	 * Login can be a single login or a list of comma-separated logins. Only available to | ||||
| 	 * @param Structures\Player|string|mixed[] $player | ||||
| 	 * @param string $link | ||||
| @@ -859,7 +873,7 @@ class Connection | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('linkType = '.print_r($linkType, true)); | ||||
| 		} | ||||
| 		 | ||||
|  | ||||
| 		return $this->execute('SendOpenLinkToLogin', array($login, $link, $linkType), $multicall); | ||||
| 	} | ||||
|  | ||||
| @@ -1229,14 +1243,8 @@ class Connection | ||||
| 		} | ||||
|  | ||||
| 		$inputData = file_get_contents($localFilename); | ||||
|  | ||||
| 		$data = new Xmlrpc\Base64($inputData); | ||||
|  | ||||
| 		if(strlen($data->getXml()) > 1024 * 1024 - 15) | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('file is too big'); | ||||
| 		} | ||||
|  | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($filename, $data), $multicall); | ||||
| 	} | ||||
|  | ||||
| @@ -1257,11 +1265,6 @@ class Connection | ||||
|  | ||||
| 		$data = new Xmlrpc\Base64($data); | ||||
|  | ||||
| 		if(strlen($data->getXml()) > 1024 * 1024 - 15) | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('data are too big'); | ||||
| 		} | ||||
|  | ||||
| 		return $this->execute('WriteFile', array($filename, $data), $multicall); | ||||
| 	} | ||||
|  | ||||
| @@ -1284,13 +1287,8 @@ class Connection | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('filename = '.print_r($filename, true)); | ||||
| 		} | ||||
| 		if(filesize($filename) > 4 * 1024) | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('file is too big'); | ||||
| 		} | ||||
|  | ||||
| 		$inputData = file_get_contents($filename); | ||||
|  | ||||
| 		$data = new Xmlrpc\Base64($inputData); | ||||
|  | ||||
| 		return $this->execute('TunnelSendDataToLogin', array($login, $data), $multicall); | ||||
| @@ -1541,9 +1539,9 @@ class Connection | ||||
|  | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($downloadRate, $uploadRate), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns the list of tags and associated values set on this server.  | ||||
| 	 * Returns the list of tags and associated values set on this server. | ||||
| 	 * The list is an array of structures {string Name, string Value}. | ||||
| 	 * Only available to Admin. | ||||
| 	 * @return array | ||||
| @@ -1552,10 +1550,10 @@ class Connection | ||||
| 	{ | ||||
| 		return $this->execute(ucfirst(__FUNCTION__)); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Set a tag and its value on the server. This method takes two parameters.  | ||||
| 	 * The first parameter specifies the name of the tag, and the second one its value.   | ||||
| 	 * Set a tag and its value on the server. This method takes two parameters. | ||||
| 	 * The first parameter specifies the name of the tag, and the second one its value. | ||||
| 	 * Only available to Admin. | ||||
| 	 * @param string $key | ||||
| 	 * @param string $value | ||||
| @@ -1575,9 +1573,9 @@ class Connection | ||||
| 		} | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($key, $value), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Unset the tag with the specified name on the server.  | ||||
| 	 * Unset the tag with the specified name on the server. | ||||
| 	 * Only available to Admin. | ||||
| 	 * @param string $key | ||||
| 	 * @param bool $multicall | ||||
| @@ -1592,9 +1590,9 @@ class Connection | ||||
| 		} | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($key), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Reset all tags on the server.  | ||||
| 	 * Reset all tags on the server. | ||||
| 	 * Only available to Admin. | ||||
| 	 * @param bool $multicall | ||||
| 	 * @return bool | ||||
| @@ -1833,7 +1831,7 @@ class Connection | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Get whether the server if a lobby, the number and maximum number of players currently managed by it.  | ||||
| 	 * Get whether the server if a lobby, the number and maximum number of players currently managed by it. | ||||
| 	 * The struct returned contains 4 fields IsLobby, LobbyPlayers, LobbyMaxPlayers, and LobbyPlayersLevel. | ||||
| 	 * @return Structures\LobbyInfo | ||||
| 	 */ | ||||
| @@ -1842,9 +1840,9 @@ class Connection | ||||
| 		$result = $this->execute(ucfirst(__FUNCTION__)); | ||||
| 		return Structures\LobbyInfo::fromArray($result); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Customize the clients 'leave server' dialog box.  | ||||
| 	 * Customize the clients 'leave server' dialog box. | ||||
| 	 * Parameters are: ManialinkPage, SendToServer url '#qjoin=login@title', | ||||
| 	 * ProposeAddToFavorites and DelayQuitButton (in milliseconds). | ||||
| 	 * Only available to Admin. | ||||
| @@ -2077,7 +2075,7 @@ class Connection | ||||
| 	 * Returns a replay containing the data needed to validate the current best time of the player. | ||||
| 	 * The parameter is the login of the player. | ||||
| 	 * @param Structures\Player|string $player | ||||
| 	 * @return string base64 encoded | ||||
| 	 * @return string | ||||
| 	 * @throws InvalidArgumentException | ||||
| 	 */ | ||||
| 	function getValidationReplay($player) | ||||
| @@ -2086,7 +2084,7 @@ class Connection | ||||
| 		{ | ||||
| 			throw new InvalidArgumentException('player must be set'); | ||||
| 		} | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($login)); | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($login))->scalar; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| @@ -2639,7 +2637,7 @@ class Connection | ||||
| 	{ | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($rules), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Send commands to the mode script. | ||||
| 	 * Only available to Admin. | ||||
| @@ -2651,9 +2649,9 @@ class Connection | ||||
| 	{ | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($commands), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Change the settings and send commands to the mode script.  | ||||
| 	 * Change the settings and send commands to the mode script. | ||||
| 	 * Only available to Admin. | ||||
| 	 * @param array $settings | ||||
| 	 * @param array $commands | ||||
| @@ -3590,9 +3588,9 @@ class Connection | ||||
| 		} | ||||
| 		return $this->execute(ucfirst(__FUNCTION__), array($fakePlayerLogin), $multicall); | ||||
| 	} | ||||
| 	 | ||||
|  | ||||
| 	/** | ||||
| 	 * Returns the token infos for a player.  | ||||
| 	 * Returns the token infos for a player. | ||||
| 	 * The returned structure is { TokenCost, CanPayToken }. | ||||
| 	 * @param Structures\Player|string $player | ||||
| 	 * @return array | ||||
| @@ -4392,28 +4390,9 @@ class Connection | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Exception Dedicated to Query Error | ||||
|  */ | ||||
| class QueryException extends \Exception | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Exception Dedicated to Connection Error | ||||
|  */ | ||||
| class ConnectionException extends \Exception | ||||
| { | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Exception Dedicated to Invalid Argument Error on Request Call | ||||
|  */ | ||||
| class InvalidArgumentException extends \Exception | ||||
| { | ||||
|  | ||||
| } | ||||
| class InvalidArgumentException extends \Exception {} | ||||
|  | ||||
| ?> | ||||
|   | ||||
| @@ -7,18 +7,17 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Base64  | ||||
| class Base64 | ||||
| { | ||||
| 	public $data; | ||||
| 	public $scalar; | ||||
| 	public $xmlrpc_type = 'base64'; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $data | ||||
| 	 */ | ||||
| 	function __construct($data) | ||||
| 	{ | ||||
| 		$this->data = $data; | ||||
| 	} | ||||
|  | ||||
| 	function getXml()  | ||||
| 	{ | ||||
| 		return '<base64>'.base64_encode($this->data).'</base64>'; | ||||
| 		$this->scalar = $data; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,481 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| if (!defined('LF')) | ||||
| { | ||||
| 	define('LF', "\n"); | ||||
| } | ||||
|  | ||||
| if (!defined('SIZE_MAX')) | ||||
| { | ||||
| 	define('SIZE_MAX', 4096*1024); | ||||
| } | ||||
|  | ||||
| class Client | ||||
| { | ||||
| 	public $socket; | ||||
| 	public $message = false; | ||||
| 	public $cb_message = array(); | ||||
| 	public $reqhandle; | ||||
| 	public $protocol = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * @var int Timeout in milli-seconds | ||||
| 	 */ | ||||
| 	public $timeout; | ||||
|  | ||||
| 	static $received; | ||||
| 	static $sent; | ||||
|  | ||||
| 	function bigEndianTest() | ||||
| 	{ | ||||
| 		list($endiantest) = array_values(unpack('L1L', pack('V', 1))); | ||||
| 		if ($endiantest != 1) | ||||
| 		{ | ||||
| 			if(!function_exists(__NAMESPACE__.'\\unpack')) | ||||
| 			{ | ||||
| 				/** | ||||
| 				 * The following code is a workaround for php's unpack function which | ||||
| 				 * does not have the capability of unpacking double precision floats | ||||
| 				 * that were packed in the opposite byte order of the current machine. | ||||
| 				 */ | ||||
| 				function unpack($format, $data) | ||||
| 				{ | ||||
| 					$ar = unpack($format, $data); | ||||
| 					$vals = array_values($ar); | ||||
| 					$f = explode('/', $format); | ||||
| 					$i = 0; | ||||
| 					foreach ($f as $f_k => $f_v) | ||||
| 					{ | ||||
| 						$repeater = intval(substr($f_v, 1)); | ||||
| 						if ($repeater == 0) | ||||
| 						{ | ||||
| 							$repeater = 1; | ||||
| 						} | ||||
| 						if ($f_v{1} == '*') | ||||
| 						{ | ||||
| 							$repeater = count($ar) - $i; | ||||
| 						} | ||||
| 						if ($f_v{0} != 'd') | ||||
| 						{ | ||||
| 							$i += $repeater; | ||||
| 							continue; | ||||
| 						} | ||||
| 						$j = $i + $repeater; | ||||
| 						for ($a = $i; $a < $j; ++$a) | ||||
| 						{ | ||||
| 							$p = pack('d', $vals[$i]); | ||||
| 							$p = strrev($p); | ||||
| 							list($vals[$i]) = array_values(unpack('d1d', $p)); | ||||
| 							++$i; | ||||
| 						} | ||||
| 					} | ||||
| 					$a = 0; | ||||
| 					foreach ($ar as $ar_k => $ar_v) | ||||
| 					{ | ||||
| 						$ar[$ar_k] = $vals[$a]; | ||||
| 						++$a; | ||||
| 					} | ||||
| 					return $ar; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * | ||||
| 	 * @param string $hostname | ||||
| 	 * @param int $port | ||||
| 	 * @param int $timeout In milliseconds | ||||
| 	 */ | ||||
| 	function __construct($hostname, $port, $timeout = 50) | ||||
| 	{ | ||||
| 		$this->socket = false; | ||||
| 		$this->reqhandle = 0x80000000; | ||||
| 		$this->timeout = $timeout; | ||||
| 		$this->init($hostname, $port); | ||||
| 	} | ||||
|  | ||||
| 	function __destruct() | ||||
| 	{ | ||||
| 		$this->terminate(); | ||||
| 	} | ||||
|  | ||||
| 	protected function init($hostname, $port) | ||||
| 	{ | ||||
|  | ||||
| 		$this->bigEndianTest(); | ||||
|  | ||||
| 		// open connection | ||||
| 		$this->socket = @fsockopen($hostname, $port, $errno, $errstr, $this->timeout); | ||||
| 		if (!$this->socket) | ||||
| 		{ | ||||
| 			throw new FatalException("transport error - could not open socket (error: $errno, $errstr)", FatalException::NOT_INITIALIZED); | ||||
| 		} | ||||
| 		// handshake | ||||
| 		$array_result = unpack('Vsize', fread($this->socket, 4)); | ||||
| 		$size = $array_result['size']; | ||||
| 		if ($size > 64) | ||||
| 		{ | ||||
| 			throw new FatalException('transport error - wrong lowlevel protocol header', FatalException::OTHER); | ||||
| 		} | ||||
| 		$handshake = fread($this->socket, $size); | ||||
| 		if ($handshake == 'GBXRemote 1') | ||||
| 		{ | ||||
| 			$this->protocol = 1; | ||||
| 		} | ||||
| 		elseif ($handshake == 'GBXRemote 2') | ||||
| 		{ | ||||
| 			$this->protocol = 2; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			throw new FatalException('transport error - wrong lowlevel protocol version', FatalException::OTHER); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function terminate() | ||||
| 	{ | ||||
| 		if ($this->socket) | ||||
| 		{ | ||||
| 			fclose($this->socket); | ||||
| 			$this->socket = false; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	protected function sendRequest(Request $request) | ||||
| 	{ | ||||
| 		$xml = $request->getXml(); | ||||
|  | ||||
| 		@stream_set_timeout($this->socket, 0, $this->timeout * 1000 * 200); | ||||
| 		// send request | ||||
| 		$this->reqhandle++; | ||||
| 		if ($this->protocol == 1) | ||||
| 		{ | ||||
| 			$bytes = pack('Va*', strlen($xml), $xml); | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			$bytes = pack('VVa*', strlen($xml), $this->reqhandle, $xml); | ||||
| 		} | ||||
|  | ||||
| 		$bytes_to_write = strlen($bytes); | ||||
|  | ||||
| 		// increase sent counter ... | ||||
| 		self::$sent += $bytes_to_write; | ||||
|  | ||||
| 		while ($bytes_to_write > 0) | ||||
| 		{ | ||||
| 			$r = fwrite($this->socket, $bytes); | ||||
| 			if ($r === false || $r == 0) | ||||
| 			{ | ||||
| 				throw new FatalException('Connection interupted', FatalException::INTERRUPTED); | ||||
| 			} | ||||
|  | ||||
| 			$bytes_to_write -= $r; | ||||
| 			if ($bytes_to_write == 0) | ||||
| 			{ | ||||
| 				break; | ||||
| 			} | ||||
|  | ||||
| 			$bytes = substr($bytes, $r); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	protected function getResult() | ||||
| 	{ | ||||
| 		$contents = ''; | ||||
| 		$contents_length = 0; | ||||
| 		do | ||||
| 		{ | ||||
| 			$size = 0; | ||||
| 			$recvhandle = 0; | ||||
| 			@stream_set_timeout($this->socket, 0, $this->timeout * 1000 * 1000); | ||||
| 			// Get result | ||||
| 			if ($this->protocol == 1) | ||||
| 			{ | ||||
| 				$contents = fread($this->socket, 4); | ||||
| 				if (strlen($contents) == 0 || $contents === false) | ||||
| 				{ | ||||
| 					throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); | ||||
| 				} | ||||
| 				$array_result = unpack('Vsize', $contents); | ||||
| 				$size = $array_result['size']; | ||||
| 				$recvhandle = $this->reqhandle; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				$contents = fread($this->socket, 8); | ||||
| 				if (strlen($contents) == 0 || $contents === false) | ||||
| 				{ | ||||
| 					throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); | ||||
| 				} | ||||
| 				$array_result = unpack('Vsize/Vhandle', $contents); | ||||
| 				$size = $array_result['size']; | ||||
| 				$recvhandle = $array_result['handle']; | ||||
| 				// -- amd64 support -- | ||||
| 				$bits = sprintf('%b', $recvhandle); | ||||
| 				if (strlen($bits) == 64) | ||||
| 				{ | ||||
| 					$recvhandle = bindec(substr($bits, 32)); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			if ($recvhandle == 0 || $size == 0) | ||||
| 			{ | ||||
| 				throw new FatalException('transport error - connection interrupted!', FatalException::INTERRUPTED); | ||||
| 			} | ||||
|  | ||||
| 			if ($size > SIZE_MAX) | ||||
| 			{ | ||||
| 				throw new Exception("transport error - answer too big ($size)", Exception::ANWSER_TOO_BIG); | ||||
| 			} | ||||
|  | ||||
| 			self::$received += $size; | ||||
|  | ||||
| 			$contents = ''; | ||||
| 			$contents_length = 0; | ||||
| 			@stream_set_timeout($this->socket, 0, $this->timeout * 1000); | ||||
| 			while ($contents_length < $size) | ||||
| 			{ | ||||
| 				$contents .= fread($this->socket, $size-$contents_length); | ||||
| 				$contents_length = strlen($contents); | ||||
| 			} | ||||
|  | ||||
| 			if (($recvhandle & 0x80000000) == 0) | ||||
| 			{ | ||||
| 				// this is a callback, not our answer! handle= $recvhandle, xml-rpc= $contents | ||||
| 				// just add it to the message list for the user to read | ||||
| 				$new_cb_message = new Message($contents); | ||||
| 				if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') | ||||
| 				{ | ||||
| 					array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 		while ((int)$recvhandle != (int)$this->reqhandle); | ||||
|  | ||||
| 		$this->message = new Message($contents); | ||||
| 		if (!$this->message->parse()) | ||||
| 		{ | ||||
| 			// XML error | ||||
| 			throw new Exception('parse error. not well formed', Exception::OTHER); | ||||
| 		} | ||||
| 		// Is the message a fault? | ||||
| 		if ($this->message->messageType == 'fault') | ||||
| 		{ | ||||
| 			throw new Exception($this->message->faultString, $this->message->faultCode); | ||||
| 		} | ||||
|  | ||||
| 		return $this->message; | ||||
| 	} | ||||
|  | ||||
|  | ||||
| 	function query() | ||||
| 	{ | ||||
| 		$args = func_get_args(); | ||||
| 		$method = array_shift($args); | ||||
|  | ||||
| 		if (!$this->socket || $this->protocol == 0) | ||||
| 		{ | ||||
| 			throw new FatalException('deb4 transport error - Client not initialized', FatalException::NOT_INITIALIZED); | ||||
| 		} | ||||
|  | ||||
| 		$request = new Request($method, $args); | ||||
|  | ||||
| 		// Check if request is larger than 1024 Kbytes | ||||
| 		if ($request->getLength() > 1024*1024-8) | ||||
| 		{ | ||||
| 			throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); | ||||
| 		} | ||||
|  | ||||
| 		$this->sendRequest($request); | ||||
| 		return $this->getResult(); | ||||
| 	} | ||||
|  | ||||
| 	// Non-blocking query method: doesn't read the response | ||||
| 	function queryIgnoreResult() | ||||
| 	{ | ||||
| 		$args = func_get_args(); | ||||
| 		$method = array_shift($args); | ||||
|  | ||||
| 		if (!$this->socket || $this->protocol == 0) | ||||
| 		{ | ||||
| 			throw new FatalException('deb 5transport error - Client not initialized', FatalException::NOT_INITIALIZED); | ||||
| 		} | ||||
|  | ||||
| 		$request = new Request($method, $args); | ||||
|  | ||||
| 		// Check if the request is greater than 512 Kbytes to avoid errors | ||||
| 		// If the method is system.multicall, make two calls (possibly recursively) | ||||
| 		if ($request->getLength() > 1024*1024-8) | ||||
| 		{ | ||||
| 			if ($method == 'system.multicall' && isset($args[0])) | ||||
| 			{ | ||||
| 				$count = count($args[0]); | ||||
| 				// If count is 1, query cannot be reduced | ||||
| 				if ($count < 2) | ||||
| 				{ | ||||
| 					throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); | ||||
| 				} | ||||
| 				$length = floor($count/2); | ||||
|  | ||||
| 				$args1 = array_slice($args[0], 0, $length); | ||||
| 				$args2 = array_slice($args[0], $length, ($count-$length)); | ||||
|  | ||||
| 				$res1 = $this->queryIgnoreResult('system.multicall', $args1); | ||||
| 				$res2 = $this->queryIgnoreResult('system.multicall', $args2); | ||||
| 				return ($res1 && $res2); | ||||
| 			} | ||||
| 			// If the method is not a multicall, just stop | ||||
| 			else | ||||
| 			{ | ||||
| 				throw new Exception('transport error - request too large!', Exception::REQUEST_TOO_BIG); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		$this->sendRequest($request); | ||||
| 	} | ||||
|  | ||||
| 	function getResponse() | ||||
| 	{ | ||||
| 		// methodResponses can only have one param - return that | ||||
| 		return $this->message->params[0]; | ||||
| 	} | ||||
|  | ||||
| 	function readCallbacks() | ||||
| 	{ | ||||
| 		if (!$this->socket || $this->protocol == 0) | ||||
| 			throw new FatalException('transport error - Client not initialized', FatalException::NOT_INITIALIZED); | ||||
| 		if ($this->protocol == 1) | ||||
| 			return false; | ||||
|  | ||||
| 		@stream_set_timeout($this->socket, 0, $this->timeout * 20);  // timeout 1 ms (to read available data) | ||||
| 		// (assignment in arguments is forbidden since php 5.1.1) | ||||
| 		$read = array($this->socket); | ||||
| 		$write = NULL; | ||||
| 		$except = NULL; | ||||
| 		$nb = false; | ||||
|  | ||||
| 		try | ||||
| 		{ | ||||
| 			$nb = @stream_select($read, $write, $except, 0, $this->timeout * 20); | ||||
| 		} | ||||
| 		catch (\Exception $e) | ||||
| 		{ | ||||
| 			if (strpos($e->getMessage(), 'Invalid CRT') !== false) | ||||
| 			{ | ||||
| 				$nb = true; | ||||
| 			} | ||||
| 			elseif (strpos($e->getMessage(), 'Interrupted system call') !== false) | ||||
| 			{ | ||||
| 				return; | ||||
| 			} | ||||
| 			else | ||||
| 			{ | ||||
| 				throw $e; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		// workaround for stream_select bug with amd64 | ||||
| 		if ($nb !== false) | ||||
| 		{ | ||||
| 			$nb = count($read); | ||||
| 		} | ||||
|  | ||||
| 		while ($nb !== false && $nb > 0) | ||||
| 		{ | ||||
| 			// Get result | ||||
| 			$contents = ''; | ||||
| 			while(strlen($contents) < 8){ | ||||
| 				$contents .= fread($this->socket, 8 - strlen($contents)); | ||||
| 				if (strlen($contents) == 0 || $contents === false) | ||||
| 				{ | ||||
| 					var_dump("deb6 transport error"); | ||||
| 					//throw new FatalException('deb6 transport error - connection interrupted!', FatalException::INTERRUPTED); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			$array_result = unpack('Vsize/Vhandle', $contents); | ||||
| 			$size = $array_result['size']; | ||||
| 			$recvhandle = $array_result['handle']; | ||||
|  | ||||
| 			if ($recvhandle == 0 || $size == 0) | ||||
| 			{ | ||||
| 				throw new FatalException('deb7 transport error - connection interrupted!', FatalException::INTERRUPTED); | ||||
| 			} | ||||
| 			if ($size > SIZE_MAX) | ||||
| 			{ | ||||
| 				throw new Exception("transport error - answer too big ($size)", Exception::ANWSER_TOO_BIG); | ||||
| 			} | ||||
|  | ||||
| 			self::$received += $size; | ||||
|  | ||||
| 			$contents = ''; | ||||
| 			$contents_length = 0; | ||||
| 			while ($contents_length < $size) | ||||
| 			{ | ||||
| 				$contents .= fread($this->socket, $size-$contents_length); | ||||
| 				$contents_length = strlen($contents); | ||||
| 			} | ||||
|  | ||||
| 			if (($recvhandle & 0x80000000) == 0) | ||||
| 			{ | ||||
| 				// this is a callback. handle= $recvhandle, xml-rpc= $contents | ||||
| 				//echo 'CALLBACK('.$contents_length.')[ '.$contents.' ]' . LF; | ||||
| 				$new_cb_message = new Message($contents); | ||||
| 				if ($new_cb_message->parse() && $new_cb_message->messageType != 'fault') | ||||
| 				{ | ||||
| 					array_push($this->cb_message, array($new_cb_message->methodName, $new_cb_message->params)); | ||||
| 				} | ||||
| 				// flo: moved to end ... | ||||
| 				// $something_received = true; | ||||
| 			} | ||||
|  | ||||
| 			// (assignment in arguments is forbidden since php 5.1.1) | ||||
| 			$read = array($this->socket); | ||||
| 			$write = NULL; | ||||
| 			$except = NULL; | ||||
|  | ||||
| 			try | ||||
| 			{ | ||||
| 				$nb = @stream_select($read, $write, $except, 0, 0); // Notimeout, just flush the data | ||||
| 			} | ||||
| 			catch (\Exception $e) | ||||
| 			{ | ||||
| 				if (strpos($e->getMessage(), 'Invalid CRT') !== false) | ||||
| 				{ | ||||
| 					$nb = true; | ||||
| 				} | ||||
| 				else | ||||
| 				{ | ||||
| 					throw $e; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			// workaround for stream_select bug with amd64 | ||||
| 			if ($nb !== false) | ||||
| 			{ | ||||
| 				$nb = count($read); | ||||
| 			} | ||||
| 		} | ||||
| 		//stream_set_blocking($this->socket, true); | ||||
| 		return !empty($this->cb_message); | ||||
| 	} | ||||
|  | ||||
| 	function getCallbackResponses() | ||||
| 	{ | ||||
| 		// (look at the end of basic.php for an example) | ||||
| 		$messages = $this->cb_message; | ||||
| 		$this->cb_message = array(); | ||||
| 		return $messages; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -1,47 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| // TODO XMLRPCLib: remettre les credits | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| if (!defined('LF')) define('LF', "\n"); | ||||
|  | ||||
| class ClientMulticall extends Client | ||||
| { | ||||
| 	public $calls = array(); | ||||
|  | ||||
| 	function addCall($methodName, $args) | ||||
| 	{ | ||||
| 		$struct = array('methodName' => $methodName, 'params' => $args); | ||||
| 		$this->calls[] = $struct; | ||||
|  | ||||
| 		return (count($this->calls) - 1); | ||||
| 	} | ||||
| 	 | ||||
| 	function multiquery() | ||||
| 	{ | ||||
| 		$result = array(); | ||||
| 		if(count($this->calls)) | ||||
| 		{ | ||||
| 			$result = parent::query('system.multicall', $this->calls); | ||||
| 			$this->calls = array();  // reset for next calls | ||||
| 		} | ||||
| 		return $result; | ||||
| 	} | ||||
|  | ||||
| 	function multiqueryIgnoreResult() | ||||
| 	{ | ||||
| 		if(count($this->calls)) | ||||
| 		{ | ||||
| 			parent::queryIgnoreResult('system.multicall', $this->calls); | ||||
| 			$this->calls = array();  // reset for next calls | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -1,68 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Date  | ||||
| { | ||||
| 	public $year; | ||||
| 	public $month; | ||||
| 	public $day; | ||||
| 	public $hour; | ||||
| 	public $minute; | ||||
| 	public $second; | ||||
|  | ||||
| 	function __construct($time)  | ||||
| 	{ | ||||
| 		// $time can be a PHP timestamp or an ISO one | ||||
| 		if (is_numeric($time))  | ||||
| 		{ | ||||
| 			$this->parseTimestamp($time); | ||||
| 		}  | ||||
| 		else  | ||||
| 		{ | ||||
| 			$this->parseIso($time); | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function parseTimestamp($timestamp)  | ||||
| 	{ | ||||
| 		$this->year = date('Y', $timestamp); | ||||
| 		$this->month = date('m', $timestamp); | ||||
| 		$this->day = date('d', $timestamp); | ||||
| 		$this->hour = date('H', $timestamp); | ||||
| 		$this->minute = date('i', $timestamp); | ||||
| 		$this->second = date('s', $timestamp); | ||||
| 	} | ||||
|  | ||||
| 	function parseIso($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); | ||||
| 	} | ||||
|  | ||||
| 	function getIso()  | ||||
| 	{ | ||||
| 		return $this->year.$this->month.$this->day.'T'.$this->hour.':'.$this->minute.':'.$this->second; | ||||
| 	} | ||||
|  | ||||
| 	function getXml()  | ||||
| 	{ | ||||
| 		return '<dateTime.iso8601>'.$this->getIso().'</dateTime.iso8601>'; | ||||
| 	} | ||||
|  | ||||
| 	function getTimestamp()  | ||||
| 	{ | ||||
| 		return mktime($this->hour, $this->minute, $this->second, $this->month, $this->day, $this->year); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -7,11 +7,6 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Exception extends \Exception  | ||||
| { | ||||
| 	const ANWSER_TOO_BIG = 1; | ||||
| 	const REQUEST_TOO_BIG = 2; | ||||
| 	const OTHER = 999; | ||||
| } | ||||
| class Exception extends \Exception {} | ||||
|  | ||||
| ?> | ||||
| ?> | ||||
|   | ||||
| @@ -1,16 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * @version     $Revision: $: | ||||
|  * @author      $Author: $: | ||||
|  * @date        $Date: $: | ||||
|  */ | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class FatalException extends Exception  | ||||
| { | ||||
| 	const NOT_INITIALIZED = 1; | ||||
| 	const INTERRUPTED = 2; | ||||
| 	const OTHER = 999; | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -0,0 +1,321 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class GbxRemote | ||||
| { | ||||
| 	const MAX_REQUEST_SIZE  = 0x200000; // 2MB | ||||
| 	const MAX_RESPONSE_SIZE = 0x400000; // 4MB | ||||
|  | ||||
| 	public static $received; | ||||
| 	public static $sent; | ||||
|  | ||||
| 	private $socket; | ||||
| 	private $timeouts = array( | ||||
| 		'open' => 5, | ||||
| 		'read' => 1000, | ||||
| 		'write' => 1000 | ||||
| 	); | ||||
| 	private $requestHandle; | ||||
| 	private $callbacksBuffer = array(); | ||||
| 	private $multicallBuffer = array(); | ||||
| 	private $lastNetworkActivity = 0; | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| 	 * @param int[string] $timeouts Override default timeouts for 'open' (in s), 'read' (in ms) and 'write' (in ms) socket operations | ||||
| 	 */ | ||||
| 	function __construct($host, $port, $timeouts = array()) | ||||
| 	{ | ||||
| 		$this->requestHandle = (int) 0x80000000; | ||||
| 		$this->timeouts = array_merge($this->timeouts, $timeouts); | ||||
| 		$this->connect($host, $port); | ||||
| 	} | ||||
|  | ||||
| 	function __destruct() | ||||
| 	{ | ||||
| 		$this->terminate(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Change timeouts | ||||
| 	 * @param int $read read timeout (in ms), null or 0 to leave unchanged | ||||
| 	 * @param int $write write timeout (in ms), null or 0 to leave unchanged | ||||
| 	 */ | ||||
| 	function setTimeouts($read=null, $write=null) | ||||
| 	{ | ||||
| 		if($read) | ||||
| 			$this->timeouts['read'] = $read; | ||||
| 		if($write) | ||||
| 			$this->timeouts['write'] = $write; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return int Network idle time in seconds | ||||
| 	 */ | ||||
| 	function getIdleTime() | ||||
| 	{ | ||||
| 		$this->assertConnected(); | ||||
| 		return time() - $this->lastNetworkActivity; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $host | ||||
| 	 * @param int $port | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function connect($host, $port) | ||||
| 	{ | ||||
| 		$this->socket = @fsockopen($host, $port, $errno, $errstr, $this->timeouts['open']); | ||||
| 		stream_set_write_buffer($this->socket, 0); | ||||
| 		if(!$this->socket) | ||||
| 			throw new TransportException('Cannot open socket', TransportException::NOT_INITIALIZED); | ||||
|  | ||||
| 		// handshake | ||||
| 		$header = $this->read(15); | ||||
| 		if($header === false) | ||||
| 			throw new TransportException('Connection interrupted during handshake', TransportException::INTERRUPTED); | ||||
|  | ||||
| 		extract(unpack('Vsize/a*protocol', $header)); | ||||
| 		/** @var $size int */ | ||||
| 		/** @var $protocol string */ | ||||
| 		if($size != 11 || $protocol != 'GBXRemote 2') | ||||
| 			throw new TransportException('Wrong protocol header', TransportException::WRONG_PROTOCOL); | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 	} | ||||
|  | ||||
| 	function terminate() | ||||
| 	{ | ||||
| 		if($this->socket) | ||||
| 		{ | ||||
| 			fclose($this->socket); | ||||
| 			$this->socket = null; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $method | ||||
| 	 * @param mixed[] $args | ||||
| 	 * @return mixed | ||||
| 	 * @throws MessageException | ||||
| 	 */ | ||||
| 	function query($method, $args=array()) | ||||
| 	{ | ||||
| 		$this->assertConnected(); | ||||
| 		$xml = Request::encode($method, $args); | ||||
|  | ||||
| 		if(strlen($xml) > self::MAX_REQUEST_SIZE-8) | ||||
| 		{ | ||||
| 			if($method != 'system.multicall' || count($args) < 2) | ||||
| 				throw new MessageException('Request too large', MessageException::REQUEST_TOO_LARGE); | ||||
|  | ||||
| 			$mid = count($args) >> 1; | ||||
| 			$this->query('system.multicall', array_slice($args, 0, $mid)); | ||||
| 			$this->query('system.multicall', array_slice($args, $mid)); | ||||
| 		} | ||||
|  | ||||
| 		$this->writeMessage($xml); | ||||
| 		return $this->flush(true); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $method | ||||
| 	 * @param mixed[] $args | ||||
| 	 */ | ||||
| 	function addCall($method, $args) | ||||
| 	{ | ||||
| 		$this->multicallBuffer[] = array( | ||||
| 			'methodName' => $method, | ||||
| 			'params' => $args | ||||
| 		); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed | ||||
| 	 */ | ||||
| 	function multiquery() | ||||
| 	{ | ||||
| 		switch(count($this->multicallBuffer)) | ||||
| 		{ | ||||
| 			case 0: | ||||
| 				return; | ||||
| 			case 1: | ||||
| 				$call = array_shift($this->multicallBuffer); | ||||
| 				return $this->query($call['methodName'], $call['params']); | ||||
| 			default: | ||||
| 				$result = $this->query('system.multicall', $this->multicallBuffer); | ||||
| 				$this->multicallBuffer = array(); | ||||
| 				return $result; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed[] | ||||
| 	 */ | ||||
| 	function getCallbacks() | ||||
| 	{ | ||||
| 		$this->assertConnected(); | ||||
| 		$this->flush(); | ||||
| 		$cb = $this->callbacksBuffer; | ||||
| 		$this->callbacksBuffer = array(); | ||||
| 		return $cb; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function assertConnected() | ||||
| 	{ | ||||
| 		if(!$this->socket) | ||||
| 			throw new TransportException('Connection not initialized', TransportException::NOT_INITIALIZED); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param bool $waitResponse | ||||
| 	 * @return mixed | ||||
| 	 * @throws FaultException | ||||
| 	 */ | ||||
| 	private function flush($waitResponse=false) | ||||
| 	{ | ||||
| 		$n = @stream_select($r=array($this->socket), $w=null, $e=null, 0); | ||||
| 		while($waitResponse || $n > 0) | ||||
| 		{ | ||||
| 			list($handle, $xml) = $this->readMessage(); | ||||
| 			list($type, $value) = Request::decode($xml); | ||||
| 			switch($type) | ||||
| 			{ | ||||
| 				case 'fault': | ||||
| 					throw FaultException::create($value['faultString'], $value['faultCode']); | ||||
| 				case 'response': | ||||
| 					if($handle == $this->requestHandle) | ||||
| 						return $value; | ||||
| 					break; | ||||
| 				case 'call': | ||||
| 					$this->callbacksBuffer[] = $value; | ||||
| 			} | ||||
|  | ||||
| 			if(!$waitResponse) | ||||
| 				$n = @stream_select($r=array($this->socket), $w=null, $e=null, 0); | ||||
| 		}; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @return mixed[] | ||||
| 	 * @throws TransportException | ||||
| 	 * @throws MessageException | ||||
| 	 */ | ||||
| 	private function readMessage() | ||||
| 	{ | ||||
| 		$header = $this->read(8); | ||||
| 		if($header === false) | ||||
| 			throw new TransportException('Connection interrupted while reading header', TransportException::INTERRUPTED); | ||||
|  | ||||
| 		extract(unpack('Vsize/Vhandle', $header)); | ||||
| 		/** @var $size int */ | ||||
| 		/** @var $handle int */ | ||||
| 		if($size == 0 || $handle == 0) | ||||
| 			throw new TransportException('Incorrect header', TransportException::PROTOCOL_ERROR); | ||||
|  | ||||
| 		if($size > self::MAX_RESPONSE_SIZE) | ||||
| 			throw new MessageException('Response too large', MessageException::RESPONSE_TOO_LARGE); | ||||
|  | ||||
| 		$data = $this->read($size); | ||||
| 		if($data === false) | ||||
| 			throw new TransportException('Connection interrupted while reading data', TransportException::INTERRUPTED); | ||||
|  | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 		return array($handle, $data); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $xml | ||||
| 	 * @throws TransportException | ||||
| 	 */ | ||||
| 	private function writeMessage($xml) | ||||
| 	{ | ||||
| 		$data = pack('V2a*', strlen($xml), ++$this->requestHandle, $xml); | ||||
| 		if(!$this->write($data)) | ||||
| 			throw new TransportException('Connection interrupted while writing', TransportException::INTERRUPTED); | ||||
| 		$this->lastNetworkActivity = time(); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param int $size | ||||
| 	 * @return boolean|string | ||||
| 	 */ | ||||
| 	private function read($size) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, 0, $this->timeouts['read'] * 1000); | ||||
|  | ||||
| 		$data = ''; | ||||
| 		while(strlen($data) < $size) | ||||
| 		{ | ||||
| 			$buf = fread($this->socket, $size - strlen($data)); | ||||
| 			if($buf === '' || $buf === false) | ||||
| 				return false; | ||||
| 			$data .= $buf; | ||||
| 		} | ||||
|  | ||||
| 		self::$received += $size; | ||||
| 		return $data; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * @param string $data | ||||
| 	 * @return boolean | ||||
| 	 */ | ||||
| 	private function write($data) | ||||
| 	{ | ||||
| 		@stream_set_timeout($this->socket, 0, $this->timeouts['write'] * 1000); | ||||
|  | ||||
| 		while(strlen($data) > 0) | ||||
| 		{ | ||||
| 			$written = fwrite($this->socket, $data); | ||||
| 			if($written === 0 || $written === false) | ||||
| 				return false; | ||||
|  | ||||
| 			$data = substr($data, $written); | ||||
| 		} | ||||
|  | ||||
| 		self::$sent += strlen($data); | ||||
| 		return true; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class TransportException extends Exception | ||||
| { | ||||
| 	const NOT_INITIALIZED = 1; | ||||
| 	const INTERRUPTED     = 2; | ||||
| 	const WRONG_PROTOCOL  = 3; | ||||
| 	const PROTOCOL_ERROR  = 4; | ||||
| } | ||||
|  | ||||
| class MessageException extends Exception | ||||
| { | ||||
| 	const REQUEST_TOO_LARGE  = 1; | ||||
| 	const RESPONSE_TOO_LARGE = 2; | ||||
| } | ||||
|  | ||||
| class FaultException extends Exception | ||||
| { | ||||
| 	static function create($faultString, $faultCode) | ||||
| 	{ | ||||
| 		switch($faultString) | ||||
| 		{ | ||||
| 			case 'Login unknown.': | ||||
| 				return new LoginUnknownException($faultString, $faultCode); | ||||
| 		} | ||||
|  | ||||
| 		return new self($faultString, $faultCode); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| class LoginUnknownException extends FaultException {} | ||||
|  | ||||
| ?> | ||||
| @@ -1,189 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Message  | ||||
| { | ||||
| 	public $message; | ||||
| 	public $messageType;  // methodCall / methodResponse / fault | ||||
| 	public $faultCode; | ||||
| 	public $faultString; | ||||
| 	public $methodName; | ||||
| 	public $params; | ||||
| 	// Current variable stacks | ||||
| 	protected $arrayStructs = array();  // Stack to keep track of the current array/struct | ||||
| 	protected $arrayStructsTypes = array();  // Stack to keep track of whether things are structs or array | ||||
| 	protected $currentStructName = array();  // A stack as well | ||||
| 	protected $param; | ||||
| 	protected $value; | ||||
| 	protected $currentTag; | ||||
| 	protected $currentTagContents; | ||||
| 	// The XML parser | ||||
| 	protected $parser; | ||||
|  | ||||
| 	function __construct ($message)  | ||||
| 	{ | ||||
| 		$this->message = $message; | ||||
| 	} | ||||
|  | ||||
| 	function parse()  | ||||
| 	{ | ||||
| 		 | ||||
| 		// first remove the XML declaration | ||||
| 		$this->message = preg_replace('/<\?xml(.*)?\?'.'>/', '', $this->message); | ||||
| 		if (trim($this->message) == '')  | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
| 		$this->parser = xml_parser_create(); | ||||
| 		// Set XML parser to take the case of tags into account | ||||
| 		xml_parser_set_option($this->parser, XML_OPTION_CASE_FOLDING, false); | ||||
| 		// Set XML parser callback functions | ||||
| 		xml_set_object($this->parser, $this); | ||||
| 		xml_set_element_handler($this->parser, 'tag_open', 'tag_close'); | ||||
| 		xml_set_character_data_handler($this->parser, 'cdata'); | ||||
| 		if (!xml_parse($this->parser, $this->message))  | ||||
| 		{ | ||||
| 			/* die(sprintf('GbxRemote 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($this->parser); | ||||
| 		// Grab the error messages, if any | ||||
| 		if ($this->messageType == 'fault')  | ||||
| 		{ | ||||
| 			$this->faultCode = $this->params[0]['faultCode']; | ||||
| 			$this->faultString = $this->params[0]['faultString']; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	function tag_open($parser, $tag, $attr)  | ||||
| 	{ | ||||
| 		$this->currentTag = $tag; | ||||
| 		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 purposes more interesting than array | ||||
| 				$this->arrayStructsTypes[] = 'array'; | ||||
| 				$this->arrayStructs[] = array(); | ||||
| 				break; | ||||
| 			case 'struct': | ||||
| 				$this->arrayStructsTypes[] = 'struct'; | ||||
| 				$this->arrayStructs[] = array(); | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function cdata($parser, $cdata)  | ||||
| 	{ | ||||
| 		$this->currentTagContents .= $cdata; | ||||
| 	} | ||||
|  | ||||
| 	function tag_close($parser, $tag)  | ||||
| 	{ | ||||
| 		$valueFlag = false; | ||||
| 		switch ($tag)  | ||||
| 		{ | ||||
| 			case 'int': | ||||
| 			case 'i4': | ||||
| 				$value = (int)trim($this->currentTagContents); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 			case 'double': | ||||
| 				$value = (double)trim($this->currentTagContents); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 			case 'string': | ||||
| 				$value = (string)trim($this->currentTagContents); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 			case 'dateTime.iso8601': | ||||
| 				$value = new Date(trim($this->currentTagContents)); | ||||
| 				// $value = $iso->getTimestamp(); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 			case 'value': | ||||
| 				// If no type is indicated, the type is string | ||||
| 				if (trim($this->currentTagContents) != '') { | ||||
| 					$value = (string)$this->currentTagContents; | ||||
| 					$this->currentTagContents = ''; | ||||
| 					$valueFlag = true; | ||||
| 				} | ||||
| 				break; | ||||
| 			case 'boolean': | ||||
| 				$value = (boolean)trim($this->currentTagContents); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 			case 'base64': | ||||
| 				$value = base64_decode($this->currentTagContents); | ||||
| 				$this->currentTagContents = ''; | ||||
| 				$valueFlag = true; | ||||
| 				break; | ||||
| 				// Deal with stacks of arrays and structs | ||||
| 			case 'data': | ||||
| 			case 'struct': | ||||
| 				$value = array_pop($this->arrayStructs); | ||||
| 				array_pop($this->arrayStructsTypes); | ||||
| 				$valueFlag = true; | ||||
| 				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 ($valueFlag)  | ||||
| 		{ | ||||
| 			/* | ||||
| 			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; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
| @@ -7,36 +7,162 @@ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Request  | ||||
| if(extension_loaded('xmlrpc')) | ||||
| { | ||||
| 	public $method; | ||||
| 	public $args; | ||||
| 	public $xml; | ||||
|  | ||||
| 	function __construct($method, $args)  | ||||
| 	abstract class Request | ||||
| 	{ | ||||
| 		$this->method = $method; | ||||
| 		$this->args = $args; | ||||
| 		$this->xml = '<?xml version="1.0" encoding="utf-8" ?><methodCall><methodName>' . $this->method . '</methodName><params>'; | ||||
| 		foreach ($this->args as $arg)  | ||||
| 		/** | ||||
| 		 * @param string $method | ||||
| 		 * @param mixed[] $args | ||||
| 		 * @return string | ||||
| 		 */ | ||||
| 		static function encode($method, $args) | ||||
| 		{ | ||||
| 			$this->xml .= '<param><value>'; | ||||
| 			$v = new Value($arg); | ||||
| 			$this->xml .= $v->getXml(); | ||||
| 			$this->xml .= '</value></param>' . LF; | ||||
| 			return xmlrpc_encode_request($method, $args, array('encoding' => 'utf-8', 'verbosity' => 'no_white_space')); | ||||
| 		} | ||||
| 		$this->xml .= '</params></methodCall>'; | ||||
| 	} | ||||
|  | ||||
| 	function getLength()  | ||||
| 	{ | ||||
| 		return strlen($this->xml); | ||||
| 	} | ||||
| 		/** | ||||
| 		 * @param string $message | ||||
| 		 * @return mixed | ||||
| 		 * @throws ParseException | ||||
| 		 */ | ||||
| 		static function decode($message) | ||||
| 		{ | ||||
| 			$value = xmlrpc_decode_request($message, $method, 'utf-8'); | ||||
| 			if($value === null) | ||||
| 				throw new ParseException(); | ||||
|  | ||||
| 	function getXml()  | ||||
| 			if($method === null) | ||||
| 			{ | ||||
| 				if(@xmlrpc_is_fault($value)) | ||||
| 					return array('fault', $value); | ||||
| 				return array('response', $value); | ||||
| 			} | ||||
| 			return array('call', array($method, $value)); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| else | ||||
| { | ||||
| 	abstract class Request | ||||
| 	{ | ||||
| 		return $this->xml; | ||||
| 		const DATE_FORMAT = 'Ymd\TH:i:s'; | ||||
|  | ||||
| 		/** | ||||
| 		 * @param string $method | ||||
| 		 * @param mixed[] $args | ||||
| 		 * @return string | ||||
| 		 */ | ||||
| 		static function encode($method, $args) | ||||
| 		{ | ||||
| 			$xml = '<?xml version="1.0" encoding="utf-8"?><methodCall><methodName>'.$method.'</methodName><params>'; | ||||
| 			foreach($args as $arg) | ||||
| 				$xml .= '<param><value>'.self::encodeValue($arg).'</value></param>'; | ||||
| 			$xml .= '</params></methodCall>'; | ||||
| 			return $xml; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @param mixed $v | ||||
| 		 * @return string | ||||
| 		 */ | ||||
| 		private static function encodeValue($v) | ||||
| 		{ | ||||
| 			switch(gettype($v)) | ||||
| 			{ | ||||
| 				case 'boolean': | ||||
| 					return '<boolean>'.((int) $v).'</boolean>'; | ||||
| 				case 'integer': | ||||
| 					return '<int>'.$v.'</int>'; | ||||
| 				case 'double': | ||||
| 					return '<double>'.$v.'</double>'; | ||||
| 				case 'string': | ||||
| 					return '<string>'.htmlspecialchars($v).'</string>'; | ||||
| 				case 'object': | ||||
| 					if($v instanceof Base64) | ||||
| 						return '<base64>'.base64_encode($v->scalar).'</base64>'; | ||||
| 					if($v instanceof \DateTime) | ||||
| 						return '<dateTime.iso8601>'.$v->format(self::DATE_FORMAT).'</dateTime.iso8601>'; | ||||
| 					$v = get_object_vars($v); | ||||
| 					// fallthrough | ||||
| 				case 'array': | ||||
| 					$return = ''; | ||||
| 					// pure array case | ||||
| 					if(array_keys($v) == range(0, count($v) - 1)) | ||||
| 					{ | ||||
| 						foreach($v as $item) | ||||
| 							$return .= '<value>'.self::encodeValue($item).'</value>'; | ||||
| 						return '<array><data>'.$return.'</data></array>'; | ||||
| 					} | ||||
| 					// else it's a struct | ||||
| 					foreach($v as $name => $value) | ||||
| 						$return .= '<member><name>'.$name.'</name><value>'.self::encodeValue($value).'</value></member>'; | ||||
| 					return '<struct>'.$return.'</struct>'; | ||||
| 			} | ||||
| 			return ''; | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @param string $message | ||||
| 		 * @return mixed | ||||
| 		 * @throws ParseException | ||||
| 		 */ | ||||
| 		static function decode($message) | ||||
| 		{ | ||||
| 			$xml = @simplexml_load_string($message); | ||||
| 			if(!$xml) | ||||
| 				throw new ParseException(); | ||||
|  | ||||
| 			if($xml->getName() == 'methodResponse') | ||||
| 			{ | ||||
| 				if($xml->fault) | ||||
| 					return array('fault', self::decodeValue($xml->fault->value)); | ||||
| 				return array('response', self::decodeValue($xml->params->param->value)); | ||||
| 			} | ||||
| 			$params = array(); | ||||
| 			foreach($xml->params->param as $param) | ||||
| 				$params[] = self::decodeValue($param->value); | ||||
| 			return array('call', array((string) $xml->methodName, $params)); | ||||
| 		} | ||||
|  | ||||
| 		/** | ||||
| 		 * @param \SimpleXMLElement $elt | ||||
| 		 * @return mixed | ||||
| 		 */ | ||||
| 		private static function decodeValue($elt) | ||||
| 		{ | ||||
| 			$elt = $elt->children(); | ||||
| 			$elt = $elt[0]; | ||||
| 			switch($elt->getName()) | ||||
| 			{ | ||||
| 				case 'boolean': | ||||
| 					return (bool) $elt; | ||||
| 				case 'i4': | ||||
| 				case 'int': | ||||
| 					return (int) $elt; | ||||
| 				case 'double': | ||||
| 					return (double) $elt; | ||||
| 				case 'string': | ||||
| 					return (string) $elt; | ||||
| 				case 'base64': | ||||
| 					return new Base64(base64_decode($elt)); | ||||
| 				case 'dateTime.iso8601': | ||||
| 					return \DateTime::createFromFormat(self::DATE_FORMAT, (string) $elt); | ||||
| 				case 'array': | ||||
| 					$arr = array(); | ||||
| 					foreach($elt->data->value as $v) | ||||
| 						$arr[] = self::decodeValue($v); | ||||
| 					return $arr; | ||||
| 				case 'struct': | ||||
| 					$struct = array(); | ||||
| 					foreach($elt as $member) | ||||
| 						$struct[(string) $member->name] = self::decodeValue($member->value); | ||||
| 					return $struct; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
| class ParseException extends Exception {} | ||||
|  | ||||
| ?> | ||||
|   | ||||
| @@ -1,144 +0,0 @@ | ||||
| <?php | ||||
| /** | ||||
|  * ManiaPlanet dedicated server Xml-RPC client | ||||
|  * | ||||
|  * @license     http://www.gnu.org/licenses/lgpl.html LGPL License 3 | ||||
|  */ | ||||
|  | ||||
| namespace Maniaplanet\DedicatedServer\Xmlrpc; | ||||
|  | ||||
| class Value  | ||||
| { | ||||
| 	public $data; | ||||
| 	public $type; | ||||
|  | ||||
| 	function __construct($data, $type = false)  | ||||
| 	{ | ||||
| 		$this->data = $data; | ||||
| 		if (!$type)  | ||||
| 		{ | ||||
| 			$type = $this->calculateType(); | ||||
| 		} | ||||
| 		$this->type = $type; | ||||
| 		if ($type == 'struct')  | ||||
| 		{ | ||||
| 			// Turn all the values in the array into new 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]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function calculateType()  | ||||
| 	{ | ||||
| 		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) && $this->data instanceof Date)  | ||||
| 		{ | ||||
| 			return 'date'; | ||||
| 		} | ||||
| 		if (is_object($this->data) && $this->data instanceof Base64)  | ||||
| 		{ | ||||
| 			return 'base64'; | ||||
| 		} | ||||
| 		// If it is a normal PHP object convert it into 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'; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	function getXml()  | ||||
| 	{ | ||||
| 		// Return XML for this value | ||||
| 		switch ($this->type)  | ||||
| 		{ | ||||
| 			case 'boolean': | ||||
| 				return '<boolean>' . ($this->data ? '1' : '0') . '</boolean>'; | ||||
| 				break; | ||||
| 			case 'int': | ||||
| 				return '<int>' . $this->data . '</int>'; | ||||
| 				break; | ||||
| 			case 'double': | ||||
| 				return '<double>' . $this->data . '</double>'; | ||||
| 				break; | ||||
| 			case 'string': | ||||
| 				return '<string>' . htmlspecialchars($this->data) . '</string>'; | ||||
| 				break; | ||||
| 			case 'array': | ||||
| 				$return = '<array><data>' . LF; | ||||
| 				foreach ($this->data as $item)  | ||||
| 				{ | ||||
| 					$return .= '	<value>' . $item->getXml() . '</value>' . LF; | ||||
| 				} | ||||
| 				$return .= '</data></array>'; | ||||
| 				return $return; | ||||
| 				break; | ||||
| 			case 'struct': | ||||
| 				$return = '<struct>' . LF; | ||||
| 				foreach ($this->data as $name => $value)  | ||||
| 				{ | ||||
| 					$return .= '	<member><name>' . $name . '</name><value>'; | ||||
| 					$return .= $value->getXml() . '</value></member>' . LF; | ||||
| 				} | ||||
| 				$return .= '</struct>'; | ||||
| 				return $return; | ||||
| 				break; | ||||
| 			case 'date': | ||||
| 			case 'base64': | ||||
| 				return $this->data->getXml(); | ||||
| 				break; | ||||
| 		} | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	function isStruct($array)  | ||||
| 	{ | ||||
| 		// 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; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| ?> | ||||
		Reference in New Issue
	
	Block a user