PHP 8

PHP 8 has been a major upgrade to PHP technology in recent years. This release brings developers performance enhancements — due to the new JIT Compiler.

As a developer, you will also appreciate useful PHP 8 attributes, new operators and types, match expressions, and many more.

In this blog, we will teach you what you need to learn about the new features of PHP8 and we will clarify it in depth. Let’s review it now: the main new features in PHP 8, why you should update as soon as you can, and how to do it.

What You Should Know About PHP 8 New Features? 

PHP8 rolled out with several developer-centric updates that you should know. So, let’s look at the three major updates in PHP 8:

Attributes let you add metadata to classes, functions, methods, parameters, properties, and class constants. Attributes are like annotations that some other languages support. The new operator, only available from PHP 8.1, can be used in initializers for default parameter values, static variables, and attribute arguments.

Meanwhile, the PHP 8 match expression is a new control structure that checks a subject expression with multiple-branched conditional expressions using the identity operator and runs the matched branch.

The PHP instanceof operator, which before could only be used with a class object, can also be used with any random expression. The JIT (Just-In-Time) compiler improves performance and usability.

At last, the new operator is a way to make a new object from a class.

A Look Into PHP 8 Attributes

As said earlier, Attributes are configuration instructions that add or modify classes, functions, methods, parameters, properties, and class constants with metadata that is structured and readable by machines.

“Structured” means the metadata information can be accessed and processed, making attributes distinct from unstructured doc-comments, which are only simple strings.

Attributes can be used to supply configuration and other information that is only needed sometimes, and thus embedded, not fixed into the PHP script.

Here’s the usual process for using PHP attributes:

  • Define an attribute class. An attribute class is a normal PHP class that has #[Attribute] on a separate line before it. The class can have a PHP class constructor if needed. An attribute is recognized by its class; the attribute name is the same as the class name when the attribute is applied.
  • Use the attribute on declarations of classes, methods, parameters, functions, properties, and class constants. For instance, if the attribute class is named Validate, the attribute is applied as #[Validate]. The same attribute can be applied to different declarations multiple times. Additionally, PHP if multiple conditions can be used on the same declaration.

Uses of PHP8 Attributes

PHP 8 Attributes have several use cases, including the following:

  • An attribute can be a better option than using the interface. The advantage is that a class that uses an attribute does not have to implement all the methods from the interface, but only the ones that are relevant. This way, the class can avoid having extra methods that are not needed.
  • Changing how the code is compiled, checked, generated, and executed.
  • Making PHP engine and extensions independent. PHP core and extensions can have some PHP attributes for some of the declarations.
  • Adding configuration information that is related to a declaration. For example, attributes on a method can show which events the method responds to.
  • Moving from docblocks to attributes.

Apart from the above uses, an attribute can be applied to any or all of the declaration types that are supported, unless it is restricted. To restrict an attribute, one or more bitmask flags can be used, such as

  • Attribute::TARGET_CLASS
  • Attribute::TARGET_FUNCTION
  • Attribute::TARGET_METHOD
  • Attribute::TARGET_PROPERTY
  • Attribute::TARGET_CLASS_CONSTANT
  • Attribute::TARGET_PARAMETER
  • Attribute::TARGET_ALL.
  • Attribute::IS_REPEATABLE.

Examples of using PHP 8 Attributes

Now that you have gained a good understanding of PHP 8  attributes, let us see how attributes can be used with an example.

Suppose you have an array that you want to sort using different sort functions, such as sort() for ascending order, rsort() for descending order, and shuffle() for random order.

You may also need to check the input array to make sure that it is not empty, or that it has more than one element to make sorting meaningful. You can use attributes to provide the sort type and the validation information.

First, you need to define an attribute class for validation.

#[Attribute]

class Validate {}

Next, you have to create another attribute class for the sort type. The bitmask flags Attribute::TARGET_CLASS and Attribute::IS_REPEATABLE show that the attribute is only for class declarations and that the attribute can be used more than once.

#[Attribute(Attribute::TARGET_CLASS|Attribute::IS_REPEATABLE)]

class SortType {

 

function __construct($sortType){

        $this->sortType = $sortType;

    }

}

Now, declare an interface with the sortArray() function.

interface Sort 

{   

    public function sortArray();

}

Lastly, implement the Sort interface by declaring a class.

class SortImpl implements Sort

{

 

…}

Inside the class, define two class properties, one for the type of sorting, and the other for the array that needs to be sorted.

public string $sortType=””;

public $arrayToSort=array(“B”, “A”, “f”, “C”);

