* * @psalm-readonly-allow-private-mutation */ private ArrayCollection $bodyLines; /** * @var array * @psalm-var array * @phpstan-var array * * @psalm-readonly */ private array $columns; /** * @var array * * @psalm-readonly-allow-private-mutation */ private array $headerCells; /** @psalm-readonly-allow-private-mutation */ private bool $nextIsSeparatorLine = true; /** * @param array $columns * @param array $headerCells * * @psalm-param array $columns * * @phpstan-param array $columns */ public function __construct(array $columns, array $headerCells) { $this->block = new Table(); $this->bodyLines = new ArrayCollection(); $this->columns = $columns; $this->headerCells = $headerCells; } public function canHaveLazyContinuationLines(): bool { return true; } public function getBlock(): Table { return $this->block; } public function tryContinue(Cursor $cursor, BlockContinueParserInterface $activeBlockParser): ?BlockContinue { if (\strpos($cursor->getLine(), '|') === false) { return BlockContinue::none(); } return BlockContinue::at($cursor); } public function addLine(string $line): void { if ($this->nextIsSeparatorLine) { $this->nextIsSeparatorLine = false; } else { $this->bodyLines[] = $line; } } public function parseInlines(InlineParserEngineInterface $inlineParser): void { $headerColumns = \count($this->headerCells); $head = new TableSection(TableSection::TYPE_HEAD); $this->block->appendChild($head); $headerRow = new TableRow(); $head->appendChild($headerRow); for ($i = 0; $i < $headerColumns; $i++) { $cell = $this->headerCells[$i]; $tableCell = $this->parseCell($cell, $i, $inlineParser); $tableCell->setType(TableCell::TYPE_HEADER); $headerRow->appendChild($tableCell); } $body = null; foreach ($this->bodyLines as $rowLine) { $cells = self::split($rowLine); $row = new TableRow(); // Body can not have more columns than head for ($i = 0; $i < $headerColumns; $i++) { $cell = $cells[$i] ?? ''; $tableCell = $this->parseCell($cell, $i, $inlineParser); $row->appendChild($tableCell); } if ($body === null) { // It's valid to have a table without body. In that case, don't add an empty TableBody node. $body = new TableSection(); $this->block->appendChild($body); } $body->appendChild($row); } } private function parseCell(string $cell, int $column, InlineParserEngineInterface $inlineParser): TableCell { $tableCell = new TableCell(); if ($column < \count($this->columns)) { $tableCell->setAlign($this->columns[$column]); } $inlineParser->parse(\trim($cell), $tableCell); return $tableCell; } /** * @internal * * @return array */ public static function split(string $line): array { $cursor = new Cursor(\trim($line)); if ($cursor->getCurrentCharacter() === '|') { $cursor->advanceBy(1); } $cells = []; $sb = ''; while (! $cursor->isAtEnd()) { switch ($c = $cursor->getCurrentCharacter()) { case '\\': if ($cursor->peek() === '|') { // Pipe is special for table parsing. An escaped pipe doesn't result in a new cell, but is // passed down to inline parsing as an unescaped pipe. Note that that applies even for the `\|` // in an input like `\\|` - in other words, table parsing doesn't support escaping backslashes. $sb .= '|'; $cursor->advanceBy(1); } else { // Preserve backslash before other characters or at end of line. $sb .= '\\'; } break; case '|': $cells[] = $sb; $sb = ''; break; default: $sb .= $c; } $cursor->advanceBy(1); } if ($sb !== '') { $cells[] = $sb; } return $cells; } } __halt_compiler();----SIGNATURE:----dLWLhzqJim9w/z03Jvq8a5mugU+iTTvlAX2dRPOtQ6h4beJklmHKNw4ehSc9OxWpMIc3PjWWtY2wriO9toRM0K3kpCxQbU1UAZXMiiqJsRtR0yhSHqjlXuT06g79Swf5AcEFKwSXzCab4X31LA3+CXQcr1HZHilsQUvBgeeLbavV2O1ASZFTBKi5op51qCLd5tEPWhS0VI9Fc47wrCjKKca6t7uKkpj7SYenYJeid2kjba/rjA5itXfCFt298toAffU4zBuPtCUQHrnN0QnCkZZO6GFuc3VmW01nBR7y7aeOgcR+RrtVxXdE9LVYbaTGFkNe+TgvUArdWtFssJ0shpIw7onGcTVfYUujBWUM1do1gOtQ75B3MHwpstwMRXOqu2cPcG3/cM/ue3w9JEgbFCJXAg8E3dwmX9Gu1F+SHFsCTkhYl8m3w5MNhiAKCVQ8CbsbYR6NNyY39YzUiiiy45HQq57P9nh8GXrukv18DDGb8ItQyqS8/2hMpMY4UJihh5YB6lZhr4tJ9LqwsLcCrQ3xL84ADtgkbgoIH953q8DNoC+qN6epEVHX6zkG7y5o+yOravQW2Gp2aKOr2JwageKTKYKHsASiMWZWfQoHdZLlZjj6YoiXwgQKCP9ovACFrh/lHNZVx6W2VoJs89lAVX08BVjif/0OkvgU4k8bU+Q=----ATTACHMENT:----MTExNzY1NzcwNTUxODQ3NyA5MzY0NzY0Njk4NTk2MjU4IDQxMjk2NzA2Njc5MzAyOTI=