Регулярное выражение для сбора урожая включает и требует директив - PullRequest
1 голос
/ 21 июля 2011

Я пытаюсь собрать все директивы включения из файла PHP, используя регулярное выражение (в Java).

Выражение должно подбирать только те, чьи имена файлов выражены как неконкатенированные строковые литералы. Единицы с константами или переменными не нужны.

Обнаружение должно работать как с одинарными, так и с двойными кавычками, include -s и require -s, плюс дополнительный трюк с _once и, наконец, что не менее важно, как с ключевыми словами, так и с вызовами в стиле функций.

Пример грубого ввода:

<?php

require('a.php');
require 'b.php';
require("c.php");
require "d.php";

include('e.php');
include 'f.php';
include("g.php");
include "h.php";

require_once('i.php');
require_once 'j.php';
require_once("k.php");
require_once "l.php";

include_once('m.php');
include_once 'n.php';
include_once("o.php");
include_once "p.php";

?>

И вывод:

["a.php","b.php","c.php","d.php","f.php","g.php","h.php","i.php","j.php","k.php","l.php","m.php","n.php","o.php","p.php"]

Есть идеи?

Ответы [ 5 ]

7 голосов
/ 21 июля 2011

Использование token_get_all.Это безопасно и не доставит вам головной боли.Также есть PEAR PHP_Parser , если вам требуется код пользователя.

5 голосов
/ 24 июля 2011

Чтобы сделать это точно, вам действительно нужно полностью разобрать исходный код PHP.Это связано с тем, что текстовая последовательность: require('a.php'); может появляться в тех местах, где она вообще не включена, например в комментариях, строках и разметке HTML.Например, следующее НЕ является реальным PHP-включением, но будет соответствовать регулярному выражению:

<?php // Examples where a regex solution gets false positives:
    /* PHP multi-line comment with: require('a.php'); */
    // PHP single-line comment with: require('a.php');
    $str = "double quoted string with: require('a.php');";
    $str = 'single quoted string with: require("a.php");';
?>
    <p>HTML paragraph with: require('a.php');</p>

При этом, если вы довольны получением нескольких ложных срабатываний, следующее единственное решение регулярного выражения выполнитдовольно хорошая работа по очистке всех имен файлов из всех PHP-вариантов:

// Get all filenames from PHP include variations and return in array.
function getIncludes($text) {
    $count = preg_match_all('/
        # Match PHP include variations with single string literal filename.
        \b              # Anchor to word boundary.
        (?:             # Group for include variation alternatives.
          include       # Either "include"
        | require       # or "require"
        )               # End group of include variation alternatives.
        (?:_once)?      # Either one may be the "once" variation.
        \s*             # Optional whitespace.
        (               # $1: Optional opening parentheses.
          \(            # Literal open parentheses,
          \s*           # followed by optional whitespace.
        )?              # End $1: Optional opening parentheses.
        (?|             # "Branch reset" group of filename alts.
          \'([^\']+)\'  # Either $2{1]: Single quoted filename,
        | "([^"]+)"     # or $2{2]: Double quoted filename.
        )               # End branch reset group of filename alts.
        (?(1)           # If there were opening parentheses,
          \s*           # then allow optional whitespace
          \)            # followed by the closing parentheses.
        )               # End group $1 if conditional.
        \s*             # End statement with optional whitespace
        ;               # followed by semi-colon.
        /ix', $text, $matches);
    if ($count > 0) {
        $filenames = $matches[2];
    } else {
        $filenames = array();
    }
    return $filenames;
}

Дополнительно 2011-07-24 Оказывается, ОП хочет решение в Java не PHP.Вот протестированная Java-программа, которая практически идентична.Обратите внимание, что я не эксперт по Java и не знаю, как динамически изменять размер массива.Таким образом, решение ниже (грубо) устанавливает массив фиксированного размера (100) для хранения массива имен файлов.

