PHP 8.0+


8.0+

== Именованные аргументы ==

myFunction(paramName: $value);
array_foobar(array: $value);

// Использование позиционных аргументов:
array_fill(0, 100, 50);

// Использование именованных аргументов:
array_fill(start_index: 0, count: 100, value: 50);
array_fill(value: 50, num: 100, start_index: 0);


== Метаатрибуты ==

Метаатрибуты похожи на интерфейсы, но могут быть опущены,
т.е. не инициализированы (имплементированы) вовсе.
Метаатрибуты полезны в рефлексии

interface ActionHandler
{
    public function execute();
}

#[Attribute]
class SetUp {}

class CopyFile implements ActionHandler
{
    public string $fileName;
    public string $targetDirectory;

    #[SetUp]
    public function fileExists()
    {
        if (!file_exists($this->fileName)) {
            throw new RuntimeException("Файл не найден");
        }
    }

    #[SetUp]
    public function targetDirectoryExists()
    {
        mkdir($this->targetDirectory);
    }

    public function execute()
    {
        copy($this->fileName, $this->targetDirectory . '/' . basename($this->fileName));
    }
}

function executeAction(ActionHandler $actionHandler)
{
    $reflection = new ReflectionObject($actionHandler);

    foreach ($reflection->getMethods() as $method) {
        $attributes = $method->getAttributes(SetUp::class);

        if (count($attributes) > 0) {
            $methodName = $method->getName();

            $actionHandler->$methodName();
        }
    }

    $actionHandler->execute();
}

$copyAction = new CopyFile();
$copyAction->fileName = "/tmp/foo.jpg";
$copyAction->targetDirectory = "/home/user";

executeAction($copyAction);


== Метаатрибуты могут иметь аргументы ==

// a.php
namespace MyExample;

use Attribute;

#[Attribute]
class MyAttribute
{
    const VALUE = 'value';

    private $value;

    public function __construct($value = null)
    {
        $this->value = $value;
    }
}

// b.php

namespace Another;

use MyExampleMyAttribute;

#[MyAttribute]
#[MyExampleMyAttribute]
#[MyAttribute(1234)]
#[MyAttribute(value: 1234)]
#[MyAttribute(MyAttribute::VALUE)]
#[MyAttribute(array("key" => "value"))]
#[MyAttribute(100 + 200)]
class Thing
{
}

#[MyAttribute(1234), MyAttribute(5678)]
class AnotherThing
{
}




== Рефлексия перебором или напрямую ==

#[Attribute]
class MyAttribute
{
    public $value;

    public function __construct($value)
    {
        $this->value = $value;
    }
}

#[MyAttribute(value: 1234)]
class Thing
{
}


// Перебором

function dumpAttributeData($reflection) {
    $attributes = $reflection->getAttributes();

    foreach ($attributes as $attribute) {
       var_dump($attribute->getName());
       var_dump($attribute->getArguments());
       var_dump($attribute->newInstance());
    }
}

dumpAttributeData(new ReflectionClass(Thing::class));
/*
string(11) "MyAttribute"
array(1) {
  ["value"]=>
  int(1234)
}
object(MyAttribute)#3 (1) {
  ["value"]=>
  int(1234)
}
*/

// Напрямую

function dumpMyAttributeData($reflection) {
    $attributes = $reflection->getAttributes(MyAttribute::class);

    foreach ($attributes as $attribute) {
       var_dump($attribute->getName());
       var_dump($attribute->getArguments());
       var_dump($attribute->newInstance());
    }
}

dumpAttributeData(new ReflectionClass(Thing::class));



== Атрибутам можно задавать область применения ==

namespace Example;

use Attribute;

#[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_FUNCTION | Attribute::IS_REPEATABLE)]
class MyAttribute
{
}



== Определение свойств в конструкторе ==

class Point {
    public function __construct(protected int $x, protected int $y = 0) {
    }
}


== Объединение типов ==

function foo(int|null $a): bool|int


== Строгая типизация ==

declare(strict_types=1);

function sum(int $a, int $b) {
    return $a + $b;
}

var_dump(sum(1, 2));
var_dump(sum(1.5, 2.5)); // Выбросит ошибку



== Выражение match для сравнения ==

Аналог switch() с действием

$return_value = match (subject_expression) {
    single_conditional_expression => return_expression,
    conditional_expression1, conditional_expression2 => return_expression,
};

$result = match ($x) {
    foo() => ...,
    $this->bar() => ..., // bar() не будет выполнен, если foo() === $x
    $this->baz => beep(), // beep() будет выполнен только если $x === $this->baz
    // etc.
};

$expressionResult = match ($condition) {
    1, 2 => foo(),
    3, 4 => bar(),
    default => baz(),
};


$age = 23;

$result = match (true) {
    $age >= 65 => 'пожилой',
    $age >= 25 => 'взрослый',
    $age >= 18 => 'совершеннолетний',
    default => 'ребёнок',
};


$condition = 5;

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


== Выражение "nullsafe" - компактная проверка is_null ==

$result = $repository?->getUser(5)?->name;



== static может быть возвращаемым типом ==

class Test {
     public function create(): static {
          return new static();
     }
}


== throw может быть стрелочной функцией ==

$fn = fn() => throw new Exception('Исключение в стрелочной функции');
$user = $session->user ?? throw new Exception('Должен быть пользователь');