Recently, I have been taking a deep dive into the the new features arriving in PHP 8. I have already talked about the new string functions in PHP 8, and recently have been doing my due diligence in understanding the new JIT. Along the way, I’ve been learning about the internals of PHP. Specifically, I’ve been reading a lot about the tokenization and opcode generation process. I wanted to be able to view php opcodes that were generated for a given PHP script. For that, I stumbled upon the Vulcan Logic Disassembler (VLD) by Derick Rethans.

The Vulcan Logic Disassembler hooks into the Zend Engine and dumps all the opcodes (execution units) of a script.

The steps I outline below is the process I followed to install VLD. It worked for me using PHP 7.2 on a clean AWS EC2 Ubuntu 18.04 image. If you are a visitor from the future, running a different version of Ubuntu or PHP, the process might be different for you.

Installing VLD

1. Update Ubuntu and install the PHP 7.2 dev package. The dev package installs not only PHP 7.2, but also other tools like PECL which we’ll need in the next step.

sudo apt-get update
sudo apt-get install php7.2-dev

2. Update PECL and install VLD. Note the version of VLD I’m installing is compatible with PHP 7.2

sudo pecl channel-update pecl.php.net
sudo pecl install channel://pecl.php.net/vld-0.17.0

3. Once VLD is installed, it should tell you that you need to load the extension. To do that, find your php.ini file. I do this by grepping the php info for php.ini using php -i | grep "php.ini". Once you have the location of your php.ini file (for me it’s /etc/php/7.2/cli/php.ini) add extension=vld.so to load the extension.

View PHP OpCodes

Once the VLD extension has been installed, you can view the opcodes for any PHP file by running php -d vld.active=1 -d vld.execute=0 -f test.php.

$ cat test.php
<?php

function add($a, $b) {
    return $a + $b;
}

echo add(5, 7);

$ php -d vld.active=1 -d vld.execute=0 -f test.php
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /Users/john/workspace/test.php
function name:  (null)
number of ops:  6
compiled vars:  none
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   7     0  E >   INIT_FCALL                                               'add'
         1        SEND_VAL                                                 5
         2        SEND_VAL                                                 7
         3        DO_FCALL                                      0  $0
         4        ECHO                                                     $0
         5      > RETURN                                                   1

branch: #  0; line:     7-    7; sop:     0; eop:     5; out0:  -2
path #1: 0,
Function add:
Finding entry points
Branch analysis from position: 0
1 jumps found. (Code = 62) Position 1 = -2
filename:       /Users/john/workspace/test.php
function name:  add
number of ops:  5
compiled vars:  !0 = $a, !1 = $b
line     #* E I O op                           fetch          ext  return  operands
-------------------------------------------------------------------------------------
   3     0  E >   RECV                                             !0
         1        RECV                                             !1
   4     2        ADD                                              ~2      !0, !1
         3      > RETURN                                                   ~2
   5     4*     > RETURN                                                   null

branch: #  0; line:     3-    5; sop:     0; eop:     4
path #1: 0,
End of function add

What do these OpCodes Mean?

I’m not going to dive too far into the specifics of OpCodes or the output of VLD in this article. Stay tuned for a more in-depth explanation of that soon. However, at a high level, the PHP interpreter generates OpCodes which are passed into the Zend Engine for execution.