PHP Reflection

Nette\Utils\Reflection is a static class with useful functions for PHP reflection.

Installation:

composer require nette/utils

All examples assume the following class alias is defined:

use Nette\Utils\Reflection;

areCommentsAvailable(): bool

Finds out if reflection has access to PHPdoc comments. Comments may not be available due to the opcode cache, see for example the directive opcache.save-comments.

expandClassName(string $name, ReflectionClass $context)string

Expands the $name of the class to full name in the context of the $context, ie in the context of its namespace and defined aliases. Thus, it returns how the PHP parser would understand $name if it were written in the body of the $context.

namespace Foo;
use Bar;

class DemoClass
{
	// new Bar, new Baz
}

$context = new ReflectionClass(Foo\DemoClass::class);
Reflection::expandClassName('Bar', $context); // 'Bar'
Reflection::expandClassName('Baz', $context); // 'Foo\Baz'

getMethodDeclaringMethod(ReflectionMethod $method): ReflectionMethod

Returns a reflection of a method that contains a declaration of $method. Usually, each method is its own declaration, but the body of the method can also be in the trait and under a different name.

Because PHP does not provide enough information to determine the actual declaration, Nette uses its own heuristics, which should be reliable.

trait DemoTrait;
{
	function foo()
	{
	}
}


class DemoClass
{
	use DemoTrait {
		DemoTrait::foo as foo2;
	}
}


$method = new ReflectionMethod('DemoClass::foo2');
Reflection::getMethodDeclaringMethod($method); // ReflectionMethod('DemoTrait::foo')

getParameterDefaultValue(ReflectionParameter $param)

Returns the default value of $param in a function or method. If it is a constant, it returns its value. If the parameter does not have a default value or the constant cannot be resolved, it throws ReflectionException.

class DemoClass
{
	function foo($param = PDO::FETCH_BOTH)
	{
	}
}

$method = new ReflectionMethod('DemoClass::foo');
Reflection::getParameterDefaultValue($method->getParameters()[0]);

getParameterType(ReflectionParameter $param, bool $asName = true): string|ReflectionType|null

Returns the type of the function or method $param parameter and normalizes self and parent to the actual class name. If the parameter has no type, it returns null.

class DemoClass
{
	function foo(self $param)
	{
	}
}

$method = new ReflectionMethod('DemoClass::foo');
Reflection::getReturnType($method->getParameters()[0]); // 'DemoClass'

This method was created when PHP did not have union and intersection types. In their case, it throws a Nette\InvalidStateException exception. This behavior can be changed by setting the $asName parameter to false and then it returns ReflectionType object instead of the type name as string. As of nette/utils version 4.0, this behavior will be the default. The $asName parameter has existed since version 3.2.4.

getPropertyDeclaringClass(ReflectionProperty $prop): ReflectionClass

Returns a reflection of a class or trait that contains a declaration of property $prop. Property can also be declared in the trait.

Because PHP does not provide enough information to determine the actual declaration, Nette uses its own heuristics, which are not reliable.

trait DemoTrait;
{
	public $foo;
}


class DemoClass
{
	use DemoTrait;
}

$prop = new ReflectionProperty(DemoClass::class, 'foo');
Reflection::getPropertyDeclaringClass($prop); // ReflectionClass('DemoTrait')

getPropertyType(ReflectionProperty $prop, bool $asName = true): string|ReflectionType|null

Returns the type of property $prop and normalizes self and parent to the actual class name. If property has no type, it returns null.

class DemoClass
{
	public self $foo;
}

$prop = new ReflectionProperty(DemoClass::class, 'foo');
Reflection::getReturnType($prop); // 'DemoClass'

This method was created when PHP did not have union and intersection types. In their case, it throws a Nette\InvalidStateException exception. This behavior can be changed by setting the $asName parameter to false and then it returns ReflectionType object instead of the type name as string. As of nette/utils version 4.0, this behavior will be the default. The $asName parameter has existed since version 3.2.4.

