77
88final class AllExamples implements Examples
99{
10+ /**
11+ * @param list<string>|null $acceptedLanguages Use empty string for unspecified language, and null for any languages
12+ */
1013 public function __construct(
11- private Comments $comments,
12- ) {
13- }
14+ private readonly Comments $comments,
15+ private readonly ?array $acceptedLanguages,
16+ ) { }
1417
1518 /**
1619 * @return \Traversable<Example>
@@ -24,17 +27,25 @@ public function getIterator(): \Traversable
2427
2528 /**
2629 * @param array<string> $paths paths to files and folder to look for PHP comments code examples in
30+ * @param list<string>|null $acceptedLanguages Use empty string for unspecified language, and null for any languages
2731 */
28- public static function fromPaths(array $paths): self
29- {
30- return new self(Comments::fromPaths($paths));
32+ public static function fromPaths(
33+ array $paths,
34+ ?array $acceptedLanguages,
35+ ): self {
36+ return new self(
37+ SourceComments::fromPaths($paths),
38+ $acceptedLanguages,
39+ );
3140 }
3241
3342 /**
3443 * @return \Traversable<Example>
3544 */
36- private function iterateComment(string $comment, Location $location): \Traversable
37- {
45+ private function iterateComment(
46+ string $comment,
47+ Location $location,
48+ ): \Traversable {
3849 $lines = new \ArrayIterator(\explode(PHP_EOL, $comment));
3950 $index = 1;
4051
@@ -46,30 +57,41 @@ private function iterateComment(string $comment, Location $location): \Traversab
4657 /**
4758 * @param \ArrayIterator<int,string> $lines
4859 */
49- private function nextExample(\ArrayIterator $lines, Location $location, int $index): ?Example
50- {
51- $codeblockStartedAt = $this->findFencedCodeBlockStart($lines);
60+ private function nextExample(
61+ \ArrayIterator $lines,
62+ Location $location,
63+ int $index,
64+ ): ?Example {
65+ $codeblockStartedAt = $this->findFencedPHPCodeBlockStart($lines);
5266
5367 if ($codeblockStartedAt === null) {
5468 return null;
5569 }
5670
57- return $this->readExample($lines, $location->startingAt($codeblockStartedAt, $index));
71+ return $this->readExample(
72+ $lines,
73+ $location->startingAt($codeblockStartedAt, $index),
74+ );
5875 }
5976
6077 /**
6178 * @param \ArrayIterator<int,string> $lines
6279 */
63- private function readExample(\ArrayIterator $lines, Location $location): ?Example
64- {
80+ private function readExample(
81+ \ArrayIterator $lines,
82+ Location $location,
83+ ): ?Example {
6584 $buffer = [];
6685
6786 while ($lines->valid()) {
6887 $line = $lines->current();
6988 $lines->next();
7089
71- if ($this->endOfAPHPFencedCodeBlock($line)) {
72- return new Example(\implode(PHP_EOL, $buffer), $location->ofLength($lines->key()));
90+ if ($this->endOfAFencedCodeBlock($line)) {
91+ return new Example(
92+ \implode(PHP_EOL, $buffer),
93+ $location->ofLength($lines->key()),
94+ );
7395 }
7496
7597 $buffer[] = \preg_replace("/^\s*\*( ?)/", "", $line);
@@ -80,28 +102,60 @@ private function readExample(\ArrayIterator $lines, Location $location): ?Exampl
80102
81103 /**
82104 * @param \ArrayIterator<int,string> $lines
105+ * phpcs:disable SlevomatCodingStandard.Complexity.Cognitive.ComplexityTooHigh
83106 */
84- private function findFencedCodeBlockStart (\ArrayIterator $lines): ?int
107+ private function findFencedPHPCodeBlockStart (\ArrayIterator $lines): ?int
85108 {
109+ $insideAFencedCodeBlock = false;
110+
86111 while ($lines->valid()) {
87112 $line = $lines->current();
88113 $lines->next();
89114
90- if ($this->startOfAPHPFencedCodeBlock($line)) {
91- return $lines->key();
115+ if ($insideAFencedCodeBlock) {
116+ if ($this->endOfAFencedCodeBlock($line)) {
117+ $insideAFencedCodeBlock = false;
118+ }
119+ } else {
120+ $lang = $this->startOfAFencedCodeBlock($line);
121+
122+ if ($lang === false) {
123+ continue;
124+ }
125+
126+ if ($this->isAcceptedLanguage($lang)) {
127+ return $lines->key();
128+ }
129+
130+ $insideAFencedCodeBlock = true;
92131 }
93132 }
94133
95134 return null;
96135 }
97136
98- private function endOfAPHPFencedCodeBlock(string $line): bool
137+ private function isAcceptedLanguage(string $lang): bool
138+ {
139+ if ($this->acceptedLanguages === null) {
140+ return true;
141+ }
142+
143+ return \in_array(needle: $lang, haystack: $this->acceptedLanguages, strict: true);
144+ }
145+
146+ private function endOfAFencedCodeBlock(string $line): bool
99147 {
100148 return \ltrim($line) === "* ```";
101149 }
102150
103- private function startOfAPHPFencedCodeBlock (string $line): bool
151+ private function startOfAFencedCodeBlock (string $line): false|string
104152 {
105- return \in_array(\ltrim($line), ["* ```", "* ```php"], strict: true);
153+ $line = \trim($line);
154+
155+ if (!\str_starts_with($line, "* ```")) {
156+ return false;
157+ }
158+
159+ return \substr($line, 5);
106160 }
107161}
0 commit comments