Skip to content

Commit 7b4a35a

Browse files
committed
[Intl] Fixed a few bugs in TextBundleWriter
1 parent a45e3da commit 7b4a35a

File tree

6 files changed

+145
-16
lines changed

6 files changed

+145
-16
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Intl\Exception;
13+
14+
/**
15+
* Thrown when a method argument had an unexpected type.
16+
*
17+
* @author Bernhard Schussek <[email protected]>
18+
*/
19+
class UnexpectedTypeException extends InvalidArgumentException
20+
{
21+
public function __construct($value, $expectedType)
22+
{
23+
parent::__construct(sprintf('Expected argument of type "%s", "%s" given', $expectedType, is_object($value) ? get_class($value) : gettype($value)));
24+
}
25+
}

src/Symfony/Component/Intl/ResourceBundle/Writer/TextBundleWriter.php

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,14 @@
1111

1212
namespace Symfony\Component\Intl\ResourceBundle\Writer;
1313

14+
use Symfony\Component\Intl\Exception\UnexpectedTypeException;
15+
1416
/**
1517
* Writes .txt resource bundles.
1618
*
17-
* The resulting files can be converted to binary .res files using the
18-
* {@link \Symfony\Component\Intl\ResourceBundle\Transformer\BundleCompiler}.
19+
* The resulting files can be converted to binary .res files using a
20+
* {@link \Symfony\Component\Intl\ResourceBundle\Compiler\BundleCompilerInterface}
21+
* implementation.
1922
*
2023
* @author Bernhard Schussek <[email protected]>
2124
*
@@ -28,11 +31,11 @@ class TextBundleWriter implements BundleWriterInterface
2831
/**
2932
* {@inheritdoc}
3033
*/
31-
public function write($path, $locale, $data)
34+
public function write($path, $locale, $data, $fallback = true)
3235
{
3336
$file = fopen($path.'/'.$locale.'.txt', 'w');
3437

35-
$this->writeResourceBundle($file, $locale, $data);
38+
$this->writeResourceBundle($file, $locale, $data, $fallback);
3639

3740
fclose($file);
3841
}
@@ -43,14 +46,16 @@ public function write($path, $locale, $data)
4346
* @param resource $file The file handle to write to.
4447
* @param string $bundleName The name of the bundle.
4548
* @param mixed $value The value of the node.
49+
* @param bool $fallback Whether the resource bundle should be merged
50+
* with the fallback locale.
4651
*
4752
* @see http://source.icu-project.org/repos/icu/icuhtml/trunk/design/bnf_rb.txt
4853
*/
49-
private function writeResourceBundle($file, $bundleName, $value)
54+
private function writeResourceBundle($file, $bundleName, $value, $fallback)
5055
{
5156
fwrite($file, $bundleName);
5257

53-
$this->writeTable($file, $value, 0);
58+
$this->writeTable($file, $value, 0, $fallback);
5459

5560
fwrite($file, "\n");
5661
}
@@ -74,16 +79,25 @@ private function writeResource($file, $value, $indentation, $requireBraces = tru
7479
return;
7580
}
7681

