
Probably, for many developers the idea of this article will be obvious and it is fine.
Over the years in the development of applications in PHP, sometimes I come across methods, even from popular small libraries, which sometimes return strange or unexpected data types.
Sometimes on code review, I can see methods that return anything but the right data structure or type.
This is an example of how not to do this
The method returns object or boolean, for what?
<?php
//...
private function getObject(int $id)
{
$object = Model::find($id);
if (empty($object)) {
return false;
}
return $object;
}
Why does the method return int if we expect a string?
<?php
//...
private function getSomeString(string $string)
{
if (empty($string)) {
return 0;
}
//some manipulations with the string $myString
return $myString;
}
etc.
A method should return the only type that is expected from the method or nothing if it is void.
The method should not return different data types when different conditions are triggered.
Key thing is that if the method returns different types it can break executable code that expects specific data type from that method.
For simple data types, everything is clear
If we expect from the method certain data type, so the method should return expected type:
<?php
//...
private function isAdmin(): boolean
{
if ($this->roleId === User::ADMIN_ROLE) {
return true;
}
return false;
}
<?php
//...
private function getTitle(int $id): string
{
$post = Post::find($id);
if (empty($post)) {
return '';
}
return $post->title;
}
For more complex types, the method has to stick to one data type structure.
If the method should return object, so return object or null if it is not found
<?php
//...
private function getPost(int $id): ?Model
{
$post = Post::find($id);
if (empty($post)) {
return null;
}
return $post;
}
But
For array or collection types the circumstances are a little different.
Method which returns such type should return array or collection in any case
So, if we expect from the method array, we must get array even the array is empty:
<?php
//If today is monday returns discount list
private function getMondayDiscounts(): array
{
$discounts = [];
if (date('w') === '1') {
$discounts = Discount::getDiscounts();
}
return $discounts;
}
So, if we expect from the method collection type, we must get a collection.
The method returns collection with user or empty collection if that users not found:
<?php
//Get all users where role is admin
private function getUsers(): array
{
return User::where('roleId', User::ADMIN_ROLE)->get();
}
Scenario for Exceptions.
The Exception should be thrown if the method found an error during the task execution, not to be confused with the lack of data
In our case below Exception is triggered if smtp key is not exist in config file, but if the method just returns null although in reality key is not exist this is misleading to the developer who will use method.
<?php
//...
private function smtpGetConnection(): SenderService
{
$smtpKey = Config::get('smtpKey');
if (empty($smtpKey)) {
throw new Exception\SmtpConnection('Key connection is not found!');
}
return new SenderService($smtpKey);
}
In conclusion, practices described above are not the last truth, there are situations when this approach for return types is not suitable.
But in general this approach makes the code cleaner and more logical, that brings less errors or bugs.
Thank you!