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 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 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. In order to address hidden dangers, in PHP 8 a new Important note In this chapter, and also in the PHP documentation, reference to attributes refers to instances of the 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 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. 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: Returning to our example of the Now, have a look at the same thing using attributes: 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 Another example has to do with union types (explained in the Exploring new data types section). You can use If you need to get attribute information from a PHP 8 class, the In the following block of code, all the attributes from the Here is the output from the code shown in the preceding snippet: The preceding output shows that attributes can be detected using the Now that you have an idea of how attributes can be used, let's continue our coverage of new features by discussing 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 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/9781789346572php.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.The Attribute class
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.Attribute class.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) {}
}
Attribute syntax
#[attribute("some text")]
/ class, property, method or function (or whatever!)
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 */ }
/ /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 */ }
#[returns("int")] is allowed, this is not: #[return("int"). The reason for this is because return is a keyword.#[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
Reflection extension has been updated to include attribute support. A new getAttributes() method that returns an array of ReflectionAttribute instances has been added.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());
}
<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
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);
}
}
match expressions, followed by named arguments.php.ini file settings can be found here: