Skip to content

Commit 8797a0c

Browse files
committed
Fix ForBlock to support single-line syntax
1 parent 52eac08 commit 8797a0c

File tree

2 files changed

+69
-54
lines changed

2 files changed

+69
-54
lines changed

src/Replacer/ForBlock.php

Lines changed: 48 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
namespace Teto\SQL\Replacer;
44

55
use Teto\SQL\ProcessorInterface;
6+
use Teto\SQL\ReplacerInterface;
67

78
use DomainException;
89

@@ -12,54 +13,65 @@
1213
* @copyright 2016 pixiv Inc.
1314
* @license https://github.com/BaguettePHP/TetoSQL/blob/master/LICENSE MPL-2.0
1415
*/
15-
class ForBlock implements ProcessorInterface
16+
class ForBlock implements ReplacerInterface
1617
{
17-
const FOR_PATTERN = '/
18-
^\s*%for\s*(?:\[(?<glue>[^\]]*)\])?\s+(?<name>:[a-zA-Z0-9_]+\s*$) # first line
19-
(?<block>\s[\s\S]*?\s) # block, includes %else
20-
^\s*%endfor$ # block termination
21-
/mx';
18+
const FOR_PATTERN = '(?:^|\s)%for\s*(?:\[(?<forGlue>[^\]]*)\])?\s+(?<forName>:[a-zA-Z0-9_]+\s) # first line
19+
(?<forBlock>\s[\s\S]*?)\s # block, includes %else
20+
(?:^|\s*)%endfor(?:\s|$) # block termination
21+
';
2222

23-
/** @var DynamicPlaceholder */
24-
private $placeholder_replacer;
23+
/** @phpstan-var list<ProcessorInterface> */
24+
private $processors;
2525

26-
public function __construct(DynamicPlaceholder $placeholder_replacer)
26+
/**
27+
* @phpstan-param list<ProcessorInterface> $processors
28+
*/
29+
public function __construct(array $processors)
2730
{
28-
$this->placeholder_replacer = $placeholder_replacer;
31+
$this->processors = $processors;
2932
}
3033

31-
public function processQuery($pdo, $sql, array $params, array &$bind_values)
34+
public function getKey()
3235
{
33-
$built_sql = preg_replace_callback(self::FOR_PATTERN, function (array $matches) use (
34-
$pdo, $params, &$bind_values
35-
) {
36-
$glue = $matches['glue'];
37-
$name = rtrim($matches['name']);
38-
if (!isset($params[$name])) {
39-
throw new DomainException(sprintf('Must be assigned parameter %s.', $name));
40-
}
41-
if (!is_array($params[$name])) {
42-
throw new DomainException(sprintf('Parameter %s must be an array.', $name));
43-
}
36+
return 'for';
37+
}
4438

45-
/** @phpstan-var array<non-empty-string, array<non-empty-string, mixed>> $array */
46-
$array = $params[$name];
39+
public function getPattern()
40+
{
41+
return self::FOR_PATTERN;
42+
}
4743

48-
$block = rtrim($matches['block'], "\n");
49-
if (strpos($block, '%for') !== false) {
50-
throw new DomainException('Nested %for is not supported.');
51-
}
44+
public function replaceQuery($pdo, array $matches, array $params, array &$bind_values)
45+
{
46+
$glue = $matches['forGlue'];
47+
if ($glue === '') {
48+
$glue = ',';
49+
}
50+
$name = rtrim($matches['forName']);
51+
if (!isset($params[$name])) {
52+
throw new DomainException(sprintf('Must be assigned parameter %s.', $name));
53+
}
54+
if (!is_array($params[$name])) {
55+
throw new DomainException(sprintf('Parameter %s must be an array.', $name));
56+
}
5257

53-
$replaced = [];
54-
foreach ($array as $row) {
55-
$replaced[] = $this->placeholder_replacer->processQuery($pdo, $block, $row, $bind_values);
56-
}
58+
/** @phpstan-var array<non-empty-string, array<non-empty-string, mixed>> $array */
59+
$array = $params[$name];
5760

58-
return implode($glue, $replaced);
59-
}, $sql);
61+
$block = $matches['forBlock'];
62+
if (strpos($block, '%for') !== false) {
63+
throw new DomainException('Nested %for is not supported.');
64+
}
6065

61-
assert($built_sql !== null);
66+
$replaced = [];
67+
foreach ($array as $row) {
68+
$new = $block;
69+
foreach ($this->processors as $processor) {
70+
$new = $processor->processQuery($pdo, $new, $row, $bind_values);
71+
}
72+
$replaced[] = \ltrim($new);
73+
}
6274

63-
return $built_sql;
75+
return implode($glue, $replaced);
6476
}
6577
}

