Posts

Working With Cookies in PHP

A core functionality of any modern web application is keeping track of our users between web requests. We could add a unique identifier to every URL but this makes our URLs hard to generate, hard to read, and hard to share. Thankfully, every modern browser supports cookies to keep track of our users between requests.

In this article, we’ll discuss how to use cookies in our PHP applications.

A cookie is a small file (maximum of 4KB) that allows us as developers to store information on our user’s computer. This information will then be sent back to our web server whenever the user makes a request. A cookie is generally used to keep track of information such as a unique identifier of the person requesting the resource so we can build the page specifically tailored to them.

Cookies have a bad rap because they’re used to track users across multiple sites for advertisers which is why every site on the internet needs to have a banner telling its users that it uses cookies. Those types of cookies are referred to as 3rd-party cookies because they’re being requested from a 3rd-party server, not the user or web server directly involved in the request.

We should not store sensitive data in cookies since it could be potentially manipulated by a malicious user. To store sensitive data securely we should use sessions instead. We’ll discuss sessions in our next article.

Cookies are transmitted back to the server in the HTTP headers. Using HTTP will encrypt the cookie data but HTTP won’t.

Cookies in PHP

PHP has built-in support for cookies all the way back to version 4.0. It does this with several helper functions and a global variable.

We must set our cookies before we start outputting the HTML of our page. This is because cookies are set in the header section of the HTTP request. PHP’s header() function has the same limitation.

PHP’s cookie support is essentially a key-value store. We use the setcookie() function to set data.

setcookie(
    string $name,
    string $value = "",
    int $expires_or_options = 0,
    string $path = "",
    string $domain = "",
    bool $secure = false,
    bool $httponly = false
): bool

There are an annoyingly large number of optional parameters to the setcookie() function. All of the options except for the name are optional.

The most important parameters are the name, which is what we’re calling the cookie, and the value, which is what the value is.

$value = 'our value';
setcookie("OurCookie", $value);

We can also set the expires parameter which allows us to say how long the client computer should keep the cookie. 0 is the default which means it’s valid until the browser is closed. Otherwise, we’ll send a UNIX timestamp for when the cookie expires. For example, we might want to keep the cookie for 30 days.

setcookie("OurCookie", $value, time() + 60 * 60 * 24 * 30);

The path parameter specifies where the cookie is valid on the domain. As an example of that, we might have one domain supporting multiple applications. There might be a finance application in the “/finance” directory and the sales application in the “/sales” directory.

We can use this parameter to allow us to say this cookie is only valid in the “/finance” directory or only valid in the “/sales” directory.

setcookie("OurCookie", $value, time() + 3600, "/finance/");
setcookie("OurCookie", $value, time() + 3600, "/sales/");

The next parameter is the domain, which allows us to set the domain for which the cookie is valid. We can set it to test.com which will allow for test.com and all of its subdomains. We can set it to finance.test.com and so it will work for finance.test.com but it won’t work for test.com and it won’t work for sales.test.com.

The last two parameters are the secure and httponly only parameters, these allow us to determine whether the cookie should be sent only over HTTPS or only over HTTP connections. By default, it’s sent over both.

To read cookie data we use the $_COOKIE global variable. It’s an associative array where the key is the name that we used in setcookie() and because it’s already an array we can use the PHP array functions that we already know.

For example the isset() function

if(!isset($_COOKIE["OurCookie"])) {
    echo "Cookie named 'OurCookie' is not set!";
} else {
    echo "Cookie 'OurCookie' is set!<br>";
    echo "Value is: " . $_COOKIE["OurCookie"];
}

To modify a cookie we call setcookie() again on the same name.

$value = 'our new value';
setcookie("OurCookie", $value);

To delete a cookie, use the setcookie() function with an expiration date in the past:

setcookie("OurCookie", $value, time() - 60);

The Downsides

There are some downsides to cookies. The first is that they’re not always secure and they can be tampered with. A user could put data in there that we don’t want and they could send it to our server. We’re limited to 4 KB which might seem like a lot but it does have its limitations and we have to transmit the whole cookie with every request which can add up.

