1<?php
2/*
3 * This file is part of the Exporter package.
4 *
5 * (c) Sebastian Bergmann <sebastian@phpunit.de>
6 *
7 * For the full copyright and license information, please view the LICENSE
8 * file that was distributed with this source code.
9 */
10
11namespace SebastianBergmann\Exporter;
12
13/**
14 * @covers SebastianBergmann\Exporter\Exporter
15 */
16class ExporterTest extends \PHPUnit_Framework_TestCase
17{
18    /**
19     * @var Exporter
20     */
21    private $exporter;
22
23    protected function setUp()
24    {
25        $this->exporter = new Exporter;
26    }
27
28    public function exportProvider()
29    {
30        $obj2 = new \stdClass;
31        $obj2->foo = 'bar';
32
33        $obj3 = (object)array(1,2,"Test\r\n",4,5,6,7,8);
34
35        $obj = new \stdClass;
36        //@codingStandardsIgnoreStart
37        $obj->null = null;
38        //@codingStandardsIgnoreEnd
39        $obj->boolean = true;
40        $obj->integer = 1;
41        $obj->double = 1.2;
42        $obj->string = '1';
43        $obj->text = "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext";
44        $obj->object = $obj2;
45        $obj->objectagain = $obj2;
46        $obj->array = array('foo' => 'bar');
47        $obj->self = $obj;
48
49        $storage = new \SplObjectStorage;
50        $storage->attach($obj2);
51        $storage->foo = $obj2;
52
53        return array(
54            array(null, 'null'),
55            array(true, 'true'),
56            array(false, 'false'),
57            array(1, '1'),
58            array(1.0, '1.0'),
59            array(1.2, '1.2'),
60            array(fopen('php://memory', 'r'), 'resource(%d) of type (stream)'),
61            array('1', "'1'"),
62            array(array(array(1,2,3), array(3,4,5)),
63        <<<EOF
64Array &0 (
65    0 => Array &1 (
66        0 => 1
67        1 => 2
68        2 => 3
69    )
70    1 => Array &2 (
71        0 => 3
72        1 => 4
73        2 => 5
74    )
75)
76EOF
77            ),
78            // \n\r and \r is converted to \n
79            array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext",
80            <<<EOF
81'this
82is
83a
84very
85very
86very
87very
88very
89very
90long
91text'
92EOF
93            ),
94            array(new \stdClass, 'stdClass Object &%x ()'),
95            array($obj,
96            <<<EOF
97stdClass Object &%x (
98    'null' => null
99    'boolean' => true
100    'integer' => 1
101    'double' => 1.2
102    'string' => '1'
103    'text' => 'this
104is
105a
106very
107very
108very
109very
110very
111very
112long
113text'
114    'object' => stdClass Object &%x (
115        'foo' => 'bar'
116    )
117    'objectagain' => stdClass Object &%x
118    'array' => Array &%d (
119        'foo' => 'bar'
120    )
121    'self' => stdClass Object &%x
122)
123EOF
124            ),
125            array(array(), 'Array &%d ()'),
126            array($storage,
127            <<<EOF
128SplObjectStorage Object &%x (
129    'foo' => stdClass Object &%x (
130        'foo' => 'bar'
131    )
132    '%x' => Array &0 (
133        'obj' => stdClass Object &%x
134        'inf' => null
135    )
136)
137EOF
138            ),
139            array($obj3,
140            <<<EOF
141stdClass Object &%x (
142    0 => 1
143    1 => 2
144    2 => 'Test\n'
145    3 => 4
146    4 => 5
147    5 => 6
148    6 => 7
149    7 => 8
150)
151EOF
152            ),
153            array(
154                chr(0) . chr(1) . chr(2) . chr(3) . chr(4) . chr(5),
155                'Binary String: 0x000102030405'
156            ),
157            array(
158                implode('', array_map('chr', range(0x0e, 0x1f))),
159                'Binary String: 0x0e0f101112131415161718191a1b1c1d1e1f'
160            ),
161            array(
162                chr(0x00) . chr(0x09),
163                'Binary String: 0x0009'
164            ),
165            array(
166                '',
167                "''"
168            ),
169        );
170    }
171
172    /**
173     * @dataProvider exportProvider
174     */
175    public function testExport($value, $expected)
176    {
177        $this->assertStringMatchesFormat(
178            $expected,
179            $this->trimNewline($this->exporter->export($value))
180        );
181    }
182
183    public function testExport2()
184    {
185        if (PHP_VERSION === '5.3.3') {
186            $this->markTestSkipped('Skipped due to "Nesting level too deep - recursive dependency?" fatal error');
187        }
188
189        $obj = new \stdClass;
190        $obj->foo = 'bar';
191
192        $array = array(
193            0 => 0,
194            'null' => null,
195            'boolean' => true,
196            'integer' => 1,
197            'double' => 1.2,
198            'string' => '1',
199            'text' => "this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext",
200            'object' => $obj,
201            'objectagain' => $obj,
202            'array' => array('foo' => 'bar'),
203        );
204
205        $array['self'] = &$array;
206
207        $expected = <<<EOF
208Array &%d (
209    0 => 0
210    'null' => null
211    'boolean' => true
212    'integer' => 1
213    'double' => 1.2
214    'string' => '1'
215    'text' => 'this
216is
217a
218very
219very
220very
221very
222very
223very
224long
225text'
226    'object' => stdClass Object &%x (
227        'foo' => 'bar'
228    )
229    'objectagain' => stdClass Object &%x
230    'array' => Array &%d (
231        'foo' => 'bar'
232    )
233    'self' => Array &%d (
234        0 => 0
235        'null' => null
236        'boolean' => true
237        'integer' => 1
238        'double' => 1.2
239        'string' => '1'
240        'text' => 'this
241is
242a
243very
244very
245very
246very
247very
248very
249long
250text'
251        'object' => stdClass Object &%x
252        'objectagain' => stdClass Object &%x
253        'array' => Array &%d (
254            'foo' => 'bar'
255        )
256        'self' => Array &%d
257    )
258)
259EOF;
260
261        $this->assertStringMatchesFormat(
262            $expected,
263            $this->trimNewline($this->exporter->export($array))
264        );
265    }
266
267    public function shortenedExportProvider()
268    {
269        $obj = new \stdClass;
270        $obj->foo = 'bar';
271
272        $array = array(
273            'foo' => 'bar',
274        );
275
276        return array(
277            array(null, 'null'),
278            array(true, 'true'),
279            array(1, '1'),
280            array(1.0, '1.0'),
281            array(1.2, '1.2'),
282            array('1', "'1'"),
283            // \n\r and \r is converted to \n
284            array("this\nis\na\nvery\nvery\nvery\nvery\nvery\nvery\rlong\n\rtext", "'this\\nis\\na\\nvery\\nvery\\nvery\\nvery...g\\ntext'"),
285            array(new \stdClass, 'stdClass Object ()'),
286            array($obj, 'stdClass Object (...)'),
287            array(array(), 'Array ()'),
288            array($array, 'Array (...)'),
289        );
290    }
291
292    /**
293     * @dataProvider shortenedExportProvider
294     */
295    public function testShortenedExport($value, $expected)
296    {
297        $this->assertSame(
298            $expected,
299            $this->trimNewline($this->exporter->shortenedExport($value))
300        );
301    }
302
303    /**
304     * @requires extension mbstring
305     */
306    public function testShortenedExportForMultibyteCharacters()
307    {
308        $oldMbLanguage = mb_language();
309        mb_language('Japanese');
310        $oldMbInternalEncoding = mb_internal_encoding();
311        mb_internal_encoding('UTF-8');
312
313        try {
314            $this->assertSame(
315              "'いろはにほへとちりぬるをわかよたれそつねならむうゐのおくや...しゑひもせす'",
316              $this->trimNewline($this->exporter->shortenedExport('いろはにほへとちりぬるをわかよたれそつねならむうゐのおくやまけふこえてあさきゆめみしゑひもせす'))
317            );
318        } catch (\Exception $e) {
319            mb_internal_encoding($oldMbInternalEncoding);
320            mb_language($oldMbLanguage);
321            throw $e;
322        }
323
324        mb_internal_encoding($oldMbInternalEncoding);
325        mb_language($oldMbLanguage);
326    }
327
328    public function provideNonBinaryMultibyteStrings()
329    {
330        return array(
331            array(implode('', array_map('chr', range(0x09, 0x0d))), 5),
332            array(implode('', array_map('chr', range(0x20, 0x7f))), 96),
333            array(implode('', array_map('chr', range(0x80, 0xff))), 128),
334        );
335    }
336
337
338    /**
339     * @dataProvider provideNonBinaryMultibyteStrings
340     */
341    public function testNonBinaryStringExport($value, $expectedLength)
342    {
343        $this->assertRegExp(
344            "~'.{{$expectedLength}}'\$~s",
345            $this->exporter->export($value)
346        );
347    }
348
349    public function testNonObjectCanBeReturnedAsArray()
350    {
351        $this->assertEquals(array(true), $this->exporter->toArray(true));
352    }
353
354    private function trimNewline($string)
355    {
356        return preg_replace('/[ ]*\n/', "\n", $string);
357    }
358}
359