4

I am working on a PHP library, which may be used in various environments by different PHP projects and I'm trying to be as minimalistic as possible.

In some circumstances I have to throw exceptions, for instance.

throw Exception('template has invalid tag');

The error like that is not very useful without the name of the tag:

throw Exception('template has invalid tag: '.$tag);

This would would be hard to localize and may result all sorts of injection problems.

QUESTION: What is the best way to pass extra variables with Exception in PHP?

(note: my library perform SQL Query building and I would prefer it to focus on the task, not solving problems with Exceptions)

twernt
  • 20,271
  • 5
  • 32
  • 41
romaninsh
  • 10,606
  • 4
  • 50
  • 70

3 Answers3

4

The internationalization is not the responsibility of your library. Create one or more Exception classes for your project (throwing \Exception is not recommended as it is too generic) and let them store the parameters in properties.

Let the error messages as they are now but also pass the values as arguments to the constructors of the new exceptions. Provide getters for them. The error messages are for the developers. The code that uses your library must catch the exceptions and display the error messages that are appropriate for them. They should not display the raw message you provide, anyway.

For example, you declare in your library:

class InvalidTagException extends \Exception
{
    protected $tag;

    public function __construct($message, $tag, $code = 0, Throwable $previous = NULL)
    {
        // Let the parent class initialize its members
        parent::__construct($message, $code, $previous);
        // Initialize own members
        $this->tag = $tag;
    }

    public function getTag()
    {
        return $tag;
    }
}

// Declare other Exception classes in a similar fashion, for each kind of exception you throw
class InvalidValueException extends \Exception
{
    // ...
}

The application that uses your library:

try {
    // call your library code here
} catch (InvalidTagException $e) {
    // Handle in application specific way

} catch (InvalidValueException $e) {
    // A different type of exception requires a different type of handling
}
axiac
  • 68,258
  • 9
  • 99
  • 134
  • Here is how I ended up implementing it: http://dsql.readthedocs.io/en/develop/advanced.html#exception-class – romaninsh May 12 '16 at 10:57
3

You can define your own exception class, and add variables in constructor.

For example:

class AnswerException extends \Exception
{
    protected $group;
    public function __construct($message = "",  $group = null, $code = 0, \Exception $previous = null){
        $this->group = $group;
        return parent::__construct($message, $code, $previous);
    }
    public function getGroup(){
        return $this->group;
    }
}

In that case, you can throw exception using following syntax:

throw new AnswerException('template has invalid tag',$tag);

Then later, you can catch AnswerException and use $exception->getGroup() function.

Oleg Liski
  • 563
  • 4
  • 15
  • Shouldn't it be `__construct($message = "", $group = null, $code = 0, ...`? – h2ooooooo Mar 21 '16 at 11:47
  • Thanks. I have accepted answer by @axiac because it's more extensive, but it suggest the same approach, so you are also correct here, Oleg. – romaninsh Mar 21 '16 at 14:04
1

One possible solution is to use sprintf like this:

throw new Exception(sprintf(_('template has invalid tag %s'), $tag));

where _() is your localization function.

But this is not the best solution, because it's still open to html injections and becomes quite messy when you have like 5 variables to pass. Wordpress uses such localization approach as far as I have seen.

DarkSide
  • 3,670
  • 1
  • 26
  • 34