15 Minutes
PHP Debugging: How to Find and Fix PHP Errors
Fix Bugs Faster! Log Collection Made Easy
PHP applications are often tricky to debug. A combination of loose typing, complex logic and a lack of runtime visibility can make it hard to catch errors before they reach our users.
But if you’re using PHP, there’s no need to stress. This guide will equip you to understand why PHP applications break, return the wrong data or behave differently across environments. We’ll cover logs, runtime checks, Xdebug, IDE tools, request debugging, and production visibility.
Here’s the full table of contents if you want to jump straight to a particular section:
Table of Contents
- What PHP debugging is
- Common PHP errors to debug
- Useful PHP debugging functions
- PHP debugging methods
- How to debug PHP errors: a sequence
- Debugging PHP with Xdebug
- PHP debugging in IDEs
- Debugging PHP web requests and APIs
- Debugging PHP database issues
- What to do next: a PHP debugging checklist
- Debugging PHP in production
- Frequently asked questions about debugging PHP
What PHP debugging is
PHP debugging means tracking a request through the application until the expected behavior changes.
This process usually involves checking error messages, logs, request data, variables, database queries, configuration, and server behavior.
Note that a PHP bug may depend on a variety of factors, including:
- The web server.
- PHP version.
- Framework routing.
- Session state.
- Permissions.
- Database data.
- Production configuration.
Something to keep in mind: When we’re debugging in PHP, it’s not just about finding a broken line of code. We often need to inspect both the application logic and the environment around it.
Common PHP errors to debug
Bugs in PHP will usually arise from one of the following: a broken page, a blank screen, a failed request, a warning, or a logged error.
| Error type | What it usually means |
|---|---|
| Syntax error | PHP cannot parse the file because of invalid code, such as a missing semicolon, bracket, or quote. |
| Fatal error | PHP stops execution because it’s hit a serious problem, such as calling an undefined function or class. |
| Warning | PHP found a problem but continued running, such as a missing file in include(). |
| Notice | PHP found a smaller issue, such as an undefined variable or array key. |
| Type error | A function or method received a value type it cannot accept. |
| Database error | The connection, query, prepared statement, or result handling failed. |
It’s essential to identify the exact error type before changing code. A fatal error, warning, and database error need different debugging paths.
Useful PHP debugging functions
We’ve already touched on some of the challenges of PHP. But the pros definitely outweigh the cons.
PHP includes loads of built-in functions that help us inspect values, trace execution, write debugging information to logs and get quick evidence before setting up a full debugger.
Here’s a quick rundown.
| Function | Use it to… |
|---|---|
var_dump() | Inspect a variable’s type and value during local debugging. |
print_r() | Readable output when inspecting arrays or objects. |
get_defined_vars() | Inspect all variables available in the current scope. |
debug_print_backtrace() | Print the current function call chain. |
debug_backtrace() | Capture the call stack for logging or deeper inspection. |
error_log() | Write debug information without breaking the page or response. |
It’s best to use output functions locally and log functions when the response must stay clean. This matters especially for JSON APIs, redirects, headers and production debugging.
PHP debugging methods
PHP debugging usually starts with the fastest evidence, then moves into heavier tools when the bug needs more context.
Start with the simplest useful signal and you’ll be on the right track. Logs and error output often explain the problem before a full debugger is needed.
| Method | Best for |
|---|---|
| Error reporting | Showing warnings, notices, type errors, and fatal errors in development. |
| PHP error logs | Finding file paths, line numbers, stack traces, and production-safe error details. |
| Print debugging | Quickly inspecting variables, arrays, objects, and request data locally. |
| Step debugging | Inspecting execution flow, changing variables, object state, and complex logic. |
Error reporting
Think of your error reporting regime like your own personal warning system. It controls which PHP errors are reported while the code runs.
In development, it’s best to use full error reporting. This ensures all the core datapoints – warnings, notices, type errors, and fatal errors – are visible while we work:
<?php
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
But when we get to production, this becomes unsafe. Visible errors can expose file paths, class names, server details, and application behavior.
PHP error logs
PHP error logs will record failures without printing them directly on the page. So you need to know where to check.
Common places to check include:
/var/log/apache2/error.log/var/log/nginx/error.log/var/log/php-fpm.log- A custom file defined with
error_log
The fastest clue is often already in the log. So check the timestamp, file path, line number, error type, and stack trace before editing code.
For production, a safer configuration usually looks like this:
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
Print debugging
Print debugging means temporarily outputting values to confirm what PHP receives or produces.
Common options include:
var_dump($user);
print_r($payload);
die();
This is useful for quick local checks, especially when inspecting arrays, objects, request data, or simple conditionals. But again, it doesn’t scale well.
Avoid this method in API responses, redirects, headers, and production pages. A forgotten var_dump() can break JSON, corrupt layouts, or expose internal data.
Step debugging
Step debugging is harder to set up than logs or print debugging. However, it gives us much better visibility into runtime behavior.
Step debugging (or stepping through code) lets us pause PHP execution and inspect the application state line by line. So we can zoom in on the specific bit that’s causing problems.
It’s best to use step debugging for complex flow situations, such as
- Loops.
- Conditionals.
- Framework routing.
- Service classes.
- Bugs that depend on execution order.
We usually require Xdebug and an IDE, like VS Code or PhpStorm.
Step debugging with breakpoints
The most efficient way to step through code is by using breakpoints. They let us pause PHP execution at a chosen line, so we can zoom in and inspect.
Some devs rely on scattered var_dump() calls to zoom into their code, but breakpoints are a much better alternative when the bug depends on sequence, state, or conditional logic.
Conditional breakpoints are useful when a line runs many times but only fails for one value, user, item, or loop iteration. Instead of stopping on every request, we can tell the debugger to pause only when a specific condition is true.
How to debug PHP errors: a sequence
Now we’ve examined the most important PHP debugging techniques, let’s quickly talk about the workflow.
Based on our own experience of debugging PHP apps, we’ve found this four-step sequence works most of the time.
- Reproduce the issue in a safe environment, such as local or staging.
- Check the PHP error log for the error type, file path, line number, and stack trace.
- Inspect the runtime data near the failure, including request values, session data, variables, and database inputs.
- Confirm the fix by rerunning the same scenario and checking the logs again.
Remember: Start with evidence before changing code. PHP errors often look obvious at first, but the failing line may only be where the real problem finally surfaces.
Now, let’s look at each of the tools you’ll rely on when debugging in PHP. Starting with the most important.
Debugging PHP with Xdebug

