본문 바로가기
PHP

[PHP] 사용자 정의 오류 처리기[set_error_handler()] 정의하기

by teamnova 2023. 10. 31.

오늘은  PHP에서 스크립트 실행 중 발생하는 오류 일부를 잡아내서 처리한 뒤 다음 코드를 계속 실행하게 만들어주는 메소드에 대해 알아보고 예제를 만들어 보겠습니다.

 

사용자 정의 오류 처리기는 다음과 같이 등록할 수 있습니다.

set_error_handler(?callable $callback, int $error_levels = E_ALL): string|array|object|null

$callback : 콜백 메소드는 해당 에러를 잡았을 때 실행할 메소드를 설정

$error_levels = E_ALL : 에러 레벨 같은 경우 해당 오류 처리기가 처리할 오류의 종류를 설정, 이때 $error_levels 같은 경우 매개변수로 값을 주지 않았을 때 기본값인 E_ALL로 설정됩니다.

또한 사용자 정의 오류 처리기는 E_ERROR, E_PARSE, E_CORE_ERROR, E_CORE_WARNING, E_COMPILE_ERROR, E_COMPILE_WARNING 에러를 처리할 수 없고 E_STRICT의 경우 set_error_handler() 메소드가 호출된 파일에서 발생한 경우만 처리할 수 있습니다.

 

콜백 메소드(첫번째 매개변수)는 다음과 같이 정의할 수 있습니다.

handler(
    int $errno,
    string $errstr,
    string $errfile = ?,
    int $errline = ?,
    array $errcontext = ?
): bool

EXAMPLE
function myErrorHandler($errno, $errstr, $errfile, $errline){
// code to execute
}

$errno: 발생한 오류 수준을 정수로 전달

$errstr: 오류 메시지를 문자열로 전달

$errfile: 오류가 발생한 파일 이름이 문자열로 전달(필수 매개변수 X)

$errline: 오류가 발생한 행 번호가 정수로 전달(필수 매개변수 X)

$errcontext: 오류가 발생한 지점의 활성 기호 테이블을 가리키는 배열이 전달, 즉 errcontext는 오류가 발생한 범위에 존재하는 모든 변수의 배열을 포함한다(PHP 7.2 버전부터 deprecated 되었고 PHP 8.0 버전부터 제거되었습니다)

위 메소드에서 true를 반환하면 해당 오류를 적절히 처리했으니 추가 처리가 필요하지 않음을 의미하고 반대로 false를 반환하거나 아무것도 반환하지 않는다면 false를 반환한 것으로 간주해 추가적인 오류 처리를 지시할 수 있습니다.

 

에러 레벨(두번째 매개변수)에는 다음과 같이 총 16가지가 있습니다.

Error Level Value Description

E_ERROR 1 복구할 수 없는 치명적인 런타임 오류로, 스크립트 실행이 즉시 중지됩니다.
E_WARNING 2 런타임 경고로, 비 치명적이며 대부분의 오류가 이 범주에 속합니다. 스크립트 실행은 중지되지 않습니다.
E_PARSE 4 컴파일 시간에 발생하는 구문 분석 오류로, 파서에 의해서만 생성됩니다.
E_NOTICE 8 런타임 알림으로, 스크립트가 정상적으로 실행될 때 발생할 수 있는 오류 상황을 나타냅니다.
E_CORE_ERROR 16 PHP 엔진 초기 시작 중에 발생하는 치명적인 오류입니다. 이는 E_ERROR와 유사하지만 PHP의 코어에서 생성됩니다.
E_CORE_WARNING 32 PHP 엔진 초기 시작 중에 발생하는 비 치명적인 오류입니다. 이는 E_WARNING과 유사하지만 PHP의 코어에서 생성됩니다.
E_COMPILE_ERROR 64 스크립트 컴파일 중에 발생하는 치명적인 오류입니다. 이는 E_ERROR와 유사하지만 Zend 스크립팅 엔진에서 생성됩니다.
E_COMPILE_WARNING 128 스크립트 컴파일 중에 발생하는 비 치명적인 오류입니다. 이는 E_WARNING과 유사하지만 Zend 스크립팅 엔진에서 생성됩니다.
E_USER_ERROR 256 사용자가 생성한 치명적인 오류 메시지로, trigger_error() 함수를 사용하여 PHP 코드에서 생성됩니다.
E_USER_WARNING 512 사용자가 생성한 비 치명적인 경고 메시지로, trigger_error() 함수를 사용하여 PHP 코드에서 생성됩니다.
E_USER_NOTICE 1024 사용자가 생성한 알림 메시지로, trigger_error() 함수를 사용하여 PHP 코드에서 생성됩니다.
E_STRICT 2048 엄격한 의미에서의 오류는 아니지만 PHP가 문제나 미래 호환성으로 이어질 수 있는 코드를 만났을 때 발생합니다.
E_RECOVERABLE_ERROR 4096 잡을 수 있는 치명적인 오류로, 오류는 치명적이었지만 PHP 엔진을 불안정한 상태로 만들지 않았습니다. 사용자 정의 오류 처리기에 의해 잡히지 않으면 (set_error_handler() 참조), 응용 프로그램은 E_ERROR처럼 중단됩니다.
E_DEPRECATED 8192 미래의 PHP 버전에서 코드가 작동하지 않을 것임을 나타내는 런타임 알림입니다.
E_USER_DEPRECATED 16384 사용자가 생성한 경고 메시지로, trigger_error() 함수를 사용하여 PHP 코드에서 생성됩니다.
E_ALL 32767 모든 에러와 경고를 나타냅니다, 5.4.0 버전에서는 E_STRICT 레빌을 제외합니다.

 

 