getReturnType(ReflectionFunctionAbstract $func, bool $asName = true): string|ReflectionType|null

Returns the return type of the function or method $func and normalizes self, static and parent to the actual class name. If the function does not have a return type, it returns null.

class DemoClass
{
	function foo(): self
	{
	}
}

$method = new ReflectionMethod('DemoClass::foo');
Reflection::getReturnType($method); // 'DemoClass'

This method was created when PHP did not have union and intersection types. In their case, it throws a Nette\InvalidStateException exception. This behavior can be changed by setting the $asName parameter to false and then it returns ReflectionType object instead of the type name as string. As of nette/utils version 4.0, this behavior will be the default. The $asName parameter has existed since version 3.2.4.

isBuiltinType(string $type)bool

Determines if $type is PHP built-in type. Otherwise, it is the class name.

Reflection::isBuiltinType('string'); // true
Reflection::isBuiltinType('Foo');    // false

toString($reflection): string

Converts a reflection to a human readable string.

$func = new ReflectionFunction('func');
echo Reflection::toString($func); // 'func' ('func()' since v3.2.0)

$class = new ReflectionClass('DemoClass');
echo Reflection::toString($class); // 'DemoClass'

$method = new ReflectionMethod('DemoClass', 'foo');
echo Reflection::toString($method); // 'DemoClass::foo' ('DemoClass::foo()' since v3.2.0)

$param = new ReflectionParameter(['DemoClass', 'foo'], 'param');
echo Reflection::toString($param); // '$param in DemoClass::foo()'

$prop = new ReflectionProperty('DemoClass', 'foo');
echo Reflection::toString($prop); // 'DemoClass::$foo'

ReflectionType

Objects Nette\Utils\ReflectionType are returned by the methods getParameterType(), getPropertyType() and getReturnType() since nette/utils 3.2.4 if the $asName parameter is set to false. As of version 4.0, this behavior will be the default.

Each type is either single, union or intersection. In single types we can check if they are built-in (i.e. native types, not classes):

$type = new Nette\Utils\ReflectionType('string');
$type->isSingle();       // true
$type->isUnion();        // false
$type->isIntersection(); // false
$type->isBuiltin();      // true

However, simple nullable types are also considered as single types:

$type = new Nette\Utils\ReflectionType('?Foo'); // or 'Foo|null'
$type->isSingle();       // true
$type->isUnion();        // true
$type->isIntersection(); // false
$type->isBuiltin();      // false

For single types, you can query the type name or the class name if the type is a class:

$type = new Nette\Utils\ReflectionType('string|null');
echo $type;                       // '?string'
echo $type->getSingleName();      // 'string'
echo $type->getSingleClassName(); // null

$type = new Nette\Utils\ReflectionType('?Foo');
echo $type;                       // '?Foo'
echo $type->getSingleName();      // 'Foo'
echo $type->getSingleClassName(); // 'Foo'

$type = new Nette\Utils\ReflectionType('Foo|Bar');
echo $type;                       // 'Foo|Bar'
echo $type->getSingleName();      // null
echo $type->getSingleClassName(); // null

The getNames() method returns the array of subtypes that make up the compound type as strings. The getTypes() method returns them as ReflectionType objects:

$type = new Nette\Utils\ReflectionType('string|null');
$type->isUnion();   // true
$type->getNames();  // ['string', 'null']
$type->getTypes();  // [new ReflectionType('string'), new ReflectionType('null')]

$type = new Nette\Utils\ReflectionType('Foo&Bar');
$type->isIntersection(); // true
$type->getNames();  // ['Foo', 'Bar']
$type->getTypes();  // [new ReflectionType('Foo'), new ReflectionType('Bar')]

The allows() method checks for type compatibility, for example if a value of a certain type could be passed as a parameter:

$type = new Nette\Utils\ReflectionType('string|null');
$type->allows('string'); // true
$type->allows('null');   // true
$type->allows('Foo');    // false

$type = new Nette\Utils\ReflectionType('mixed');
$type->allows('null');   // true

Related blog posts