Реализация функции php переопределяет ненулевой аргумент - PullRequest
0 голосов
/ 19 ноября 2018

Я столкнулся с этим сегодня с PHP (по крайней мере, 7.1 и 7.2) со следующим кодом:

namespace PlaceHolderX\Tests\PHPUnit\Unit;

use PHPUnit\Framework\TestCase;

final class BreakingClassesTest extends TestCase
{

    public function testBreak(): void
    {
        $tester = new SomeClassA();
        $tester->test();
        $this->assertNull($tester->get());
    }

}

interface InterfaceA {

    public function test(string $testString): void;

}

class SomeClassA implements InterfaceA
{
    /** @var null|string */
    private $testString;

    public function test(string $testString = null): void
    {
        $this->testString = $testString;
    }

    public function get(): ?string
    {
        return $this->testString;
    }
}

Итак, у меня есть интерфейс (InterfaceA), у которого есть метод, который требует строку,Этот аргумент не может быть пустым, потому что, если бы я хотел, я бы указал его следующим образом:

public function test(?string $testString): void;

Но в классе реализации (SomeClassA) я могу переопределить определение аргумента значением по умолчанию, равным нулю, что приводит кповедение, которое я не намеревался использовать в своем интерфейсе.

Итак, мой главный вопрос: почему это возможно?Конечно, нам нужно будет проверить это в обзорах кода, но это легко упустить.

Я попытался найти причину такого поведения, но не смог найти объяснения.Возможно, мои критерии поиска выключены.

Ответы [ 3 ]

0 голосов
/ 19 ноября 2018

В PHP7.2 реализовано расширение типов параметров. Это своего рода противоречие. К сожалению, PHP в настоящее время не поддерживает противоречивость параметров, но в проекте есть и rfc для поддержки этого.

Основная идея такова: если у вас есть дочерний класс, вы можете использовать более широкие типы параметров в дочернем классе. Для типов возврата действует противоположность (ковариация).

Если это хорошая или плохая практика, зависит от ваших потребностей. Насколько я знаю, другие языки ведут себя так же.

Для дальнейшего чтения есть два rfc:

0 голосов
/ 22 ноября 2018

Суперкласс должен быть заменен его подклассами. Так что подкласс должен уметь делать все, что делает суперкласс, но он также может делать больше.

В вашем примере суперкласс / интерфейс не знает, как обрабатывать нуль. Но подкласс делает, и это хорошо, потому что пользователи суперкласса будут передавать только ненулевые, поскольку они думают, что контракт суперкласса действует.

0 голосов
/ 19 ноября 2018

PHP позволяет устанавливать или изменять значения по умолчанию в реализациях, если тип совпадает. Одно предостережение: все типы с подсказками допускают значение NULL в качестве значения по умолчанию.

Если кто-то найдет конкретное объяснение этому, я могу обновить этот ответ, но до 7.1 единственным способом объявить необязательный параметр было присвоить значение по умолчанию, равное нулю. Синтаксис ?string не существует, поэтому такое поведение может быть следствием этого и все еще существует для обратной совместимости.

Если вы попытаетесь установить значение по умолчанию, скажем, целое число, вы увидите сообщение об ошибке, которое показывает:

Fatal error: Default value for parameters with a string type can only be string or NULL

На данный момент разработчик, по-видимому, несет ответственность за то, чтобы значение реализации по умолчанию соответствовало объявлению интерфейса, допускающему или допускающему нулевое значение.

...