The new features of PHP 8.1

by Enrico Zimuel
Principal Software Engineer @ Elastic



PUG Torino, Jun 23, 2021

About me

PHP 8.1

New features (some)

  • Enums (RFC)
  • Fibers (RFC)
  • Never type (RFC)
  • array_is_list (RFC)
  • fsync and fdatasync (RFC)
  • Final class constant (RFC)
  • Static variables in inheritance methods (RFC)
  • New in initializers* (RFC)

* under discussion

PHP 8.1 schedule

Enums

Enums

Enum represents a collection of constant values


enum Currency {
    case GBP;
    case EUR;
}

class Product {
    function __construct(
        private string $name, 
        private float $amount, 
        private Currency $currency) {
            var_dump($currency);
    }
}

$a = new Product('Green', 5.50, Currency::EUR); // enum(Currency::EUR)
$b = new Product('Red', 6.20, "EUR"); // Fatal error

Enums values (Backed enums)


enum Currency: string {
    case GBP = '£'; // string or int
    case EUR = '€';
}

$currency = Currency::EUR;

var_dump($currency->value); // €
var_dump($currency->name); // EUR
var_dump(Currency::from('€')); // enum(Currency::EUR)
var_dump(Currency::tryFrom('$')); // NULL

Enums value listing


enum Currency: string {
    case GBP = '£';
    case EUR = '€';
}

var_dump(Currency::cases());
/*
array(2) {
    [0]=>
    enum(Currency::GBP)
    [1]=>
    enum(Currency::EUR)
}
*/

Enums functions


enum Currency: string {
    case GBP = '£';
    case EUR = '€';

    public function format (float $amount)
    {
        $pattern = match ($this) {
            Currency::GBP => "{$this->value} %.2f",
            Currency::EUR => "%.2f {$this->value}",
        };
        return sprintf($pattern, $amount);
    }
}

$cur = Currency::GBP;
echo $cur->format(42.75); // £ 42.75

Enums static functions


enum Currency: string {
    case GBP = '£'; // string or int
    case EUR = '€';

    static public function fromLocale (string $locale)
    {
        return match($locale) {
            'nl_NL' => static::EUR,
            'it_IT' => static::EUR,
            'en_GB' => static::GBP,
        };
    }
}

$cur = Currency::fromLocale('it_IT');
echo $cur->value; // €

Fibers

Fibers

  • Fibers implement cooperative multitasking in PHP
  • A Fiber is a code block that maintains its own stack (variables and state), that can be started, suspended, or terminated cooperatively by the main code and the Fiber

Cooperative multitasking

Concurrent execution

Not executed in parallel

  • The Fiber and the main execution flow does not happen at the same time
  • It is up to the main execution flow to start a Fiber, and when it starts, the Fiber is executed exclusively
  • Only one Fiber can be executed at time

Fiber class

					
final class Fiber
{
    public function __construct(callable $callback) {}
    public function start(mixed ...$args): mixed {}
    public function resume(mixed $value = null): mixed {}
    public function getReturn(): mixed {}
    public function throw(Throwable $exception): mixed {}
    public function isStarted(): bool {}
    public function isSuspended(): bool {}
    public function isRunning(): bool {}
    public function isTerminated(): bool {}

    public static function suspend(mixed $value = null): mixed {}
    public static function getCurrent(): ?self {}
}

Basic Example

					
$fiber = new Fiber(function (): void {
    $value = Fiber::suspend('fiber');
    echo "Value used to resume fiber: ", $value, "\n";
});
	
$value = $fiber->start();
echo "Value from fiber suspending: ", $value, "\n";
$fiber->resume('test');

// Value from fiber suspending: fiber
// Value used to resume fiber: test

Example (1)

						
$files = [
    'source/1.png' => 'dest/a.png',
    'source/2.png' => 'dest/b.png',
    'source/3.png' => 'dest/c.png',
];