tests/Replacer/ForBlockTest.php

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
use OutOfRangeException;
77
use Teto\SQL\QueryBuilder;
88
use Teto\SQL\DummyPDO;
9+
use Teto\SQL\Processor\PregCallbackReplacer;
910
use Yoast\PHPUnitPolyfills\Polyfills\ExpectException;
1011
use Yoast\PHPUnitPolyfills\Polyfills\ExpectPHPException;
1112
use Yoast\PHPUnitPolyfills\TestCases\TestCase;
@@ -20,14 +21,14 @@ final class ForBlockTest extends TestCase
2021
use ExpectException;
2122
use ExpectPHPException;
2223

23-
/** @var ForBlock */
24+
/** @var PregCallbackReplacer */
2425
private $subject;
2526

2627
public function set_up()
2728
{
2829
$placeholder_replacer = new Placeholder();
29-
$this->subject = new ForBlock([
30-
$placeholder_replacer
30+
$this->subject = new PregCallbackReplacer([
31+
new ForBlock([new PregCallbackReplacer([$placeholder_replacer])])
3132
]);
3233
}
3334

@@ -77,38 +78,40 @@ public function acceptDataProvider()
7778
[':a' => 'A3', ':b' => 'B3'],
7879
]
7980
],
80-
' @A1@ - @B1@
81-
@A2@ - @B2@
82-
@A3@ - @B3@
83-
',
81+
'@A1@ - @B1@,@A2@ - @B2@,@A3@ - @B3@',
8482
],
8583
[
86-
'%for :arr :a@string - :b@string %endfor',
84+
'%for :arr :a@string - :b@string %endfor',
8785
[
8886
':arr' => [
8987
[':a' => 'A1', ':b' => 'B1'],
9088
[':a' => 'A2', ':b' => 'B2'],
9189
[':a' => 'A3', ':b' => 'B3'],
9290
]
9391
],
94-
' @A1@ - @B1@
95-
@A2@ - @B2@
96-
@A3@ - @B3@
97-
',
92+
'@A1@ - @B1@,@A2@ - @B2@,@A3@ - @B3@',
9893
],
9994
[
100-
'%for[] :arr :a@string - :b@string %endfor ',
95+
'%for[] :arr :a@string - :b@string %endfor ',
10196
[
10297
':arr' => [
10398
[':a' => 'A1', ':b' => 'B1'],
10499
[':a' => 'A2', ':b' => 'B2'],
105100
[':a' => 'A3', ':b' => 'B3'],
106101
]
107102
],
108-
' @A1@ - @B1@
109-
@A2@ - @B2@
110-
@A3@ - @B3@
111-
',
103+
'@A1@ - @B1@,@A2@ - @B2@,@A3@ - @B3@',
104+
],
105+
[
106+
'%for[,] :arr :a@string - :b@string %endfor ',
107+
[
108+
':arr' => [
109+
[':a' => 'A1', ':b' => 'B1'],
110+
[':a' => 'A2', ':b' => 'B2'],
111+
[':a' => 'A3', ':b' => 'B3'],
112+
]
113+
],
114+
'@A1@ - @B1@,@A2@ - @B2@,@A3@ - @B3@',
112115
],
113116
];
114117
}
@@ -159,7 +162,7 @@ public function rejeceptDataProvider()
159162
]
160163
],
161164
[
162-
'class' => get_class(new OutOfRangeException),
165+
'class' => get_class(new OutOfRangeException()),
163166
'message' => 'param ":b" expected but not assigned',
164167
]
165168
],

0 commit comments

Comments
 (0)