We'll be discovering and testing a completely unreleased feature of php-src from an RFC that's still under discussion.
If you've ever wanted to be ahead of the curve of PHP features or you've just wanted to contribute back to PHP internals, testing an unreleased feature from an RFC is a fun and educational way to do so.
RFC TL;DR
Before features can be added to PHP or before any breaking changes can occur, the change must first go through the Request for Comments (RFC) process.
It's pretty rare for an RFC to exist without a link to the implementation which exists as a pull request to the official php-src repo with the "RFC" label.
So how do we pull down one of these implementations to start playing with it while it's still under development? Let's look at one that's under development at the time of writing.
Typed properties 2.0 RFC
Back in 2016, Joe Watkins & Phil Sturgeon created an RFC to add typed properties to PHP 7.1. Unfortunately there were some issues with the implementation that ultimately caused the RFC to be declined in the voting phase.
Fast-forward to June 2018, Bob Weinand & Nikita Popov are reviving Joe & Phil's work and giving typed properties another shot with the typed properties 2.0 RFC.
Typed properties TL;DR
In a nutshell, typed properties allow us to add type declarations directly to class properties. So instead of defining the types with DocBlocs like we do in PHP today...
class Foo
{
/** @var int $id */
public $id;
}
...with typed properties we can declare the types directly on the property before the variable name.
class Foo
{
public int $id;
}
This new feature is much less verbose, easier to read, and enforced by the PHP runtime. All really great things!
Compiling PHP from source
If you haven't already cloned the php-src repo to your machine and compiled it from source, follow the steps outlined in my post on compiling PHP from source.
Git setup
The following steps assume you have a remote called upstream
that points to the original php-src repo. You can check your remotes by running git remote -v
from the command line in the php-src
directory.
$ git remote -v
origin git@github.com:SammyK/php-src.git (fetch)
origin git@github.com:SammyK/php-src.git (push)
upstream git@github.com:php/php-src.git (fetch)
upstream git@github.com:php/php-src.git (push)
If you don't see a reference to upstream, you can add it with the following command.
$ git remote add upstream git@github.com:php/php-src.git
Fetch the RFC implementation from GitHub
You can fetch a pull request (PR) from GitHub into a branch that you define with the following template command.
$ git fetch upstream pull/{pr-id}/head:{some-branch-name}
For example, in the typed properties 2.0 RFC, there's a link at the top to the implementation which is a PR on GitHub (#3313). Using our template command from above, we replace {pr-id}
with the PR ID 3313
and replace {some-branch-name}
with whatever we want - we'll just use rfc-typed-properties
. Then we'll check out our new branch.
$ git fetch upstream pull/3313/head:rfc-typed-properties
$ git checkout rfc-typed-properties
Now all we have to do is compile PHP!
$ make distclean \
&& ./vcsclean \
&& ./buildconf \
&& ./configure --enable-debug --enable-cli
&& make
Hopefully everything worked out well. Let's check the version of our compiled PHP binary.
$ sapi/cli/php --version
PHP 7.3.0-dev (cli) (built: Jun 23 2018 20:08:49) ( DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies
If you don't get any errors, you should be good to go.
Playing with the implementation
Create a new file called typed-properties.php
in the php-src
directory (or wherever you like) and paste in the following code:
<?php
class User
{
public int $id;
public string $name;
public function __construct(int $id, string $name)
{
$this->id = $id;
$this->name = $name;
}
public function dump(): void
{
var_dump($this->id, $this->name);
}
}
$user = new User(42, "Foo User");
$user->dump();
Then see if it runs with the compiled PHP binary.
$ sapi/cli/php typed-properties.php
int(42)
string(8) "Foo User"
W00t! We're now able to play around with the new typed properties implementation before it's released to the public! Try to tweak the PHP script and add other properties with other type declarations to get a feel for how typed properties works. Nullable types are also supported.
I recommend trying to declare a typed property with no default value and then trying to access the property somewhere else. Did you get an error? Spoiler: You will.
Testing the implementation in the real-world
If we really wanted to test the typed properties implementation in a larger capacity, we could choose an existing unit-tested project that we're familiar with. The key is to run the unit tests with our compiled version of PHP. You can do that by prefixing the PHPUnit command with the path to the compiled PHP binary.
$ /path/to/php-src/sapi/cli/php ./vendor/bin/phpunit
Add an alias to the complied PHP version
To save some keystrokes & since my compiled version of PHP is always in the same location for all my work on php-src, I've set up an alias in my bash profile called cphp
(compiled PHP) that points to the compiled version.
alias cphp="/path/to/php-src/sapi/cli/php"
Now I can just run cphp
in the CLI any time I want to reference my compiled PHP version.
$ cphp --version
PHP 7.3.0-dev (cli) (built: Jun 23 2018 20:08:49) ( ZTS DEBUG )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.3.0-dev, Copyright (c) 1998-2018 Zend Technologies
Assuming your bash profile is located at ~/.bash_profile
and your PHP runtime is located at /path/to/php-src/sapi/cli/php
, you can add the same alias like so.
$ echo 'alias cphp="/path/to/php-src/sapi/cli/php"' >> ~/.bash_profile \
&& source ~/.bash_profile
Now you should be able to run PHPUnit with your complied PHP version as the runtime.
$ cphp ./vendor/bin/phpunit
To be sure of the specific PHP runtime being used, you can add the -v
flag (verbose mode) to see which runtime PHPUnit is running.
$ cphp ./vendor/bin/phpunit -v
PHPUnit 5.7.20 by Sebastian Bergmann and contributors.
Runtime: PHP 7.3.0-dev
Configuration: /path/to/phpunit.xml
...................................................... 54 / 54 (100%)
Time: 683 ms, Memory: 14.00MB
OK (54 tests, 76 assertions)
Adding typed properties to the real-world project
Once you've run the implementation with PHPUnit as described above and all the tests are passing, we can start adding types to properties in our code.
Before we start typing our properties, I recommend creating a new branch in our project so that we don't accidentally commit unreleased PHP features to the master branch of our project.
In our project root, let's create a new branch called test-typed-properties
to isolate our changes.
$ git checkout -b test-typed-properties
Now we can just go class-by-class and add types to the properties. If there's already DocBlocs in place, it's as easy as cutting and pasting the declaration. For example, we can change the following.
<?php
namespace App\Champions;
class ChampionStore
{
/** @var Champion|null */
private static $champion;
// ...
}
...to...
<?php
namespace App\Champions;
class ChampionStore
{
/** @var Champion|null */
private static ?Champion $champion;
// ...
}
And in this particular case the DocBlocs aren't giving any additional information other than declaring the property type, so we can go ahead and kill the DocBloc altogether.
<?php
namespace App\Champions;
class ChampionStore
{
private static ?Champion $champion;
// ...
}
Ah. So much cleaner. Now run the unit tests and fix any errors that pop up.
What to do if you find a bug
If while testing the implementation you run into a bug, don't submit it to bugs.php.net like you would normally do with bugs in PHP. Instead you can comment directly on the PR with a small repro snippet to reproduce the unintended behavior.
For example, while I was testing the typed-properties implementation, I ran into a small bug with an error message and commented on the PR with a repro. Nikita responded with gratitude and patched the bug right away.
And now we're contributing back to PHP internals. Congrats and thank you!
Good luck!
I really hope you enjoyed being ahead of the curve of PHP features. May you continue to pull down new proposed implementations of PHP and keep this fun little party going. Until next time!