鉴于我们项目中的每个PHP文件都包含一个类定义,如何确定文件中定义的类或类?
我知道我可以正确使用class
语句的文件,但我更愿意做一些效率更高的事情。
答案 0 :(得分:61)
对于我正在处理的项目,我需要这样的东西,这是我写的函数:
function file_get_php_classes($filepath) {
$php_code = file_get_contents($filepath);
$classes = get_php_classes($php_code);
return $classes;
}
function get_php_classes($php_code) {
$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;
}
答案 1 :(得分:16)
如果您只想检查文件而不加载文件,请使用token_get_all()
:
<?php
header('Content-Type: text/plain');
$php_file = file_get_contents('c2.php');
$tokens = token_get_all($php_file);
$class_token = false;
foreach ($tokens as $token) {
if (is_array($token)) {
if ($token[0] == T_CLASS) {
$class_token = true;
} else if ($class_token && $token[0] == T_STRING) {
echo "Found class: $token[1]\n";
$class_token = false;
}
}
}
?>
基本上,这是一个简单的有限状态机。在PHP中,tokens的序列将是:
T_CLASS
:'class'关键字; T_WHITESPACE
:'class'之后的空格; T_STRING
:班级名称。所以这段代码会处理你得到的任何奇怪的间距或换行,因为它使用了PHP用来执行文件的相同解析器。如果token_get_all()
无法解析它,PHP也不能解析。
顺便说一下,您使用token_name()
将令牌号转换为常量名。
这是我的c2.php:
<?php
class MyClass {
public __construct() {
}
}
class MyOtherClass {
public __construct() {
}
}
?>
输出:
Found class: MyClass
Found class: MyOtherClass
答案 2 :(得分:5)
我需要来自带有命名空间的文件的解析类,所以我修改了代码。如果有人需要,也就是这样:
public function getPhpClasses($phpcode) {
$classes = array();
$namespace = 0;
$tokens = token_get_all($phpcode);
$count = count($tokens);
$dlm = false;
for ($i = 2; $i < $count; $i++) {
if ((isset($tokens[$i - 2][1]) && ($tokens[$i - 2][1] == "phpnamespace" || $tokens[$i - 2][1] == "namespace")) ||
($dlm && $tokens[$i - 1][0] == T_NS_SEPARATOR && $tokens[$i][0] == T_STRING)) {
if (!$dlm) $namespace = 0;
if (isset($tokens[$i][1])) {
$namespace = $namespace ? $namespace . "\\" . $tokens[$i][1] : $tokens[$i][1];
$dlm = true;
}
}
elseif ($dlm && ($tokens[$i][0] != T_NS_SEPARATOR) && ($tokens[$i][0] != T_STRING)) {
$dlm = false;
}
if (($tokens[$i - 2][0] == T_CLASS || (isset($tokens[$i - 2][1]) && $tokens[$i - 2][1] == "phpclass"))
&& $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING) {
$class_name = $tokens[$i][1];
if (!isset($classes[$namespace])) $classes[$namespace] = array();
$classes[$namespace][] = $class_name;
}
}
return $classes;
}
答案 3 :(得分:4)
我的片段也是。可以解析具有多个类,接口,数组和命名空间的文件。 返回一个数组,其中包含类+类型(类,接口,抽象)除以名称空间。
<?php
/**
*
* Looks what classes and namespaces are defined in that file and returns the first found
* @param String $file Path to file
* @return Returns NULL if none is found or an array with namespaces and classes found in file
*/
function classes_in_file($file)
{
$classes = $nsPos = $final = array();
$foundNS = FALSE;
$ii = 0;
if (!file_exists($file)) return NULL;
$er = error_reporting();
error_reporting(E_ALL ^ E_NOTICE);
$php_code = file_get_contents($file);
$tokens = token_get_all($php_code);
$count = count($tokens);
for ($i = 0; $i < $count; $i++)
{
if(!$foundNS && $tokens[$i][0] == T_NAMESPACE)
{
$nsPos[$ii]['start'] = $i;
$foundNS = TRUE;
}
elseif( $foundNS && ($tokens[$i] == ';' || $tokens[$i] == '{') )
{
$nsPos[$ii]['end']= $i;
$ii++;
$foundNS = FALSE;
}
elseif ($i-2 >= 0 && $tokens[$i - 2][0] == T_CLASS && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING)
{
if($i-4 >=0 && $tokens[$i - 4][0] == T_ABSTRACT)
{
$classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'ABSTRACT CLASS');
}
else
{
$classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'CLASS');
}
}
elseif ($i-2 >= 0 && $tokens[$i - 2][0] == T_INTERFACE && $tokens[$i - 1][0] == T_WHITESPACE && $tokens[$i][0] == T_STRING)
{
$classes[$ii][] = array('name' => $tokens[$i][1], 'type' => 'INTERFACE');
}
}
error_reporting($er);
if (empty($classes)) return NULL;
if(!empty($nsPos))
{
foreach($nsPos as $k => $p)
{
$ns = '';
for($i = $p['start'] + 1; $i < $p['end']; $i++)
$ns .= $tokens[$i][1];
$ns = trim($ns);
$final[$k] = array('namespace' => $ns, 'classes' => $classes[$k+1]);
}
$classes = $final;
}
return $classes;
}
输出类似的东西......
array
'namespace' => string 'test\foo' (length=8)
'classes' =>
array
0 =>
array
'name' => string 'bar' (length=3)
'type' => string 'CLASS' (length=5)
1 =>
array
'name' => string 'baz' (length=3)
'type' => string 'INTERFACE' (length=9)
array
'namespace' => string 'this\is\a\really\big\namespace\for\testing\dont\you\think' (length=57)
'classes' =>
array
0 =>
array
'name' => string 'yes_it_is' (length=9)
'type' => string 'CLASS' (length=5)
1 =>
array
'name' => string 'damn_too_big' (length=12)
'type' => string 'ABSTRACT CLASS' (length=14)
2 =>
array
'name' => string 'fogo' (length=6)
'type' => string 'INTERFACE' (length=9)
可以帮助别人!
答案 4 :(得分:4)
或者您可以轻松使用Nette\Reflection中的AnnotationsParser(可使用作曲家安装):
use Nette\Reflection\AnnotationsParser;
$classes = AnnotationsParser::parsePhp(file_get_contents($fileName));
var_dump($classes);
输出将是这样的:
array(1) {
["Your\Class\Name"] =>
array(...) {
// property => comment
},
["Your\Class\Second"] =>
array(...) {
// property => comment
},
}
parsePhp() method基本上与其他答案中的例子类似,但你不必自己声明或测试解析。
答案 5 :(得分:2)
使用PHP的函数 get_declared_classes()。这将返回当前脚本中定义的类数组。
答案 6 :(得分:1)
我已经扩展了Venkat D的答案,包括返回方法,以及搜索目录。 (此具体示例是为CodeIgniter构建的,它将返回./system/application/controller文件中的所有方法 - 换句话说,您可以通过系统调用的每个公共URL。)
function file_get_php_classes($filepath,$onlypublic=true) {
$php_code = file_get_contents($filepath);
$classes = get_php_classes($php_code,$onlypublic);
return $classes;
}
function get_php_classes($php_code,$onlypublic) {
$classes = array();
$methods=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];
$methods[$class_name] = array();
}
if ($tokens[$i - 2][0] == T_FUNCTION
&& $tokens[$i - 1][0] == T_WHITESPACE
&& $tokens[$i][0] == T_STRING) {
if ($onlypublic) {
if ( !in_array($tokens[$i-4][0],array(T_PROTECTED, T_PRIVATE))) {
$method_name = $tokens[$i][1];
$methods[$class_name][] = $method_name;
}
} else {
$method_name = $tokens[$i][1];
$methods[$class_name][] = $method_name;
}
}
}
return $methods;
}
function mapSystemClasses($controllerdir="./system/application/controllers/",$onlypublic=true) {
$result=array();
$dh=opendir($controllerdir);
while (($file = readdir($dh)) !== false) {
if (substr($file,0,1)!=".") {
if (filetype($controllerdir.$file)=="file") {
$classes=file_get_php_classes($controllerdir.$file,$onlypublic);
foreach($classes as $class=>$method) {
$result[]=array("file"=>$controllerdir.$file,"class"=>$class,"method"=>$method);
}
} else {
$result=array_merge($result,mapSystemClasses($controllerdir.$file."/",$onlypublic));
}
}
}
closedir($dh);
return $result;
}
答案 7 :(得分:0)
您可以忽略这样的抽象类(请注意T_ABSTRACT标记):
function get_php_classes($php_code)
{
$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 && !($tokens[$i - 3] && $i - 4 >= 0 && $tokens[$i - 4][0] == T_ABSTRACT))
{
$class_name = $tokens[$i][1];
$classes[] = $class_name;
}
}
return $classes;
}