OOP Error Handling In PHP - Exceptions & Try Catch Finally Blocks - Full PHP 8 Tutorial

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
[Music] we covered the basics of error handling in the first section of this course in lesson 1.28 in this lesson we'll expand on that and talk about the exceptions which are the oop way of handling errors before we get started if you enjoy my lessons and have not yet subscribed please consider subscribing to ensure that you don't miss any of the new videos so what is an exception an exception is simply an object of some exception class that describes an error it disrupts the normal flow of the code execution exceptions in php can be thrown manually using the throw keyword or it can be a result of some sort of error in the php whether it's coming from built-in php's functions classes are coming from your own code you can only throw exceptions if the instance of the thrown object is either an exception class or the instance of the throwable interface let's look at an example i have this invoice class here as well as the customer class the invoice class uses property promotion in the constructor which accepts the customer object and has a single method called process which accepts a mount argument then we're simply simulating the processing of the invoice here the customer object itself has a single promoted property called billing info and then we have a getter method for the billing info property now let's say that we did not want to allow invoices to be processed if the given amount was less than or equal to zero and also maybe we didn't want to allow invoices to be processed if the customer was missing billing information like billing address or billing name this is where throwing exceptions could come in handy even though you could add that validation higher up maybe in controller or somewhere else it is always a good idea to do it in the low level so that you can be sure that anywhere where the process method is used you're not allowing certain invoices to be processed based on the conditions so here we can do something like this if amount is less than or equal to zero then we can use the throw keyword followed by the new keyword and we can throw one of the built-in exceptions there is a base exception class called exception so we can throw that for now which will simply throw a generic exception let's provide some generic message here something like invalid invoice amount now of course we should not be comparing floats directly for equality but for this example i'm just going to keep it simple and just keep it as is and we covered why you shouldn't compare floats directly for equality in the first section of the course within the lesson about the floats data type so let's run the code and test this out and so far everything is working because we are passing in the 25 as the amount and it is not less than or equal to zero and therefore everything is working now let's pass a negative 25 and let's run the code again and sure enough we're getting this exception thrown with the error message that we specified now the thing is that this exception class here is very generic we can be more specific about our exception before we do that though let's take a look at what the exception class offers so we have the message the code the file where the error happened the line number it contains the clone method which is set to final meaning that you cannot clone exception objects we have a constructor that accepts message code and the previous exception which enables us to link exceptions to each other then we also have some final methods here which means that you cannot override these methods when you're extending exception class now if you scroll a little bit more down here we see this error class that implements throwable if you scroll up to the exception class we see that it also implements drawable essentially both exception class and error class are types of exceptions that implement drawable interface exception is the base class for all exceptions that can include user defined exceptions as well while error class is the base class for php's internal error exceptions since php7 both the exception class and the error class implement drawable interface which allow us to catch both exceptions and errors using the throwable interface and we'll talk about this more in few minutes now as i mentioned before php has some built-in exceptions there are more specific for example what are we trying to do here we are throwing an exception because the given argument is invalid right just so it happens php has a built-in exception called invalid argument exception and it makes sense in this case because the given argument is invalid now if we run the code it still throws an exception but it throws invalid argument exception if we click into that we see that it's just a regular class that simply extends logic exception and if we click into the logic exception it's just another class that extends exception so essentially all exceptions are based off of the exception class unless they are php's internal errors we can do the same if the billing information is missing so we can check if the billing info is empty and if it is we can simply throw invalid argument exception the thing is though the invalid argument exception in this case does not really make sense because the given argument which is the amount is valid because if we get to this section that means that this section passed here and the given argument is valid the issue is with the customer object and its content we can try to use one of the available exceptions that standard php library or spl offers and if we can't find any suitable built-in exception then we can always throw the base exception class so instead of invalid argument exception we can simply throw the base exception class or we can simply throw logic exception but this does not really tell us much about the exception right and if we just throw the base exception class it's even worse because we're just throwing the base exception class without any additional information other than the error message so it's better to be specific if you find yourself needing to throw the base exception class and you cannot find a suitable built-in exception then you could always create custom exception classes and extend the functionality of either the base exception class or one of the built-in exception classes so let's see what kind of custom exception would make more sense in here we can simply call our custom exception something like missing billing info exception and let's create that class and let's create that class within the app exception namespace and as mentioned before we cannot throw objects of classes that are not instances of the base exception class or the throwable interface so if we go back to the invoice here we see that id is underlining it here letting us know that the thrown object must be an instance of the exception or the throwable interface so we need to simply extend the base exception class here and we can get rid of the constructor because we could use the constructor of the base exception class now if we go back here we see that the underlining is gone and let's test this out if i go back to index.php let's pass in the valid amount and we run the code and we see that now we're getting missing billing info exception now if we needed to throw this exception from somewhere else in the code we would kind of need to repeat this error message over and over again so we would need to take this and paste it into the other sections of the code where we might want to throw this exception now because we have the custom exception class we can simply move this default error message within the custom exception class itself and therefore avoid the duplication of the error message so we can take this and we can simply override the message property here and set it to the missing billing information and now if we run the code everything is still working when an exception is thrown and there is no catch block the exception will bubble up the call stack to the original calling function until it finds the matching catch block if no catch block is found then it will look for the global exception handler and if that's not set then it will simply stop execution with the fatal error so i mentioned few keywords here i mentioned the catch block and i also mentioned the global exception handler and we'll talk about the both noticing the exception message here it says uncode missing billing info exception this means that exceptions can be caught using the try catch block so we can add the try catch block wherever the process method is called from so we can do something like this try to process this invoice and then catch the exception a try block must have at least one catch block or it must have a finally block and finally block simply gets called every time regardless if an exception was thrown or not the catch block is what allows you to have your script continue working unless an exception is drawn from within the catch block itself you can gracefully handle the exception in the catch block by maybe logging the exception somewhere and then allow the script to continue or you can simply lock the exception and rethrow the same exception or throw a different type of exception and so on as you notice the catch block simply gets the exception object in here and this exception object can be used to get information about the error so we can simply echo out the get message and these are the methods that we looked at before and we can simply get the file and get the line let's clear this out run the code and we see that we get the exception message along with the file where the error happened and the line number something to note about the catch block is that this variable here was required prior to php 8 but if you don't make use of that variable you don't really need to have it since php just exception class is enough so maybe you're not doing anything and you simply want to catch that exception and do something else and you don't really interact with that exception object prior to php8 you still have to have this but since php8 you can simply get rid of it and everything will still work you can also have multiple catch blocks to catch different types of exceptions and handle them differently so for example the process method throws two different types of exceptions right we're throwing a valid argument and missing billing information exception if we passed an invalid amount then this is not going to be caught so if we run the code we see that exception was thrown we can simply catch that exception as well and simply echo out invalid argument exception and let's add some line breaks here and run the code and we see that now exception is caught something to note about multiple catch blocks is that the code execution will continue after the first catch block that gets executed unless the catch block itself throws an exception so for example if we threw an invalid argument exception from within the missing billing info exception catch block you might think that the second catch block would catch the invalid argument exception it is not going to catch that exception because as soon as the first catch block is executed it will simply continue the code execution or when the exception is thrown within the catch block he's going to bubble up in the stack so he would need to add another try catch block higher up in the stack if he wanted to catch that exception now if the handling of the both types of exceptions are the same then you could simply catch them within the same line using the pipe character which will simply indicate that you're catching multiple types of exceptions and handling them the same way so we can do it this way and get rid of this catch block from here and if we run the code it still catches the invalid argument exception and if we pass in the valid amount it catches the missing billing info exception let's bring the variable back here and simply echo out message and run the code again and we see that we're catching the missing billing information exception because both of these exceptions extend from the base exception class you could actually catch all kinds of exceptions using the exception class and this will still work and now we will catch both invalid argument as well as missing billing info exception but we can also catch other types of exceptions that extend from the exception class if you wanted to execute some code regardless if the exception was thrown or not this is where you could add a finally block so we could define finally here and let's tackle something here indicating that finally block was reached let's run the code and we see that finally block gets printed whenever exception is thrown if exception is not thrown let's pass in some billing information here and run the code we see that the invoice was processed successfully no exception was thrown but the finally black was still executed so as you can see finally block will execute regardless if the exception was drawn or not something to know about the finally block is that if you have a return statement within the try or catch blocks it will return the value after the final block is executed however if you have the return statement within the finally block then both return statements will be executed but the value will be returned from the final block let's see an example let's move this block of code into a function called process and then let's return true within the try block indicating that invoice was successfully processed and returned false within the catch block indicating that it failed let's call this process method from here and bar dump the return value of that let's run the code and sure enough the final block is still executed even when we're returning from the try block and we see that the boolean true was returned let's now trigger an exception so the return statement from the catch block should be executed let's change the amount to negative 25 which should trigger the exception let's run the code and sure enough the final block is still executed and boolean false is returned let's add the return statement in the final block and let's return negative 1 and run the code we see that now returned value is negative 1 and not false even though the catch block was also executed so to summarize what's happening here is that the return statement from either the try or the catch block is executed but the value is only returned after the finally block is executed and if the finally block also returns a value then the value from the finally blocks return statement will be returned instead of the value from try or catch blocks return statements note that the return statement of the catch block in this case is still executed it's just the value of the finalist return statement that is returned we can prove that by simply calling some function within the return statement let's add a function called foo that simply prints full and returns false let's run the code and sure enough we see that foo is printed but negative one is what is returned because that is what's returned from the finally block so to sum this up what happens when exception is thrown is that it will bubble up the call stack until it finds the matching catch block it will execute all the finally blocks along the way and if the exception is not caught then it will execute the global exception handler if it's set if the exception handler is not set then it will simply result in a fatal error so let's talk about the global exception handler next you can register global exception handler by using set exception handler function which takes a function name callback or a closure as an argument this function here gets a single argument which is simply an object of exception that is being thrown and we're type hinting this object with the throwable interface and the reason for that is because not all exceptions extend the base exception class exceptions have existed in php since version 5. however it was pretty weak and php was generating its own internal errors without exceptions this means that you were not able to catch such errors like you could catch exceptions and then handle them specific ways you had to define custom error handlers php 7 and php 8 changed the way errors are reported most errors are now reported by throwing the error exceptions so we have two types of exceptions we have the regular exceptions that can be user defined or built in php exceptions and we have error exceptions before we continue with the exception handler let's look at the hierarchy of the exceptions in the php so as you can see we have the throwable interface all the way on the top and then we have error class and exception class an exception class is what's being extended from the custom exceptions that we create or from the built-in exceptions that standard php library offers the error class here is the base class for some of the built-in errors like the type error or the value error or the division by zero error which is the child of the arithmetic error and that is the child of the error class now when we catch exceptions and we type hint the exception class there it will only catch those type of exceptions and it's not going to catch any of these errors and same applies to this exception handler that's why we are type hinting it to be throwable and not exception prior to php 7 this would be the right way to do it but since php7 this will no longer catch the type errors or any of the built-in php errors therefore we need to use the throwable and we can easily test that we can simply use one of the php's built-in functions called arrayrand which simply picks the random keys from the array and let's pass in an empty array and let's pick one random key let's simply echo this out and run the code as you can see the exception handler is not catching this error because it is throwing a value error here and we are type hinting it to the exception even if we added try catch around it and we run the code we see that it still does not catch it however we can change this to value error and then we run the code again we see that now it's able to catch it we're getting this error message here saying that argument number one cannot be empty now you probably will never see somebody catching value error like this instead you would see somebody catching all types of errors using throwable so if we run the code now we see that it still catches it so that is the reason why since php 7 the type hint of this exception object in the exception handler needs to be throwable and not exception so we change this to throwable and we get rid of the try catch block from here and we run the code and we see that now the exception handler is able to catch that error the exception handler function is actually pretty useful if you want to provide some kind of global exception handling also just to note that phpa changed the way some of the errors are reported in php for example this line here prior to php8 would simply result in warning but since php8 it results in value error exception so a lot of the internal warnings or errors that were triggered by php prior to phb8 are now triggering exceptions instead which is a good thing because prior to php8 you would not be able to catch such errors using try catch blocks and now you can so at this point you might be asking can't you simply return some default values instead of throwing exceptions why would i want to throw exceptions within the invoice class right here can't we simply just use the return statement to return something and maybe lock some message somewhere in the logging storage the answer is yes you can do that and it's fine to do it if such cases are kind of expected if you're expecting that amount can be zero or less than zero and you're expecting it to be okay then sure you can simply have the return statement and log whatever needs to be logged and have the handling be deferred to somewhere else in the code however if this is an exceptional behavior and you should not be able to continue with the code execution if the amount is invalid then exception should be thrown so that's what you should be looking out for when deciding whether you should throw an exception or not if it's an exceptional behavior then you should definitely throw an exception if it's something that's allowed and expected then you could simply use a return statement or do something else before we wrap this up i want to show you another example of exceptions that you might come across in some frameworks or code bases you might see some custom exception classes with static methods that act as factories kind of to create objects of exceptions for example instead of using the new keyword here you might see somebody do something like this custom exception missing billing info and this is actually pretty common practice one reason why that's useful is when you don't want to be too specific about your custom exceptions for example before this we were being very specific right we're throwing missing billing in for exception and that's being very specific and it has the error message hard-coded which is perfectly fine but in some cases you might want to have a generic exception that is related to your domain or in this case to invoices so you could have a class called invoiceexception and we don't want to pass in the error message here stating that it's missing billing info because we're going to go back to the same problem where we would be duplicating the error message instead of doing that we can introduce static methods so instead of the new keyword we can simply throw the return value of a method called missing billing info and now we can create this class and extend the base exception class and we can simply add this method and we can simply return the new object using the static keyword and here we can hard code the error message saying that missing billing info now if we run the code we see that everything is still working it's throwing invoice exception with missing billing info message we can go even further and instead of throwing involved argument exception here we can add another method to the invoice exception and throw that instead we can simply throw the return value of the invalid amount method on the invoice exception class so we can add this method and we can do the same thing here return new static and put that error message there and now if we go back to the index.php and provide the invalid amount and run the code we see that everything is still working is throwing invoice exception with the proper error message so either way is fine if you want to be more specific you can be more specific and define custom exceptions or you can go this route where you define a generic exception that's related to your specific domain or a specific class like invoice in this case and then provide static methods to simply generate those exceptions with the hard-coded messages that way you're not repeating those messages this is it for this video hope you enjoyed it thank you so much for watching please give this video a thumbs up share and subscribe and i'll see you on the next one
Info
Channel: Program With Gio
Views: 2,972
Rating: undefined out of 5
Keywords: advanced php course, learn php the right way, object oriented php, php, php course, php in 2021, php tutorial, php oop tutorial for beginners full, php8, php exceptions, exception handling in php, throw new exceptions, catch exceptions php, oop error handling php
Id: XQ5Pd-6Hnjk
Channel Id: undefined
Length: 21min 17sec (1277 seconds)
Published: Fri Jun 04 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.