82+
if ($value instanceof \Traversable) {
83+
$value = iterator_to_array($value);
84+
}
85+
7786
if (is_array($value)) {
78-
if (count($value) === count(array_filter($value, 'is_int'))) {
87+
$intValues = count($value) === count(array_filter($value, 'is_int'));
88+
89+
$keys = array_keys($value);
90+
91+
// check that the keys are 0-indexed and ascending
92+
$intKeys = $keys === range(0, count($keys) - 1);
93+
94+
if ($intValues && $intKeys) {
7995
$this->writeIntVector($file, $value, $indentation);
8096

8197
return;
8298
}
8399

84-
$keys = array_keys($value);
85-
86-
if (count($keys) === count(array_filter($keys, 'is_int'))) {
100+
if ($intKeys) {
87101
$this->writeArray($file, $value, $indentation);
88102

89103
return;
@@ -182,16 +196,35 @@ private function writeArray($file, array $value, $indentation)
182196
/**
183197
* Writes a "table" node.
184198
*
185-
* @param resource $file The file handle to write to.
186-
* @param array $value The value of the node.
187-
* @param int $indentation The number of levels to indent.
199+
* @param resource $file The file handle to write to.
200+
* @param array|\Traversable $value The value of the node.
201+
* @param int $indentation The number of levels to indent.
202+
* @param bool $fallback Whether the table should be merged
203+
* with the fallback locale.
204+
*
205+
* @throws UnexpectedTypeException When $value is not an array and not a
206+
* \Traversable instance.
188207
*/
189-
private function writeTable($file, array $value, $indentation)
208+
private function writeTable($file, $value, $indentation, $fallback = true)
190209
{
210+
if (!is_array($value) && !$value instanceof \Traversable) {
211+
throw new UnexpectedTypeException($value, 'array or \Traversable');
212+
}
213+
214+
if (!$fallback) {
215+
fwrite($file, ":table(nofallback)");
216+
}
217+
191218
fwrite($file, "{\n");
192219

193220
foreach ($value as $key => $entry) {
194221
fwrite($file, str_repeat(' ', $indentation + 1));
222+
223+
// escape colons, otherwise they are interpreted as resource types
224+
if (false !== strpos($key, ':') || false !== strpos($key, ' ')) {
225+
$key = '"'.$key.'"';
226+
}
227+
195228
fwrite($file, $key);
196229

197230
$this->writeResource($file, $entry, $indentation + 1);

src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/Fixtures/en.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,22 @@ en{
1414
2,
1515
3,
1616
}
17+
NotAnIntVector{
18+
0:int{0}
19+
2:int{1}
20+
1:int{2}
21+
3:int{3}
22+
}
23+
IntVectorWithStringKeys{
24+
a:int{0}
25+
b:int{1}
26+
c:int{2}
27+
}
28+
TableWithIntKeys{
29+
0:int{0}
30+
1:int{1}
31+
3:int{3}
32+
}
1733
FalseBoolean{"false"}
1834
TrueBoolean{"true"}
1935
Null{""}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
en_nofallback:table(nofallback){
2+
Entry{"Value"}
3+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
escaped{
2+
"EntryWith:Colon"{"Value"}
3+
"Entry With Spaces"{"Value"}
4+
}

src/Symfony/Component/Intl/Tests/ResourceBundle/Writer/TextBundleWriterTest.php

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ class TextBundleWriterTest extends \PHPUnit_Framework_TestCase
3636
protected function setUp()
3737
{
3838
$this->writer = new TextBundleWriter();
39-
$this->directory = sys_get_temp_dir() . '/TextBundleWriterTest/' . rand(1000, 9999);
39+
$this->directory = sys_get_temp_dir().'/TextBundleWriterTest/'.rand(1000, 9999);
4040
$this->filesystem = new Filesystem();
4141

4242
$this->filesystem->mkdir($this->directory);
@@ -54,6 +54,9 @@ public function testWrite()
5454
'Array' => array('foo', 'bar', array('Key' => 'value')),
5555
'Integer' => 5,
5656
'IntVector' => array(0, 1, 2, 3),
57+
'NotAnIntVector' => array(0 => 0, 2 => 1, 1 => 2, 3 => 3),
58+
'IntVectorWithStringKeys' => array('a' => 0, 'b' => 1, 'c' => 2),
59+
'TableWithIntKeys' => array(0 => 0, 1 => 1, 3 => 3),
5760
'FalseBoolean' => false,
5861
'TrueBoolean' => true,
5962
'Null' => null,
@@ -62,6 +65,51 @@ public function testWrite()
6265
'Entry2' => 'String',
6366
));
6467

65-
$this->assertFileEquals(__DIR__ . '/Fixtures/en.txt', $this->directory . '/en.txt');
68+
$this->assertFileEquals(__DIR__.'/Fixtures/en.txt', $this->directory.'/en.txt');
69+
}
70+
71+
public function testWriteTraversable()
72+
{
73+
$this->writer->write($this->directory, 'en', new \ArrayIterator(array(
74+
'Entry1' => new \ArrayIterator(array(
75+
'Array' => array('foo', 'bar', array('Key' => 'value')),
76+
'Integer' => 5,
77+
'IntVector' => array(0, 1, 2, 3),
78+
'NotAnIntVector' => array(0 => 0, 2 => 1, 1 => 2, 3 => 3),
79+
'IntVectorWithStringKeys' => array('a' => 0, 'b' => 1, 'c' => 2),
80+
'TableWithIntKeys' => array(0 => 0, 1 => 1, 3 => 3),
81+
'FalseBoolean' => false,
82+
'TrueBoolean' => true,
83+
'Null' => null,
84+
'Float' => 1.23,
85+
)),
86+
'Entry2' => 'String',
87+
)));
88+
89+
$this->assertFileEquals(__DIR__.'/Fixtures/en.txt', $this->directory.'/en.txt');
90+
}
91+
92+
public function testWriteNoFallback()
93+
{
94+
$data = array(
95+
'Entry' => 'Value'
96+
);
97+
98+
$this->writer->write($this->directory, 'en_nofallback', $data, $fallback = false);
99+
100+
$this->assertFileEquals(__DIR__.'/Fixtures/en_nofallback.txt', $this->directory.'/en_nofallback.txt');
101+
}
102+
103+
public function testEscapeKeysIfNecessary()
104+
{
105+
$this->writer->write($this->directory, 'escaped', array(
106+
// Keys with colons must be escaped, otherwise the part after the
107+
// colon is interpreted as resource type
108+
'EntryWith:Colon' => 'Value',
109+
// Keys with spaces must be escaped
110+
'Entry With Spaces' => 'Value',
111+
));
112+
113+
$this->assertFileEquals(__DIR__.'/Fixtures/escaped.txt', $this->directory.'/escaped.txt');
66114
}
67115
}

0 commit comments

Comments
 (0)