PHP 예외 처리(Exception Handling)


예외(Exceptions)는 지정된 오류가 발생하는 경우 스크립트의 통상적인 흐름을 변경할 때 사용된다.


What is an Exception

PHP 5 에는 에러를 다루는 새로운 객체지향적 방법이 지원된다.

예외처리는 지정된(예외적인) 에러 조건이 발생하면 코드 실행의 통상적인 흐름을 변경하는데 사용된다. 이 조건을 예외(exception)라고 부른다.

예외가 발생(trigger) 될 때 일반적으로 다음이 발생한다.:

  • 현재 코드 상태가 저장된다.
  • 코드 실행은 미리 정의된(사용자 지정의) 예외 처리 함수로 전환된다. 
  • 상황에 따라서, 핸들러가 "저장된 코드 상태에서 다시 시작하기도 하고" / "스크립트 실행을 종료하거나" / "코드의 다른 위치에서 스크립트를 계속하기도" 한다. 

다양한 오류처리 방법들은 다음과 같다.:

  • 예외(Exceptions)의 기본 사용
  • 사용자 정의 예외 핸들러 만들기
  • 다중 예외(exceptions)
  • 예외를 다시 던지기(Re-throwing)
  • 최상위 예외 핸들러 설정

Note: 예외(Exceptions)는 오류상태에선만 사용되어야 하고, 코드의 특정 장소로 이동하는데 사용되어서는 안된다. 


Basic Use of Exceptions

예외가 발생되면(thrown), 다음에 따라오는 코드는 실행되지 않고, PHP 는 "catch" 블록을 찾기 위해 노력한다. 

예외가 잡히지 않으면(is not caught), 치명적인 오류가 "Uncaught Exception" 메시지와 함께 나타난다.

잡히지 않는 예외를 발생시키도록 해보자:

<?php
//create function with an exception
function checkNum($number) {
  if($number>1) {
    throw new Exception("Value must be 1 or below");
  }
  return true;
}

//trigger exception
checkNum(2);
?>

위의 코드는 다음과 같은 에러가 발생한다: ( @ .../apache2/logs/error.log )

Fatal error: Uncaught exception 'Exception'
with message 'Value must be 1 or below' in C:\webfolder\test.php:6
Stack trace: #0 C:\webfolder\test.php(12):
checkNum(28) #1 {main} thrown in C:\webfolder\test.php on line 6

Try, throw and catch

위의 예제에서 에러를 피하기 위해서는, 예외를 처리하기에 적합한 코드를 작성해야합니다. 

적절한 예외 코드는 다음을 포햠해야 한다:

  1. Try - 예외를 사용하는 함수는 "try" 블록 안에 놓여야 한다. 예외가 트리거되지 않으면 코드는 정상정으로 계속된다. 그러나 예외가 트리거되면, 예외가 "던져진다(thrown)"
  2. Throw - 이것은 예외를 트리거하는 방법이다. 각 "throw" 는 적어도 하나의 "catch"를 가져야 한다.
  3. Catch - "catch" 블록은 예외를 회수(검색)하여, 예외 정보를 포함하는 객체를 생성한다.

유효한 코드로 예외를 트리거하는 시도를 해보자 :

<?php
//create function with an exception
function checkNum($number) {
  if($number>1) {
    throw new Exception("Value must be 1 or below");
  }
  return true;
}

//trigger exception in a "try" block
try {
  checkNum(2);
  //If the exception is thrown, this text will not be shown
  echo 'If you see this, the number is 1 or below';
}

//catch exception
catch(Exception $e) {
  echo 'Message: ' .$e->getMessage();
}
?>

위의 코드는 다음과 같은 에러를 갖는다.:

Message: Value must be 1 or below

Example explained:

위의 코드는 예외를 던지고(exception) 그것을 잡는다(catches):

  1. checkNum() 함수가 생성된다. 숫자가 1보다 큰 지를 검사한다. 그렇다면, 예외(exception)를 던진다.
  2. checkNum() 함수는 "try" 블록 안에서 호출된다.
  3. checkNum() 함수 안에서 예외(exception) 가 던져진다.
  4. "catch" 블록은 예외(exception)를 검색하고 에외 정보를 포함하는 객체($e)를 생성한다
  5. 예외로부터의 에러 메시지가 예외 객체로부터 $e->getMessage() 를 호출하여 echo된다. 

