I am currently developing my PHP SDK to interact with a REST API. Let's say the API can create a 'car' model. To create a car I would write something like this...
$car = $api->create('car', array(
'wheels' => 6,
'color' => 'blue'
));
If another developer downloads my SDK and tries to create a car model incorrectly and forgets to include required arguments. How can I throw an exception via the SDK to notify the developer of missing arguments, other than them seeing a PHP error like Warning: Missing argument 1 for BMW::create() which does not include many details.
function foo($bar, $baz) {
if (!isset($bar, $baz)) {
throw new InvalidArgumentException("Detailed description of what's wrong here");
}
...
}
PHP will trigger a warning, but will still execute your function as usual (which is... oh well, let's not dwell on it). That means you can do your regular argument checking inside your function and throw exceptions or trigger_errors all you want in as much detail as you want.
Please go through this page...
http://php.net/manual/en/language.exceptions.php
And try something like following
<?php
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
return 1/$x;
}
try {
echo inverse(5) . "\n";
echo inverse(0) . "\n";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
// Continue execution
echo "Hello World\n";
?>
As of PHP 7.1, invoking user-defined functions with too few arguments will result in an Error exception. You do not need to implement the functionality yourself.
https://www.php.net/manual/en/migration71.incompatible.php
Related
Consider this simple generator function in PHP.
function createAGenerator() {
echo 'Before First Yield',"\n";
yield 'First Yield';
echo 'Before Second Yield',"\n";
yield 'Second Yield';
echo 'Before Third Yield',"\n";
yield 'Third Yield';
}
If I throw into this generator using the generator object's throw method
$generator = createAGenerator();
try {
$generator->throw(new Exception('Throwing into a Generator'));
} catch(Exception $e) {
echo 'Caught Exception: ', $e->getMessage(), "\n";
}
echo 'Resuming Main Program Execution',"\n";
the generator function will re-throw the exception for me to catch. This all works as I expect.
However -- my generator now seems permanently stuck. If I try to move on to the next yield, or send a new value in, the generator appears to just return NULL. For example, the following program
<?php
function createAGenerator() {
echo 'Before First Yield',"\n";
yield 'First Yield';
echo 'Before Second Yield',"\n";
yield 'Second Yield';
echo 'Before Third Yield',"\n";
yield 'Third Yield';
}
$generator = createAGenerator();
try {
$generator->throw(new Exception('Throwing into a Generator'));
} catch(Exception $e) {
echo 'Caught Exception: ', $e->getMessage(), "\n";
}
echo 'Resuming Main Program Execution',"\n";
var_dump($generator->send('Some Value'));
var_dump($generator->current());
var_dump($generator->next());
var_dump($generator->current());
Returns the following output.
Before First Yield
Caught Exception: Throwing into a Generator
Resuming Main Program Execution
NULL
NULL
NULL
NULL
Is there a way for a generator to recover from this? Or does an uncaught exception in a generator "break" this current instance of the generator?
When you throw an exception, the generator will skip right to the end of the function. This is exactly what happens in a regular functions, if that function called something that threw an exception.
Your two options are:
To catch it in the generator (or use finally).
To not throw it at all
The idea of throwing an exception and use it as a "temporary" messaging system but resume normal operation is a bit weird. It sounds like a bit of an abuse of exceptions. Perhaps what you are trying to achieve can be accomplished without throwing an exception.
If you're trying to model an operation that has multiple steps and each can individually fail or succeed, one option might be to just yield the exception and not throw it.
A very simple question from someone without much experience. The following try catch block has the section, "(Exception $e)" : is this like sql, where $e becomes an alias of Exception? If so, is this type of alias assignment used elsewhere in php, because I haven't come across it? I have searched for hours without being able to find an explanation on the web.
function inverse($x) {
if (!$x) {
throw new Exception('Division by zero.');
}
else return 1/$x;
}
try {
echo inverse(5) . "<br/>";
echo inverse(0) . "<br/>";
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "<br/>";
}
echo 'Hello World';
What you mention is a filter construction. It resembles a declaration as known from other, declarative languages. However it has a different meaning in fact. Actually php does not have the concept of an explicit declaration (which is a shame...).
Take a look at this example:
function my_func($x) {
try {
/* do something */
if (1===$x)
throw new SpecialException('x is 1.');
else if (is_numeric($x)) }
throw new SpecialException('x is numeric.');
else return $x;
}
catch (SpecialException $e) {
echo "This is a special exception!";
/* do something with object $e of type SpecialException */
}
catch (Exception $e) {
echo "This is a normal exception!";
/* do something with object $e of type SpecialException */
}
}
Here it becomes clear what the construction is for: it filters out by type of exception. So the question which of several catch blocks is executed can be deligated to the type of exception having been thrown. This allows a very fine granulated exception handling, where required. Without such feature only one catch block would be legal and you'd have to implement conditional tests for potential exception types in each catch block. This feature makes the code much more readable, although it is some kind of break in the php syntax.
You don't have to, but you can create own exception classes with special behavior and, more important, accepting and carrying more information about what actually happened.
It's OO PHP. The $e is an instance of the exception object.
$e could easily be labelled anything else, so long as it's referred to thereon when you want to getmessages, etc.
For instance;
try {
echo inverse(5) . "<br/>";
echo inverse(0) . "<br/>";
} catch (Exception $oops) {
echo 'Caught exception: ', $oops->getMessage(), "<br/>";
}
I would like the error log to show each error on a new line beginning with a time stamp.
Currently the errors just show up in a connected chunk.
catch(PDOException $e) {
file_put_contents('logs/insert_errors.txt', $e->getMessage(), FILE_APPEND);
}
I'm sure this is fairly simple. Just my first time working with PDO and not sure how to accomplish this.
Preamble: As #Phil mentioned this has nothing to do with PDO.
There are two parts to your question:
a) it is all in one connected line
The reason is quite obvious, you are not adding a new line character at the end. What that character(s) is(are) depend on your OS, but PHP solves that for you with the PHP_EOL constant that you can just append to the end of your string.
b) you want a timestamp at the beginning of each line
Again, the issue is because you didn't put it there. Just adding the output of date('r') before printing out the message should be enough.
The real solution: a real logger
You may want to look at using a complete logging solution for your code. Someting like log4php, for example.
Homebrew solution
Alternatively, if that is too much, you could build your own logger. A good small example one can be found in the comments of the documentation of PHP's error_log function that I adapted for your needs:
Class Log {
const ERROR_DIR = '/home/site/error_log/db_errors.log';
// Log PDO errors
public function error($msg) {
$date = date('r');
$log = sprintf('%s: %s%s', $date, %msg, PHP_EOL);
error_log($log, 3, self::ERROR_DIR);
}
// shortcut for PDO exceptions
public function exception($e) {
$this->error($e->getMessage())
}
}
try {
$log = new log();
$log->error($msg); //use for general messages
} except (Exception $e)
$log->exception($e); //use for exceptions
}
Simply format the string to be inserted, eg
catch (PDOException $e) {
$error = sprintf('%s: %s%s', date('c'), $e->getMessage(), PHP_EOL);
file_put_contents('logs/insert_errors.txt', $error, FILE_APPEND);
// don't forget to re-throw the exception otherwise your program will
// continue execution
throw $e;
}
I have a class Person.
I want to add error handling into my script, so that say, the user enters an incorrect email address the script will tell them. Usually not a problem at all, but now I am using OO classes I am in unfamiliar territory.
So. I guess I want to know how to handle multiple exceptions. Or do I need to try each line of code one at a time and catch each line? This seems slightly excessive. Ideally I'd like to do the following:
try {
$people[$new]->set_fullname($_POST['name']);
$people[$new]->set_active(true);
$people[$new]->set_add1(rEsc($_POST['add1']));
$people[$new]->set_add2(rEsc($_POST['add2']));
$people[$new]->set_add3(rEsc($_POST['add3']));
$people[$new]->set_add4(rEsc($_POST['add4']));
$people[$new]->set_postcode(rEsc($_POST['postcode']));
$people[$new]->set_phone(rEsc($_POST['phone']));
$people[$new]->set_email(rEsc($_POST['email']));
} catch {
echo 'Caught exception: ', $e->getMessage(), "\n";
}
But in my error handling, How can I catch multiple errors? I'd like to push all the error messages into an array and display them each nicely in the webpage. As far as I can see on php.net it seems that I can only catch one error message at a time.
Do I really have to try {} catch {} each line of code?
Imho this shouldn't throw exceptions in the first place. Simply loop through the fields and add the possible errors to some $errors array.
Users screwing up fields is not an exceptional case. I don't even think the user object should be able to validate an emailaddress. That seems to be like a responsibility of the Form.
Also am I wondering what that rEsc function is you are using. Not only are you using a global function which makes it virtually impossible to swap it out for some other function in the future (tight coupling), but also the name is chosen badly. Also do I fail to see why you would want to escape stuff in that place (I guess that is what the thing does). Only escape / sanitize data when you are using it. And I'm wondering for what you are escaping your data, because if it is for database input there are far better ways.
try {
$people[$new]->set_fullname($_POST['name']);
$people[$new]->set_active(true);
$people[$new]->set_add1(rEsc($_POST['add1']));
$people[$new]->set_add2(rEsc($_POST['add2']));
$people[$new]->set_add3(rEsc($_POST['add3']));
$people[$new]->set_add4(rEsc($_POST['add4']));
$people[$new]->set_postcode(rEsc($_POST['postcode']));
$people[$new]->set_phone(rEsc($_POST['phone']));
$people[$new]->set_email(rEsc($_POST['email']));
} catch (Exception $e) {
echo 'Caught exception: ', $e->getMessage(), "\n";
} catch (EmailFormatException $em) {
echo 'Caught exception: '. $e->getMessage();
}
Just continue it like that
Here's how I would design this:
Create a validate() method on the Person class that verifies every property and returns an array of strings that explain the errors to the user. If there are no errors, have the method return null.
Do not use exceptions at all. They are slow; they complicate code maintenance (and you're seeing the symptoms in the approach you've taken so far)
Remove the custom methods for setting properties of the Person object. PHP is not Java. Set the properties directly.
Putting this all together:
class Person {
public $name;
public $address1;
public $address2;
public function validate() { }
}
And then your code:
$obj = new Person();
$obj->name = "Bob";
$obj->address1 = "1 Elm St.";
$validationResult = $obj->validate();
if ( $validationResult != null) { // there were errors
print_r($validationResult);
}
You can make a foreach statement that sets the data that needs validation with try/catch inside the loop in order to populate an array with the errors, like that:
$errors = [];
foreach (['field1', 'field2', ...] as $field) {
try {
$method = "set_{$field}";
$people[$new]->$method(rEsc($_POST[$field]));
} catch (Exception $e) {
$errors[] = $e->getMessage();
}
}
I am trying to understand what the best approach would be to handle Exceptions in the following scenario:
I have a class employee:
class employee extends person {
private $salary;
private $baseSalary = 6.5;
function __construct($f, $m, $l, $a,$fsalary=0){
if(!is_numeric($fsalary)){
throw new Exception("Age supplied is not a number", 114);
}
parent::__construct($f, $m, $l, $a);
$this->salary=$fsalary;
}
function GetDetails(){
return parent::GetName().
"<br/>".
$this->salary;
}
function __toString(){
return $this->GetDetails();
}
}
And using this:
try{
if(!$f = new employee("Sarah", "Sebastian", "Pira", "abc")){
throw new Exception();
}
else {
echo $f;
}
}
catch (Exception $e){
echo "<br/>";
echo var_dump($e);
}
Now I would think it would be a good idea to throw an exception in the class and then use just one catch block in all the scripts that would be using an employee object - But this doesn't seem to work - I need to have a try catch block within the class - Is this the correct way of looking at this?
Thanks
I think what you're saying is that you want to do something like this:
try {
class Employee extends Person {
// ...blah blah...
}
}
catch(Exception $e) {
// handle exception
}
...and then be able to insantiate it in other classes, without explicitly catching any exceptions:
// try { << this would be removed
$employee = new Employee();
// }
// catch(Exception $e) {
// (a whole bunch of code to handle the exception here)
// }
You can't do that, because then the try/catch block in the class will only catch any exceptions that occur when defining the class. They won't be caught when you try to instantiate it because your new Employee line is outside the try/catch block.
So really, your problem is that you want to be able to re-use a try/catch block in multiple places without re-writing the code. In that case, your best solution is to move the contents of the catch block out to a separate function that you can call as necessary. Define the function in the Employee class file and call it like this:
try {
$employee = new Employee();
$employee->doSomeStuff();
$employee->doMoreStuffThatCouldThrowExceptions();
}
catch(Exception $e) {
handle_employee_exception($e);
}
It doesn't get rid of the try/catch block in every file, but it does mean that you don't have to duplicate the implementation of the exception-handling all the time. And don't define handle_employee_exception as an instance method of the class, do it as a separate function, otherwise it will cause a fatal error if the exception is thrown in the constructor because the variable won't exist.
You should read more about Exceptions in PHP.
You can handle exceptions within the methods of the class, sure. But you should rethink how you want to do this and... why.
Good practice is also creating own exception class, so you are able to distinguish exceptions thrown by your module / class from the exceptions thrown by something else. It looks like that (see more):
class EmployeeModule_Exception extends Exception {}
and when it comes to throwing exception:
// the second parameter below is error code
throw new EmployeeModule_Exception('some message', 123);
Catching is similar, only the below example will catch only your module's exceptions:
try {
// some code here
} catch (EmployeeModule_Exception $e) {
// display information about exception caught
echo 'Error message: ' . $e->getMessage() . '<br />';
echo 'Error code: ' . $e->getCode();
}