DEV Community

loading...
Cover image for PHP Tips and Tricks

PHP Tips and Tricks

mychi_darko profile image Michael Darko ใƒป10 min read

Working with PHP has been one of the most fun experiences for me, working on both major and minor projects and learning something new on every journey.

I'll be sharing some little tricks that have helped me cut down a couple of lines of code and others that totally flipped my workflow like this:

""

Hit me up if you have any ideas, questions, comments... Without further ado, let's get into it.

Tip 1: (if and else)

You may have seen this before on tutorials or some other articles, but this is something really important I should mention, a bit in-depth. It's not wrong to use else and else if blocks in your code, in fact, they were made for use, however, in some cases, these blocks become redundant. Let's look at an example:

function output_gender(bool $user_is_male) {
    if ($user_is_male) {
        return "User is male";
    } else {
        return "User is female";
    }
}
Enter fullscreen mode Exit fullscreen mode

In this case the output_gender function returns a set output based on $user_is_male is true or false. When return is used in a function, any code below the return statement is totally ignored, so, if $user_is_male is true, then the else block will be ignored since a value is returned. With this concept, we can get rid of the else block like this:

function output_gender(bool $user_is_male) {
    if ($user_is_male) {
        return "User is male";
    }

    return "User is female";
}
Enter fullscreen mode Exit fullscreen mode

We know that the if statement won't run if the condition passed in is false. This means it will skip straight to the "User is female" part.

Tip 2: (if blocks: less vs more)

Tip 2 builds upon the tip we just looked at above but goes in a bit deeper. In an if/else or even using an example like tip 1, you might have conditions where one block, either the if or else, has less code than the other. It's better in such situations to handle the block with less code first. Let's look at a real example.

public function categoryWithPosts($category)
{
    $category = Category::find($category);

    if ($category) {
        $category->posts = $category->posts()->published()->get();
        return response(['data' => $category], 200);
    } else {
        return response(['error' => 'Category not found'], 404);
    }
}
Enter fullscreen mode Exit fullscreen mode

This code above checks for a post category and runs a condition based on whether the category is found or not. If we're to go with ONLY tip 1, we'll have code looking like this:

public function categoryWithPosts($category)
{
    $category = Category::find($category);

    if ($category) {
        $category->posts = $category->posts()->published()->get();
        // having any more code here would
        // bloat this part of the function
        return response(['data' => $category], 200);
    }

    return response(['error' => 'Category not found'], 404);
}
Enter fullscreen mode Exit fullscreen mode

This code is correct, however, you can clearly see that our major code is wrapped with {}, and pushed in further. If this code were significantly longer, it would be a pain to keep it all within the if block. Following tip 2, we can have this instead:

public function categoryWithPosts($category)
{
    $category = Category::find($category);

    if (!$category) {
        return response(['error' => 'Category not found'], 404);
    }

    $category->posts = $category->posts()->published()->get();
    // we can freely have more code here
    // without worrying about the code looking weird
    return response(['data' => $category], 200);
}
Enter fullscreen mode Exit fullscreen mode

Since the else block has less code, we use a negative statement with ! to make that code run first. So our if rather contains if not category, run code.... This gives us more room to handle our major code freely.

Tip 3: (verifying multiple strings)

Let's just say we want to find if a certain variable is or isn't one of many strings, we'd obviously have to write a bunch of conditional statements to verify this:

$item = "candy";

switch ($item) {
    case 'candy':
        return true;
    case 'toy':
        return true;
    default:
        return false;
}
// we're not adding break because we're using return

// or
if ($item == 'candy' || $item == 'toy') {
    return true;
}

return false;
Enter fullscreen mode Exit fullscreen mode

This code returns false if the item variable is neither candy nor toy. This is perfectly correct, however, this is very repetitive. Instead, we can check an array for the string we want to find:

if (in_array($item, ["candy", "toy"])) {
    return true;
}

return false;
Enter fullscreen mode Exit fullscreen mode

Even this can be shortened further because in_array returns a boolean.

return in_array($item, ["candy", "toy"]);
Enter fullscreen mode Exit fullscreen mode

We just shortened these lines to just a single line, clean right? How does this work? We have an array containing the strings we want to check for. Then we pass that into in_array. This creates a simple condition like:

