php 从文件中获取类名

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7153000/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-26 02:10:00  来源:igfitidea点击:

Get class name from file

phpreflection

提问by Dani

I have a php file which contains only one class. how can I know what class is there by knowing the filename? I know I can do something with regexp matching but is there a standard php way? (the file is already included in the page that is trying to figure out the class name).

我有一个 php 文件,它只包含一个类。如何通过知道文件名来知道那里是什么类?我知道我可以用正则表达式匹配做一些事情,但是有标准的 php 方式吗?(该文件已包含在试图找出类名的页面中)。

回答by netcoder

There are multiple possible solutions to this problem, each with their advantages and disadvantages. Here they are, it's up to know to decide which one you want.

这个问题有多种可能的解决方案,每个都有其优点和缺点。在这里,由您决定您想要哪一个。

Tokenizer

分词器

This method uses the tokenizer and reads parts of the file until it finds a class definition.

此方法使用分词器并读取文件的一部分,直到找到类定义。

Advantages

好处

  • Do not have to parse the file entirely
  • Fast (reads the beginning of the file only)
  • Little to no chance of false positives
  • 不必完全解析文件
  • 快速(仅读取文件开头)
  • 几乎没有误报的机会

Disadvantages

缺点

  • Longest solution
  • 最长解

Code

代码

$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];
                }
            }
        }
    }
}

Regular expressions

常用表达

Use regular expressions to parse the beginning of the file, until a class definition is found.

使用正则表达式解析文件的开头,直到找到类定义。

Advantages

好处

  • Do not have to parse the file entirely
  • Fast (reads the beginning of the file only)
  • 不必完全解析文件
  • 快速(仅读取文件开头)

Disadvantages

缺点

  • High chances of false positives (e.g.: echo "class Foo {";)
  • 误报的几率高(如:echo "class Foo {";

Code

代码

$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;
    }
}

Note: The regex can probably be improved, but no regex alone can do this perfectly.

注意:正则表达式可能可以改进,但没有正则表达式可以完美地做到这一点。

Get list of declared classes

获取已声明类的列表

This method uses get_declared_classes()and look for the first class defined after an include.

此方法使用get_declared_classes()并查找在包含之后定义的第一个类。

Advantages

好处

  • Shortest solution
  • No chance of false positive
  • 最短解
  • 没有误报的机会

Disadvantages

缺点

  • Have to load the entire file
  • Have to load the entire list of classes in memory twice
  • Have to load the class definition in memory
  • 必须加载整个文件
  • 必须在内存中加载整个类列表两次
  • 必须在内存中加载类定义

Code

代码

$classes = get_declared_classes();
include 'test2.php';
$diff = array_diff(get_declared_classes(), $classes);
$class = reset($diff);

Note: You cannot simply do end()as others suggested. If the class includes another class, you will get a wrong result.

注意:你不能简单地end()按照别人的建议去做。如果该类包含另一个类,您将得到错误的结果。



This is the Tokenizersolution, modified to include a $namespacevariable containing the class namespace, if applicable:

这是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];
                }
            }
        }
    }
}

Say you have this class:

假设你有这个类:

namespace foo\bar {
    class hello { }
}

...or the alternative syntax:

...或替代语法:

namespace foo\bar;
class hello { }

You should have the following result:

你应该有以下结果:

var_dump($namespace); // \foo\bar
var_dump($class);     // hello

You could also use the above to detect the namespace a file declares, regardless of it containing a class or not.

您还可以使用上面的方法来检测文件声明的命名空间,无论它是否包含类。

回答by hakre

You can make PHP do the work by just including the file and get the last declared class:

您可以通过只包含文件并获取最后一个声明的类来让 PHP 完成这项工作:

$file = 'class.php'; # contains class Foo

include($file);
$classes = get_declared_classes();
$class = end($classes);
echo $class; # Foo

If you need to isolate that, wrap it into a commandline script and execute it via shell_exec:

如果您需要隔离它,请将其包装到命令行脚本中并通过shell_exec以下方式执行:

