From fe1d88aa0af0a40e640f9ea3a455ee5c1caad5dc Mon Sep 17 00:00:00 2001 From: gggeek Date: Wed, 18 Jan 2023 00:26:11 +0000 Subject: [PATCH] allow server to stick more closely to accepted-charset when it hs mbstring onboard --- src/Server.php | 32 +++++++++++++++++++++++--------- tests/6HTTPTest.php | 28 ++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/Server.php b/src/Server.php index c7c06719..51b6f01f 100644 --- a/src/Server.php +++ b/src/Server.php @@ -531,22 +531,36 @@ class Server if ($this->response_charset_encoding == 'auto') { $respEncoding = ''; if (isset($_SERVER['HTTP_ACCEPT_CHARSET'])) { - // here we should check if we can match the client-requested encoding with the encodings we know we can generate. - /// @todo we should parse q=0.x preferences instead of getting first charset specified... - $clientAcceptedCharsets = explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET'])); + // here we check if we can match the client-requested encoding with the encodings we know we can generate. + // we parse q=0.x preferences instead of preferring the first charset specified + $clientAcceptedCharsets = array(); + foreach(explode(',', strtoupper($_SERVER['HTTP_ACCEPT_CHARSET'])) as $c) { + if (preg_match('/^([^;]+);Q=([0-9.]+)/', $c, $matches)) { + $c = $matches[1]; + $w = $matches[2]; + } else { + $c = preg_replace('/;.*/', '', $c); + $w = 1; + } + $clientAcceptedCharsets[(trim($c))] = $w; + } + arsort($clientAcceptedCharsets); + $clientAcceptedCharsets = array_keys($clientAcceptedCharsets); + // Give preference to internal encoding -/// @todo if mbstring is enabled, we can support other charsets too! Add a method to the Encoder! $knownCharsets = array(PhpXmlRpc::$xmlrpc_internalencoding, 'UTF-8', 'ISO-8859-1', 'US-ASCII'); + // if mbstring is enabled, we can support other charsets too! + /// @todo add a method to the Charset helper to retrieve this list (and remove from it junk entries) + if (function_exists('mb_list_encodings')) { + $knownCharsets = array_unique(array_merge($knownCharsets, mb_list_encodings())); + } foreach ($knownCharsets as $charset) { foreach ($clientAcceptedCharsets as $accepted) { - if (strpos($accepted, $charset) === 0) { + if ($accepted == strtoupper($charset)) { $respEncoding = $charset; - break; + break 2; } } - if ($respEncoding) { - break; - } } } } else { diff --git a/tests/6HTTPTest.php b/tests/6HTTPTest.php index 4293e288..cef0965e 100644 --- a/tests/6HTTPTest.php +++ b/tests/6HTTPTest.php @@ -98,6 +98,34 @@ class HTTPTest extends ServerTest } } + public function testAcceptCharset() + { + if (!function_exists('mb_list_encodings')) + { + $this->markTestSkipped('mbstring missing: cannot test accept-charset'); + return; + } + + $r = new \PhpXmlRpc\Request('examples.stringecho', array(new \PhpXmlRpc\Value('€'))); + //chr(164) + + $originalEncoding = \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding; + \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = 'UTF-8'; + + $this->addQueryParams(array('RESPONSE_ENCODING' => 'auto')); + $this->client->accepted_charset_encodings = array( + 'utf-1234;q=0.1', + 'windows-1252;q=0.8' + ); + $v = $this->send($r, 0, true); + $h = $v->httpResponse(); + $this->assertEquals('text/xml; charset=Windows-1252', $h['headers']['content-type']); + if ($v) { + $this->assertEquals('€', $v->value()->scalarval()); + } + \PhpXmlRpc\PhpXmlRpc::$xmlrpc_internalencoding = $originalEncoding; + } + /** * @dataProvider getSingleHttpTestMethods * @param string $method -- 2.47.0