if $item is inside the array holding "candy" and "toy", return true, else false
Enter fullscreen mode Exit fullscreen mode

You might be wondering, why not just return whether $item is candy or toy directly since that too is just one line, like this:

return ($item == 'candy' || $item == 'toy');
Enter fullscreen mode Exit fullscreen mode

This will give us the same result, however let's say we were checking 10 strings:

return ($letter == 'a' || $letter == 'b' || $letter == 'c' || $letter == 'd' ...);
Enter fullscreen mode Exit fullscreen mode

You can clearly see that this easily gets out of hand, compared to this:

return in_array($letter, ["a", "b", "c", "d", ...]);
Enter fullscreen mode Exit fullscreen mode

Note that the first parameter of in_array is the string we're actually checking

Tip 4: (??)

?? is probably the easiest way to create inline conditions without 2 parts. What do I mean? Let's look at an example, that would do all the explaining for me.

$data = [
    "a" => 1,
    "b" => 2,
    "c" => null,
];

return $data["c"] ? $data["c"] : "No data";
Enter fullscreen mode Exit fullscreen mode

The last line here checks if the key c in $data is truthy, if not it returns "No data".

We can rewrite the last line with ?? to look like this:

// ...
return $data["c"] ?? "No data";
Enter fullscreen mode Exit fullscreen mode

In this case ?? behaves like the || logical operator in other languages. A real-world example of this would look like this:

$user = getUserFromDb($user_id) ?? trigger_error("User id is invalid");

echo $user;
Enter fullscreen mode Exit fullscreen mode

getUserFromDb is to return a user from a database somewhere, however, if the user isn't found, instead of setting the user variable, we break the application with trigger_error. Without ?? we'd have to write this instead:

$user = getUserFromDb($user_id);

if (!$user) {
    trigger_error("User id is invalid");
}

echo $user;
Enter fullscreen mode Exit fullscreen mode

Tip 5: (Recursiveness over repetition)

I think this tip is pretty straightforward, try to use recursiveness rather than repeating yourself a lot. There are situations that'll make you repeat some code, that's fine, but if you find you're repeating the same code, just make it a method. Where does recursiveness come in? Let's look at an example: This is a method I wrote for my Leaf framework's request object, to return a particular field passed into the request.

/**
 * Returns request data
 *
 * This methods returns data passed into the request (request or form data).
 * This method returns get, post, put patch, delete or raw faw form data or NULL
 * if the data isn't found.
 *
 * @param string|array $params The parameter(s) to return
 * @param bool $safeData Sanitize output
 */
Enter fullscreen mode Exit fullscreen mode

This means that this method can take in either an array or string and based on the input, it would return a string or an array. The solution would be to check if the input was an array, loop over it to get the strings in the array then perform the data fetch on those strings, which would look like this.

public function get($params, bool $safeData = true)
{
    if (is_string($params)) return $this->body($safeData)[$params] ?? null;

    $data = [];
    foreach ($params as $param) {
        $data[$param] = $this->body($safeData)[$params] ?? null;
    }
    return $data;
}
Enter fullscreen mode Exit fullscreen mode

Here, you notice $this->body($safeData)[$params] ?? null is being repeated, not just that, but what if an array holding another array is passed in instead. Since this is a library, there's no telling what sorts of things users would pass in there, so I did this instead.

public function get($params, bool $safeData = true)
{
    if (is_string($params)) return $this->body($safeData)[$params] ?? null;

    $data = [];
    foreach ($params as $param) {
        $data[$param] = $this->get($param, $safeData); // I called the function again
    }
    return $data;
}
Enter fullscreen mode Exit fullscreen mode

This makes sure that until the looped value is a string, it won't attempt to fetch its data. A small trick compared to those above, but definitely useful. Note that this function is class scoped, hence the use of $this

Tip 6: (PHP + HTML)

This is for when you want to write PHP in your HTML or HTML in your PHP๐Ÿ˜…. We'd usually do something like:

<?php
foreach ($items as $item) {
    echo '
        <div class="product__card">
            <h3>{$item->name}</h3>
        </div>
    ';
}
?>
Enter fullscreen mode Exit fullscreen mode

Although this is fine, you can clearly see, we're outputting the HTML as a string. The bulkier the HTML, the more stressful it becomes to match tags and keep track of exactly what part of the HTML we're writing is. There's a neat solution for this.

