PHP中带有自定义ID和自定义消息的错误处理

时间:2019-03-18 08:31:48

标签: php class error-handling

我在一个项目中,我不想直接向用户抛出错误。相反,我想要针对错误的自定义消息。

稍后,我还需要保留一个错误号,以便从类外部自定义错误消息,例如错误消息数组。

所以我做了我自己的事情,在其中设置$error = null,然后将error设置为一个数字,该数字后来成为消息。

问题

这种方法是否有缺点?我更适合尝试/捕捉还是其他?我希望代码尽量简洁明了。

在此简短代码示例中,错误处理似乎是该类的重要组成部分。在我的几百行真实代码中,它并不是整个代码的很大部分

http://sandbox.onlinephpfunctions.com/code/623b388b70603bf7f020468aa9e310f7340cd108

<?php
class Project {
  private $error = null;

  public function callMeFirst($num) {
    $this->nestedLevelOne($num);
    $this->nestedLevelTwo($num);
    $this->setResults();
  }

  public function callMeSecond($num) {
    $this->nestedLevelTwo($num);
    $this->setResults();
  }

  private function nestedLevelOne($num) {
    // Do stuff
    if($num !== 1) {
      $this->error = ['id' => 1, 'value' => $num];
    }
  }

  private function nestedLevelTwo($num) {
    // Do stuff
    if($num !== 20) {
      $this->error = ['id' => 2, 'value' => $num];
    }
  }

  private function message($args) {
    extract($args);

    $message = [
      1 => "Nested level one error: $value",
      2 => "Another error at level two: $value",
    ];

    return ['id' => $id, 'message' => $message[$id]];
  }

  private function setResults() {
    $results['success'] = ($this->error === null) ? true : false;

    if($this->error !== null) {
      $results['error'] = $this->message($this->error);
    }
    $this->results = $results;
  }
}

$project = new Project();
$project->callMeFirst(1);
$project->callMeFirst(2);

print_r($project->results);

它将输出

Array
(
  [success] => 
  [error] => Array
    (
      [id] => 2
        [message] => Another error at level two: 2
    )
)

我要问的原因是我有一种感觉,在这种情况下我可能会重新发明轮子。是吗?

如果有更好的解决方案,我将很高兴看到该代码的外观。

1 个答案:

答案 0 :(得分:1)

我可能会将业务逻辑与错误处理分开,以进一步简化每个部分。通过使用异常,可以使业务逻辑更简单。只要遇到不允许的情况,您就简单地throw例外,从而完全避免进入任何不一致的状态。业务逻辑类不必关心如何进一步处理此错误,它只需要引发该错误即可。然后,您应该围绕该业务逻辑类创建一个单独的包装器,该包装器只关心处理任何错误并将其格式化为数组或其他类型的响应,这些响应将在其他地方处理。遵循以下原则:

class ProjectException extends Exception {
    public function __construct($num) {
        parent::__construct(get_called_class() . ": $num");
    }
}

class NestedLevelOneException extends ProjectException {
    // customise __construct here if desired
}

class NestedLevelTwoException extends ProjectException {}

class Project {
    public function callMeFirst($num) {
        $this->nestedLevelOne($num);
        $this->nestedLevelTwo($num);
    }

    public function callMeSecond($num) {
        $this->nestedLevelTwo($num);
    }

    protected function nestedLevelOne($num) {
        if ($num !== 1) {
            throw new NestedLevelOneException($num);
        }

        // do stuff
    }

    protected function nestedLevelTwo($num) {
        if ($num !== 20) {
            throw new NestedLevelTwoException($num);
        }

        // do stuff
    }
}

class ProjectService {
    protected $project;

    public function __construct(Project $project = null) {
        $this->project = $project ?: new Project;
    }

    public function process($a, $b) {
        try {
            $this->project->callMeFirst($a);
            $this->project->callMeSecond($b);
            return ['success' => true];
        } catch (ProjectException $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }
}

$api = new ProjectService;
print_r($api->process(1, 2));
  1. 通过定义三个单独的异常,您可以在处理错误的方式和位置上获得很大的灵活性。您可以专门捕获NestedLevel*Exception,也可以使用ProjectException捕获它们中的任何一个。

  2. 通过让您的方法引发异常,您可以获得灵活的错误处理可能性。您可以自由地不捕获异常并让程序终止,如果不满足您的业务要求之一,那将是完全合理的。或者,您可以在准备处理该错误的级别上捕获异常,并将其转变为可以采取措施的有意义的东西。

  3. 通过将错误消息的生成移到异常中,可以使错误类型及其消息保持独立。在一个地方,您可以定义可能发生的种错误及其错误消息的外观。而不是将其散布在整个代码库中。而且您仍然可以在用户界面中自由选择其他错误消息,例如用于将各种错误本地化为多种语言;只需检查异常对象的类型即可。

  4. 通过使用单独的ProjectService来处理这些异常并将其转换为数组响应,可以缩小每个类的responsibilities的范围,并使每个类更加灵活和简单。