$file = 'class.php'; # contains class Foo

$class = shell_exec("php -r \"include('$file'); echo end(get_declared_classes());\"");    
echo $class; # Foo

If you dislike commandline scripts, you can do it like in this question, however that code does not reflect namespaces.

如果你不喜欢命令行脚本,你可以像在这个问题中那样做,但是该代码不反映命名空间。

回答by Mahmoud Zalt

Thanks to some people from Stackoverflow and Github, I was able to write this amazing fully working solution:

感谢 Stackoverflow 和 Github 的一些人,我能够编写出这个令人惊叹的完整解决方案:

/**
 * get the full name (name \ namespace) of a class from its file path
 * result example: (string) "I\Am\The\Namespace\Of\This\Class"
 *
 * @param $filePathName
 *
 * @return  string
 */
public function getClassFullNameFromFile($filePathName)
{
    return $this->getClassNamespaceFromFile($filePathName) . '\' . $this->getClassNameFromFile($filePathName);
}


/**
 * build and return an object of a class from its file path
 *
 * @param $filePathName
 *
 * @return  mixed
 */
public function getClassObjectFromFile($filePathName)
{
    $classString = $this->getClassFullNameFromFile($filePathName);

    $object = new $classString;

    return $object;
}





/**
 * get the class namespace form file path using token
 *
 * @param $filePathName
 *
 * @return  null|string
 */
protected function getClassNamespaceFromFile($filePathName)
{
    $src = file_get_contents($filePathName);

    $tokens = token_get_all($src);
    $count = count($tokens);
    $i = 0;
    $namespace = '';
    $namespace_ok = false;
    while ($i < $count) {
        $token = $tokens[$i];
        if (is_array($token) && $token[0] === T_NAMESPACE) {
            // Found namespace declaration
            while (++$i < $count) {
                if ($tokens[$i] === ';') {
                    $namespace_ok = true;
                    $namespace = trim($namespace);
                    break;
                }
                $namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
            }
            break;
        }
        $i++;
    }
    if (!$namespace_ok) {
        return null;
    } else {
        return $namespace;
    }
}

/**
 * get the class name form file path using token
 *
 * @param $filePathName
 *
 * @return  mixed
 */
protected function getClassNameFromFile($filePathName)
{
    $php_code = file_get_contents($filePathName);

    $classes = array();
    $tokens = token_get_all($php_code);
    $count = count($tokens);
    for ($i = 2; $i < $count; $i++) {
        if ($tokens[$i - 2][0] == T_CLASS
            && $tokens[$i - 1][0] == T_WHITESPACE
            && $tokens[$i][0] == T_STRING
        ) {

            $class_name = $tokens[$i][1];
            $classes[] = $class_name;
        }
    }

    return $classes[0];
}

回答by karser

I modified Nette\Reflection\AnnotationsParserthat so it returns an array of namespace+classname that are defined in the file

我修改Nette\Reflection\AnnotationsParser了它,因此它返回文件中定义的命名空间+类名数组

$parser = new PhpParser();
$parser->extractPhpClasses('src/Path/To/File.php');


class PhpParser
{
    public function extractPhpClasses(string $path)
    {
        $code = file_get_contents($path);
        $tokens = @token_get_all($code);
        $namespace = $class = $classLevel = $level = NULL;
        $classes = [];
        while (list(, $token) = each($tokens)) {
            switch (is_array($token) ? $token[0] : $token) {
                case T_NAMESPACE:
                    $namespace = ltrim($this->fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\', '\');
                    break;
                case T_CLASS:
                case T_INTERFACE:
                    if ($name = $this->fetch($tokens, T_STRING)) {
                        $classes[] = $namespace . $name;
                    }
                    break;
            }
        }
        return $classes;
    }

    private function fetch(&$tokens, $take)
    {
        $res = NULL;
        while ($token = current($tokens)) {
            list($token, $s) = is_array($token) ? $token : [$token, $token];
            if (in_array($token, (array) $take, TRUE)) {
                $res .= $s;
            } elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], TRUE)) {
                break;
            }
            next($tokens);
        }
        return $res;
    }
}

