Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
PHP 8 Programming Tips, Tricks and Best Practices

You're reading from   PHP 8 Programming Tips, Tricks and Best Practices A practical guide to PHP 8 features, usage changes, and advanced programming techniques

Arrow left icon
Product type Paperback
Published in Aug 2021
Publisher Packt
ISBN-13 9781801071871
Length 528 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Doug Bierer Doug Bierer
Author Profile Icon Doug Bierer
Doug Bierer
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface 1. Section 1: PHP 8 Tips
2. Chapter 1: Introducing New PHP 8 OOP Features FREE CHAPTER 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

Working with attributes

Another significant addition to PHP 8 is the addition of a brand-new class and language construct known as attributes. Simply put, attributes are replacements for traditional PHP comment blocks that follow a prescribed syntax. When the PHP code is compiled, these attributes are converted internally into Attribute class instances.

This new feature is not going to have an immediate impact on your code today. It will start to become more and more influential, however, as the various PHP open source vendors start to incorporate attributes into their code.

The Attribute class addresses a potentially significant performance issue we discuss in this section, pertaining to an abuse of the traditional PHP comment block to provide meta-instructions. Before we dive into that issue and how Attribute class instances address the problem, we first must review PHP comments.

Overview of PHP comments

The need for this form of language construct arose with the increasing use (and abuse!) of the plain workhorse PHP comment. As you are aware, comments come in many forms, including all of the following:

# This is a "bash" shell script style comment
// this can either be inline or on its own line
/* This is the traditional "C" language style */
/**
 * This is a PHP "DocBlock"
 */

The last item, the famous PHP DocBlock, is now so widely used it's become a de facto standard. The use of DocBlocks is not a bad thing. On the contrary—it's often the only way a developer is able to communicate information about properties, classes, and methods. The problem only arises in how it is treated by the PHP interpretation process.

PHP DocBlock considerations

The original intent of the PHP DocBlock has been stretched by a number of extremely important PHP open-source projects. One striking example is the Doctrine Object-Relational Mapper (ORM) project. Although not mandatory, many developers choose to define ORM properties using annotations nested inside PHP DocBlocks.

Have a look at this partial code example, which defines a class interacting with a database table called events:

namespace Php7\Entity;
use Doctrine\ORM\Mapping as ORM;
/**
 * @ORM\Table(name="events")
 * @ORM\Entity("Application\Entity\Events")
 */
