Welcome to a tutorial on how to debug PHP code. When it comes to troubleshooting, some beginners foam in the mouth and go into a trance of “very difficult, only for experts”. No. Debugging is a basic skill that does not require any special tools.
The simplest form of debugging in PHP involves:
- Turning on error reporting.
- Reading the error messages.
- Tracing where the problem is and fixing it.
That’s all. Even if you don’t know how to fix the error, asking around and searching with the error message is better than screaming “not working”. Read on for more debugging tips!
TABLE OF CONTENTS
DOWNLOAD & NOTES
Here is the download link to the example code, so you don’t have to copy-paste everything.
EXAMPLE CODE DOWNLOAD
Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
SORRY FOR THE ADS...
But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.
Buy Me A Coffee Code Boxx eBooks
PART 1) TURN ON ERROR REPORTING
The first step of debugging is to stop working in the dark, at least learn how to get the error message.
PHP ERROR REPORTING MECHANICS
<?php
// (A) ERROR REPORTING LEVEL
// https://www.php.net/manual/en/errorfunc.constants.php
error_reporting(E_ALL & ~E_NOTICE); // ALL EXCEPT NOTICES
// error_reporting(E_ALL); // ALL KINDS OF ERROR
// error_reporting(0); // NO ERROR REPORTING
// (B) ERROR LOG
ini_set("log_errors", 1); // SAVE ERROR TO LOG FILE
ini_set("error_log", __DIR__ . DIRECTORY_SEPARATOR . "error.log"); // LOG FILE
// (C) DISPLAY ERROR MESSAGES
ini_set("display_errors", 1);
When it comes to error reporting in PHP, there are only 3 mechanics:
error_reporting()
– Set the error reporting level.- PHP has a long list of error levels, everything from “critical errors” to “notices only”.
- So,
E_ALL & ~E_NOTICE
will show all errors except for notices (that are not critical). 1
will show all “errors”,0
will switch off error reporting. Both are not recommended.
- Save the error into a log file.
log_errors
specifies if you want to save the errors into a log file.error_log
is the target log file.
display_errors
display the error on the screen.- Don’t confuse this with
error_reporting()
. Error reporting sets what kinds of errors need to be reported. display_errors
simply sets if the error should be displayed on the screen.- That is, we can turn off
display_errors
but still keep the errors in a log file. But if we turn offerror_reporting()
, no errors will be reported at all.
- Don’t confuse this with
RECOMMENDED FOR TEST SERVER
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 1);
ini_set("log_errors", 0);
When you are working on your test server, just set PHP to show all errors.
RECOMMENDED FOR LIVE SERVER
error_reporting(E_ALL & ~E_NOTICE);
ini_set("display_errors", 0);
ini_set("log_errors", 1);
ini_set("error_log", "PATH/error.log");
Yep, these should be hard-coded into php.ini
already. On a live server, we hide the error messages but keep them in a log file. For security purposes, we don’t want to show the error message, and script name to the user.
PART 2) COMMON MISTAKES
Now that you have eyes on an error message, the next step is to make some sense of what it means. Here are some common examples.
FATAL ERRORS – UNDEFINED FUNCTIONS & FILES
<?php
$foobar = doesnotexist();
require "doesnotexist.php";
“Call to undefined function”, what does that mean? If that is not Captain Obvious enough – The function you tried to call is not defined and does not exist. Could be a silly typo mistake, or you forgot to include the file somewhere.
SYNTAX ERRORS – MISSING OPERATORS
<?php
$foobar = "Doge Bork"
$hello = "World";
$arr = ["Foo", "Bar'};
“Syntax error on line 3”. Just trace the code back a little, there is a missing ;
on line 2. This can also mean a missing ,.(){}
somewhere…
WARNINGS – WRONG DATA TYPE
<?php
function addition ($x, $y) {
return $x + $y;
}
echo addition(1, "x");
Warnings are not critical errors, but they are still of significant importance. For example, putting a string into a function that seemingly needs numbers instead. While this will not break the system, it may end up with the wrong results.
NOTICES – NOT REALLY IMPORTANT…
<?php
if ($_POST["foo"]) {
echo "YES!";
}
An annoying kind of “error”. Notices are often of little importance, and they are just here to remind you of stuff like “you have not defined this variable before”, or “this is not recommended”.
SILENT ERRORS – WRONG RESULTS
<?php
function add ($x, $y) {
// Original intention
// return $x + $y;
// Someone did a *small* typo mistake
return $x - $y;
}
echo add(88, 22);
// Should be 110, but result is 66
These are the deadly ones. These errors do not throw any messages, are hard to spot, and the script continues to run “as usual”. You will only realize that there is an error when the scripts do not produce the correct results.
PART 3) ERROR TRACING & TIPS
Some errors are easy to spot. Just go to the script and line that the error message indicates, eyeball a few lines of code. But how do we deal with complicated scripts? Here are a few troubleshooting tips.
DUMP THE VARIABLES
<?php
// (A) START SESSION
session_start();
// (B) LET'S SAY WE HAVE A SHOPPING CART
$_SESSION["cart"] = [
"123" => [
"name" => "Foo bar",
"qty" => 99
],
"234" => [
"name" => "Doge",
"qty" => 88
]
];
// (C) WHAT'S IN THE CART?
print_r($_SESSION);
var_dump($_SESSION);
So, what is in an array? What is in the session that may be causing problems? print_r()
and var_dump()
are your best friends.
STACK TRACE
<?php
require "second.php";
$testObj = new Test();
$testObj->foo();
<?php
class Test {
function foo () {
var_dump(debug_backtrace());
debug_print_backtrace();
}
}
In big projects, it is common to have multiple scripts sharing many library files. So which scripts and functions were previously called? Use debug_backtrace()
and debug_print_backtrace()
to find out.
MANUAL BREAKPOINT
<?php
// (A) INIT
$varA = ["Hello", "World", "Foo", "Bar"];
$varB = "";
// (B) CONCAT
foreach ($varA as $txt) { $varB .= $txt; }
// (C) DISPLAY & STOP HERE
print_r($varA);
echo $varB;
exit();
// (D) MORE PROCESSING
$varA[] = "Doge";
$varB .= "Last";
With debugging tools, we can set “breakpoints” to pause and check what is in which variables at that point. But no worries, we don’t actually need debugging tools to do that – Just output the variables that you want to check and use die()
or exit()
to manually stop the script at a specific point.
CUSTOM ERROR LOG
<?php
// (A) CURL INIT
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://DOES-NOT-EXIST.com/dummy.php",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false
]);
// (B) CURL FETCH
$result = curl_exec($curl);
if ($result === false) {
error_log("Failed connecting to https://DOES-NOT-EXIST.com", 0);
}
curl_close($curl);
Remember the error log from earlier? Code ninjas can’t be sitting at the desk, monitoring, and waiting for errors to happen 24/7. So utilize the error_log()
function to capture your own custom error messages.
USE A GOOD EDITOR OR IDE
Captain Obvious to the rescue! If you have not installed a good code editor, do so now. It helps to automatically indent your code, and highlight all the silly mistakes.
P.S. This is VSCode, and there are plenty of free code editors. Links below.
EXTRAS
That’s all for this guide, and here is a small section on some extras and links that may be useful to you.
LINKS & REFERENCES
- Runtime Error Configuration – PHP
- Predefined Error Levels – PHP
- Error Log – PHP
- Free Code Editors – Code Boxx
THE END
Thank you for reading, and we have come to the end. I hope that it has helped you to better understand, and if you want to share anything with this guide, please feel free to comment below. Good luck and happy coding!
Thanks,
A little complement : all my php files begin with a
require_once ‘commun/enTete.php’ that require session, functions …
I have a development site with the same domainname but with .local.
I add only line :
if (substr($_SERVER[‘HTTP_HOST’],-6) == “.local”) require_once ‘commun/local.php’ ;
and local.php contains your code and my own trace functions