Use PHP Class constants
📚 What is a constant?
A constant is an “identifier” for a value that cannot be altered by the program during its execution.
// index.php
define(“MAXSIZE”, 100);
// main.php
echo MAXSIZE; // output 100
echo constant(“MAXSIZE”); // same thing as the previous line
In this example, we store the value100
in a constant MAXSIZE
, from the definition and the example we understand that constants can be used to store “frozen” values. Anytime you need to deal with a value THAT cannot be updated by your program you can use a constant.
📒 Class Constants
It is also possible to define constant on a per-class basis remaining the same and unchangeable, by default class constant are public. You can access to the class constant from the FQCN (full qualified classname), the class itself or a instance of the object.
ℹ️ (constants are string and they can be lowercase but by convention, we use uppercase to make them identifiable)
👀👇
💡Public constants are very powerful and convenient, you can refer to them anywhere in the codebase, and since these are strings they make the code more readable, and in addition, they are defined once so it’s easier to avoid typos.
🔧 Suppose you have an application like medium, there is a feature that marks an article as published provided it is not deleted. In my example, I have a model Article
and a handler PublishArticleHandler
responsible to mark an Article
as published.
❓Did you catch the typo?
Read it again more slowly…
👀 Not yet?
Did you see line 22?
if($article->getStatus() === 'delted') // "delted" instead of "deleted"
🐛Without tests, you cannot spot this error and this kind of typo is very common, who didn’t waste 2 hours of debugging for a simple typo? 🤯
🔐 Using a class constant save time by making the code easier to read, see an improved version:
(📔 A better improvement is to remove setters, move the logic inside the model and use enums 🌟 but it’s not the purpose of this article)
🌋 Public class constants are designed to be reusable, most of the time it’s better to use a constant than a hardcoded value, think about readability, comprehension, autocompletion for codebase navigation or update, typos error prevention…
🌟 From my point of view should use class constants everywhere you refer to the value. If you use hardcoded values there is a good chance that you will forget to modify them the day you change the value and even if you think about it you should modify them one by one whereas if you use a constant the modification via your IDE will be effective everywhere.
Let me add some examples:
$qb->select(‘a’)
->from(‘Article’, ‘a’)
->where(‘a.status = ‘:status’)
❌->setParameter(‘status’,'deleted');
can be replaced by:
✅ ->setParameter('status', Article::DELETED);
public function isDeleted(): bool
{
❌return $this->status === 'deleted';
}
can be replaced by:
✅ return $this->status === Article::DELETED;
⚠️ In my humble opinion the only case when you should not refer to a constant is in your tests, having the value hardcoded in tests is safer and makes the test closer to the use case.
The other use case where I think it’s not necessary to use class constant is when you deal with HTTP status codes, a common thing that I saw and I did (I was wrong 😅) is to use Symfony\Component\HttpFoundation\Response
constants like: “HTTP_OK”, “HTTP_BAD_REQUEST”, “HTTP_INTERNAL_SERVER_ERROR”…
🤔 Change my mind but using these constants instead of “200”, “400”, “500” add noise, confusion and makes your code coupled to an external library for a little benefit.
Another use case for use constant is to define non-sensitive configuration parameters that never or rarely change, let me show you an example in a Symfony application.
💡Define the constant
final class Email
{
public const CONTACT = 'contact@smaine.me'
// other stuff
}
🎉 Then use it in your configuration
# config/services.yamlparameters:
contact_email: !php/const Email::CONTACT_defaults:
bind:
$contactEmail: !php/const Email::CONTACT
# OR
$contactEmail: '%contact_email%' 😅
🎁 You can also use constant in twig
# footer.hmtl.twig
<a href="mailto:{{ constant(Email::CONTACT) }}">Email Us</a>
🔥 and in annotation
final class Article
{
public const DELETED = 'deleted';
public const PUBLISHED = 'published';
public const DRAFT = 'draft';public const ALL = [
self::DELETED,
self::PUBLISHED,
self::DRAFT,
];use Symfony\Component\Validator\Constraints as Assert;final class ArticleDto
{
/**
* @Assert\Choice(choices=Article::ALL, message="Invalid status")
*/
private $status;
🙂 In conclusion, I would recommand to use constant if the value never or rarely change except in your tests.
Thanks for reading, feel free to reach me on twitter and linkedin 👋