Declare a method to check that the array that you want to sort is not empty. Use the #[Validate] attribute for the method. This attribute makes the method visible using the Reflection APIs.

#[Validate]

    public function arrayEmpty()

    {

        if (count($this->arrayToSort) === 0) {

            throw new RuntimeException(“Array is empty; please provide a non-empty array”);

        }

    }

Declare another method to ensure that the array has more than one element, and use the #[Validate] attribute for it.

#[Validate]

    public function arraySize()

    {

        if (sizeof($this->arrayToSort) < 2) {

            throw new RuntimeException(“Please provide an array of size 2 or more”);

        }

    }

Write a function to sort the array according to the SortType value. Use ascending/descending/shuffle sort methods.

public function sortArray()

    { 

        

         if ($this->sortType == “asc”) {

            …

 

        } elseif ($this->sortType == “desc”) {

            …

 

        } else {

              

             …

 

    }

Write a function named performSort(Sort $sort) that does the sorting. In the function, check the validity of the input before sorting it.

Create a class and use the #[SortType] attribute on it. You can use this attribute more than once to do different kinds of sorting.

#[SortType(sortType: “desc”)] 

#[SortType(sortType: “shuffle”)]  

#[SortType(sortType: “asc”)]              

class QSort

{

}

 

Next, create the class SortImpl instance.

$sort = new SortImpl();

Use the Reflection API to access the attributes of the class.

$ref    =   new ReflectionClass(QSort::class);

$attrs  =   $ref->getAttributes();  

Loop through the attributes array and use the performSort() function to sort the input according to each sort type. Then, display the sorted output.

foreach ($attrs as $attr) {

 

}

A Look into PHP 8 new Operator

To instantiate a class, we use the new operator. Starting from PHP 8.1, we can use the new operator with any expression, as long as we enclose it in parentheses.

new (expression)(…$args)

We can also use the new operator in various initializers, such as for parameters, static variables, global constants, and attributes. We will see some examples of this in the following sections.

How the new Operator can be Used in Function Parameter Default Values?

We will use a different version of the array sort example to show how the new operator can be used in function parameter default values. Let’s say we have a class named SortArray that has a method sortArray($arrayToSort) to arrange an array from low to high.

class SortArray {

    public function sortArray($arrayToSort) {

}

}

We have another class named ReverseSortArray that has a method to order an array from high to low.

class ReverseSortArray {

    public function sortArray($arrayToSort) {

}

}

We will now define a function that takes any array and an array sorter as arguments and sorts the array. We can use the new operator to create a SortArray object as the default array sorter.

function sortAnyArray($arrayToSort, $arraySorter = new SortArray)

{

    return $arraySorter->sortArray($arrayToSort);

}

How the new Operator can be Used in Variable and Constant Initializers?

The new operator may be used in static variable initializers and global constant initializers. The new operator is not supported, though, in static and non-static class property initializers, nor in class constant initializers. The following script demonstrates which variable and constant initializers are supported, and which aren’t.

You can use the new operator to initialize static variables and global constants. However, the new operator does not work for class properties, whether they are static or not, or for class constants. Additionally, the array constants can only be used in initializers. The script below shows which initializers are valid and which are not.

<?php

 

class A{ }

 

class B{

//static $a = new A; //New expressions are not supported in this context

//public $a = new A; //New expressions are not supported in this context 

//const C = new A; //New expressions are not supported in this context

}

static $a = new A;

 

const C = new A;

How the new Operator can be Used in Attribute Arguments?

You can also use the new operator to pass attribute arguments. Let’s take the same example that we used to show the Attributes feature but with a slight change. Recall that we used the #[SortType] attribute to mark the QSort class with the attribute arguments as strings.

#[SortType(sortType: “desc”)] 

#[SortType(sortType: “shuffle”)]  

#[SortType(sortType: “asc”)]              

class QSort

{

}

You need to define a class named Str that takes a string argument as a parameter for the constructor.

class Str{

    function __construct($str){

        $this->value = $str;

    }

 

    function __toString(){

        return $this->value;

    }

     

}

Now, use the new Operator in the attribute arguments:

#[SortType(sortType: new Str(“desc”))] 

#[SortType(sortType: new Str(“shuffle”))]  

#[SortType(sortType: new Str(“asc”))]              

class QSort

{

}

Unsupported new Operator Contexts

We have already discussed some situations where the new operator cannot be used, such as initializing static and non-static class properties, and class constants. You may face errors like PHP “constant expression contains invalid operations.” Besides these, there are some other cases where the new operator is not allowed:

  • Dynamic class name that is not a string
  • Unpacking arguments in a constant expression
  • Anonymous class in a constant expression
  • The constant expression that is not supported

A Look Into the new match expression

PHP 8 brings a new feature called PHP match expression, which is a way of controlling the flow of the program based on the value of a given expression. The match expression compares the value with one or more options using the same logic as the === operator and returns the value that corresponds to the first matching option. The PHP match statement is like a switch statement, but with some differences:

  • The PHP match expression uses the === operator to compare values, while the switch uses the == operator.
  • The PHP8 match expression has a return value, while a switch doesn’t. The return value can be stored in a variable.
  • The match expression stops automatically after finding a matching option. The switch needs a break; a statement to stop.
  • The PHP 8 match expression does not continue to the next option, as a switch does if there is no break; statement.
  • The PHP match expression allows multiple values separated by a comma (,) in the same option, while the switch doesn’t.
  • The match expression must cover all possible values of the expression.

Outputting the Return Value

To begin with a basic example, the match expression in the following code checks the integer value of 1, which is the expression to be matched, with several options including the default one.

<?php

 

echo match (1) {

    0 => ‘A’,

    1 => ‘B’,

    2 => ‘C’,

    default => ‘Default’,

};

In the next code, none of the options except the default one match the value of the expression to be checked.

<?php

 

echo match (3) {

    0 => ‘A’,

    1 => ‘B’,

    2 => ‘C’,

    default => ‘Default’,

};

The output is:

Default

You can’t combine the default option with other options in the code as shown below:

<?php

 

echo match (3) {

    0 => ‘A’,

    1 => ‘B’,

    2, default => ‘C’,

     

};

When you run the script, the following error will generate:

Parse error: syntax error, unexpected token “default”, expecting “=>”

A more complicated example, the next code has a variable that is created from the \Ds\Vector class. The expression to be matched uses the vector variable like an array.

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’);

