ИТ Блог. Администрирование серверов на основе Linux (Ubuntu, Debian, CentOS, openSUSE)

Что нового в PHP 8 (функции, улучшения и JIT-компилятор). Часть 2

Что нового в PHP 8 (функции, улучшения и JIT-компилятор)

Улучшения PHP 8 и новые функции

Помимо JIT, мы можем ожидать множество функций и улучшений в PHP 8. Следующий список – это отобранный нами выбор предстоящих дополнений и изменений, которые должны сделать PHP более надежным и эффективным.

  1. Проверка для методов абстрактных черт
  2. Несовместимые сигнатуры методов
  3. Массивы, начинающиеся с отрицательного индекса
  4. Union Types 2.0
  5. Ошибки согласованного типа для внутренних функций
  6. Выражение выражения
  7. Слабые карты
  8. Завершающая запятая в списке параметров
  9. Разрешить класс синтаксиса ::  на объектах
  10. Атрибуты v2

 

Проверка для методов абстрактных черт

Черты определяются как «механизм повторного использования кода в языках с одним наследованием, таких как PHP». Как правило, они используются для объявления методов, которые могут использоваться в нескольких классах.

Черта также может содержать абстрактные методы. Эти методы просто объявляют сигнатуру метода, но реализация метода должна быть сделана внутри класса с использованием признака.

Согласно руководству по PHP:

«Черты поддерживают использование абстрактных методов для наложения требований на выставочный класс».

 

Это также означает, что сигнатуры методов должны совпадать. Другими словами, тип и количество обязательных аргументов должны быть одинаковыми.

В любом случае, по словам Никиты Попова, автора RFC, проверка подписи в настоящее время проводится только местно:

  • Она не применяется в наиболее распространенном случае, когда реализация метода обеспечивается классом using.
  • Она применяется, если реализация происходит из родительского класса.
  • Она применяется, если реализация происходит из дочернего класса.

Следующий пример от Никиты относится к первому случаю (не принудительная подпись):

trait T {
	abstract public function test(int $x);
}
 
class C {
	use T;

	// Разрешено, но не должно быть связано с недопустимым типом.
	public function test(string $x) {}
}

 

С учетом вышесказанного, RFC предлагает всегда выдавать фатальную ошибку, если метод реализации не совместим с методом абстрактной черты, независимо от его происхождения:

Fatal error: Declaration of C::test(string $x) must be compatible with T::test(int $x) in /path/to/your/test.php on line 10

 

Этот RFC был единогласно одобрен.

Несовместимые сигнатуры методов

В PHP ошибки наследования из-за несовместимых сигнатур методов выдают либо фатальную ошибку, либо предупреждение в зависимости от того, что является причиной ошибки.

Если класс реализует интерфейс, несовместимые сигнатуры методов приводят к фатальной ошибке. Согласно документации Object Interfaces:

«Класс, реализующий интерфейс, должен использовать сигнатуру метода, совместимую с LSP (принцип подстановки Лискова). Невыполнение этого требования приведет к фатальной ошибке ».

 

Вот пример ошибки наследования с интерфейсом:

interface I {
	public function method(array $a);
}
class C implements I {
	public function method(int $a) {}
}

 

В PHP 7.4 приведенный выше код выдаст следующую ошибку:

Fatal error: Declaration of C::method(int $a) must be compatible with I::method(array $a) in /path/to/your/test.php on line 7

 

Функция в дочернем классе с несовместимой подписью выдаст предупреждение. Смотрите следующий код из RFC:

class C1 {
	public function method(array $a) {}
}
class C2 extends C1 {
	public function method(int $a) {}
}

 

В PHP 7.4 приведенный выше код будет просто выдавать предупреждение:

Warning: Declaration of C2::method(int $a) should be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

 

Теперь RFC предлагает всегда выдавать фатальную ошибку для несовместимых сигнатур методов. В PHP 8 код, который мы видели выше, будет подсказывать следующее:

Fatal error: Declaration of C2::method(int $a) must be compatible with C1::method(array $a) in /path/to/your/test.php on line 7

 

Массивы, начинающиеся с отрицательного индекса

В PHP, если массив начинается с отрицательного индекса ( start_index < 0), следующие индексы будут начинаться с 0. Посмотрите на следующий пример:

$a = array_fill(-5, 4, true);
var_dump($a);

 

В PHP 7.4 результат будет следующим:

array(4) {
	[-5]=>
	bool(true)
	[0]=>
	bool(true)
	[1]=>
	bool(true)
	[2]=>
	bool(true)
}

 

