2

Today my objective is to retrieve all PHP class names associated with their namespace names but I got stuck. Here is an example of what I have:

$content =<<<END
<?php

namespace test;

class a { }

class b { }

namespace foo;

class bar { }
?>
END;

preg_match_all('~^\s*((?:namespace)\s+(\w+);)?\s*(?:abstract\s+|final\s+)?(?:class|interface)\s+(\w+)~mi', $content, $classes);
var_dump($classes);

The expression works only if there is at most one class in namespace but I can't figure out how to make it match all classes according to namespace.

Eddie C.
  • 918
  • 10
  • 16

3 Answers3

6

You can try the following pattern:

((?:\\{1,2}\w+|\w+\\{1,2})(?:\w+\\{0,2})+)

For some reason, I had to match both Namespace\Class and Namespace\\Class. But you have replace the {1,2} and {0,2} by respectively nothing and ?.

Test it online: https://regex101.com/r/fE2kU9/3

Eddie C.
  • 918
  • 10
  • 16
magnetik
  • 4,351
  • 41
  • 58
  • 1
    Actually not quite correct, since this would still pass but is an invalid namespace: `\Ending\Slash\\`. You need to add `\w+` to the end of the last group to ensure the group has a final namespace class - see these tests https://regex101.com/r/rQpdmc/3/tests – buggedcom Nov 23 '16 at 08:09
4

Perhaps it is easier to use the PHP tokenizer instead of a regular expression. Simply walk over the list of tokens looking for T_NAMESPACE and T_CLASS.

Example (untested):

$map = array();
$tokens = token_get_all($source_code);
$namespace = 'GLOBAL';
foreach ($tokens as $token) {
    if (!is_string($token)) {
        list($id, $text) = $token;
        if ($id == T_NAMESPACE) {
            $namespace = $text;
        }
        if ($id == T_CLASS) {
            $map[$namespace] = $text;
        }
    }
}
print_r($map);
Sander Marechal
  • 22,978
  • 13
  • 65
  • 96
1

i do this function for my project and it work using tokens and finder library

/**
 * @param \Symfony\Component\Finder\SplFileInfo $file
 * @return string
 */
private function getAbsoluteClassNameFromFile($file)
{
    $tokens = PhpToken::tokenize($file->getContents());
    $namespace = [];
    foreach ($tokens as $index => $token) {
        if ($token->is(T_NAMESPACE) && $tokens[$index+2]->is(T_STRING)){
            for ($i = $index+2 ;!$tokens[$i]->is(T_WHITESPACE);$i++){
                if ($tokens[$i]->text === ";"){
                    continue;
                }
                $namespace[] = $tokens[$i]->text;
            }
            return implode('',$namespace)."\\".$file->getFilenameWithoutExtension();
        }
    }
    return "\\".$file->getFilenameWithoutExtension();
}