Существует несколько возможных решений этой проблемы, каждое из которых имеет свои преимущества и недостатки. Вот они, решать, какой из них вам нужен.
Tokenizer
Этот метод использует токенизатор и читает части файла, пока не найдет определение класса.
Преимущества
- Не нужно полностью анализировать файл
- Быстрый (читает только начало файла)
- Мало или нет шансов на ложные срабатывания
Недостатки
Код
$fp = fopen($file, 'r');
$class = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
$tokens = token_get_all($buffer);
if (strpos($buffer, '{') === false) continue;
for (;$i<count($tokens);$i++) {
if ($tokens[$i][0] === T_CLASS) {
for ($j=$i+1;$j<count($tokens);$j++) {
if ($tokens[$j] === '{') {
$class = $tokens[$i+2][1];
}
}
}
}
}
Регулярные выражения
Используйте регулярные выражения для анализа начала файла, пока не будет найдено определение класса.
Преимущества
- Не нужно полностью анализировать файл
- Быстрый (читает только начало файла)
Недостатки
- Высокие шансы ложных срабатываний (например:
echo "class Foo {";
)
Код
$fp = fopen($file, 'r');
$class = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
if (preg_match('/class\s+(\w+)(.*)?\{/', $buffer, $matches)) {
$class = $matches[1];
break;
}
}
Примечание: регулярное выражение, вероятно, может быть улучшено, но ни одно регулярное выражение не может сделать это идеально.
Получить список объявленных классов
Этот метод использует get_declared_classes()
и ищет первый класс, определенный после включения.
Преимущества
- Кратчайшее решение
- Нет шансов на ложное срабатывание
Недостатки
- Нужно загрузить весь файл
- Необходимо загрузить весь список классов в памяти дважды
- Необходимо загрузить определение класса в память
Код
$classes = get_declared_classes();
include 'test2.php';
$diff = array_diff(get_declared_classes(), $classes);
$class = reset($diff);
Примечание: вы не можете просто сделать end()
, как предлагали другие. Если класс включает другой класс, вы получите неправильный результат.
Это решение Tokenizer , измененное для включения переменной $ namespace , содержащей пространство имен класса, если применимо:
$fp = fopen($file, 'r');
$class = $namespace = $buffer = '';
$i = 0;
while (!$class) {
if (feof($fp)) break;
$buffer .= fread($fp, 512);
$tokens = token_get_all($buffer);
if (strpos($buffer, '{') === false) continue;
for (;$i<count($tokens);$i++) {
if ($tokens[$i][0] === T_NAMESPACE) {
for ($j=$i+1;$j<count($tokens); $j++) {
if ($tokens[$j][0] === T_STRING) {
$namespace .= '\\'.$tokens[$j][1];
} else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
break;
}
}
}
if ($tokens[$i][0] === T_CLASS) {
for ($j=$i+1;$j<count($tokens);$j++) {
if ($tokens[$j] === '{') {
$class = $tokens[$i+2][1];
}
}
}
}
}
Скажем, у вас есть этот класс:
namespace foo\bar {
class hello { }
}
... или альтернативный синтаксис:
namespace foo\bar;
class hello { }
Вы должны получить следующий результат:
var_dump($namespace); // \foo\bar
var_dump($class); // hello
Вы также можете использовать приведенное выше для определения пространства имен, которое объявляет файл, независимо от того, содержит оно класс или нет.