it-swarm.com.ru

Правильный способ справиться PHP 7 типов возврата

Я занят созданием приложения и хочу использовать PHP 7 типов возврата. Теперь я прочитал на php.net, что это было дизайнерское решение, что нельзя возвращать null, когда определен тип возврата.

Как правильно это сделать?

Один из вариантов - блок try ... catch:

public function getMyObject() : MyObject
{ 
     return null;
}

try
{
    getMyObject();
}
catch(Exception $e)
{
    //Catch the exception
}

У меня нет хорошего представления об этом, потому что мой код будет огромным блоком try ... catch, поскольку мне нужно написать блок try ... catch для каждого метода, который возвращает объект.

Null Object Pattern является хорошим решением для этого, но мне не нравится идея создать NullObject для каждого объекта в моем приложении. Есть ли правильный способ сделать это? 

27
S.Pols

У меня нет хорошего представления об этом, потому что мой код будет огромным блоком try/catch, так как мне нужно написать блок try/catch для каждого метода, который возвращает объект.

Альтернативой (с вашим кодом) будет всегда проверять возвращаемое значение для null, чтобы не получать ошибок при использовании null в качестве объекта.

Использование исключений является более выразительным и делает ошибки видимыми на ранней стадии, что хорошо. Поэтому либо убедитесь, что ваш метод всегда возвращает объект, либо сгенерируйте исключение, если он не может выполнить свой контракт. В противном случае тип возвращаемого значения не имеет реального значения.

6
Fabian Schmengler

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

Однако вы должны бросить это исключение самостоятельно.

public function getMyObject() : MyObject
{
    throw new MyCustomException("Cannot get my object");
}

Затем вы можете поймать это конкретное исключение и действовать соответственно.

27
Niet the Dark Absol

Вы можете использовать Option тип данных.

Этот тип имеет Something или Nothing в качестве обернутого значения.

Вы можете найти более подробное описание здесь и одну из открытых реализаций в PHP здесь

PS Пожалуйста, не используйте исключения, параметры или дополнительные методы для такой логики, это неправильный инструмент для работы.

6
nikita2206

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

На данный момент, ваши варианты:

1. Удалите тип возврата из сигнатуры вашего метода:

public function getMyObject()
{ 
     return null;
}

2. Бросьте и поймайте исключение (согласно ответу @ NiettheDarkAbsol):

public function getMyObject() : MyObject
{
    throw new MyCustomException("Cannot get my object");
}

3. Рефакторинг на два метода:

private $myObject;
//TODO think of a better method name
public function canGetMyObject() : bool
{
    $this->myObject = new myObject();
    return true;
}

public function getMyObject() : MyObject
{
    if(!$this->myObject){
        throw new MyCustomException("Cannot get my object");
    }
    return $this->myObject;
}

Телефонный код:

if($cls->canGetMyObject()){
    $myObject  = $cls->getMyObject();
}

4. Используйте тип возврата bool и выходной параметр:

public function tryGetMyObject(&$out) : bool
{
    $out = new myObject();
    return true;
}

Телефонный код:

$myObject = null;

if($cls->tryGetMyObject($myObject)){      
    $myObject->someMethod(); //$myObject is an instance of MyObject class
}

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

3
Steve

Начиная с PHP7.1, вы можете использовать типы возвращаемых значений:

public function getMyObject(): ?MyObject {
  return null;
}

http://php.net/manual/en/migration71.new-features.php

1
Jared

Я собирался попросить то же самое. Я не вижу особого смысла для этого.

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

<?php

class User
{
    protected $name;

    /**
     * User constructor.
     *
     * @param $name
     */
    public function __construct(string $name)
    {
        $this->name = $name;
    }

    /**
     * @return mixed
     */
    public function getName() : string
    {
        return $this->name;
    }
}


function findUserByName(string $name, array $users) : User {
    foreach ($users as $user) {
        if ($user->getName() == $name) {
            return $user;
        }
    }

    return null;
}


$users = [
    new User('John'), new User('Daniel')
];

$user = findUserByName('John', $users);

echo $user->getName()."\n";

$user2 = findUserByName('Micheal', $users);

Я хотел бы сказать Php, что я могу получить либо пользователя, либо ноль. Теперь мне нужно всегда оборачивать его в блок try try, и я не вижу особого смысла объявлять тип возвращаемого значения в этом случае. С другой стороны, если бы моя функция findUserByName была сложной, я мог бы случайно вернуть, например, имя пользователя вместо полного объекта пользователя, поэтому я хотел бы определить тип возвращаемого значения.

Я думаю, что более разумным было бы определение нескольких типов возвращаемых значений для функций. Иногда вы создаете функцию, которая возвращает один объект или массив объектов, и было бы неплохо определить, эта функция должна возвращать, например, User или массив пользователей: User[]

В случае OP (а также и моего) мы могли бы объявить тип возвращаемого значения как User, null, и это решило бы проблему.

0
Marcin Nabiałek