0

I am useing a class in my code from the base framework. But it might not be available yet:

use BaseFramework\Libs\SpecialException;

So this use-Statement will result in an error. I.e. for frameworks, where this SpecialException is not available I would like to do:

use Exception as SpecialException;

so that I do not need to change my code.

I learned that the use is only creating an alias to the full named class.

I would like to use the originial SpecialException, if this is not possible I would like to use Exception. I am wondering, what is the best practice or recommended way in PHP to solve this?

cornelinux
  • 877
  • 1
  • 9
  • 17
  • This is perhaps a sign that you should instead implement your own custom exception that you have full control over and throw that instead. – bcmcfc Dec 28 '16 at 17:07
  • You are right. It would be better to ship my own Exceptoin. But the SpecialException is a new feature in the framework. And I want to run my module with the old version of the framework and the new one. – cornelinux Dec 28 '16 at 17:25

4 Answers4

1

You can decide which one to throw using class_exists, it's going to be pretty nasty to actually use though.

Example:

try {
   // do something
} catch (\Exception $e) {
    // you'd still need to catch a common exception to all your custom types
    if (class_exists('SomeCustomException')) {
        throw new SomeCustomException; // or whatever
    }
}

But you'd need to do that or something equally awful everywhere.

Your question suggests the actual answer here is to implement your own custom exception and throw that instead, as you have full control over it then.

Sometimes frameworks get around this kind of issue by having shared interoperability packages, so they can conform to common interfaces, throw the same exceptions and so on.

bcmcfc
  • 25,966
  • 29
  • 109
  • 181
  • THanks for pointing out the class_exists. I will use it at the beginning of the file... – cornelinux Dec 28 '16 at 17:34
  • I was not able to use this in the file header. But it ended up with throwing the SpecialException if it exists and doing returning false if it does not. This works out to be most robust. https://github.com/NetKnights-GmbH/privacyidea-owncloud-app/commit/4939a7a4b738c365cf9573da16ec6e47ead409dc – cornelinux Dec 28 '16 at 18:48
  • @cornelinux please see my modified answer. I feel my solution is better than this (I'm open to corrections and suggestions). – BugHunterUK Dec 28 '16 at 20:07
  • This solution is not totally complete, but for me checking the class_exists at this very single point, where the TwoFactorException needs to be raised totally fixed the problem. After all it also turned out, that in the *else* case I do not need to raise an exception but only return a boring *false* ;-) – cornelinux Dec 28 '16 at 20:52
0

Since SpecialException might contains methods, variables and stuff that Exception doesn't contain, there is no rock-solid way to achieve what you need. Just replacing a class with a more generic one, might lead to trouble once you use some of the more dedicated methods.

You can see this post for working with class-aliases to achieve your desired behaviour, but for the reason meantioned above I wouldn't recommend it: Why use class alisases?

You rather should use the factory-Pattern, just import the super-type of your eventually-custom-class and work with that super-type.

As soon, as you need to call a method on an instance, where you are not sure if that method is present (due to up-casting) - your class definition (or at least the method required) is placed into the wrong level inside the inheritance tree.

Community
  • 1
  • 1
dognose
  • 20,360
  • 9
  • 61
  • 107
0

Why not just extend Exception? Something like this ...

namespace ProjectName\Exceptions\SpecialException;

class SpecialException extends Exception
{
    // Implement custom properties and methods if required. Optional.
}

Here we have a custom class that uses SpecialException:

use \ProjectName\Exceptions\SpecialException;

class DocumentRepository
{
    public static function fetchByID($docID)
    {
        throw new SpecialException("Document does not exist");
    }
}

And now you don't need to worry about whether or not SpecialException exists or not. If calling code throws a regular Exception it will get caught, but if it throws a SpecialException it will still get caught as the new exceptions base class is Exception.

try
{
    $doc = DocumentRepository::fetchByID(12);
}
catch(Exception $e)
{
    die($e->getMessage());
}

Or, if you want to catch the SpecialException you can do (and I highly recommend this):

try
{
    $doc = DocumentRepository::fetchByID(12);
}
catch(SpecialException $e)
{
    die($e->getMessage());
}

Update to answer the problem in your comment

As a developer using a framework you have a location where you store your custom classes, files etc. right? Let me assume that this location is ProjectName/lib. And lets assume the framework you're using lives in the directory ProjectName/BaseFramework.

Your custom SpecialException will live in ProjectName/lib/Exceptions/SpecialException.php.

Currently, the framework doesn't include this exception. So in the files you wish to use SpecialException you use the following use line:

use \ProjectName\Exceptions\SpecialException

When the framework finally does implement this SpecialException you simply replace that use line with this one:

use \BaseProject\Exceptions\SpecialException

It's as simple as that.

If you try to do this in the way other users have suggested you will have dead code in your system. When SpecialException is finally implemented the checks on which type of Exception to use will be redundant.

This assumes you're using something like composer or something else that handles autoloading.

BugHunterUK
  • 8,346
  • 16
  • 65
  • 121
  • As soon as the BaseFramwork provides the SpecialException I want to use this original SpecialException. – cornelinux Dec 28 '16 at 17:40
  • @cornelinux I've updated my answer to reflect your issue. I hope that helps. – BugHunterUK Dec 28 '16 at 20:07
  • There are two versions of ownCloud: 9.1 and future 9.2. Both have a two factor framework. Starting with 9.2 the framework will provide a Class *TwoFactorException*. My privacyIDEA ownCloud App uses this TwoFactorException because it provides better feedback for the users. I can not define this "SpecialException" in my App. This makes no sense, since it is defined in the framework. But I want to have an App that works for 9.1 and 9.2. So this is why the other solution is better for my understanding. And this is why I can not define the "SpecialException" in my own App. – cornelinux Dec 28 '16 at 20:50
  • @cornelinux This should have all been provided in the question, so you can get relevant answers and best approaches. I will research this and adjust my answer. The author of the accepted answer suggested custom Exceptions, and you agreed that it would be a better approach in the comment to your question. I provided an answer based on that. – BugHunterUK Dec 28 '16 at 20:52
0

OK, thanks to some clues by @dognose and @bcmcfc this works for me:

 use BaseFramework\Libs\SpecialException;
 if (!class_exists("SpecialException")) {
     class_alias("Exception", "SpecialException");
 }
cornelinux
  • 877
  • 1
  • 9
  • 17