$vector->push(‘b’, ‘c’);

 

$vector[] = ‘d’;

 

echo match ($vector[1]) {

  ‘a’ => “a”,

  ‘b’ => “b”,

};

Assigning the Return Value to a Variable

You can also store the return value in a variable, and use any expressions you want for the match conditions. In this script, the match expression uses the boolean value true as the subject expression and checks if some other built-in string functions are true or false.

<?php

 

$text = ‘A B C D’;

 

$result = match (true) {

    str_word_count($text)==3 || str_word_count($text)==2 => ‘2 or 3’,

    str_word_count($text)==4 || str_word_count($text)==5 => ‘4 or 5’,

    

};

 

var_dump($result);

The output is:

string(6) “4 or 5”

No Type Coercion

The match expression does a strict comparison and does not change the types of values. This is different from the switch statement, which does a loose comparison and can convert the types. Here is an example of a loose comparison with a switch statement.

The script below will output the value of a because it matches the first condition.

<?php

$var = 0;

switch((string)$var) 

{

    case  0  : echo ‘a’; break; // This tests for NULL or empty string   

    default : echo ‘b’; break; // Everything else, including zero

}

In contrast, the match in the following script makes a strict comparison and matches the default condition to output Default.

<?php

$var = 0;

 

echo match ((string)$var) {

    0 => ‘a’,

    default => ‘Default’,

};

Matching NULL Values using Identity Comparison

The match expression compares the values by their exact type using the identity operator (===). This means that even NULL values are matched by the match expression. The Ds\Vector::push() method returns NULL, so the match expression in this script will match the NULL values that come from the Ds\Vector::push() method.

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’); 

 

match ($vector->push(‘b’)) {

  $vector->push(‘d’) => $vector->push(‘b’),

  $vector->pop() => $vector->pop(),

};

 

print_r($vector);

The output is:

Ds\Vector Object ( [0] => a [1] => b [2] => d [3] => b )

You can use identity comparison ( === ) to check any values, even if they are not defined. You can also store the NULL value that match gives back in a variable. In this example, the match returns NULL and the first case is true.

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’); 

 

$vector = match ($vector->push(‘b’)) {

  $vector->push(‘c’) => $vector->push(‘b’),

  $vector->pop() => $vector->pop(),

};

 

var_dump($vector);

sizeof PHP Function

The sizeof() PHP function is another name for the count() function. The sizeof() function measures how many elements are in an array, or how many properties are in an object. It gives back the number of elements in an array.

Syntax

sizeof(arr, mode)

Comma-separated Multiple Conditions

