139

I would like to be able to do something like this:

class ThingIDs
{
    const Something = 1;
    const AnotherThing = 2;
}

$thing = 'Something';
$id = ThingIDs::$thing;

This doesn't work. Is there a straightforward way of doing something equivalent? Note that I'm stuck with the class; it's in a library I can't rewrite. I'm writing code that takes arguments on the command line, and I would really like it to take symbolic names instead of id numbers.

Ben
  • 68,572
  • 20
  • 126
  • 174

8 Answers8

229

Use the constant() function:

$id = constant("ThingIDs::$thing");
AymDev
  • 6,626
  • 4
  • 29
  • 52
Dan Simon
  • 12,891
  • 3
  • 49
  • 55
  • 4
    Side note: if you want to first check whether or not the constant is defined, it's `defined("ThingIDs::$thing");` – Parris Varney Nov 12 '14 at 14:33
  • 3
    or $id = constant("self::$thing"); – Line Mar 23 '15 at 09:27
  • 7
    Similar to this, ```$id = constant(sprintf('%s::%s', ThingIDs::class, $thing));``` – David Baucum Jul 05 '16 at 19:37
  • 5
    @DavidBaucum why would you wanna over-complicate this? The request is quite simple and involves no outside, user manipulable input. Aside from that, you're calling another function because, I guess, you don't like the string to be concatenated with delimiters? – ReSpawN Nov 03 '16 at 13:26
  • 6
    Depending on your use-case, my solution is less complicated. Specifically if you are using PSR-4 autoloading, then it can be ugly in your code to have the FQDN spelled out everywhere. By use ```use``` at the top of the file and then using the ```::class``` method to magically get the FQDN improves readability. – David Baucum Nov 08 '16 at 14:11
31

Use Reflection

$r = new ReflectionClass('ThingIDs');
$id = $r->getConstant($thing);
Phil
  • 157,677
  • 23
  • 242
  • 245
  • 3
    Reflections really provide a lot of insight into classes, methods and more and it seems a lot of people are afraid to take that step to understand them. Great answer. – Mike Mackintosh Dec 05 '12 at 16:56
  • 2
    @mikemackintosh I've taken steps to understand them, but haven't seen much in terms of performance impact vs the accepted answer. THAT is something I'm interested in knowing. Instantiating a new class seems like it'd have a bigger performance hit than simply calling a constant statically. What are your thoughts on that? – dudewad Sep 23 '13 at 02:27
17

If you are using namespaces, you should include the namespace with the class.

echo constant('My\Application\ThingClass::ThingConstant'); 
Jordi Kroon
  • 2,607
  • 3
  • 31
  • 55
5

Helper function

You can use a function like this:

function class_constant($class, $constant)
{
    if ( ! is_string($class)) {
        $class = get_class($class);
    }

    return constant($class . '::' . $constant);
}

It takes two arguments:

  • Class name or object instance
  • Class constant name

If an object instance is passed, its class name is inferred. If you use PHP 7, you can use ::class to pass appropriate class name without having to think about namespaces.

Examples

class MyClass
{
    const MY_CONSTANT = 'value';
}

class_constant('MyClass', 'MY_CONSTANT'); # 'value'
class_constant(MyClass::class, 'MY_CONSTANT'); # 'value' (PHP 7 only)

$myInstance = new MyClass;
class_constant($myInstance, 'MY_CONSTANT'); # 'value'
Glutexo
  • 547
  • 6
  • 13
3
<?php

class Dude {
    const TEST = 'howdy';
}

function symbol_to_value($symbol, $class){
    $refl = new ReflectionClass($class);
    $enum = $refl->getConstants();
    return isset($enum[$symbol])?$enum[$symbol]:false;
}

// print 'howdy'
echo symbol_to_value('TEST', 'Dude');
Josh
  • 12,602
  • 2
  • 41
  • 47
0

If you have a reference to the class itself then you can do the following:

if (defined(get_class($course). '::COURSES_PER_INSTANCE')) {
   // class constant is defined
}
crmpicco
  • 16,605
  • 26
  • 134
  • 210
0

My problem was similiar to this subject. When you have the object, but not the class name, you could use:

$class_name = get_class($class_object);
$class_const = 'My_Constant';

$constant_value = constant($class_name.'::'.$class_const);
Andrei
  • 160
  • 2
  • 14
0

I know I'm a bit late, but I hope this can help anyway.

Based on Phil's answer, I created a default enumerator class that can be extended.

class DefaultEnum
{
    static public function getConstantText(string $constant)
    {
        try {
            // Get child class name that called this method
            $child_class = get_called_class();

            $reflection = new ReflectionClass($child_class);
            $const = $reflection->getConstant($constant);

            return $const;
        } catch (\ReflectionException $e) {
            // ...
        }
    }
}

class CustomEnum extends DefaultEnum
{
    const something = 'abcd';
    const something2 = 'ABCD';
}

You can call this method like this

CustomEnum::getConstantText('something');

It will return 'abcd'.

The function get_called_class() is a function that returns the class name that called this method and it works specifically for static methods.

In this case $child_class value will be CustomEnum::class. ReflectionClass accepts strings and object as parameter.