Get class name from file

后端 未结 10 1831
轮回少年
轮回少年 2020-12-08 10:29

I have a php file which contains only one class. how can I know what class is there by knowing the filename? I know I can do something with regexp matching but is there a st

相关标签:
10条回答
  • 2020-12-08 11:00

    Thanks to some people from Stackoverflow and Github, I was able to write this amazing fully working solution:

    /**
     * get the full name (name \ namespace) of a class from its file path
     * result example: (string) "I\Am\The\Namespace\Of\This\Class"
     *
     * @param $filePathName
     *
     * @return  string
     */
    public function getClassFullNameFromFile($filePathName)
    {
        return $this->getClassNamespaceFromFile($filePathName) . '\\' . $this->getClassNameFromFile($filePathName);
    }
    
    
    /**
     * build and return an object of a class from its file path
     *
     * @param $filePathName
     *
     * @return  mixed
     */
    public function getClassObjectFromFile($filePathName)
    {
        $classString = $this->getClassFullNameFromFile($filePathName);
    
        $object = new $classString;
    
        return $object;
    }
    
    
    
    
    
    /**
     * get the class namespace form file path using token
     *
     * @param $filePathName
     *
     * @return  null|string
     */
    protected function getClassNamespaceFromFile($filePathName)
    {
        $src = file_get_contents($filePathName);
    
        $tokens = token_get_all($src);
        $count = count($tokens);
        $i = 0;
        $namespace = '';
        $namespace_ok = false;
        while ($i < $count) {
            $token = $tokens[$i];
            if (is_array($token) && $token[0] === T_NAMESPACE) {
                // Found namespace declaration
                while (++$i < $count) {
                    if ($tokens[$i] === ';') {
                        $namespace_ok = true;
                        $namespace = trim($namespace);
                        break;
                    }
                    $namespace .= is_array($tokens[$i]) ? $tokens[$i][1] : $tokens[$i];
                }
                break;
            }
            $i++;
        }
        if (!$namespace_ok) {
            return null;
        } else {
            return $namespace;
        }
    }
    
    /**
     * get the class name form file path using token
     *
     * @param $filePathName
     *
     * @return  mixed
     */
    protected function getClassNameFromFile($filePathName)
    {
        $php_code = file_get_contents($filePathName);
    
        $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[0];
    }
    
    0 讨论(0)
  • 2020-12-08 11:05

    This sample returns all classes. If you're looking for a class which is derived of a specific one, use is_subclass_of

    $php_code = file_get_contents ( $file );
        $classes = array ();
        $namespace="";
        $tokens = token_get_all ( $php_code );
        $count = count ( $tokens );
    
        for($i = 0; $i < $count; $i ++)
        {
            if ($tokens[$i][0]===T_NAMESPACE)
            {
                for ($j=$i+1;$j<$count;++$j)
                {
                    if ($tokens[$j][0]===T_STRING)
                        $namespace.="\\".$tokens[$j][1];
                    elseif ($tokens[$j]==='{' or $tokens[$j]===';')
                        break;
                }
            }
            if ($tokens[$i][0]===T_CLASS)
            {
                for ($j=$i+1;$j<$count;++$j)
                    if ($tokens[$j]==='{')
                    {
                        $classes[]=$namespace."\\".$tokens[$i+2][1];
                    }
            }
        }
        return $classes;
    
    0 讨论(0)
  • 2020-12-08 11:05

    You can do this in two ways:

    • complex solution: open the file and through regex extract the class-name (like /class ([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/)
    • simply solution: name all your php files with the class-name contained (eg: the class TestFoo in the file TestFoo.php or TestFoo.class.php)
    0 讨论(0)
  • 2020-12-08 11:06

    You can make PHP do the work by just including the file and get the last declared class:

    $file = 'class.php'; # contains class Foo
    
    include($file);
    $classes = get_declared_classes();
    $class = end($classes);
    echo $class; # Foo
    

    If you need to isolate that, wrap it into a commandline script and execute it via shell_exec:

    $file = 'class.php'; # contains class Foo
    
    $class = shell_exec("php -r \"include('$file'); echo end(get_declared_classes());\"");    
    echo $class; # Foo
    

    If you dislike commandline scripts, you can do it like in this question, however that code does not reflect namespaces.

    0 讨论(0)
  • 2020-12-08 11:09

    There are multiple possible solutions to this problem, each with their advantages and disadvantages. Here they are, it's up to know to decide which one you want.

    Tokenizer

    This method uses the tokenizer and reads parts of the file until it finds a class definition.

    Advantages

    • Do not have to parse the file entirely
    • Fast (reads the beginning of the file only)
    • Little to no chance of false positives

    Disadvantages

    • Longest solution

    Code

    $fp = fopen($file, 'r');
    $class = $buffer = '';
    $i = 0;
    while (!$class) {
        if (feof($fp)) break;
    
        $buffer .= fread($fp, 512);
        $tokens = token_get_all($buffer);
    
        if (strpos($buffer, '{') === false) continue;
    
        for (;$i<count($tokens);$i++) {
            if ($tokens[$i][0] === T_CLASS) {
                for ($j=$i+1;$j<count($tokens);$j++) {
                    if ($tokens[$j] === '{') {
                        $class = $tokens[$i+2][1];
                    }
                }
            }
        }
    }
    

    Regular expressions

    Use regular expressions to parse the beginning of the file, until a class definition is found.

    Advantages

    • Do not have to parse the file entirely
    • Fast (reads the beginning of the file only)

    Disadvantages

    • High chances of false positives (e.g.: echo "class Foo {";)

    Code

    $fp = fopen($file, 'r');
    $class = $buffer = '';
    $i = 0;
    while (!$class) {
        if (feof($fp)) break;
    
        $buffer .= fread($fp, 512);
        if (preg_match('/class\s+(\w+)(.*)?\{/', $buffer, $matches)) {
            $class = $matches[1];
            break;
        }
    }
    

    Note: The regex can probably be improved, but no regex alone can do this perfectly.

    Get list of declared classes

    This method uses get_declared_classes() and look for the first class defined after an include.

    Advantages

    • Shortest solution
    • No chance of false positive

    Disadvantages

    • Have to load the entire file
    • Have to load the entire list of classes in memory twice
    • Have to load the class definition in memory

    Code

    $classes = get_declared_classes();
    include 'test2.php';
    $diff = array_diff(get_declared_classes(), $classes);
    $class = reset($diff);
    

    Note: You cannot simply do end() as others suggested. If the class includes another class, you will get a wrong result.


    This is the Tokenizer solution, modified to include a $namespace variable containing the class namespace, if applicable:

    $fp = fopen($file, 'r');
    $class = $namespace = $buffer = '';
    $i = 0;
    while (!$class) {
        if (feof($fp)) break;
    
        $buffer .= fread($fp, 512);
        $tokens = token_get_all($buffer);
    
        if (strpos($buffer, '{') === false) continue;
    
        for (;$i<count($tokens);$i++) {
            if ($tokens[$i][0] === T_NAMESPACE) {
                for ($j=$i+1;$j<count($tokens); $j++) {
                    if ($tokens[$j][0] === T_STRING) {
                         $namespace .= '\\'.$tokens[$j][1];
                    } else if ($tokens[$j] === '{' || $tokens[$j] === ';') {
                         break;
                    }
                }
            }
    
            if ($tokens[$i][0] === T_CLASS) {
                for ($j=$i+1;$j<count($tokens);$j++) {
                    if ($tokens[$j] === '{') {
                        $class = $tokens[$i+2][1];
                    }
                }
            }
        }
    }
    

    Say you have this class:

    namespace foo\bar {
        class hello { }
    }
    

    ...or the alternative syntax:

    namespace foo\bar;
    class hello { }
    

    You should have the following result:

    var_dump($namespace); // \foo\bar
    var_dump($class);     // hello
    

    You could also use the above to detect the namespace a file declares, regardless of it containing a class or not.

    0 讨论(0)
  • 2020-12-08 11:13

    You could get all declared classes before you include the file using get_declared_classes. Do the same thing after you have included it and compare the two with something like array_diff and you have your newly added class.

    0 讨论(0)
提交回复
热议问题