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