Xdebug is the de facto standard debugging tool for PHP.
This extension is designed for step debugging, stack traces, profiling, and development diagnostics. A key feature is the built-in step debugger, which lets us pause PHP execution and take a granular look at the code.
It’s best to use Xdebug when logs show where the bug happens, but not why it happens. It’s especially useful when you’ve got bugs involving conditions, loops, framework routing, object state, or execution order.
How to install and configure Xdebug
A basic Xdebug 3 configuration often looks like this:
zend_extension=xdebug
xdebug.mode=debug
xdebug.start_with_request=yes
xdebug.client_host=localhost
xdebug.client_port=9003
Older tutorials often mention the 9000 port. But if you’re working with Xdebug 3, you’ll be using 9003 as the default step debugging port.
After changing the configuration, be sure to restart Apache, Nginx, PHP-FPM, or the local PHP server.
Connect Xdebug to the IDE
Xdebug connects from PHP to a debugging client, such as VS Code or PhpStorm.
The main setting is xdebug.client_host, which tells Xdebug where the IDE is listening.
For Docker or remote servers, localhost may point to the container or server, not the developer machine. This is one of the most common reasons Xdebug does not stop at breakpoints.
The key features of Xdebug
Here are the most important features you’ll use in your day-to-day.
| Xdebug feature | What it helps us inspect |
|---|---|
| Breakpoints | Pause PHP execution at a specific line before the bug happens. |
| Step over | Run the current line without entering another function. |
| Step into | Enter a function to inspect its internal behavior. |
| Step out | Finish the current function and return to the caller. |
| Watch expressions | Track variables or conditions while the code runs. |
| Call stack | See which functions led to the current line. |
| Debug console | Evaluate expressions while execution is paused. |
How to fix Xdebug connection issues
Most Xdebug problems come from connection settings rather than PHP code.
Check these first:
- Xdebug is visible in
phpinfo(). xdebug.mode=debugis enabled.- The IDE is listening for connections.
- The port matches the IDE settings.
xdebug.client_hostpoints to the right machine.- PHP-FPM, Apache, or Nginx was restarted.
Important: if Xdebug never stops at breakpoints, be sure to debug the connection before debugging the PHP code.
PHP debugging in IDEs
IDEs make PHP debugging easier by connecting Xdebug breakpoints to the actual project files.
They are useful when the bug depends on
- Execution order.
- Framework routing.
- Object state.
- Values changing across multiple files.
The main requirement is path mapping. The IDE needs to match the file running on the server, container, or local PHP process with the same file in our project.
Debugging PHP in VS Code

