Skip to content

Commit 6338bd8

Browse files
committed
add 3 new methods to make life tolerable for jsonrpc subclasses
1 parent e7de754 commit 6338bd8

3 files changed

Lines changed: 87 additions & 50 deletions

File tree

NEWS.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
- improved: allow to force usage of HTTP 1.1. when using curl for calls, via usage of 'http11_only'
44

5+
- improved: added new methods: `Server::generatePayload($resp, $respCharset)`,
6+
`Server::printPayload($payload, $resp->getContentType(), $respEncoding)` and `HTTP::setAcceptedStatusCodes($statusCodes)`
7+
to help subclasses such as the Json-Rpc server and request
8+
59

610
## XML-RPC for PHP version 4.11.3 - 2025/10/3
711

src/Helper/Http.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ class Http
1010
{
1111
use LoggerAware;
1212

13+
protected $acceptedStatusCodes = array('200');
14+
1315
/**
1416
* Decode a string that is encoded with "chunked" transfer encoding as defined in RFC 2068 par. 19.4.6.
1517
* Code shamelessly stolen from nusoap library by Dietrich Ayala.
@@ -131,7 +133,7 @@ public function parseResponseHeaders(&$data, $headersProcessed = false, $debug =
131133
$httpResponse['status_code'] = $matches[2];
132134
}
133135

134-
if ($httpResponse['status_code'] !== '200') {
136+
if (!in_array($httpResponse['status_code'], $this->acceptedStatusCodes)) {
135137
$errstr = substr($data, 0, strpos($data, "\n") - 1);
136138
$this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': HTTP error, got response: ' . $errstr);
137139
throw new HttpException(PhpXmlRpc::$xmlrpcstr['http_error'] . ' (' . $errstr . ')', PhpXmlRpc::$xmlrpcerr['http_error'], null, $httpResponse['status_code']);
@@ -280,4 +282,13 @@ public function parseAcceptHeader($header)
280282
arsort($accepted);
281283
return array_keys($accepted);
282284
}
285+
286+
/**
287+
* @param string[] $statusCodes
288+
* @return void
289+
*/
290+
public function setAcceptedStatusCodes($statusCodes)
291+
{
292+
$this->acceptedStatusCodes = $statusCodes;
293+
}
283294
}

src/Server.php

Lines changed: 71 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -390,59 +390,13 @@ public function service($data = null, $returnPayload = false)
390390
static::$_xmlrpcs_occurred_errors . "+++END+++");
391391
}
392392

393-
$header = $resp->xml_header($respCharset);
394-
if ($this->debug > 0) {
395-
$header .= $this->serializeDebug($respCharset);
396-
}
397-
398-
// Do not create response serialization if it has already happened. Helps to build json magic
399-
/// @todo what if the payload was created targeting a different charset than $respCharset?
400-
/// Also, if we do not call serialize(), the request will not set its content-type to have the charset declared
401-
$payload = $resp->getPayload();
402-
if (empty($payload)) {
403-
$payload = $resp->serialize($respCharset);
404-
}
405-
$payload = $header . $payload;
393+
$payload = $this->generatePayload($resp, $respCharset);
406394

407395
if ($returnPayload) {
408396
return $payload;
409397
}
410398

411-
// if we get a warning/error that has output some text before here, then we cannot
412-
// add a new header. We cannot say we are sending xml, either...
413-
if (!headers_sent()) {
414-
header('Content-Type: ' . $resp->getContentType());
415-
// we do not know if client actually told us an accepted charset, but if it did we have to tell it what we did
416-
header("Vary: Accept-Charset");
417-
418-
// http compression of output: only if we can do it, and we want to do it, and client asked us to,
419-
// and php ini settings do not force it already
420-
$phpNoSelfCompress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
421-
if ($this->compress_response && $respEncoding != '' && $phpNoSelfCompress) {
422-
if (strpos($respEncoding, 'gzip') !== false && function_exists('gzencode')) {
423-
$payload = gzencode($payload);
424-
header("Content-Encoding: gzip");
425-
header("Vary: Accept-Encoding");
426-
} elseif (strpos($respEncoding, 'deflate') !== false && function_exists('gzcompress')) {
427-
$payload = gzcompress($payload);
428-
header("Content-Encoding: deflate");
429-
header("Vary: Accept-Encoding");
430-
}
431-
}
432-
433-
// Do not output content-length header if php is compressing output for us: it will mess up measurements.
434-
// Note that Apache/mod_php will add (and even alter!) the Content-Length header on its own, but only for
435-
// responses up to 8000 bytes
436-
if ($phpNoSelfCompress) {
437-
header('Content-Length: ' . (int)strlen($payload));
438-
}
439-
} else {
440-
/// @todo allow the user to easily subclass this in a way which allows the resp. headers to be already sent
441-
/// by now without flagging it as an error. Possibly check for presence of Content-Type header
442-
$this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
443-
}
444-
445-
print $payload;
399+
$this->printPayload($payload, $resp->getContentType(), $respEncoding);
446400

