#development #php

PHP's reflection capabilities are powerful tools that allow you to inspect and manipulate class information at runtime. One handy use case is fetching class constants dynamically. However, if you're working with inheritance, you may want to filter out constants that come from a parent class and focus only on those declared in the child class.

In this post, we'll go through how to achieve this using PHP's ReflectionClass.

Why use Reflection to get constants?

While it's easy to declare constants in PHP, there are situations where you need to fetch all constants of a class dynamically. For instance, when working with a list of predefined privileges or settings (constants), you might want to get them in an array for processing or validation.

Here's a simple example of a class with constants:

1class Setting extends Model
2{
3    public const MANAGE_USERS = 'manage.users';
4    public const MANAGE_TEAMS = 'manage.teams';
5}

In the above Setting class, we define several constants related to various system settings. Using reflection, we can fetch these constants dynamically at runtime and return them as an associative array.

Using Reflection to get class constants

PHP's ReflectionClass provides an easy way to retrieve class information, including constants. Here's how to create a method to fetch all constants of a class:

1public static function getConstants(): array
2{
3    $reflection = new \ReflectionClass(self::class);
4    return $reflection->getConstants();
5}

How it works

  • ReflectionClass(self::class): Creates a reflection of the class where the method is called.
  • getConstants(): Returns an associative array where the keys are constant names and the values are the constant values.

You can use this method to get all constants like so:

1$constants = Setting::getConstants();
2print_r($constants);

This will output:

Array
(
    [MANAGE_USERS] => manage.users
    [MANAGE_TEAMS] => manage.teams
    [CREATED_AT] => created_at
    [UPDATED_AT] => updated_at
)

As you might notice, the constants also include all the ones inherited from the parent class.

Handling inherited constants

If the class extends a base class (like Model in our example), some constants might be inherited from the parent class. Often, you'll want to filter these out and only work with constants declared in the current class. This can be done by comparing the constants of the child class with those of the parent class.

Filtering Out Inherited Constants

Here's the updated method that filters out constants inherited from the parent class:

 1public static function getConstants(): array
 2{
 3    $reflection = new \ReflectionClass(self::class);
 4    $parentClass = $reflection->getParentClass();
 5
 6    // Get constants of the current class
 7    $childConstants = $reflection->getConstants();
 8
 9    // Get constants of the parent class, if any
10    $parentConstants = $parentClass !== false ? $parentClass->getConstants() : [];
11
12    // Filter out constants inherited from the parent class
13    return array_diff_assoc($childConstants, $parentConstants);
14}

Explanation

  • Reflection of self::class: This fetches the constants declared in the Setting class.
  • getParentClass(): This checks if the class has a parent and gets the parent class reflection object.
  • array_diff_assoc(): Compares the constants of the child class with those of the parent class and removes any that are the same (i.e., inherited).

Now, when you call Setting::getConstants(), it will return only the constants declared in the Setting class, excluding those inherited from Model:

1$constants = Setting::getConstants();
2print_r($constants);

This will give you:

Array
(
    [MANAGE_USERS] => manage.users
    [MANAGE_TEAMS] => manage.teams
)

Conclusion

Using PHP's reflection API, you can easily extract class constants and filter out inherited ones. This can be particularly useful when working with large codebases where constants are defined across multiple classes. By reflecting on the class and filtering with array_diff_assoc(), you ensure that you only get the constants you need from the child class.

This approach keeps your code dynamic and flexible, allowing you to handle constants without hardcoding any logic that depends on inheritance structures.