New #php 7 and 8 features.

New #php 7 and 8 features.

php developer can bookmark for their reference

·

8 min read

Some php features those attracted me recently

Features list

Match expressions

When I saw match first I was thought about switch and but later find out match is smarter than switch. key difference with switch.

  • A match arm compares values strictly (===) instead of loosely as the switch statement does.
  • A match expression returns a value.
  • match arms do not fall-through to later cases the way switch statements do.
  • A match expression must be exhaustive.

Match Examples

// using a switch statement
switch ($status) {
 case 'Published':
  $message = 'The post has been published';
  break;
 case 'Draft':
  $message = 'The post is in draft state';
  break;
}
// as a match expression
$message = match($status) {
  'Published' => 'The post has been published',
  'Draft' => 'The post is in draft state',
};
// if no condition match then return default value.
$expressionResult = match ($condition) {
    1, 2 => foo(),
    3, 4 => bar(),
    default => baz(),
};
// if any arms not satisfied the condition then an `UnhandledMatchError` occurred.
$condition = 5;

try {
    match ($condition) {
        1, 2 => foo(),
        3, 4 => bar(),
    };
} catch (\UnhandledMatchError $e) {
    var_dump($e);
}

Null and Void return types

Now we can set return type as null or void. if we use ? before type then if any case user does not exist in database then its automatically return null.

Void and null Examples

// Notice the `?` before the type
function getUser(int $userId): ?User {
 // can return null
}
function setUsername(int $userId, string $username): void {
  persistInDB($userId, $username);
 // won't return anything, or optionally can do:
 // `return;`
}

Never return type

A function or method declared with the never type indicates that it will not return a value and will either throw an exception or end the script’s execution with a call of die(), exit(), trigger_error(), or something similar.

example of never

function redirect(string $uri): never {
    header('Location: ' . $uri);
    exit();
}

function redirectToLoginPage(): never {
    redirect('/login');
    echo 'Hello'; // <- dead code detected by static analysis
}

Array destructuring

PHP 7.1 released a new way of assigning the array’s elements to variables. It’s called array destructuring. array elements assign to variable or skipped any element.

examples of array destructuring

// arrays
$products = [[1, 2, 3, 4], [5, 6, 7]];
[$publishedProducts, $draftProducts] = $products;
// or skip
[, $draftProducts] = $products;
// associative arrays
$products = [
  'title' => 'Modern Php',
  'description' => '...',
  'status' => 'Published'
];
['title' => $title, 'description' => $description] = $product;

Spread operator within arrays

Since PHP 7.4, PHP supports array spread operator ( ...) for array unpacking. An array can be unpacked with another array if the array expression is prefixed with the spread operator. This effectively results in an array_merge of all arrays in the expression.

// arrays
$userPosts = [1, 2, 3, 4, 5];
$userPosts = [...$userPosts, 6];
// associative arrays
$userPosts = [
  'id-a' => 'Published',
  'id-b' => 'Draft',
];
$userPosts = [...$userPosts, 'id-c' => 'Draft'];

Enumerations (enums)

Use enum instead of a set of constants and get validation out of the box.

Examples enums

// previously using a class
class Status {
  const DRAFT = 'Draft';
  const PUBLISHED = 'Published';
  const ARCHIVED = 'Archived';
}
// using an enum
enum PostStatus {
  case Draft;
  case Published;
  case Archived;
}
$status = PostStatus::Draft;

Arrow functions

fn (args) => expression

(!) Arrow functions can't be multi-line

Example of arrow functions

$publishedProducts = array_filter($products,
  fn($product) => $product->status === 'Published'
)

Named parameters

Named parameter as an extension of the existing positional parameters. Named arguments allow passing arguments to a function based on the parameter name, rather than the parameter position. This makes the meaning of the argument self-documenting, makes the arguments order-independent and allows skipping default values arbitrarily.

Named arguments are passed by prefixing the value with the parameter name followed by a colon. Using reserved keywords as parameter names is allowed. The parameter name must be an identifier, specifying dynamically is not allowed.

Example of named arguments

function enableThisConfig($optionA, $optionB, $somethingElse, $anotherOne) {
 //
}
// 6 months later reading this.. ??
enableThisConfig(true, true, false, 'DEV');
// using named params
enableThisConfig(
  optionA: true,
  optionB: true,
  somethingElse: false,
  anotherOne: 'DEV',
);

Null coalescing operator

The null coalescing operator (??) has been added as syntactic sugar for the common case of needing to use a ternary in conjunction with isset(). It returns its first operand if it exists and is not null; otherwise it returns its second operand.

Example of null coalescing

// Fetches the value of $_GET['user'] and returns 'nobody'
// if it does not exist.
$username = $_GET['user'] ?? 'nobody';
// This is equivalent to:
$username = isset($_GET['user']) ? $_GET['user'] : 'nobody';

