Skip to content

Commit 5bc8e1a

Browse files
committed
Refactored UPD. array func. to only use functions
1 parent a42c2e2 commit 5bc8e1a

File tree

4 files changed

+104
-112
lines changed

4 files changed

+104
-112
lines changed

features/phpcr_query_update.feature

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2
3232
And the node at "/cms/articles/article1" should have the property "tags" with value "Rockets" at index "1"
3333

3434
Scenario: Set a multivalue value
35-
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = [ 'Rockets', 'Dragons' ] WHERE a.tags = 'Trains'" command
35+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array('Rockets', 'Dragons') WHERE a.tags = 'Trains'" command
3636
And I save the session
3737
Then the command should not fail
3838
And the node at "/cms/articles/article1" should have the property "tags" with value "Rockets" at index "0"
@@ -61,7 +61,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2
6161
And the node at "/cms/articles/article1" should have the property "tags" with value "Automobiles" at index "1"
6262

6363
Scenario: Remove single multivalue by index
64-
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags[0] = NULL WHERE a.tags = 'Planes'" command
64+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 0, NULL) WHERE a.tags = 'Planes'" command
6565
And I save the session
6666
Then the command should not fail
6767
And I should see the following:
@@ -84,7 +84,7 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2
8484
And the node at "/cms/articles/article1" should have the property "tags" with value "Kite" at index "3"
8585

8686
Scenario: Replace a multivalue property by index
87-
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags[1] = 'Kite', a.tags[2] = 'foobar' WHERE a.tags = 'Planes'" command
87+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 1, 'Kite'), a.tags = array_set(a.tags, 2, 'foobar') WHERE a.tags = 'Planes'" command
8888
And I save the session
8989
Then the command should not fail
9090
And I should see the following:
@@ -96,17 +96,25 @@ Feature: Execute a a raw UPDATE query in JCR_SQL2
9696
And the node at "/cms/articles/article1" should have the property "tags" with value "foobar" at index "2"
9797

9898
Scenario: Replace a multivalue property by invalid index
99-
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags[10] = 'Kite' WHERE a.tags = 'Planes'" command
99+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 10, 'Kite') WHERE a.tags = 'Planes'" command
100100
Then the command should fail
101101
And I should see the following:
102102
"""
103-
Multivalue index "10" does not exist
103+
Multivalue index "10" does not exist
104+
"""
105+
106+
Scenario: Attempt to update a numerically named property (must use a selector)
107+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, a.10, 'Kite') WHERE a.tags = 'Planes'" command
108+
Then the command should fail
109+
And I should see the following:
110+
"""
111+
[PHPCR\PathNotFoundException] Property 10
104112
"""
105113

106114
Scenario: Replace a multivalue property by invalid index with array (invalid)
107-
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags[1] = ['Kite'] WHERE a.tags = 'Planes'" command
115+
Given I execute the "UPDATE [nt:unstructured] AS a SET a.tags = array_set(a.tags, 0, array('Kite')) WHERE a.tags = 'Planes'" command
108116
Then the command should fail
109117
And I should see the following:
110118
"""
111-
Cannot set index to array value on "tags"
119+
Cannot use an array as a value in a multivalue property
112120
"""

src/PHPCR/Shell/Query/FunctionOperand.php

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,59 @@ public function __construct($functionName, $arguments)
2222
$this->arguments = $arguments;
2323
}
2424

25-
public function replaceColumnOperands(RowInterface $row)
25+
private function replaceColumnOperands($functionMap, RowInterface $row)
2626
{
2727
foreach ($this->arguments as $key => $value) {
2828
if ($value instanceof ColumnOperand) {
2929
$this->arguments[$key] = $row->getNode($value->getSelectorName())->getPropertyValue($value->getPropertyName());
3030
}
31+
32+
if ($value instanceof FunctionOperand) {
33+
$this->arguments[$key] = $value->execute($functionMap, $row, $value);
34+
}
3135
}
3236
}
3337

