PHP의 매직 메소드와 관련하여`trigger_error`와`throw Exception`


13

마술 방법trigger_error 의 맥락에서 올바른 사용법 (있는 경우)에 대해 동료와 토론하고 있습니다 . 첫째, 나는 이 경우를 제외하고trigger_error피해야 한다고 생각합니다 .

하나의 메소드를 가진 클래스가 있다고 가정 해보십시오. foo()

class A {
    public function foo() {
        echo 'bar';
    }
}

이제 똑같은 인터페이스를 제공하고 마술 메서드를 사용하여 모든 메서드 호출을 포착한다고 가정 해 봅시다.

class B {
    public function __call($method, $args) {
        switch (strtolower($method)) {
        case 'foo':
            echo 'bar';
            break;
        }
    }
}

$a = new A;
$b = new B;

$a->foo(); //bar
$b->foo(); //bar

두 클래스는 응답 방식이 동일 foo()하지만 유효하지 않은 메소드를 호출 할 때 다릅니다.

$a->doesntexist(); //Error
$b->doesntexist(); //Does nothing

내 주장은 마법의 메소드는 trigger_error 알 수없는 방법이 잡힐 때 것입니다

class B {
    public function __call($method, $args) {
        switch (strtolower($method)) {
        case 'foo':
            echo 'bar';
            break;
        default:
            $class = get_class($this);
            $trace = debug_backtrace();
            $file = $trace[0]['file'];
            $line = $trace[0]['line'];
            trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);
            break;
        }
    }
}

두 클래스가 (거의) 동일하게 동작하도록

$a->badMethod(); //Call to undefined method A::badMethod() in [..] on line 28
$b->badMethod(); //Call to undefined method B::badMethod() in [..] on line 32

내 유스 케이스는 ActiveRecord 구현입니다. 내가 사용하는 __call본질적으로 같은 일을하지만 같은 수정을 캐치와 손잡이 방법으로 Distinct또는 Ignore, 예를 들어,

selectDistinct()
selectDistinctColumn($column, ..)
selectAll()
selectOne()
select()

또는

insert()
replace()
insertIgnore()
replaceIgnore()

방법처럼 where(),from() , groupBy(), 등 하드 코딩입니다.

실수로 전화하면 내 주장이 강조됩니다 insret() . 내 활성 레코드 구현이 모든 메소드를 하드 코딩하면 오류가 발생합니다.

좋은 추상화와 마찬가지로 사용자는 구현 세부 사항을 알지 못하고 인터페이스에만 의존해야합니다. 매직 메소드를 사용하는 구현이 다르게 동작해야하는 이유는 무엇입니까? 둘 다 오류 여야합니다.

답변:


7

동일한 액티브 인터페이스의 두 구현을 ( select(), where()등)

class ActiveRecord1 {
    //Hardcodes all methods
}

class ActiveRecord2 {
    //Uses __call to handle some methods, hardcodes the rest
}

예를 들어 ActiveRecord1::insret(), 첫 번째 클래스에서 잘못된 메소드를 호출하면 기본 PHP 동작은 오류 를 트리거하는 것 입니다. 유효하지 않은 함수 / 메소드 호출은 합리적인 응용 프로그램이 포착하고 처리하려는 조건이 아닙니다. 물론, 루비 나 파이썬과 같은 언어로 오류를 잡을 수 있습니다. 예외 인 있지만 다른 언어 (JavaScript / 정적 언어 등)는 실패합니다.

PHP로 돌아 가기-두 클래스가 모두 동일한 인터페이스를 구현한다면 왜 같은 동작을 나타내지 않아야합니까?

경우 __call또는 __callStatic잘못된 방법을 발견, 그들은 언어의 모방 기본 동작에 오류를 트리거해야

$class = get_class($this);
$trace = debug_backtrace();
$file = $trace[0]['file'];
$line = $trace[0]['line'];
trigger_error("Call to undefined method $class::$method() in $file on line $line", E_USER_ERROR);

나는 예외에 대해 오류를 사용 해야하는지 여부를 논쟁하지 않고 있지만 (100 %는 안된다) PHP의 마법 방법은 예외입니다-말장난 :)- 언어의 맥락 에서이 규칙


1
미안하지만 나는 그것을 사지 않습니다. PHP에서 클래스가 처음 구현되었을 때 10 년 전에 PHP에 예외가 없었기 때문에 왜 우리는 양이어야 4.something합니까?
Matthew Scharley

일관성을 위해 @Matthew. 나는 예외 대 오류에 대해 토론하지 않고 (토론은 없습니다) 또는 PHP가 설계에 실패했는지 (그렇지 않습니다), 나는이 독특한 상황에서 일관성 을 위해 언어의 행동을 모방하는 것이 가장 좋습니다
chriso

