Polyfill
10 months ago
AbstractCsv.php
10 months ago
ByteSequence.php
10 months ago
CannotInsertRecord.php
10 months ago
CharsetConverter.php
10 months ago
ColumnConsistency.php
10 months ago
EncloseField.php
10 months ago
EscapeFormula.php
10 months ago
Exception.php
10 months ago
HTMLConverter.php
10 months ago
Info.php
10 months ago
InvalidArgument.php
10 months ago
MapIterator.php
10 months ago
RFC4180Field.php
10 months ago
Reader.php
10 months ago
ResultSet.php
10 months ago
Statement.php
10 months ago
Stream.php
10 months ago
SyntaxError.php
10 months ago
TabularDataReader.php
10 months ago
UnableToProcessCsv.php
10 months ago
UnavailableFeature.php
10 months ago
UnavailableStream.php
10 months ago
Writer.php
10 months ago
XMLConverter.php
10 months ago
functions.php
10 months ago
functions_include.php
10 months ago
ResultSet.php
239 lines
| 1 | <?php |
| 2 | |
| 3 | /** |
| 4 | * League.Csv (https://csv.thephpleague.com) |
| 5 | * |
| 6 | * (c) Ignace Nyamagana Butera <nyamsprod@gmail.com> |
| 7 | * |
| 8 | * For the full copyright and license information, please view the LICENSE |
| 9 | * file that was distributed with this source code. |
| 10 | */ |
| 11 | declare (strict_types=1); |
| 12 | namespace IAWPSCOPED\League\Csv; |
| 13 | |
| 14 | use CallbackFilterIterator; |
| 15 | use Iterator; |
| 16 | use JsonSerializable; |
| 17 | use LimitIterator; |
| 18 | use function array_flip; |
| 19 | use function array_search; |
| 20 | use function is_string; |
| 21 | use function iterator_count; |
| 22 | use function iterator_to_array; |
| 23 | /** |
| 24 | * Represents the result set of a {@link Reader} processed by a {@link Statement}. |
| 25 | * @internal |
| 26 | */ |
| 27 | class ResultSet implements TabularDataReader, JsonSerializable |
| 28 | { |
| 29 | /** |
| 30 | * The CSV records collection. |
| 31 | * |
| 32 | * @var Iterator |
| 33 | */ |
| 34 | protected $records; |
| 35 | /** |
| 36 | * The CSV records collection header. |
| 37 | * |
| 38 | * @var array<string> |
| 39 | */ |
| 40 | protected $header = []; |
| 41 | /** |
| 42 | * New instance. |
| 43 | */ |
| 44 | public function __construct(Iterator $records, array $header) |
| 45 | { |
| 46 | $this->validateHeader($header); |
| 47 | $this->records = $records; |
| 48 | $this->header = $header; |
| 49 | } |
| 50 | /** |
| 51 | * @throws SyntaxError if the header syntax is invalid |
| 52 | */ |
| 53 | protected function validateHeader(array $header) : void |
| 54 | { |
| 55 | if ($header !== ($filtered_header = \array_filter($header, 'is_string'))) { |
| 56 | throw SyntaxError::dueToInvalidHeaderColumnNames(); |
| 57 | } |
| 58 | if ($header !== \array_unique($filtered_header)) { |
| 59 | throw SyntaxError::dueToDuplicateHeaderColumnNames($header); |
| 60 | } |
| 61 | } |
| 62 | /** |
| 63 | * {@inheritdoc} |
| 64 | */ |
| 65 | public function __destruct() |
| 66 | { |
| 67 | unset($this->records); |
| 68 | } |
| 69 | /** |
| 70 | * Returns a new instance from a League\Csv\Reader object. |
| 71 | */ |
| 72 | public static function createFromTabularDataReader(TabularDataReader $reader) : self |
| 73 | { |
| 74 | return new self($reader->getRecords(), $reader->getHeader()); |
| 75 | } |
| 76 | /** |
| 77 | * Returns the header associated with the result set. |
| 78 | * |
| 79 | * @return string[] |
| 80 | */ |
| 81 | public function getHeader() : array |
| 82 | { |
| 83 | return $this->header; |
| 84 | } |
| 85 | /** |
| 86 | * {@inheritdoc} |
| 87 | */ |
| 88 | public function getIterator() : Iterator |
| 89 | { |
| 90 | return $this->getRecords(); |
| 91 | } |
| 92 | /** |
| 93 | * {@inheritdoc} |
| 94 | */ |
| 95 | public function getRecords(array $header = []) : Iterator |
| 96 | { |
| 97 | $this->validateHeader($header); |
| 98 | $records = $this->combineHeader($header); |
| 99 | foreach ($records as $offset => $value) { |
| 100 | (yield $offset => $value); |
| 101 | } |
| 102 | } |
| 103 | /** |
| 104 | * Combine the header to each record if present. |
| 105 | */ |
| 106 | protected function combineHeader(array $header) : Iterator |
| 107 | { |
| 108 | if ($header === $this->header || [] === $header) { |
| 109 | return $this->records; |
| 110 | } |
| 111 | $field_count = \count($header); |
| 112 | $mapper = static function (array $record) use($header, $field_count) : array { |
| 113 | if (\count($record) != $field_count) { |
| 114 | $record = \array_slice(\array_pad($record, $field_count, null), 0, $field_count); |
| 115 | } |
| 116 | /** @var array<string|null> $assocRecord */ |
| 117 | $assocRecord = \array_combine($header, $record); |
| 118 | return $assocRecord; |
| 119 | }; |
| 120 | return new MapIterator($this->records, $mapper); |
| 121 | } |
| 122 | /** |
| 123 | * {@inheritdoc} |
| 124 | */ |
| 125 | public function count() : int |
| 126 | { |
| 127 | return iterator_count($this->records); |
| 128 | } |
| 129 | /** |
| 130 | * {@inheritdoc} |
| 131 | */ |
| 132 | public function jsonSerialize() : array |
| 133 | { |
| 134 | return iterator_to_array($this->records, \false); |
| 135 | } |
| 136 | /** |
| 137 | * {@inheritdoc} |
| 138 | */ |
| 139 | public function fetchOne(int $nth_record = 0) : array |
| 140 | { |
| 141 | if ($nth_record < 0) { |
| 142 | throw InvalidArgument::dueToInvalidRecordOffset($nth_record, __METHOD__); |
| 143 | } |
| 144 | $iterator = new LimitIterator($this->records, $nth_record, 1); |
| 145 | $iterator->rewind(); |
| 146 | $result = $iterator->current(); |
| 147 | if (!\is_array($result)) { |
| 148 | return []; |
| 149 | } |
| 150 | return $result; |
| 151 | } |
| 152 | /** |
| 153 | * {@inheritdoc} |
| 154 | */ |
| 155 | public function fetchColumn($index = 0) : Iterator |
| 156 | { |
| 157 | $offset = $this->getColumnIndex($index, 'offset', __METHOD__); |
| 158 | $filter = static function (array $record) use($offset) : bool { |
| 159 | return isset($record[$offset]); |
| 160 | }; |
| 161 | $select = static function (array $record) use($offset) : string { |
| 162 | return $record[$offset]; |
| 163 | }; |
| 164 | $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select); |
| 165 | foreach ($iterator as $tKey => $tValue) { |
| 166 | (yield $tKey => $tValue); |
| 167 | } |
| 168 | } |
| 169 | /** |
| 170 | * Filter a column name against the header if any. |
| 171 | * |
| 172 | * @param string|int $field the field name or the field index |
| 173 | * @param string $method the calling method |
| 174 | * |
| 175 | * @throws Exception if the field is invalid or not found |
| 176 | * |
| 177 | * @return string|int |
| 178 | */ |
| 179 | protected function getColumnIndex($field, string $type, string $method) |
| 180 | { |
| 181 | if (is_string($field)) { |
| 182 | return $this->getColumnIndexByValue($field, $type, $method); |
| 183 | } |
| 184 | return $this->getColumnIndexByKey($field, $type, $method); |
| 185 | } |
| 186 | /** |
| 187 | * Returns the selected column name. |
| 188 | * |
| 189 | * @throws Exception if the column is not found |
| 190 | */ |
| 191 | protected function getColumnIndexByValue(string $value, string $type, string $method) : string |
| 192 | { |
| 193 | if (\false !== array_search($value, $this->header, \true)) { |
| 194 | return $value; |
| 195 | } |
| 196 | throw InvalidArgument::dueToInvalidColumnIndex($value, $type, $method); |
| 197 | } |
| 198 | /** |
| 199 | * Returns the selected column name according to its offset. |
| 200 | * |
| 201 | * @throws Exception if the field is invalid or not found |
| 202 | * |
| 203 | * @return int|string |
| 204 | */ |
| 205 | protected function getColumnIndexByKey(int $index, string $type, string $method) |
| 206 | { |
| 207 | if ($index < 0) { |
| 208 | throw InvalidArgument::dueToInvalidColumnIndex($index, $type, $method); |
| 209 | } |
| 210 | if ([] === $this->header) { |
| 211 | return $index; |
| 212 | } |
| 213 | $value = array_search($index, array_flip($this->header), \true); |
| 214 | if (\false === $value) { |
| 215 | throw InvalidArgument::dueToInvalidColumnIndex($index, $type, $method); |
| 216 | } |
| 217 | return $value; |
| 218 | } |
| 219 | /** |
| 220 | * {@inheritdoc} |
| 221 | */ |
| 222 | public function fetchPairs($offset_index = 0, $value_index = 1) : Iterator |
| 223 | { |
| 224 | $offset = $this->getColumnIndex($offset_index, 'offset', __METHOD__); |
| 225 | $value = $this->getColumnIndex($value_index, 'value', __METHOD__); |
| 226 | $filter = static function (array $record) use($offset) : bool { |
| 227 | return isset($record[$offset]); |
| 228 | }; |
| 229 | $select = static function (array $record) use($offset, $value) : array { |
| 230 | return [$record[$offset], $record[$value] ?? null]; |
| 231 | }; |
| 232 | $iterator = new MapIterator(new CallbackFilterIterator($this->records, $filter), $select); |
| 233 | /** @var array{0:int|string, 1:string|null} $pair */ |
| 234 | foreach ($iterator as $pair) { |
| 235 | (yield $pair[0] => $pair[1]); |
| 236 | } |
| 237 | } |
| 238 | } |
| 239 |