// Coalescing can be chained: this will return the first
// defined value out of $_GET['user'], $_POST['user'], and
// 'nobody'.
$username = $_GET['user'] ?? $_POST['user'] ?? 'nobody';

Null coalescing assignment operator

The null coalescing operator (??) is a syntactic sugar of the ternary operator and isset(). The null coalescing assignment operator assigns the right operand to the left one if the left operand is null.

Examples of null coalescing assignment operator

// our previous example
$data['name'] = $data['name'] ?? 'Guest';
// to
$data['name'] ??= 'Guest';

Null-safe operator

Examples of Null-safe operator

The null-safe operator allows reading the value of property and method return value chaining, where the null-safe operator short-circuits the retrieval if the value is null, without causing any errors. The syntax is similar to the property/method access operator (->), and following the nullable type pattern, the null-safe operator is ?->.

class User {
  public function getPreferences() {}
}
class Preferences {
  public function getColorScheme() {}
}
$user = new User;
// ❌ preferences could be null
$colorScheme = $user->getPreferences()->getColorScheme();
// alternative
$preferences = $user->getPreferences();
$colorScheme = $preferences ? $preferences->getColorScheme() : null;
// using null-safe operator
$colorScheme = $user->getPreferences()?->getColorScheme();

Spaceship operator

The spaceship operator is used for comparing two expressions. It returns -1, 0 or 1 when $a is respectively less than, equal to, or greater than $b. Comparisons are performed according to PHP's usual type comparison rules.

Example of Spaceship operator

echo 1 <=> 1; // 0
echo 1 <=> 2; // -1
echo 2 <=> 1; // 1

// Floats
echo 1.5 <=> 1.5; // 0
echo 1.5 <=> 2.5; // -1
echo 2.5 <=> 1.5; // 1

// Strings
echo "a" <=> "a"; // 0
echo "a" <=> "b"; // -1
echo "b" <=> "a"; // 1

New string utils

Previously we would use strpos or some other creative solution. But now we can also use str_starts_with, str_ends_with or str_contains

$string = 'Modern PHP';
if (str_starts_with($string, 'Modern')) {}
if (str_ends_with($string, 'PHP')) {}
if (str_contains($string, 'Modern')) {}

Return Types

Now we can set return type and They ensure that the value is of the specified type at call time, otherwise a TypeError is thrown. When overriding a parent method, the child's method must match any return type declaration on the parent. If the parent doesn't define a return type, then the child method may do so.

Examples of return type

function getPost(int $postId): Post {
 //
}

Union types

php8 you can now declare any number of arbitrary types to for properties, arguments, and return types. There are a few exceptions that don't make sense to be used together, such as string|void.

You can completely get rid of the @var, @param, and @return PHPDoc comments now in favor of the types enforced in the code itself. This will certainly help clean the boilerplate comments that were there purely because they could not be declared in code before.

Existing nullable types are not removed, nor deprecated. You can continue to use ?string as a shorthand to string|null.

The iterable type is not going to be removed either, and will be functionally equal to array|Traversable.

Example of union types

In in the example $cost can be integer or float.

function updateTotal(int|float $cost) {
 //
}

Grouped imports

A small but welcome addition. Its similar to javascript import. same namespace multiple class import with one command.

Example of group import

// all in separate lines
use App\Entity\Task;
use App\Entity\Reminder;
use App\Entity\Todo;
// now
use App\Entity\{Task, Reminder, Todo};

Constructor property promotion

Constructor Property Promotion is a new syntax in PHP 8 that allows class property declaration and constructor assignment right from the constructor. Constructor Property Promotion is a shorthand syntax to declare and assign class properties from the constructor. This avoids having to type the class property name from four times to just once, and property type from twice to just once. Your constructor can still run additional code within the constructor. Properties will be assigned before the rest of the constructor code is executed.

Example of constructor property promotion

// from this
class User {
  public string $name;
  public Address $address;
  public function __construct(string $name, Address $address) {
    $this->name = $name;
    $this->address = $address;
 }
}
// to this
class User {
  public function __construct(protected string $name, protected Address $address) {
  // nothing else needed
  }
}

WeakMaps

Not really using Weakmaps, not even in Javascript, but I find it a great addition.

Example of weakmaps

// code taken from https://php.watch/versions/8.0/weakmap
class CommentRepository {
  private WeakMap $comments_cache;
  public function getCommentsByPost(Post $post): ?array {
    if (!isset($this->comments_cache[$post])) {
      $this->comments_cache[$post] = $this->loadComments($post);
    }
    return $this->comments_cache[$post]
  }
}

Reference

Resources that help me to write this post.

Did you find this article valuable?

Support NZIAN blog by becoming a sponsor. Any amount is appreciated!