Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 85 additions & 27 deletions lib/Horde/String.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ public static function convertCharset($input, $from, $to, $force = false)
return $input;
}

if(strlen($input) === 0) {
return $input;
}

return self::_convertCharset($input, $from, $to);
}

Expand Down Expand Up @@ -145,12 +149,7 @@ protected static function _convertCharset($input, $from, $to)

/* Try iconv with transliteration. */
if (Horde_Util::extensionExists('iconv')) {
unset($php_errormsg);
ini_set('track_errors', 1);
$out = @iconv($from, $to . '//TRANSLIT', $input);
$errmsg = isset($php_errormsg);
ini_restore('track_errors');
if (!$errmsg && $out !== false) {
if (($out = self::_convertCharsetIconv($input, $from, $to)) !== false) {
return $out;
}
}
Expand All @@ -170,6 +169,25 @@ protected static function _convertCharset($input, $from, $to)
return $input;
}

/**
* Internal function used to do charset transliteration with iconv.
*
* @param string $input See self::convertCharset().
* @param string $from See self::convertCharset().
* @param string $to See self::convertCharset().
*
* @return mixed The converted string, or false on error.
*/
protected static function _convertCharsetIconv(string $input, string $from, string $to): string
{
error_clear_last();
$out = @iconv($from, $to . '//TRANSLIT', $input);
if (is_null(error_get_last()) && $out !== false) {
return $out;
}
return false;
}

/**
* Makes a string lowercase.
*
Expand Down Expand Up @@ -488,37 +506,77 @@ protected static function _pos(
)
{
if (Horde_Util::extensionExists('mbstring')) {
unset($php_errormsg);
$track_errors = ini_set('track_errors', 1);
$ret = @call_user_func('mb_' . $func, $haystack, $needle, $offset, self::_mbstringCharset($charset));
ini_set('track_errors', $track_errors);
if (!isset($php_errormsg)) {
return $ret;
if (($out = @self::_posMbstring($haystack, $needle, $offset, $charset, $func)) !== false) {
return $out;
}
}

if (Horde_Util::extensionExists('intl')) {
unset($php_errormsg);
$track_errors = ini_set('track_errors', 1);
$ret = self::convertCharset(
@call_user_func(
'grapheme_' . $func,
self::convertCharset($haystack, $charset, 'UTF-8'),
self::convertCharset($needle, $charset, 'UTF-8'),
$offset
),
'UTF-8',
$charset
);
ini_set('track_errors', $track_errors);
if (!isset($php_errormsg)) {
return $ret;
if(($out = @self::_posIntl($haystack, $needle, $offset, $charset, $func)) !== false) {
return $out;
}
}

return $func($haystack, $needle, $offset);
}

/**
* Internal function to perform string position searches using mbstring.
*
* @param string $haystack See self::_pos
* @param string $needle See self::_pos
* @param integer $offset See self::_pos
* @param string $charset See self::_pos
* @param string $func See self::_pos
*
* @return mixed The position of occurrence, or false on error.
*/
protected static function _posMbstring(
$haystack, $needle, $offset, $charset, $func
)
{
error_clear_last();
$ret = @call_user_func('mb_' . $func, $haystack, $needle, $offset, self::_mbstringCharset($charset));
if (is_null(error_get_last())) {
return $ret;
}

return false;
}

/**
* Internal function to perform string position searches using intl.
*
* @param string $haystack See self::_pos
* @param string $needle See self::_pos
* @param integer $offset See self::_pos
* @param string $charset See self::_pos
* @param string $func See self::_pos
*
* @return mixed The position of occurrence, or false on error.
*/
protected static function _posIntl(
$haystack, $needle, $offset, $charset, $func
)
{
error_clear_last();
$ret = self::convertCharset(
@call_user_func(
'grapheme_' . $func,
self::convertCharset($haystack, $charset, 'UTF-8'),
self::convertCharset($needle, $charset, 'UTF-8'),
$offset
),
'UTF-8',
$charset
);
if (is_null(error_get_last())) {
return $ret;
}

return false;
}

/**
* Returns a string padded to a certain length with another string.
* This method behaves exactly like str_pad() but is multibyte safe.
Expand Down
26 changes: 26 additions & 0 deletions test/Horde/Util/Mock/String.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
/**
* Wrapper to test internal Horde_String methods.
*
* @author Daniel Ziegenberg <[email protected]>
* @category Horde
* @license http://www.horde.org/licenses/lgpl21 LGPL 2.1
* @package Util
*/
class Horde_Util_Mock_String extends Horde_String
{
public static function testConvertCharsetIconv(string $input, string $from, string $to)
{
return self::_convertCharsetIconv($input, $from, $to);
}

public static function testPosMbstring(string $haystack, string $needle, int $offset, string $charset, string $func)
{
return self::_posMbstring($haystack, $needle, $offset, $charset, $func);
}

public static function testPosIntl(string $haystack, string $needle, int $offset, string $charset, string $func)
{
return self::_posIntl($haystack, $needle, $offset, $charset, $func);
}
}
57 changes: 57 additions & 0 deletions test/Horde/Util/StringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -731,4 +731,61 @@ public function invalidUtf8Provider()
);
}

/**
* @dataProvider ConvertCharsetIconvProvider
*/
public function testConvertCharsetIconv(string $input, string $from, string $to, $expected): void
{
$this->assertEquals(
$expected,
Horde_Util_Mock_String::testConvertCharsetIconv($input, $from, $to)
);
}

public function ConvertCharsetIconvProvider()
{
return [
'valid character sequence' => ['This will work.', 'UTF-8', 'ISO-8859-1', 'This will work.'],
'illegal character in input string' => ["This is the Euro symbol '€'.", 'UTF-8', 'ISO-8859-1', false]
];
}

/**
* @dataProvider posMbstringProvider
*/
public function testPosMbstring(string $haystack, string $needle, int $offset, string $charset, string $func, $expected): void
{
$this->assertEquals(
$expected,
Horde_Util_Mock_String::testPosMbstring($haystack, $needle, $offset, $charset, $func)
);
}

public function posMbstringProvider()
{
return [
'valid character sequence' => ['Some random string.', 'Some', 0, 'UTF-8', 'strpos', 0],
'invalid search offset' => ['Some random string', 'Some', 50, 'UTF-8', 'strpos', false]
];
}

/**
* @dataProvider posIntlProvider
*/
public function testPosIntl(string $haystack, string $needle, int $offset, string $charset, string $func, $expected): void
{
$this->assertEquals(
$expected,
Horde_Util_Mock_String::testPosIntl($haystack, $needle, $offset, $charset, $func)
);
}

public function posIntlProvider()
{
return [
'valid character sequence' => ['Some random string.', 'Some', 0, 'UTF-8', 'strpos', 0],
'invalid search offset' => ['Some random string', 'Some', 50, 'UTF-8', 'strpos', false]
];
}

}