Skip to content

Commit ccca2f1

Browse files
Merge pull request #4 from transistive/literals
Literals
2 parents fffdd61 + 1e4c9ac commit ccca2f1

File tree

3 files changed

+155
-16
lines changed

3 files changed

+155
-16
lines changed

src/Query.php

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -125,35 +125,42 @@ public static function variable(string $variable): Variable
125125
* Creates a new literal from the given value. This function automatically constructs the appropriate
126126
* class based on the type of the value given.
127127
*
128-
* @param integer|float|bool|string $literal The literal to construct
128+
* @param mixed $literal The literal to construct
129129
* @return StringLiteral|Boolean|Decimal
130130
*/
131131
public static function literal($literal): PropertyType
132132
{
133-
$literalType = gettype($literal);
134-
135-
switch ($literalType) {
136-
case "string":
137-
return new StringLiteral($literal);
138-
case "boolean":
139-
return new Boolean($literal);
140-
case "double":
141-
case "float":
142-
case "integer":
143-
return new Decimal($literal);
144-
default:
145-
throw new InvalidArgumentException("The literal type " . $literalType . " is not supported by Cypher");
133+
if (is_string($literal) || (is_object($literal) && method_exists($literal, '__toString'))) {
134+
return new StringLiteral((string) $literal);
146135
}
136+
137+
if (is_bool($literal)) {
138+
return new Boolean($literal);
139+
}
140+
141+
if (is_int($literal) || is_float($literal)) {
142+
return new Decimal($literal);
143+
}
144+
145+
$actualType = is_object($literal) ? get_class($literal) : gettype($literal);
146+
147+
throw new InvalidArgumentException("The literal type " . $actualType . " is not supported by Cypher");
147148
}
148149

149150
/**
150151
* Creates a list of expressions.
151152
*
152-
* @param AnyType[] $expressions
153+
* @param iterable $values
153154
* @return ExpressionList
154155
*/
155-
public static function list(array $expressions): ExpressionList
156+
public static function list(iterable $values): ExpressionList
156157
{
158+
$expressions = [];
159+
foreach ($values as $value) {
160+
$expressions[] = $value instanceof AnyType ?
161+
$value : self::literal($value);
162+
}
163+
157164
return new ExpressionList($expressions);
158165
}
159166

tests/Unit/InTest.php

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
use PHPUnit\Framework\TestCase;
2525
use TypeError;
26+
use WikibaseSolutions\CypherDSL\ExpressionList;
2627
use WikibaseSolutions\CypherDSL\In;
28+
use WikibaseSolutions\CypherDSL\Literals\StringLiteral;
2729
use WikibaseSolutions\CypherDSL\Types\AnyType;
2830
use WikibaseSolutions\CypherDSL\Types\CompositeTypes\ListType;
2931
use WikibaseSolutions\CypherDSL\Types\PropertyTypes\PropertyType;
@@ -46,6 +48,17 @@ public function testToQuery()
4648
$this->assertSame("((a IN b) IN c)", $inequality->toQuery());
4749
}
4850

51+
public function testInExpressionList()
52+
{
53+
$inequality = new In($this->getQueryConvertableMock(PropertyType::class, "a"), new ExpressionList([new StringLiteral('a'), new StringLiteral('b')]));
54+
55+
$this->assertSame("(a IN ['a', 'b'])", $inequality->toQuery());
56+
57+
$inequality = new In($inequality, $this->getQueryConvertableMock(ListType::class, "c"));
58+
59+
$this->assertSame("((a IN ['a', 'b']) IN c)", $inequality->toQuery());
60+
}
61+
4962
public function testDoesNotAcceptAnyTypeAsOperands()
5063
{
5164
$this->expectException(TypeError::class);

tests/Unit/QueryTest.php

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
namespace WikibaseSolutions\CypherDSL\Tests\Unit;
2323

24+
use InvalidArgumentException;
2425
use PHPUnit\Framework\TestCase;
2526
use WikibaseSolutions\CypherDSL\Assignment;
2627
use WikibaseSolutions\CypherDSL\Clauses\Clause;
@@ -114,6 +115,77 @@ public function testList()
114115
$this->assertInstanceOf(ExpressionList::class, $list);
115116
}
116117

