Last active
August 26, 2024 16:17
-
-
Save cwhite92/f0aaf008e1679b27768fbb8c884df6f7 to your computer and use it in GitHub Desktop.
Get the FQCN from a PHP file specified by file path
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
public static function fqcnFromPath(string $path): string | |
{ | |
$namespace = $class = $buffer = ''; | |
$handle = fopen($path, 'r'); | |
while (!feof($handle)) { | |
$buffer .= fread($handle, 512); | |
// Suppress warnings for cases where `$buffer` ends in the middle of a PHP comment. | |
$tokens = @token_get_all($buffer); | |
// Filter out whitespace and comments from the tokens, as they are irrelevant. | |
$tokens = array_filter($tokens, fn($token) => $token[0] !== T_WHITESPACE && $token[0] !== T_COMMENT); | |
// Reset array indexes after filtering. | |
$tokens = array_values($tokens); | |
foreach ($tokens as $index => $token) { | |
// The namespace is a `T_NAME_QUALIFIED` that is immediately preceded by a `T_NAMESPACE`. | |
if ($token[0] === T_NAMESPACE && isset($tokens[$index + 1]) && $tokens[$index + 1][0] === T_NAME_QUALIFIED) { | |
$namespace = $tokens[$index + 1][1]; | |
continue; | |
} | |
// The class name is a `T_STRING` which makes it unreliable to match against, so check if we have a | |
// `T_CLASS` token with a `T_STRING` token ahead of it. | |
if ($token[0] === T_CLASS && isset($tokens[$index + 1]) && $tokens[$index + 1][0] === T_STRING) { | |
$class = $tokens[$index + 1][1]; | |
} | |
} | |
if ($namespace && $class) { | |
// We've found both the namespace and the class, we can now stop reading and parsing the file. | |
break; | |
} | |
} | |
fclose($handle); | |
return $namespace.'\\'.$class; | |
} |
There can be problems when a multiple of 512 lands in the middle of the class name. you can resolve it by making sure the class name was not the last token parsed by the token_get_all. Simplest way I found was to decrement a counter on every token, and set the counter to 2 when you find the class name. Then do if ($namespace && $class && $lastTokenIsClassNameCounter !== 1) {
. That way if your class name was the last token in the buffer, and it may have gotten split, you do at least one more read from the file. Won't work if your php file is malformed, but then your php file is malformed :)
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Based on my testing this works for the following examples:
and even some wonky whitespace or comments: