Contributing to PHP: How to Fix Bugs in the PHP Core

Share this article

Previously, we covered contributing to PHP’s documentation. Now, we will be covering how to get involved with PHP’s core. To do this, we will be looking at the workflow for fixing a simple bug in the core.

PHP logo

Since submitting new features to PHP has already been explained pretty well, we will not be covering that here. Also, this article does not seek to teach PHP’s internals. For more information on that, please see my previous posts on adding features to PHP.

Resolving Bugs

Fixing bugs in the core is a great way to gain a basic familiarity with PHP’s internals. It only requires a basic knowledge of C and is an easy way to help with the effort of improving PHP. But before doing so, a basic familiarity is required with PHP’s version management.

PHP Version Management Lifecycle

PHP minor versions follow a yearly release cycle, and each minor version has 3 years of support. The first 2 years provide “active support” for general bug fixes, and the final year provides “security support” for security fixes only. After the 3 year cycle has ended, support for that PHP version is dropped.

The currently supported PHP versions can be seen on the php.net website. At the time of writing, PHP 5.5 is in security support, and PHP 5.6 and 7 are in active support.

Fixing a Bug

To demonstrate a basic workflow, let’s resolve bug #71635 from bugs.php.net. The bug report states that there is a segfault when invoking DatePeriod::getEndDate() when no end date is available. So the first thing we will want to do is confirm its validity.

For bugs that look trivial (with little or no environmental setup requirements), we can begin by quickly seeing if the bug can be reproduced in 3v4l. (3v4l is a handy tool that runs a snippet of code on hundreds of PHP versions.) This allows us to see all of the affected PHP versions, which is handy to quickly find out if older, still supported versions of PHP are affected. As we can see, PHP exits with a segfault for all versions 5.6.5 through to 7.0.4.

3v4l result screenshot

Regardless of whether the bug can be replicated in 3v4l or not, we’re going to need to replicate it locally before we can go about fixing it. For this, you’ll need to fork php/php-src and clone your fork locally. If you already did this some time ago, you may need to update your clone, along with retrieving all of the latest tagged releases (with git remote update).

We’re going to work on the PHP 5.6 branch since that’s the lowest version of PHP affected by this bug (whilst still being actively supported). (Had this bug affected PHP 5.5, we would still ignore this version and work against PHP 5.6 due to this bug not being security related.) The standard workflow for submitting bug fixes is to target the fix to the lowest affected (whilst still supported) PHP version. One of the php/php-src developers will then merge the fix upwards as necessary.

So let’s checkout a copy of the PHP 5.6 branch to work in:

git checkout -b fix-dateperiod-segfault upstream/php-5.6

We then build PHP and attempt to reproduce the segfault locally by creating a file (say segfault.php) with the following code:

<?php

$period = new DatePeriod(new DateTimeImmutable("now"), new DateInterval("P2Y4DT6H8M"), 2);
var_dump($period->getEndDate());

We then run segfault.php with the freshly built PHP binary:

sapi/cli/php -n segfault.php

(The -n flag means that a php.ini file will not be used for configuration. This is particularly handy to use if you have custom extensions loaded into your default php.ini file, since it will prevent a load of errors from popping up each time you execute a file with a local PHP binary.)

Once confirmed that we can trigger this locally, we can then create a test for it. Let’s call this test file bug71635.phpt and place it in the ext/date/tests/ folder with the following contents:

--TEST--
Bug #71635 (segfault in DatePeriod::getEndDate() when no end date has been set)
--FILE--
<?php
date_default_timezone_set('UTC');
$period = new DatePeriod(new DateTimeImmutable("now"), new DateInterval("P2Y4DT6H8M"), 2);

var_dump($period->getEndDate());
?>
--EXPECT--
NULL

Running that single test shows that it does not pass:

make test TESTS=ext/date/tests/bug71635.phpt

We now run a debugger of our choice on the segfault.php file that we created earlier. (I use LLDB because that’s what Mac OS X bundles with now, but GDB is another similar debugger that has overlapping commands.)

lldb sapi/cli/php a.php

(The -n command has not been used this time, since it seems to mess with lldb.)

Now we’re in the LLDB debugger, we type run to execute the file. It should show where in the code the segfault occurred:

Error display

Whilst the first frame doesn’t seem to show us anything overly meaningful (unless you program in asm), we can see that the program stopped because of an EXC_BAD_ACCESS. It also showed us that the pointer address it attempted to manipulate was 0x0, so we can see that we have a null pointer access.

Using the bt command shows us the backtrace of the segfault (every frame leading up to the segfault). Looking at frame #1 (by entering frame select 1), we are back into C code and can see the line causing the problem:

Problematic line detected

From this, we can infer that the culprit is dpobj->end evaluating to null, and thus attempting to dereference it causes the segfault. So, we place a check above this to see if dpobj->end is a null pointer, and if so, simply return from the function (doing this as early as possible):