위 내용을 바탕으로 예제를 하나 만들어 보겠습니다.

// 사용자 정의 에러핸들러
function myErrorHandler($errno, $errstr, $errfile, $errline)
{
    // 사용자가 만든 에러핸들러에서 처리할 수 있는지 확인
    if (!(error_reporting() & $errno)) {
	// error_reporting() 메소드는 에러가 있다면 에러레벨의 수준에 해당하는 값을 반환하고 없다면 0을 반환
	// $errno은 에러레벨의 수준을 반환하는데 에러가 없다면 0을 가짐
	// 둘 중 하나 또는 둘 다 0을 반환한다면 false를 반환해 추가적인 에러처리가 필요함을 알림
        return false;
    }

    // $errstr에 있는 HTML 코드가 실행되지 않게 문자열로 그자체로 사용할 수 있게 변환
    $errstr = htmlspecialchars($errstr);

    switch ($errno) { // 입력된 $errno에 따라서 코드를 실행
    case E_USER_ERROR:
        echo "<b>My ERROR</b> [$errno] $errstr<br />\\n";
        echo "  Fatal error on line $errline in file $errfile";
        echo ", PHP " . PHP_VERSION . " (" . PHP_OS . ")<br />\\n";
        echo "Aborting...<br />\\n";
        exit(1);

    case E_USER_WARNING:
        echo "<b>My WARNING</b> [$errno] $errstr<br />\\n";
        break;

    case E_USER_NOTICE:
        echo "<b>My NOTICE</b> [$errno] $errstr<br />\\n";
        break;

    default:
        echo "Unknown error type: [$errno] $errstr<br />\\n";
        break;
    }

    // 에러를 처리한 뒤 true를 반환해 추가적인 에러처리가 필요없음을 알림
    return true;
}

// 사용자 정의 에러핸들러를 테스트하기 위한 메소드
function scale_by_log($vect, $scale)
{
    if (!is_numeric($scale) || $scale <= 0) {
		// trigger_error() 메소드를 사용해 에러 발생
        trigger_error("log(x) for x <= 0 is undefined, you used: scale = $scale", E_USER_ERROR);
    }

    if (!is_array($vect)) {
		// trigger_error() 메소드를 사용해 에러 발생
        trigger_error("Incorrect input vector, array of values expected", E_USER_WARNING);
        return null;
    }

    $temp = array();
    foreach($vect as $pos => $value) {
        if (!is_numeric($value)) {
			// trigger_error() 메소드를 사용해 에러 발생
            trigger_error("Value at position $pos is not a number, using 0 (zero)", E_USER_NOTICE);
            $value = 0;
        }
        $temp[$pos] = log($scale) * $value;
    }

    return $temp;
}

// 사용자 정의 에러핸들러 설정 및 참조
$old_error_handler = set_error_handler("myErrorHandler");

//  에러를 발생시키기 위해 숫자가 아닌 아이템을 갖고 있는 첫번째 배열을 만듬
echo "vector a\\n";
$a = array(2, 3, "foo", 5.5, 43.3, 21.11);
print_r($a);

// 두번째 배열 생성하며 scale_by_log() 메소드 사용
echo "----\\nvector b - a notice (b = log(PI) * a)\\n";
$b = scale_by_log($a, M_PI);
print_r($b);

// 에러를 발생시키기 위해 scale_by_log() 메소드 첫번째 매개변수에 의도적으로 배열이 아닌 string을 입력
echo "----\\nvector c - a warning\\n";
$c = scale_by_log("not array", 2.3);
var_dump($c); // NULL

// 치명적인 에러를 발생시키기 위해 scale_by_log() 메소드 두번째 매개변수에 음수를 입력
echo "----\\nvector d - fatal error\\n";
$d = scale_by_log($a, -2.5);
var_dump($d); // 치명적 에러 발생 후 exit(1) 메소드가 실행되어 절대로 실행될 수 없는 코드

이렇게 set_error_handler() 메소드를 사용해 사용자가 원하는 대로 PHP에서 발생하는 에러들을 처리하는 방법을 알아보았습니다.

다음에는 에러가 아니라 예외를 처리하는 set_exception_handler() 사용법을 알아보겠습니다.