38+
public function execute($functionMap, $row, $value)
39+
{
40+
$this->replaceColumnOperands($functionMap, $row);
41+
42+
$functionName = $value->getFunctionName();
43+
if (!isset($functionMap[$functionName])) {
44+
throw new InvalidQueryException(sprintf('Unknown function "%s", known functions are "%s"',
45+
$functionName,
46+
implode(', ', array_keys($functionMap))
47+
));
48+
}
49+
50+
$callable = $functionMap[$functionName];
51+
$args = $value->getArguments();
52+
array_unshift($args, $this);
53+
$value = call_user_func_array($callable, $args);
54+
55+
return $value;
56+
}
57+
58+
public function validateScalarArray($array)
59+
{
60+
if (!is_array($array)) {
61+
throw new \InvalidArgumentException(sprintf(
62+
'Expected array value, got: %s',
63+
var_export($array, true)
64+
));
65+
}
66+
67+
foreach ($array as $key => $value) {
68+
if (false == is_scalar($value)) {
69+
throw new \InvalidArgumentException(sprintf(
70+
'Cannot use an array as a value in a multivalue property. Value was: %s',
71+
var_export($array, true)
72+
));
73+
}
74+
}
75+
}
76+
77+
3478
public function getFunctionName()
3579
{
3680
return $this->functionName;

src/PHPCR/Shell/Query/UpdateParser.php

Lines changed: 2 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -116,28 +116,6 @@ private function parseUpdates()
116116
$next = $this->scanner->fetchNextToken();
117117
}
118118

119-
// handle array operations
120-
if ($next === '[') {
121-
$next = $this->scanner->fetchNextToken();
122-
123-
if ($next === ']') {
124-
$property['array_op'] = self::ARRAY_OPERATION_ADD;
125-
} else {
126-
if (is_numeric($next)) {
127-
$property['array_op'] = self::ARRAY_OPERATION_SUB;;
128-
$property['array_index'] = $next;
129-
}
130-
$this->scanner->expectToken(']');
131-
}
132-
133-
// parse operator
134-
$this->scanner->expectToken('=');
135-
} else {
136-
if ($next !== '=') {
137-
throw new InvalidQueryException(sprintf('Expected assignation operator "=", got "%s" in "%s"', $next, $this->sql2));
138-
}
139-
}
140-
141119
// parse right side
142120
$property['value'] = $this->parseOperand();
143121

