allow server to stick more closely to accepted-charset when it hs mbstring onboard
authorgggeek <giunta.gaetano@gmail.com>
Wed, 18 Jan 2023 00:26:11 +0000 (00:26 +0000)
committergggeek <giunta.gaetano@gmail.com>
Wed, 18 Jan 2023 00:26:11 +0000 (00:26 +0000)
src/Server.php
tests/6HTTPTest.php

index c7c0671..51b6f01 100644 (file)
@@ -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 {
index 4293e28..cef0965 100644 (file)
@@ -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