Thrift-1427: PHP library uses non-multibyte safe functions with mbstring function overloading
Client: php
Patch: Bryan Alves
Fixes issue with php overloaded mbstring to be binary-safe for strlen and substr.
git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1207960 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/php/src/TStringUtils.php b/lib/php/src/TStringUtils.php
new file mode 100644
index 0000000..69672e8
--- /dev/null
+++ b/lib/php/src/TStringUtils.php
@@ -0,0 +1,81 @@
+<?php
+
+interface TStringFunc {
+ public function substr($str, $start, $length = null);
+ public function strlen($str);
+}
+
+class TStringFunc_Core
+implements TStringFunc {
+ public function substr($str, $start, $length = null) {
+ // specifying a null $length would return an empty string
+ if($length === null) {
+ return substr($str, $start);
+ }
+ return substr($str, $start, $length);
+ }
+
+ public function strlen($str) {
+ return strlen($str);
+ }
+}
+
+class TStringFunc_Mbstring
+implements TStringFunc {
+ public function substr($str, $start, $length = null) {
+ /**
+ * We need to set the charset parameter, which is the second
+ * optional parameter and the first optional parameter can't
+ * be null or false as a "magic" value because that would
+ * cause an empty string to be returned, so we need to
+ * actually calculate the proper length value.
+ */
+ if($length === null) {
+ $length = $this->strlen($str) - $start;
+ }
+
+ return mb_substr($str, $start, $length, '8bit');
+ }
+
+ public function strlen($str) {
+ return mb_strlen($str, '8bit');
+ }
+}
+
+class TStringFuncFactory {
+ private static $_instance;
+
+ /**
+ * Get the Singleton instance of TStringFunc implementation that is
+ * compatible with the current system's mbstring.func_overload settings.
+ *
+ * @return TStringFunc
+ */
+ public static function create() {
+ if(!self::$_instance) {
+ self::_setInstance();
+ }
+
+ return self::$_instance;
+ }
+
+ private static function _setInstance() {
+ /**
+ * Cannot use str* functions for byte counting because multibyte
+ * characters will be read a single bytes.
+ *
+ * See: http://us.php.net/manual/en/mbstring.overload.php
+ */
+ if(ini_get('mbstring.func_overload') & 2) {
+ self::$_instance = new TStringFunc_Mbstring();
+ }
+ /**
+ * mbstring is not installed or does not have function overloading
+ * of the str* functions enabled so use PHP core str* functions for
+ * byte counting.
+ */
+ else {
+ self::$_instance = new TStringFunc_Core();
+ }
+ }
+}
diff --git a/lib/php/src/Thrift.php b/lib/php/src/Thrift.php
index d2a9441..c845395 100644
--- a/lib/php/src/Thrift.php
+++ b/lib/php/src/Thrift.php
@@ -785,3 +785,5 @@
}
include_once $GLOBALS['THRIFT_ROOT'].'/protocol/TProtocol.php';
include_once $GLOBALS['THRIFT_ROOT'].'/transport/TTransport.php';
+include_once $GLOBALS['THRIFT_ROOT'].'/TStringUtils.php';
+
diff --git a/lib/php/src/protocol/TBinaryProtocol.php b/lib/php/src/protocol/TBinaryProtocol.php
index b5bc750..40b9f9b 100644
--- a/lib/php/src/protocol/TBinaryProtocol.php
+++ b/lib/php/src/protocol/TBinaryProtocol.php
@@ -180,7 +180,7 @@
}
public function writeString($value) {
- $len = strlen($value);
+ $len = TStringFuncFactory::create()->strlen($value);
$result = $this->writeI32($len);
if ($len) {
$this->trans_->write($value, $len);
diff --git a/lib/php/src/protocol/TCompactProtocol.php b/lib/php/src/protocol/TCompactProtocol.php
index 6ecbd09..9f0d407 100644
--- a/lib/php/src/protocol/TCompactProtocol.php
+++ b/lib/php/src/protocol/TCompactProtocol.php
@@ -121,7 +121,7 @@
public function writeVarint($data) {
$out = $this->getVarint($data);
- $result = strlen($out);
+ $result = TStringFuncFactory::create()->strlen($out);
$this->trans_->write($out, $result);
return $result;
}
@@ -308,7 +308,7 @@
}
public function writeString($value) {
- $len = strlen($value);
+ $len = TStringFuncFactory::create()->strlen($value);
$result = $this->writeVarint($len);
if ($len) {
$this->trans_->write($value, $len);
@@ -653,7 +653,7 @@
}
}
- $ret = strlen($out);
+ $ret = TStringFuncFactory::create()->strlen($out);
$this->trans_->write($out, $ret);
return $ret;
diff --git a/lib/php/src/transport/TBufferedTransport.php b/lib/php/src/transport/TBufferedTransport.php
index e841564..9b27d02 100644
--- a/lib/php/src/transport/TBufferedTransport.php
+++ b/lib/php/src/transport/TBufferedTransport.php
@@ -87,7 +87,7 @@
}
public function putBack($data) {
- if (strlen($this->rBuf_) === 0) {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
$this->rBuf_ = $data;
} else {
$this->rBuf_ = ($data . $this->rBuf_);
@@ -104,7 +104,7 @@
* the buffered readAll.
*/
public function readAll($len) {
- $have = strlen($this->rBuf_);
+ $have = TStringFuncFactory::create()->strlen($this->rBuf_);
if ($have == 0) {
$data = $this->transport_->readAll($len);
} else if ($have < $len) {
@@ -115,31 +115,31 @@
$data = $this->rBuf_;
$this->rBuf_ = '';
} else if ($have > $len) {
- $data = substr($this->rBuf_, 0, $len);
- $this->rBuf_ = substr($this->rBuf_, $len);
+ $data = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
}
return $data;
}
public function read($len) {
- if (strlen($this->rBuf_) === 0) {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
$this->rBuf_ = $this->transport_->read($this->rBufSize_);
}
- if (strlen($this->rBuf_) <= $len) {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) <= $len) {
$ret = $this->rBuf_;
$this->rBuf_ = '';
return $ret;
}
- $ret = substr($this->rBuf_, 0, $len);
- $this->rBuf_ = substr($this->rBuf_, $len);
+ $ret = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
return $ret;
}
public function write($buf) {
$this->wBuf_ .= $buf;
- if (strlen($this->wBuf_) >= $this->wBufSize_) {
+ if (TStringFuncFactory::create()->strlen($this->wBuf_) >= $this->wBufSize_) {
$out = $this->wBuf_;
// Note that we clear the internal wBuf_ prior to the underlying write
@@ -151,7 +151,7 @@
}
public function flush() {
- if (strlen($this->wBuf_) > 0) {
+ if (TStringFuncFactory::create()->strlen($this->wBuf_) > 0) {
$this->transport_->write($this->wBuf_);
$this->wBuf_ = '';
}
diff --git a/lib/php/src/transport/TFramedTransport.php b/lib/php/src/transport/TFramedTransport.php
index b1c0bb5..bc759de 100644
--- a/lib/php/src/transport/TFramedTransport.php
+++ b/lib/php/src/transport/TFramedTransport.php
@@ -98,20 +98,20 @@
return $this->transport_->read($len);
}
- if (strlen($this->rBuf_) === 0) {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
$this->readFrame();
}
// Just return full buff
- if ($len >= strlen($this->rBuf_)) {
+ if ($len >= TStringFuncFactory::create()->strlen($this->rBuf_)) {
$out = $this->rBuf_;
$this->rBuf_ = null;
return $out;
}
- // Return substr
- $out = substr($this->rBuf_, 0, $len);
- $this->rBuf_ = substr($this->rBuf_, $len);
+ // Return TStringFuncFactory::create()->substr
+ $out = TStringFuncFactory::create()->substr($this->rBuf_, 0, $len);
+ $this->rBuf_ = TStringFuncFactory::create()->substr($this->rBuf_, $len);
return $out;
}
@@ -121,7 +121,7 @@
* @param string $data data to return
*/
public function putBack($data) {
- if (strlen($this->rBuf_) === 0) {
+ if (TStringFuncFactory::create()->strlen($this->rBuf_) === 0) {
$this->rBuf_ = $data;
} else {
$this->rBuf_ = ($data . $this->rBuf_);
@@ -150,8 +150,8 @@
return $this->transport_->write($buf, $len);
}
- if ($len !== null && $len < strlen($buf)) {
- $buf = substr($buf, 0, $len);
+ if ($len !== null && $len < TStringFuncFactory::create()->strlen($buf)) {
+ $buf = TStringFuncFactory::create()->substr($buf, 0, $len);
}
$this->wBuf_ .= $buf;
}
@@ -161,11 +161,11 @@
* followed by the actual data.
*/
public function flush() {
- if (!$this->write_ || strlen($this->wBuf_) == 0) {
+ if (!$this->write_ || TStringFuncFactory::create()->strlen($this->wBuf_) == 0) {
return $this->transport_->flush();
}
- $out = pack('N', strlen($this->wBuf_));
+ $out = pack('N', TStringFuncFactory::create()->strlen($this->wBuf_));
$out .= $this->wBuf_;
// Note that we clear the internal wBuf_ prior to the underlying write
diff --git a/lib/php/src/transport/THttpClient.php b/lib/php/src/transport/THttpClient.php
index 880011a..118f0d3 100644
--- a/lib/php/src/transport/THttpClient.php
+++ b/lib/php/src/transport/THttpClient.php
@@ -85,7 +85,7 @@
* @param string $uri
*/
public function __construct($host, $port=80, $uri='', $scheme = 'http') {
- if ((strlen($uri) > 0) && ($uri{0} != '/')) {
+ if ((TStringFuncFactory::create()->strlen($uri) > 0) && ($uri{0} != '/')) {
$uri = '/'.$uri;
}
$this->scheme_ = $scheme;
@@ -175,7 +175,7 @@
'Accept: application/x-thrift',
'User-Agent: PHP/THttpClient',
'Content-Type: application/x-thrift',
- 'Content-Length: '.strlen($this->buf_));
+ 'Content-Length: '.TStringFuncFactory::create()->strlen($this->buf_));
$options = array('method' => 'POST',
'header' => implode("\r\n", $headers),
diff --git a/lib/php/src/transport/TMemoryBuffer.php b/lib/php/src/transport/TMemoryBuffer.php
index a0b1a54..13aa9e5 100644
--- a/lib/php/src/transport/TMemoryBuffer.php
+++ b/lib/php/src/transport/TMemoryBuffer.php
@@ -54,20 +54,20 @@
}
public function read($len) {
- if (strlen($this->buf_) === 0) {
+ if (TStringFuncFactory::create()->strlen($this->buf_) === 0) {
throw new TTransportException('TMemoryBuffer: Could not read ' .
$len . ' bytes from buffer.',
TTransportException::UNKNOWN);
}
- if (strlen($this->buf_) <= $len) {
+ if (TStringFuncFactory::create()->strlen($this->buf_) <= $len) {
$ret = $this->buf_;
$this->buf_ = '';
return $ret;
}
- $ret = substr($this->buf_, 0, $len);
- $this->buf_ = substr($this->buf_, $len);
+ $ret = TStringFuncFactory::create()->substr($this->buf_, 0, $len);
+ $this->buf_ = TStringFuncFactory::create()->substr($this->buf_, $len);
return $ret;
}
@@ -77,6 +77,6 @@
}
public function available() {
- return strlen($this->buf_);
+ return TStringFuncFactory::create()->strlen($this->buf_);
}
}
diff --git a/lib/php/src/transport/TPhpStream.php b/lib/php/src/transport/TPhpStream.php
index 94b11a6..e16b472 100644
--- a/lib/php/src/transport/TPhpStream.php
+++ b/lib/php/src/transport/TPhpStream.php
@@ -86,12 +86,12 @@
}
public function write($buf) {
- while (strlen($buf) > 0) {
+ while (TStringFuncFactory::create()->strlen($buf) > 0) {
$got = @fwrite($this->outStream_, $buf);
if ($got === 0 || $got === FALSE) {
- throw new TException('TPhpStream: Could not write '.strlen($buf).' bytes');
+ throw new TException('TPhpStream: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes');
}
- $buf = substr($buf, $got);
+ $buf = TStringFuncFactory::create()->substr($buf, $got);
}
}
diff --git a/lib/php/src/transport/TSocket.php b/lib/php/src/transport/TSocket.php
index daf9020..0203384 100644
--- a/lib/php/src/transport/TSocket.php
+++ b/lib/php/src/transport/TSocket.php
@@ -283,23 +283,23 @@
$write = array($this->handle_);
// keep writing until all the data has been written
- while (strlen($buf) > 0) {
+ while (TStringFuncFactory::create()->strlen($buf) > 0) {
// wait for stream to become available for writing
$writable = @stream_select($null, $write, $null, $this->sendTimeoutSec_, $this->sendTimeoutUsec_);
if ($writable > 0) {
// write buffer to stream
$written = @stream_socket_sendto($this->handle_, $buf);
if ($written === -1 || $written === false) {
- throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '.
+ throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
$this->host_.':'.$this->port_);
}
// determine how much of the buffer is left to write
- $buf = substr($buf, $written);
+ $buf = TStringFuncFactory::create()->substr($buf, $written);
} else if ($writable === 0) {
- throw new TTransportException('TSocket: timed out writing '.strlen($buf).' bytes from '.
+ throw new TTransportException('TSocket: timed out writing '.TStringFuncFactory::create()->strlen($buf).' bytes from '.
$this->host_.':'.$this->port_);
} else {
- throw new TTransportException('TSocket: Could not write '.strlen($buf).' bytes '.
+ throw new TTransportException('TSocket: Could not write '.TStringFuncFactory::create()->strlen($buf).' bytes '.
$this->host_.':'.$this->port_);
}
}
diff --git a/lib/php/src/transport/TTransport.php b/lib/php/src/transport/TTransport.php
index e0e336d..f8c8d86 100644
--- a/lib/php/src/transport/TTransport.php
+++ b/lib/php/src/transport/TTransport.php
@@ -83,7 +83,7 @@
$data = '';
$got = 0;
- while (($got = strlen($data)) < $len) {
+ while (($got = TStringFuncFactory::create()->strlen($data)) < $len) {
$data .= $this->read($len - $got);
}
return $data;