Skip to content

Commit

Permalink
Improve Query feature codebase
Browse files Browse the repository at this point in the history
  • Loading branch information
nyamsprod committed May 23, 2024
1 parent 00bbff9 commit 9e3df37
Show file tree
Hide file tree
Showing 15 changed files with 92 additions and 55 deletions.
6 changes: 0 additions & 6 deletions src/Query/Constraint/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,6 @@
use ReflectionException;
use Traversable;

use function is_array;
use function array_filter;
use function iterator_to_array;

use const ARRAY_FILTER_USE_BOTH;

/**
* Enable filtering a record based on the value of a one of its cell.
*
Expand Down
6 changes: 6 additions & 0 deletions src/Query/Constraint/Comparison.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ public static function fromOperator(string $operator): self
}

/**
* Values comparison.
*
* The method return true if the values satisfy the comparison operator, otherwise false is returned.
*
* @throws QueryException
*/
public function compare(mixed $subject, mixed $reference): bool
Expand Down Expand Up @@ -108,6 +112,8 @@ private static function isSingleValue(mixed $value): bool
}

/**
* Assert if the reference value can be used with the Enum operator.
*
* @throws QueryException
*/
public function accept(mixed $reference): void
Expand Down
9 changes: 3 additions & 6 deletions src/Query/Constraint/Criteria.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,8 @@
use League\Csv\Query\Predicate;
use League\Csv\Query\PredicateCombinator;

use Traversable;
use function array_reduce;
use function is_array;
use function array_filter;

use const ARRAY_FILTER_USE_BOTH;