447401
// return response, in case subclasses want it
448402
return $resp;
@@ -673,7 +627,7 @@ protected function parseRequestHeaders(&$data, &$reqEncoding, &$respEncoding, &$
673627
* @return Response
674628
* @throws \Exception in case the executed method does throw an exception (and depending on server configuration)
675629
*
676-
* @todo either rename this function or move the 'execute' part out of it...
630+
* @todo either rename this function or, probably better, move the 'execute' part out of it...
677631
*/
678632
public function parseRequest($data, $reqEncoding = '')
679633
{
@@ -968,6 +922,74 @@ protected function execute($req, $params = null, $paramTypes = null)
968922
return $r;
969923
}
970924

925+
/**
926+
* @param Response $resp
927+
* @param string $respCharset
928+
* @return string
929+
*/
930+
protected function generatePayload($resp, $respCharset)
931+
{
932+
$header = $resp->xml_header($respCharset);
933+
if ($this->debug > 0) {
934+
$header .= $this->serializeDebug($respCharset);
935+
}
936+
937+
// Do not create response serialization if it has already happened. Helps to build json magic
938+
/// @todo what if the payload was created targeting a different charset than $respCharset?
939+
/// Also, if we do not call serialize(), the request will not set its content-type to have the charset declared
940+
$payload = $resp->getPayload();
941+
if (empty($payload)) {
942+
$payload = $resp->serialize($respCharset);
943+
}
944+
945+
return $header . $payload;
946+
}
947+
948+
/**
949+
* @param string $payload
950+
* @param string $respContentType
951+
* @param string $respEncoding
952+
* @return void
953+
*/
954+
protected function printPayload($payload, $respContentType, $respEncoding)
955+
{
956+
// if we get a warning/error that has output some text before here, then we cannot
957+
// add a new header. We cannot say we are sending xml, either...
958+
if (!headers_sent()) {
959+
header('Content-Type: ' . $respContentType);
960+
// we do not know if client actually told us an accepted charset, but if it did we have to tell it what we did
961+
header("Vary: Accept-Charset");
962+
963+
// http compression of output: only if we can do it, and we want to do it, and client asked us to,
964+
// and php ini settings do not force it already
965+
$phpNoSelfCompress = !ini_get('zlib.output_compression') && (ini_get('output_handler') != 'ob_gzhandler');
966+
if ($this->compress_response && $respEncoding != '' && $phpNoSelfCompress) {
967+
if (strpos($respEncoding, 'gzip') !== false && function_exists('gzencode')) {
968+
$payload = gzencode($payload);
969+
header("Content-Encoding: gzip");
970+
header("Vary: Accept-Encoding");
971+
} elseif (strpos($respEncoding, 'deflate') !== false && function_exists('gzcompress')) {
972+
$payload = gzcompress($payload);
973+
header("Content-Encoding: deflate");
974+
header("Vary: Accept-Encoding");
975+
}
976+
}
977+
978+
// Do not output content-length header if php is compressing output for us: it will mess up measurements.
979+
// Note that Apache/mod_php will add (and even alter!) the Content-Length header on its own, but only for
980+
// responses up to 8000 bytes
981+
if ($phpNoSelfCompress) {
982+
header('Content-Length: ' . (int)strlen($payload));
983+
}
984+
} else {
985+
/// @todo allow the user to easily subclass this in a way which allows the resp. headers to be already sent
986+
/// by now without flagging it as an error. Possibly check for presence of Content-Type header
987+
$this->getLogger()->error('XML-RPC: ' . __METHOD__ . ': http headers already sent before response is fully generated. Check for php warning or error messages');
988+
}
989+
990+
print $payload;
991+
}
992+
971993
/**
972994
* Registered as callback for when the XMLParser has found the name of the method to execute.
973995
* Handling that early allows to 1. stop parsing the rest of the xml if there is no such method registered, and

0 commit comments

Comments
 (0)