Get use statement from class
Asked Answered
T

4

9

Not quite sure of the best title but I will explain what I am asking as best I can. Assume I have the following file:

MyCustomClass.php

<?php    

namespace MyNamespace;

use FooNamespace\FooClass;
use BarNamespace\BarClass as Bar;
use BazNamespace\BazClass as BazSpecial;

class MyCustomClass {

    protected $someDependencies = [];

    public function __construct(FooClass $foo, Bar $bar) {

        $someDependencies[] = $foo;
        $someDependencies[] = $bar;
    }
}

Now if I were to use reflection, I could get the fully qualified class names from the type hints in the construct.

However, I would recieve FooNamespace\FooClass and BarNamespace\BarClass. Not, FooNamespace\FooClass and BarNamespace\Bar. I would also get no reference to BazNamespace\BazClass.

Basically, my question is: How can I get the fully qualified names from MyCustomClass.php while only knowing FooClass, Bar, and, BazSpecial?

I do not want to use a file parser as this will eat performance. I want to be able to do something like:

$class = new ReflectionClass('MyCustomClass');
...
$class->getUsedClass('FooClass'); // FooNamespace\FooClass
$class->getUsedClass('Bar'); // BarNamespace\BarClass
$class->getUsedClass('BazSpecial'); // BazNamespace\BazClass

How would I go about doing this?

Tadeas answered 18/5, 2015 at 16:12 Comment(0)
T
2

Seeing as no one has answered, I assume there is not an easy way to achieve this. I have therefore created my own class called ExtendedReflectionClass which achieves what I need.

I have created a gist with the class file and a readme, which is at the bottom so get scrolling!.

ExtendedReflectionClass

Usage example:

require 'ExtendedReflectionClass.php';
require 'MyCustomClass.php';

$class = new ExtendedReflectionClass('MyNamespace\Test\MyCustomClass');

$class->getUseStatements();    
// [
//     [
//         'class' => 'FooNamespace\FooClass',
//         'as' => 'FooClass'
//     ],
//     [
//         'class' => 'BarNamespace\BarClass',
//         'as' => 'Bar'
//     ],
//     [
//         'class' => 'BazNamespace\BazClass',
//         'as' => 'BazSpecial'
//     ]
// ]

$class->hasUseStatement('FooClass'); // true
$class->hasUseStatement('BarNamespace\BarClass'); // true
$class->hasUseStatement('BazSpecial'); // true

$class->hasUseStatement('SomeNamespace\SomeClass'); // false
Tadeas answered 19/5, 2015 at 10:30 Comment(1)
@TheodoreR.Smith 1. i guess i didnt see the need to create a package.. I can't even remember what I needed it for, was 4 years ago 2. yea I do, however again I didn't see the need to use composer over two require statements for a proof of concept. What do you mean by UAF? 3. thanks. I wish I could remember what I needed it for though.Tadeas
B
0

I use the TokenFinderTool for that.

Basically, it uses tokens to extract the use statements.

As far as I know, \Reflection objects in php unfortunately don't have such a method yet.

The code below extracts the use import statements from a file, using the TokenFinder tool.

$tokens = token_get_all(file_get_contents("/path/to/MyCompany/MyClass.php"));
a(TokenFinderTool::getUseDependencies($tokens));

Will output:

array (size=9)
  0 => string 'Bat\CaseTool' (length=12)
  1 => string 'Bat\FileSystemTool' (length=18)
  2 => string 'Bat\StringTool' (length=14)
  3 => string 'Bat\ValidationTool' (length=18)
  4 => string 'CopyDir\AuthorCopyDirUtil' (length=25)
  5 => string 'PhpBeast\AuthorTestAggregator' (length=29)
  6 => string 'PhpBeast\PrettyTestInterpreter' (length=30)
  7 => string 'PhpBeast\Tool\ComparisonErrorTableTool' (length=38)
  8 => string 'Tiphaine\TiphaineTool' (length=21)

Note: if you have a class name only, you can use this snippet instead:

$o = new \ReflectionClass($className);
$tokens = token_get_all(file_get_contents($$o->getFileName()));
$useStatements = TokenFinderTool::getUseDependencies($tokens);
Brahmana answered 15/2, 2019 at 0:0 Comment(0)
M
0

Use BetterReflection (composer req roave/better-reflection)

            $classInfo = (new BetterReflection())
                ->reflector()
                ->reflectClass($class);
            $uses = [];
            foreach ($classInfo->getDeclaringNamespaceAst()->stmts as $stmt) {
                foreach ($stmt->uses??[] as $use) {
                    $uses[] = join('\\', $use->name->parts);
                }
            }
            print_r($uses);
Mideast answered 30/9, 2022 at 15:56 Comment(0)
P
0

This is my solution for a similar task. The solution does not take into account the option with as, because I didn't need it. But short and could be useful.

    /**
     * @param class-string $className
     * @return string[]
     * @throws InternalServerErrorException
     */
    public static function getClassUseStatements(string $className): array
    {
        try {
            $reflectionClass = new \ReflectionClass($className);
        } catch (\ReflectionException $e) {
            throw new InternalServerErrorException($e->getMessage());
        }

        if (false === $reflectionClass->getFileName()) {
            throw new InternalServerErrorException("Can not get fileName of class '$className'.");
        }

        $fileContent = \file_get_contents($reflectionClass->getFileName());

        if (false === $fileContent) {
            throw new InternalServerErrorException(
                "Can not get contents from file {$reflectionClass->getFileName()}."
            );
        }

        $tokens = \token_get_all($fileContent);

        $useStatements = [];

        while (null !== key($tokens)) {
            $token = \current($tokens);

            if (\is_array($token) && $token[0] === T_USE) {
                \next($tokens);
                \next($tokens);

                $token = \current($tokens);

                if (\is_array($token) && $token[0] === T_NAME_QUALIFIED) {
                    $useStatements[] = $token[1];
                }
            }

            \next($tokens);
        }

        return $useStatements;
    }
Pia answered 2/5, 2023 at 8:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.