From 45484cdd476778076ed1eafd41cedc010d73f756 Mon Sep 17 00:00:00 2001 From: watanabe Date: Thu, 13 Jun 2013 10:20:58 +0900 Subject: [PATCH] changed opPDODatabaseSessionStorage::write() to replace 4 bytes UTF-8 characters to U+FFFD in MySQL non binary string column (refs #3388) --- lib/user/opPDODatabaseSessionStorage.class.php | 51 ++++++++++++++++++++++ .../user/opPDODatabaseSessionStrageTest.class.php | 37 ++++++++++++++++ 2 files changed, 88 insertions(+) create mode 100644 test/unit/user/opPDODatabaseSessionStrageTest.class.php diff --git a/lib/user/opPDODatabaseSessionStorage.class.php b/lib/user/opPDODatabaseSessionStorage.class.php index 1386389..4b415ff 100644 --- a/lib/user/opPDODatabaseSessionStorage.class.php +++ b/lib/user/opPDODatabaseSessionStorage.class.php @@ -26,4 +26,55 @@ class opPDODatabaseSessionStorage extends sfPDOSessionStorage return parent::sessionOpen($path, $name); } + + /** + * Writes data to this storage replacing 4 byte utf8 characters. + * + * @param string $key A unique key identifying your data + * @param mixed $data Data associated with your key + * + * @see sfSessionStorage + */ + public function write($key, $data) + { + // "utf8", a type of character set in MySQL, can't handle 4 bytes utf8 characters + // so we replace such a character to "U+FFFD" (A unicode "REPLACEMENT CHARACTER"). + if (!$this->isReadyFor4BytesUtf8()) + { + $data = $this->replace4BytesUtf8Characters($data); + } + + parent::write($key, $data); + } + + protected function replace4BytesUtf8Characters($value) + { + if (is_array($value)) + { + $result = array(); + foreach ($value as $k => $v) + { + $result[$this->replace4BytesUtf8Characters($k)] = $this->replace4BytesUtf8Characters($v); + } + + return $result; + } + elseif (!is_string($value)) + { + return $value; + } + + // See: RFC 3629 (section 4, Syntax of UTF-8 Byte Sequences) + // http://tools.ietf.org/html/rfc3629#section-4 + return preg_replace('/( + \xF0[\x90-\xBF][\x80-\xBF]{2}| # %xF0 %x90-BF 2( UTF8-tail ) + [\xF1-\xF3][\x80-\xBF]{3}| # %xF1-F3 3( UTF8-tail ) + \xF4[\x80-\x8F][\x80-\xBF]{2} # %xF4 %x80-8F 2( UTF8-tail ) + )/x', "\xEF\xBF\xBD", $value); + } + + protected function isReadyFor4BytesUtf8() + { + return 'mysql' !== $this->db->getAttribute(PDO::ATTR_DRIVER_NAME); + } } diff --git a/test/unit/user/opPDODatabaseSessionStrageTest.class.php b/test/unit/user/opPDODatabaseSessionStrageTest.class.php new file mode 100644 index 0000000..7285e5e --- /dev/null +++ b/test/unit/user/opPDODatabaseSessionStrageTest.class.php @@ -0,0 +1,37 @@ + 'session', + 'database' => 'doctrine', + 'db_id_col' => 'id', + 'db_data_col' => 'session_data', + 'db_time_col' => 'time', +)); + +$t->ok($storage instanceof sfStorage, 'sfPDOSessionStorage is an instance of sfStorage'); +$t->ok($storage instanceof sfDatabaseSessionStorage, 'sfPDOSessionStorage is an instance of sfDatabaseSessionStorage'); + +$storage->sessionOpen(); + +// main test + +$sessionId = '1'; +$newSessionData = 'foo:bar:baz'; +$storage->write($sessionId, $newSessionData); +$t->is($storage->read($sessionId), $newSessionData, 'session data can get data correctly'); + +$sessionId = '1'; +$newSessionData = 'testⓉⒺⓈⓉテスト🅃🄴🅂🅃てすと'; +$strConverted = 'testⓉⒺⓈⓉテスト����てすと'; +$storage->write($sessionId, $newSessionData); +$t->is($storage->read($sessionId), $strConverted, 'session data using 4 byte utf characters can get data correctly'); -- 2.4.4