118+
public function testListOfLiterals()
119+
{
120+
$list = Query::list(["hello", "world", 1.0, 1, 2, 3, true]);
121+
122+
$this->assertInstanceOf(ExpressionList::class, $list);
123+
}
124+
125+
public function testListOfMixed()
126+
{
127+
$list = Query::list([$this->getQueryConvertableMock(AnyType::class, "hello"), "world"]);
128+
129+
$this->assertInstanceOf(ExpressionList::class, $list);
130+
}
131+
132+
public function testListOfAnyType()
133+
{
134+
$list = Query::list([$this->getQueryConvertableMock(AnyType::class, "hello"), $this->getQueryConvertableMock(AnyType::class, "world")]);
135+
136+
$this->assertInstanceOf(ExpressionList::class, $list);
137+
}
138+
139+
public function testNestedList()
140+
{
141+
$list = Query::list([Query::list([])]);
142+
143+
$this->assertInstanceOf(ExpressionList::class, $list);
144+
}
145+
146+
public function testIteratorList()
147+
{
148+
$iterator = new class implements \Iterator {
149+
private int $count = 0;
150+
151+
public function current()
152+
{
153+
return 1;
154+
}
155+
156+
public function next()
157+
{
158+
$this->count++;
159+
return 1;
160+
}
161+
162+
public function key()
163+
{
164+
return 0;
165+
}
166+
167+
public function valid()
168+
{
169+
// In order to avoid an infinite loop
170+
return $this->count < 10;
171+
}
172+
173+
public function rewind()
174+
{
175+
}
176+
};
177+
178+
$list = Query::list($iterator);
179+
180+
$this->assertInstanceOf(ExpressionList::class, $list);
181+
}
182+
183+
public function testInvalidList()
184+
{
185+
$this->expectException(InvalidArgumentException::class);
186+
Query::list([new class() {}]);
187+
}
188+
117189
public function testMap()
118190
{
119191
$map = Query::map([]);
@@ -373,6 +445,53 @@ public function testBuildEmpty()
373445
$this->assertSame("", $query->build());
374446
}
375447

448+
public function testInt()
449+
{
450+
$literal = Query::literal(1);
451+
self::assertInstanceOf(Decimal::class, $literal);
452+
self::assertEquals('1', $literal->toQuery());
453+
}
454+
455+
public function testFloat()
456+
{
457+
$literal = Query::literal(1.2);
458+
self::assertInstanceOf(Decimal::class, $literal);
459+
self::assertEquals('1.2', $literal->toQuery());
460+
}
461+
462+
public function testString()
463+
{
464+
$literal = Query::literal('abc');
465+
self::assertInstanceOf(StringLiteral::class, $literal);
466+
self::assertEquals("'abc'", $literal->toQuery());
467+
}
468+
469+
public function testStringAble()
470+
{
471+
$literal = Query::literal(new class () {
472+
public function __toString(): string
473+
{
474+
return 'stringable abc';
475+
}
476+
});
477+
self::assertInstanceOf(StringLiteral::class, $literal);
478+
self::assertEquals("'stringable abc'", $literal->toQuery());
479+
}
480+
481+
public function testBool()
482+
{
483+
$literal = Query::literal(true);
484+
self::assertInstanceOf(Boolean::class, $literal);
485+
self::assertEquals("true", $literal->toQuery());
486+
}
487+
488+
public function testInvalidLiteral()
489+
{
490+
$literal = Query::literal(true);
491+
$this->expectException(InvalidArgumentException::class);
492+
Query::literal($literal);
493+
}
494+
376495
public function testWikiExamples()
377496
{
378497
/*

0 commit comments

Comments
 (0)