Теперь RFC предлагает изменить вещи так, чтобы второй индекс был в start_index + 1 зависимости от значения start_index.

В PHP 8 приведенный выше код приведет к следующему массиву:

array(4) {
	[-5]=>
	bool(true)
	[-4]=>
	bool(true)
	[-3]=>
	bool(true)
	[-2]=>
	bool(true)
}

 

В PHP 8 массивы, начинающиеся с отрицательного индекса, меняют свое поведение. Узнайте больше о обратной несовместимости в RFC.

 

Union Types 2.0

Типы объединения принимают значения, которые могут быть разных типов. В настоящее время PHP не обеспечивает поддержку типов объединения, за исключением синтаксиса ?Type и специального типа iterable.

До PHP 8 типы объединения могли быть указаны только в аннотациях phpdoc, как показано в следующем примере из RFC:

class Number {
	/**
	 * @var int|float $number
	 */
	private $number;

	/**
	 * @param int|float $number
	 */
	public function setNumber($number) {
		$this->number = $number;
	}

	/**
	 * @return int|float
	 */
	public function getNumber() {
		return $this->number;
	}
}

 

Теперь RFC 2.0 для типов Union предлагает добавить поддержку типов объединения в сигнатурах функций, чтобы мы больше не полагались на встроенную документацию, а T1|T2|… вместо этого определяли бы типы объединения с помощью синтаксиса:

class Number {
	private int|float $number;

	public function setNumber(int|float $number): void {
		$this->number = $number;
	}

	public function getNumber(): int|float {
		return $this->number;
	}
}

 

Как объяснил Никита Попов в RFC,

«Поддержка типов объединения в языке позволяет нам перемещать больше информации о типах из phpdoc в сигнатуры функций, что дает следующие преимущества:

  • Типы на самом деле обязательны, поэтому ошибки могут быть выявлены на ранней стадии.
  • Поскольку они применяются принудительно, информация о типах с меньшей вероятностью устареет или пропустит крайние случаи.
  • Типы проверяются во время наследования, следуя принципу подстановки Лискова.
  • Типы доступны через Reflection.
  • Синтаксис гораздо менее шаблонный, чем phpdoc.

Типы объединения поддерживают все доступные типы с некоторыми ограничениями:

 

Ошибки согласованного типа для внутренних функций

При передаче параметра недопустимого типа внутренние и пользовательские функции ведут себя по-разному.

Пользовательские функции выдают a TypeError, но внутренние функции ведут себя по-разному, в зависимости от нескольких условий. Во всяком случае, типичным поведением является предупреждение и возврат null. Смотрите следующий пример в PHP 7.4:

var_dump(strlen(new stdClass));

 

Это приведет к следующему предупреждению:

Warning: strlen() expects parameter 1 to be string, object given in /path/to/your/test.php on line 4
NULL

 

Если strict_types включен или информация аргумента указывает типы, поведение будет другим. В таких случаях ошибка типа обнаруживается и приводит к TypeError.

Такая ситуация может привести к ряду проблем, хорошо объясненных в разделе вопросов RFC.

Чтобы устранить эти несоответствия, этот RFC предлагает сделать API-интерфейсы для внутреннего анализа параметров всегда генерировать ThrowErrorв случае несоответствия типов параметров.

В PHP 8 приведенный выше код выдает следующую ошибку:

Fatal error: Uncaught TypeError: strlen(): Argument #1 ($str) must be of type string, object given in /path/to/your/test.php:4
Stack trace:
#0 {main}
  thrown in /path/to/your/test.php on line 4

 

Выражения

В PHP throw это утверждение, поэтому его нельзя использовать там, где разрешено только выражение.

Этот RFC предлагает преобразовать оператор throw в выражение, чтобы его можно было использовать в любом контексте, где выражения разрешены. Например, функции стрелок, нулевой оператор объединения, троичные операторы и операторы Элвиса и т. д.

Смотрите следующие примеры из RFC:

$callable = fn() => throw new Exception();

// $значение не может быть обнулено.
$value = $nullableValue ?? throw new InvalidArgumentException();
 
// $ценность - это истина.
$value = $falsableValue ?: throw new InvalidArgumentException();

 

Начало статьи: Что нового в PHP 8 (функции, улучшения и JIT-компилятор)

Продолжение: Что нового в PHP 8 (функции, улучшения и JIT-компилятор). Часть 3

Exit mobile version