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