Detecting Bugs in PHP Code with PHPStan: A Static Analysis Tool

Learn how to detect bugs early in your PHP codebase using PHPStan, a powerful static analyzer. Improve code quality with practical tips and examples.

Writing clean, bug-free code is a goal for every developer — but manual testing and reviews aren’t always enough. That’s where static analysis tools like PHPStan come in. PHPStan helps you catch bugs in your PHP code without executing it. By analyzing your source files, it detects potential issues before they ever become runtime errors.

In this article, we’ll explore what PHPStan is, how it works, and how you can integrate it into your PHP projects to improve code quality.

What is PHPStan?

PHPStan is a static analysis tool specifically designed for PHP. It scans your codebase and finds problems such as:

  • Type mismatches
  • Undefined variables or methods
  • Dead or unreachable code
  • Incorrect method calls

Unlike traditional debuggers, PHPStan doesn’t execute your code — it reads and analyzes your PHP files directly, looking for structural or logical inconsistencies based on static rules.

Why Use PHPStan?

Here are a few reasons to integrate PHPStan into your development workflow:

  • Early bug detection: Spot bugs before running the code.
  • Improved code quality: Enforce strict typing and coding standards.
  • Reduced runtime errors: Catch potential issues during development.
  • Better refactoring: Safely restructure code with confidence.

PHPStan becomes especially powerful when used alongside modern PHP features like type hints, union types, and generics.

Getting Started with PHPStan

Installing PHPStan is very easy. You can install it in your project through composer:

$ composer require --dev phpstan/phpstan

This installs PHPStan as a development dependency.

Running PHPStan

We can now run PHPStan from the base directory of our project. To analyze your code, use the following command:

$ vendor/bin/phpstan analyze src

Replace src with the directory containing your PHP files.

A breakdown of this command:

  • vendor/bin/phpstan is the executable
  • analyze tells PHPStan to analyze all files in the given directories
  • src is the directory we want to analyse

Try running this in your own project and see what kind of potential errors are living in your codebase.

By default, PHPStan analyzes the code with a conservative level of strictness. However, you can make it more strict using levels from 0 (lenient) to 8 (strictest):

vendor/bin/phpstan analyse src --level=5

Configuration with phpstan.neon

For more control, you can create a phpstan.neon configuration file in your project root:

parameters:
    level: 5
    paths:
        - src
        - app
    ignoreErrors:
        - '#Call to an undefined method.*#'

This allows you to customize which folders to analyze, define rules to ignore, and adjust the strictness level.

Common Issues Caught by PHPStan

PHPStan can detect a wide range of issues:

  • Calling methods or accessing properties that don’t exist.
  • Passing the wrong type of arguments to a function or method.
  • Returning values of the wrong type.
  • Forgetting to return a value in non-void functions.
  • Using undefined variables.

These issues often lead to hard-to-find bugs in production. Catching them early saves time and effort.

Integrating PHPStan into CI

To make PHPStan even more effective, integrate it into your CI/CD pipeline. This ensures code is analyzed automatically on every push or pull request, helping maintain a high standard across your team.

For most of my personal projects, I use TravisCI. Since we’ve included PHPStan as a dev-dependency in our composer.json file we just have to add the PHPStan executable to the scripts that the CI-software needs to run.

For TravisCI, this means just changing the default script in a .travis.yml like this:

language: php
php:
  - '8.0'
install: composer install

# Simply add these lines
script:
    - vendor/bin/phpunit
    - vendor/bin/phpstan analyse src tests --level=5

The default script that TravisCI runs for PHP projects is phpunit. Now we’ve added PHPStan to it. If PHPStan finds any errors within your project, the build will fail.

Similarly, in GitHub Actions:

- name: Run PHPStan
  run: vendor/bin/phpstan analyse --level=5

Conclusion

PHPStan is a must-have tool for modern PHP development. It helps you catch bugs early, write cleaner code, and reduce technical debt over time. Whether you’re maintaining a legacy project or starting a new one, integrating PHPStan into your workflow is a smart move.

