PHP Compiler Internals
PHP Compiler Internals
Sebastian Bergmann
Involved in the PHP
project since 2000
Creator of PHPUnit
Co-Founder and
Principal Consultant
with thePHP.cc
Under PHP's Hood
Extensions
(date, dom, gd, json, mysql, pcre, pdo, reflection, session, standard, …)
<ST_IN_SCRIPTING>"if"
"if" { {
return T_IF;
}
Lexical Analysis
PHP Tokens
T_NUM_STRING T_SR
Syntax Analysis
Analyze a sequence of tokens
Syntax Analysis
Parser Generators
Filename: /home/sb/if.php
Function: main
Number of oplines: 8
3 2 EXT_STMT
3 PRINT ~0 '*'
4 FREE ~0
4 5 JMP ->6
6 6 EXT_STMT
7 RETURN 1
PHP Bytecode
Bytecode visualization with bytekit-cli
1 <?php
2 if (TRUE) {
3 print '*';
4 }
5 ?>
sb@thinkpad ~ % bytekit --graph /tmp --format svg if.php
PHP Bytecode
Disassembling with bytekit-cli
1 <?php
2 $a = 1;
3 $b = 2;
4 print $a + $b;
5 ?>
sb@thinkpad ~ % bytekit add.php
bytekit-cli 1.0.0 by Sebastian Bergmann.
Filename: /home/sb/add.php
Function: main
Number of oplines: 10
Compiled variables: !0 = $a, !1 = $b
FETCH_DIM_FUNC_ARG INIT_STATIC_METHOD_CALL
FETCH_OBJ_FUNC_ARG ISSET_ISEMPTY_VAR
FETCH_UNSET ISSET_ISEMPTY_DIM_OBJ
FETCH_DIM_UNSET PRE_INC_OBJ
FETCH_OBJ_UNSET PRE_DEC_OBJ
FETCH_DIM_TMP_VAR POST_INC_OBJ
FETCH_CONSTANT POST_DEC_OBJ
EXT_STMT ASSIGN_OBJ
EXT_FCALL_BEGIN INSTANCEOF
EXT_FCALL_END DECLARE_CLASS
EXT_NOP DECLARE_INHERITED_CLASS
TICKS DECLARE_FUNCTION
SEND_VAR_NO_REF RAISE_ABSTRACT_ERROR
CATCH ADD_INTERFACE
THROW VERIFY_ABSTRACT_CLASS
FETCH_CLASS ASSIGN_DIM
CLONE ISSET_ISEMPTY_PROP_OBJ
INIT_METHOD_CALL HANDLE_EXCEPTION
Extending the Compiler
Test First!
Zend/tests/unless.phpt
--TEST--
unless statement
--FILE--
<?php
unless (FALSE) {
print 'unless FALSE is TRUE, this is printed';
}
unless (TRUE) {
print 'unless TRUE is TRUE, this is printed';
}
?>
--EXPECT--
unless FALSE is TRUE, this is printed
Extending the Compiler
Add token for unless to the scanner
Add rule for unless to the parser
Generate bytecode for unless in the compiler
Add token for unless to ext/tokenizer
Add unless scanner token
Zend/zend_language_scanner.l
<ST_IN_SCRIPTING>"if" {
return T_IF;
}
<ST_IN_SCRIPTING>"unless" {
return T_UNLESS;
}
<ST_IN_SCRIPTING>"elseif" {
return T_ELSEIF;
}
<ST_IN_SCRIPTING>"endif" {
return T_ENDIF;
}
<ST_IN_SCRIPTING>"else" {
return T_ELSE;
}
Add unless parser rule
Zend/zend_language_parser.y
%token T_NAMESPACE
%token T_NS_C
%token T_DIR
%token T_NS_SEPARATOR
%token T_UNLESS
.
.
unticked_statement:
'{' inner_statement_list '}'
| T_IF '(' expr ')' {
.
.
| T_UNLESS '(' expr ')' {
zend_do_unless_cond(&$3, &$4 TSRMLS_CC);
} statement {
zend_do_if_after_statement(&$4, 1 TSRMLS_CC);
} {
zend_do_if_end(TSRMLS_C);
}
.
.
How if is compiled
Zend/zend_compile.c
void zend_do_if_cond
(const znode *cond, znode *closing_bracket_token TSRMLS_DC)
{
typedef struct _znode {
int op_type;
union {
zval constant;
zend_uint var;
zend_uint opline_num;
zend_op_array *op_array;
zend_op *jmp_addr;
struct {
zend_uint var;
zend_uint type;
} EA;
} u;
} } znode;
struct _zend_op {
opcode_handler_t handler;
znode result;
znode op1;
znode op2;
ulong extended_value;
uint lineno;
zend_uchar opcode;
} };
opline->opcode = ZEND_JMPZ;
opline->opcode = ZEND_JMPZ;
opline->op1 = *cond;
opline->opcode = ZEND_JMPZ;
opline->op1 = *cond;
closing_bracket_token->u.opline_num =
if_cond_op_number;
SET_UNUSED(opline->op2);
INC_BPC(CG(active_op_array));
}
Perform book keeping tasks such as marking the second operand of the
new opline as unused or incrementing the backpatching counter for the
current oparray
Add unless to compiler
Zend/zend_compile.c
void zend_do_unless_cond
(const znode *cond, znode *closing_bracket_token TSRMLS_DC)
{
int unless_cond_op_number =
get_next_op_number(CG(active_op_array));
zend_op *opline =
get_next_op(CG(active_op_array) TSRMLS_CC);
opline->opcode = ZEND_JMPNZ;
opline->op1 = *cond;
closing_bracket_token->u.opline_num =
unless_cond_op_number;
SET_UNUSED(opline->op2);
INC_BPC(CG(active_op_array));
}
Filename: /home/sb/unless.php
Function: main
Number of oplines: 8
3 2 EXT_STMT
3 PRINT ~0 '*'
4 FREE ~0
4 5 JMP ->6
6 6 EXT_STMT
7 RETURN 1
Run the test
sb@thinkpad php-5.3-unless % make test TESTS=Zend/tests/unless.phpt
Build complete.
Don't forget to run 'make test'.
=====================================================================
PHP : /usr/local/src/php/php-5.3-unless/sapi/cli/php
PHP_SAPI : cli
PHP_VERSION : 5.3.0RC3-dev
ZEND_VERSION: 2.3.0
PHP_OS : Linux 2.6.28-11-generic #42-Ubuntu SMP Fri Apr 17 01:57:59 UTC 2009 i686 GNU/Linux
INI actual : /usr/local/src/php/php-5.3-unless/tmp-php.ini
More .INIs :
CWD : /usr/local/src/php/php-5.3-unless
Extra dirs :
VALGRIND : Not used
=====================================================================
Running selected tests.
PASS unless statement [Zend/tests/unless.phpt]
=====================================================================
Number of tests : 1 1
Tests skipped : 0 ( 0.0%) --------
Tests warned : 0 ( 0.0%) ( 0.0%)
Tests failed : 0 ( 0.0%) ( 0.0%)
Expected fail : 0 ( 0.0%) ( 0.0%)
Tests passed : 1 (100.0%) (100.0%)
---------------------------------------------------------------------
Time taken : 0 seconds
=====================================================================
Add unless to ext/tokenizer
ext/tokenizer/tokenizer_data.c
http://www.php.net/manual/en/tokens.php
http://www.zapt.info/opcodes.html
Sara Golemon: ”Extending and Embedding PHP”
http://derickrethans.nl/vld.php
http://bytekit.org/
http://github.com/sebastianbergmann/bytekit-cli/
License
This presentation material is published under the Attribution-Share Alike 3.0 Unported
license.
You are free:
✔ to Share – to copy, distribute and transmit the work.
✔ to Remix – to adapt the work.
Under the following conditions:
● Attribution. You must attribute the work in the manner specified by the author or
licensor (but not in any way that suggests that they endorse you or your use of the
work).
● Share Alike. If you alter, transform, or build upon this work, you may distribute the
resulting work only under the same, similar or a compatible license.
For any reuse or distribution, you must make clear to others the license terms of this
work.
Any of the above conditions can be waived if you get permission from the copyright
holder.
Nothing in this license impairs or restricts the author's moral rights.