So far, we've seen some basic techniques for debugging problems in client-side code (Javascript), and problems in the way that data is being passed back in forth between the client and the server. But what happens when the problem is in the server-side code, i.e., in the PHP?
Many PHP developers are used to seeing server-side errors show up in their browsers automatically, because PHP's display_errors
directive is set in their PHP configuration file by default to on
. This is a bad idea, for a number of reasons:
As we discussed in the last section, it's important to distinguish between the code that is running on the server, and the response that it sends back to the client.
In general, code can contain sensitive information that we do not want to share with the end user - for example, passwords and API keys. If something goes wrong with the database connection code, it will generate a stack trace that contains the database credentials. By dumping this trace into the response, your application is risking making this information public:
Therefore we want to keep a tight rein on what makes it into the response, and this means turning off display_errors
.
You might say, "well wait a minute, that's only a problem when I'm in production! I can still use display_errors
in development, right?"
The answer is yes, you can, but there are some other reasons that I strongly recommend against it.
For traditional GET
and POST
requests, such as navigating to a page or submitting a form using your browser's default submission handling, the default behavior of most browsers is to directly display the response it receives in the main viewport. This is done automatically, so we don't even really think of it as a request-response transaction.
However with the rise of "Web 2.0" and more complex web applications, we've seen the widespread adoption of AJAX for submitting requests and working with response data, all without refreshing the page. Often times, the user doesn't even know that a request has been issued!
In these cases, any error messages we receive from PHP via the server's response will get held up inside the XHR object that Javascript uses to process the request. We'll never see these error messages unless we either:
This is hardly a convenient and predictable way to get at debugging and error messages!
Dumping server-side error and debugging messages into the HTTP response further blurs the distinction between server-side and client-side concerns for new developers. It's no wonder that some novice PHP developers end up thinking that their PHP script is being run in their browser!
At the end of the day, the HTTP response should be a carefully crafted piece of content designed to be consumed in a specific way by the client - not a dump of every possible type of output that the server might generate.
I should point out that there is a difference between developer-facing messages, and user-facing messages. User-facing messages are messages which are intended for the end user - things like "Please choose at least one owl" or "Sorry, your owlId is incorrect!" These are generally explicitly generated by your application, and can and should be sent in an HTTP response.
Developer-facing messages, on the other hand, should only be seen by developers and system administrators. These are things like the MySQL credentials error and stack trace we saw earlier. These are the types of messages we want to avoid injecting into our HTTP response.
So then, how do we deliver debugging and error messages to our tech team?
Fortunately, we have means for shunting messages to a log file.
To begin with, PHP natively supports error logging by setting the log_errors
directive to on
, and specifying a path to an error log file (what we will call the php error log) with the error_log
directive. Enabling log_errors
and disabling display_errors
will cause PHP to dump error messages to the log instead of the response.
To manually invoke the error logger, we can use the error_log
function. This will let us write to the php error log:
// Print a simple string to the log
error_log("Fetching owls from database...");
// Print an array or object to the log
error_log(print_r($owls, true));
We can use this as a simple alternative to using echo
to print debugging information directly to the response.
It'd be nice if we could separate our reports of actual, unexpected errors from routine debugging statements, by sending them to separate log files. UserFrosting supports this as well, and creates the following log files in the app/logs/
directory:
debug.log
errors.log
mail.log
debug.log
is meant for dumping routine debugging statements. To log something to the debugging log, simply use the Debug
facade:
use UserFrosting\Sprinkle\Core\Facades\Debug;
...
Debug::debug("Fetching owls from database...");
Debug::debug("Owls found:", $owls);
Debug
is a facade for a Monolog logger instance, whose debug
method takes a string as the first parameter and an optional array as a second parameter, and writes them to a log file. Monolog also supports more advanced logging capabilities - check their documentation for more details.
errors.log
is where most run-time error messages generated by uncaught exceptions are sent. Slim, the microframework upon which UserFrosting is built, converts all PHP errors into exceptions. Thus, the majority of application error messages will be found in this log. You can read more about how the error-handling system as a whole works in Chapter 10.
mail.log
is a special logger for mail-related activity. The underlying phpMailer instance that we use reports its SMTP activity to this log. The level of detail can be specified with the mail.smtp_debug
configuration value, using the values specified in the PHPMailer documentation:
0
No output1
Commands2
Data and commands3
As 2 plus connection status4
Low-level data output