class Events {
    /**
     * @ORM\Column(name="id",type="integer",nullable=false)
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="IDENTITY")
     */
    private $id;
    /**
     * @ORM\Column(name="event_key", type="string", 
          length=16, nullable=true, options={"fixed"=true})
     */
    private $eventKey;
    // other code not shown

If you were to use this class as part of a Doctrine ORM implementation, Doctrine would open the file and parse the DocBlocks, searching for @ORM annotations. Despite some concerns over the time and resources needed to parse DocBlocks, this is an extremely convenient way to define the relationship between object properties and database table columns, and is popular with developers who use Doctrine.

Tip

Doctrine offers a number of alternatives to this form of ORM, including Extensible Markup Language (XML) and native PHP arrays. For more information, see https://www.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html#annotations-reference.

Hidden dangers associated with the misuse of DocBlocks

There is yet another danger associated with this abuse of the original purpose of a DocBlock. In the php.ini file, there is a setting named opcache.save_comments. If disabled, this would cause the OpCode cache engine (OPcache) to ignore all comments, including DocBlocks. If this setting is in effect, a Doctrine-based application using @ORM annotations in DocBlocks would malfunction.

Another problem has to do with how comments are parsed—or, more to the point, how comments are not parsed. In order to use the contents of a comment, the PHP application needs to open the file and parse it line by line. This is an expensive process in terms of time and resource utilization.

The Attribute class

In order to address hidden dangers, in PHP 8 a new Attribute class is provided. Instead of using DocBlocks with annotations, developers can define the equivalent in the form of attributes. An advantage of using attributes rather than DocBlocks is that they are a formal part of the language and are thus tokenized and compiled along with the rest of your code.

Important note

In this chapter, and also in the PHP documentation, reference to attributes refers to instances of the Attribute class.

Actual performance metrics are not yet available that compare the loading of PHP code containing DocBlocks with the loading of code that contains attributes.

Although the benefits of this approach are not yet seen, as the various open source project vendors start to incorporate attributes into their offerings you will start to see an improvement in speed and performance.

Here is the Attribute class definition:

class Attribute {
    public const int TARGET_CLASS = 1;
    public const int TARGET_FUNCTION = (1 << 1);
    public const int TARGET_METHOD = (1 << 2);
    public const int TARGET_PROPERTY = (1 << 3);
    public const int TARGET_CLASS_CONSTANT = (1 << 4);
    public const int TARGET_PARAMETER = (1 << 5);
    public const int TARGET_ALL = ((1 << 6) - 1);
    public function __construct(
        int $flags = self::TARGET_ALL) {}
}

As you can see from the class definition, the main contribution from this class, used internally by PHP 8, is a set of class constants. The constants represent bit flags that can be combined using bitwise operators.

Attribute syntax

Attributes are enclosed using a special syntax borrowed from the Rust programming language. What goes inside the square brackets is pretty much left to the developer. An example can be seen in the following snippet:

#[attribute("some text")] 
// class, property, method or function (or whatever!)

Returning to our example of the SingleChar class, here's how it might appear using traditional DocBlocks:

// /repo/src/Php7/Image/SingleChar.php
namespace Php7\Image;
/**
 * Creates a single image, by default black on white
 */
class SingleChar {
    /**
     * Allocates a color resource
     *
     * @param array|int $r,
     * @param int $g
     * @param int $b]
     * @return int $color
     */
    public function colorAlloc() 
    { /* code not shown */ } 

Now, have a look at the same thing using attributes:

// /repo/src/Php8/Image/SingleChar.php
namespace Php8\Image;
#[description("Creates a single image")]
class SingleChar {
    #[SingleChar\colorAlloc\description("Allocates color")]
    #[SingleChar\colorAlloc\param("r","int|array")]
    #[SingleChar\colorAlloc\param("g","int")]
    #[SingleChar\colorAlloc\param("b","int")]
    #[SingleChar\colorAlloc\returns("int")]
    public function colorAlloc() { /* code not shown */ }

As you can see, in addition to providing a more robust compilation and avoiding the hidden dangers mentioned, it's also more efficient in terms of space usage.

Tip

What goes inside the square brackets does have some restrictions; for example, although #[returns("int")] is allowed, this is not: #[return("int"). The reason for this is because return is a keyword.

Another example has to do with union types (explained in the Exploring new data types section). You can use #[param("int|array test")] in an attribute, but this is not allowed: #[int|array("test")]. Another peculiarity is that class-level attributes must be placed immediately before the class keyword and after any use statements.

Viewing attributes using Reflection

If you need to get attribute information from a PHP 8 class, the Reflection extension has been updated to include attribute support. A new getAttributes() method that returns an array of ReflectionAttribute instances has been added.

In the following block of code, all the attributes from the Php8\Image\SingleChar::colorAlloc() method are revealed:

<?php
// /repo/ch01/php8_attrib_reflect.php
define('FONT_FILE', __DIR__ . '/../fonts/FreeSansBold.ttf');
require_once __DIR__ . '/../src/Server/Autoload/Loader.php';
$loader = new \Server\Autoload\Loader();
use Php8\Image\SingleChar;
$char    = new SingleChar('A', FONT_FILE);
$reflect = new ReflectionObject($char);
$attribs = $reflect->getAttributes();
echo "Class Attributes\n";
foreach ($attribs as $obj) {
    echo "\n" . $obj->getName() . "\n";
    echo implode("\t", $obj->getArguments());
}
echo "Method Attributes for colorAlloc()\n";
$reflect = new ReflectionMethod($char, 'colorAlloc');
$attribs = $reflect->getAttributes();
foreach ($attribs as $obj) {
    echo "\n" . $obj->getName() . "\n";
    echo implode("\t", $obj->getArguments());
}

Here is the output from the code shown in the preceding snippet:

<pre>Class Attributes
Php8\Image\SingleChar
Php8\Image\description
Creates a single image, by default black on whiteMethod
Attributes for colorAlloc()
Php8\Image\SingleChar\colorAlloc\description
Allocates a color resource
Php8\Image\SingleChar\colorAlloc\param
r    int|array
Php8\Image\SingleChar\colorAlloc\param
g    int
Php8\Image\SingleChar\colorAlloc\param
b    int
Php8\Image\SingleChar\colorAlloc\returns
int

The preceding output shows that attributes can be detected using the Reflection extension classes. Finally, the actual method is shown in this code example:

namespace Php8\Image;use Attribute;
use Php8\Image\Strategy\ {PlainText,PlainFill};
#[SingleChar]
#[description("Creates black on white image")]
class SingleChar {
    // not all code is shown
    #[SingleChar\colorAlloc\description("Allocates color")]
    #[SingleChar\colorAlloc\param("r","int|array")]
    #[SingleChar\colorAlloc\param("g","int")]
    #[SingleChar\colorAlloc\param("b","int")]
    #[SingleChar\colorAlloc\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);
    }
}

Now that you have an idea of how attributes can be used, let's continue our coverage of new features by discussing match expressions, followed by named arguments.

Tip

For more information on this new feature, have a look at the following web page:

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

Also, see this update:

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

Information on PHP DocBlocks can be found here:

https://phpdoc.org/

For more information about Doctrine ORM, have a look here:

https://www.doctrine-project.org/projects/orm.html

Documentation on php.ini file settings can be found here:

https://www.php.net/manual/en/ini.list.php

Read about PHP Reflection here:

https://www.php.net/manual/en/language.attributes.reflection.php

Information about the Rust programming language can be found in this book: https://www.packtpub.com/product/mastering-rust-second-edition/9781789346572

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 $19.99/month. Cancel anytime
Banner background image