poo - namespace php
Determinar qué clases se definen en un archivo de clase PHP (8)
Dado que cada archivo PHP en nuestro proyecto contiene una única definición de clase, ¿cómo puedo determinar qué clase o clases se definen dentro del archivo?
Sé que podría volver a generar el archivo para class
declaraciones de class
, pero preferiría hacer algo que sea más eficiente.
He extendido un poco la respuesta de Venkat D para incluir la devolución de los métodos y para buscar a través de un directorio. (Este ejemplo específico está diseñado para CodeIgniter, que devolverá todos los métodos en los archivos ./system/application/controller, en otras palabras, todas las URL públicas a las que puede llamar a través del 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;
}
Mi fragmento también Puede analizar archivos con múltiples clases, interfaces, matrices y espacios de nombres. Devuelve una matriz con clases + tipos (clase, interfaz, resumen) dividida por espacios de nombres.
<?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;
}
Produce algo como esto ...
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)
Podría ayudar a alguien!
Necesitaba algo así para un proyecto en el que estoy trabajando, y aquí están las funciones que escribí:
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;
}
Necesitaba analizar clases del archivo con espacios de nombres, así que modifiqué el código. Si alguien también necesita, aquí está:
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;
}
O puede usar fácilmente AnnotationsParser de Nette/Reflection (instalable usando el compositor):
use Nette/Reflection/AnnotationsParser;
$classes = AnnotationsParser::parsePhp(file_get_contents($fileName));
var_dump($classes);
La salida será entonces algo como esto:
array(1) {
["Your/Class/Name"] =>
array(...) {
// property => comment
},
["Your/Class/Second"] =>
array(...) {
// property => comment
},
}
El método parsePhp () básicamente hace algo similar como ejemplos en otras respuestas, pero no tiene que declarar ni probar el análisis sintético.
Puede ignorar clases abstractas como esta (observe el 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;
}
Si solo desea verificar un archivo sin cargarlo, use 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;
}
}
}
?>
Básicamente, esta es una máquina de estados finitos simple. En PHP, la secuencia de tokens será:
-
T_CLASS
: palabra clave ''clase''; -
T_WHITESPACE
: espacio (s) después de ''clase''; -
T_STRING
: nombre de la clase.
Por lo tanto, este código manejará cualquier espaciado extraño o líneas nuevas que obtengas bien porque está usando el mismo analizador que PHP usa para ejecutar el archivo. Si token_get_all()
no puede analizarlo, tampoco puede PHP.
Por cierto, usa token_name()
para convertir un número de token en su nombre de constante.
Aquí está mi c2.php:
<?php
class MyClass {
public __construct() {
}
}
class MyOtherClass {
public __construct() {
}
}
?>
Salida:
Found class: MyClass
Found class: MyOtherClass
Use la función de PHP get_declared_classes () . Esto devuelve una matriz de clases definidas en el script actual.