2
\$\begingroup\$

This is a question about best practices. I have a project I am building and I have the following base Exception class:

Namespace Project;
class Exception extends \Exception{


    //=============== BASE EXCEPTIONS 0 - 1000 ===============//
    /**
     * placeholder for unknown exeptions - the default type
     * @var number
     */
    const UNKNOWN_ERROR                 = 0;

    //===================== GENERAL =======================//

    const NOT_YET_IMPLIMENTED           = 1001;
    const NON_UNIQUE_EXCEPTION          = 1002;



    /**
     * 
     * @param string $message
     * @param number $code
     * @param \Exception $previous
     */
    public function __construct( $message="", $code = 0, \Exception $previous = null ){

        if( $previous && $previous->getCode() == $code ){
            //we do some appending we don't wan't to re-append when re-throwing with the same code
            $message = $previous->getMessage();
        }else{
            $message = $this->getCustomMessage($code, $message);
        }
        parent::__construct($message, $code, $previous);
    }

    /**
     * Convert the __CLASS__::* constants into a basic error message and add
     * the extra bit for some more granularity
     * @param int $code
     * @param string $extra
     * @return string
     */
    public function getCustomMessage( $code, $extra ){
        $extra = trim( $extra );
        //get an array of class constants
        $ReflectionObj = new \ReflectionObject( $this );
        $constants = $ReflectionObj->getConstants();
        //find the constant by value ( values must be unique )
        $message = array_search($code, $constants);
        //format the message
        $message = ucwords( strtolower( str_replace("_", " ", $message)));
        $message .=  ( $extra == '' ) ? '' : ': '.$extra  ;
        return $message;
    }

}

This all works as desired and allows me to use the error constants as the message, such as:

 throw new Exception();
 throw new Exception('', Exception::UNKNOWN_ERROR);
 throw new Exception('I know what this error is', Exception::UNKNOWN_ERROR);

Which for the $e->message is:

- 'Unkown Error';
- 'Unkown Error';
- 'Unkown Error: I know what this error is';

Etc...

What I would like to do is by employing the magical power of eval (with the appropriate safeguards of course):

eval( "
    namespace Project\Exception;
    class $classname extends \Project\Exception{}
");

Where $classname would also be the error constant. So that I could essentially throw exceptions with an appropriate class for each without having to actually create the minimalistic class for each one.

An example would be:

try{
    $error = Exception::UNKNOWN_ERROR; // I know this is a number but for example sake we will say it equates to 'UNKNOWN_ERROR'
    throw new $error;
}catch( \Project\Exception\UNKNOWN_ERROR $e ){

}

Thus I would be able to catch the errors by checking the class names, not the error codes, while still maintaining a small code-base for the exception handler (I plan to have a lot of exceptions). My fear is that when extending exceptions I may lose track of the error numbers and may have some exception classes with conflicting error numbers, which I want to be unique for the whole project. So by employing this method, I have only the one main class to store my constants in but still get the benefits of having many exception classes.

Here is a little update on a new test I just ran. It seems that it is quite possible to throw a dynamic exception from the controller of the parent exception. This is pretty fantastic. To achieve this, a few special steps must be taken but nothing to radical.

\$\endgroup\$
3
  • \$\begingroup\$ Sense this post I have come up with this Composer Package I built. github.com/ArtisticPhoenix/eJinn - basically it takes a PHP config file and uses that to generate the Exception class files. This way it can all be handled during Development and we have real actual classes to work with but not with the hassle of building them. With this new one you add a line in like this "555" => "MyException" and run eJinn (command line etc) and it will build them for you. \$\endgroup\$ Mar 15, 2019 at 17:52
  • \$\begingroup\$ This is still sort of a Beta project, but it dosn't need to be included in an actual live site. Only the generated Exception and Interfaces need be included. It just may be wise to back up your classes it generates if making any changes to namespaces etc. It will not delete the generated files so if you "delete" a config line it's up to you to remove that no longer needed file. What I do, is just copy the files from my Exception folder(s) and then run it. I had actually forgot about this question. \$\endgroup\$ Mar 15, 2019 at 17:57
  • \$\begingroup\$ I added the GitHub link above and here is the actual Composer package packagist.org/packages/evo/ejinn \$\endgroup\$ Mar 15, 2019 at 17:58

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.