-
-
Notifications
You must be signed in to change notification settings - Fork 339
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
509 additions
and
62 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,243 @@ | ||
<?php | ||
|
||
/** | ||
* League.Csv (https://csv.thephpleague.com) | ||
* | ||
* (c) Ignace Nyamagana Butera <[email protected]> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
|
||
declare(strict_types=1); | ||
|
||
namespace League\Csv; | ||
|
||
use ArrayIterator; | ||
use Iterator; | ||
use mysqli_result; | ||
use PDO; | ||
use PDOStatement; | ||
use PgSql\Result; | ||
use RuntimeException; | ||
use SQLite3Result; | ||
use Throwable; | ||
use ValueError; | ||
|
||
use function array_column; | ||
use function array_map; | ||
use function pg_fetch_assoc; | ||
use function pg_field_name; | ||
use function pg_num_fields; | ||
use function pg_result_seek; | ||
use function range; | ||
|
||
use const SQLITE3_ASSOC; | ||
|
||
final class RdbmsResult implements TabularData | ||
{ | ||
private function __construct(private readonly Iterator $records, private readonly array $headers) | ||
{ | ||
} | ||
|
||
public function getHeader(): array | ||
{ | ||
return $this->headers; | ||
} | ||
|
||
public function getIterator(): Iterator | ||
{ | ||
return $this->records; | ||
} | ||
|
||
public static function tryFrom(object $result): ?self | ||
{ | ||
try { | ||
return self::from($result); | ||
} catch (Throwable) { | ||
return null; | ||
} | ||
} | ||
|
||
/** | ||
* @throws RuntimeException If the DB result is unknown or unsupported | ||
*/ | ||
public static function from(object $result): self | ||
{ | ||
return new self(self::records($result), self::columnNames($result)); | ||
} | ||
|
||
/** | ||
* @throws RuntimeException If the DB result is unknown or unsupported or no column names information is found. | ||
* | ||
* @return array<string> | ||
*/ | ||
public static function columnNames(object $result): array | ||
{ | ||
return match (true) { | ||
$result instanceof PDOStatement => array_map( | ||
function (int $i) use ($result): string { | ||
$metadata = $result->getColumnMeta($i); | ||
false !== $metadata || throw new RuntimeException('Unable to get metadata for column '.$i); | ||
|
||
return $metadata['name']; | ||
}, | ||
range(0, $result->columnCount() - 1) | ||
), | ||
$result instanceof Result => array_map(fn (int $index) => pg_field_name($result, $index), range(0, pg_num_fields($result) - 1)), | ||
$result instanceof mysqli_result => array_column($result->fetch_fields(), 'name'), | ||
$result instanceof SQLite3Result => array_map($result->columnName(...), range(0, $result->numColumns() - 1)), | ||
default => throw new ValueError('Unknown or unsupported RDBMS result object '.$result::class), | ||
}; | ||
} | ||
|
||
public static function records(object $result): Iterator | ||
{ | ||
return match (true) { | ||
$result instanceof SQLite3Result => new class ($result) implements Iterator { | ||
private array|false $current; | ||
private int $key = 0; | ||
|
||
public function __construct(private SQLite3Result $result) | ||
{ | ||
} | ||
|
||
public function rewind(): void | ||
{ | ||
$this->result->reset(); | ||
$this->current = $this->result->fetchArray(SQLITE3_ASSOC); | ||
$this->key = 0; | ||
} | ||
|
||
public function current(): array|false | ||
{ | ||
return $this->current; | ||
} | ||
|
||
public function key(): string|int|null | ||
{ | ||
return $this->key; | ||
} | ||
|
||
public function next(): void | ||
{ | ||
$this->current = $this->result->fetchArray(SQLITE3_ASSOC); | ||
$this->key++; | ||
} | ||
|
||
public function valid(): bool | ||
{ | ||
return false !== $this->current; | ||
} | ||
}, | ||
$result instanceof mysqli_result => new class ($result) implements Iterator { | ||
private array|false|null $current; | ||
private int $key = 0; | ||
|
||
public function __construct(private mysqli_result $result) | ||
{ | ||
} | ||
|
||
public function rewind(): void | ||
{ | ||
$this->result->data_seek(0); | ||
$this->current = $this->result->fetch_assoc(); | ||
$this->key = 0; | ||
} | ||
|
||
public function current(): array|false|null | ||
{ | ||
return $this->current; | ||
} | ||
|
||
public function key(): string|int|null | ||
{ | ||
return $this->key; | ||
} | ||
|
||
public function next(): void | ||
{ | ||
$this->current = $this->result->fetch_assoc(); | ||
$this->key++; | ||
} | ||
|
||
public function valid(): bool | ||
{ | ||
return false !== $this->current | ||
&& null !== $this->current; | ||
} | ||
}, | ||
$result instanceof Result => new class ($result) implements Iterator { | ||
private array|false|null $current; | ||
private int $key = 0; | ||
|
||
public function __construct(private Result $result) | ||
{ | ||
} | ||
|
||
public function rewind(): void | ||
{ | ||
pg_result_seek($this->result, 0); | ||
$this->current = pg_fetch_assoc($this->result); | ||
$this->key = 0; | ||
} | ||
|
||
public function current(): array|false|null | ||
{ | ||
return $this->current; | ||
} | ||
|
||
public function key(): string|int|null | ||
{ | ||
return $this->key; | ||
} | ||
|
||
public function next(): void | ||
{ | ||
$this->current = pg_fetch_assoc($this->result); | ||
$this->key++; | ||
} | ||
|
||
public function valid(): bool | ||
{ | ||
return false !== $this->current | ||
&& null !== $this->current; | ||
} | ||
}, | ||
$result instanceof PDOStatement => new class ($result) implements Iterator { | ||
private ?ArrayIterator $cacheIterator; | ||
|
||
public function __construct(private PDOStatement $result) | ||
{ | ||
} | ||
|
||
public function rewind(): void | ||
{ | ||
$this->cacheIterator ??= new ArrayIterator($this->result->fetchAll(PDO::FETCH_ASSOC)); | ||
$this->cacheIterator->rewind(); | ||
} | ||
|
||
public function current(): mixed | ||
{ | ||
return $this->cacheIterator?->current() ?? false; | ||
} | ||
|
||
public function key(): string|int|null | ||
{ | ||
return $this->cacheIterator?->key() ?? null; | ||
} | ||
|
||
public function next(): void | ||
{ | ||
$this->cacheIterator?->next(); | ||
} | ||
|
||
public function valid(): bool | ||
{ | ||
return $this->cacheIterator?->valid() ?? false; | ||
} | ||
}, | ||
default => throw new ValueError('Unknown or unsupported RDBMS result object '.$result::class), | ||
}; | ||
} | ||
} |
Oops, something went wrong.