일관성이 항상 좋은 것은 아닙니다. 일관되지만 열악한 디자인은 여전히 ​​열악한 디자인으로 하루 종일 사용하기가 어렵습니다.
Matthew Scharley

@Matthew는 사실이지만 잘못된 메서드 호출에서 오류를 발생시키는 IMO는 디자인이 좋지 않습니다. 잘못된 메소드 호출 을 잡아서 처리하려면?
chriso

@ chriso : 우주는 충분히 커서 당신이나 내가 그런 일이 일어나기를 꿈꾸지 않을 유스 케이스가 있습니다. __call()동적 라우팅을 수행 하는 데 사용 하고 있습니다. 실제로 누군가가 실패한 경우를 처리하고 싶을지도 모릅니다. 어쨌든, 이것은 원으로 가고 있으므로 이것이 나의 마지막 주석이 될 것입니다. 하루가 끝날 때, 당신은 무엇을할지 판단하십시오. 더 나은 지원 대 일관성. 특별한 처리가없는 경우 두 방법 모두 응용 프로그램에 동일한 영향을 미칩니다.
Matthew Scharley

3

나는 나의 의견이있는 의견을 거기에 버릴 것입니다. 그러나 만약 당신이 trigger_error어디서나 사용한다면 , 당신은 무언가 잘못하고있는 것입니다. 예외는 갈 길입니다.

예외의 장점 :

  • 잡힐 수 있습니다. 이것은 큰 이점이며 필요한 유일한 것이어야합니다. 사람들은 무언가 잘못 될 가능성이 있다고 생각하면 실제로 다른 것을 시도 할 수 있습니다. 오류는 당신에게이 기회를주지 않습니다. 사용자 지정 오류 처리기를 설정하더라도 단순히 예외를 잡기 위해 촛불을 들지 않습니다. 귀하의 질문에 대한 귀하의 의견과 관련하여, '합리적인'신청서는 전적으로 신청서의 내용에 달려 있습니다. 사람들은 자신의 경우에는 결코 일어날 수 없다고 생각하면 예외를 잡을 수 없습니다. 사람들에게 선택권을주지 않는 것은 Bad Thing ™입니다.
  • 스택 추적. 문제가 발생하면 문제가 발생한 장소상황 을 알 수 있습니다 . 일부 핵심 방법에서 오류가 발생하는 위치를 추적하려고 시도한 적이 있습니까? 매개 변수가 너무 적은 함수를 호출하면 쓸모없는 오류가 발생하여 호출하는 메소드의 시작을 강조 표시하고 호출 위치를 완전히 생략합니다.
  • 명쾌함. 위의 두 가지를 결합하면 더 명확한 코드를 얻을 수 있습니다. 사용자 지정 오류 처리기를 사용하여 오류를 처리 (예 : 오류에 대한 스택 추적 생성)하려고하면 오류가 실제로 생성되는 위치가 아닌 모든 오류 처리가 하나의 함수에 있습니다.

존재하지 않는 메소드를 호출하여 문제를 해결하는 것은 유효한 가능성 일 수 있습니다 . 이것은 전적으로 작성하는 코드의 컨텍스트에 따라 다르지만 이러한 상황이 발생할 수 있습니다. 정확한 사용 사례를 해결하기 위해 일부 데이터베이스 서버는 다른 데이터베이스 서버가 아닌 일부 기능을 허용 할 수 있습니다. 기능을 확인하기 위해 함수와 함수 에서 try/ catch및 예외를 사용 __call()하는 것은 완전히 다른 주장입니다.

내가 생각할 수있는 유일한 유스 케이스 trigger_error는 for E_USER_WARNING또는 lower입니다. E_USER_ERROR비록 트리거하는 것은 내 의견으로는 항상 오류입니다.


응답 주셔서 감사합니다 :)-1) 나는 거의 항상 예외에 대해 예외를 사용해야한다는 데 동의합니다. 그러나이 문맥에서 당신의 주장은 실패한다고 생각합니다.
chriso

2
" 존재하지 않는 메소드를 호출하는 것은 유효한 가능성 일 수 있습니다. "-전적으로 동의하지 않습니다. 내가 사용한 모든 언어에서 존재하지 않는 함수 / 메소드를 호출하면 오류가 발생합니다. 잡히고 다루어야하는 조건 이 아닙니다 . 정적 언어는 유효하지 않은 메소드 호출로 컴파일 할 수 없으며 동적 언어는 호출에 도달하면 실패합니다.
chriso

두 번째 편집 내용을 읽으면 사용 사례가 표시됩니다. __call을 사용하는 방법과 하드 코딩 된 방법을 사용하는 방법으로 같은 클래스의 두 가지 구현이 있다고 가정합니다. 구현 세부 사항을 무시하면 왜 두 클래스가 동일한 인터페이스를 구현할 때 다르게 동작해야합니까? 하드 코딩 된 메소드가있는 클래스에서 유효하지 않은 메소드를 호출하면 PHP가 오류 를 트리거합니다 . trigger_error__call 또는 __callStatic의 맥락에서 사용 하면 언어의 기본 동작을 모방합니다.
chriso