The PHP multiple conditional expressions can be comma-separated. Here’s a script showing example with single condition:

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’); 

 

match (‘push’) {

  ‘push’ => $vector->push(‘b’),

  ‘pop’ => $vector->pop(),

};

 

print_r($vector);

The output is :

Ds\Vector Object ( [0] => a [1] => b )

Now, using the same example, we will add comma-separated multiple conditions.

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’); 

 

match (‘puush’) {

  ‘push’,’puush’ => $vector->push(‘b’) 

   

};

 

print_r($vector);

Exhaustiveness

A match expression needs to cover all the possible cases of the subject expression. If none of the conditions match the subject expression, the script will fail. For example, in the script below, the subject expression is not handled by any of the conditions.

<?php

 

$vector = new \Ds\Vector();

 

$vector->push(‘a’); 

 

match (‘set’) {

  ‘push’ => $vector->push(‘b’),

  ‘pop’ => $vector->pop(),

};

 

print_r($vector);

The error output is:

Uncaught UnhandledMatchError: Unhandled match case ‘set’

The instanceof PHP Operator Supports Arbitrary Expression

One of the new features in PHP 8 is that developers can use any expression with the instanceof operator, as long as it meets two conditions: The expression has to be enclosed in brackets, and The expression has to result in a string value.

To show how the new instanceof PHP operator works, make a PHP file called sample.php. Define three variables for different kinds of collections, such as \Ds\Vector, and \Ds\Set.

$collection_a = new \Ds\Vector([1, 2, 3]);

$collection_b = new \Ds\Vector();

$collection_c = new \Ds\Set();

Add an arbitrary function that returns a class, such as \Ds\Vector::class as a string.

function getSomeClass(): string

{

    return \Ds\Vector::class;

}

To see the results of using the PHP instanceof operator with various collection variables, print them out. The instanceof operator expects an expression that gives a string value for each use.

The code for sample.php is shown below.

<?php

 

$collection_a = new \Ds\Vector([1, 2, 3]);

$collection_b = new \Ds\Vector();

$collection_c = new \Ds\Set();

 

function getSomeClass(): string

{

    return \Ds\Vector::class;

}

 

var_dump($collection_a instanceof (‘\Ds’.’\Vector’));

var_dump($collection_a instanceof (‘\Ds’.’\Hashtable’));

 

var_dump($collection_b instanceof (‘\Ds’.’\Vector’));

 

var_dump($collection_b instanceof (‘\Ds’.’\Set’));

var_dump($collection_c instanceof (‘\Ds’.’\Set’));

var_dump(new \Ds\Vector instanceof (getSomeClass()));

var_dump($collection_a instanceof (‘\Ds’.’\Set’));

The script runs ok with PHP 8 and generates output:

bool(true) 

bool(false)

bool(true) 

bool(false) 

bool(true) 

bool(true) 

bool(false) 

A Look into JIT(Just-In-Time) Compiler

PHP 8 brings a new feature called Just-in-time (JIT) compilation, which aims to:

  • Enhance the speed and PHP 8 performance.
  • Make PHP more adaptable for non-Web tasks that need a lot of computing power, where PHP can offer significant performance improvements.
  • Open up the possibility of using PHP, instead of C, to create built-in functions.

PHP 8 introduces two new features for JIT compilation:

  • Tracing JIT
  • Function JIT

Function JIT only improves the code inside a single function, while Tracing JIT improves the entire stack trace. Tracing JIT is proven to increase the speed 3 times on artificial tests, and 1.5 to 2 times on long-lasting queries.

Conclusion

In this guide, tutorial, or whatever you call it, we have talked about the latest and developer-centric updates and improvements to PHP 8 attributes and classes. Hope you have understood these new improvements to PHP 8 attributes, match statement, match expression, instanceof PHP, and class constructor. For more relevant tutorials and guides on PHP, you can check out our other blogs.

FAQs

What is a Terraform Conditional Attribute in PHP 8?

A Terraform conditional attribute is a way to customize resources based on a boolean expression using ? : syntax.

How can I Fix the Linting Error “Unexpected empty object pattern”?

To fix the linting error “Unexpected empty object pattern”, you can either assign a variable, use _ as a placeholder, or disable the rule.

What is the attr.evolve Function in PHP 8?

The attr.evolve function is a Python function that creates a new instance of an attrs class with some attributes updated.

How to Use a PHP Class Constructor?

To use a PHP class constructor, you can define a special method named __construct() inside the class and pass any arguments to initialize the object properties.

Leave a Reply

Your email address will not be published. Required fields are marked *