PHP多个if/elseif和错误消息/处理最佳实践

fre*_*red 4 php error-handling if-statement

这是一个我经常遇到的问题,我从来没有找到/想出最佳实践情况.例外可能是要走的路,但是我正在使用的应用程序没有使用它们,所以我试图坚持使用当前使用的方法.

如果需要检查3,4,5或更多不同的条件并且设置了错误消息或处理继续,则在语句,返回,消息等中布置if的最佳方法是什么.最好的做法是在代码开头实际进行所有错误检查吗?

这是一个真实世界类型条件的例子.

function process($objectId,$userId,$newData)
{
    $error = '';
    if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId))
    {
        if($this->isValid($newData))
        {
            if($object->isWriteable())
            {
                if($object->write($newData))
                {
                    // No error. Success!
                }
                else
                {
                    $error = 'Unable to write object';
                }
            }
            else
            {
                $error = 'Object not writeable';
            }
        }
        else
        {
            $error = 'Data invalid';
        }
    }
    else
    {
        $error = 'Object invalid';
    }
    return $error;
}
Run Code Online (Sandbox Code Playgroud)

要么

function process($objectId,$userId,$newData)
{
    $error = '';
    if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId))
    {
        $error = 'Object invalid';
    }
    elseif(!$this->isValid($newData))
    {
        $error = 'Data invalid';
    }
    elseif(!$object->isWriteable())
    {
         $error = 'Object not writeable';
    }
    elseif(!$object->write($newData))
    {
        $error = 'Unable to write to object';
    }
    else
    {
        // Success!
    }
    return $error;
}
Run Code Online (Sandbox Code Playgroud)

我很清楚,在这种情况下,选项2是要走的路.它更清晰.现在,我们可以让它更复杂一点:

function process($objectId,$userId,$newData)
{
    $error = '';
    if(($object = $this->getObject($objectId)) && $object->userOwnsObject($userId))
    {
        $this->setValidationRules();
        $parent = $object->getParentObject();
        $parent->prepareForChildUpdate();

        if($this->isValid($newData,$parent))
        {
            $newData = $this->preProcessData($newData);

            if($object->isWriteable())
            {
                // doServerIntensiveProcess() has no return value and must be done between these two steps
                $this->doServerIntensiveProcess();

                if($object->write($newData))
                {
                    // No error. Success!
                    $parent->childUpdated();
                }
                else
                {
                    $error = 'Unable to write object';
                }
            }
            else
            {
                $error = 'Object not writeable';
            }
        }
        else
        {
            $error = 'Data invalid';
        }
    }
    else
    {
        $error = 'Object invalid';
    }
    return $error;
}
Run Code Online (Sandbox Code Playgroud)

或者这有一些问题

function process($objectId,$userId,$newData)
{
    $error = '';
    if((!$object = $this->getObject($objectId)) && !$object->userOwnsObject($userId))
    {
        $error = 'Object invalid';
    }
    // Is it wrong to hate multi-line conditionals?
    elseif(!$this->setValidationRules() || (!$parent = $object->getParentObject()) ||
        !$parent->prepareForChildUpdate() || !$this->isValid($newData,$parent))
    {
        $error = 'Data invalid';
    }
    elseif((!$newData = $this->preProcessData($newData)) || !$object->isWriteable())
    {
         $error = 'Object not writeable';
    }
    // Where does doServerIntensiveProcess() with no return value go??
    elseif(!$object->write($newData))
    {
        $error = 'Unable to write to object';
    }
    else
    {
        // Success!
         $parent->childUpdated();
    }
    return $error;
}
Run Code Online (Sandbox Code Playgroud)

我只是不确定处理这个嵌套的最佳方法 - 如果那么那么做 - 然后 - 如果这样那么做 - 那种功能.感谢您提供的任何见解!

Rob*_*itt 5

我倾向于保持代码清洁是这样的:

function process($objectId,$userId,$newData)
{
    $object = $this->getObject($objectId);

    if($object === false)
    {
        return "message";
    }

    if($object->userOwnsObject($userId) === false)
    {
        return "message";
    }

    if($this->setValidationRules() === false)
    {
        return "unable to set validation rules";
    }

    if(false !== ($parent = $object->getParentObject()))
    {
        return "unable to get parent object";
    }

    /*... etc ...*/

    //if your here the all the checks above passed.
}
Run Code Online (Sandbox Code Playgroud)

通过这样做你也可以节省你的资源直接返回到位,代码看起来更干净,不需要2个巢

但是如果你从头开始构建函数我不明白为什么你不能在你的新代码中使用异常,它不会干扰当前的应用程序,并使生活更简单

function process($objectId,$userId,$newData)
{    
    if(false !== ($parent = $object->getParentObject()))
    {
          throw Exception("unable to get parent object");
    }

    /*... etc ...*/
}
Run Code Online (Sandbox Code Playgroud)

try
{
    $this->process(....);
}
catch(Exception $e)
{
    show_error_page('invalid.php',$e);
}
Run Code Online (Sandbox Code Playgroud)

或者另一种方法是使用一个名为InternalErrorlike 的静态方法创建一个错误处理类

abstract class Error
{
    public static InternalError(Exception $Ex)
    {
        Logger::LogException($Ex);
        //Then flush all buffers and show internal error,
    }
}
Run Code Online (Sandbox Code Playgroud)

所以不是上面的show_error_page,你可以这样做:

try
{
    $this->process(....);
}
catch(Exception $e)
{
    Error::InternalError($e); //this provides user with an interface to report the error that has just been logged.
}
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您Exception的所有人都可以在您的管理系统中进行记录,这意味着您可以更快地跟踪错误,而不是依赖会员明显地看到错误,而是通过电子邮件表单向他们道歉,要求他们描述他们的错误.尝试这样做,错误ID将附加到表单,因此您可以跟踪用户的错误.

这是IMO错误处理的最佳形式.