Determinazione quali classi sono definite in un file di classe PHP
Domanda
Dato che ogni file PHP nel nostro progetto contiene una definizione di classe sola, come posso determinare quale classe o le classi sono definiti all'interno del file?
So che potrei appena regex il file per le dichiarazioni class
, ma io preferisco fare qualcosa che è più efficiente.
Soluzione
Avevo bisogno di qualcosa di simile per un progetto cui sto lavorando, e qui ci sono le funzioni che ho scritto:
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;
}
Altri suggerimenti
Se si desidera solo per controllare un file senza caricarlo utilizzare 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;
}
}
}
?>
In sostanza, si tratta di una semplice macchina a stati finiti. In PHP la sequenza di sarà:
-
T_CLASS
: parola chiave 'classe'; -
T_WHITESPACE
: lo spazio (s) dopo la 'classe'; -
T_STRING
:. Nome della classe
Quindi, questo codice consente di gestire qualsiasi spaziatura strano o ritorni a capo si ottiene bene perché è utilizzando lo stesso parser PHP utilizza per eseguire il file. Se token_get_all()
non può analizzarlo, e non le può PHP.
A proposito, si utilizza token_name()
per trasformare un numero di token in suo nome costante.
Ecco la mia c2.php:
<?php
class MyClass {
public __construct() {
}
}
class MyOtherClass {
public __construct() {
}
}
?>
Output:
Found class: MyClass
Found class: MyOtherClass
ho bisogno di lezioni di parsing di file con spazi dei nomi, quindi ho modificato il codice. Se qualcuno bisogno di troppo, qui è:
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;
}
In alternativa è possibile usare facilmente AnnotationsParser da Nette \ Riflessione (installabile utilizzando composer):
use Nette\Reflection\AnnotationsParser;
$classes = AnnotationsParser::parsePhp(file_get_contents($fileName));
var_dump($classes);
uscita sarà quindi qualcosa di simile:
array(1) {
["Your\Class\Name"] =>
array(...) {
// property => comment
},
["Your\Class\Second"] =>
array(...) {
// property => comment
},
}
Metodo Il parsePhp () fondamentalmente fa qualcosa di simile come esempi in altre risposte, ma non si deve dichiarare né testare i voi stessi di analisi.
Il mio frammento di troppo. Può analizzare i file con le classi multiple, interfacce, array e gli spazi dei nomi. Restituisce un array con classi + tipi (di classe, interfaccia, astratto) diviso per gli spazi dei nomi.
<?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;
}
Emette qualcosa di simile ...
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)
Potrebbe aiutare qualcuno!
La funzione di Utilizzare PHP get_declared_classes () . Ciò restituisce un array di classi definite nello script corrente.
ho esteso Venkat D's risposta un po 'per includere restituzione dei metodi, e di cercare attraverso una directory. (Questo esempio specifico è costruito per CodeIgniter, che restituirà tutti i metodi nei file ./system/application/controller -. In altre parole, ogni URL pubblico che si può chiamare attraverso il sistema)
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;
}
È possibile ignorare le classi astratte come questo (notare il token 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;
}