@ chriso : PHP가 아닌 동적 언어 는 예외 가 발생 하여 실패 합니다. 예를 들어 루비는 NoMethodError원하는 경우 잡을 수 있는 것을 던집니다 . 내 개인적인 견해로는 PHP에서 오류는 큰 실수입니다. 핵심이 오류보고를 위해 깨진 방법을 사용한다고해서 자신의 코드가 의미하는 것은 아닙니다.
Matthew Scharley

@ chriso 코어가 여전히 오류를 사용하는 유일한 이유는 이전 버전과의 호환성을위한 것입니다. PHP6가 PHP5와 같은 또 하나의 도약이 될 수 있기를 바랍니다. 하루가 끝나면 오류와 예외 모두 동일한 결과를 생성합니다. 즉, 코드 실행이 즉시 종료됩니다. 예외를 제외하고 왜 그리고 어디서 훨씬 쉽게 진단 할 수 있습니다.
Matthew Scharley

3

표준 PHP 오류는 더 이상 사용되지 않는 것으로 간주해야합니다. PHP는 오류, 경고 및 알림을 전체 스택 추적으로 예외로 변환하기위한 내장 클래스 ErrorException을 제공합니다. 다음과 같이 사용하십시오.

function errorToExceptionHandler($errNo, $errStr, $errFile, $errLine, $errContext)
{
if (error_reporting() == 0) return;
throw new ErrorException($errStr, 0, $errNo, $errFile, $errLine);
}
set_error_handler('errorToExceptionHandler');

그것을 사용하면이 질문은 혼란스러워집니다. 내장 오류로 인해 예외가 발생하므로 자체 코드도 마찬가지입니다.


1
이것을 사용하면 E_NOTICEs를 예외로 만들지 않도록 오류 유형을 확인해야합니다 . 그것은 나쁠 것입니다.
Matthew Scharley

1
아니요, E_NOTICES를 예외로 바꾸는 것이 좋습니다! 모든 통지는 오류로 간주되어야합니다. 예외로 변환하는지 여부에 관계없이 좋습니다.
Wayne

2
일반적으로 나는 당신에게 동의하지만, 두 번째로 타사 코드를 사용하기 시작하면 이것은 빨리 얼굴에 떨어집니다.
Matthew Scharley

거의 모든 타사 라이브러리는 E_STRICT | 안전합니다. 내가 사용하지 않는 코드를 사용하고 있다면 들어가서 고칠 것이다. 나는 문제없이 몇 년 동안 이 방법으로 일했습니다 .
Wayne

당신은 분명히 듀얼을 사용하지 않았습니다. 핵심은 이처럼 정말 좋지만 많은 타사 모듈은 그렇지 않습니다.
Matthew Scharley

0

IMO, 이것은 다음에 대한 완벽하게 유효한 사용 사례입니다 trigger_error.

function handleError($errno, $errstring, $errfile, $errline, $errcontext) {
    if (error_reporting() & $errno) {
        // only process when included in error_reporting
        return handleException(new \Exception($errstring, $errno));
    }
    return true;
}

function handleException($exception){
    // Here, you do whatever you want with the generated
    // exceptions. You can store them in a file or database,
    // output them in a debug section of your page or do
    // pretty much anything else with it, as if it's a
    // normal variable

    switch ($code) {
        case E_ERROR:
        case E_CORE_ERROR:
        case E_USER_ERROR:
            // Make sure script exits here
            exit(1);
        default:
            // Let script continue
            return true;
    }
}

// Set error handler to your custom handler
set_error_handler('handleError');
// Set exception handler to your custom handler
set_exception_handler('handleException');


// ---------------------------------- //

// Generate warning
trigger_error('This went wrong, but we can continue', E_USER_WARNING);

// Generate fatal error :
trigger_error('This went horrible wrong', E_USER_ERROR);

이 전략을 사용 하면 함수 내에 $errcontext있으면 매개 변수 를 얻습니다.$exception->getTrace()handleException . 이것은 특정 디버깅 목적에 매우 유용합니다.

불행히도, 이것은 trigger_error컨텍스트에서 직접 사용하는 경우에만 작동 하므로 래퍼 함수 / 메소드를 사용하여 trigger_error함수의 별칭을 지정할 수 없습니다 (따라서 다음과 같은 작업을 수행 할 수 없음)function debug($code, $message) { return trigger_error($message, $code); } 추적에서 컨텍스트 데이터를 원하는 경우 ).

더 나은 대안을 찾고 있었지만 지금까지는 찾지 못했습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.