0

With PHP I am using the following class structure for exceptions:

class ProjectException extends Exception {
          ... logical code for handling exceptions, functions that are relevant to every exception my project throws go here
}

class SpecificException extends ProjectException {
     protected $codePrefix = 'SE';

     const ERROR_SOMETHING_BAD_HAPPENED              = 100;
     const ERROR_SOMETHING_SIMMILARLY_BAD_HAPPENED   = 101;

     /* no functions go in this class, this class is only to specify the code prefix of the exception, and the error codes used by it */
}

If I want to catch an exception, I usually use the following type of exception catching:

 try {
    /** try something here **/
 } catch (SpecificException $e) {
    switch ($e->getCode()) {
       case SpecificException::ERROR_SOMETHING_BAD_HAPPENED:
         /** react to the exception based on it's code, and try to recover **/
       default:
         /** react to every unexpected code, which may appear if the tried function is modified,
             and throws a new exception code of the same type, and stop the execution of the 
             script, storing the error in the database **/
    }
 }

My problem is the following. I would like to "lock" some files from editing, for example some exception files. Why I would want to do this, is because I am trying to make part of my code portable, so that it can be used in other projects that we are working on also.

My idea was, that later on, in new projects, if something new comes up, and a new exception code should be made, I will extend the "locked" exception, and write the new error code as a const in the extending class. A real life scenario that could happen is the Account class. A bare Account class is already designed in the portable code, with login and register functionalities, but on each different project, these accounts will receive new functionalities which are project specific. Thus, in the new project, I will extend the portable projects Account class, and write the related functionalities there.

Theoretically, this could work, but I realized, that managing this later on will be a real pain I think, because:

  1. when catching exceptions, I now either need to catch the parent exception, to catch every exception thrown, or I would have to write two (three, four, etc.) separate catch blocks, depending on how many times I extended the original parent exception
  2. if I catch the parent exception, I can't just throw in all codes, without knowing the origin of the code (parent or child class)

Basically I would have to write simmilar try catch blocks to these:

try {
  /** some code to try **/
} catch (SpecificException $e) {
  /** this will catch everything that is SpecificException or SpecificExtendedException or SpecificDoubleExtendedException **/
  switch ($e->getCode()) {
      case SpecificException::ERROR_IN_SPECIFIC:
         /** do something **/
         break;
      case SpecificException::ERROR_TWO_IN_SPECIC:
         /** do something **/
         break;
      case SpecificDoubleExtendedException::ERROR_IN_SECOND_EXTENDED_EXCEPTION:
         /** do something **/
         break;
      default:
         /** as it was originally written **/
         break;
    } 
}

Any one has any idea on how should I proceed? What would be the most natural way to "lock" some portable files, and add new Exception codes to the exceptions that are defined in those "locked" files?

1
  • I'm not sure I understand what you're trying to do, but this might be more appropriate for Software Engineering. Commented Jan 17, 2020 at 22:59

1 Answer 1

1

You could simply add a method like this in your ProjectException class:

public function getCodeFor(string $name): int
{
  return (int)constant("static::ERROR_$name");
}

This uses the constant helper function along with the static keyword to retrieve a constant defined in the actual class of the object that method was called on.

Then you could have your try block this way, which would work for any class extending SpecificException:

switch ($e->getCode()) {
  case $e->getCodeFor('SOMETHING_BAD_HAPPENED'):
    /** do something **/
    break;
  case $e->getCodeFor('SOMETHING_SIMILARLY_BAD_HAPPENED'):
    /** do something **/
    break;
  case $e->getCodeFor('SOMETHING_SPECIFICALLY_BAD_HAPPENED'):
    /** do something **/
    break;
}

Demo: https://3v4l.org/sAQdE

Note: obviously, using strings makes you lose the IDE refactoring capability of your constants. But since you can't know in advance which classes will define a given code (or you can, but would like not to have to), then this is a reasonable workaround.

Note 2: there might be better ways to do things design-wise (and as @Barmar pointed out, asking Software Engineering might be more appropriate for architectural talk). This answer is merely attempting to provide a programming solution to your current issue, without altering said design.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.