<?php foreach ($items as $item): ?>
    <div class="product__card">
        <h3><?php echo $item->name; ?></h3>
    </div>
<?php endforeach; ?>
Enter fullscreen mode Exit fullscreen mode

You can clearly see how we're maintaining our HTML formatting and code alignment...and no, this is not a templating engine, this is just PHP making things simple for us. One major thing about PHP is how it allows the same thing to be done in many different ways. In this example above, we're using:

foreach (...):
// code
endforeach;

// also works with if
if (...):
// code
endif;

// also
if (...) #one line code

while():
// ...
endwhile;
Enter fullscreen mode Exit fullscreen mode

Tip 7: (Writing functional blocks)

Functional blocks can range from a large feature to a single lined wrapper, around a default PHP function, the point is just to create that functional block. This isn't just to avoid repetition, but also to speed up your workflow and make your code more readable.

You can write a simple method to create a redirect like this:

function redirectTo($route) {
    header("location: $route", true, 302);
}
Enter fullscreen mode Exit fullscreen mode

So instead of writing header("location: /home", true, 302) everytime, it makes more sense to write redirectTo("/home"). The same applies to 3rd party libraries, and long processes, writing a reusable block of code in an open way eg:

UserNotification::send($user_id, $notification);
Enter fullscreen mode Exit fullscreen mode

is obviously better than writing a bunch of lines every time you have to send a notification to a user. Another very small but very useful tip.

Tip 8: (Using Types)

Another straightforward one. This is one of the least used, but very powerful features available in PHP. This is a feature that can save you and other developers a whole lot of stress (if you work with a team).

Of course, you can write function descriptions like the example in tip 5 above, but it becomes quite a daunting task to write function descriptions for all your functions and variables in a large project.

Let's take a look at how types can save our lives later:

function getItem($item) {
    // $item is expected to be an array
    // for whatever reason
    return allItems()[$item[0]];
}
Enter fullscreen mode Exit fullscreen mode

If a different developer works on the project or even yourself after a few weeks, seeing the getItem method, the $item variable there is obviously expected to be a string, but the function was written to handle an array.

The dangerous thing here is that passing in a string won't break the app, it would still run perfectly. Why?

If "chair" is passed into the function, it will be evaluated to allItems()["c"], which will end up causing errors that will keep you up at 12am๐Ÿ˜…. This can easily be avoided like this:

function getItem(array $item) {
    return allItems()[$item[0]];
}
Enter fullscreen mode Exit fullscreen mode

This will make sure that whatever is passed in here is the type needed. You can read more from php.net

You can also use methods like is_string and is_array which we saw above like this:

function getItem($item) {
    if (!is_array($item)) throwErr("item should be array");

    return allItems()[$item[0]];
}
Enter fullscreen mode Exit fullscreen mode

