JREAM

PHP 8 New Features

Although PHP 8 was release in 2021, I like to continue staying up to date with as much as I can. Without further ado, here are some cool PHP Features and Snippets.

Table of Contents

Type Declarations

Although enforcing types is not new to PHP it has had some great improvements with primitives (string, boolean, integer) this iteration.

Don't get type declarations confused with type casting. Type declarations are added to method arguments, return values and class properties.

A type declaration provides tighter control over passing data through PHP in order to reduce bugs. Type casting attempts to change from one to to another (eg: int to string)

Type Casting

Here's what type-casting looks like. Changing one type to another, if possible. These have parenthesis around them (type).

<?php
// This is type casting
$ape = (int) 200;       // integer
$bit = (string) 200;    // string
$pow = (array) "super"  // array

// You can always check with:
gettype($ape);

Type Declaration

And here is what type declaration looks like. I'll show you a few things you may already know and we'll expand a bit on this with the new features.

<?php
// Requires Parameter to be a string
function getName(string $name) {
  return $name;
}

// Requires Parameters to be string, and it must return a string.
function getFullName(string $first, string $last): string
{
  return "$first $last";
}

Union Types

Now it gets a little better with Union Types. These allow parameters to be one or several types. What if you had an integer but wanted to allow a float?

<?php
// Requires both parameters to be one or the other,
// The return type is also a Union.
function number(int|float $x, int|float $y): int|float
{
  return $x + $y;
}

Keep in mind I am only speaking about primitives. You could do type declarations for OOP for a long time already. This will just help many of the smaller methods tidy up with a stricter code base.

If you don't like the Union Types and find declaring 2 or more redundant you can always allow mixed like below, but in a way defeats the purpose a little bit:

<?php
function status(mixed $data): string {
  if (is_array($data)) { /** .. do something */}
}

Don't forget just like in Java if your method doesn't return anything you can declare the return type to void.

Using Named Arguments

This is unusual to see for me but you can now use an argument name (the variable definition) and skip over things you don't need. Python had has this feature for ages and looks a bit nicer in python. Nevertheless, we can do this now.

<?php
// A basic method definition
function getEmail(string $email): string {
  return $email;
}

// Call the function by the named parameter!
getEmail(email: 'none@none.com');

// Works of BIF also, eg:
$result = htmlspecialchars('<html></html>', double_encode: false);

Return Type: never

This is somewhat similar to the void return type, however the never type ceases further execution where void would.

function redirect(string $url): never {
  header("Location: $url");
  exit;
}

array_is_list

This will check the array keys and they must be in sequential order, from 0 to n with no gaps.

array_is_list([1, 2, 3]); // true
array_is_list([1, 'cat', 3]); // true
array_is_list([1 => 'pig', 5 => 'cow']); // false, keys are not sequential and does not start with a 0
array_is_list(['name' => 5, 'cat', 3]); // false, key is named

Class Read Only Property

You may have a public property but with readonly you can set it and foreget it. It's almost like a private property with a getter and no setter.

class Person {
  public readonly string $name;
  public function __construct(string $name) {
    $this->name = $name;
  }
}
$person = new Person('John');
$person->name = 'Jane'; // Error, cannot change

Get Current Objects ::Class

You used to have to call $get_class($instance) to find your calling class name. Now every object has a built in static constant of ::class.

<?php
$obj = new \stdClass();
// Instead of this
echo get_class($obj);

// We can grab the magic constant
echo $obj::class;

Nullable Parameter Types

We can also have Nullable parameter types which is pretty great. If we wanted to declare a type as a string but at some point had no string data and just passed null we'd be out of luck. But we can use a ? before the type to allow the type or a nullable type.

<?php
class House
{
  // Adding the "?" mark allows it to be either!
  public function __construct(int $id, ?string $name = null)
  {
  }
}

Null-Safe Method Chaining

Since we hopped onto the null train we might as well look at another feature that was added. Null-safe chaining is quite elegant and it's similar to JavaScripts object checking with objName?.param to see if a parameter is set without giving errors.

<?php
class UserAdvisor
{
  public function setAccount(array $details)
  {
    $this->details = $details;
    return null; // Forgot to return this object, uh oh!
  }

  public function setLocation(string $location)
  {
    $this->location = $location;
    return $this; // Return "this" class to chain methods
  }
}

$advisor = new UserAdvisor();
// This will give us an error!
$advisor->setAccount(['data'])->setLocation('Palm Beach');

In the simplified example I mad, a real world example may have many chained events which would requires much more error checking.

To resolve this, we can use a ? before the next method chain (just like the nullable parameter type) and we will avoid a world of checking our data as we go through the one-off chain.

<?php
// Adding the "?" before the next method does the trick!
$advisor->setAccount(['data'])?->setLocation('Palm Beach');

Constructor Property Promotion

When you create a class we always have to define the variables/properties with their scope within the class definition (public, protected, private). At least we should be doing that not just for protecting certain ones and leaving the others public by default but because it makes for splendid code readability.

First let's took a way we are all used to. It is a little tedious to declare and assign the same thing times isn't it? Keep in mind this is only two variables, who knows what kind of crazy classes are out there?!

// Common Way of doing Property Instantiation
class Shape
{
  // 1. We declare the scope
  public int $x;
  public int $y;

  public function __construct(int $x, int $y)
  {
    // 2. We set thee class variables
    $this->x = $x;
    $this->y = $y;
  }
}

$shape = new Shape(5, 10);

Here's the Constructor Property Promotion. I think it could get a bit messy unless it's separated on lines of code. It is a bit strange to read a scoped class variables in a method definition to me still.

<?php
// Once constructed $shape->x will be all set in one go.
class Shape
{
  // 1. We just
  public function __construct(public int $x, public int $y)
  {
    // (No need to set $this->x or y here)
  }
}

$shape = new Shape(25, 25);

Parameter Trailing Commas

Since we are looking at methods its worth pointing out that PHP now allows trailing commas in the parameter list. Whether it's a big deal or not I can't say.

<?php
// Notice the trailing "," comma. This is allowed.
function callMe($phone, $areaCode,) {
  return;
}

String Starts/Ends With and Contains

We've been using strpos('haystack', 'needle') === 1 or substr to manage of our good old string management toolbox. Here are three new functions added for string.

<?php
// These are case sensitive as far as I've tested.
str_starts_with(haystack: 'This is my string', needle: 'T');  // true
str_ends_with('This is my string', 'string');                 // true
str_contains('This is my string', 'my');                       // true

Match Feature, like Case/Switch

They've added a new match() feature that behaves just like a case/switch statement yet it's easier to write and use quickly.

<?php
$result = match ('dog') {
  'cat' => 'meow',
  'fish' => 'gurggle',
  'dog' => 'woof'
}; // Returns 'woof'

Other Notable Features

  • Stringable Interface makes it easy to declare types on a method that can accept/return strings or object with __toString().
  • PHPToken Class for an OOP alternative to token_get_all().
  • Weak Maps A dictionary object that may be used for building caches that don't live long.
  • Attributes Passing meta-data to your code without having to use DocBlocks anymore.