import java.util.regex.*;
public class TEST {
    // Set maximum size of array of filenames.
    public static final int MAX_NAMES = 100;
    // Get all filenames from PHP include variations and return in array.
    public static String[] getIncludes(String text)
    {
        int count = 0;                          // Count of filenames.
        String filenames[] = new String[MAX_NAMES];
        String filename;
        Pattern p = Pattern.compile(
            "# Match include variations with single string filename. \n" +
            "\\b             # Anchor to word boundary.              \n" +
            "(?:             # Group include variation alternatives. \n" +
            "  include       # Either 'include',                     \n" +
            "| require       # or 'require'.                         \n" +
            ")               # End group of include variation alts.  \n" +
            "(?:_once)?      # Either one may have '_once' suffix.   \n" +
            "\\s*            # Optional whitespace.                  \n" +
            "(?:             # Group for optional opening paren.     \n" +
            "  \\(           # Literal open parentheses,             \n" +
            "  \\s*          # followed by optional whitespace.      \n" +
            ")?              # Opening parentheses are optional.     \n" +
            "(?:             # Group for filename alternatives.      \n" +
            "  '([^']+)'     # $1: Either a single quoted filename,  \n" +
            "| \"([^\"]+)\"  # or $2: a double quoted filename.      \n" +
            ")               # End group of filename alternativess.  \n" +
            "(?:             # Group for optional closing paren.     \n" +
            "  \\s*          # Optional whitespace,                  \n" +
            "  \\)           # followed by the closing parentheses.  \n" +
            ")?              # Closing parentheses is optional .     \n" +
            "\\s*            # End statement with optional ws,       \n" +
            ";               # followed by a semi-colon.               ",
            Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.COMMENTS);
        Matcher m = p.matcher(text);
        while (m.find() && count < MAX_NAMES) {
            // The filename is in either $1 or $2
            if (m.group(1) != null) filename = m.group(1);
            else                    filename = m.group(2);
            // Add this filename to array of filenames.
            filenames[count++] = filename;
        }
        return filenames;
    }
    public static void main(String[] args)
    {
        // Test string full of various PHP include statements.
        String text = "<?php\n"+
            "\n"+
            "require('a.php');\n"+
            "require 'b.php';\n"+
            "require(\"c.php\");\n"+
            "require \"d.php\";\n"+
            "\n"+
            "include('e.php');\n"+
            "include 'f.php';\n"+
            "include(\"g.php\");\n"+
            "include \"h.php\";\n"+
            "\n"+
            "require_once('i.php');\n"+
            "require_once 'j.php';\n"+
            "require_once(\"k.php\");\n"+
            "require_once \"l.php\";\n"+
            "\n"+
            "include_once('m.php');\n"+
            "include_once 'n.php';\n"+
            "include_once(\"o.php\");\n"+
            "include_once \"p.php\";\n"+
            "\n"+
            "?>\n";
        String filenames[] = getIncludes(text);
        for (int i = 0; i < MAX_NAMES && filenames[i] != null; i++) {
            System.out.print(filenames[i] +"\n");
        }
    }
}
4 голосов
/ 21 июля 2011

/(?:require|include)(?:_once)?[( ]['"](.*)\.php['"]\)?;/

Должно работать для всех указанных вами случаев и захватывает только имя файла без расширения

Тестовый скрипт:

<?php

$text = <<<EOT
require('a.php');
require 'b.php';
require("c.php");
require "d.php";

include('e.php');
include 'f.php';
include("g.php");
include "h.php";

require_once('i.php');
require_once 'j.php';
require_once("k.php");
require_once "l.php";

include_once('m.php');
include_once 'n.php';
include_once("o.php");
include_once "p.php";

EOT;

$re = '/(?:require|include)(?:_once)?[( ][\'"](.*)\.php[\'"]\)?;/';
$result = array();

preg_match_all($re, $text, $result);

var_dump($result);

Чтобы получить имена файлов, которые вы хотели, прочитайте $results[1]

Я, вероятно, должен указать, что я тоже неравнодушен к ответу cweiske и что, если вы действительно не хотите просто выполнять упражнения в регулярных выражениях (или хотите сделать это, скажем, с использованием grep), то вам следует использовать токенизатор.

1 голос
/ 21 июля 2011

Следующее должно работать очень хорошо:

/^(require|include)(_once)?(\(\s+)("|')(.*?)("|')(\)|\s+);$/

Вам понадобится четвертая захваченная группа.

0 голосов
/ 21 июля 2011

Это работает для меня:

preg_match_all('/\b(require|include|require_once|include_once)\b(\(| )(\'|")(.+)\.php(\'|")\)?;/i', $subject, $result, PREG_PATTERN_ORDER);
$result = $result[4];
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...