VS Code is one of the most popular dashboards for PHP debugging. Here are two of the things we really like at Bugfender:
- Linting catches syntax issues while we write.
- The PHP Debug extension connects VS Code to Xdebug for breakpoints.
Here’s a basic workflow:
- Confirm PHP validation is working in VS Code.
- Install the PHP Debug extension.
- Create a
.vscode/launch.jsonfile. - Add a “Listen for Xdebug” configuration.
- Start the debugger.
- Trigger the PHP request.
- Confirm the breakpoint is reached.
It’s worth using VS Code when we want lightweight PHP debugging with syntax checks, Xdebug support, and a fast editor workflow. It works particularly well for local projects, smaller PHP applications, and teams already using VS Code.
Debugging PHP in PhpStorm

PhpStorm is a useful alternative to VS Code, with built-in PHP debugging support for local, remote, Docker and framework-based projects.
At Bugfender, we tend to use it when the project needs deeper, PHP-specific tooling. It’s particularly good with large codebases, framework-heavy applications, Docker setups and remote debugging workflows.
A basic workflow is:
- Configure the PHP interpreter.
- Validate the Xdebug setup.
- Set server path mappings.
- Start listening for debug connections.
- Trigger the PHP request.
- Inspect the breakpoint, variables, and call stack.
Debugging PHP web requests and APIs
Historically, developers have sometimes suggested that debugging PHP web apps is particularly challenging because it’s a series of request/response transactions rather than a continuous flow.
However, we can avoid a lot of potential problems if we’re properly prepared.
PHP web debugging follows the request lifecycle:
- Input.
- Routing.
- Application logic.
- Database calls.
- Response output.
As a general point of guidance, it’s best to start by confirming the request reaches the expected route, controller, or script. If PHP is handling the wrong path, the rest of the debugging process starts in the wrong place.
Now let’s unpack each individual aspect of the process, with some actionable takeaways.
Form submissions and login issues
Form and login bugs usually come from missing fields, validation failures, CSRF tokens, sessions, redirects, or failed database writes.
Here’s a checklist to help you prevent these bugs before they creep into your code.
| Check | What to inspect |
|---|---|
| Request data | Confirm $_POST, $_GET, or $_REQUEST contains the expected fields and values. |
| Validation | Check required fields, validation rules, CSRF token status, and error messages. |
| Session state | Confirm the user is authenticated and the session has not expired or reset. |
| Redirects | Check the destination URL after submission and whether headers were already sent. |
| Database write | Confirm the insert, update, or login lookup actually succeeds. |
Don’t log passwords, tokens, or personal data. Debugging should explain the failure without creating issues around consent and data storage.
AJAX and API responses
AJAX and API bugs are easier to debug from the browser Network tab first.
| Check | What to inspect |
|---|---|
| Request | Confirm the method, URL, payload, headers, and content type match what PHP expects. |
| Response | Check the status code, response body, returned headers, and JSON structure. |
| PHP logs | Look for warnings, notices, fatal errors, or framework exceptions triggered during the request. |
| Output noise | Check for echo, whitespace, warnings, or var_dump() output corrupting the response. |
Avoid var_dump() in API responses because it breaks JSON. Use error_log() or structured logging instead.
Webhooks and third-party APIs
Webhooks add a further layer of complexity because external services trigger them, often outside our normal testing flow. Here’s how to manage that.
| Check | What to inspect |
|---|---|
| Raw payload | Store the original request body so we can replay the webhook locally. |
| Headers | Check signatures, timestamps, content type, and authorization headers. |
| Validation | Confirm signature verification, event type handling, and required fields. |
| Retry behavior | Check whether duplicate webhook deliveries are processed safely. |
| Response code | Return the correct success or failure status so the provider knows whether to retry. |
Store enough context to replay the webhook locally. Without the original payload, we are debugging a ghost. Dramatic, but not productive.
Debugging PHP database issues
Database bugs often look like PHP bugs because they surface inside the application.
The real issue may be a failed connection, malformed query, empty result, wrong parameter type, missing index, or transaction problem.
Check these first:
- Connection details – confirm PHP is using the expected host, username, password, database name, and port.
- Environment differences – compare local, staging, and production database settings before changing code.
- Query structure – inspect the SQL statement PHP is actually sending.
- Bound parameters – log the query template and parameter values separately when using prepared statements.
- Database errors – read the actual database error message instead of relying on the PHP symptom.
- Result handling – check whether the query returns empty results,
false,null, or an unexpected data shape. - Transactions – confirm commits, rollbacks, and multi-step writes behave as expected.
Do not debug SQL from memory. Debug the exact query PHP sends to the database.
What to do next: a PHP debugging checklist
So far we’ve focused mainly on what to inspect when you’re debugging PHP apps. But then what?
Having a checklist is one thing, but you also need to know what to do when you find a problem.
So here are the ‘next steps’ for all the big questions you’ll face when debugging.
| Question | Next action |
|---|---|
| Can we reproduce the issue? | If yes, save the exact steps, URL, input data, user role, and environment. If no, check production logs, request data, and user/session context first. |
| Which environment is affected? | Compare local, staging, and production settings, including PHP version, configuration, cache, permissions, database data, and server behavior. |
| What does the PHP error log say? | Use the error type, file, line, timestamp, and stack trace to decide which part of the code to inspect next. |
| What data is PHP receiving? | Check request values, headers, sessions, cookies, payloads, uploaded files, and values passed into the failing function. |
| Is the database involved? | Inspect the connection, query template, bound parameters, result handling, and database error message. |
| Do we need temporary logs? | Add logs only where the existing error message does not explain the runtime state clearly enough. |
| Does this need Xdebug? | Use Xdebug when the bug depends on execution order, changing variables, loops, conditions, or object state. |
| Did the fix solve the same scenario? | Rerun the exact path that reproduced the issue before calling the bug fixed. |
| Did production stay clean after deployment? | Check production logs, remote logs, and session context after release. |
| Did we remove temporary debug output? | Remove var_dump(), print_r(), die(), test logs, and anything that could break pages or APIs. |
Good PHP debugging is mostly sequencing: confirm the evidence first, then move toward heavier tools only when the simple checks are not enough. And when you’re asking your own questions, keep them open: ‘yes’ and ‘no’ generally won’t point you to the answers.
Debugging PHP in production
Now we need to go beyond human inspection, and start working with logs.
When our projects enter production we usually cannot pause execution, display raw errors, or recreate the exact user environment locally. So we need to ensure that errors are logged privately while users see a controlled error message, not file paths, SQL details, class names, or server configuration.
Useful production logs should capture:
- Error message.
- File and line.
- Request URL.
- HTTP method.
- User or session ID.
- Request ID.
- Relevant application state.
- Browser, device, or environment details.
How to get context for each error
This is the hard part. A stack trace can show where PHP failed, but it doesn’t always explain why that failure happened for a specific user, account state, request, or production configuration.
And here’s where you might want to check out our own native product.
Bugfender is designed to help when local PHP debugging is not enough. By collecting remote logs and session context from real environments, we can understand what happened before, during, and after an issue without asking users to reproduce it perfectly.
When the question changes from “what line failed?” to “what happened to this user in production?”, try Bugfender for free for remote logs with session context.
Frequently asked questions about debugging PHP
Can I debug PHP without changing php.ini?
Yes, you can debug PHP without changing php.ini by using application-level error handling, framework logs, error_log(), temporary runtime settings, and web server logs.
For local debugging, we can also enable error reporting inside a specific script. For production, permanent configuration should stay safer and log errors privately.
Where are PHP error logs stored?
PHP error logs may be stored in the PHP log, web server log, PHP-FPM log, framework log, or a custom file defined by error_log.
Common locations include /var/log/apache2/error.log, /var/log/nginx/error.log, /var/log/php-fpm.log, and project-level log folders such as storage/logs in Laravel.
Why do PHP errors appear locally but not in production?
PHP errors may appear locally but not in production because the environments use different error display settings.
Local environments often show errors directly to make debugging faster. Production environments should usually hide visible errors and write them to private logs instead, so users do not see file paths, server details, or internal application behavior.
How do we debug intermittent PHP errors?
Intermittent PHP errors need logs with enough context to show what happened when the issue appeared.
Capture the error message, request URL, user or session ID, input data shape, environment, timing, and related database or API behavior. If the bug cannot be reproduced locally, remote logs and session context become more useful than breakpoints.
What should not be logged when debugging PHP?
Do not log passwords, authentication tokens, API keys, payment details, session secrets, full personal data, or raw sensitive payloads.
A useful debug log should explain the failure without exposing private data. When context is needed, log safe identifiers, masked values, request IDs, and high-level state instead.
Expect The Unexpected!
Debug Faster With Bugfender