确定在PHP类文件中定义了哪些类
鉴于我们的项目中的每个PHP文件都包含一个类定义,我怎样才能确定文件中定义了哪些类或类?
我知道我可以将class
文件的class
语句正则expression式,但是我更愿意做一些更有效的事情。
我需要这样一个项目,我正在工作,这里是我写的function:
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; }
如果你只是想检查一个文件而不加载它使用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中, 令牌的顺序是:
-
T_CLASS
:'class'关键字; -
T_WHITESPACE
:'class'后面的空格; -
T_STRING
:class级的名字。
所以这段代码将处理任何奇怪的间距或换行符,因为它使用了PHP用来执行文件的相同的parsing器。 如果token_get_all()
不能parsing它,PHP也不能parsing它。
顺便说一下,您使用token_name()
将令牌号码转换为常量名称。
这是我的c2.php:
<?php class MyClass { public __construct() { } } class MyOtherClass { public __construct() { } } ?>
输出:
Found class: MyClass Found class: MyOtherClass
我需要使用名称空间从文件parsing类,所以我修改了代码。 如果有人也需要,那就是:
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; }
或者你可以很容易地使用Nette \ Reflection中的 AnnotationsParser(可以使用composer安装):
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()方法基本上和其他答案中的例子类似,但是你不必自己声明或者testingparsing。
我的片段也是。 可以用多个类,接口,数组和命名空间parsing文件。 返回一个由类+types(类,接口,抽象)除以名称空间的数组。
<?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 'fodass' (length=6) 'type' => string 'INTERFACE' (length=9)
可以帮助别人!
使用PHP的函数get_declared_classes() 。 这将返回当前脚本中定义的类的数组。
我已经扩展了Venkat D的答案,包括返回方法,并通过目录search。 (这个特定的例子是为CodeIgniter构build的,它将返回./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; }
你可以忽略像这样的抽象类(注意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; }