그러나, "모든 던짐(throw)은 캐치(catch)를 가져야 함" 규칙을 해결할 방법은 통과하는 에러를 처리하기 위한 최상위 수준의 예외 처리기(top level exception handler)를 설정하는 것이다. 


사용자 정의 예외 클래스(Custom Exception Class) 만 들기

사용자 정의 예외 처리기를 만드는 것은 매우 간단하다. 우리는 단순히 PHP 에서 예외가 발생했을 때 호출될 수 있는 함수를 가진 특별한 클래스를 만들면 된다. 클래스는 예외 클래스의 확장(extension)이어야만 한다. 

사용자 정의 예외 클래스는 PHP 의 예외클래스(exception class)로부터 속성(properties)을 상속받으며, 그것에 사용자 정의 함수를 추가할 수 있다. 

예외 클래스를 만들어 보자 :

<?php
class customException extends Exception {
  public function errorMessage() {
    //error message
    $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
    .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
    return $errorMsg;
  }
}

$email = "someone@example...com";

try {
  //check if
  if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
    //throw exception if email is not valid
    throw new customException($email);
  }
}

catch (customException $e) {
  //display custom message
  echo $e->errorMessage();
}
?>

새로운 클래스는 추가로 errorMessage() 함수를 가진 기존의 예외 클래스의 복사본이다.  이는 기존의 클래스의 복사본이므로, 기존의 클래스로부터 속성과 메소드를 상속 받게 되고,  getLine() , getFile() 과 getMessage() 같은 예외 클래스의 메소드들을 사용할 수 있게 된다.

Example explained:

위의 코드는 예외를 던고(throws), 이를 사용자 정의 예외 클래스로 받는다(catches) :

  1. customException() class 는 기존의 exception class의 확장(extension)으로 생성된다. 이로써 기존의 exception class 로부터 모든 메소드와 속성을 상속받는다. 
  2. errorMessage() 함수가 만들어 집니다. 이 함수는 이-메일 주소가 유효하지 않으면 에러 메시지를 반환합니다.
  3. $email 변수는 유효하지 않은 이메일 주소인 문자열로 설정됩니다.
  4. "try" 블록이 실행되고, 이메일 주소가 유효하지 않으므로 예외가 던져집니다. 
  5. "catch" 블록이 예외를 잡아내어(catches) 에러 메시지를 표시한다.

Multiple Exceptions

한 스크립트가 여러 조건을 확인하기 위해서 여러 예외를 사용하는 것이 가능하다. 

여러  if..else 블록,  switch, 또는 중첩된 다중 예외를 사용할 수 있다. 이러한 예외들은 서로 다른 예외 클래스를 사용할 수 있으며, 서로 다른 에러 메시지를 반환 할 수 있다. :

<?php
class customException extends Exception {
  public function errorMessage() {
    //error message
    $errorMsg = 'Error on line '.$this->getLine().' in '.$this->getFile()
    .': <b>'.$this->getMessage().'</b> is not a valid E-Mail address';
    return $errorMsg;
  }
}

$email = "someone@example.com";

try {
  //check if
  if(filter_var($email, FILTER_VALIDATE_EMAIL) === FALSE) {
    //throw exception if email is not valid
    throw new customException($email);
  }
  //check for "example" in mail address
  if(strpos($email, "example") !== FALSE) {
    throw new Exception("$email is an example e-mail");
  }
}

catch (customException $e) {
  echo $e->errorMessage();
}

catch(Exception $e) {
  echo $e->getMessage();
}
?>

Example explained:

위의 코드는 두가지 조건을 시험하고 조건 중 하나가 충족되지 않으면 예외가 발생합니다.:

  1. customException() class 가 기존의 예외 클래스의 확장으로 생성됩니다. 이로써 기존의 exception class 로부터 모든 메소드와 속성을 상속받는다.
  2. errorMessage() 함수가 만들어 집니다. 이 함수는 이-메일 주소가 유효하지 않으면 에러 메시지를 반환합니다.
  3. $email 변수는 유효한 이메일 주소인 문자열로 설정되지만, 문자열 "example"을 포함합니다.
  4. "try"블록이 실행되고, 첫 번재 조건에서 예외가 발생하지 않습니다. 
  5. 두 번째 조건은 이메일에 문자열 "example"이 포함되어 있으므로 예외를 트리거합니다.
  6. "catch"블록이 예외를 잡아내어(catches) 정확한 에러 메시지를 표시한다.

