Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
214 changes: 89 additions & 125 deletions src/ResultSet/AbstractResultSet.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,7 @@

use function count;
use function current;
use function gettype;
use function is_array;
use function is_object;
use function method_exists;
use function reset;

abstract class AbstractResultSet implements ResultSetInterface
Expand All @@ -43,70 +40,63 @@ abstract class AbstractResultSet implements ResultSetInterface
protected int $position = 0;

/**
* Set the data source for the result set
*
* @throws InvalidArgumentException|Exception
* @throws RuntimeException
*/
#[Override]
public function initialize(iterable $dataSource): ResultSetInterface
public function buffer(): ResultSetInterface
{
// reset buffering
if (is_array($this->buffer)) {
if ($this->buffer === -2) {
throw new RuntimeException('Buffering must be enabled before iteration is started');
} elseif ($this->buffer === null) {
$this->buffer = [];
}

if ($dataSource instanceof ResultInterface) {
$this->fieldCount = $dataSource->getFieldCount();
$this->dataSource = $dataSource;
if ($dataSource->isBuffered()) {
$this->buffer = -1;
}

if (is_array($this->buffer)) {
if ($this->dataSource instanceof ResultInterface) {
$this->dataSource->rewind();
}

return $this;
}

if (is_array($dataSource)) {
// its safe to get numbers from an array
$first = current($dataSource);
reset($dataSource);
$this->fieldCount = $first === false ? 0 : count($first);
$this->dataSource = new ArrayIterator($dataSource);
$this->buffer = -1; // array's are a natural buffer
} elseif ($dataSource instanceof IteratorAggregate) {
/** @phpstan-ignore assign.propertyType */
$this->dataSource = $dataSource->getIterator();
} else {
/** @phpstan-ignore assign.propertyType */
$this->dataSource = $dataSource;
}

return $this;
}

/**
* @throws RuntimeException
* Countable: return count of rows
*/
public function buffer(): ResultSetInterface
#[Override]
#[ReturnTypeWillChange]
public function count(): ?int
{
if ($this->buffer === -2) {
throw new RuntimeException('Buffering must be enabled before iteration is started');
} elseif ($this->buffer === null) {
$this->buffer = [];
if ($this->dataSource instanceof ResultInterface) {
$this->dataSource->rewind();
}
if ($this->count !== null) {
return $this->count;
}

return $this;
if ($this->dataSource instanceof Countable) {
$this->count = count($this->dataSource);
}

return $this->count;
}

public function isBuffered(): bool
/**
* Iterator: get current item
*/
#[Override]
public function current(): array|object|null
{
return $this->buffer === -1 || is_array($this->buffer);
if (-1 === $this->buffer) {
// datasource was an array when the resultset was initialized
return $this->dataSource->current();
}

if ($this->buffer === null) {
$this->buffer = -2; // implicitly disable buffering from here on
} elseif (is_array($this->buffer) && isset($this->buffer[$this->position])) {
return $this->buffer[$this->position];
}

$data = $this->dataSource->current();
if (is_array($this->buffer)) {
$this->buffer[$this->position] = $data;
}

return is_array($data) ? $data : null;
}

/**
Expand Down Expand Up @@ -150,20 +140,53 @@ public function getFieldCount(): int
}

/**
* Iterator: move pointer to next item
* Set the data source for the result set
*
* @throws InvalidArgumentException|Exception
*/
#[Override]
public function next(): void
public function initialize(iterable $dataSource): ResultSetInterface
{
if ($this->buffer === null) {
$this->buffer = -2; // implicitly disable buffering from here on
// reset buffering
if (is_array($this->buffer)) {
$this->buffer = [];
}

if (! is_array($this->buffer) || $this->position === $this->dataSource->key()) {
$this->dataSource->next();
if ($dataSource instanceof ResultInterface) {
$this->fieldCount = $dataSource->getFieldCount();
$this->dataSource = $dataSource;
if ($dataSource->isBuffered()) {
$this->buffer = -1;
}

if (is_array($this->buffer)) {
$this->dataSource->rewind();
}

return $this;
}

$this->position++;
if (is_array($dataSource)) {
// its safe to get numbers from an array
$first = current($dataSource);
reset($dataSource);
$this->fieldCount = $first === false ? 0 : count($first);
$this->dataSource = new ArrayIterator($dataSource);
$this->buffer = -1; // array's are a natural buffer
} elseif ($dataSource instanceof IteratorAggregate) {
/** @phpstan-ignore assign.propertyType */
$this->dataSource = $dataSource->getIterator();
} else {
/** @phpstan-ignore assign.propertyType */
$this->dataSource = $dataSource;
}

return $this;
}

public function isBuffered(): bool
{
return $this->buffer === -1 || is_array($this->buffer);
}

/**
Expand All @@ -176,41 +199,20 @@ public function key(): int
}

/**
* Iterator: get current item
* Iterator: move pointer to next item
*/
#[Override]
public function current(): array|object|null
public function next(): void
{
if (-1 === $this->buffer) {
// datasource was an array when the resultset was initialized
return $this->dataSource->current();
}

if ($this->buffer === null) {
$this->buffer = -2; // implicitly disable buffering from here on
} elseif (is_array($this->buffer) && isset($this->buffer[$this->position])) {
return $this->buffer[$this->position];
}

$data = $this->dataSource->current();
if (is_array($this->buffer)) {
$this->buffer[$this->position] = $data;
}

return is_array($data) ? $data : null;
}

/**
* Iterator: is pointer valid?
*/
#[Override]
public function valid(): bool
{
if (is_array($this->buffer) && isset($this->buffer[$this->position])) {
return true;
if (! is_array($this->buffer) || $this->position === $this->dataSource->key()) {
$this->dataSource->next();
}

return $this->dataSource->valid();
$this->position++;
}

/**
Expand All @@ -227,53 +229,15 @@ public function rewind(): void
}

/**
* Countable: return count of rows
*/
#[Override]
#[ReturnTypeWillChange]
public function count(): ?int
{
if ($this->count !== null) {
return $this->count;
}

if ($this->dataSource instanceof Countable) {
$this->count = count($this->dataSource);
}

return $this->count;
}

/**
* Cast result set to array of arrays
*
* @throws RuntimeException If any row is not castable to an array.
* Iterator: is pointer valid?
*/
#[Override]
public function toArray(): array
public function valid(): bool
{
$return = [];
foreach ($this as $row) {
if (is_array($row)) {
$return[] = $row;
continue;
}

if (
! is_object($row)
|| (
! method_exists($row, 'toArray')
&& ! method_exists($row, 'getArrayCopy')
)
) {
throw new RuntimeException(
'Rows as part of this DataSource, with type ' . gettype($row) . ' cannot be cast to an array'
);
}

$return[] = method_exists($row, 'toArray') ? $row->toArray() : $row->getArrayCopy();
if (is_array($this->buffer) && isset($this->buffer[$this->position])) {
return true;
}

return $return;
return $this->dataSource->valid();
}
}
17 changes: 17 additions & 0 deletions src/ResultSet/ArrayObjectResultSetInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace PhpDb\ResultSet;

use ArrayObject;

/**
* Capability interface for a ResultSet whose rows clone an ArrayObject prototype.
*/
interface ArrayObjectResultSetInterface
{
public function getRowPrototype(): ArrayObject;

public function setRowPrototype(ArrayObject $rowPrototype): ResultSetInterface&ArrayObjectResultSetInterface;
}
4 changes: 1 addition & 3 deletions src/ResultSet/Exception/ExceptionInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

use PhpDb\Exception;

interface ExceptionInterface extends Exception\ExceptionInterface
{
}
interface ExceptionInterface extends Exception\ExceptionInterface {}

Check failure on line 9 in src/ResultSet/Exception/ExceptionInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/ExceptionInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a interface must be on the line after the definition

Check failure on line 9 in src/ResultSet/Exception/ExceptionInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/ExceptionInterface.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a interface must be on the line after the definition
4 changes: 1 addition & 3 deletions src/ResultSet/Exception/InvalidArgumentException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

use PhpDb\Exception;

class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface
{
}
class InvalidArgumentException extends Exception\InvalidArgumentException implements ExceptionInterface {}

Check failure on line 9 in src/ResultSet/Exception/InvalidArgumentException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/InvalidArgumentException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a class must be on the line after the definition

Check failure on line 9 in src/ResultSet/Exception/InvalidArgumentException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/InvalidArgumentException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a class must be on the line after the definition
4 changes: 1 addition & 3 deletions src/ResultSet/Exception/RuntimeException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,4 @@

use PhpDb\Exception;

class RuntimeException extends Exception\RuntimeException implements ExceptionInterface
{
}
class RuntimeException extends Exception\RuntimeException implements ExceptionInterface {}

Check failure on line 9 in src/ResultSet/Exception/RuntimeException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/RuntimeException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a class must be on the line after the definition

Check failure on line 9 in src/ResultSet/Exception/RuntimeException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Closing brace must be on a line by itself

Check failure on line 9 in src/ResultSet/Exception/RuntimeException.php

View workflow job for this annotation

GitHub Actions / QA Checks (PHPCodeSniffer [8.2, locked], ubuntu-latest, laminas/laminas-continuous-integration-ac...

Opening brace of a class must be on the line after the definition
Loading
Loading