@@ -238,6 +238,158 @@ public static function getLinesToBeIgnored($filename, $cacheTokens = TRUE)
238
238
return self ::$ ignoredLines [$ filename ];
239
239
}
240
240
241
+ /**
242
+ * Checks whether or not it is safe to include file. It ensures that file contains only class/function definitions.
243
+ * It also checks that function/class definitions do not exist prior to including.
244
+ *
245
+ * @param $filename string Filename to be checked
246
+ * @param $toplevel_funcs array List of function calls at top level that are allowed (e.g. array('define'))
247
+ * @param $classes array Classes that are present in file with respect of namespaces
248
+ *
249
+ * @return bool
250
+ */
251
+ public static function canIncludeFile ($ filename , array $ toplevel_funcs , &$ classes , &$ errmsg )
252
+ {
253
+ if (!is_readable ($ filename ) || !is_file ($ filename )) return false ;
254
+ $ source = file_get_contents ($ filename );
255
+ if ($ source === false ) return false ;
256
+ $ tokens = token_get_all ($ source );
257
+ if ($ tokens === false ) return false ;
258
+
259
+ $ toplevel_funcs = array_flip ($ toplevel_funcs );
260
+ $ state = "default " ;
261
+ $ depth = 0 ; // depth of "(" or "{"
262
+ $ line = 1 ;
263
+ $ namespace = "" ;
264
+
265
+ foreach ($ tokens as $ row ) {
266
+ // printf("%-12s ", $state);
267
+ if (is_array ($ row )) {
268
+ list ($ token , $ text , $ line ) = $ row ;
269
+ $ text = str_replace ("\n" , '\\n ' , $ text );
270
+ // echo token_name($token) . " '$text' on line $line\n";
271
+ if ($ token === T_WHITESPACE || $ token === T_COMMENT || $ token === T_DOC_COMMENT ) continue ;
272
+
273
+ switch ($ state ) {
274
+ case "default " :
275
+ if ($ token !== T_OPEN_TAG ) {
276
+ $ errmsg = "Have something before <?php tag on line $ line " ;
277
+ return false ;
278
+ }
279
+ $ state = "root " ;
280
+ break ;
281
+ case "root " :
282
+ if ($ token === T_STRING ) { // function call
283
+ if (!isset ($ toplevel_funcs [$ text ])) {
284
+ $ errmsg = "Forbidden top level function call: $ text(...) on line $ line " ;
285
+ return false ;
286
+ }
287
+ $ state = "funccall " ;
288
+ $ depth = 0 ;
289
+ } else if ($ token === T_ABSTRACT || $ token === T_FINAL ) {
290
+ continue ;
291
+ } else if ($ token === T_CLASS ) {
292
+ $ state = "classdef " ;
293
+ } else if ($ token === T_CLOSE_TAG ) {
294
+ $ state = "default " ;
295
+ } else if ($ token === T_NAMESPACE ) {
296
+ $ state = "namespace " ;
297
+ $ namespace = "" ;
298
+ } else if ($ token === T_USE ) {
299
+ $ state = "use " ;
300
+ } else if ($ token === T_FUNCTION ) {
301
+ $ state = "funcdef " ;
302
+ } else {
303
+ $ errmsg = "Disallowed top level token " . token_name ($ token ) . " ( $ text) on line $ line " ;
304
+ return false ;
305
+ }
306
+ break ;
307
+ case "namespace " :
308
+ $ namespace .= $ text ;
309
+ break ;
310
+ case "classdef " :
311
+ if ($ token === T_EXTENDS ) {
312
+ $ state = "extends " ;
313
+ } else if ($ token === T_IMPLEMENTS ) {
314
+ $ state = "implements " ;
315
+ } else if ($ token === T_STRING ) {
316
+ $ classname = $ namespace . "\\" . $ text ;
317
+ if (class_exists ($ classname )) {
318
+ $ errmsg = "Class ' $ classname' already exists on line $ line " ;
319
+ return false ;
320
+ } else {
321
+ $ classes [] = $ classname ;
322
+ }
323
+ } else {
324
+ $ errmsg = "Unexpected token " . token_name ($ token ) . " ( $ text) on line $ line " ;
325
+ return false ;
326
+ }
327
+ break ;
328
+ case "funccall_end " :
329
+ $ errmsg = "Unexpected terminator for function call: " . token_name ($ token ) . " ( $ text) on line $ line " ;
330
+ return false ;
331
+ }
332
+
333
+ } else {
334
+ // echo "$row\n";
335
+ switch ($ state ) {
336
+ case "funccall " :
337
+ if ($ row === "( " ) $ depth ++;
338
+ else if ($ row === ") " ) $ depth --;
339
+ if ($ depth == 0 ) $ state = "funccall_end " ;
340
+ break ;
341
+ case "funccall_end " :
342
+ if ($ row !== "; " ) {
343
+ $ errmsg = "Unexpected terminator for function call: ' $ row' on line $ line " ;
344
+ return false ;
345
+ }
346
+ $ state = "root " ;
347
+ break ;
348
+ case "namespace " :
349
+ if ($ row === "; " ) {
350
+ $ state = "root " ;
351
+ } else {
352
+ $ errmsg = "Unexpected token ' $ row' on line $ line (expected ';') " ;
353
+ return false ;
354
+ }
355
+ break ;
356
+ case "use " :
357
+ if ($ row === "; " ) $ state = "root " ;
358
+ break ;
359
+ case "classdef " :
360
+ case "extends " :
361
+ case "implements " :
362
+ if ($ row === "{ " ) {
363
+ $ state = "class " ;
364
+ $ depth = 1 ;
365
+ } else {
366
+ $ errmsg = "Unexpected token ' $ row' on line $ line (expected '{') " ;
367
+ return false ;
368
+ }
369
+ break ;
370
+ case "funcdef " :
371
+ if ($ row === "{ " ) {
372
+ $ state = "function " ;
373
+ $ depth = 1 ;
374
+ }
375
+ break ;
376
+ case "function " :
377
+ if ($ row === "{ " ) $ depth ++;
378
+ else if ($ row === "} " ) $ depth --;
379
+ if ($ depth == 0 ) $ state = "root " ;
380
+ break ;
381
+ case "class " :
382
+ if ($ row === "{ " ) $ depth ++;
383
+ else if ($ row === "} " ) $ depth --;
384
+ if ($ depth == 0 ) $ state = "root " ;
385
+ break ;
386
+ }
387
+ }
388
+ }
389
+
390
+ return true ;
391
+ }
392
+
241
393
/**
242
394
* @param float $a
243
395
* @param float $b
0 commit comments