Reader small image

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

Product typeBook
Published inAug 2021
Reading LevelIntermediate
PublisherPackt
ISBN-139781801071871
Edition1st Edition
Languages
Right arrow
Author (1)
Doug Bierer
Doug Bierer
author image
Doug Bierer

Doug Bierer has been hooked on computers since his first program, written on a DEC PDP-8, in 1971. In his wide-ranging career, he has been a professional contract programmer since 1978, having written applications in BASIC, PL/I, assembler, FORTH, C, C++, dBase/FoxBase/Clipper, Pascal, Perl, Java, and PHP. He deployed his first website in 1993 while living in San Francisco. He speaks four languages, has traveled extensively, and now resides in Thailand. He also spent some years doing system administration and TCP/IP networking. Some of his technical works include PHP 7 Programming Cookbook and Learning MongoDB 4.x (Packt), as well as Learning PHP and MySQL, Learning PHP Security and Learning Doctrine (O'Reilly Media).
Read more about Doug Bierer

Right arrow

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

Previous PageNext Page
You have been reading a chapter from
PHP 8 Programming Tips, Tricks and Best Practices
Published in: Aug 2021Publisher: PacktISBN-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.
undefined
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 $15.99/month. Cancel anytime

Author (1)

author image
Doug Bierer

Doug Bierer has been hooked on computers since his first program, written on a DEC PDP-8, in 1971. In his wide-ranging career, he has been a professional contract programmer since 1978, having written applications in BASIC, PL/I, assembler, FORTH, C, C++, dBase/FoxBase/Clipper, Pascal, Perl, Java, and PHP. He deployed his first website in 1993 while living in San Francisco. He speaks four languages, has traveled extensively, and now resides in Thailand. He also spent some years doing system administration and TCP/IP networking. Some of his technical works include PHP 7 Programming Cookbook and Learning MongoDB 4.x (Packt), as well as Learning PHP and MySQL, Learning PHP Security and Learning Doctrine (O'Reilly Media).
Read more about Doug Bierer