던져진(thrown) 예외는 class customException의 것이고 customException catch 가 없다면, 단지 기본 예외 처리만 있으며, 예외는 거기에서 처리될 것이다.


Re-throwing Exceptions

예외가 발생 했을 때, 때때로 표준 방식이 아닌 다르게 그것을 처리하고 싶을 수 있다. 예외를 "catch" 블록 안에서 두 번 던질(throw) 수 있다.  

스크립트는 사용자에게 시스템 오류를 숨겨야 한다. 시스템 에러는 코드 작성자에게는 중요할 수 있으나, 사용자에게는 필요가 없다. 사용자에게 일을 더 쉽게 해주기 위해서 당신은 사용자 친화적인 메시지와 함께 예외를 다시-던질(re-throw) 수 있습니다. :

<?php
class customException extends Exception {
  public function errorMessage() {
    //error message
    $errorMsg = $this->getMessage().' is not a valid E-Mail address.';
    return $errorMsg;
  }
}

$email = "someone@example.com";

try {
  try {
    //check for "example" in mail address
    if(strpos($email, "example") !== FALSE) {
      //throw exception if email is not valid
      throw new Exception($email);
    }
  }
  catch(Exception $e) {
    //re-throw exception
    throw new customException($email);
  }
}

catch (customException $e) {
  //display custom message
  echo $e->errorMessage();
}
?>

Example explained:

위의 코드는 이메일 주소가 문자열 "example"을 포함하는지 시험하고, 그런 경우에는 예외를 다시-던진다(re-thrown).:

  1. customException() class 가 기존의 예외 클래스의 확장으로 생성됩니다. 이로써 기존의 exception class 로부터 모든 메소드와 속성을 상속받는다.
  2. errorMessage() 함수가 만들어 집니다. 이 함수는 이-메일 주소가 유효하지 않으면 에러 메시지를 반환합니다.
  3. $email  변수는 유효한 이메일 주소인 문자열로 설정되지만, 문자열 "example"을 포함합니다.
  4. "try" 블록은 예외를 다시-던질수(re-throw) 있게 하도록 또 다른 "try" 블록을 포함합니다.
  5. e-mail 이 문자열 "example" 을 포함하므로 예외가 트리거 됩니다.
  6. "catch" 블록이 예외를 잡아 "customException"을 다시 던집니다.
  7. "customException" 이 잡혀지며 에러메시지가 표시됩니다.

예외가 현재 "try" 블록에서 catch 되지 않으면, "상위레벨"("higher levels")의 블록을 검색하게 됩니다. .


Set a Top Level Exception Handler

set_exception_handler() 함수는 모든 캐치되지 않은 예외를 처리하기위한 사용자-정의 함수를 설정합니다. 

<?php
function myException($exception) {
  echo "<b>Exception:</b> " . $exception->getMessage();
}

set_exception_handler('myException');

throw new Exception('Uncaught Exception occurred');
?>

위의 코드의 출력은 다음과 같아야 합니다. :

Exception: Uncaught Exception occurred

위의 코드에는 "catch" 클록이 없습니다. 대신에, 최고 레밸의 예외처리가 트리거됩니다. 이 함수는 캐치되지 않은 예외를 캐치하는데 사용되야 합니다.


Rules for exceptions

  • 코드는 잠재적 예외를 캐치하는 것을 돕기 위해서 블록에 둘러싸여 있을 수 있다.
  • 각 try 블록 또는 "throw" 는 적어도 하나의 대응하는 캐치블록이 있어야 한다. 
  • 다중 캐치 블록은 예외의 여러 다른 클래스를 캐치하는데 사용될 수 있다.
  • 예외는 try  블록 안의 캐치 블록에서 던져질 (또는 다시-던져질) 수 있다.

간단한 규칙: 당신이 무언가는 던지면(throw), 그것을 잡아야(catch)만 합니다.