/**
* @phpstan-import-type Condition from PredicateCombinator
Expand Down Expand Up @@ -116,9 +113,9 @@ public function __invoke(mixed $value, int|string $key): bool
public function filter(iterable $value): Iterator
{
return new CallbackFilterIterator(match (true) {
is_array($value) => new ArrayIterator($value),
$value instanceof Iterator => $value,
default => new IteratorIterator($value),
$value instanceof Traversable => new IteratorIterator($value),
default => new ArrayIterator($value),
}, $this);
}

Expand Down
7 changes: 3 additions & 4 deletions src/Query/Limit.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@
use LimitIterator;
use Traversable;

use function array_slice;
use function is_array;
use function iterator_to_array;

final class Limit
{
private function __construct(
Expand All @@ -43,6 +39,9 @@ public static function new(int $offset, int $length): self
return new self($offset, $length);
}

/**
* Allows iteration over a limited subset of items in an iterable structure.
*/
public function slice(iterable $value): LimitIterator
{
return new LimitIterator(
Expand Down
7 changes: 6 additions & 1 deletion src/Query/Ordering/Column.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
use OutOfBoundsException;
use ReflectionException;

use function is_array;
use function is_string;
use function iterator_to_array;
use function strtoupper;
use function trim;

Expand All @@ -45,8 +48,10 @@ private function __construct(

/**
* @param ?Closure(mixed, mixed): int $callback
*
* @throws QueryException
*/
public static function sortBy(
public static function sortOn(
string|int $column,
string|int $direction,
?Closure $callback = null
Expand Down
6 changes: 3 additions & 3 deletions src/Query/Ordering/ColumnTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ final class ColumnTest extends QueryTestCase
public function it_can_order_the_tabular_date_in_descending_order(): void
{
$stmt = $this->stmt->orderBy(
Column::sortBy('Country', 'down')
Column::sortOn('Country', 'down')
);

self::assertSame('UK', $stmt->process($this->document)->first()['Country']);
Expand All @@ -33,7 +33,7 @@ public function it_can_order_the_tabular_date_in_descending_order(): void
public function it_can_order_the_tabular_date_in_ascending_order(): void
{
$stmt = $this->stmt->orderBy(
Column::sortBy('Country', 'up')
Column::sortOn('Country', 'up')
);

self::assertSame('UK', $stmt->process($this->document)->nth(4)['Country']);
Expand All @@ -43,7 +43,7 @@ public function it_can_order_the_tabular_date_in_ascending_order(): void
public function it_can_order_using_a_specific_order_algo(): void
{
$stmt = $this->stmt->orderBy(
Column::sortBy(
Column::sortOn(
'Country',
'desc',
fn (string $first, string $second): int => strlen($first) <=> strlen($second) /* @phpstan-ignore-line */
Expand Down
2 changes: 1 addition & 1 deletion src/Query/Ordering/MultiSort.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@

use ArrayIterator;
use Closure;

use Iterator;
use IteratorIterator;
use League\Csv\Query\Sort;
use League\Csv\Query\SortCombinator;
use OutOfBoundsException;
use Traversable;

use function array_map;

/**
Expand Down
7 changes: 3 additions & 4 deletions src/Query/Ordering/MultiSortTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
use PHPUnit\Framework\Attributes\Test;

final class MultiSortTest extends QueryTestCase

{
#[Test]
public function it_will_sort_nothing_if_no_sort_algorithm_is_provided(): void
Expand All @@ -32,7 +31,7 @@ public function it_will_sort_nothing_if_no_sort_algorithm_is_provided(): void
public function it_can_order_the_tabular_date_when_an_algo_is_provided(): void
{
$stmt = $this->stmt->orderBy(
MultiSort::all()->append(Column::sortBy('Country', 'up'))
MultiSort::all()->append(Column::sortOn('Country', 'up'))
);

self::assertSame('UK', $stmt->process($this->document)->nth(4)['Country']);
Expand All @@ -41,8 +40,8 @@ public function it_can_order_the_tabular_date_when_an_algo_is_provided(): void
#[Test]
public function it_respect_the_fifo_order_to_apply_sorting(): void
{
$countryOrder = Column::sortBy('Country', 'ASC');
$idOrder = Column::sortBy('CustomerID', 'DeSc');
$countryOrder = Column::sortOn('Country', 'ASC');
$idOrder = Column::sortOn('CustomerID', 'DeSc');

self::assertNotSame(
$this->stmt
Expand Down
11 changes: 11 additions & 0 deletions src/Query/Predicate.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@
*/
interface Predicate
{
/**
* The class predicate method.
*
* Evaluates each element of an iterable structure based on its value and its offset.
* The method must return true if the predicate is satisfied, false otherwise.
*/
public function __invoke(mixed $value, string|int $key): bool;

/**
* Filters elements of an iterable structure using the class predicate method.
*
* @see Predicate::__invoke
*/
public function filter(iterable $value): Iterator;
}
1 change: 0 additions & 1 deletion src/Query/PredicateCombinator.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
namespace League\Csv\Query;

use Closure;
use League\Csv\Query\Predicate;

/**
* @phpstan-type Condition Predicate|Closure(mixed, array-key): bool
Expand Down
48 changes: 24 additions & 24 deletions src/Query/Row.php
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static function from(mixed $value): Row
});
}

private function __construct(private readonly array|object $record)
private function __construct(private readonly array|object $row)
{
}

Expand All @@ -51,7 +51,7 @@ private function __construct(private readonly array|object $record)
*
* @throws ReflectionException
* @throws QueryException If the value can not be retrieved
*@see Row::select()
* @see Row::select()
*
*/
public function value(string|int $key): mixed
Expand All @@ -74,8 +74,8 @@ public function value(string|int $key): mixed
public function select(string|int ...$key): array
{
return match (true) {
is_object($this->record) => self::getObjectPropertyValue($this->record, ...$key),
default => self::getArrayEntry($this->record, ...$key),
is_object($this->row) => self::getObjectPropertyValue($this->row, ...$key),
default => self::getArrayEntry($this->row, ...$key),
};
}

Expand All @@ -84,26 +84,26 @@ public function select(string|int ...$key): array
*
* @return non-empty-array<array-key, mixed>
*/
private function getArrayEntry(array $value, string|int ...$keys): array
private function getArrayEntry(array $row, string|int ...$keys): array
{
$res = [];
$arrValues = array_values($value);
$arrValues = array_values($row);
foreach ($keys as $key) {
if (array_key_exists($key, $res)) {
continue;
}
$offset = $key;
if (is_int($offset)) {
if (!array_is_list($value)) {
$value = $arrValues;
if (!array_is_list($row)) {
$row = $arrValues;
}

if ($offset < 0) {
$offset += count($value);
$offset += count($row);
}
}

$res[$key] = array_key_exists($offset, $value) ? $value[$offset] : throw QueryException::dueToUnknownColumn($key, $value);
$res[$key] = array_key_exists($offset, $row) ? $row[$offset] : throw QueryException::dueToUnknownColumn($key, $row);
}

return [] !== $res ? $res : throw QueryException::dueToMissingColumn();
Expand All @@ -115,21 +115,21 @@ private function getArrayEntry(array $value, string|int ...$keys): array
*
* @return non-empty-array<array-key, mixed>
*/
private static function getObjectPropertyValue(object $value, string|int ...$keys): array
private static function getObjectPropertyValue(object $row, string|int ...$keys): array
{
$res = [];
$refl = new ReflectionObject($value);
$object = new ReflectionObject($row);
foreach ($keys as $key) {
if (array_key_exists($key, $res)) {
continue;
}

if (is_int($key)) {
throw QueryException::dueToUnknownColumn($key, $value);
throw QueryException::dueToUnknownColumn($key, $row);
}

if ($refl->hasProperty($key) && $refl->getProperty($key)->isPublic()) {
$res[$key] = $refl->getProperty($key)->getValue($value);
if ($object->hasProperty($key) && $object->getProperty($key)->isPublic()) {
$res[$key] = $object->getProperty($key)->getValue($row);
continue;
}

Expand All @@ -139,26 +139,26 @@ private static function getObjectPropertyValue(object $value, string|int ...$key
}
$methodNameList[] = self::camelCase($key, 'get');
foreach ($methodNameList as $methodName) {
if ($refl->hasMethod($methodName)
&& $refl->getMethod($methodName)->isPublic()
&& 1 > $refl->getMethod($methodName)->getNumberOfRequiredParameters()
if ($object->hasMethod($methodName)
&& $object->getMethod($methodName)->isPublic()
&& 1 > $object->getMethod($methodName)->getNumberOfRequiredParameters()
) {
$res[$key] = $refl->getMethod($methodName)->invoke($value);
$res[$key] = $object->getMethod($methodName)->invoke($row);
continue 2;
}
}

if (method_exists($value, '__call')) {
$res[$key] = $refl->getMethod('__call')->invoke($value, $methodNameList[1]);
if (method_exists($row, '__call')) {
$res[$key] = $object->getMethod('__call')->invoke($row, $methodNameList[1]);
continue;
}

if ($value instanceof ArrayAccess && $value->offsetExists($key)) {
$res[$key] = $value->offsetGet($key);
if ($row instanceof ArrayAccess && $row->offsetExists($key)) {
$res[$key] = $row->offsetGet($key);
continue;
}

throw QueryException::dueToUnknownColumn($key, $value);
throw QueryException::dueToUnknownColumn($key, $row);
}

return [] !== $res ? $res : throw QueryException::dueToMissingColumn();
Expand Down
10 changes: 10 additions & 0 deletions src/Query/Sort.php
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,17 @@
*/
interface Sort
{
/**
* The class comparison method.
*
* The method must return an integer less than, equal to, or greater than zero
* if the first argument is considered to be respectively less than, equal to,
* or greater than the second.
*/
public function __invoke(mixed $valueA, mixed $valueB): int;

/**
* Sort an iterable structure with the class comparison method and maintain index association.
*/
public function sort(iterable $value): Iterator;
}
17 changes: 17 additions & 0 deletions src/Serializer/Denormalizer.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,24 +55,39 @@ public function __construct(string $className, array $propertyNames = [])
$this->postMapCalls = $this->setPostMapCalls();
}

/**
* Enable converting empty string to the null value.
*/
public static function allowEmptyStringAsNull(): void
{
self::$emptyStringAsNull = true;
}

/**
* Disable converting empty string to the null value.
*/
public static function disallowEmptyStringAsNull(): void
{
self::$emptyStringAsNull = false;
}

/**
* Register a global type conversion callback to convert a field into a specific type.
*
* @throws MappingFailed
*/
public static function registerType(string $type, Closure $callback): void
{
CallbackCasting::register($type, $callback);
}

/**
* Unregister a global type conversion callback to convert a field into a specific type.
*
* @param string $type
*
* @return bool
*/
public static function unregisterType(string $type): bool
{
return CallbackCasting::unregisterType($type);
Expand All @@ -84,6 +99,8 @@ public static function unregisterAllTypes(): void
}

/**
* Register a callback to convert a field into a specific type.
*
* @throws MappingFailed
*/
public static function registerAlias(string $alias, string $type, Closure $callback): void
Expand Down
Loading

0 comments on commit 9e3df37

Please sign in to comment.