Search icon
Subscription
0
Cart icon
Close icon
You have no products in your basket yet
Arrow left icon
All Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Newsletters
Free Learning
Arrow right icon
PHP 8 Programming Tips, Tricks and Best Practices

You're reading from  PHP 8 Programming Tips, Tricks and Best Practices

Product type Book
Published in Aug 2021
Publisher Packt
ISBN-13 9781801071871
Pages 528 pages
Edition 1st Edition
Languages
Author (1):
Doug Bierer Doug Bierer
Profile icon Doug Bierer

Table of Contents (17) Chapters

Preface 1. Section 1: PHP 8 Tips
2. Chapter 1: Introducing New PHP 8 OOP Features 3. Chapter 2: Learning about PHP 8's Functional Additions 4. Chapter 3: Taking Advantage of Error-Handling Enhancements 5. Chapter 4: Making Direct C-Language Calls 6. Section 2: PHP 8 Tricks
7. Chapter 5: Discovering Potential OOP Backward-Compatibility Breaks 8. Chapter 6: Understanding PHP 8 Functional Differences 9. Chapter 7: Avoiding Traps When Using PHP 8 Extensions 10. Chapter 8: Learning about PHP 8's Deprecated or Removed Functionality 11. Section 3: PHP 8 Best Practices
12. Chapter 9: Mastering PHP 8 Best Practices 13. Chapter 10: Improving Performance 14. Chapter 11: Migrating Existing PHP Apps to PHP 8 15. Chapter 12: Creating PHP 8 Applications Using Asynchronous Programming 16. Other Books You May Enjoy

Exploring new data types

One thing any entry-level PHP developer learns is which data types PHP has available and how to use them. The basic data types include int (integer), float, bool (Boolean), and string. Complex data types include array and object. In addition, there are other data types such as NULL and resource. In this section, we discuss a few new data types introduced in PHP 8, including union types and mixed types.

Important note

It's extremely important not to confuse a data type with a data format. This section describes data types. A data format, on the other hand, would be a way of representing data used as part of a transmission or for storage. Examples of a data format would include XML, JavaScript Object Notation (JSON), and YAML Ain't Markup Language (YAML).

Union types

Unlike other data types such as int or string, it's important to note that there is no data type explicitly called union. Rather, when you see a reference to union types, what is meant is that PHP 8 introduces a new syntax that allows you to specify a union of types, instead of just one. Let's now have a look at the generic syntax for union types.

Union type syntax

The generic syntax for union types is as follows:

function ( type|type|type $var) {}

In place of type, you would supply any of the existing data types (for example, float or string). There are a few restrictions, however, which for the most part make complete sense. This table summarizes the more important restrictions:

Table 1.3 – Disallowed union types

Table 1.3 – Disallowed union types

As you can see from this list of exceptions, defining a union type is primarily a matter of common sense.

Tip

Best practice: When using union types, type coercion (the process whereby PHP converts a data type internally to satisfy the requirements of the function) can be an issue if strict type checking is not enforced. Accordingly, it's a best practice to add the following at the top of any file where union types are used: declare(strict_types=1);.

For more information, see the documentation reference here:

https://www.php.net/manual/en/language.types.declarations.php#language.types.declarations.strict

Union type example

For a simple illustration, let's return to the SingleChar class used as an example in this chapter. One of the methods is colorAlloc(). This method allocates a color from an image, leveraging the imagecolorallocate() function. It accepts as arguments integer values that represent red, green, and blue.

For the sake of argument, let's say that the first argument could actually be an array representing three values—one each for red, green, and blue. In this case, the argument type for the first value cannot be int otherwise, if an array were provided, an error would be thrown if strict type checking were to be turned on.

In earlier versions of PHP, the only solution would be to remove any type check from the first argument and to indicate that multiple types are accepted in the associated DocBlock. Here's how the method might appear in PHP 7:

/**
 * Allocates a color resource
 *
 * @param array|int $r
 * @param int $g
 * @param int $b]
 * @return int $color
 */
public function colorAlloc($r, $g = 0, $b = 0) {
    if (is_array($r)) {
        [$r, $g, $b] = $r;
    }
    return \imagecolorallocate($this->image, $r, $g, $b);
}

