/** * compile root part of smd schema overwriting parent json compile method * * @error 14902 * @param XO $obj expects reference variable of schema object * @return void */ protected function compileRoot(&$obj) { $obj->SMDVersion = xapp_get_option(self::VERSION, $this); if (xapp_is_option(self::DESCRIPTION, $this)) { $obj->description = xapp_get_option(self::DESCRIPTION, $this); } if (xapp_is_option(self::CONTENT_TYPE, $this)) { $obj->contentType = xapp_get_option(self::CONTENT_TYPE, $this); } $obj->transport = xapp_get_option(self::TRANSPORT, $this); $obj->envelope = xapp_get_option(self::ENVELOPE, $this); if (xapp_is_option(self::CALLBACK, $this)) { $obj->jsonpCallbackParameter = xapp_get_option(self::CALLBACK, $this); } if (xapp_is_option(self::TARGET, $this)) { $obj->target = xapp_get_option(self::TARGET, $this); } else { $obj->target = $this->getTarget(); } }
/** * executing requested service if found passing result from service invoking to response * or pass compile smd map to response if no service was called. if a callback was supplied * will wrap result into callback function * * @error 14706 * @return void */ protected function execute($call) { $get = $this->request()->getGet(); $response = $this->response(); try { $result = $this->invoke($call, $call[3]); $this->response()->skipHeader(); if (array_key_exists(xapp_get_option(self::CALLBACK, $this), $get)) { } else { $result = $response->encode($result); } $response->body($result); } catch (Exception $e) { if (xapp_is_option(self::EXCEPTION_CALLBACK, $this)) { $e = call_user_func_array(xapp_get_option(self::EXCEPTION_CALLBACK, $this), array(&$e, $this, $call)); if (!$e instanceof Exception) { if (xapp_get_option(self::COMPLY_TO_JSONRPC_1_2, $this)) { if (array_key_exists($e->getCode(), $this->codeMap)) { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, $this->codeMap[$e->getCode()], $response); } else { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, 500, $response); } } if (xapp_is_option(self::ERROR_CALLBACK, $this) && array_key_exists(xapp_get_option(self::ERROR_CALLBACK, $this), $get)) { $e = $get[xapp_get_option(self::ERROR_CALLBACK, $this)] . '(' . $response->encode($e) . ')'; } else { if (array_key_exists(xapp_get_option(self::CALLBACK, $this), $get)) { $e = $get[xapp_get_option(self::CALLBACK, $this)] . '(' . $response->encode($e) . ')'; } else { $e = $response->encode($e); } } $response->body($e); } } } }
/** * IPugin interface impl. * * setup() must be called before load * * @error 15404 * @return integer Returns error code due to the initialization. */ function setup() { //extract service configuration $this->serviceConfig = xapp_get_option(self::SERVICE_CONF, $this); //logging if (xapp_is_option(self::LOGGING_CONF, $this) && $this->serviceConfig) { $logConfig = xapp_get_option(self::SERVICE_CONF); if ($logConfig && $logConfig[XC_CONF_LOGGER] != null) { $this->logger = $logConfig[XC_CONF_LOGGER]; } else { //setup logger } } //cache if (xapp_is_option(self::CACHE_CONF, $this) && $this->serviceConfig) { $cacheConfig = xapp_get_option(self::CACHE_CONF); if ($cacheConfig) { $this->cache = Xapp_Cache::instance($this->CACHE_NS, "file", array(Xapp_Cache_Driver_File::PATH => xapp_get_option(XC_CONF_CACHE_PATH, $this->serviceConfig), Xapp_Cache_Driver_File::CACHE_EXTENSION => $this->CACHE_NS, Xapp_Cache_Driver_File::DEFAULT_EXPIRATION => 200)); } } }
/** * expects the directories passed via class constructor to be stored as autoloadable directories for preloading * see Xapp_Autoloader for explanations. * * @see Xapp_Autoloader * @error 10709 * @param null|string|array $dirs expects a directory path or multiple as array * @return void * @throws Xapp_Error */ protected function init($dirs = null) { $this->_dirs[self::hash(xapp_path(XAPP_PATH_XAPP))] = array(xapp_path(XAPP_PATH_XAPP), 'Xapp'); if (xapp_is_option(self::DIRECTORIES, $this)) { $dirs = array_merge((array) $dirs, xapp_get_option(self::DIRECTORIES, $this)); } if ($dirs !== null && (is_array($dirs) || is_string($dirs))) { if (is_string($dirs)) { $dirs = array(array($dirs)); } else { $dirs = (array) $dirs; foreach ($dirs as &$dir) { if (!is_array($dir)) { $dir = array($dir); } } } unset($dir); foreach ($dirs as $key => $dir) { $tmp = null; $dir = (array) $dir; $hash = self::hash($dir[0]); if (array_key_exists(1, $dir)) { $ns = array_slice($dir, 1); } else { if (!is_int($key) && is_string($key)) { $ns = array($key); } else { $ns = array(); } } if (is_dir($dir[0])) { if (!array_key_exists($hash, $this->_dirs)) { $this->_dirs[$hash] = array_merge(array(rtrim($dir[0], DS) . DS), $ns); continue; } } else { if (stripos($dir[0], xapp_path(XAPP_PATH_ROOT)) === false) { $tmp = (string) realpath(xapp_path(XAPP_PATH_ROOT) . $dir[0]); } if (!is_dir($tmp)) { $tmp = null; } if ($tmp === null && stripos($dir[0], xapp_path(XAPP_PATH_BASE)) === false) { $tmp = (string) realpath(xapp_path(XAPP_PATH_BASE) . $dir[0]); } if (!is_dir($tmp)) { $tmp = null; } if ($tmp === null && stripos($dir[0], xapp_path(XAPP_PATH_XAPP)) === false) { $tmp = (string) realpath(xapp_path(XAPP_PATH_XAPP) . $dir[0]); } if (!is_dir($tmp)) { $tmp = null; } if ($tmp !== null) { if (!array_key_exists($hash, $this->_dirs)) { $this->_dirs[$hash] = array_merge(array(rtrim($tmp, DS) . DS), $ns); } } else { throw new Xapp_Error(xapp_sprintf(_("relative dir: %s is not a valid dir"), $dir[0]), 1070901); } } } } if (xapp_is_option(self::INCLUDE_PATHS, $this)) { $paths = explode(PATH_SEPARATOR, get_include_path()); foreach ($paths as $path) { if (strpos($path, DS) !== false && is_readable($path)) { $this->_dirs[self::hash($path)] = (array) $path; } } } }
/** * init instance by checking for memcached extension and validating server options * * @error 15801 * @return void * @throws Xapp_Cache_Driver_Exception */ protected function init() { if (!extension_loaded('memcached')) { throw new Xapp_Cache_Driver_Exception(_("memcached is not supported by this system"), 1580101); } if (xapp_is_option(self::COMPRESS, $this)) { xapp_set_option(self::OPTIONS, array(Memcached::OPT_COMPRESSION => xapp_get_option(self::COMPRESS, $this)), $this); } if (xapp_is_option(self::CONNECTION_TIMEOUT, $this)) { xapp_set_option(self::OPTIONS, array(Memcached::OPT_CONNECT_TIMEOUT => xapp_get_option(self::CONNECTION_TIMEOUT, $this)), $this); } if (xapp_is_option(self::INSTANCE, $this)) { $this->_memcached = xapp_get_option(self::INSTANCE, $this); } else { if (xapp_is_option(self::PERSISTENT_ID, $this)) { $this->_memcached = new Memcached(xapp_get_option(self::PERSISTENT_ID, $this)); } else { $this->_memcached = new Memcached(); } if (xapp_is_option(self::OPTIONS, $this)) { foreach (xapp_get_option(self::OPTIONS, $this) as $k => $v) { if (!$this->_memcached->setOption((int) $k, $v)) { throw new Xapp_Cache_Driver_Exception(xapp_sprintf(_("unable to set memcached option: %s"), $k), 1580102); } } } $server = xapp_get_option(self::SERVER, $this); if (is_object($server)) { xapp_set_option(self::SERVER, array($server), $this, true); } else { if (is_array($server) && !array_key_exists(0, $server)) { xapp_set_option(self::SERVER, array($server), $this, true); } } foreach ((array) xapp_get_option(self::SERVER, $this) as $server) { $server = (array) $server; if (!array_key_exists('host', $server)) { throw new Xapp_Cache_Driver_Exception(_("memcached parameter: host must be set"), 1580103); } if (!array_key_exists('port', $server)) { throw new Xapp_Cache_Driver_Exception(_("memcached parameter: port must be set"), 1580104); } if (!$this->_memcached->addServer($server['host'], (int) $server['port'], array_key_exists('weight', $server) ? (int) $server['weight'] : 0)) { throw new Xapp_Cache_Driver_Exception(xapp_sprintf(_("unable to add server for host: %s and port: %d"), array($server['host'], $server['port'])), 1580105); } } } }
/** * flush exception as json error object encapsulated in js callback or custom error callback function if supplied * * @error 14708 * @param Exception $error expects the exception to flush */ public function error(Exception $error) { $get = $this->request()->getGet(); $response = $this->response(); if (xapp_get_option(self::COMPLY_TO_JSONRPC_1_2, $this)) { if (array_key_exists($error->getCode(), $this->codeMap)) { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, $this->codeMap[$error->getCode()], $response); } else { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, 500, $response); } } $error = $this->compileError($error); if (xapp_is_option(self::ERROR_CALLBACK, $this) && array_key_exists(xapp_get_option(self::ERROR_CALLBACK, $this), $get)) { $error = $get[xapp_get_option(self::ERROR_CALLBACK, $this)] . '(' . $response->encode($error) . ')'; } else { if (array_key_exists(xapp_get_option(self::CALLBACK, $this), $get)) { $error = $get[xapp_get_option(self::CALLBACK, $this)] . '(' . $response->encode($error) . ')'; } else { $error = $response->encode($error); } } $response->body($error); $this->flush(); }
/** * compile a valid profile entry expecting a entry name id and a valid time value * * @error 15205 * @param string $name expects a valid entry name id * @param float $time expects a valid microsecond value * @param array $args expects optional data to include in entry compiling * @return array */ protected function compile($name, $time, array $args = array()) { if (!array_key_exists($name, $this->_data)) { $this->_data[$name] = array(); } if (!function_exists('_xapp_compile')) { function _xapp_compile($args, $sep = ', ') { foreach ($args as $k => &$v) { if (is_array($v)) { $v = implode((string) $sep, $v); } } return $args; } } $args = _xapp_compile($args, xapp_get_option(self::STRING_SEPARATOR, $this)); $log = array($name, number_format((double) $time * xapp_get_option(self::TIME_FACTOR, $this), 2, '.', '')); $log = array_merge($log, $args); if (xapp_is_option(self::PROFILE_MEMORY, $this)) { $log = array_merge($log, array(self::byteConv(memory_get_usage(true)), self::byteConv(memory_get_peak_usage(true)))); } return $this->_data[$name][] = $log; }
/** * validates all options that have been defined throwing rpc gateway faults if any of the * options fail to validate. see constant descriptions for what each constant does * * @error 14015 * @param string $option expects the option name * @param null|mixed $value expects the options value * @throws Xapp_Rpc_Gateway_Exception * @throws Xapp_Rpc_Fault */ protected function validate($option, $value = null) { $user = null; $option = strtoupper($option); switch ($option) { case self::BASIC_AUTH: if ($this->request()->has('PHP_AUTH_USER', 'SERVER') && $this->request()->has('PHP_AUTH_PW', 'SERVER') && isset($value[0]) && isset($value[1])) { if ($this->server()->request()->getFrom('PHP_AUTH_USER', 'SERVER') !== $value[0] || $this->server()->request()->getFrom('PHP_AUTH_PW', 'SERVER') !== $value[1]) { Xapp_Rpc_Fault::t("basic auth error - user or password not correct", array(1401501, -32001)); } } else { Xapp_Rpc_Fault::t("basic auth error - credentials not set", array(1401502, -32002)); } break; case self::ALLOW_IP: if (Xapp_Rpc_Request::getClientIp() !== null && !in_array(Xapp_Rpc_Request::getClientIp(), $value)) { Xapp_Rpc_Fault::t("request denied from service", array(1401503, -32003)); } break; case self::DENY_IP: if (Xapp_Rpc_Request::getClientIp() !== null && in_array(Xapp_Rpc_Request::getClientIp(), $value)) { Xapp_Rpc_Fault::t("request denied from service", array(1401503, -32003)); } break; case self::DISABLE: if ((bool) $value) { Xapp_Rpc_Fault::t("gateway is disabled", array(1401504, -32004)); } break; case self::DISABLE_SERVICE: if ($this->server()->hasServices()) { foreach ($this->server()->getServices() as $service) { if (preg_match(Xapp_Rpc::regex($value), $service)) { Xapp_Rpc_Fault::t("requested service: {$service} is disabled", array(1401505, -32005)); } } } break; case self::ALLOW_HOST: if (Xapp_Rpc_Request::getHost() !== null && !in_array(Xapp_Rpc_Request::getHost(), $value)) { Xapp_Rpc_Fault::t("host denied from service", array(1401506, -32006)); } break; case self::DENY_HOST: if (Xapp_Rpc_Request::getHost() !== null && in_array(Xapp_Rpc_Request::getHost(), $value)) { Xapp_Rpc_Fault::t("host denied from service", array(1401506, -32006)); } break; case self::FORCE_HTTPS: if ((bool) $value && Xapp_Rpc_Request::getScheme() !== 'HTTPS') { Xapp_Rpc_Fault::t("request from none http secure host denied", array(1401507, -32007)); } break; case self::ALLOW_USER_AGENT: if ($this->request()->has('HTTP_USER_AGENT', 'SERVER') && !preg_match('/(' . implode('|', trim($value, '()')) . ')/i', $this->request()->getFrom('HTTP_USER_AGENT', 'SERVER'))) { Xapp_Rpc_Fault::t("client denied from service", array(1401508, -32008)); } break; case self::DENY_USER_AGENT: if ($this->request()->has('HTTP_USER_AGENT', 'SERVER') && preg_match('/(' . implode('|', trim($value, '()')) . ')/i', $this->request()->getFrom('HTTP_USER_AGENT', 'SERVER'))) { Xapp_Rpc_Fault::t("client denied from service", array(1401508, -32008)); } break; case self::ALLOW_REFERER: if (Xapp_Rpc_Request::getReferer() !== null && !preg_match('/(' . implode('|', trim($value, '()')) . ')/i', Xapp_Rpc_Request::getReferer())) { Xapp_Rpc_Fault::t("referer denied from service", array(1401509, -32009)); } break; case self::SIGNED_REQUEST: if ((bool) $value) { $tmp = array(); if (xapp_is_option(self::SIGNED_REQUEST_EXCLUDES, $this)) { foreach ($this->server()->getServices() as $service) { if (!preg_match(Xapp_Rpc::regex(xapp_get_option(self::SIGNED_REQUEST_EXCLUDES, $this)), $service)) { $tmp[] = $service; } } } if (sizeof($tmp) > 0) { $sign = $this->request()->getParam(xapp_get_option(self::SIGNED_REQUEST_SIGN_PARAM, $this), false); $method = strtolower(xapp_get_option(self::SIGNED_REQUEST_METHOD, $this)); switch ($method) { case 'host': $user = $this->request()->getHost(); break; case 'ip': $user = $this->request()->getClientIp(); break; case 'user': $user = $this->request()->getParam(xapp_get_option(self::SIGNED_REQUEST_USER_PARAM, $this), false); break; default: throw new Xapp_Rpc_Gateway_Exception(_("unsupported signed request user identification method"), 1401514); } if ($user === false || $user === null) { Xapp_Rpc_Fault::t(vsprintf("signed request value for: %s not found in request", array(xapp_get_option(self::SIGNED_REQUEST_USER_PARAM, $this))), array(1401512, -32011)); } if ($sign === false || $sign === null) { Xapp_Rpc_Fault::t(vsprintf("signed request value for: %s not found in request", array(xapp_get_option(self::SIGNED_REQUEST_SIGN_PARAM, $this))), array(1401513, -32011)); } $key = $this->getKey($user, null); $params = $this->request()->getParams(); if (array_key_exists('xdmTarget', $params)) { unset($params['xdmTarget']); } if (array_key_exists('view', $params)) { unset($params['view']); } if (array_key_exists('xfToken', $params)) { unset($params['xfToken']); } if (array_key_exists('time', $params)) { unset($params['time']); } if (array_key_exists('xdm_e', $params)) { unset($params['xdm_e']); } if (array_key_exists('user', $params)) { unset($params['user']); } if (array_key_exists('xdm_c', $params)) { unset($params['xdm_c']); } if (array_key_exists('xdm_p', $params)) { unset($params['xdm_p']); } if (xapp_is_option(self::SIGNED_REQUEST_CALLBACK, $this)) { if (!(bool) call_user_func_array(xapp_get_option(self::SIGNED_REQUEST_CALLBACK, $this), array($this->request(), $params, $key))) { Xapp_Rpc_Fault::t("verifying signed request failed", array(1401510, -32010)); } } else { if ($key !== null) { if (isset($params[xapp_get_option(self::SIGNED_REQUEST_SIGN_PARAM, $this)])) { unset($params[xapp_get_option(self::SIGNED_REQUEST_SIGN_PARAM, $this)]); } if ($sign !== self::sign($params, $key)) { Xapp_Rpc_Fault::t("verifying signed request failed", array(1401510, -32010)); } } else { throw new Xapp_Rpc_Gateway_Exception(_("user identification key must be set when using internal signed request verification"), 1401511); } } } } break; default: } }
/** * class destructor iterates through error stack an sorts all errors according to intended action * or target. since error have different severity level some errors need to be routed to specific * log writers only. therefore: the default implementation will route all errors to the designated * log writer. if the default log writers are overwritten than all errors are written to all registered * log writers at the same time - regardless of error log severity and mapped error action, e.g. only * mailing alert errors via email. * * @error 11605 * @return void */ public function __destruct() { $all = array(); $mail = array(); $file = array(); $map = xapp_get_option(self::ACTION_MAP, $this); foreach ($this->stack as $k => $v) { if (!array_key_exists((int) $v->severity, $map)) { $v->severity = self::LOG; } if (array_key_exists((int) $v->severity, $map)) { $v->action = $map[(int) $v->severity]; } if (isset($v->action) && !is_null($v->action)) { if (in_array($v->action, array(self::DUMP))) { xapp_console($v->object, null, 'error'); } if (in_array($v->action, array(self::LOG, self::LOG | self::MAIL))) { $file[] = $v->message; } if (in_array($v->action, array(self::MAIL, self::LOG | self::MAIL))) { $mail[] = $v->object; } $all[] = $v->object; } } if (xapp_is_option(self::WRITER, $this)) { $this->write($all); } else { if (sizeof($file) > 0) { $this->write($file, 'file'); } if (sizeof($mail) > 0) { if (xapp_is_option(self::EMAIL, $this)) { $this->write($mail, 'mail'); } } } }
/** * compile service part of smd schema * * @error 14806 * @param XO $obj expects reference variable of schema object * @return void */ protected function compileService(&$obj) { $tmp = array(); $separator = xapp_get_option(self::CLASS_METHOD_SEPARATOR, $this); if (xapp_is_option(self::SERVICE_OVER_GET, $this)) { foreach ($this->map() as $key => $val) { if (is_array($val)) { foreach ($val as $k => $v) { $v->transport = 'POST'; if (xapp_is_option(self::METHOD_TARGET, $this)) { $v->target = $this->getTarget("{$key}.{$k}"); } $tmp[$key . $separator . $k] = $v; } } else { $val->transport = 'POST'; if (xapp_is_option(self::METHOD_TARGET, $this)) { $val->target = $this->getTarget($key); } $tmp[$key] = $val; } } } else { foreach ($this->map() as $key => $val) { if (is_array($val)) { foreach ($val as $k => $v) { $v->transport = 'POST'; $v->target = $this->getTarget(); $tmp[$key . $separator . $k] = $v; } } else { $val->transport = 'POST'; if (xapp_is_option(self::METHOD_TARGET, $this)) { $val->target = $this->getTarget(); } $tmp[$key] = $val; } } } $obj->services = $tmp; }
/** * setter/getter method to set or get headers. to get headers call method without any parameters * returning all registered header values as array. to set header either pass raw header to first * argument leaving second argument unset or pass header key => value pair as first and second * argument. the raw header must be passed as one string like "Pragma: public" otherwise pass * key to first argument "Pragma" and value "public" to second argument. you can also pass multiple * headers using the first two arguments passing an array of keys in first and array of values in * second argument. size of these arrays must match or will throw exception. pass replacement * value and header code in third and fourth parameter as if you would use phps internal header(). * function will return always all registerd headers as array. when calling method without any arguments * will first time merge all header from class options to header array unsetting the class option * * @error 14505 * @param null|string|array $mixed expects raw header string, header key as string or array of keys * @param null|mixed|array $value expects null for raw header, mixed for header value or array of values * @param null|bool $replace expects optional boolean value on whether to replace header * @param null|int $code expects optional response code * @return array * @throws Xapp_Rpc_Response_Exception */ public function header($mixed = null, $value = null, $replace = null, $code = null) { if ($mixed !== null) { //raw header in first parameter if (is_string($mixed) && $value === null) { $mixed = explode(':', $mixed); if (sizeof($mixed) === 2) { $mixed = array(array(trim($mixed[0]), trim($mixed[1]))); } else { throw new Xapp_Rpc_Response_Exception(_("response raw header passed is not valid"), 1450501); } //header with key => value pair in first and second parameter } else { if (is_string($mixed) && !is_array($value)) { $mixed = array(array(trim($mixed), trim($value))); //headers with key => value pairs as array in first and second parameter } else { if (is_array($mixed) && is_array($value)) { $mixed = array($mixed, $value); //header not recognized } else { throw new Xapp_Rpc_Response_Exception(_("response header passed is not recognized"), 1450502); } } } for ($i = 0; $i < sizeof($mixed); $i++) { if (isset($mixed[$i][0]) && isset($mixed[$i][1])) { if (preg_match("/^content\\-type\\:\\s+([^\\s]+)\\s+charset\\=(.+)\$/i", $mixed[$i][0] . ': ' . $mixed[$i][1], $m)) { xapp_set_option(self::CONTENT_TYPE, strtolower(trim($m[1], '; ')), $this); xapp_set_option(self::CHARSET, strtolower(trim($m[2], '; ')), $this); } $obj = new XO(); $obj->name = $mixed[$i][0]; $obj->value = $mixed[$i][1]; $obj->replace = null; $obj->code = null; if ($replace !== null) { $obj->replace = (bool) $replace; } if (isset($mixed[$i][2])) { $obj->replace = (bool) $mixed[$i][2]; } if ($code !== null) { $obj->code = (int) $code; } if (isset($mixed[$i][3])) { $obj->code = (int) $mixed[$i][3]; } $this->_header[] = $obj; } else { throw new Xapp_Rpc_Response_Exception(_("response header miss match when passing keys and values as arrays"), 1450503); } } } else { if (xapp_is_option(self::HEADER, $this)) { $header = xapp_get_option(self::HEADER, $this); if (!is_array($header[0])) { $header = array($header); } xapp_unset_option(self::HEADER, $this); $this->header($header); } } return $this->_header; }
private function setup() { $this->serviceConfig = xapp_get_option(self::SERVICE_CONF, $this); //cache if (xapp_is_option(self::CACHE_CONF, $this) && $this->serviceConfig) { $cacheConfig = xapp_get_option(self::CACHE_CONF); if ($cacheConfig) { $this->cache = Xapp_Cache::instance("PluginManager", "file", array(Xapp_Cache_Driver_File::PATH => xapp_get_option(XC_CONF_CACHE_PATH, $this->serviceConfig), Xapp_Cache_Driver_File::CACHE_EXTENSION => "pmmanager", Xapp_Cache_Driver_File::DEFAULT_EXPIRATION => 200)); } } }
/** * get the smd target path in absolute or relative mode for services or base url for smd * output if first parameter is not set. this function will return target in htaccess rewrite * style if the option REWRITE_URL is set. this option can be either boolean true let the smd * resolve the rewrite pattern automatically which is nothing but extending the base path of * the url to contain the service class and function parameter like: "/base/rpc/index.php" * becomes "/base/rpc/class.function" or "/base/rpc/function" because rewrite will remap this * rule to "/base/rpc/index.php?service=class.function". you can also pass a string with your * rewrite rule as "/base/rpc/foo/{$class}/{$function}" with {($|%)} $ or % placeholder values. * always make sure your htaccess rule reflects the rewrite rule and vice versa and that service * values are always rewritten to $_GET "service" parameter. the second parameter defines whether * to return the url as relative or absolute or if not set will look for global option RELATIVE_TARGETS * * @error 14113 * @param null|string $service expects optional the service method/function and class in dot notation * @param null|bool $relative defined whether to return path absolute or relative * @return string * @throws Xapp_Rpc_Smd_Exception */ protected function getTarget($service = null, $relative = null) { $class = null; $function = null; $separator = xapp_get_option(self::CLASS_METHOD_SEPARATOR, $this); $url = Xapp_Rpc_Request::url(null, -1); $host = rtrim($url['host'], '/ '); $path = trim($url['path'], '/ '); if (preg_match('/(\\.([^\\/]+|$))/i', $path, $m, PREG_OFFSET_CAPTURE)) { if (isset($m[1]) && is_array($m[1])) { $path = substr($path, 0, $m[1][1] + strlen($m[1][0])); } } if (xapp_is_option(self::REWRITE_URL, $this) && strpos($path, 'index.') !== false) { $path = substr($path, 0, strpos($path, 'index.')); } if ($service !== null) { if (strpos($service, $separator) !== false) { $class = substr($service, 0, strpos($service, $separator)); $function = substr($service, strpos($service, $separator) + 1); } else { $class = null; $function = $service; } } if ($service !== null) { if (xapp_is_option(self::REWRITE_URL, $this)) { $_url = xapp_get_option(self::REWRITE_URL, $this); if (is_bool($_url) && $_url) { $_url = rtrim($path, '/') . '/{$class}{$separator}{$function}'; } $_url = trim($_url, '/ '); if (($_url = parse_url($_url)) !== false) { $path = trim(preg_replace(array('/\\{(?:\\$|\\%)([^\\}]+)\\}/ie', '/\\/+/'), array("\$\$1", '/'), $_url['path']), '/ '); } else { throw new Xapp_Rpc_Smd_Exception(xapp_sprintf(_("smd rewrite rule: %s is not a valid url or url path value"), $_url), 1411301); } } else { $path = $path . "?service={$service}"; } } if ($relative === null) { $relative = xapp_get_option(self::RELATIVE_TARGETS, $this); } if ((bool) $relative) { return '/' . ltrim($path, '/ '); } else { return $url['scheme'] . '://' . $host . (isset($url['port']) && !empty($url['port']) ? ':' . $url['port'] : '') . '/' . $path; } }
/** * executing requested service/call by validating call and invoking requested class/method or function * * @error 14605 * @param array $call expects the call to execute * @return void * @throws Exception */ protected function execute($call) { $data = array(); $version = $this->request()->getVersion($call[3], true); if (!is_null($version)) { $data[$version[0]] = $version[1]; } try { $this->validate($call); $data['result'] = $this->invoke($call, array_key_exists('params', $call[3]) ? $call[3]['params'] : null); if (version_compare((string) xapp_get_option(self::VERSION), '2.0', '<')) { $data['error'] = null; } } catch (Exception $e) { if (xapp_get_option(self::COMPLY_TO_JSONRPC_1_2, $this)) { if (array_key_exists($e->getCode(), $this->codeMap)) { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, $this->codeMap[$e->getCode()], $response); } else { xapp_set_option(Xapp_Rpc_Response::STATUS_CODE, 500, $response); } } if (xapp_is_option(self::EXCEPTION_CALLBACK, $this)) { $e = call_user_func_array(xapp_get_option(self::EXCEPTION_CALLBACK, $this), array(&$e, $this, $call)); if ($e instanceof Exception) { $data['error'] = $this->compileError($e); } else { $data['error'] = $e; } } else { if (sizeof($this->_calls) === 1) { throw $e; } else { $data['error'] = $this->compileError($e); } } } if (array_key_exists('id', $call[3])) { $data['id'] = $call[3]['id']; } $this->_data[] = $data; $data = null; }
/** * invoke class/method or function via rpc or outside rpc functionality for testing purposes. the first parameter * thus can be a valid callable when used outside of rpc service or the internal array containing all call parameters * from concrete rpc server implementation. throws exception with extended exception properties if in debug mode. * summarizes all caught applications exceptions when invoking callable to a general application error so sensitive * error messages can be omitted. when invoked with named parameters will reorder parameters to reflect * order of method/function call since call_user_func_array needs to pass parameter named or unnamed in * correct order * * @error 14211 * @param array|callable $call expects either valid callable or array with all call parameters * @param array $params expects the parameters to pass to function/method to invoke * @return mixed * @throws Xapp_Rpc_Server_Exception * @throws Xapp_Rpc_Fault * @throws Exception */ public function invoke($call, $params = null) { $key = null; $hash = null; $class = null; $return = null; $result = null; $callable = null; try { //invoke from outside of rpc service if (is_callable($call)) { $callable = $call; if (is_array($call)) { $call = array(null, $call[1], is_object($call[0]) ? get_class($call[0]) : $call[0]); $class = new ReflectionClass($call[2]); } else { if (strpos((string) $call, '::') !== false) { $call = array(null, substr($call, strpos($call, '::') + 2), substr($call, 0, strpos($call, '::'))); $class = new ReflectionClass($call[2]); } else { $call = array(null, $call, $call); $class = null; } } } else { if (is_array($call)) { if ($call[2] !== null) { if (xapp_get_option(self::NAMESPACE_IDENTIFIER, $this) === NAMESPACE_SEPARATOR) { if (strpos($call[2], NAMESPACE_SEPARATOR) !== false) { $call[2] = NAMESPACE_SEPARATOR . str_replace(array('\\', '/', '_', '.'), xapp_get_option(self::NAMESPACE_IDENTIFIER, $this), trim($call[2], ' ' . NAMESPACE_SEPARATOR)); } else { $call[2] = str_replace(array('/', '_', '.'), xapp_get_option(self::NAMESPACE_IDENTIFIER, $this), trim($call[2])); } } else { $call[2] = str_replace(array('\\', '/', '_', '.'), xapp_get_option(self::NAMESPACE_IDENTIFIER, $this), trim($call[2])); } $key = "{$call[2]}.{$call[1]}"; try { if (array_key_exists(strtolower($call[2]), $this->_objects)) { $class = new ReflectionClass($this->_objects[strtolower($call[2])]); } else { $class = new ReflectionClass($call[2]); } if ($class->hasMethod($call[1])) { $method = $class->getMethod($call[1]); if ($method->isPublic()) { if ($method->isStatic()) { $callable = array($class->getName(), $call[1]); } else { $callable = array(array_key_exists(strtolower($call[2]), $this->_objects) ? $this->_objects[strtolower($call[2])] : $class->newInstance(), $call[1]); } } else { Xapp_Rpc_Fault::t("method: {$call[1]} of class: {$call[2]} is not public", array(1421105, -32601)); } } else { Xapp_Rpc_Fault::t("method: {$call[1]} of class: {$call[2]} does not exist", array(1421104, -32601)); } } catch (ReflectionException $e) { throw new Xapp_Rpc_Server_Exception(xapp_sprintf(_("unable to initialize class due to reflection error: %d, %s"), $e->getCode(), $e->getMessage()), 1421103); } } else { $key = $call[1]; $callable = $call[1]; } } else { throw new Xapp_Rpc_Server_Exception(_("invalid callable passed to invoke method"), 1421106); } } if (is_callable($callable)) { if (is_array($callable) && is_object($callable[0])) { $this->_class = $callable[0]; } if (!is_null($key) && !is_null($params) && array_values((array) $params) !== (array) $params) { $tmp = array(); $arr = (array) $params; $map = $this->smd()->get($key); if (!is_null($map)) { foreach ((array) $map->parameters as $p) { if (array_key_exists($p->name, $arr)) { $tmp[$p->name] = $arr[$p->name]; } } } $params = $tmp; $tmp = null; $arr = null; $map = null; } if (!is_null($key) && xapp_is_option(self::CACHE, $this) && preg_match(Xapp_Rpc::regex(xapp_get_option(self::CACHE_SERVICES, $this)), $call[0])) { if (xapp_get_option(self::CACHE_BY_TRANSACTION_ID, $this)) { $hash = $call[3]['id']; } else { $hash = Xapp_Cache::hash(serialize($call[0]) . serialize($params)); } if (xapp_get_option(self::CACHE, $this)->has($hash)) { return xapp_get_option(self::CACHE, $this)->get($hash); } } if (!xapp_get_option(self::PARAMS_AS_ARRAY, $this)) { $params = xapp_array_to_object($params); } xapp_event('xapp.rpc.server.beforeCall', array($this, array(&$result, $call[1], $call[2], $params))); if ($this->hasClass() && $class->implementsInterface('Xapp_Rpc_Interface_Callable')) { $return = $this->getClass()->onBeforeCall($this, array(&$result, $call[1], $call[2], &$params)); } if ($return !== false && $result === null) { $this->response()->result($result = call_user_func_array($callable, (array) $params)); } if ($return === false && $this->hasClass() && $class->implementsInterface('Xapp_Rpc_Interface_Callable')) { $return = $this->getClass()->onAbort($this, array(&$result, $call[1], $call[2], &$params)); } else { $return = null; } if ($return !== null) { $result = $return; } if ($this->hasClass() && $class->implementsInterface('Xapp_Rpc_Interface_Callable')) { $return = $this->getClass()->onAfterCall($this, array(&$result)); } if ($return !== null) { $result = $return; } xapp_event('xapp.rpc.server.afterCall', array($this, array(&$result))); if (!is_null($hash)) { if (!xapp_get_option(self::CACHE, $this)->has($hash)) { xapp_get_option(self::CACHE, $this)->set($hash, $result); } } $callable = null; $return = null; $params = null; $class = null; $hash = null; return $result; } else { throw new Xapp_Rpc_Server_Exception(_("unable to invoke function since first argument is not a callable"), 1421102); } } catch (Exception $e) { if ($this->hasClass() && $class->implementsInterface('Xapp_Rpc_Interface_Callable')) { $error = $this->getClass()->onError($this, $e); if ($error instanceof Exception) { $e = $error; $error = null; } } if (!$e instanceof Xapp_Rpc_Server_Exception) { $data = array(); $debug = xapp_get_option(self::DEBUG, $this); if (is_bool($debug) && $debug === true || is_int($debug) && $debug === 1) { $data['message'] = $e->getMessage(); $data['code'] = $e->getCode(); $data['class'] = get_class($e); $data['file'] = $e->getFile(); $data['line'] = $e->getLine(); if ($e instanceof ErrorException) { $data['severity'] = $e->getSeverity(); } if ($e instanceof Xapp_Rpc_Fault && $e->hasData()) { $data['data'] = $e->getData(); } } else { if (is_int($debug) && $debug === 2) { $data['message'] = $e->getMessage(); $data['code'] = $e->getCode(); if ($e instanceof ErrorException) { $data['severity'] = $e->getSeverity(); } if ($e instanceof Xapp_Rpc_Fault && $e->hasData()) { $data['data'] = $e->getData(); } } } if (xapp_is_option(self::APPLICATION_ERROR, $this)) { if ($e instanceof Xapp_Rpc_Fault && $e->hasFault()) { $f = $e->getFault(); } else { $f = -32500; } if (($code = (int) $e->getCode()) !== 0) { Xapp_Rpc_Fault::t(xapp_sprintf("application error: %d", array($code)), array(1421101, $f), XAPP_ERROR_IGNORE, $data); } else { Xapp_Rpc_Fault::t("application error", array(1421101, $f), XAPP_ERROR_IGNORE, $data); } } else { if ((bool) $debug) { Xapp_Rpc_Fault::t($e->getMessage(), $e->getCode(), XAPP_ERROR_IGNORE, $data); } else { throw $e; } } } else { throw $e; } } return null; }