systematizing error codes for a web app in php? - php

I'm working on a class-based php web app. I have some places where objects are interacting, and I have certain situations where I'm using error codes to communicate to the end user -- typically when form values are missing or invalid. These are situations where exceptions are unwarranted ( and I'm not sure I could avoid the situations with exceptions anyways).
In one object, I have some 20 code numbers, each of which correspond to a user-facing message, and a admin/developer-facing message, so both parties know what's going on. Now that I've worked over the code several times, I find that it's difficult to quickly figure out what code numbers in the series I've already used, so I accidentally create conflicting code numbers. For instance, I just did that today with 12, 13, 14 and 15.
How can I better organize this so I don't create conflicting error codes? Should I create one singleton class, errorCodes, that has a master list of all error codes for all classes, systematizing them across the whole web app? Or should each object have its own set of error codes, when appropriate, and I just keep a list in the commentary of the object, to use and update that as I go along?
Edit: So I'm liking the suggestions to use constants or named constants within the class. That gives me a single place where I programatically define and keep track of error codes and their messages.
The next question: what kind of interface do I provide to the outside world for this class' error codes and messages? Do I do something like triggerError(20) in the class, and then provide a public method to return the error code, the string constant, and the user- and admin-facing message?

You could create a couple of defines to create named constants for all your error codes :
define('ERROR_CODE_SQL_QUERY', 1);
define('ERROR_CODE_PAGE_NOT_FOUND', 2);
define('ERROR_CODE_NOT_ALLOWED', 3);
// and so on
And, then, use the constants in your code :
if ($errorCode == ERROR_CODE_SQL_QUERY) {
// deal with SQL errors
}
With that, nowhere in your code you'll use the numerical value : everywhere (except in the on and only file where you put the defines), you'll use the codes.
It means :
Less risk of errors, as all numerical values are set in only one file
Less risk of errors, as you'll use the constants, that have a name which indicates what it means
And code that's easier to read.
Another idea could be to create a class to deal with errors :
class Error {
const CODE_SQL_QUERY = 1;
const CODE_PAGE_NOT_FOUND = 2;
const CODE_NOT_ALLOWED = 3;
// Add some methods here, if needed
}
And, then, use something like this :
if ($errorCode == Error::CODE_SQL_QUERY) {
// deal with SQL errors
}
Which one is the best ?
It's probably a matter of personnal preferences... If you need to add some methods to deal with the errors, using a class might be useful. Else, defines are a great solution too.

At the very least, can you bump the code numbers up to be class constants or members?
class MyErrorProneClass {
const TURNED_INTO_A_NEWT = 12;
...
public function dontBurnMe() {
// echo your error here using self::TURNED_INTO_A_NEWT
}
This way you can manage the errors in the same place where you use them, rather than having to maintenance a large central file. I tried something to that effect in the past and it becomes difficult to keep up.
Generating error numbers programmatically may be a better long-term solution. If you could use information about the file or line number (__FILE__ and __LINE__ respectively), that would help.
Hope that moves in the right direction at least.
Thanks, Joe
Edit:
A class member would follow this syntax instead:
class MyErrorProneClass {
protected static $turnedIntoANewt = 12;
...
public function dontBurnMe() {
// echo your error here using self::$turnedIntoANewt
}
Since constants are public by default, you can access them from other classes directly if you want. So, from the outside, the error would be referenced as:
MyErrorProneClass::TURNED_INTO_A_NEWT
For associating to messages, you would use a mapping (either in a database, or in some localization file) from error ID (and frontend/backend) to displayed string. This use of keys for messages isn't optimal, but it would allow you to change error messages without changing code as well.

If you don't know already it might be an idea to use trigger_error (), plus an error handler if you want to present the user with a better error message.

Have you thought about using exceptions? They may be a good choice for your problem here although adding them to your project now would probably require some restructuring.
You can extend the basic exception class so it fits your problem in terms the of user / developer error message separation.

Related

PHP 7 "declaration..should be compatible" for argument types

I'm using a framework which has method defined something like
class Abc {
public function doThis($what) {
...
}
}
Since I'm using PHP 7 and also fan of PHP codesniffer, it tells me to define function argument types, that said I have wrote class in my code:-
class Pqr extends Abc {
public function doThis(string $what) {
...
}
}
This code gives me warning Declaration of Pqr::doThis(string $what) should be compatible with Abc::doThis($what)
It seems PHP is treating $what in Abc class differently (not as string). Since Abc is part of framework and I cannot do anything about it. I do not want to remove argument types in my code and want to keep cngode more strict. Disabling all warnings would be bad idea.
Anything better we have to fix this issue ?
Code Sniffer may well be telling you to do something, and you may want to follow its advice, but if your framework isn't doing it then you may not be able to do it either. You can't dicatate the code rules to the framework; you have to live with what it imposes on you, even if that goes against Code Sniffer's rules.
My advice is to simply ignore this issue. Code Sniffer is a great tool, and its advice is worth following, but there are times when you simply can't do so.
If your goal is to get your system to show zero Code Sniffer warnings, then you can do so by explicitly adding markers to your code telling Code Sniffer to ignore specific rules at various points in your code. Code Sniffer has the ability to ignore sections of code; this is described in it's Advanced Usage documentation page.

How to properly call a class while providing error reporting at the class-caller level

I am writing fresh code, as part of refactoring an older legacy codebase.
Specifically, I am writing a Device class that will be used to compute various specifications of a device.
Device class depends on device's model number and particle count and I can call it as $device = new Device($modelNumber, $particleCount);
Problem: since this class will go into existing legacy code, I have no direct influence on if this class will be called properly. For Device to work, it needs to have correct model number and correct particle count. If it does not receive the proper configuration data, internally device will not be initialized, and the class will not work. I think that I need to find a way to let the caller know that there was an error, in case an invalid configuration data was supplied. How do I structure this to be in line with object oriented principles?
Or, alternatively, do I need to concern myself with this? I think there is a principle that if you supply garbage, you get garbage back, aka my class only needs to work properly with proper data. If improper data is supplied, it can bake a cake instead, or do nothing (and possibly fail silently). Well, I am not sure if this principle will be great. I do need something to complain if supplied configuration data is bad.
Here is some code of what I am thinking:
$device = new Device($x, $y);
$device->getData();
The above will fail or produce bad or no data if $x or $y are outside of device specs. I don't know how to handle this failure. I also want to assume that $device is valid when I call getData() method, and I can't make that assumption.
or
$device = new Device($x, $y);
if ($device->isValid())
$device->getData();
else
blow_up("invalid device configuration supplied");
The above is better, but the caller has to now they are to call isValid() function. This also "waters down" my class. It has to do two things: 1) create device, 2) verify device configuration is valid.
I can create a DeviceChecker class that deals with configuration vefication. And maybe that's a solution. It bothers me a little that DeviceChecker will have to contain some part of the logic that is already in Device class.
Questions
what problem am I trying to solve here? Am I actually trying to design an error handling system in addition to my "simple class" issue? I think I probably am... Well, I don't have the luxury of doing this at the moment (legacy code base is huge). Is there anything I can do now that is perhaps localized to the pieces of code I touch? That something is what I am looking for with this question.
I think you need to use below code to verify your passed arguments in construct
class Device {
public function __constructor($modelNumber, $particleCount) {
if(!$this->isValid($modelNumber, $particleCount) {
return false; //or return any error
}
}
}
This will check the passed params are valid or not and create object based on that only, otherwise return false or any error.

Zend Framework partialLoop with associative array of models

I'm making a web application in Zend Framework. I've reached the stage of cleaning up. As things often go, I have a couple of messy view scripts that have become utterly unreadable (tons of (v)sprintf's and loops).
There's one view that's an absolute nightmare... (no/inaccurate comments, shorthand... all things considered to be mortal sins). Just an example:
$rows[$c] .= '<div>'.sprintf('<select id="%s" name="%1$s">',$t.'['.$ref->getCode().']').str_replace('>'.$ref->getCValue().'<',' selected="selected">'.$ref->getCValue().'<','<option>'.implode('</option><option>',$this->vals['P']).'</option>').'</select></div>';
In this particular case, I have an array of models that looks like this:
$arr = array('FOO'=> $Mylib_Model_Person,'BAR'=> $Mylib_Model_Person2);//1~50 mdls
I would like to use a partial loop, but there's a problem:
$this->partialLoop('controller/_myPartial.phtml',array('model'=>$arr));
//in the partial loop:
Zend_Debug::dump($this->m);
I see all my models correctly, but their keys have all been turned into properties.
$this->FOO->someMethod();//works fine
Bur I want it to be:
<span><?php echo $key; ?></span><span><?php echo $model->someMethod(); ?></span>
I've also tried using $this->partialLoop()->setObjectKey('Mylib_Model_Person');, but that didn't seem to make any difference, other then confuse me.
The only solutions I see is either array_map, but that would defeat the point (I'm trying to end up with a clean view script); or rewrite a part of my service layer, to return the data ready structured, and keep the array_map there.
I can't help thinking that what I want to do, essentially use a partialLoop as an array_map callback, is possible. If it isn't, is there an alternative? Any thoughts?
I've tried get_object_properties($this), and iterate through the object properties, to no avail, the loop simply doesn't get executed(?!)
As it turns out $this->partialLoop()->setObjectKey('Mylib_Model_Person'); should have been $this->partialLoop()->setObjectKey('model');. If I do change this, and start the partial loop by dumping $this->model, I see my model. However:
echo $this->model->someMethod(); //throws error: method on non-object
Zend_Debug::dump(get_class_methods($this->model));//shows all methods, including someMethod()
And to add insult to injury, tears and confusion. The model implements the toArray-thing, so I tried:
echo $this->model['someData'];//Error: cannot use object of type Mylib_Model_Person as array!!
So, it's not an object when I try to use methods, it's an object when trying to access data as an array, and when using the magic getter method ($this->model->some_Data) it doesn't do anything. No errors, but no output either. The view is rendered as is.
I'm thinking I ran into a bug. I'll rapport it. Consider this:
$methods = get_class_methods($this->model);
while($m = array_shift($methods))
{
if (substr($m,0,3) === 'get')
{
Zend_Debug::dump($m);//e.g getName
Zend_Debug::dump($this->model->{$m}());//'Foobar'
$m = 'someMethod';//copy-paste, so typo's aren't to blame
Zend_Debug::dump($this->model->{$m}());//'the data I was after'
}
}
So that works, but the, if I try:
$this->model->{'someMethod'}();//Error again
//or even:
$m = 'someMethod';
echo $this->model->{$m}();//Error...
That can't be right
I found out what the problem was. Our development server used to be set up better in terms of error reporting:
I assumed E_ALL | E_STRICT, but I had a look only to find it changed to a cruddy E_COMPILE_ERROR | E_ERROR | E_CORE_ERROR. Seeing as some values in the array can be false rather then an object, at some point in the partialLoop script, a notice should have been raised - with the correct ini settings, of course.
That was the cause of the unexpected behaviour; that, and a silly bug or two.

How do you define Exception?

I'm used to Zend Framework, when you write your own component, you make it's own Exception file, but on per file basis, then you have such structure:
Zend/View/Exception.php
Zend/View/Helper/Exception.php
Zend/View/Renderer/Exception.php
etc.
I'm ok with, I also use Doctrine2 and Exception are "stored" in a different way
something like (in a Zend way)
and in Zend/View/Exception.php
class Exception {
public static function invalidArguement() {
return new self('Invalid arguement was given, etc..');
}
I understand that the second approach is less flexible but more accurate because it throws exception according the error.
The first approach is just a way to be able to throw a Zend_View_Exception with a custom messagE.
Also, what about one Exception file per, Exception.
Like the following structure :
Exception/InvalidArguement.php
Exception/AuthentificationFailed.php
Exception/QuantityLimit.php
Is there any best practices? Any pros/cons?
For me the best practice is to group exceptions related to their issue.
For example if you have a number of Auth exceptions, like InvalidDetails, UserNotFound put them here
Library/Auth/Exceptions/InvalidDetails.php
Library/Auth/Exceptions/UserNotFound.php
Each exception should be an extension of Zend_Exception ( unless you've extended it yourself )
this way you can do:
throw new Library_Auth_Exception_InvalidDetails("Invalid details when trying to login");
the benefit of using this method is you DONT need to have a message, the Exception name can cover it enough.
My assumptions here is you setup a namespace for Library called Library and everything is within there.
I tend to group everything, so a typical Auth library could be:
Auth/Forms/Login.php
Auth/Exception/InvalidUser.php
Auth/Orm/Abstract.php
Auth/Orm/Doctrine.php
HTH
I've never worked with Zend framework but if this at all helps, I would at least make a common Exception class and all those other ones extend that rather than just make one for each.

How to design error reporting in PHP

How should I write error reporting modules in PHP?
Say, I want to write a function in PHP: 'bool isDuplicateEmail($email)'.
In that function, I want to check if the $email is already present in the database.
It will return 'true', if exists. Else 'false'.
Now, the query execution can also fail, In that time I want to report 'Internal Error' to the user.
The function should not die with typical mysql error: die(mysql_error(). My web app has two interfaces: browser and email(You can perform certain actions by sending an email).
In both cases it should report error in good aesthetic.
Do I really have to use exception handling for this?
Can anyone point me to some good PHP project where I can learn how to design robust PHP web-app?
In my PHP projects, I have tried several different tacts. I've come to the following solution which seems to work well for me:
First, any major PHP application I write has some sort of central singleton that manages application-level data and behaviors. The "Application" object. I mention that here because I use this object to collect generated feedback from every other module. The rendering module can query the application object for the feedback it deems should be displayed to the user.
On a lower-level, every class is derived from some base class that contains error management methods. For example an "AddError(code,string,global)" and "GetErrors()" and "ClearErrors". The "AddError" method does two things: stores a local copy of that error in an instance-specific array for that object and (optionally) notifies the application object of this error ("global" is a boolean) which then stores that error for future use in rendering.
So now here's how it works in practice:
Note that 'Object' defines the following methods: AddError ClearErrors GetErrorCodes GetErrorsAsStrings GetErrorCount and maybe HasError for convenience
// $GLOBALS['app'] = new Application();
class MyObject extends Object
{
/**
* #return bool Returns false if failed
*/
public function DoThing()
{
$this->ClearErrors();
if ([something succeeded])
{
return true;
}
else
{
$this->AddError(ERR_OP_FAILED,"Thing could not be done");
return false;
}
}
}
$ob = new MyObject();
if ($ob->DoThing())
{
echo 'Success.';
}
else
{
// Right now, i may not really care *why* it didn't work (the user
// may want to know about the problem, though (see below).
$ob->TrySomethingElse();
}
// ...LATER ON IN THE RENDERING MODULE
echo implode('<br/>',$GLOBALS['app']->GetErrorsAsStrings());
The reason I like this is because:
I hate exceptions because I personally believe they make code more convoluted that it needs to be
Sometimes you just need to know that a function succeeded or failed and not exactly what went wrong
A lot of times you don't need a specific error code but you need a specific error string and you don't want to create an error code for every single possible error condition. Sometimes you really just want to use an "opfailed" code but go into some detail for the user's sake in the string itself. This allows for that flexibility
Having two error collection locations (the local level for use by the calling algorithm and global level for use by rendering modules for telling the user about them) has really worked for me to give each functional area exactly what it needs to get things done.
Using MVC, i always use some sort of default error/exception handler, where actions with exceptions (and no own error-/exceptionhandling) will be caught.
There you could decide to answer via email or browser-response, and it will always have the same look :)
I'd use a framework like Zend Framework that has a thorough exception handling mechanism built all through it.
Look into exception handling and error handling in the php manual. Also read the comments at the bottom, very useful.
There's aslo a method explained in those page how to convert PHP errors into exceptions, so you only deal with exceptions (for the most part).

Categories