You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

483 lines
19 KiB
PHP

<?php
/**********************************************************\
| |
| hprose |
| |
| Official WebSite: http://www.hprose.com/ |
| http://www.hprose.net/ |
| http://www.hprose.org/ |
| |
\**********************************************************/
/**********************************************************\
* *
* HproseHttpServer.php *
* *
* hprose http server library for php5. *
* *
* LastModified: Nov 13, 2013 *
* Author: Ma Bingyao <andot@hprfc.com> *
* *
\**********************************************************/
require_once('HproseCommon.php');
require_once('HproseIO.php');
class HproseHttpServer {
private $errorTable = array(E_ERROR => 'Error',
E_WARNING => 'Warning',
E_PARSE => 'Parse Error',
E_NOTICE => 'Notice',
E_CORE_ERROR => 'Core Error',
E_CORE_WARNING => 'Core Warning',
E_COMPILE_ERROR => 'Compile Error',
E_COMPILE_WARNING => 'Compile Warning',
E_USER_ERROR => 'User Error',
E_USER_WARNING => 'User Warning',
E_USER_NOTICE => 'User Notice',
E_STRICT => 'Run-time Notice',
E_RECOVERABLE_ERROR => 'Error');
private $functions;
private $funcNames;
private $resultModes;
private $simpleModes;
private $debug;
private $crossDomain;
private $P3P;
private $get;
private $input;
private $output;
private $error;
private $filter;
private $simple;
public $onBeforeInvoke;
public $onAfterInvoke;
public $onSendHeader;
public $onSendError;
public function __construct() {
$this->functions = array();
$this->funcNames = array();
$this->resultModes = array();
$this->simpleModes = array();
$this->debug = false;
$this->crossDomain = false;
$this->P3P = false;
$this->get = true;
$this->filter = NULL;
$this->simple = false;
$this->error_types = E_ALL & ~E_NOTICE;
$this->onBeforeInvoke = NULL;
$this->onAfterInvoke = NULL;
$this->onSendHeader = NULL;
$this->onSendError = NULL;
}
/*
__filterHandler & __errorHandler must be public,
however we should never call them directly.
*/
public function __filterHandler($data) {
if (preg_match('/<b>.*? error<\/b>:(.*?)<br/', $data, $match)) {
if ($this->debug) {
$error = preg_replace('/<.*?>/', '', $match[1]);
}
else {
$error = preg_replace('/ in <b>.*<\/b>$/', '', $match[1]);
}
$data = HproseTags::TagError .
HproseFormatter::serialize(trim($error), true) .
HproseTags::TagEnd;
}
if ($this->filter) $data = $this->filter->outputFilter($data);
return $data;
}
public function __errorHandler($errno, $errstr, $errfile, $errline) {
if ($this->debug) {
$errstr .= " in $errfile on line $errline";
}
$this->error = $this->errorTable[$errno] . ": " . $errstr;
$this->sendError();
return true;
}
private function sendHeader() {
if ($this->onSendHeader) {
call_user_func($this->onSendHeader);
}
header("Content-Type: text/plain");
if ($this->P3P) {
header('P3P: CP="CAO DSP COR CUR ADM DEV TAI PSA PSD IVAi IVDi ' .
'CONi TELo OTPi OUR DELi SAMi OTRi UNRi PUBi IND PHY ONL ' .
'UNI PUR FIN COM NAV INT DEM CNT STA POL HEA PRE GOV"');
}
if ($this->crossDomain) {
if (array_key_exists('HTTP_ORIGIN', $_SERVER) && $_SERVER['HTTP_ORIGIN'] != "null") {
header("Access-Control-Allow-Origin: " . $_SERVER['HTTP_ORIGIN']);
header("Access-Control-Allow-Credentials: true");
}
else {
header('Access-Control-Allow-Origin: *');
}
}
}
private function sendError() {
if ($this->onSendError) {
call_user_func($this->onSendError, $this->error);
}
ob_clean();
$this->output->write(HproseTags::TagError);
$writer = new HproseSimpleWriter($this->output);
$writer->writeString($this->error);
$this->output->write(HproseTags::TagEnd);
ob_end_flush();
}
private function doInvoke() {
$simpleReader = new HproseSimpleReader($this->input);
do {
$functionName = $simpleReader->readString(true);
$aliasName = strtolower($functionName);
$resultMode = HproseResultMode::Normal;
if (array_key_exists($aliasName, $this->functions)) {
$function = $this->functions[$aliasName];
$resultMode = $this->resultModes[$aliasName];
$simple = $this->simpleModes[$aliasName];
}
elseif (array_key_exists('*', $this->functions)) {
$function = $this->functions['*'];
$resultMode = $this->resultModes['*'];
$simple = $this->resultModes['*'];
}
else {
throw new HproseException("Can't find this function " . $functionName . "().");
}
if ($simple === NULL) $simple = $this->simple;
$writer = ($simple ? new HproseSimpleWriter($this->output) : new HproseWriter($this->output));
$args = array();
$byref = false;
$tag = $simpleReader->checkTags(array(HproseTags::TagList,
HproseTags::TagEnd,
HproseTags::TagCall));
if ($tag == HproseTags::TagList) {
$reader = new HproseReader($this->input);
$args = &$reader->readList();
$tag = $reader->checkTags(array(HproseTags::TagTrue,
HproseTags::TagEnd,
HproseTags::TagCall));
if ($tag == HproseTags::TagTrue) {
$byref = true;
$tag = $reader->checkTags(array(HproseTags::TagEnd,
HproseTags::TagCall));
}
}
if ($this->onBeforeInvoke) {
call_user_func($this->onBeforeInvoke, $functionName, $args, $byref);
}
if (array_key_exists('*', $this->functions) && ($function === $this->functions['*'])) {
$arguments = array($functionName, &$args);
}
elseif ($byref) {
$arguments = array();
for ($i = 0; $i < count($args); $i++) {
$arguments[$i] = &$args[$i];
}
}
else {
$arguments = $args;
}
$result = call_user_func_array($function, $arguments);
if ($this->onAfterInvoke) {
call_user_func($this->onAfterInvoke, $functionName, $args, $byref, $result);
}
// some service functions/methods may echo content, we need clean it
ob_clean();
if ($resultMode == HproseResultMode::RawWithEndTag) {
$this->output->write($result);
return;
}
elseif ($resultMode == HproseResultMode::Raw) {
$this->output->write($result);
}
else {
$this->output->write(HproseTags::TagResult);
if ($resultMode == HproseResultMode::Serialized) {
$this->output->write($result);
}
else {
$writer->reset();
$writer->serialize($result);
}
if ($byref) {
$this->output->write(HproseTags::TagArgument);
$writer->reset();
$writer->writeList($args);
}
}
} while ($tag == HproseTags::TagCall);
$this->output->write(HproseTags::TagEnd);
ob_end_flush();
}
private function doFunctionList() {
$functions = array_values($this->funcNames);
$writer = new HproseSimpleWriter($this->output);
$this->output->write(HproseTags::TagFunctions);
$writer->writeList($functions);
$this->output->write(HproseTags::TagEnd);
ob_end_flush();
}
private function getDeclaredOnlyMethods($class) {
$all = get_class_methods($class);
if ($parent_class = get_parent_class($class)) {
$inherit = get_class_methods($parent_class);
$result = array_diff($all, $inherit);
}
else {
$result = $all;
}
return $result;
}
public function addMissingFunction($function, $resultMode = HproseResultMode::Normal, $simple = NULL) {
$this->addFunction($function, '*', $resultMode, $simple);
}
public function addFunction($function, $alias = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
if (is_callable($function)) {
if ($alias === NULL) {
if (is_string($function)) {
$alias = $function;
}
else {
$alias = $function[1];
}
}
if (is_string($alias)) {
$aliasName = strtolower($alias);
$this->functions[$aliasName] = $function;
$this->funcNames[$aliasName] = $alias;
$this->resultModes[$aliasName] = $resultMode;
$this->simpleModes[$aliasName] = $simple;
}
else {
throw new HproseException('Argument alias is not a string');
}
}
else {
throw new HproseException('Argument function is not a callable variable');
}
}
public function addFunctions($functions, $aliases = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
$aliases_is_null = ($aliases === NULL);
$count = count($functions);
if (!$aliases_is_null && $count != count($aliases)) {
throw new HproseException('The count of functions is not matched with aliases');
}
for ($i = 0; $i < $count; $i++) {
$function = $functions[$i];
if ($aliases_is_null) {
$this->addFunction($function, NULL, $resultMode, $simple);
}
else {
$this->addFunction($function, $aliases[$i], $resultMode, $simple);
}
}
}
public function addMethod($methodname, $belongto, $alias = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
if ($alias === NULL) {
$alias = $methodname;
}
if (is_string($belongto)) {
$this->addFunction(array($belongto, $methodname), $alias, $resultMode, $simple);
}
else {
$this->addFunction(array(&$belongto, $methodname), $alias, $resultMode, $simple);
}
}
public function addMethods($methods, $belongto, $aliases = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
$aliases_is_null = ($aliases === NULL);
$count = count($methods);
if (is_string($aliases)) {
$aliasPrefix = $aliases;
$aliases = array();
foreach ($methods as $name) {
$aliases[] = $aliasPrefix . '_' . $name;
}
}
if (!$aliases_is_null && $count != count($aliases)) {
throw new HproseException('The count of methods is not matched with aliases');
}
for ($i = 0; $i < $count; $i++) {
$method = $methods[$i];
if (is_string($belongto)) {
$function = array($belongto, $method);
}
else {
$function = array(&$belongto, $method);
}
if ($aliases_is_null) {
$this->addFunction($function, $method, $resultMode, $simple);
}
else {
$this->addFunction($function, $aliases[$i], $resultMode, $simple);
}
}
}
public function addInstanceMethods($object, $class = NULL, $aliasPrefix = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
if ($class === NULL) $class = get_class($object);
$this->addMethods($this->getDeclaredOnlyMethods($class), $object, $aliasPrefix, $resultMode, $simple);
}
public function addClassMethods($class, $execclass = NULL, $aliasPrefix = NULL, $resultMode = HproseResultMode::Normal, $simple = NULL) {
if ($execclass === NULL) $execclass = $class;
$this->addMethods($this->getDeclaredOnlyMethods($class), $execclass, $aliasPrefix, $resultMode, $simple);
}
public function add() {
$args_num = func_num_args();
$args = func_get_args();
switch ($args_num) {
case 1: {
if (is_callable($args[0])) {
return $this->addFunction($args[0]);
}
elseif (is_array($args[0])) {
return $this->addFunctions($args[0]);
}
elseif (is_object($args[0])) {
return $this->addInstanceMethods($args[0]);
}
elseif (is_string($args[0])) {
return $this->addClassMethods($args[0]);
}
break;
}
case 2: {
if (is_callable($args[0]) && is_string($args[1])) {
return $this->addFunction($args[0], $args[1]);
}
elseif (is_string($args[0])) {
if (is_string($args[1]) && !is_callable(array($args[1], $args[0]))) {
if (class_exists($args[1])) {
return $this->addClassMethods($args[0], $args[1]);
}
else {
return $this->addClassMethods($args[0], NULL, $args[1]);
}
}
return $this->addMethod($args[0], $args[1]);
}
elseif (is_array($args[0])) {
if (is_array($args[1])) {
return $this->addFunctions($args[0], $args[1]);
}
else {
return $this->addMethods($args[0], $args[1]);
}
}
elseif (is_object($args[0])) {
return $this->addInstanceMethods($args[0], $args[1]);
}
break;
}
case 3: {
if (is_callable($args[0]) && is_null($args[1]) && is_string($args[2])) {
return $this->addFunction($args[0], $args[2]);
}
elseif (is_string($args[0]) && is_string($args[2])) {
if (is_string($args[1]) && !is_callable(array($args[0], $args[1]))) {
return $this->addClassMethods($args[0], $args[1], $args[2]);
}
else {
return $this->addMethod($args[0], $args[1], $args[2]);
}
}
elseif (is_array($args[0])) {
if (is_null($args[1]) && is_array($args[2])) {
return $this->addFunctions($args[0], $args[2]);
}
else {
return $this->addMethods($args[0], $args[1], $args[2]);
}
}
elseif (is_object($args[0])) {
return $this->addInstanceMethods($args[0], $args[1], $args[2]);
}
break;
}
throw new HproseException('Wrong arguments');
}
}
public function isDebugEnabled() {
return $this->debug;
}
public function setDebugEnabled($enable = true) {
$this->debug = $enable;
}
public function isCrossDomainEnabled() {
return $this->crossDomain;
}
public function setCrossDomainEnabled($enable = true) {
$this->crossDomain = $enable;
}
public function isP3PEnabled() {
return $this->P3P;
}
public function setP3PEnabled($enable = true) {
$this->P3P = $enable;
}
public function isGetEnabled() {
return $this->get;
}
public function setGetEnabled($enable = true) {
$this->get = $enable;
}
public function getFilter() {
return $this->filter;
}
public function setFilter($filter) {
$this->filter = $filter;
}
public function getSimpleMode() {
return $this->simple;
}
public function setSimpleMode($simple = true) {
$this->simple = $simple;
}
public function getErrorTypes() {
return $this->error_types;
}
public function setErrorTypes($error_types) {
$this->error_types = $error_types;
}
public function handle() {
if (!isset($HTTP_RAW_POST_DATA)) $HTTP_RAW_POST_DATA = file_get_contents("php://input");
if ($this->filter) $HTTP_RAW_POST_DATA = $this->filter->inputFilter($HTTP_RAW_POST_DATA);
$this->input = new HproseStringStream($HTTP_RAW_POST_DATA);
$this->output = new HproseFileStream(fopen('php://output', 'wb'));
set_error_handler(array(&$this, '__errorHandler'), $this->error_types);
ob_start(array(&$this, "__filterHandler"));
ob_implicit_flush(0);
ob_clean();
$this->sendHeader();
if (($_SERVER['REQUEST_METHOD'] == 'GET') and $this->get) {
return $this->doFunctionList();
}
elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
try {
switch ($this->input->getc()) {
case HproseTags::TagCall: return $this->doInvoke();
case HproseTags::TagEnd: return $this->doFunctionList();
default: throw new HproseException("Wrong Request: \r\n" . $HTTP_RAW_POST_DATA);
}
}
catch (Exception $e) {
$this->error = $e->getMessage();
if ($this->debug) {
$this->error .= "\nfile: " . $e->getFile() .
"\nline: " . $e->getLine() .
"\ntrace: " . $e->getTraceAsString();
}
$this->sendError();
}
}
$this->input->close();
$this->output->close();
}
public function start() {
$this->handle();
}
}
?>