Skip to content

Commit 81e2f24

Browse files
committed
Create ArrayStringifier
1 parent e9de43c commit 81e2f24

File tree

2 files changed

+338
-0
lines changed

2 files changed

+338
-0
lines changed
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Stringifier.
5+
*
6+
* (c) Henrique Moody <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the "LICENSE.md"
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Respect\Stringifier\Stringifiers;
15+
16+
use function array_keys;
17+
use function implode;
18+
use function is_array;
19+
use function is_int;
20+
use function sprintf;
21+
use Respect\Stringifier\Stringifier;
22+
23+
/**
24+
* Converts an array value into a string.
25+
*
26+
* @author Henrique Moody <[email protected]>
27+
*/
28+
final class ArrayStringifier implements Stringifier
29+
{
30+
/**
31+
* @var Stringifier
32+
*/
33+
private $stringifier;
34+
35+
/**
36+
* @var int
37+
*/
38+
private $maximumDepth;
39+
40+
/**
41+
* @var int
42+
*/
43+
private $itemsLimit;
44+
45+
/**
46+
* Initializes the stringifier.
47+
*
48+
* @param Stringifier $stringifier
49+
* @param int $maximumDepth
50+
* @param int $itemsLimit
51+
*/
52+
public function __construct(Stringifier $stringifier, int $maximumDepth, int $itemsLimit)
53+
{
54+
$this->stringifier = $stringifier;
55+
$this->maximumDepth = $maximumDepth;
56+
$this->itemsLimit = $itemsLimit;
57+
}
58+
59+
/**
60+
* {@inheritdoc}
61+
*/
62+
public function stringify($raw, int $depth): ?string
63+
{
64+
if (!is_array($raw)) {
65+
return null;
66+
}
67+
68+
if ($depth >= $this->maximumDepth) {
69+
return '...';
70+
}
71+
72+
if (empty($raw)) {
73+
return '{ }';
74+
}
75+
76+
$items = [];
77+
$itemsCount = 0;
78+
$isSequential = $this->isSequential($raw);
79+
foreach ($raw as $key => $value) {
80+
if (++$itemsCount > $this->itemsLimit) {
81+
$items[$itemsCount] = ' ... ';
82+
break;
83+
}
84+
85+
$items[$itemsCount] = '';
86+
if (false === $isSequential) {
87+
$items[$itemsCount] .= sprintf('%s: ', $this->stringifier->stringify($key, $depth + 1));
88+
}
89+
$items[$itemsCount] .= $this->stringifier->stringify($value, $depth + 1);
90+
}
91+
92+
return sprintf('{ %s }', implode(', ', $items));
93+
}
94+
95+
private function isSequential(array $raw): bool
96+
{
97+
return array_keys($raw) === range(0, count($raw) - 1);
98+
}
99+
}
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
<?php
2+
3+
/*
4+
* This file is part of Respect/Stringifier.
5+
*
6+
* (c) Henrique Moody <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the "LICENSE.md"
9+
* file that was distributed with this source code.
10+
*/
11+
12+
declare(strict_types=1);
13+
14+
namespace Respect\Stringifier\Test\Stringifiers;
15+
16+
use function is_array;
17+
use Respect\Stringifier\Quoter;
18+
use Respect\Stringifier\Stringifier;
19+
use Respect\Stringifier\Stringifiers\ArrayStringifier;
20+
use PHPUnit\Framework\TestCase;
21+
22+
final class ArrayStringifierTest extends TestCase
23+
{
24+
/**
25+
* @test
26+
*/
27+
public function shouldNotConvertToStringWhenRawValueIsNotAnArray(): void
28+
{
29+
$raw = false;
30+
$depth = 0;
31+
32+
$stringifierMock = $this->createMock(Stringifier::class);
33+
$stringifierMock
34+
->expects($this->never())
35+
->method('stringify');
36+
37+
$quoterMock = $this->createMock(Quoter::class);
38+
$quoterMock
39+
->expects($this->never())
40+
->method('quote');
41+
42+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5 );
43+
44+
self::assertNull($arrayStringifier->stringify($raw, $depth));
45+
}
46+
47+
/**
48+
* @test
49+
*/
50+
public function shouldReturnAPlaceHolderWhenDepthIsEqualsToMaximumDepth(): void
51+
{
52+
$raw = [];
53+
$depth = 42;
54+
$maximumDepth = 42;
55+
56+
$expected = '...';
57+
58+
$stringifierMock = $this->createMock(Stringifier::class);
59+
$stringifierMock
60+
->expects($this->never())
61+
->method('stringify');
62+
63+
$quoterMock = $this->createMock(Quoter::class);
64+
$quoterMock
65+
->expects($this->never())
66+
->method('quote');
67+
68+
$arrayStringifier = new ArrayStringifier($stringifierMock, $maximumDepth, 5);
69+
70+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
71+
}
72+
73+
/**
74+
* @test
75+
*/
76+
public function shouldReturnAPlaceHolderWhenDepthIsBiggerThanMaximumDepth(): void
77+
{
78+
$raw = [];
79+
$depth = 42;
80+
$maximumDepth = 41;
81+
82+
$expected = '...';
83+
84+
$stringifierMock = $this->createMock(Stringifier::class);
85+
$stringifierMock
86+
->expects($this->never())
87+
->method('stringify');
88+
89+
$quoterMock = $this->createMock(Quoter::class);
90+
$quoterMock
91+
->expects($this->never())
92+
->method('quote');
93+
94+
$arrayStringifier = new ArrayStringifier($stringifierMock, $maximumDepth, 5);
95+
96+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
97+
}
98+
99+
/**
100+
* @test
101+
*/
102+
public function shouldReturnAPlaceHolderWhenRawValueIsAnEmptyArray(): void
103+
{
104+
$raw = [];
105+
$depth = 0;
106+
107+
$expected = '{ }';
108+
109+
$stringifierMock = $this->createMock(Stringifier::class);
110+
$stringifierMock
111+
->expects($this->never())
112+
->method('stringify');
113+
114+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5);
115+
116+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
117+
}
118+
119+
/**
120+
* @test
121+
*/
122+
public function shouldConvertToStringWhenRawValueIsAnArray(): void
123+
{
124+
$raw = [1, 2, 3];
125+
$depth = 0;
126+
127+
$expected = '{ 1, 2, 3 }';
128+
129+
$stringifierMock = $this->createMock(Stringifier::class);
130+
$stringifierMock
131+
->expects($this->any())
132+
->method('stringify')
133+
->willReturnCallback(function ($raw): string {
134+
return (string) $raw;
135+
});
136+
137+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5);
138+
139+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
140+
}
141+
142+
/**
143+
* @test
144+
*/
145+
public function shouldConvertToStringWhenRawValueIsNested(): void
146+
{
147+
$raw = [1, [2, 3], 4, 5, [6]];
148+
$depth = 0;
149+
150+
$expected = '{ 1, nested, 4, 5, nested }';
151+
152+
$stringifierMock = $this->createMock(Stringifier::class);
153+
$stringifierMock
154+
->expects($this->any())
155+
->method('stringify')
156+
->willReturnCallback(function ($raw): string {
157+
if (is_array($raw)) {
158+
return 'nested';
159+
}
160+
161+
return (string) $raw;
162+
});
163+
164+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5);
165+
166+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
167+
}
168+
169+
/**
170+
* @test
171+
*/
172+
public function shouldConvertToStringWhenKeysAreNotSequential(): void
173+
{
174+
$raw = [1, 2, 3 => 3];
175+
$depth = 0;
176+
177+
$expected = '{ 0: 1, 1: 2, 3: 3 }';
178+
179+
$stringifierMock = $this->createMock(Stringifier::class);
180+
$stringifierMock
181+
->expects($this->any())
182+
->method('stringify')
183+
->willReturnCallback(function ($raw): string {
184+
return (string) $raw;
185+
});
186+
187+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5);
188+
189+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
190+
}
191+
192+
/**
193+
* @test
194+
*/
195+
public function shouldConvertToStringWhenKeysAreNotInteger(): void
196+
{
197+
$raw = ['foo' => 1, 'bar' => 2];
198+
$depth = 0;
199+
200+
$expected = '{ foo: 1, bar: 2 }';
201+
202+
$stringifierMock = $this->createMock(Stringifier::class);
203+
$stringifierMock
204+
->expects($this->any())
205+
->method('stringify')
206+
->willReturnCallback(function ($raw): string {
207+
return (string) $raw;
208+
});
209+
210+
$arrayStringifier = new ArrayStringifier($stringifierMock, 3, 5);
211+
212+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
213+
}
214+
215+
/**
216+
* @test
217+
*/
218+
public function shouldUseAPlaceholderWhenLimitOfItemsIsReached(): void
219+
{
220+
$itemsLimit = 5;
221+
222+
$raw = [1, 2, 3, 4, 5, 6, 7, 8, 9];
223+
$depth = 0;
224+
225+
$expected = '{ 1, 2, 3, 4, 5, ... }';
226+
227+
$stringifierMock = $this->createMock(Stringifier::class);
228+
$stringifierMock
229+
->expects($this->any())
230+
->method('stringify')
231+
->willReturnCallback(function ($raw): string {
232+
return (string) $raw;
233+
});
234+
235+
$arrayStringifier = new ArrayStringifier($stringifierMock, 1, $itemsLimit);
236+
237+
self::assertSame($expected, $arrayStringifier->stringify($raw, $depth));
238+
}
239+
}

0 commit comments

Comments
 (0)