The only indication of the data type for the first parameter, $r, is the @param array|int $r DocBlock annotation and the fact that there is no data type hint associated with that argument. In PHP 8, taking advantage of union types, notice the difference here:

#[description("Allocates a color resource")]
#[param("int|array r")]
#[int("g")]
#[int("b")]
#[returns("int")]
public function colorAlloc(
    int|array $r, int $g = 0, int $b = 0) {
    if (is_array($r)) {
        [$r, $g, $b] = $r;
    }
    return \imagecolorallocate($this->image, $r, $g, $b);
}

In the preceding example, in addition to the presence of attribute that indicates the first argument can accept either an array or an int type, in the method signature itself, the int|array union type makes this choice clear.

mixed type

mixed is another new type introduced in PHP 8. Unlike a union type, mixed is an actual data type that represents the ultimate union of types. It's used to indicate that any and all data types are accepted. In a certain sense, PHP already has this facility: simply omit the data type altogether, and it's an implied mixed type!

Tip

You will see references to a mixed type in the PHP documentation. PHP 8 formalizes this representation by making it an actual data type.

Why use a mixed type?

Hold for a second—you might be thinking at this point: why bother using a mixed type at all? To put your mind at ease, this is an excellent question, and there is no compelling reason to use this type.

However, by using mixed in a function or method signature, you clearly signal your intention for the use of this parameter. If you were to simply leave the data type blank, other developers later using or reviewing your code might think that you forgot to add the type. At the very least, they will be uncertain of the nature of the untyped argument.

The effect of a mixed type on inheritance

As a mixed type represents the ultimate example of widening, it can be used to widen the data type definition when one class extends from another. Here is an example using a mixed type, illustrating this principle:

  1. First, we define the parent class with the more restrictive data type of object, as follows:
    // /repo/ch01/php8_mixed_type.php
    declare(strict_types=1);
    class High {
        const LOG_FILE = __DIR__ . '/../data/test.log';  
        protected static function logVar(object $var) {     
            $item = date('Y-m-d') . ':'
                  . var_export($var, TRUE);
            return error_log($item, 3, self::LOG_FILE);
        }
    }
  2. Next, we define a Low class that extends High, as follows:
    class Low extends High {
        public static function logVar(mixed $var) {
            $item = date('Y-m-d') . ':'
                . var_export($var, TRUE);
            return error_log($item, 3, self::LOG_FILE);
        }
    }

    Note in the Low class that the data type for the logVar()method has been widened into mixed.

  3. Finally, we create an instance of Low and execute it with test data. As you can see from the results shown in the following code snippet, everything works fine:
    if (file_exists(High::LOG_FILE)) unlink(High::LOG_FILE)
    $test = [
        'array' => range('A', 'F'),
        'func' => function () { return __CLASS__; },
        'anon' => new class () { 
            public function __invoke() { 
                return __CLASS__; } },
    ];
    foreach ($test as $item) Low::logVar($item);
    readfile(High::LOG_FILE);

Here is the output from the preceding example:

2020-10-15:array (
  0 => 'A',
  1 => 'B',
  2 => 'C',
  3 => 'D',
  4 => 'E',
  5 => 'F',
)2020-10-15:Closure::__set_state(array(
))2020-10-15:class@anonymous/repo/ch01/php8_mixed_type.php:28$1::__set_state(array())

The preceding code block logs a variety of different data types and then displays the contents of the log file. In the process, this shows us there are no inheritance issues in PHP 8 when a child class overrides a parent class method and substitutes a data type of mixed in place of a more restrictive data type, such as object.

Next, we have a look at using typed properties.

Tip

Best practice: Assign specific data types to all arguments when defining functions or methods. If a few different data types are acceptable, define a union type. Otherwise, if none of this applies, fall back to a mixed type.

For information on union types, see this documentation page:

https://wiki.php.net/rfc/union_types_v2

For more information on a mixed type, have a look here: https://wiki.php.net/rfc/mixed_type_v2.

You have been reading a chapter from
PHP 8 Programming Tips, Tricks and Best Practices
Published in: Aug 2021 Publisher: Packt ISBN-13: 9781801071871
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at €14.99/month. Cancel anytime}