Parsing Classes, Functions and Arguments in PHP

2019-04-07 23:53发布

问题:

I want to create a function which receives a single argument that holds the path to a PHP file and then parses the given file and returns something like this:

class NameOfTheClass
   function Method1($arg1, $arg2, $arg2)
   private function Method2($arg1, $arg2, $arg2)
   public function Method2($arg1, $arg2, $arg2)

abstract class AnotherClass
   function Method1($arg1, $arg2, $arg2)
   private function Method2($arg1, $arg2, $arg2)
   public function Method2($arg1, $arg2, $arg2)

function SomeFunction($arg1, $arg2, $arg3)

This function should return all the classes, methods and function that exist in the given file with all the defined identifiers (abstract, public, private, protected, static, extends, interfaces, ...).

My first tought was to use regular expressions to do this, however these behave quite badly with comments, ie: /* this function returns(max(salary)) */ and become quite complex if I want to properly support scopes.

Another possible solution was to use the following built-in PHP functions:

get_declared_classes
get_declared_interfaces
get_defined_functions
get_class_methods

However these functions don't allow me to see the file where the classes / methods / functions are defined and thus it's not very useful.

I believe the Tokenizer extension is the solution for my problem, however I have never used this extension before.

回答1:

If you are using PHP 5, the Reflection API is your tool.

Example:

$class = new ReflectionClass("NameOfTheClass");
$methods = $class->getMethods();
foreach($methods as $m) {
    print $m->name;
    $m->isPrivate() ? print "Private" : print "";
    $m->isPublic() ? print "Public" : print "";
    $params = $m->getParameters();
    foreach($params as $p) {
        print $p->getName();
        }
}


回答2:

I suggest the following procedure:

  1. store the current output of get_declared_classes, get_declared_interfaces and get_defined_functions(if you really need to support them)
  2. include the file
  3. compare get_declared_classes, get_declared_interfaces and get_defined_functions with the ones you stored to see what's new
  4. use reflection to analyze them
  5. goto step 2 for the next file


回答3:

Like you found out yourself, regex are quite not the right tool for the job, here ^^

And, like you said, the built-in functions you proposed are not that helpful either -- only thing that might be helpful is that they allow you to know which class exists... But they'll return builtin classes too :-(

Using the Tokenizer extension seems a bit overkill/hard to me ; I would probably not go that way, actually : too "low-level", I suppose.


Instead, I would take a look at PHP's Reflection API : it exists exactly to reverse-engineer classes, interfaces, functions, ...

So, I suppose it would be quite well-suited for what you are trying to do.


Edit : here is a quick example :

First, let's try to do reflection on a class :

include dirname(__FILE__) . '/temp-2.php';
$rC = new ReflectionClass('MyFirstClass');

You can now find out in which file it was declared, and which methods are in it :

var_dump($rC->getFileName());
var_dump($rC->getMethods());

Which will get you :

string '/home/squale/developpement/tests/temp/temp-2.php' (length=48)

array
  0 => &
    object(ReflectionMethod)[2]
      public 'name' => string '__construct' (length=11)
      public 'class' => string 'MyFirstClass' (length=12)
  1 => &
    object(ReflectionMethod)[3]
      public 'name' => string 'glop' (length=4)
      public 'class' => string 'MyFirstClass' (length=12)


And now, to get informations on each method :

foreach ($rC->getMethods() as $rM) {
    var_dump($rM, $rM->getParameters());
    echo '-----';
}

You'll get :

object(ReflectionMethod)[3]
  public 'name' => string '__construct' (length=11)
  public 'class' => string 'MyFirstClass' (length=12)

array
  0 => &
    object(ReflectionParameter)[4]
      public 'name' => string 'arg1' (length=4)
  1 => &
    object(ReflectionParameter)[5]
      public 'name' => string 'arg2' (length=4)

-----

object(ReflectionMethod)[2]
  public 'name' => string 'glop' (length=4)
  public 'class' => string 'MyFirstClass' (length=12)

array
  0 => &
    object(ReflectionParameter)[5]
      public 'name' => string 'a' (length=1)


From there, you should be able to dig a bit more ; and arrive to what you first asked ;-)


As a sidenote : there is one thing I have no idea about is : "how to find which classes / methods are declared in a given file" :-(

If anyone has an idea, it'll be welcome !



回答4:

I can't help you with your particular question, as I've never used Tokenizer either.

You don't mention what you're using this for, but would something like phpDoc do what you need?