Advanced Error Handling in PHP (Part 2)

Explore advanced techniques for error handling in PHP including exceptions, custom error handlers, and best practices in Part 2 of this series.

In this second part of our series, we’ll dive deeper into error handling in PHP. We’ll cover how to read, process, and present your error logs so you can monitor and debug more effectively. If you haven’t gone through Part 1 (logging errors), it’s a good idea to start there first.

Reading Error Logs Programmatically

Once you have error logs being generated, the next step is to read them in a usable way. The goal is to convert the log file into a structured format, so you can display recent errors first, filter entries, etc to make error handling easy.

Here is a sample PHP method that reads an error log file:

public function errorLogs($filePath = 'error.log') {

        $fileContent = file($filePath);

        $errorsArray = array();
        if(sizeof($fileContent) == 0) {
            return false;
        }

        foreach($fileContent as $row) {
            $errors = explode(":  ", $row);

            if(empty($errors[1])) continue;
            $errorsArray[] = $errors;
        }

        return array_reverse($errorsArray, true);
}

Explanation:

$fileContent = file($filePath);

This line of code will read the file line by line from the provided file path.

if(sizeof($fileContent) == 0) {
    return false;
}

Checks whether the file is empty; if yes, returns false to indicate there’s nothing to process.

foreach($fileContent as $row) {
      $errors = explode(":  ", $row);

      if(empty($errors[1])) continue;
      $errorsArray[] = $errors;
}

This part of the function will loop through the log contents row by row.

explode(": ", $row)

Splits each line at the pattern ": " — usually separating a timestamp from the error message.

if (empty($errors[1])) continue;

Skips any lines that don’t have an error message portion after splitting.

$errorsArray[] = $errors;

Adds the parsed pieces to an array.

return array_reverse($errorsArray, true);

Reverses the order, so the newest log entries appear first in whatever display you build.

Why You’d Do This

Prioritize recent errors: By reversing the array, newer errors show up first so you don’t have to scroll through older logs.

Filter out noise: Skipping lines with missing data helps prevent malformed entries from causing trouble.

Make things display-friendly: Once you have structured data (e.g. timestamp + message), you can feed this into UI tables or dashboards.

Beginner’s Guide to Error Handling in PHP – Part 1

Learn the basics of error handling in PHP. Understand production safety and developer insights with logging errors using error_reporting.

Effective error handling in PHP is essential for developers—it helps identify issues while keeping your production environment secure and user-friendly.

Balancing Error Display vs. Security

On a production server, showing PHP errors directly on the screen poses security risks, as they may reveal sensitive file paths or internal logic. Hence, many developers suppress error display using:

ini_set('error_reporting', 0);
error_reporting(0);

ini_set('display_errors', FALSE);

However, completely hiding errors without logging them makes debugging nearly impossible.

Capturing and Logging Errors Safely

A more practical approach is to keep errors hidden from users but log them for developers to review, like below:

ini_set('error_reporting', E_ALL);
error_reporting(E_ALL);

ini_set('log_errors', TRUE);
ini_set('html_errors', FALSE);
ini_set('error_log', LOG_PATH.'error.log');

ini_set('display_errors', FALSE);

Explanation:

  • Enable reporting for all errors (E_ALL), ensuring nothing is missed.
  • Direct errors to logs, turning off HTML formatting (html_errors = FALSE) for cleaner log formatting.
  • Specify a custom log file path (via LOG_PATH . 'error.log'), enabling modular logging for different parts of your application.
  • Disable display of errors to protect end users from seeing raw error output.

Benefits of This Approach

  • Production safety: Users can’t see system internals or error details.
  • Developer insight: All error information will be logged in centralized log files.
  • Modular logging: You can segregate logs per module for quicker diagnostics.

