<?php
class mgrUnexceptedResponse extends Exception {
public $response;
function __construct(SimpleXMLElement $response) {
$this->message = 'Unexcepted XML-response received from server';
$this->response = $response;
}
}
class mgrError extends Exception {
public $code;
public $obj;
public $val;
public $errorText;
public $errorCodes = array(
1 => 'Internal error',
2 => 'Element already exists',
3 => 'Element not exists',
4 => 'Invalid value',
5 => 'Limit exceed',
6 => 'Access denied',
7 => 'Licence problem',
8 => 'Message error',
9 => 'Direct error',
10 => 'Addon error',
11 => 'Not enought money',
100 => 'Unknown error'
);
public function __construct(SimpleXMLElement $error) {
$this->code = intval(mgrAPI::getAttribute($error, 'code'));
$this->errorText = trim(strval($error));
if ($this->code<1 || $this->code>999) throw new mgrUnexceptedResponse($error);
$this->obj = mgrAPI::getAttribute($error, 'obj');
$this->val = mgrAPI::getAttribute($error, 'val');
$this->message = 'Error ' . $this->code;
if (isset($this->errorCodes[$this->code])) $this->message .= ' (' . $this->errorCodes[$this->code] . ')';
if ($this->errorText!=='') $this->message .= ': ' . $this->errorText;
}
}
class mgrAPI {
const AUTH_ANONYMOUS = 1;
const AUTH_SESSID = 2;
const AUTH_AUTHINFO = 3;
const AUTH_SHELL = 4;
protected $authType = null;
protected $sessid = null;
protected $username = null;
protected $password = null;
protected $mgrURL = null;
protected $mgrctlCommand = null;
protected $lastResponse = null;
public $curlOptions = null;
public $stderrHandler = null;
public $throwMgrErrors = true;
/***************************
*** Конструкторы объекта ***
***************************/
public static function constructAnonymous($mgrURL) {
$result = new mgrAPI;
$result->initAnonymous($mgrURL);
return $result;
}
public static function constructShell($mgrctlCommand) {
$result = new mgrAPI;
$result->initShell($mgrctlCommand);
return $result;
}
public static function constructAuthinfo($mgrURL, $username, $password) {
$result = new mgrAPI;
$result->initAuthinfo($mgrURL, $username, $password);
return $result;
}
public static function constructSessid($mgrURL, $sessid) {
$result = new mgrAPI;
$result->initSessid($mgrURL, $sessid);
return $result;
}
public static function constructSessidLogin($mgrURL, $username, $password) {
$result = new mgrAPI;
$result->initSessidLogin($mgrURL, $username, $password);
return $result;
}
protected function __construct() {
}
/************************************************
*** Инициализаторы инстанса для конструкторов ***
************************************************/
protected function initAnonymous($mgrURL) {
$this->mgrURL = $mgrURL;
$this->authType = $this::AUTH_ANONYMOUS;
}
protected function initShell($mgrctlCommand) {
$this->mgrctlCommand = $mgrctlCommand;
$this->authType = $this::AUTH_SHELL;
}
protected function initAuthinfo($mgrURL, $username, $password) {
$this->mgrURL = $mgrURL;
$this->username = $username;
$this->password = $password;
$this->authType = $this::AUTH_AUTHINFO;
}
protected function initSessid($mgrURL, $sessid) {
$this->mgrURL = $mgrURL;
$this->sessid = $sessid;
$this->authType = $this::AUTH_SESSID;
return $this->sessid;
}
protected function initSessidLogin($mgrURL, $username, $password) {
$this->mgrURL = $mgrURL;
$this->username = $username;
$this->password = $password;
$arguments = compact('username', 'password');
$response = $this->httpRequest('auth', $arguments);
if (isset($response->authfail)) throw new Exception('Authentication failed');
$this->checkError($response);
if (empty($response->auth)) throw new mgrUnexceptedResponse($response);
$this->sessid = $this->getAttribute($response->auth, 'id');
if (is_null($this->sessid)) throw new mgrUnexceptedResponse($response);
$this->authType = $this::AUTH_SESSID;
return $this->sessid;
}
protected function initSessidKey($mgrURL, $username, $key) {
$this->mgrURL = $mgrURL;
$this->username = $username;
$arguments = compact('key', 'username');
$response = $this->httpRequest('auth', $arguments);
if (isset($response->authfail)) throw new Exception('Authentication failed');
$this->checkError($response);
if (empty($response->auth)) throw new mgrUnexceptedResponse($response);
$this->sessid = $this->getAttribute($response->auth, 'id');
if (is_null($this->sessid)) throw new mgrUnexceptedResponse($response);
$this->authType = $this::AUTH_SESSID;
return $this->sessid;
}
/***********************************
*** Рабочий код инстанса объекта ***
***********************************/
public function __get($property) {
static $allowed = array(
'authType',
'sessid',
'username',
'password',
'mgrURL',
'mgrctlCommand',
'lastResponse'
);
if (in_array($property, $allowed)) return $this->$property;
throw new Exception(get_class($this) . ": attempt to read inaccessible property $property");
}
public function newKey($username=null, $key=null) {
if (is_null($key)) $key = $this->keygen(32);
elseif (strlen($key)<16) throw new Exception('Length of key cannot be less than 16 characters');
$arguments = array('key'=>$key);
if (!is_null($username)) $arguments['username'] = $username;
$response = $this->query('session.newkey', $arguments);
$this->checkError($response);
if (isset($response->ok)) return $key;
throw new mgrError($response->error);
}
public function query($function, $arguments=null) {
switch ($this->authType):
case $this::AUTH_ANONYMOUS:
if (!is_array($arguments)) $arguments = array();
$result = $this->httpRequest($function, $arguments);
return $result;
case $this::AUTH_SESSID:
if (!is_array($arguments)) $arguments = array();
$arguments['auth'] = $this->sessid;
$result = $this->httpRequest($function, $arguments);
return $result;
case $this::AUTH_AUTHINFO:
if (!is_array($arguments)) $arguments = array();
$arguments['authinfo'] = $this->username . ':' . $this->password;
$result = $this->httpRequest($function, $arguments);;
return $result;
case $this::AUTH_SHELL:
if (!is_array($arguments)) $arguments = array();
$arguments['out'] = 'xml';
$command = escapeshellcmd($this->mgrctlCommand);
$command .= ' ' . escapeshellcmd($function);
foreach ($arguments as $parameterName=>$parameterValue) {
$command .= ' ';
$command .= escapeshellarg($parameterName);
$command .= '=';
$command .= escapeshellarg($parameterValue);
}
$this->cmdOutput($command, $stdout, $stderr);
if ($stderr!=='' && !is_null($this->stderrHandler)) {
if (is_string($this->stderrHandler)) {
if (!function_exists($this->stderrHandler)) throw new Exception("stderrHandler function '{$this->stderrHandler}' not exists");
}
elseif (is_object($this->stderrHandler)) {
$ok = ($x instanceof Closure);
if (!$ok) throw new Exception('Unexcepted type of stderrHandler (1)');
}
else {
throw new Exception('Unexcepted type of stderrHandler (2)');
}
$handlerArguments = array($this, $function, &$arguments, &$stdout, &$stderr);
call_user_func_array($this->stderrHandler, $handlerArguments);
}
if ($stdout==='') {
if ($stderr!=='') throw new Exception("Empty stdout received from mgrctl. Stderr: $stderr");
throw new Exception('Empty stdout received from mgrctl');
}
$result = new SimpleXMLElement($stdout);
return $result;
default:
throw new Exception('Unsupported authType: ' . $this->authType);
endswitch;
}
function tryQuery() {
$result = call_user_func_array(array($this, 'query'), func_get_args());
if (isset($result->error)) throw new mgrError($result->error);
return $result;
}
public function skipInvalidSSL($skip) {
if (!is_array($this->curlOptions)) $this->curlOptions = array();
if ($skip) {
$this->curlOptions[CURLOPT_SSL_VERIFYHOST] = 0;
$this->curlOptions[CURLOPT_SSL_VERIFYPEER] = 0;
} else {
$this->curlOptions[CURLOPT_SSL_VERIFYHOST] = 2;
$this->curlOptions[CURLOPT_SSL_VERIFYPEER] = 1;
}
}
protected function httpRequest($function, $arguments=null, &$requestInfo=null) {
$this->lastResponse = null;
if (!is_array($arguments)) $arguments = array();
$arguments['out'] = 'xml';
$arguments['func'] = $function;
$response = $this->httpPost($this->mgrURL, $arguments, $this->curlOptions, $requestInfo);
if (isset($requestInfo['content_type']))
if (!preg_match('/^text\/xml($| )/i', $requestInfo['content_type']))
throw new Exception('Server returns non-xml response');
if ($response==='') throw new Exception('HTTP-response with empty body received from server');
$result = new SimpleXMLElement($response);
$this->lastResponse = $result;
return $result;
}
protected static function httpGet($url, $curlOptions=null, &$requestInfo=null) {
$ch = curl_init($url);
if (!is_resource($ch)) throw new Exception('Failed to initialize cURL');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
if (is_array($curlOptions)) curl_setopt_array($ch, $curlOptions);
$result = curl_exec($ch);
$requestInfo = curl_getinfo($ch);
$errno = curl_errno($ch);
if ($errno!==0) throw new Exception("cURL error $errno: " . curl_error($ch));
curl_close($ch);
return $result;
}
protected static function httpPost($url, $postData=null, $curlOptions=null, &$requestInfo=null) {
$ch = curl_init($url);
if (!is_resource($ch)) throw new Exception('Failed to initialize cURL');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
if (!is_null($postData)) curl_setopt($ch, CURLOPT_POSTFIELDS, $postData);
if (is_array($curlOptions)) curl_setopt_array($ch, $curlOptions);
$result = curl_exec($ch);
$requestInfo = curl_getinfo($ch);
$errno = curl_errno($ch);
if ($errno!==0) throw new Exception("cURL error $errno: " . curl_error($ch));
curl_close($ch);
return $result;
}
protected static function keygen($length=32) {
$result = '';
for ($j=0; $j<$length; $j++) switch(rand(0, 2)):
case 0: $result .= chr(rand(97, 122)); break;
case 1: $result .= chr(rand(65, 90)); break;
case 2: $result .= chr(rand(48, 57)); break;
endswitch;
return $result;
}
public static function getAttribute(SimpleXMLElement $tag, $attributeName) {
foreach ($tag->attributes() as $name=>$value) if ($name===$attributeName) return strval($value);
return null;
}
public function checkError(SimpleXMLElement $lastResponse=null) {
if (is_null($lastResponse)) $lastResponse = $this->lastResponse;
if (isset($lastResponse->error)) throw new mgrError($lastResponse->error);
}
protected function cmdOutput($command, &$stdout, &$stderr) {
$stdout = '';
$stderr = '';
$descriptorspec = array(
1 => array('pipe', 'w'), // stdout
2 => array('pipe', 'w') // stderr
);
$proc = proc_open($command, $descriptorspec, $pipes);
if (!is_resource($proc)) throw new Exception("Failed to create process by command: $command");
$stdout = stream_get_contents($pipes[1]);
$stderr = stream_get_contents($pipes[2]);
proc_close($proc);
}
}
?>