What You Need To Know

  • Cookies allow us to store information on user’s computer
  • Less than 4KB
  • PHP 4.0 and greater have supported
  • setcookie($name, $value)
  • $_COOKIE global varibles

Using Precommit Hooks For Static Code Analysis

In our last several articles, we’ve discussed how to use the PHP_CodeSniffer library to verify our code is following an agreed-upon coding standard and how to run the phpcs extension in VSCode to see where we’re not following the code as we type. We’ll eventually discuss how to move these checks to a Continuous Integration server but wouldn’t it be great if we could make sure our code was passing these checks before we push them anywhere?

In this article we’ll discuss how to use Git’s pre-commit hooks to run our static code analysis tools on just the set of files we’ve modified.

Read More

Fixing Long Functions With The Extract Function Refactoring

One of our core tenets of development is that code is read more than it’s written. To that end, we must make our code as easy to read and understand as possible. No one writes perfect code on the first try so it’s important that we continually refine our code so it’s easy for the next developer to read even if we’re the next developer.

In this article, we’re going to discuss what the extract function refactor is, what code smells are an indication to use it, and then work through an example in PHP.

Code Smell

We should use the extract function refactoring when we have code that’s duplicated in multiple places or when we can isolate a portion of a larger method to make multiple methods.

Duplicate code is less than ideal in our codebase because this duplication causes us to maintain the same logic twice and it’s easy for us to not update all the duplicate code which can cause bugs to enter our codebase.

“Large methods” can be hard to define. Some people have a hard limit for their teams while others say it needs to fit on a single screen. The logic of what can fit inside a single screen is a fun concept now that some people use their monitors in a vertical orientation.

A good rule to use is that a function should be as small as possible while still being easy to read. Small functions are also easier to test and debug as they generally only have a few paths.

What Is the Extract Function Code Refactoring?

In this technique, we take a section of code and make it a new function.

To perform this refactoring we’ll:

  1. Copy the section of code we’re extracting and then paste it into a new method
  2. Look for any local variables and add them as parameters to the method
  3. Replace the extracted code with a call to the newly created method
  4. Run our tests and make sure they all pass
  5. Look for places where we can use the newly extracted method

Example

Let’s work through an example. Our codebase contains the following function.

public function rebuildEstimatesBasedOnIncompleteTasks(): void
{
    // get the incomplete tasks assigned to this project
    $tasks = Task::where('project_id', $this->id)
        ->whereNull('end_date')
        ->get();

    $total = 0;
    foreach ($tasks as $task) {
        if ($task->time_estimate > 1) {
            $total += $task->time_estimate;
        }

        if ($task->time_estimate === null) {
            $total += 2;
        }
    }

    $this->estimated_date = now()->modify("+{$total} days");
    $this->save();
}

Because we used an intention revealing name of rebuildEstimatesBasedOnIncompleteTasks we can tell that we’re going to be rebuilding estimates based on our incomplete tasks but it’s hard to quickly parse exactly which part of the function does what.

Let’s extract a new function that calculates the total of a set of tasks.

  1. Copy the section of code we’re extracting and then paste it into a new method
public function rebuildEstimatesBasedOnIncompleteTasks(): void
{
    // get the incomplete tasks assigned to this project
    $tasks = Task::where('project_id', $this->id)
        ->whereNull('end_date')
        ->get();

    $total = 0;
    foreach ($tasks as $task) {
        if ($task->time_estimate > 1) {
            $total += $task->time_estimate;
        }

        if ($task->time_estimate === null) {
            $total += 2;
        }
    }

    $this->estimated_date = now()->modify("+{$total} days");
    $this->save();
}

public function calculateEstimatedHoursForTasks(): int
{
    $total = 0;
    foreach ($tasks as $task) {
        if ($task->time_estimate > 1) {
            $total += $task->time_estimate;
        }

        if ($task->time_estimate === null) {
            $total += 2;
        }
    }

    return $total;
}
  1. Look for any local variables and add them as parameters to the method
public function calculateEstimatedHoursForTasks(Collection $tasks): int
{
    $total = 0;
    foreach ($tasks as $task) {
        if ($task->time_estimate > 1) {
            $total += $task->time_estimate;
        }

        if ($task->time_estimate === null) {
            $total += 2;
        }
    }

    return $total;
}
  1. Replace the extracted code with a call to the newly created method
