@@ -121,39 +121,85 @@ public function formatArgsAsText(array $args): string
121
121
*/
122
122
public function fileExcerpt (string $ file , int $ line , int $ srcContext = 3 ): ?string
123
123
{
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 );
143
132
144
- $ lines = [];
145
133
if (0 > $ srcContext ) {
146
- $ srcContext = \count ($ content );
134
+ $ srcContext = \count ($ lines );
147
135
}
148
136
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
+ }
152
143
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 );
154
168
}
155
169
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> ' ;
157
203
}
158
204
159
205
/**
@@ -243,7 +289,7 @@ protected static function fixCodeMarkup(string $line): string
243
289
// missing </span> tag at the end of line
244
290
$ opening = strpos ($ line , '<span ' );
245
291
$ closing = strpos ($ line , '</span> ' );
246
- if (false !== $ opening && (false === $ closing || $ closing > $ opening )) {
292
+ if (false !== $ opening && (false === $ closing || $ closing < $ opening )) {
247
293
$ line .= '</span> ' ;
248
294
}
249
295
0 commit comments