5 Constant Lists That Give Context to your Integers and Strings

Native enums is a new PHP 8.1 feature. This feature brings more focus on what enums are and where we can use them.

Not on PHP 8.1 yet? Don't worry, before that, enums were emulated by public constants. Some constant lists are already part of your favorite framework, ORM, or utils package for years.

Today we look at 5 constant lists that you can use today to replace that int or string and give it a context.

Why even Bother with Enums?

if ($duration < 14400) {
   return 'today';
}

return 'later';

We might ask, why even bother using some constants? Everybody knows 14400 is the number of minutes in a day or is it seconds... or is it 1440?

-if ($duration < 14400) {
+if ($duration < self::DAY_IN_MINUTES) {
    return 'today';
 }

 return 'later;

We are using constant to remove these questions. Somebody already did the calculation before me and was much better at the result. We avoid the crash of thinking fast and thinking slow at the same time - great book about how we think about others peoples' thinking.


There is more pratcical benefits in using constants over scalars:



I've shared one such constant on Twitter, and it got much more traction and responses than I expected:

I've picked the best and least known constant lists in this post.


Ready for some hot constant code? Let's go:

1. HTTP Response Codes

After calling outside API service, we have to verify the result or the reason why it failed. Codes like 404 and 200 are mainstream, but what about the less known ones?

Can you tell me in 2 seconds what is the condition checking here?

$response = $this->callExternalApi(...);
if ($response->getCode() === 403) {
    // what happened?
}

We know something is wrong... maybe something was not found? Maybe the URL is outdated?

Maybe is not good enough, and maybe, we can simply read it:

+use Symfony\Component\HttpFoundation\Response;

 $response = $this->callExternalApi(...);
-if ($response->getCode() === 403) {
+if ($response->getCode() === Response::HTTP_FORBIDDEN) {
     // what happened?
 }

The constant also differentiates HTTP response value, Calgary telephone code and an integer id in a test.

Where is the List?

2. Request Methods

Where is a response, there must be a request first:

$response = $this->callExternalApi(..., 'get');

We've all debugged an external API. If we're lucky, it has documentation. If we're not lucky, the documentation is outdated and the endpoint silently fails (I look at you Twitter, Meetup.com...).

A few hours later, we try the constant out of despair:

+use Symfony\Component\HttpFoundation\Request;

-$response = $this->callExternalApi(..., 'get');
+$response = $this->callExternalApi(..., Request::METHOD_GET);

And it works!


Why? Some API packages can mitigate the lowercased "get" to correct "GET", but some can not. We can mitigate bugs like these by using a standardized constant that uses the correct version.


Where is the List?


P.S. Be creative... where else do we use "GET" or "POST" methods?

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;

final class PostController extends AbstractController
{
    #[Route(path: '/blog/{slug}', methods: [Request::METHOD_GET])]
    public function __invoke(string $slug): Response
    {
    }
}

3. An hour, a Month, or a Year?

During my 19-years PHP developer career, I've used the calculator countless times just to produce correct time numbers like 8640... ehm, 86400.


The moment I saw the following constant list first time, it blew both my mind and calculator away:

+use Nette\Utils\DateTime;

-if ($durationInSeconds < 86400) {
+if ($durationInSeconds < DateTime::DAY) {
     return 'less than a day';
 }

 return 'keep waiting';

The DAY_IN_SECONDS name would be better, but at least the constant has a comment to clarify this.


These constants come very handily with header expiration or time to leave (TTLs) in cache:

use Nette\Utils\DateTime;

$httpResponse->setHeader('Access-Control-Max-Age', 12 * DateTime::HOUR);

Where is the List?

4. Accurate Event Subscribers

There are probably dozens of events in the Symfony event system we can hook into. We already use subscribers over listeners and the getSubscribedEvents() method:

use Symfony\Component\EventDispatcher\EventSubscriberInterface;

final class SomeEventSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            'kernel.controller' => ['onKernelController'],
        ];
    }
}

Instead of strings for event names, we can use Symfony constant list:

+use Symfony\Component\HttpKernel\KernelEvents;

 return [
-    'kernel.controller' => ['onKernelController'],
+    KernelEvents::CONTROLLER => ['onKernelController'],
 ];

Thanks to this constant, we open previously hidden knowledge = where is the event invoked and what subscribers use the event:

Where are the Lists?

5. Doctrine Column Types

Back in the PHP 7.4 days, we used to write Doctrine annotations in a weird string-like format, called comments, right above the property:

use Doctrine\ORM\Mapping as ORM;

class Post
{
    /**
     * @ORM\Column(type="int")
     */
    private $wordCount;
}

Based on the type= part, the Doctrine was able to with database column type. Now I'm not sure about my memory, was it "int" or "integer"? We use "int" everywhere, even in native PHP type declarations, so that should be correct, right?


Well, time flew by, and now we can use PHP 8.0-native attributes:

use Doctrine\ORM\Mapping as ORM;

class Post
{
    #[ORM\Column(type: "int")]
    private $wordCount;
}

The syntax changed, PHPStan and Rector can work with these better, and PhpStorm autocomplete more reliable. But my memory issue remains - was it "int" or "integer"? Is there some solution for my memory leak?

Fortunately, there is:

 use Doctrine\ORM\Mapping as ORM;
+use Doctrine\DBAL\Types\Types;

 class Post
 {
-     #[ORM\Column(type: "int")]
+     #[ORM\Column(type: Types::INTEGER)]
     private $wordCount;
 }

Now we can state the intention - define a column to work with integer numbers, and the knowledge is safely delegated to the tool itself - a constant it uses.

Where is the List?

Thanks to Alexander Schranz for mentioning.


Who do we Write Code For?

Now we know what constant enum-like lists we can use, but I'd like to return to why we use them. We all know that 200 is success code, and 301 is a permanent redirect... but do we know how many bits are in 1024 bytes?

"We don't write the code for use at present.
We write for future fellow developers we'll never meet."

If we pay more attention to writing standardized code now, future developers will thank us for making their lives easier. Maybe we will also thanks ourselves ;).

Honorable Mentions

Have I missed a constant list that you use daily and makes your life easier? Please share in the comments or on Twitter to put it here too.


Happy coding!




Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors. I'd really appreciate it!