Debugging (PHP) Applications



PHP Benelux, 28. 1. 2018
© David Buchmann, Liip AG

twitter.com/bramcohen/status/51714087842877440

Organisation




Introduction

Debugging

Security Bugs


This workshop focuses on finding the location of bugs. Scanning for security vulnerabilities is worth its own Workshop.

Bug Report

Look at the list of open bugs if already reported

Title

Platform/Environment
What did you do - how to reproduce?
What did you expect to happen?
What was the actual result?
Screenshot if visual, copy console output if text (use ``` in github)
            

For OSS: Pull request with failing test, or even a fix?

Respect!

Demo Tutorial Application


Demo Tutorial Application

http://debugging.lo
vagrant up
vagrant ssh
            

Bug Hunting

Usage

git reset --hard
git checkout [branchname]
            
If you get random errors about files in /vagrant/var/cache/dev/ not found, do:
rm -rf var/cache/dev/
            
I often use obvious errors to illustrate a case. Real bugs will often be more complicated, but the strategies apply...

[b1] Bug 1

http://debugging.lo

Look at exception message

[b1] RTFEM: Wrong Typehint

Cannot autowire service "App\Importer\Likes": argument "$em" of method "__construct()" references class "Doctrine\ORM\EntityManager" but no such service exists. Try changing the type-hint to one of its parents: interface "Doctrine\O RM\EntityManagerInterface", or interface "Doctrine\Common\Persistence\ObjectManager".


Exceptions are your friends, read them carefully.

[b2] Bug 2

bin/console app:import

Argument -v makes output more verbose.
Look at the stack trace.

[b2] Stacktrace

App\Importer\Mapper\LikeMapper->map() at .../Likes.php:49
App\Importer\Likes->import() at .../AppImportCommand.php:60
App\Command\AppImportCommand->execute() at .../Command.php:252 ...

$this->productRepository->find($data['CustomerId']);
            
Exception stack trace provides context of errors

[b3] DDD: Die Driven Debugging

bin/console app:import

The CustomerMapper checks if customer id already exists.
Add die('cache')|found|new in CustomerMapper::map

[b3] DDD

$customer = $this->customerRepository->find($data['ProductId']);

At most, use die() to check if you are in the expected place of the code

[b4] Use proper exceptions

bin/console app:import

Search code for "oops"
Search for die( and throw exceptions with decent messages

[b4] Use proper exceptions


do not make your collegues and your future self hate you

Demo: Step by step debugger

                ; config to run the remote debugger
                xdebug.remote_enable=1
                xdebug.remote_autostart=1
                xdebug.remote_connect_back=1
                xdebug.idekey="XDEBUG-DRIFTER"
            

Demo: Step by step debugger

  1. Configure your IDE
  2. Set IDE to listen to debugging
  3. ifconfig to find IP of vboxnet1
  4. php -d xdebug.remote_host=10.10.10.1 ./bin/console app:import
  5. https://blog.theodo.fr/2016/08/configure-xdebug-phpstorm-vagrant/

[b5] Segfault/Infinite Loops

bin/phpunit

At which test does it hang? Try --debug

[b5] Infinite Loop

Last line says CsvProductLoaderTest::testLoadFileNotFound

progressive report to see which test fails hard

[b6] Search codebase for context

http://debugging.lo

Look at the page source for some context, then search the codebase to see what happens around it.

[b6] Search codebase for context

<h2 style="color:green;">Found {{ customer_count }} customers {{ render(controller('App\\Controller\\HomeController::extra')) }}


Program output can provide context where to look for errors

[b7] Search in logfiles

bin/console app:import --reset -q
http://debugging.lo shows only 99 customers instead of 100

Search for non-debug messages in logfile:
echo '' > var/log/dev.log
bin/console app:import --reset -q
cat var/log/dev.log | grep -v DEBUG
            

[b7] Search in logfiles

app.WARNING: Invalid number of columns for App\Importer\Loader\CsvLikeLoader at CSV row 26


Search logfiles for id's or message fragments. It helps if you know when the problem happened.

[b8] Unit Test (for debugging)

We built a (horrible) security into our website. But its faulty:
http://debugging.lo/?secret=I%20am%20David
http://debugging.lo/?secret=David

Build a unit test for the HomeController::index method to cover all cases.

[b8] Unit Test

if (!stripos($secret, 'david')) {


Build a unit test when debugging a specific component. Often you want to keep that test afterwards.

[b9] Do not assume

bin/phpunit
all green, great?
run bin/console app:import