public function rebuildEstimatesBasedOnIncompleteTasks(): void
{
    // get the incomplete tasks assigned to this project
    $tasks = Task::where('project_id', $this->id)
        ->whereNull('end_date')
        ->get();

    $total = $this->calculateEstimatedHoursForTasks();

    $this->estimated_date = now()->modify("+{$total} days");
    $this->save();
}
  1. Run our tests and make sure they all pass

We’ll run our total test suite.

  1. Look for places where we can use the newly extracted method

In this case, we don’t have anywhere that we can reuse this new method but it could come in handy in the future.

What You Need To Know

  • Extract Function Refactor allows us to extra code from one function into a new one
  • This reduces duplication and improves readability

Creating Your Own Standard for PHP_CodeSniffer

In “The PSR Coding Standards” we discussed how we have three options when picking a coding standard.

  1. We can create a coding standard from scratch.
  2. We can borrow a coding standard that already exists and use it as is.
  3. We can borrow a coding standard that exists and then add or remove rules that don’t fit our team’s environment.

In this article, we’re going to discuss how we can implement the third option by creating our own standard for phpcs using the PSR12 standard as a starting point. If you missed either our article about “The PSR Coding Standards” or “PHP_CodeSniffer” you might want to read them first.

Read More

What is Code Refactoring?

One of our core tenets of development is that code is read more than it’s written. To that end, we must make our code as easy to read and understand as possible. No one writes perfect code on the first try so it’s important that we continually refine our code so it’s easy for the next developer to read even if we’re the next developer.

In this article, we’ll discuss what refactoring is, how to make it dead simple to do, and discuss the next set of articles in this series.

Read More

PHP_CodeSniffer in VSCode Using the phpcs Extension

In our previous article, we discussed how to use the PHP_CodeSniffer scripts from the command line to verify our code matches the standards we’ve created. The huge downside to that process is that we’re constantly having to switch between our editor and our terminal to check our code. It’s enough to make us want to pull out our hair.

In this article, we’ll discuss how to use the phpcs extension for Visual Studio Code to check our code as we write it.

Read More

PHP_CodeSniffer

In our previous article, we discussed why we need a coding standard and why we should be using the PSR-2 or PSR-12 coding standards. In this article, we’re going to discuss a tool we can use to verify that our code is using that standard and correct it if it’s not.

Read More

Grandfather-father-son Backup

Happy World Backup Day!. To celebrate we’re doing a quick article to discuss a topic important to backups the Grandfather-father-son backup.

Read More

The PSR Coding Standards

There are as many ways of formatting our code as there are developers who write it. When we’re working on a team there needs to be a common way to format the code to maximize team cohesion.

By having a coding standard we can define exactly how we expect the code to be formatted and make it easier for everyone to read. In this article we’ll discuss why we need a coding standard, what should be included, and why we should start by looking at the PSR family of standards.

Read More

How To Work With Enumerations in Laravel Framework 9

Laravel 9 was released on February 8th, 2022 and it came with some nice new features that we want to highlight.

In this article, we’ll discuss how to work with enumerations in Laravel Framework 9+

Read More
RSS

Join Our Mailing List!

View previous campaigns.

Top Posts

  1. Working With Soft Deletes in Laravel (By Example)
  2. Fixing CMake was unable to find a build program corresponding to "Unix Makefiles"
  3. Upgrading to Laravel 8.x
  4. Get The Count of the Number of Users in an AD Group
  5. Multiple Vagrant VMs in One Vagrantfile
  6. Fixing the "this is larger than GitHub's recommended maximum file size of 50.00 MB" error
  7. Changing the Directory Vagrant Stores the VMs In
  8. Accepting Android SDK Licenses From The OSX Command Line
  9. Fixing the 'Target class [config] does not exist' Error
  10. Using Rectangle to Manage MacOS Windows

subscribe via RSS

All content copyright This Programming Thing 2012 - 2021
Blogging about PHP, MySQL, Zend Framework, MySQL, Server Administration and Programming in general