$fiber = new Fiber(function(array $files): void {
    foreach($files as $source => $destination) {
        copy($source, $destination);
        Fiber::suspend([$source, $destination]);
    }
});

Example (2)

						
$copied = $fiber->start($files); // [$source, $destination]
$copied_count = 1;
$total_count  = count($files);

while(!$fiber->isTerminated()) {
    $perc = round($copied_count / $total_count, 2) * 100;
    printf("[%d%%]: '%s' to '%s'\n", $perc, $copied[0], $copied[1]);
    $copied = $fiber->resume();
    ++$copied_count;
}
printf("Completed\n");

// [33%]: 'source/1.png' to 'dest/a.png'
// [67%]: 'source/2.png' to 'dest/b.png'
// [100%]: 'source/3.png' to 'dest/c.png'
// Completed

Never type

Never type

  • never is a new return type
  • a function that is declared with the never return type indicates that it will never return a value, and always throws an exception or terminates with a die/exit call

Example


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

redirect('Test');
echo "This will never be executed!";

Example with error


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

redirect('Test'); 
// Fatal error: never-returning function must not implicitly return
echo "This will never be executed!";

array_is_list

array_is_list

array_is_list() returns whether a given array is an array with all sequential integers starting from 0

					
$array = [4, 12, 3];
var_dump(array_is_list($array)); // true

$array = [0 => 'a', 1 => 'b'];
var_dump(array_is_list($array)); // true

$array = [ 'foo' => 'bar', 'baz' => 'bad'];
var_dump(array_is_list($array)); // false

$array = [0 => 'apple', 2 => 'bar'];
var_dump(array_is_list($array)); // false

fsync and fdatasync

fsync

  • Fsync synchronizes changes to the file, including its meta-data. This is similar to fflush, but it also instructs the operating system to write to the storage media
  • Can be useful in applications that consistent and persistent storage is important

fdatasync

  • Fdatasync The fdatasync function synchronizes stream contents to storage media, just like fsync does, but it does not synchronize file meta-data
  • This function is only effectively different in POSIX systems (e.g. in Windows, this function is aliased to fsync)

Example


$file = 'test.txt';

$fh = fopen($file, 'w');
fwrite($fh, 'test data');
fwrite($fh, "\r\n");
fwrite($fh, 'additional data');

fsync($fh);
fclose($fh);

Final class constant

Final class constant*


class Universe 
{
    final public const SPEED_OF_LIGHT = 299_458_792;
}

class RingSpace extends Universe
{
    public const SPEED_OF_LIGHT = 12_758;
}

// Fatal error: RingSpace::SPEED_OF_LIGHT cannot override
//     final constant Universe::SPEED_OF_LIGHT	

* Not available in PHP 8.1.0alpha1

New in Initializers*

* under discussion

New in Initializers

In PHP 8.0


class Foo {
  private Logger $logger;

  public function __construct(?Logger $logger = null)
  {
    $this->logger = $logger ?? new NullLogger;
  }
}

In PHP 8.1


class Foo {
  public function __construct(private Logger $logger = new NullLogger)
  {}
}

New in Initializers

Static variables


function counter() {
    static $i = new Counter;
}

Constants


class Repository {
    const DB = new Database(dsn: 'sqlite://:memory:');
}

Static variables in inheritance methods

Static variables in inheritance methods


class A {
    public static function counter() {
        static $i = 0;
        return ++$i;
    }
}
class B extends A {}

printf ("%d %d %d %d",
    A::counter(),
    A::counter(),
    B::counter(),
    B::counter()
);
// PHP <= 8.0, 1 2 1 2
// PHP >= 8.1, 1 2 3 4	

References

Thanks!

Follow me: @ezimuel

Subscribe to PUG Torino on Meetup!



Creative Commons License
This work is licensed under a
Creative Commons Attribution-ShareAlike 3.0 Unported License.
I used reveal.js to make this presentation.