@@ -121,39 +121,85 @@ public function formatArgsAsText(array $args): string
121121 */
122122 public function fileExcerpt (string $ file , int $ line , int $ srcContext = 3 ): ?string
123123 {
124- if (is_file ($ file ) && is_readable ($ file )) {
125- // highlight_file could throw warnings
126- // see https://bugs.php.net/25725
127- $ code = @highlight_file ($ file , true );
128- if (\PHP_VERSION_ID >= 80300 ) {
129- // remove main pre/code tags
130- $ code = preg_replace ('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s ' , '\\1 ' , $ code );
131- // split multiline span tags
132- $ code = preg_replace_callback ('#<span ([^>]++)>((?:[^< \\n]*+ \\n)++[^<]*+)</span># ' , function ($ m ) {
133- return "<span $ m [1 ]> " .str_replace ("\n" , "</span> \n<span $ m [1 ]> " , $ m [2 ]).'</span> ' ;
134- }, $ code );
135- $ content = explode ("\n" , $ code );
136- } else {
137- // remove main code/span tags
138- $ code = preg_replace ('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s ' , '\\1 ' , $ code );
139- // split multiline spans
140- $ code = preg_replace_callback ('#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span># ' , fn ($ m ) => "<span $ m [1 ]> " .str_replace ('<br /> ' , "</span><br /><span $ m [1 ]> " , $ m [2 ]).'</span> ' , $ code );
141- $ content = explode ('<br /> ' , $ code );
142- }
124+ if (!is_file ($ file ) || !is_readable ($ file )) {
125+ return null ;
126+ }
127+
128+ $ contents = file_get_contents ($ file );
129+
130+ if (!str_contains ($ contents , '<?php ' ) && !str_contains ($ contents , '<?= ' )) {
131+ $ lines = explode ("\n" , $ contents );
143132
144- $ lines = [];
145133 if (0 > $ srcContext ) {
146- $ srcContext = \count ($ content );
134+ $ srcContext = \count ($ lines );
147135 }
148136
149- for ($ i = max ($ line - $ srcContext , 1 ), $ max = min ($ line + $ srcContext , \count ($ content )); $ i <= $ max ; ++$ i ) {
150- $ lines [] = '<li ' .($ i == $ line ? ' class="selected" ' : '' ).'><a class="anchor" id="line ' .$ i .'"></a><code> ' .self ::fixCodeMarkup ($ content [$ i - 1 ]).'</code></li> ' ;
151- }
137+ return $ this ->formatFileExcerpt (
138+ $ this ->extractExcerptLines ($ lines , $ line , $ srcContext ),
139+ $ line ,
140+ $ srcContext
141+ );
142+ }
152143
153- return '<ol start=" ' .max ($ line - $ srcContext , 1 ).'"> ' .implode ("\n" , $ lines ).'</ol> ' ;
144+ // highlight_string could throw warnings
145+ // see https://bugs.php.net/25725
146+ $ code = @highlight_string ($ contents , true );
147+
148+ if (\PHP_VERSION_ID >= 80300 ) {
149+ // remove main pre/code tags
150+ $ code = preg_replace ('#^<pre.*?>\s*<code.*?>(.*)</code>\s*</pre>#s ' , '\\1 ' , $ code );
151+ // split multiline span tags
152+ $ code = preg_replace_callback (
153+ '#<span ([^>]++)>((?:[^< \\n]*+ \\n)++[^<]*+)</span># ' ,
154+ static fn (array $ m ): string => "<span $ m [1 ]> " .str_replace ("\n" , "</span> \n<span $ m [1 ]> " , $ m [2 ]).'</span> ' ,
155+ $ code
156+ );
157+ $ lines = explode ("\n" , $ code );
158+ } else {
159+ // remove main code/span tags
160+ $ code = preg_replace ('#^<code.*?>\s*<span.*?>(.*)</span>\s*</code>#s ' , '\\1 ' , $ code );
161+ // split multiline spans
162+ $ code = preg_replace_callback (
163+ '#<span ([^>]++)>((?:[^<]*+<br \/>)++[^<]*+)</span># ' ,
164+ static fn (array $ m ): string => "<span $ m [1 ]> " .str_replace ('<br /> ' , "</span><br /><span $ m [1 ]> " , $ m [2 ]).'</span> ' ,
165+ $ code
166+ );
167+ $ lines = explode ('<br /> ' , $ code );
154168 }
155169
156- return null ;
170+ if (0 > $ srcContext ) {
171+ $ srcContext = \count ($ lines );
172+ }
173+
174+ return $ this ->formatFileExcerpt (
175+ array_map (
176+ self ::fixCodeMarkup (...),
177+ $ this ->extractExcerptLines ($ lines , $ line , $ srcContext ),
178+ ),
179+ $ line ,
180+ $ srcContext
181+ );
182+ }
183+
184+ private function extractExcerptLines (array $ lines , int $ selectedLine , int $ srcContext ): array
185+ {
186+ return \array_slice (
187+ $ lines ,
188+ max ($ selectedLine - $ srcContext , 0 ),
189+ min ($ srcContext * 2 + 1 , \count ($ lines ) - $ selectedLine + $ srcContext ),
190+ true
191+ );
192+ }
193+
194+ private function formatFileExcerpt (array $ lines , int $ selectedLine , int $ srcContext ): string
195+ {
196+ $ start = max ($ selectedLine - $ srcContext , 1 );
197+
198+ return "<ol start= \"{$ start }\"> " .implode ("\n" , array_map (
199+ static fn (string $ line , int $ num ): string => '<li ' .(++$ num === $ selectedLine ? ' class="selected" ' : '' )."><a class= \"anchor \" id= \"line {$ num }\"></a><code> {$ line }</code></li> " ,
200+ $ lines ,
201+ array_keys ($ lines ),
202+ )).'</ol> ' ;
157203 }
158204
159205 /**
@@ -243,7 +289,7 @@ protected static function fixCodeMarkup(string $line): string
243289 // missing </span> tag at the end of line
244290 $ opening = strpos ($ line , '<span ' );
245291 $ closing = strpos ($ line , '</span> ' );
246- if (false !== $ opening && (false === $ closing || $ closing > $ opening )) {
292+ if (false !== $ opening && (false === $ closing || $ closing < $ opening )) {
247293 $ line .= '</span> ' ;
248294 }
249295
0 commit comments