Would you like to learn advanced error handling techniques in PHP like exceptions, custom handlers, or reading log files? Just let me know—happy to continue!

Update a bunch of images at once and export them as separate images using GIMP

Scaling of images can be achieved without using any scripts/extensions, but to export all images as separate image files, we need to install a plugin in GIMP named ‘Export Layers’.

You can download this plugin from the following link,

https://kamilburda.github.io/gimp-export-layers

This plugin is available for Windows, Linux, and macOS. For Windows, it can be installed using an executable file.

After installation of this plugin, restart GIMP.

Now, that we have the plugin installed, there is a very simple way to accomplish this task using the following easy steps.

  1. File > Open as layers to select all images to perform a specific task (This is one single action since the file selector allows the selection of multiple images)
  2. Image > Scale image to 75×75 to scale all layers together (We can perform many different actions similar to scaling like transforming, resizing, cropping, etc.)
  3. File > Export Layers will open a dialog that appears allows you to choose the output folder and file extension.
Export Layers Dialog Box

The above steps will save all your image layers to separate files. This could reduce so many steps of similar tasks.

You can perform many different actions like transforming, cropping, resizing, etc. using same steps.

Setup and use a virtual python environment in Ubuntu

With virtualenvwrapper (user-friendly wrappers for the functionality of virtualenv)

Install virtualenv

Install virtualenv with

sudo apt-get install virtualenv

(for Ubuntu 14.04 (trusty) install python-virtualenv)

Install virtualenvwrapper

The reason we are also installing virtualenvwrapper is that it offers nice and simple commands to manage your virtual environments. There are two ways to install virtualenvwrapper:

As Ubuntu package (from Ubuntu 16.04)

Run sudo apt install virtualenvwrapper then run echo "source /usr/share/virtualenvwrapper/virtualenvwrapper.sh" >> ~/.bashrc

Using pip

  1. Install and/or update pip

    Install pip for Python 2 with
    sudo apt-get install python-pip

    or for Python 3
    sudo apt-get install python3-pip

    (if you use Python 3, you may need to use pip3 instead of pip in the rest of this guide).

    Optional (but recommended): 
    Turn on bash autocomplete for pip Run
    pip completion --bash >> ~/.bashrc

    and run 

    source ~/.bashrc 

    to enable.
  2. Install virtualenvwrapper Because we want to avoid sudo pip we install virtualenvwrapper locally (by default under ~/.local) with:
    pip install --user virtualenvwrapper

    and

    echo "export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3" >> ~/.bashrc
  3. Source virtualenvwrapper in .bashrc

    echo "source ~/.local/bin/virtualenvwrapper.sh" >> ~/.bashrc

Setup virtualenv and virtualenvwrapper:

First, we export the WORKON_HOME variable which contains the directory in which our virtual environments are to be stored. Let’s make this ~/.virtualenvs

export WORKON_HOME=~/.virtualenvs

now also create this directory

mkdir $WORKON_HOME

and put this export in our ~/.bashrc file so this variable gets automatically defined

echo "export WORKON_HOME=$WORKON_HOME" >> ~/.bashrc

We can also add some extra tricks like the following, which makes sure that if pip creates an extra virtual environment, it is also placed in our WORKON_HOME directory:

echo "export PIP_VIRTUALENV_BASE=$WORKON_HOME" >> ~/.bashrc

Source ~/.bashrc to load the changes

source ~/.bashrc

Test if it works

Now we create our first virtual environment. The -p argument is optional, it is used to set the Python version to use; it can also be python3 for example.

mkvirtualenv -p python2.7 test

You will see that the environment will be set up, and your prompt now includes the name of your active environment in parentheses. Also if you now run

python -c "import sys; print sys.path"

you should see a lot of /home/user/.virtualenv/... because it now doesn’t use your system site packages.

You can deactivate your environment by running

deactivate

and if you want to work on it again, simply type

workon test

Finally, if you want to delete your environment, type

rmvirtualenv test

Enjoy!