Динамическое создание наследования классов в PHP 5.3 - PullRequest
2 голосов
/ 13 ноября 2011

Я столкнулся с непростой проблемой наследования и иерархии исключений , предлагаемой стандартной библиотекой PHP (SPL).

В настоящее время я создаю вспомогательную библиотеку на PHP для API на основе REST. Эти API могут возвращать свои собственные сообщения об ошибках в форме объектов JSON, и эти объекты включают информацию, выходящую за пределы свойств, предлагаемых исключением PHP. Вот быстрый пример:

{"error":{"time":"2011-11-11T16:11:56.230-05:00","message":"error message","internalCode":10}}

Иногда «сообщение» включает в себя внутреннюю структуру, которая может выиграть от дополнительного анализа. Мне нравится идея создания определенного подкласса Exception, например:

$error = $json->error;
throw new UnexpectedValueException($error->message, $error-internalCode);

Который позже можно выборочно поймать:

catch (UnexpectedValueException $e)
{
  ...
}

И теперь мы подошли к моей дилемме: я хотел бы расширить объекты исключения SPL, чтобы они могли иметь атрибут «время», а также выполнить дополнительный анализ «сообщения». Однако я хотел бы расширить их на их уровне , в отличие от создания расширения базового класса Exception, чтобы сохранить возможность выборочного перехвата исключений. Наконец, я хотел бы избежать создания тринадцати различных дочерних классов (количество типов исключений , определенных в SPL ), если это вообще возможно.

В идеале я мог бы начать с родительского объекта customException:

class customException
{
  public $time;
  public $message;
  public $internalCode;

  public function __construct($time, $message, $internalCode)
  {
    $this->time = $time;
    $this->message = $message;
    $this->internalCode = $internalCode;
  }

  public function parseMessage()
  {
    // Do some parsing of message
    return $parsedMessage;
  }
}

Тогда у меня был бы Фабричный класс, который можно было бы вызывать так:

class ExceptionFactory
{
  static public function createException(Exception $e, $exceptionParent)
  {
    $json = json_decode($e->message);
    return new customException($json->time, $json->message, $json->internalCode) extends $exceptionParent;  // Won't work, but hopefully you get the idea
  }
}

После прочтения наследования динамического класса php я, вероятно, смогу достичь этого с помощью eval(), но мне это кажется неправильным Если мне придется написать тринадцать дочерних классов, я обнаружу, что хочу использовать множественное наследование для нужного родительского класса $exceptionParent и customException. Как бы вы посоветовали мне решить эту дилемму? Заранее спасибо за ваши идеи!

Ответы [ 2 ]

0 голосов
/ 13 ноября 2011

Одним из распространенных решений является использование «маркеров интерфейсов» для указания «их уровня»

interface MyExceptionLevel extends ParentExceptionLevel {}

class MyException extends Exception implements MyExceptionLevel{}

try {
  // code
} catch (MyException $e) {}

// or

try {
  // code
} catch (MyExceptionLevel $e) {}

Я рекомендую не использовать слишком много магии, особенно в такой разумной точке, как обработка ошибок / исключений.

0 голосов
/ 13 ноября 2011

Имея что-то вроде:

class MyException extends \Exception {
  const EXCEPTION_TYPE_FOO = 1;
  const EXCEPTION_TYPE_BAR = 2;
  const EXCEPTION_TYPE_JSON_MESSAGE = 3;

  $protected $_data = array();
  $protected $_exceptionType = null;

  public function __construct( $type = null ) {
    if( null !== $type ) 
      $this->_exceptionType = $type;
  }

  public function __get( $name ) { 
    if( isset($this->_data[$name]) ) {
      if( $name == 'message' ) {
        switch( $this->_exceptionType ) {
           case MyException::EXCEPTION_TYPE_JSON_MESSAGE:
             return json_decode($this->_data[$name]);
           // other exception types
           default:
              return $this->_data[$name];
        }
      } 
      return $this->_data[$name];
    }

    return null;
  }

  public function __set( $name, $value ) {
    $this->_data[$name] = $value;
  }
}

Так что теперь вы можете иметь:

$e = new MyException(MyException::EXCEPTION_TYPE_JSON_MESSAGE);
$e->time = time();
$e->code = '404';
$e->message = json_encode(array('testing'));

А когда вы его поймаете

catch( MyException $e ) {
  print_r( gettype($e->message) );
}

Должен вернуть массив.

Я не проверял код, я только написал его, но вы поняли.

...