回答by Emeka Mbah

I spent lots of productive time looking for a way around this. From @netcoder's solution its obvious there are lots of cons in all the solutions so far.

我花了很多富有成效的时间来寻找解决这个问题的方法。从@netcoder 的解决方案来看,显然到目前为止所有解决方案都存在很多缺点。

So I decided to do this instead. Since most PHP classes has class name same as filename, we could get the class name from the filename. Depending on your project you could also have a naming convention. NB: This assume class does not have namespace

所以我决定改为这样做。由于大多数 PHP 类的类名与文件名相同,我们可以从文件名中获取类名。根据您的项目,您还可以有一个命名约定。注意:这个假设类没有命名空间

<?php
    $path = '/path/to/a/class/file.php';

    include $path;

    /*get filename without extension which is the classname*/
    $classname = pathinfo(basename($path), PATHINFO_FILENAME);

    /* you can do all this*/
    $classObj = new $classname();

    /*dough the file name is classname you can still*/
    get_class($classObj); //still return classname

回答by vadim

$st = get_declared_classes();
include "classes.php"; //one or more classes in file, contains class class1, class2, etc...

$res = array_values(array_diff_key(get_declared_classes(),$st));
print_r($res); # Array ([0] => class1 [1] => class2 [2] ...)

回答by AbiusX

This sample returns all classes. If you're looking for a class which is derived of a specific one, use is_subclass_of

此示例返回所有类。如果您正在寻找派生自特定类的类,请使用 is_subclass_of

$php_code = file_get_contents ( $file );
    $classes = array ();
    $namespace="";
    $tokens = token_get_all ( $php_code );
    $count = count ( $tokens );

    for($i = 0; $i < $count; $i ++)
    {
        if ($tokens[$i][0]===T_NAMESPACE)
        {
            for ($j=$i+1;$j<$count;++$j)
            {
                if ($tokens[$j][0]===T_STRING)
                    $namespace.="\".$tokens[$j][1];
                elseif ($tokens[$j]==='{' or $tokens[$j]===';')
                    break;
            }
        }
        if ($tokens[$i][0]===T_CLASS)
        {
            for ($j=$i+1;$j<$count;++$j)
                if ($tokens[$j]==='{')
                {
                    $classes[]=$namespace."\".$tokens[$i+2][1];
                }
        }
    }
    return $classes;

回答by jeroen

You could get all declared classes before you include the file using get_declared_classes. Do the same thing after you have included it and compare the two with something like array_diffand you have your newly added class.

您可以在使用包含文件之前获取所有声明的类get_declared_classes。在包含它之后做同样的事情,并将两者与类似的东西进行比较array_diff,你就有了新添加的类。

回答by Fabio Cicerchia

You can do this in two ways:

您可以通过两种方式执行此操作:

  • complex solution: open the file and through regex extract the class-name (like /class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/)
  • simply solution: name all your php files with the class-name contained (eg: the class TestFooin the file TestFoo.phpor TestFoo.class.php)
  • 复杂的解决方案:打开文件并通过正则表达式提取类名(如/class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/
  • 简单的解决方案:使用包含的类名命名所有 php 文件(例如:TestFoo文件中的类TestFoo.phpTestFoo.class.php

回答by jn1kk

You may be able to use the autoload function.

您也许可以使用自动加载功能。

function __autoload($class_name) {
    include "special_directory/" .$class_name . '.php';
}

And you can echo $class_name. But that requires a directory with a single file.

你可以回显 $class_name。但这需要一个包含单个文件的目录。

But it is standard practice to have one class in each file in PHP. So Main.class.php will contain Main class. You may able to use that standard if you are the one coding.

但是在 PHP 中的每个文件中都有一个类是标准做法。所以 Main.class.php 将包含 Main 类。如果您是唯一的编码人员,您可能可以使用该标准。