La determinación de lo que las clases se definen en un archivo de clase PHP

StackOverflow https://stackoverflow.com/questions/928928

  •  06-09-2019
  •  | 
  •  

Pregunta

Teniendo en cuenta que cada archivo PHP en nuestro proyecto contiene una definición de clase única, ¿cómo puedo determinar qué clase o clases se definen en el archivo?

Sé que sólo pudiera regex el archivo de declaraciones class, pero yo prefiero hacer algo que es más eficiente.

¿Fue útil?

Solución

necesitaba algo como esto para un proyecto 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;
}

Otros consejos

Si lo que desea es comprobar un archivo sin cargarla usar 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, se trata de una sencilla máquina de estados finitos. En PHP la secuencia de tokens rel="noreferrer"> será:

  • T_CLASS: palabra clave 'clase';
  • T_WHITESPACE: espacio (s) después de la 'clase';
  • T_STRING:. Nombre de la clase

Así que este código se encargará de cualquier separación raro o saltos de línea se obtiene muy bien porque está utilizando el mismo analizador PHP utiliza para ejecutar el archivo. Si no puede analizar token_get_all() él, ni puede hacer con PHP.

Por cierto, utiliza token_name() a su vez un número de token en su nombre constante.

Aquí está mi c2.php:

<?php
class MyClass {
  public __construct() {
  }
}

class MyOtherClass {
  public __construct() {
  }
}
?>

Salida:

Found class: MyClass
Found class: MyOtherClass

que necesitaba clases de análisis sintáctico de archivo con espacios de nombres, por lo que he modificado el código. Si alguien necesita también, aquí es:

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 se podría utilizar fácilmente AnnotationsParser de Nette \ Reflexión (instalable utilizando 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
  },
}
método

El parsePhp () básicamente hace algo similar como ejemplos en otras respuestas, pero usted no tiene que declarar ni probar los mismos análisis.

Mi fragmento también. Se puede analizar archivos con múltiples clases, interfaces, matrices y espacios de nombres. Devuelve una matriz con clases de tipos (+ clase, interfaz abstracta) 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;
    }

retorna 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 'fogo' (length=6)
          'type' => string 'INTERFACE' (length=9)

Podría ayudar a alguien!

La función del uso de PHP get_declared_classes () . Esto devuelve una serie de clases definidas en el script actual.

Hemos ampliado en Venkat D's respuesta un poco para incluir la devolución de los métodos, y para buscar a través de un directorio. (Este ejemplo específico se construye para CodeIgniter, que devolverá todos los métodos en los archivos ./system/application/controller -. En otras palabras, cada URL pública que se 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;
}

Puede pasar por alto las clases abstractas como esto (tenga en cuenta 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;
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top