PHP_METHOD(DatePeriod, getEndDate)
{
        php_period_obj   *dpobj;
        php_date_obj     *dateobj;

        if (zend_parse_parameters_none() == FAILURE) {
                return;
        }

        dpobj = (php_period_obj *)zend_object_store_get_object(getThis() TSRMLS_CC);
+
+        if (!dpobj->end) {
+                return;
+        }

        php_date_instantiate(dpobj->start_ce, return_value TSRMLS_CC);
        dateobj = (php_date_obj *)zend_object_store_get_object(return_value TSRMLS_CC);
        dateobj->time = timelib_time_ctor();

        *dateobj->time = *dpobj->end;
        if (dpobj->end->tz_abbr) {
                dateobj->time->tz_abbr = strdup(dpobj->end->tz_abbr);
        }
        if (dpobj->end->tz_info) {
                dateobj->time->tz_info = dpobj->end->tz_info;
        }
}

(Returning from a method implicitly makes the function return null (as all internal PHP functions do on failure). This is because the return_value variable (which is accessible in any function definition) holds the function’s actual return value, and it defaults to null.)

So let’s build PHP and run our test again:

make test TESTS=ext/date/tests/bug71635.phpt

It should now pass! Now we can simply commit the updated file and the corresponding bug test, and then submit a PR against the 5.6 branch of php/php-src.

Conclusion

This article has demonstrated a simple workflow used when resolving bugs in the core. Solving bugs is a great starting point to getting involved with PHP’s internals, and it requires very little knowledge of C.

Bug fixing also serves as a nice series of small programming challenges for those who are bored of the algorithmic-based challenges found at Project Euler and similar websites. And with over 5,000 open bug reports, there’s certainly no shortage of bugs to tackle!

Frequently Asked Questions (FAQs) about Fixing Bugs in PHP Core

How can I get started with contributing to PHP core?

Contributing to PHP core can be a rewarding experience, especially if you’re passionate about PHP. The first step is to set up a PHP development environment. You’ll need to clone the PHP source code from the official PHP Git repository. Once you’ve done that, you can compile and test PHP. It’s also recommended to familiarize yourself with the PHP internals, which you can do by reading the PHP Internals Book.

What is the process of reporting a bug in PHP?

If you’ve found a bug in PHP, you can report it on the PHP bug tracking system. You’ll need to provide a detailed description of the bug, including the PHP version you’re using, the operating system, and any other relevant information. It’s also helpful to include a small test case that reproduces the bug.

How can I debug PHP code effectively?

Debugging PHP code effectively requires a good understanding of PHP and the tools available for debugging. One of the most popular tools is Xdebug, which provides a range of features for debugging PHP code. It’s also important to understand the error reporting levels in PHP, which can help you identify and fix bugs.

What are some common bugs in PHP and how can I avoid them?

Some common bugs in PHP include syntax errors, logical errors, and runtime errors. Syntax errors are usually caused by missing or misplaced punctuation, while logical errors are mistakes in the program’s logic. Runtime errors occur while the program is running. To avoid these bugs, it’s important to write clean, well-structured code and to test your code thoroughly.

How can I contribute to PHP without writing code?

There are many ways to contribute to PHP without writing code. You can help by reporting bugs, writing documentation, translating documentation into other languages, or helping with the PHP website. You can also contribute by participating in the PHP community, for example by answering questions on forums or contributing to discussions on the PHP internals mailing list.

How can I test PHP code effectively?

Testing PHP code effectively requires a good understanding of testing principles and the tools available for testing PHP code. PHPUnit is a popular tool for unit testing PHP code. It’s also important to write testable code, which means writing code that is modular and has a clear separation of concerns.

What is the PHP internals mailing list and how can I participate?

The PHP internals mailing list is a forum for discussing the development of PHP. It’s where proposals for new features are discussed and decisions about the future of PHP are made. You can participate by subscribing to the mailing list and contributing to the discussions.

How can I keep up to date with the latest developments in PHP?

There are many resources available for keeping up to date with the latest developments in PHP. The PHP website is a good place to start, as it provides news and updates about PHP. You can also follow PHP on social media, subscribe to PHP newsletters, or join PHP communities.

What are some best practices for writing PHP code?

Some best practices for writing PHP code include using meaningful variable names, commenting your code, following a consistent coding style, and avoiding the use of global variables. It’s also important to use error handling and to sanitize user input to prevent security vulnerabilities.

How can I improve my PHP coding skills?

Improving your PHP coding skills requires practice and continuous learning. You can practice by working on projects, solving coding challenges, or contributing to open source projects. You can also improve your skills by reading PHP books, taking online courses, or attending PHP conferences and meetups.

Thomas PuntThomas Punt
View Author

Thomas is a recently graduated Web Technologies student from the UK. He has a vehement interest in programming, with particular focus on server-side web development technologies (specifically PHP and Elixir). He contributes to PHP and other open source projects in his free time, as well as writing about topics he finds interesting.

BrunoSbugCcontributingdebuggingOpen SourcePHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week