packages/requirements/src/Config/Markdown/QuotationBlocks.php

1<?php
2
3declare(strict_types=1);
4
5namespace Requirements\Config\Markdown;
6
7use League\CommonMark\Extension\CommonMark\Node\Block\BlockQuote;
8use League\CommonMark\Extension\CommonMark\Node\Inline\Link;
9use League\CommonMark\Node\Block\Paragraph;
10use League\CommonMark\Node\Node;
11use Requirements\Input\InvalidInputException;
12use stdClass;
13
14/**
15 * Reads the evidence quotations that follow a card's statement.
16 *
17 * Each block quotation may be followed by a paragraph holding only its source link.
18 */
19final class QuotationBlocks
20{
21    /**
22     * @param Presentation $presentation Receives the citation link of each quotation
23     * @param Citation|null $source Resolves citations against the definition's source
24     */
25    public function __construct(private readonly Presentation $presentation, private readonly ?Citation $source)
26    {
27    }
28
29    /**
30     * Reads the leading quotations of the remaining card blocks.
31     *
32     * @param list<Node> $blocks The blocks after the statement
33     * @param string $file The definition file
34     * @param string $id The item ID
35     *
36     * @return array{list<stdClass>, list<Node>} The evidence entries and the blocks after them
37     *
38     * @throws InvalidInputException When a quotation lacks a source, a selector or text, or its citation disagrees
39     */
40    public function read(array $blocks, string $file, string $id): array
41    {
42        $evidence = [];
43        while (isset($blocks[0]) && $blocks[0] instanceof BlockQuote) {
44            $quote = $blocks[0];
45            array_shift($blocks);
46            $attribution = null;
47            if (isset($blocks[0]) && $blocks[0] instanceof Paragraph && $blocks[0]->firstChild() instanceof Link && $blocks[0]->firstChild()->next() === null) {
48                $attribution = Nodes::link($blocks[0]);
49                array_shift($blocks);
50            }
51            $reader = new Quotation();
52            try {
53                $evidence[] = $reader->read($quote, $this->source, $attribution);
54            } catch (InvalidInputException $error) {
55                throw new InvalidInputException("$file: $id: " . $error->getMessage(), 0, $error);
56            }
57            $this->presentation->citations[$id][] = $reader->link;
58        }
59        return [$evidence, $blocks];
60    }
61}
62