Tip 9: (Frameworks/Libraries aren't evil)

I'll be real over here, open-source libraries cause problems! Sometimes the libraries we bring in cause more problems for us instead of helping us. This might sound like I'm totally trashing open source packages, I'm not, I also write open-source packages myself, so obviously not!

My point is that you should read more on packages you bring in, read their documentation, check their issues on GitHub, don't take unnecessary risks. One thing I'll advise, and that goes back to Tip 7, write wrappers for features around the packages you bring in. This will give you a bit more control and also make your code cleaner.

In regards to frameworks, you might have heard this before, but you should familiarize yourself with PHP first. PHP frameworks, no matter the language they were written in still use PHP's principles and style, so the first step is to obviously familiarize yourself with PHP.

Next would be to pick something you're comfortable with and stick to it. There are many choices out there:

  • Laravel: if you love magic, laravel does literally everything for you (unless you decide otherwise)
  • Slim: A rest API framework, has a sort of "bring your own" vibe
  • Leaf: That's what I wrote, inspired by Slim and Laravel, it gives you the magic you can control.

I only mentioned frameworks I actually use to avoid bias.

Tip 10: (Don't just code!)

Alright, this one is a bonus tip. It applies to not just PHP, but technically almost every language/framework you work with. What I mean by don't just code is relatively straightforward.

Let's say you want to write a method that sort of requests a payment from a user's account, jumping straight into coding out this feature may (or may not) end up getting you confused at some point, where you'll have to stop, scroll back up, check something from a file somewhere, or something similar.

What am I proposing? Here:

// in class scope
public function requestPayout()
{
    // parse token to get user id

    // fetch user from DB with id

    // check if the user is eligible for payouts

    // get user balance with user helper

    // check and throwErr for insufficient funds

    // ...
}
Enter fullscreen mode Exit fullscreen mode

The above just allows you to do all the required thinking before actually jumping in to write any features. It also in a way helps you cross-check what you're building since you'll end up listing out all your processes out first.

Thanks for reading

These are a few tips and tricks I've discovered on my PHP journey, some of these might work for you and others may not, feel free to choose whichever you're comfortable with and stick to those.

It's quite wrong to say these are good ways of doing stuff so use these only, as I mentioned before, PHP is the type of language that provides many different ways of doing the same thing, so if you have anything you'll like to share, a new tip, a faster way to do something I mentioned, something you don't agree with, just reach out to me.

Discussion (14)

pic
Editor guide
Collapse
phantas0s profile image
Matthieu Cneude

Oh. An article about PHP. I thought Tailwind / React / JS were eating them all.

I've been advocating the first tip for years. Now, I can't see why it's better anymore. The if else make the code clearer in fact: it's less easy to miss than the return statement.

I think it just pleases our... sense of beauty? Code formatting is 99% subjective IMHO.

Collapse
nicolus profile image
Nicolas Bailly

I keep going back and forth on the "return early" style.

The main advantage for me is that you get back 4 precious spaces at the beginning of every line in your "main" path. The disadvantage as you said is it's easier to see a return statement at the end of the function and miss the fact that it could have returned something else entirely.

At the end of the day, I guess if either of those is a real issue, it just means your method is too complicated and it's time to refactor by adding a new simpler method.

Collapse
leob profile image
leob

"Early return" FTW ;)

Collapse
nicolus profile image
Nicolas Bailly • Edited

no, this is not a templating engine, this is just PHP making things simple for us.

insert obligatory "But PHP is a templating engine" remark here. It hasn't been used like one in years, but originally it was a templating engine, which is why we can do this, and why to this day every php file has to start with <?php

Collapse
mychi_darko profile image
Michael Darko Author

๐Ÿ˜‚๐Ÿ˜‚๐Ÿ˜‚ Good one. However, PHP has moved on from that little extension it used to be.

Collapse
goodevilgenius profile image
Dan Jones • Edited

The first tip is a really important one, although, with that particular example, with something so simple, a ternary would be more appropriate.

function output_gender(bool $user_is_male) {
    return $user_is_male ? "User is male" : "User is female";
}
Enter fullscreen mode Exit fullscreen mode

Also, one more tip related to this example: careful with how you name stuff. This function is called output_gender, but it doesn't actually output anything. It simply returns a string indicating the gender. Only call something output, or print if it actually does that.

All in all, I would rewrite this function as:

function getGender(bool $userIsMale): string {
    return $userIsMale ? "male" : "female";
}
Enter fullscreen mode Exit fullscreen mode

I also prefer camelCase, but that's just a personal preference, and doesn't really affect readability or anything.

There's also a whole other discussion about whether it's appropriate to make assumptions about binary gender in your code. But that's not exactly the point of this discussion.

Collapse
mychi_darko profile image
Michael Darko Author

Thanks๐Ÿ˜Š I agree that ternary would be better for that example, but I was just trying to make a point. A little secret, I prefer camel case too๐Ÿคญ

Collapse
sznroe profile image
Sujan Rai

Loved It

Collapse
koas profile image
Koas

Very nice article! I didnโ€™t know about the endif, thanks!

Collapse
brnathanlima profile image
Nathanael Lima

Great tips. Somo of them I already use and the others I'll try to use for now on.

Collapse
torstendittmann profile image
Torsten Dittmann

Interesting read and great article ๐Ÿ‘

Collapse
mychi_darko profile image
Michael Darko Author

Thanks๐Ÿ˜Š

Collapse
abidkhairyak profile image
AbidKhairyAK

nice article! I think this is enough for my dinner haha...

Collapse
aalphaindia profile image
Pawan Pawar

Nice one!!