@@ -159,6 +137,8 @@ private function isLiteral($token)
159137
{
160138
if (substr($token, 0, 1) === '\'') {
161139
return true;
140+
} elseif (is_numeric($token)) {
141+
return true;
162142
} elseif (substr($token, 0, 1) === '"') {
163143
return true;
164144
}
@@ -170,10 +150,6 @@ private function parseOperand()
170150
{
171151
$token = strtoupper($this->scanner->lookupNextToken());
172152

173-
if ('[' === $token) {
174-
return $this->parseArrayValue();
175-
}
176-
177153
if ($this->scanner->lookupNextToken(1) == '(') {
178154
$functionData = $this->parseFunction();
179155
return new FunctionOperand($functionData[0], $functionData[1]);
@@ -192,24 +168,6 @@ private function parseOperand()
192168
return new ColumnOperand($columnData[0], $columnData[1]);
193169
}
194170

195-
private function parseArrayValue()
196-
{
197-
$this->scanner->expectToken('[');
198-
199-
$values = array();
200-
$next = true;
201-
while ($next && $next !== ']') {
202-
$values[] = $this->parseLiteralValue();
203-
204-
$next = $this->scanner->fetchNextToken();
205-
if (!in_array($next, array(',', ']'))) {
206-
throw new InvalidQueryException(sprintf('Invalid array delimiter "%s" in "%s"', $next, $this->sql2));
207-
}
208-
}
209-
210-
return $values;
211-
}
212-
213171
private function parseFunction()
214172
{
215173
$functionName = $this->scanner->fetchNextToken();

src/PHPCR/Shell/Query/UpdateProcessor.php

Lines changed: 42 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ class UpdateProcessor
2222
public function __construct()
2323
{
2424
$this->functionMap = array(
25-
'array_replace' => function ($v, $x, $y) {
25+
'array_replace' => function ($operand, $v, $x, $y) {
26+
$operand->validateScalarArray($v);
2627
foreach ($v as $key => $value) {
2728
if ($value === $x) {
2829
$v[$key] = $y;
@@ -31,7 +32,7 @@ public function __construct()
3132

3233
return $v;
3334
},
34-
'array_remove' => function ($v, $x) {
35+
'array_remove' => function ($operand, $v, $x) {
3536
foreach ($v as $key => $value) {
3637
if ($value === $x) {
3738
unset($v[$key]);
@@ -40,10 +41,38 @@ public function __construct()
4041

4142
return array_values($v);
4243
},
43-
'array_append' => function ($v, $x) {
44+
'array_append' => function ($operand, $v, $x) {
45+
$operand->validateScalarArray($v);
4446
$v[] = $x;
4547
return $v;
4648
},
49+
'array' => function () {
50+
$values = func_get_args();
51+
52+
// first argument is the operand
53+
array_shift($values);
54+
return $values;
55+
},
56+
'array_set' => function ($operand, $current, $index, $value) {
57+
if (!isset($current[$index])) {
58+
throw new \InvalidArgumentException(sprintf(
59+
'Multivalue index "%s" does not exist',
60+
$index
61+
));
62+
}
63+
64+
if (null !== $value && !is_scalar($value)) {
65+
throw new \InvalidArgumentException('Cannot use an array as a value in a multivalue property');
66+
}
67+
68+
if (null === $value) {
69+
unset($current[$index]);
70+
} else {
71+
$current[$index] = $value;
72+
}
73+
74+
return array_values($current);
75+
},
4776
);
4877
}
4978

@@ -71,75 +100,28 @@ protected function handleExisting($row, $node, $propertyData)
71100
$phpcrProperty = $node->getProperty($propertyData['name']);
72101
$value = $propertyData['value'];
73102

74-
if ($phpcrProperty->isMultiple()) {
75-
return $this->handleMultiValue($row, $node, $phpcrProperty, $propertyData);
103+
if ($value instanceof FunctionOperand) {
104+
return $this->handleFunction($row, $node, $phpcrProperty, $propertyData);
76105
}
77106

78107
return $value;
79108
}
80109

81-
protected function handleMultiValue($row, $node, $phpcrProperty, $propertyData)
110+
protected function handleFunction($row, $node, $phpcrProperty, $propertyData)
82111
{
83112
$currentValue = $phpcrProperty->getValue();
84113
$value = $propertyData['value'];
85114

86-
// there is an array operator ([] or [x])
87-
if (isset($propertyData['array_op'])) {
88-
$arrayOp = $propertyData['array_op'];
89-
if ($arrayOp === UpdateParser::ARRAY_OPERATION_ADD) {
90-
foreach ((array) $value as $v) {
91-
$currentValue[] = $v;
92-
}
93-
94-
return $currentValue;
95-
} elseif ($arrayOp === UpdateParser::ARRAY_OPERATION_SUB) {
96-
$arrayIndex = $propertyData['array_index'];
97-
98-
if (!isset($currentValue[$arrayIndex])) {
99-
throw new \InvalidArgumentException(sprintf(
100-
'Multivalue index "%s" does not exist in multivalue field "%s"', $arrayIndex, $propertyData['name']
101-
));
102-
}
103-
104-
if (null === $value) {
105-
unset($currentValue[$arrayIndex]);
106-
return array_values($currentValue);
107-
}
108-
109-
if (is_array($value)) {
110-
throw new \InvalidArgumentException(sprintf('Cannot set index to array value on "%s"', $propertyData['name']));
111-
}
112-
113-
$currentValue[$arrayIndex] = $value;
114-
115-
return $currentValue;
116-
}
117-
}
115+
$value = $value->execute($this->functionMap, $row, $value);
118116

119-
if (is_array($value)) {
120-
return $value;
121-
}
122-
123-
if ($value instanceof FunctionOperand) {
124-
$value->replaceColumnOperands($row);
125-
$functionName = $value->getFunctionName();
126-
if (!isset($this->functionMap[$functionName])) {
127-
throw new InvalidQueryException(sprintf('Unknown function "%s", known functions are "%s"',
128-
$functionName,
129-
implode(', ', array_keys($this->functionMap))
117+
if ($phpcrProperty->isMultiple()) {
118+
// do not allow updating multivalue with scalar
119+
if (false === is_array($value) && sizeof($currentValue) > 1) {
120+
throw new \InvalidArgumentException(sprintf(
121+
'<error>Cannot update multivalue property "%s" with a scalar value.</error>',
122+
$phpcrProperty->getName()
130123
));
131124
}
132-
133-
$callable = $this->functionMap[$functionName];
134-
$value = call_user_func_array($callable, $value->getArguments());
135-
}
136-
137-
// do not allow updating multivalue with scalar
138-
if (false === is_array($value) && sizeof($currentValue) > 1) {
139-
throw new \InvalidArgumentException(sprintf(
140-
'<error>Cannot update multivalue property "%s" with a scalar value.</error>',
141-
$phpcrProperty->getName()
142-
));
143125
}
144126

145127
return $value;

0 commit comments

Comments
 (0)