com presentations: PHP NorthWest 'All The Jumps' talk: jump-phpnw16.xml slides/internals/abstract-tree.jpg slides/internals/ast.xml slides/internals/ast1.xml slides/internals/branches.jpg slides/internals/branches.xml slides/internals/bytecode.jpg slides/internals/bytecode0.xml slides/internals/bytecode1.xml "slides/internals/bytecode\302\275.xml" slides/internals/catch.jpg slides/internals/catch.xml slides/internals/complex-dot.png slides/intern
[email protected] (Derick Rethans) Fri, 30 Sep 2016 11:14:57 +0000
| Newsgroups | php.pres |
|---|---|
| Message-ID | <[email protected]> |
Commit: d993def5fa85bbaecd77a10d8f9f8126583ed329 Author: Derick Rethans <[email protected]> Fri, 30 Sep 2016 12:14:57 +0100 Parents: 23e62764b7d67547d4379225926aba704d4ad276 Branches: master Link: http://git.php.net/?p=presentations.git;a=commitdiff;h=d993def5fa85bbaecd77a10d8f9f8126583ed329 Log: PHP NorthWest 'All The Jumps' talk Changed paths: A jump-phpnw16.xml A slides/internals/abstract-tree.jpg A slides/internals/ast.xml A slides/internals/ast1.xml A slides/internals/branches.jpg A slides/internals/branches.xml A slides/internals/bytecode.jpg A slides/internals/bytecode0.xml A slides/internals/bytecode1.xml A "slides/internals/bytecode\302\275.xml" A slides/internals/catch.jpg A slides/internals/catch.xml A slides/internals/complex-dot.png A slides/internals/deadcode-vld.xml A slides/internals/deadcode.png A slides/internals/deadcode.xml A slides/internals/jumps-complex-dot.xml A slides/internals/jumps-complex.xml A slides/internals/jumps-do-while.xml A slides/internals/jumps-for-rewritten.xml A slides/internals/jumps-for.xml A slides/internals/jumps-foreach.xml A slides/internals/jumps-if-else.xml A slides/internals/jumps-if.xml A slides/internals/jumps-try-catch.xml A slides/internals/jumps-while.xml A slides/internals/jumps.jpg A slides/internals/jumps.xml A slides/internals/loops.png A slides/internals/loops.xml A slides/internals/parse-state-machine.xml A slides/internals/parse-tokenization.xml A slides/internals/recap-content.xml A slides/internals/recap.jpg A slides/internals/recap.xml A slides/internals/ring.png A slides/internals/rings.xml A slides/internals/rosetta.jpg A slides/internals/scanner-ast.xml A slides/internals/scanner-ast2.xml A slides/internals/scanner-rules.xml A slides/internals/scanner.xml A slides/internals/stages.jpg A slides/internals/stages.xml A slides/internals/title-agenda.xml A slides/internals/title-parsing.xml A slides/internals/title-stages.xml A slides/internals/tokens.jpg M slides/mongodb/me.xml M templates/default/div.ezt
diff_d993def5fa85bbaecd77a10d8f9f8126583ed329.txt
(text/plain, 50.6 KB)
diff --git a/jump-phpnw16.xml b/jump-phpnw16.xml new file mode 100644 index 0000000..c551fbe --- /dev/null +++ b/jump-phpnw16.xml @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="utf-8"?> +<presentation css="10gen-strict.css"> +<topic>PHP</topic> +<title>It's All About the Jumps</title> +<event>PHP North West 2016</event> +<location>Manchester, UK</location> +<date>October 1st, 2016</date> +<speaker>Derick Rethans</speaker> +<email>[email protected]</email> +<twitter>derickr</twitter> +<joindin>https://joind.in/18836</joindin> + +<slide>slides/mongodb/title.xml</slide> +<slide>slides/mongodb/me.xml</slide> + +<slide>slides/internals/title-agenda.xml</slide> + +<slide>slides/internals/title-stages.xml</slide> +<!-- INTRODUCTION +Compile stages: +Parse script +Create AST +Convert to opcodes +Run the code +Opcache for caching +--> +<slide>slides/internals/stages.xml</slide> + +<!-- STAGES +Parse +- Show parser rules +- Show "compiler" rules +--> + +<slide>slides/internals/title-parsing.xml</slide> +<slide>slides/internals/parse-state-machine.xml</slide> +<slide>slides/internals/parse-tokenization.xml</slide> +<slide>slides/internals/scanner.xml</slide> +<slide>slides/internals/scanner-rules.xml</slide> +<slide>slides/internals/ast.xml</slide> +<slide>slides/internals/ast1.xml</slide> +<slide>slides/internals/scanner-ast.xml</slide> +<slide>slides/internals/scanner-ast2.xml</slide> +<slide>slides/internals/bytecode0.xml</slide> +<slide>slides/internals/bytecode½.xml</slide> +<slide>slides/internals/bytecode1.xml</slide> + +<slide>slides/internals/jumps.xml</slide> + +<!-- +AST +- Show AST for various control structures + +Opcodes +- Show AST for the same control structures +--> + +<slide>slides/internals/jumps-if.xml</slide> +<slide>slides/internals/jumps-if-else.xml</slide> + +<slide>slides/internals/rings.xml</slide> +<slide>slides/internals/jumps-for.xml</slide> +<slide>slides/internals/jumps-for-rewritten.xml</slide> +<slide>slides/internals/jumps-while.xml</slide> +<slide>slides/internals/jumps-do-while.xml</slide> +<slide>slides/internals/jumps-foreach.xml</slide> +<slide>slides/internals/jumps-complex.xml</slide> +<slide>slides/internals/loops.xml</slide> +<slide>slides/internals/jumps-complex-dot.xml</slide> + +<!-- +Try/Catch nonsense +- FASTCALL/FASTRET +--> +<slide>slides/internals/catch.xml</slide> +<slide>slides/internals/jumps-try-catch.xml</slide> + +<!-- +Dead code analysis +- Show how Xdebug does it through VLD +--> +<slide>slides/internals/deadcode.xml</slide> +<slide>slides/internals/deadcode-vld.xml</slide> + +<!-- +Path analysis +- Branch and Path +--> +<slide>slides/internals/branches.xml</slide> +<slide>slides/xdebug/vld-path-2-paths.xml</slide> +<slide>slides/xdebug/vld-path-4-code.xml</slide> +<slide>slides/xdebug/vld-path-4-output.xml</slide> + +<slide>slides/xdebug/vld-path-3-code.xml</slide> +<slide>slides/xdebug/vld-path-3-output.xml</slide> +<slide>slides/xdebug/vld-path-3-paths.xml</slide> + +<!-- Conclusion + +Code -> Tokens -> AST -> opode + +Transformation of control structures + +Useful implementations + +--> +<slide>slides/internals/recap.xml</slide> +<slide>slides/internals/recap-content.xml</slide> + + + +<slide>slides/mongodb/questions.xml</slide> + +<slide>slides/mongodb/resources.xml</slide> + +</presentation> diff --git a/slides/internals/abstract-tree.jpg b/slides/internals/abstract-tree.jpg new file mode 100644 index 0000000..86ff496 Binary files /dev/null and b/slides/internals/abstract-tree.jpg differ diff --git a/slides/internals/ast.xml b/slides/internals/ast.xml new file mode 100644 index 0000000..6684e5e --- /dev/null +++ b/slides/internals/ast.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Abstract Syntax Tree</title> +<image align="center" filename="abstract-tree.jpg" attr=""/> +</slide> diff --git a/slides/internals/ast1.xml b/slides/internals/ast1.xml new file mode 100644 index 0000000..a04e184 --- /dev/null +++ b/slides/internals/ast1.xml @@ -0,0 +1,12 @@ +<slide> +<title>Abstract Syntax Tree</title> + +<list> + <bullet>An AST describes the structure of the parser script</bullet> + <bullet>Each node is a language construct</bullet> + <bullet>Nested structures are represented through a tree</bullet> + <bullet>Not all the original information from the original source code is kept</bullet> + <bullet>The *php-ast* extension on Nikita Popov's (nikic) GitHub account can visualize them</bullet> + <bullet>Optimisations are possible by modifications of the tree</bullet> +</list> +</slide> diff --git a/slides/internals/branches.jpg b/slides/internals/branches.jpg new file mode 100644 index 0000000..737b66e Binary files /dev/null and b/slides/internals/branches.jpg differ diff --git a/slides/internals/branches.xml b/slides/internals/branches.xml new file mode 100644 index 0000000..a7229bc --- /dev/null +++ b/slides/internals/branches.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Branch Analysis</title> +<image align="center" filename="branches.jpg" attr="Sergey Kochkarev - https://www.flickr.com/photos/skochkar/24747666840/ - Public Domain"/> +</slide> diff --git a/slides/internals/bytecode.jpg b/slides/internals/bytecode.jpg new file mode 100644 index 0000000..23ffb03 Binary files /dev/null and b/slides/internals/bytecode.jpg differ diff --git a/slides/internals/bytecode0.xml b/slides/internals/bytecode0.xml new file mode 100644 index 0000000..2f836ad --- /dev/null +++ b/slides/internals/bytecode0.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Byte Code</title> +<image align="center" filename="bytecode.jpg" attr=""/> +</slide> diff --git a/slides/internals/bytecode1.xml b/slides/internals/bytecode1.xml new file mode 100644 index 0000000..58fc01c --- /dev/null +++ b/slides/internals/bytecode1.xml @@ -0,0 +1,33 @@ +<slide> +<title>AST is converted to bytecode</title> + +<example inline="1"> +1: AST_METHOD + flags: MODIFIER_PUBLIC (256) + name: |00dd00|__construct| + params: AST_PARAM_LIST + 0: AST_PARAM + name: |0000dd|"name"| + stmts: AST_STMT_LIST + 0: |dd00dd|AST_ASSIGN| + var: |00dddd|AST_PROP| + expr: AST_VAR + name: |00dddd|"this"| + prop: |00dddd|"name"| + expr: AST_VAR + name: |0000dd|"name"| +</example> +<br/> +<example inline="1"> +compiled vars: |0000dd|!0 = $name| +line #* E I O op fetch ext return operands +------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 8 0 E > EXT_NOP + 1 RECV |0000dd|!0| + 10 2 EXT_STMT + 3 |dd00dd|ASSIGN||00dddd|_OBJ| |00dddd|'name'| + 4 |dd00dd|OP_DATA| |0000dd|!0| + 11 5 EXT_STMT + 6 > RETURN null +</example> +</slide> diff --git "a/slides/internals/bytecode\302\275.xml" "b/slides/internals/bytecode\302\275.xml" new file mode 100644 index 0000000..fb36136 --- /dev/null +++ "b/slides/internals/bytecode\302\275.xml" @@ -0,0 +1,12 @@ +<slide> +<title>Byte Code</title> + +<list> + <bullet>In PHP also called Opcodes</bullet> + <bullet>Each function, method, or main body is represented by an Oparray</bullet> + <bullet>Each Oparray contains Opcodes with instructions for the Zend Engine</bullet> + <bullet>The Zend Engine executes each opcode in turn</bullet> + <bullet>Very similar to Assembler instructions</bullet> + <bullet>You can visualize them with the PECL *vld* extension</bullet> +</list> +</slide> diff --git a/slides/internals/catch.jpg b/slides/internals/catch.jpg new file mode 100644 index 0000000..0b46d18 Binary files /dev/null and b/slides/internals/catch.jpg differ diff --git a/slides/internals/catch.xml b/slides/internals/catch.xml new file mode 100644 index 0000000..792a971 --- /dev/null +++ b/slides/internals/catch.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Exceptions (Try/Catch)</title> +<image align="center" filename="catch.jpg" attr="Soggydan Benenovitch (https://www.flickr.com/photos/38518750@N00/4858510277)"/> +</slide> diff --git a/slides/internals/complex-dot.png b/slides/internals/complex-dot.png new file mode 100644 index 0000000..3fe802b Binary files /dev/null and b/slides/internals/complex-dot.png differ diff --git a/slides/internals/deadcode-vld.xml b/slides/internals/deadcode-vld.xml new file mode 100644 index 0000000..14c8224 --- /dev/null +++ b/slides/internals/deadcode-vld.xml @@ -0,0 +1,28 @@ +<slide> +<title>Dead Code</title> + +<image align="right" filename="deadcode.png"/> + +<example inline="1">&lt;?php +if ( $a &lt; 42 ) { + echo "fourty"; + return; + *echo "two";* +} +</example> + +<example inline="1"> +line #* E I O op return operands + 2 0 E > IS_SMALLER ~1 !0, 42 + 1 > JMPZ ~1, ->5 + 3 2 > ECHO 'fourty' + 4 3 > RETURN null + 5 *4\* ECHO 'two'* + 5 > > RETURN 1 +</example> + +<list> +<bullet>Follow all branches</bullet> +<bullet>Find unreached opcodes</bullet> +</list> +</slide> diff --git a/slides/internals/deadcode.png b/slides/internals/deadcode.png new file mode 100644 index 0000000..b31d3cb Binary files /dev/null and b/slides/internals/deadcode.png differ diff --git a/slides/internals/deadcode.xml b/slides/internals/deadcode.xml new file mode 100644 index 0000000..31546d8 --- /dev/null +++ b/slides/internals/deadcode.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Dead Code</title> +<image align="center" filename="rosetta.jpg" attr="Cristian Bortes - https://www.flickr.com/photos/bortescristian/2931856207 - CC-BY"/> +</slide> diff --git a/slides/internals/jumps-complex-dot.xml b/slides/internals/jumps-complex-dot.xml new file mode 100644 index 0000000..3b4503a --- /dev/null +++ b/slides/internals/jumps-complex-dot.xml @@ -0,0 +1,36 @@ +<slide> +<title>Complex Loops</title> + +<image align="right" filename="complex-dot.png"/> + +<example inline="1">&lt;?php +$a = [ 2.7, 4.9 ]; +|dd0000|foreach ( $a as $key => $value )| { + |00dd00|if ( $value &lt; 3 ) {| + echo $key; + } else { + |0000dd|if ( $key > 0 ) {| + echo $value; + } + } +} +</example> + +<example inline="1"> +line #* E I O op return operands + 2 0 E > ASSIGN !0, &lt;array> + 3 1 > |dd0000|FE_RESET_R $4 !0, ->12| + 2 > > |dd0000|FE_FETCH_R ~5 $4, !1, ->12| + 3 > |dd0000|ASSIGN !2, ~5| + 4 4 |00dd00|IS_SMALLER ~7 !1, 3| + 5 > |00dd00|JMPZ ~7, ->8| + 5 6 > ECHO !2 + 7 > |00dd00|JMP ->11| + 7 8 > |0000dd|IS_SMALLER ~8 0, !2| + 9 > |0000dd|JMPZ ~8, ->11| + 8 10 > ECHO !1 + 11 > > |dd0000|JMP ->2| + 12 > |dd0000|FE_FREE $4| + 13 > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-complex.xml b/slides/internals/jumps-complex.xml new file mode 100644 index 0000000..816efaa --- /dev/null +++ b/slides/internals/jumps-complex.xml @@ -0,0 +1,33 @@ +<slide> +<title>Complex Loops</title> + +<example inline="1">&lt;?php +$a = [ 2.7, 4.9 ]; +foreach ( $a as $key => $value ) { + if ( $value &lt; 3 ) { + echo $key; + } else { + if ( $key > 0 ) { + echo $value; + } + } +} +</example> + +<example inline="1">line #* E I O op fetch ext return operands + 2 0 E > ASSIGN !0, &lt;array> + 3 1 > FE_RESET_R $4 !0, ->12 + 2 > > FE_FETCH_R ~5 $4, !1, ->12 + 3 > ASSIGN !2, ~5 + 4 4 IS_SMALLER ~7 !1, 3 + 5 > JMPZ ~7, ->8 + 5 6 > ECHO !2 + 7 > JMP ->11 + 7 8 > IS_SMALLER ~8 0, !2 + 9 > JMPZ ~8, ->11 + 8 10 > ECHO !1 + 11 > > JMP ->2 + 12 > FE_FREE $4 + 13 > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-do-while.xml b/slides/internals/jumps-do-while.xml new file mode 100644 index 0000000..9a445e2 --- /dev/null +++ b/slides/internals/jumps-do-while.xml @@ -0,0 +1,22 @@ +<slide> +<title>DO .. WHILE</title> + +<example inline="1">&lt;?php +|dd0000|$i = 10;| +do { + |0000dd|echo $i;| +} while ( |00dd00|--$i| ); +</example> + +<example inline="1">compiled vars: !0 = $i +line #* E I O op fetch ext return operands + 2 0 E > EXT_STMT + 1 |dd0000|ASSIGN !0, 10| + 3 2 NOP + 4 3 > EXT_STMT + 4 |0000dd|ECHO !0| + 5 5 |00dd00|PRE_DEC $2 !0| + 6 > |00dd00|JMPNZ $2, ->3| + 7 > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-for-rewritten.xml b/slides/internals/jumps-for-rewritten.xml new file mode 100644 index 0000000..98a3ee0 --- /dev/null +++ b/slides/internals/jumps-for-rewritten.xml @@ -0,0 +1,36 @@ +<slide> +<title>FOR (rewritten)</title> + +<example inline="1"> +for ( |dd0000|$i = 0|; |00dd00|$i &lt; 42|; |0000dd|++$i| ) { + |dd00dd|echo $i;| +} +</example> + +<example inline="1"> + |dd0000|$i = 0;| + goto COND; +STMT: + |dd00dd|echo $i;| + |0000dd|++$i;| +COND: + |00dd00|$_ = $i &lt; 42;| + |00dd00|if ( ! $_ ) {| + |00dd00|goto STMT;| + |00dd00|}| +</example> + +<example inline="1">compiled vars: !0 = $i +line #* E I O op fetch ext return operands + 2 0 E > EXT_STMT + 1 |dd0000|ASSIGN !0, 0| + 2 > |dd0000|JMP ->6| + 4 3 > |dd00dd|EXT_STMT | + 4 |dd00dd|ECHO !0| + 2 5 |0000dd|PRE_INC !0| + 6 > |00dd00|IS_SMALLER ~3 !0, 42| + 7 |00dd00|EXT_STMT | + 8 > |00dd00|JMPNZ ~3, ->3| + 9 > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-for.xml b/slides/internals/jumps-for.xml new file mode 100644 index 0000000..e51dc1a --- /dev/null +++ b/slides/internals/jumps-for.xml @@ -0,0 +1,50 @@ +<slide> +<title>FOR</title> + +<table class="two"> +<tr> + +<td> +<example inline="1"> +&lt;?php +for ( |dd0000|$i = 0|; |00dd00|$i &lt; 42|; |0000dd|++$i| ) { + |dd00dd|echo $i;| +} +</example> +</td> + +<td> +<example inline="1"> + |dd0000|init: AST_EXPR_LIST| + 0: AST_ASSIGN + var: AST_VAR + name: "i" + expr: 0 + |00dd00|cond: AST_EXPR_LIST| + 0: AST_BINARY_OP + flags: BINARY_IS_SMALLER (19) + left: AST_VAR + name: "i" + right: 42 + |0000dd|loop: AST_EXPR_LIST| + 0: AST_PRE_INC + var: AST_VAR + name: "i" + |dd00dd|stmts: …|</example> +</td> +</tr></table> + +<example inline="1">compiled vars: !0 = $i +line #* E I O op fetch ext return operands + 2 0 E > EXT_STMT + 1 |dd0000|ASSIGN !0, 0| + 2 > |dd0000|JMP ->6| + 4 3 > |dd00dd|EXT_STMT | + 4 |dd00dd|ECHO !0| + 2 5 |0000dd|PRE_INC !0| + 6 > |00dd00|IS_SMALLER ~3 !0, 42| + 7 |00dd00|EXT_STMT | + 8 > |00dd00|JMPNZ ~3, ->3| + 9 > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-foreach.xml b/slides/internals/jumps-foreach.xml new file mode 100644 index 0000000..430a7d0 --- /dev/null +++ b/slides/internals/jumps-foreach.xml @@ -0,0 +1,28 @@ +<slide> +<title>FOREACH</title> + +<example inline="1">&lt;?php +|dd0000|$a = [ 2.7, 4.9 ];| +|00dd00|foreach ( $a as $key => $value )| { + echo $key, $value; +} +</example> + +<example inline="1"> +compiled vars: !0 = $a, !1 = $value, !2 = $key +line #* E I O op fetch ext return operands + 2 0 E > EXT_STMT + 1 |dd0000|ASSIGN !0, &lt;array>| + 3 2 EXT_STMT + 3 > |00dd00|FE_RESET_R $4 !0,| |dd00dd|->11| + *4* > > |00dd00|FE_FETCH_R ~5 $4, !1,| |dd00dd|->11| + 5 > |00dd00|ASSIGN !2, ~5| + 4 6 EXT_STMT + 7 ECHO !2 + 8 EXT_STMT + 9 ECHO !1 + 10 > *JMP ->4* + |dd00dd|11 > FE_FREE $4| + 12 > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-if-else.xml b/slides/internals/jumps-if-else.xml new file mode 100644 index 0000000..d2df699 --- /dev/null +++ b/slides/internals/jumps-if-else.xml @@ -0,0 +1,51 @@ +<slide> +<title>IF with ELSE</title> + +<table class="two"> +<tr> + +<td> +<example inline="1"> +if ( |0000dd|$a| == |00dd00|3.1415926| ) { + echo "Circles"; +} else { + echo "Squares"; +} +</example> +</td> + +<td> +<example inline="1">AST_STMT_LIST + 0: AST_IF + 0: AST_IF_ELEM + cond: AST_BINARY_OP + flags: BINARY_IS_EQUAL (17) + left: AST_VAR + name: "a" + right: 3.1415926 + stmts: AST_STMT_LIST + 0: AST_ECHO + expr: "Circles" + 1: AST_IF_ELEM + cond: null + stmts: AST_STMT_LIST + 0: AST_ECHO + expr: "Squares"</example> +</td> +</tr></table> + +<example inline="1"> +compiled vars: |0000dd|!0 = $a| +line #* E I O op return operands +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 2 0 E > EXT_STMT + 1 IS_EQUAL ~1 |0000dd|!0|, 3.14159 + 2 > *JMPZ* ~1, ->|00dddd|6| + 3 3 > EXT_STMT + 4 ECHO 'Circles' + 5 > *JMP* ->|00dddd|8| + 5 |00dddd|6| > EXT_STMT + 7 ECHO 'Squares' + |00dddd|8| > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-if.xml b/slides/internals/jumps-if.xml new file mode 100644 index 0000000..4e2e812 --- /dev/null +++ b/slides/internals/jumps-if.xml @@ -0,0 +1,36 @@ +<slide> +<title>IF</title> + +<example inline="1"> +if ( |0000dd|$a| == |00dd00|42| ) +{ + echo "To life, the universe, and everything.\n"; +} +</example> + +<example inline="1"> +AST_STMT_LIST + 0: AST_IF + 0: AST_IF_ELEM + cond: AST_BINARY_OP + flags: BINARY_IS_EQUAL (17) + left: AST_VAR + |0000dd|name: "a"| + right: |00dd00|42| + stmts: AST_STMT_LIST + 0: AST_ECHO + expr: "To life, the universe, and everything. +</example> + +<example inline="1"> +compiled vars: |0000dd|!0 = $a| +line #* E I O op return operands +----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + 2 0 E > EXT_STMT + 1 IS_EQUAL |dd0000|~1| |0000dd|!0|, |00dd00|42| + 2 > *JMPZ* |dd0000|~1|, ->|00dddd|5| + 4 3 > EXT_STMT + 4 ECHO 'To+life%2C+the+universe%2C+and+everything.%0A' + |00dddd|5| > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-try-catch.xml b/slides/internals/jumps-try-catch.xml new file mode 100644 index 0000000..68c7c84 --- /dev/null +++ b/slides/internals/jumps-try-catch.xml @@ -0,0 +1,33 @@ +<slide> +<title>TRY … CATCH … FINALLY</title> + +<example inline="1">&lt;?php +try { + echo "in try"; +} *catch* ( Exception1 $e ) { + echo "exception 1 thrown"; +} *catch* ( Exception2 $e ) { + echo "exception 2 thrown"; +|00dd00|} finally {| + |0000dd|echo "in finally";| +} +echo "after"; +</example> + +<example inline="1"> +line #* E I O op return operands + 3 0 E > ECHO 'in+try' + 1 > JMP |00dd00|->7| + 4 2 *E > > CATCH* 'Exception1', !0, |dd00dd|->5| + 5 3 > ECHO 'exception+1+thrown' + 4 > JMP |00dd00|->7| + 6 |dd00dd|5| *E > > CATCH* 'Exception2', !0 + 7 6 > ECHO 'exception+2+thrown' + 8 |00dd00|7 > > FAST_CALL| ->9 + 8 > > JMP ->11 + 9 |0000dd|9 > ECHO 'in+finally'| + |0000dd|10 > FAST_RET| + 11 11 > ECHO 'after' + 12 > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps-while.xml b/slides/internals/jumps-while.xml new file mode 100644 index 0000000..3946dde --- /dev/null +++ b/slides/internals/jumps-while.xml @@ -0,0 +1,23 @@ +<slide> +<title>WHILE</title> + +<example inline="1">&lt;?php +|dd0000|$i = 10;| +*while* ( |00dd00|--$i| ) { + |0000dd|echo $i;| +} +</example> + +<example inline="1">compiled vars: !0 = $i +line #* E I O op fetch ext return operands + 2 0 E > EXT_STMT + 1 |dd0000|ASSIGN !0, 10| + 3 2 EXT_STMT + 3 > *JMP* ->|dd00dd|6| + 4 4 > EXT_STMT + 5 |0000dd|ECHO !0| + 3 |dd00dd|6| > |00dd00|PRE_DEC $2 !0| + 7 > |00dd00|JMPNZ $2, ->4| + 8 > > RETURN 1 +</example> +</slide> diff --git a/slides/internals/jumps.jpg b/slides/internals/jumps.jpg new file mode 100644 index 0000000..84a6400 Binary files /dev/null and b/slides/internals/jumps.jpg differ diff --git a/slides/internals/jumps.xml b/slides/internals/jumps.xml new file mode 100644 index 0000000..dbf0971 --- /dev/null +++ b/slides/internals/jumps.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Jumps</title> +<image align="center" filename="jumps.jpg" attr="Thor-Wiggo Skille (http://www.nfk.no/Artikkel.aspx?AId=25071&back=1&MId1=4557&MId2=&MId3=&)"/> +</slide> diff --git a/slides/internals/loops.png b/slides/internals/loops.png new file mode 100644 index 0000000..7bfad4c Binary files /dev/null and b/slides/internals/loops.png differ diff --git a/slides/internals/loops.xml b/slides/internals/loops.xml new file mode 100644 index 0000000..8ffa712 --- /dev/null +++ b/slides/internals/loops.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Complex Loops</title> +<image align="center" filename="loops.png" attr="http://xkcd.com/657"/> +</slide> diff --git a/slides/internals/parse-state-machine.xml b/slides/internals/parse-state-machine.xml new file mode 100644 index 0000000..4db6cce --- /dev/null +++ b/slides/internals/parse-state-machine.xml @@ -0,0 +1,17 @@ +<slide> +<title>Tokenization</title> + +<list> + <bullet>It's a big state machine starting with %INITIAL%</bullet> + <bullet>States: %INITIAL%, %ST_IN_SCRIPTING%, %ST_DOUBLE_QUOTES%, %ST_NOWDOC%…</bullet> + <bullet>Tokens can change the state: + <example><![CDATA[<INITIAL>"<?php"([ \t]|{NEWLINE}) { + HANDLE_NEWLINE(yytext[yyleng-1]); + BEGIN(ST_IN_SCRIPTING); + RETURN_TOKEN(T_OPEN_TAG); +} ]]></example> + </bullet> + <bullet>No *meaning* is given to these tokens</bullet> + <bullet>PHP comes with a *tokenizer* extension</bullet> +</list> +</slide> diff --git a/slides/internals/parse-tokenization.xml b/slides/internals/parse-tokenization.xml new file mode 100644 index 0000000..ac5b2a1 --- /dev/null +++ b/slides/internals/parse-tokenization.xml @@ -0,0 +1,49 @@ +<slide> +<title>Tokenization</title> + +<div effect="fade-out"> +<blurb>Example script:</blurb> +<example inline="1"><![CDATA[<?php +namespace DramIO; +class Whisky +{ + private $name; + + public function __construct( $name ) + { + $this->name = $name; + } + + public function drink() + { + echo "Drinking {$this->name}\n"; + } +} +?>]]></example> +</div> + +<div effect="fade-in"> +<blurb>Example script:</blurb> +<example inline="1"><![CDATA[<?php +namespace DramIO; +class Whisky +{ + private $name; + + public function __construct($name) + { + $this->name = $name;␃]]></example> +<blurb>In tokens:</blurb> +<example inline="1"><![CDATA[*T_OPEN_TAG* <?php +*T_NAMESPACE* |777777|T_WS| *T_NS_SEPARATOR* *T_STRING* DramIO ; |777777|T_WS| +*T_CLASS* |777777|T_WS| *T_NS_SEPARATOR* *T_STRING* Whisky |777777|T_WS| +{ |777777|T_WS| + *T_PRIVATE* |777777|T_WS| *T_VARIABLE* $name ; |777777|T_WS| + + *T_PUBLIC* |777777|T_WS| *T_FUNCTION* |777777|T_WS| *T_STRING* __construct ( *T_VARIABLE* $name ) |777777|T_WS| + { |777777|T_WS| + *T_VARIABLE* $this *T_OBJECT_OPERATOR* -> *T_STRING* name |777777|T_WS| = |777777|T_WS| *T_VARIABLE* $name ; + ]]></example> +</div> + +</slide> diff --git a/slides/internals/recap-content.xml b/slides/internals/recap-content.xml new file mode 100644 index 0000000..4f72b29 --- /dev/null +++ b/slides/internals/recap-content.xml @@ -0,0 +1,18 @@ +<slide> +<title>Recap</title> + +<list> + <bullet>Stages:<br/>Code → Tokens → Parsing → AST → Byte Code</bullet> + <bullet>All looping structures are jumps</bullet> + <bullet>Code analysis for Fun and Profit</bullet> +</list> + +<break/> +<blurb>*Tools*</blurb> +<list> + <bullet>PHP's tokenizer: http://php.net/tokenizer</bullet> + <bullet>Nikita's AST extension: https://github.com/nikic/php-ast</bullet> + <bullet>VLD: https://pecl.php.net/vld</bullet> +</list> + +</slide> diff --git a/slides/internals/recap.jpg b/slides/internals/recap.jpg new file mode 100644 index 0000000..95fb736 Binary files /dev/null and b/slides/internals/recap.jpg differ diff --git a/slides/internals/recap.xml b/slides/internals/recap.xml new file mode 100644 index 0000000..e129dc0 --- /dev/null +++ b/slides/internals/recap.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Recap</title> +<image align="center" filename="recap.jpg" attr="Jens Johnsson - https://www.pexels.com/photo/mountains-nature-arrow-guide-66100/ - CC0"/> +</slide> diff --git a/slides/internals/ring.png b/slides/internals/ring.png new file mode 100644 index 0000000..795fb64 Binary files /dev/null and b/slides/internals/ring.png differ diff --git a/slides/internals/rings.xml b/slides/internals/rings.xml new file mode 100644 index 0000000..a4d0dc9 --- /dev/null +++ b/slides/internals/rings.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Rings</title> +<image align="center" filename="ring.png" attr="© OpenStreetMap contributors"/> +</slide> diff --git a/slides/internals/rosetta.jpg b/slides/internals/rosetta.jpg new file mode 100644 index 0000000..ca52e15 Binary files /dev/null and b/slides/internals/rosetta.jpg differ diff --git a/slides/internals/scanner-ast.xml b/slides/internals/scanner-ast.xml new file mode 100644 index 0000000..09b8009 --- /dev/null +++ b/slides/internals/scanner-ast.xml @@ -0,0 +1,327 @@ +<slide> +<title>Abstract Syntax Tree</title> + +<blurb>Raw output from %ast\parse_code% function:</blurb> +<example> +object(ast\Node)#1 (4) { + ["kind"]=> + int(133) + ["flags"]=> + int(0) + ["lineno"]=> + int(1) + ["children"]=> + array(2) { + [0]=> + object(ast\Node)#2 (4) { + ["kind"]=> + int(257) + ["flags"]=> + int(0) + ["lineno"]=> + int(2) + ["children"]=> + array(1) { + ["name"]=> + object(ast\Node)#3 (4) { + ["kind"]=> + int(2048) + ["flags"]=> + int(2) + ["lineno"]=> + int(2) + ["children"]=> + array(1) { + ["name"]=> + string(6) "DramIO" + } + } + } + } + [1]=> + object(ast\Node\Decl)#4 (7) { + ["kind"]=> + int(69) + ["flags"]=> + int(0) + ["lineno"]=> + int(4) + ["children"]=> + array(3) { + ["extends"]=> + NULL + ["implements"]=> + NULL + ["stmts"]=> + object(ast\Node)#5 (4) { + ["kind"]=> + int(133) + ["flags"]=> + int(0) + ["lineno"]=> + int(5) + ["children"]=> + array(3) { + [0]=> + object(ast\Node)#6 (4) { + ["kind"]=> + int(139) + ["flags"]=> + int(1024) + ["lineno"]=> + int(6) + ["children"]=> + array(1) { + [0]=> + object(ast\Node)#7 (4) { + ["kind"]=> + int(774) + ["flags"]=> + int(0) + ["lineno"]=> + int(6) + ["children"]=> + array(2) { + ["name"]=> + string(4) "name" + ["default"]=> + NULL + } + } + } + } + [1]=> + object(ast\Node\Decl)#8 (7) { + ["kind"]=> + int(68) + ["flags"]=> + int(256) + ["lineno"]=> + int(8) + ["children"]=> + array(4) { + ["params"]=> + object(ast\Node)#9 (4) { + ["kind"]=> + int(137) + ["flags"]=> + int(0) + ["lineno"]=> + int(8) + ["children"]=> + array(1) { + [0]=> + object(ast\Node)#10 (4) { + ["kind"]=> + int(773) + ["flags"]=> + int(0) + ["lineno"]=> + int(8) + ["children"]=> + array(3) { + ["type"]=> + NULL + ["name"]=> + string(4) "name" + ["default"]=> + NULL + } + } + } + } + ["uses"]=> + NULL + ["stmts"]=> + object(ast\Node)#11 (4) { + ["kind"]=> + int(133) + ["flags"]=> + int(0) + ["lineno"]=> + int(9) + ["children"]=> + array(1) { + [0]=> + object(ast\Node)#12 (4) { + ["kind"]=> + int(517) + ["flags"]=> + int(0) + ["lineno"]=> + int(10) + ["children"]=> + array(2) { + ["var"]=> + object(ast\Node)#13 (4) { + ["kind"]=> + int(513) + ["flags"]=> + int(0) + ["lineno"]=> + int(10) + ["children"]=> + array(2) { + ["expr"]=> + object(ast\Node)#14 (4) { + ["kind"]=> + int(256) + ["flags"]=> + int(0) + ["lineno"]=> + int(10) + ["children"]=> + array(1) { + ["name"]=> + string(4) "this" + } + } + ["prop"]=> + string(4) "name" + } + } + ["expr"]=> + object(ast\Node)#15 (4) { + ["kind"]=> + int(256) + ["flags"]=> + int(0) + ["lineno"]=> + int(10) + ["children"]=> + array(1) { + ["name"]=> + string(4) "name" + } + } + } + } + } + } + ["returnType"]=> + NULL + } + ["endLineno"]=> + int(11) + ["name"]=> + string(11) "__construct" + ["docComment"]=> + NULL + } + [2]=> + object(ast\Node\Decl)#16 (7) { + ["kind"]=> + int(68) + ["flags"]=> + int(256) + ["lineno"]=> + int(13) + ["children"]=> + array(4) { + ["params"]=> + object(ast\Node)#17 (4) { + ["kind"]=> + int(137) + ["flags"]=> + int(0) + ["lineno"]=> + int(13) + ["children"]=> + array(0) { + } + } + ["uses"]=> + NULL + ["stmts"]=> + object(ast\Node)#18 (4) { + ["kind"]=> + int(133) + ["flags"]=> + int(0) + ["lineno"]=> + int(14) + ["children"]=> + array(1) { + [0]=> + object(ast\Node)#19 (4) { + ["kind"]=> + int(282) + ["flags"]=> + int(0) + ["lineno"]=> + int(15) + ["children"]=> + array(1) { + ["expr"]=> + object(ast\Node)#20 (4) { + ["kind"]=> + int(131) + ["flags"]=> + int(0) + ["lineno"]=> + int(15) + ["children"]=> + array(3) { + [0]=> + string(9) "Drinking " + [1]=> + object(ast\Node)#21 (4) { + ["kind"]=> + int(513) + ["flags"]=> + int(0) + ["lineno"]=> + int(15) + ["children"]=> + array(2) { + ["expr"]=> + object(ast\Node)#22 (4) { + ["kind"]=> + int(256) + ["flags"]=> + int(0) + ["lineno"]=> + int(15) + ["children"]=> + array(1) { + ["name"]=> + string(4) "this" + } + } + ["prop"]=> + string(4) "name" + } + } + [2]=> + string(1) " +" + } + } + } + } + } + } + ["returnType"]=> + NULL + } + ["endLineno"]=> + int(16) + ["name"]=> + string(5) "drink" + ["docComment"]=> + NULL + } + } + } + } + ["endLineno"]=> + int(17) + ["name"]=> + string(6) "Whisky" + ["docComment"]=> + NULL + } + } +} +</example> +</slide> diff --git a/slides/internals/scanner-ast2.xml b/slides/internals/scanner-ast2.xml new file mode 100644 index 0000000..1a05cc3 --- /dev/null +++ b/slides/internals/scanner-ast2.xml @@ -0,0 +1,88 @@ +<slide> +<title>Abstract Syntax Tree (formatted)</title> + +<div effect="fade-out"> +<example inline="1">AST_STMT_LIST + 0: AST_CONST + … + 1: *AST_CLASS* + flags: 0 + name: Whisky + extends: null + implements: null + stmts: AST_STMT_LIST + 0: AST_PROP_DECL + flags: MODIFIER_PRIVATE (1024) + 0: AST_PROP_ELEM + name: "name" + default: null + 1: *AST_METHOD* + flags: *MODIFIER_PUBLIC* (256) + name: *__construct* + params: AST_PARAM_LIST + 0: AST_PARAM + flags: 0 + type: null + name: "name" + default: null + uses: null + *stmts: AST_STMT_LIST* + 0: AST_ASSIGN + var: AST_PROP + expr: AST_VAR + name: "this" + prop: "name" + expr: AST_VAR + name: "name" + returnType: null + 2: AST_METHOD + flags: MODIFIER_PUBLIC (256) + name: drink + params: AST_PARAM_LIST + uses: null + stmts: AST_STMT_LIST + 0: AST_ECHO + expr: AST_ENCAPS_LIST + 0: "Drinking " + 1: AST_PROP + expr: AST_VAR + name: "this" + prop: "name" + 2: " + " + returnType: null</example> +</div> + +<div effect="fade-in"> +<example inline="1"> +|0000dd|public| function |00dd00|__construct|( |0000dd|$name| ) +{ + |00dddd|$this->name| |dd00dd|=| |ff0000|$name| +} +</example> + +<br/> + +<example inline="1"> +1: AST_METHOD + flags: MODIFIER_PUBLIC (256) + name: |00dd00|__construct| + params: AST_PARAM_LIST + 0: AST_PARAM + flags: 0 + type: null + name: |0000dd|"name"| + default: null + uses: null + stmts: AST_STMT_LIST + 0: |dd00dd|AST_ASSIGN| + var: |00dddd|AST_PROP| + expr: AST_VAR + name: |00dddd|"this"| + prop: |00dddd|"name"| + expr: AST_VAR + name: |ff0000|"name"| + returnType: null +</example> +</div> +</slide> diff --git a/slides/internals/scanner-rules.xml b/slides/internals/scanner-rules.xml new file mode 100644 index 0000000..21fd79f --- /dev/null +++ b/slides/internals/scanner-rules.xml @@ -0,0 +1,30 @@ +<slide> +<title>Scanner Rules</title> + +<list> + <bullet>Gives Meaning to Tokens</bullet> + <bullet>Constructs AST through Rules: +<example inline="1">class_declaration_statement: + class_modifiers T_CLASS |dd00dd|{ $&lt;num>$ = CG(zend_lineno); }| + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = *zend_ast_create_decl*(ZEND_AST_CLASS, $1, $&lt;num>3, $7, *zend_ast_get_str*($4), $5, $6, $9, NULL); } + | +… +; +</example> + <example inline="1"> + case 167: +#line 493 "/home/derick/dev/php/php-src.git/Zend/zend_language_parser.y" + |dd00dd|{ (yyval.num) = CG(zend_lineno); }| + break; + + case 168: +#line 495 "/home/derick/dev/php/php-src.git/Zend/zend_language_parser.y" + { (yyval.ast) = *zend_ast_create_decl*(ZEND_AST_CLASS, (yyvsp[(1) - (10)].num), (yyvsp[(3) - (10)].num), (yyvsp[(7) - (10)].str), *zend_ast_get_str*((yyvsp[(4) - (10)].ast)), (yyvsp[(5) - (10)].ast), (yyvsp[(6) - (10)].ast), (yyvsp[(9) - (10)].ast), NULL); } + break; + </example> + +</bullet> +</list> +</slide> + diff --git a/slides/internals/scanner.xml b/slides/internals/scanner.xml new file mode 100644 index 0000000..c8982e4 --- /dev/null +++ b/slides/internals/scanner.xml @@ -0,0 +1,31 @@ +<slide> +<title>Scanner Rules</title> + +<example inline="1">top_statement: + statement { $$ = $1; } + | function_declaration_statement { $$ = $1; } + | |0000dd|class_declaration_statement| { $$ = $1; } + | trait_declaration_statement { $$ = $1; } +… +; + +|0000dd|class_declaration_statement|: + |dd00dd|class_modifiers| T_CLASS { $&lt;num>$ = CG(zend_lineno); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = *zend_ast_create_decl*(ZEND_AST_CLASS, $1, $&lt;num>3, $7, *zend_ast_get_str*($4), $5, $6, $9, NULL); } + | T_CLASS { $&lt;num>$ = CG(zend_lineno); } + T_STRING extends_from implements_list backup_doc_comment '{' class_statement_list '}' + { $$ = *zend_ast_create_decl*(ZEND_AST_CLASS, 0, $&lt;num>2, $6, *zend_ast_get_str*($3), $4, $5, $8, NULL); } +; + +|dd00dd|class_modifiers|: + |00dddd|class_modifier| { $$ = $1; } + | |dd00dd|class_modifiers| |00dddd|class_modifier| { $$ = *zend_add_class_modifier*($1, $2); } +; + +|00dddd|class_modifier|: + T_ABSTRACT { $$ = ZEND_ACC_EXPLICIT_ABSTRACT_CLASS; } + | T_FINAL { $$ = ZEND_ACC_FINAL; } +; +</example> +</slide> diff --git a/slides/internals/stages.jpg b/slides/internals/stages.jpg new file mode 100644 index 0000000..cdca15c Binary files /dev/null and b/slides/internals/stages.jpg differ diff --git a/slides/internals/stages.xml b/slides/internals/stages.xml new file mode 100644 index 0000000..087efc4 --- /dev/null +++ b/slides/internals/stages.xml @@ -0,0 +1,54 @@ +<slide> +<title style="center">Stages</title> + +<break lines="2"/> +<div effect="fade-out"> + <div class="center"> + <blurb>Parse Script</blurb> + <blurb>Create Logical Representation</blurb> + <blurb>Create Executable Code</blurb> + <blurb>Execute Bytecode</blurb> + </div> +</div> + +<div effect="fade-in-out"> + <div class="center"> + <blurb>*Parse Script*</blurb> + <blurb class="small">Convert code into Tokens</blurb><br/> + <blurb>Create Logical Representation</blurb> + <blurb>Create Executable Code</blurb> + <blurb>Execute Bytecode</blurb> + </div> +</div> + +<div effect="fade-in-out"> + <div class="center"> + <blurb>Parse Script</blurb><br/> + <blurb>*Create Logical Representation*</blurb> + <blurb class="small">Convert Tokens into Abstract Syntax Tree (AST)</blurb><br/> + <blurb>Create Executable Code</blurb> + <blurb>Execute Bytecode</blurb> + </div> +</div> + +<div effect="fade-in-out"> + <div class="center"> + <blurb>Parse Script</blurb> + <blurb>Create Logical Representation</blurb><br/> + <blurb>*Create Executable Code*</blurb> + <blurb class="small">Convert AST into Opcodes (Bytecode)</blurb><br/> + <blurb>Execute Bytecode</blurb> + </div> +</div> + +<div effect="fade-in"> + <div class="center"> + <blurb>Parse Script</blurb> + <blurb>Create Logical Representation</blurb> + <blurb>Create Executable Code</blurb><br/> + <blurb>*Execute Bytecode*</blurb> + <blurb class="small">Run the Opcodes with the Zend Engine</blurb><br/> + </div> +</div> + +</slide> diff --git a/slides/internals/title-agenda.xml b/slides/internals/title-agenda.xml new file mode 100644 index 0000000..0eed01b --- /dev/null +++ b/slides/internals/title-agenda.xml @@ -0,0 +1,9 @@ +<slide> +<title>Agenda</title> + +<list> + <bullet>Stages</bullet> + <bullet>Conversion Stages</bullet> + <bullet>Conclusion</bullet> +</list> +</slide> diff --git a/slides/internals/title-parsing.xml b/slides/internals/title-parsing.xml new file mode 100644 index 0000000..d43564f --- /dev/null +++ b/slides/internals/title-parsing.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Parsing</title> +<image align="center" filename="tokens.jpg" attr="$1LENCE D00600D at English Wikipedia"/> +</slide> diff --git a/slides/internals/title-stages.xml b/slides/internals/title-stages.xml new file mode 100644 index 0000000..f6a1f31 --- /dev/null +++ b/slides/internals/title-stages.xml @@ -0,0 +1,4 @@ +<slide style="title"> +<title>Stages</title> +<image align="center" filename="stages.jpg" attr=""/> +</slide> diff --git a/slides/internals/tokens.jpg b/slides/internals/tokens.jpg new file mode 100644 index 0000000..389d2bd Binary files /dev/null and b/slides/internals/tokens.jpg differ diff --git a/slides/mongodb/me.xml b/slides/mongodb/me.xml index e0d34b0..59a5055 100644 --- a/slides/mongodb/me.xml +++ b/slides/mongodb/me.xml @@ -6,7 +6,7 @@ <blurb>Derick Rethans</blurb> <list> - <bullet>I'm *Dutch/British* living in London</bullet> + <bullet>I'm ---Dutch/British--- European, living in London</bullet> <bullet>One of the PHP/HHVM *MongoDB* driver maintainers</bullet> <bullet>Author of the PHP debugger tool *Xdebug*, PHP's Date/Time/Timezone support, and a bunch of other PHP extensions</bullet> <bullet>I ♥🌍 maps, I ♥🍺 beer, I ♥🥃 whisky</bullet> diff --git a/templates/default/div.ezt b/templates/default/div.ezt index b21712b..69d3d39 100644 --- a/templates/default/div.ezt +++ b/templates/default/div.ezt @@ -1,7 +1,7 @@ {use $node, $pres} {var $class = get_attribute( $node, 'class' )} {var $effect = get_attribute( $node, 'effect' )} -<div{if $effect} class='effect-{$effect}'{/if}{if $class} class="{$class}"{/if}> +<div{if $effect || $class} class='{/if}{if $effect}effect-{$effect}{/if} {if $class}{literal} {/literal}{$class}{/if} {if $effect || $class}'{/if}> {foreach $node->childNodes as $child} {if is_set( $child->tagName )} {include $child->tagName . '.ezt' send $child as $node, $pres}