diff --git a/src/Utils/PhpFunctionsScanner.php b/src/Utils/PhpFunctionsScanner.php index 33c311a2..be9ea8fa 100644 --- a/src/Utils/PhpFunctionsScanner.php +++ b/src/Utils/PhpFunctionsScanner.php @@ -16,6 +16,52 @@ public function __construct($code) $this->tokens = token_get_all($code); } + /** + * Decodes a T_CONSTANT_ENCAPSED_STRING string. + * + * @param string $value + * + * @return string + */ + public static function decodeString($value) + { + $result = ''; + if ($value[0] === "'" || strpos($value, '$') === false) { + if (strpos($value, '\\') === false) { + $result = substr($value, 1, -1); + } else { + $result = eval("return $value;"); + } + } else { + $value = substr($value, 1, -1); + while (($p = strpos($value, '\\')) !== false) { + if (!isset($value[$p + 1])) { + break; + } + if ($p > 0) { + $result .= substr($value, 0, $p); + } + $value = substr($value, $p + 1); + $p = strpos($value, '$'); + if ($p === false) { + $result .= eval('return "\\'.$value.'";'); + $value = ''; + break; + } + if ($p === 0) { + $result .= '$'; + $value = substr($value, 1); + } else { + $result .= eval('return "\\'.substr($value, 0, $p).'";'); + $value = substr($value, $p); + } + } + $result .= $value; + } + + return $result; + } + /** * {@inheritdoc} */ @@ -39,15 +85,7 @@ public function getFunctions() //add an argument to the current function if (isset($bufferFunctions[0]) && ($value[0] === T_CONSTANT_ENCAPSED_STRING)) { - $val = $value[1]; - - if ($val[0] === '"') { - $val = str_replace('\\"', '"', $val); - } else { - $val = str_replace("\\'", "'", $val); - } - - $bufferFunctions[0][2][] = substr($val, 1, -1); + $bufferFunctions[0][2][] = static::decodeString($value[1]); continue; } diff --git a/tests/DecodePhpStringTest.php b/tests/DecodePhpStringTest.php new file mode 100644 index 00000000..f022b7e6 --- /dev/null +++ b/tests/DecodePhpStringTest.php @@ -0,0 +1,32 @@ +name}"', '{$obj->name}'), + array('"a\x20b $c"', 'a b $c'), + ); + } + + /** + * @dataProvider decodeStringsProvider + */ + public function testDecodeStrings($source, $decoded) + { + $this->assertSame($decoded, Gettext\Utils\PhpFunctionsScanner::decodeString($source)); + } +} diff --git a/tests/PhpCodeExtractorTest.php b/tests/PhpCodeExtractorTest.php index 33b127be..3c9c12a3 100644 --- a/tests/PhpCodeExtractorTest.php +++ b/tests/PhpCodeExtractorTest.php @@ -60,4 +60,14 @@ public function testMultiline() $this->assertInstanceOf('Gettext\\Translation', $translation); $this->assertEquals($original, $translation->getOriginal()); } + + public function testSpecialChars() + { + $translations = Gettext\Extractors\PhpCode::fromFile(__DIR__.'/files/special-chars.php'); + + $this->assertInstanceOf('Gettext\\Translation', $translations->find(null, 'plain')); + $this->assertInstanceOf('Gettext\\Translation', $translations->find(null, 'DATE \\a\\t TIME')); + $this->assertInstanceOf('Gettext\\Translation', $translations->find(null, "FIELD\tFIELD")); + $this->assertCount(3, $translations); + } } diff --git a/tests/files/special-chars.php b/tests/files/special-chars.php new file mode 100644 index 00000000..5dd008ca --- /dev/null +++ b/tests/files/special-chars.php @@ -0,0 +1,7 @@ +
+

+

+

+

+

+