[PRES] [presentations] master: AFUP talk

[email protected] (Derick Rethans) Tue, 30 Sep 2025 12:15:05 +0000
Newsgroups php.pres
Message-ID <[email protected]>
Author: Derick Rethans (derickr)
Date: 2025-09-30T13:14:59+01:00

Commit: https://github.com/php/presentations/commit/5dfacc6b9c343c52e68a057b00fe7ddacd91d70b
Raw diff: https://github.com/php/presentations/commit/5dfacc6b9c343c52e68a057b00fe7ddacd91d70b.diff

AFUP talk

Changed paths:
  A  php-afup25.xml
  A  slides/internals/abstain.png
  A  slides/internals/php85-array-first-last.xml
  A  slides/internals/php85-clone-with.xml
  A  slides/internals/php85-deprecated-trait-attribute.xml
  A  slides/internals/php85-error-backtraces.xml
  A  slides/internals/php85-filter-throw.xml
  A  slides/internals/php85-final-property-promotion.xml
  A  slides/internals/php85-grapheme-add-locale.xml
  A  slides/internals/php85-grapheme-levenstein3.xml
  A  slides/internals/php85-opcache-required.xml
  A  slides/internals/php85-pipes-debug.xml
  A  slides/internals/php85-pipes-examples.xml
  A  slides/internals/php85-url-parsing-api.xml
  A  slides/internals/pie.jpg
  A  slides/internals/policy-adopt-pie.xml
  A  slides/internals/policy-vote-abstain.xml
  A  templates/default/ordered-list.ezt
  M  slides/internals/php85-pipes.xml


Diff:

diff --git a/php-afup25.xml b/php-afup25.xml
new file mode 100644
index 00000000..d216f44f
--- /dev/null
+++ b/php-afup25.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="utf-8"?>
+<presentation css="derick.css">
+<topic>PHP</topic>
+<title>What's New in PHP 8.5</title>
+<event>Forum PHP 2025</event>
+<location>Paris, France</location>
+<date>October 9th, 2025</date>
+<speaker>Derick Rethans</speaker>
+<email>[email protected]</email>
+<twitter>derickr</twitter>
+<mastodon>@[email protected]</mastodon>
+<url>https://derickrethans.nl/talks/php-afup25</url>
+<joindin></joindin>
+<slide>slides/xdebug/title.xml</slide>
+<slide>slides/mongodb/me.xml</slide>
+
+
+Syntax:
+
+https://wiki.php.net/rfc/closures_in_const_expr
+<slide>slides/internals/php85-closures-in-constant-expressions.xml</slide>
+
+https://wiki.php.net/rfc/fcc_in_const_expr
+<slide>slides/internals/php85-fcc-in-constant-expressions.xml</slide>
+
+https://wiki.php.net/rfc/pipe-operator-v3
+<slide>slides/internals/php85-pipes.xml</slide>
+<slide>slides/internals/php85-pipes-examples.xml</slide>
+<slide>slides/internals/php85-pipes-debug.xml</slide>
+
+https://wiki.php.net/rfc/asymmetric-visibility-v2
+<slide>slides/internals/php84-asymmetric-visibility.xml</slide>
+<slide>slides/internals/php84-asymmetric-visibility-2.xml</slide>
+
+https://wiki.php.net/rfc/static-aviz
+<slide>slides/internals/php85-asymmetric-visibility-static.xml</slide>
+
+https://wiki.php.net/rfc/final_promotion
+<slide>slides/internals/php85-final-property-promotion.xml</slide>
+
+https://wiki.php.net/rfc/clone_with_v2
+<slide>slides/internals/php85-clone-with.xml</slide>
+
+Major additions:
+
+https://wiki.php.net/rfc/url_parsing_api
+<slide>slides/internals/php85-url-parsing-api.xml</slide>
+
+https://wiki.php.net/rfc/make_opcache_required
+<slide>slides/internals/php85-opcache-required.xml</slide>
+
+
+Minor additions:
+
+Attributes:
+
+https://wiki.php.net/rfc/attributes-on-constants
+<slide>slides/internals/php85-attributes-on-constants.xml</slide>
+
+https://wiki.php.net/rfc/marking_return_value_as_important
+<slide>slides/internals/php85-nodiscard-attribute.xml</slide>
+
+https://wiki.php.net/rfc/deprecated_traits
+<slide>slides/internals/php85-deprecated-trait-attribute.xml</slide>
+
+<!--
+https://wiki.php.net/rfc/delayedtargetvalidation_attribute
+<s lide>slides/internals/php85-delayed-target-validation-attribute.xml</slide>
+-->
+
+https://wiki.php.net/rfc/error_backtraces_v2
+<slide>slides/internals/php85-error-backtraces.xml</slide>
+
+https://wiki.php.net/rfc/filter_throw_on_failure
+<slide>slides/internals/php85-filter-throw.xml</slide>
+
+https://wiki.php.net/rfc/array_first_last
+<slide>slides/internals/php85-array-first-last.xml</slide>
+
+
+Language stuff:
+https://wiki.php.net/rfc/grapheme_str_split
+<slide>slides/internals/php84-grapheme-str-split.xml</slide>
+<slide>slides/internals/php84-grapheme-str-split-2.xml</slide>
+<slide>slides/internals/php84-grapheme-str-split-3.xml</slide>
+
+https://wiki.php.net/rfc/grapheme_levenshtein
+<slide>slides/internals/php85-grapheme-levenstein.xml</slide>
+<slide>slides/internals/php85-grapheme-levenstein2.xml</slide>
+
+https://wiki.php.net/rfc/grapheme_add_locale_for_case_insensitive
+<slide>slides/internals/php85-grapheme-levenstein3.xml</slide>
+<slide>slides/internals/php85-grapheme-add-locale.xml</slide>
+
+Deprecations:
+
+Deprecations for PHP 8.5
+https://wiki.php.net/rfc/deprecations_php_8_5
+
+
+Policy:
+
+Abstain voting option for RFCs
+https://wiki.php.net/rfc/rfc_vote_abstain
+<slide>slides/internals/policy-vote-abstain.xml</slide>
+
+Recommend PIE and deprecate PECL
+https://wiki.php.net/rfc/adopt_pie_deprecate_pecl
+<slide>slides/internals/policy-adopt-pie.xml</slide>
+
+
+<slide>slides/internals/questions-new.xml</slide>
+<slide>slides/xdebug/resources.xml</slide>
+
+</presentation>
diff --git a/slides/internals/abstain.png b/slides/internals/abstain.png
new file mode 100644
index 00000000..8fd456e2
Binary files /dev/null and b/slides/internals/abstain.png differ
diff --git a/slides/internals/php85-array-first-last.xml b/slides/internals/php85-array-first-last.xml
new file mode 100644
index 00000000..e0ade50d
--- /dev/null
+++ b/slides/internals/php85-array-first-last.xml
@@ -0,0 +1,73 @@
+<slide>
+<title>PHP 8.5: array_first() and array_last()</title>
+
+<div effect="fade-out">
+<blurb>PHP 7.3 added %array_key_first()% and %array_key_last()%:</blurb>
+<example><![CDATA[<?php
+$timezone = new DateTimeZone("Europe/Kyiv");
+$trans = $timezone->getTransitions();
+var_dump(array_key_first($trans), array_key_last($trans));
+]]></example>
+
+<example>
+int(0)
+int(122)
+</example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>But accessing values was still "odd":</blurb>
+<example inline="1"><![CDATA[&lt;?php
+$timezone = new DateTimeZone("Europe/Kyiv");
+*$trans* = $timezone->getTransitions();
+var_dump(*$trans*[array_key_first(*$trans*)], *$trans*[array_key_last(*$trans*)]);
+]]></example>
+
+<example>
+array(5) {
+  ["ts"]=> int(-9223372036854775808)
+  ["time"]=> string(34) "-292277022657-01-27T08:29:52+00:00"
+  ["offset"]=> int(7324)
+  ["isdst"]=> bool(false)
+  ["abbr"]=> string(3) "LMT"
+}
+array(5) {
+  ["ts"]=> int(2140045200)
+  ["time"]=> string(25) "2037-10-25T01:00:00+00:00"
+  ["offset"]=> int(7200)
+  ["isdst"]=> bool(false)
+  ["abbr"]=> string(3) "EET"
+}
+</example>
+</div>
+
+<div effect="fade-in">
+<blurb>PHP 8.5 adds %array_first()% and %array_last()%:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+$timezone = new DateTimeZone("Europe/Kyiv");
+*$trans* = $timezone->getTransitions();
+var_dump(*array_first*(*$trans*), *array_last*(*$trans*));
+]]></example>
+
+<example>
+array(5) {
+  ["ts"]=> int(-9223372036854775808)
+  ["time"]=> string(34) "-292277022657-01-27T08:29:52+00:00"
+  ["offset"]=> int(7324)
+  ["isdst"]=> bool(false)
+  ["abbr"]=> string(3) "LMT"
+}
+array(5) {
+  ["ts"]=> int(2140045200)
+  ["time"]=> string(25) "2037-10-25T01:00:00+00:00"
+  ["offset"]=> int(7200)
+  ["isdst"]=> bool(false)
+  ["abbr"]=> string(3) "EET"
+}
+</example>
+</div>
+
+</slide>
+
+
+
diff --git a/slides/internals/php85-clone-with.xml b/slides/internals/php85-clone-with.xml
new file mode 100644
index 00000000..30a32f01
--- /dev/null
+++ b/slides/internals/php85-clone-with.xml
@@ -0,0 +1,55 @@
+<slide>
+<title>PHP 8.5: Clone With</title>
+
+<div effect="fade-out">
+<blurb>Applying a ~wither~ method:</blurb>
+<example><![CDATA[<?php
+final readonly class Response {
+    public function __construct(
+        public int $statusCode,
+        public string $reasonPhrase,
+    ) {}
+ 
+    public function withStatus($code, $reasonPhrase = ''): Response
+    {
+        $values = get_object_vars($this);
+        $values['statusCode'] = $code;
+        $values['reasonPhrase'] = $reasonPhrase;
+        return new self(
+            ...$values
+        );
+    }
+}]]></example>
+<list>
+    <bullet>Only works if all properties are assignable via %__construct%</bullet>
+</list>
+</div>
+
+<div effect="fade-in">
+<blurb>PHP 8.5: Change %clone% to a function with optional arguments:</blurb>
+<example><![CDATA[<?php
+final readonly class Response {
+    public function __construct(
+        public int $statusCode,
+        public string $reasonPhrase,
+    ) {}
+ 
+    public function withStatus($code, $reasonPhrase = ''): Response
+    {
+        return clone($this, [
+            "statusCode" => $code,
+            "reasonPhrase" => $reasonPhrase,
+        ]);
+    }
+}]]></example>
+</div>
+
+<div effect="fade-in">
+<list>
+    <bullet>The magic %__clone% method will still be called (first)</bullet>
+    <bullet>Visibility, Property Hooks, and %__set% all work as expected</bullet>
+    <bullet>%readonly% is temporarily not enforced during cloning</bullet>
+</list>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-deprecated-trait-attribute.xml b/slides/internals/php85-deprecated-trait-attribute.xml
new file mode 100644
index 00000000..336e5f27
--- /dev/null
+++ b/slides/internals/php85-deprecated-trait-attribute.xml
@@ -0,0 +1,31 @@
+<slide>
+<title>PHP 8.5: Deprecated Trait Attribute</title>
+
+<blurb>PHP 8.4 introduced the %#[Deprecated]% attribute:</blurb>
+
+<example result="0"><![CDATA[<?php
+class Clazz {
+    #[\Deprecated("use test() instead", since: "2024-10-08")]
+    function test2() {
+    }
+}
+
+$c = new Clazz()->test2();
+?>]]></example>
+
+<div effect="fade-in">
+<blurb>PHP 8.5 adds support for the attributes on *traits*:</blurb>
+
+<example result="3"><![CDATA[<?php
+#[\Deprecated]
+trait LoggingTrait
+{
+    public function log(string $message) : void
+    {
+        // Log the message
+    }
+}
+?>]]></example>
+
+</div>
+</slide>
diff --git a/slides/internals/php85-error-backtraces.xml b/slides/internals/php85-error-backtraces.xml
new file mode 100644
index 00000000..5e0ce02c
--- /dev/null
+++ b/slides/internals/php85-error-backtraces.xml
@@ -0,0 +1,45 @@
+<slide>
+<title>PHP 8.5: Backtrace with Fatal Error</title>
+
+<example inline="1"><![CDATA[&lt;?php |aaaaaa|// mydatetimeclass.php|
+class MyDateTimeClass implements DateTimeInterface {}]]></example>
+
+<example inline="1"><![CDATA[&lt;?php |aaaaaa|// autoload.php|
+function autoloader(string $class) {
+    include strtolower($class) . '.php';
+}
+spl_autoload_register(autoloader(...));]]></example>
+
+<example inline="1"><![CDATA[&lt;?php |aaaaaa|// code.php|
+require 'autoload.php';
+$mdt = new MyDateTimeClass(); ]]></example>
+
+<div effect="fade-in-out">
+<blurb>PHP 8.4:</blurb>
+<example>Fatal error: DateTimeInterface can't be implemented by user classes in
+    mydatetimeclass.php on line 2</example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>PHP 8.5:</blurb>
+<example>Fatal error: DateTimeInterface can't be implemented by user classes in
+    mydatetimeclass.php on line 2
+Stack trace:
+#0 autoload.php(4): include()
+#1 test.php(4): autoloader('MyDateTimeClass')
+#2 {main}</example>
+</div>
+
+<div effect="fade-in">
+<blurb>PHP with Xdebug:</blurb>
+<example>Fatal error: DateTimeInterface can't be implemented by user classes in
+     mydatetimeclass.php on line 2
+
+Call Stack:
+    0.0002     520872   1. {main}() test.php:0
+    0.0003     523432   2. autoloader($class = 'MyDateTimeClass') test.php:4
+    0.0003     525000   3. include('mydatetimeclass.php') autoload.php:4
+</example>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-filter-throw.xml b/slides/internals/php85-filter-throw.xml
new file mode 100644
index 00000000..c07da6e6
--- /dev/null
+++ b/slides/internals/php85-filter-throw.xml
@@ -0,0 +1,68 @@
+<slide>
+<title>PHP 8.5: FILTER_THROW_ON_FAILURE</title>
+
+<div effect="fade-out">
+<blurb>Default %filter_var% behaviour:</blurb>
+<example><![CDATA[<?php
+function validateUser(string $email, string $userId, string $userName) : bool
+{
+    if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
+        return false;
+    }
+    if (filter_var($userId, FILTER_VALIDATE_INT) === false) {
+        return false;
+    }
+    if (filter_var(
+        $userName, FILTER_VALIDATE_REGEXP,
+        ['options' => ['regexp' => '/^[a-z]+$/']]
+    ) === false) {
+        return false;
+    }
+    return true;
+}]]>
+</example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>%filter_var% with %FILTER_NULL_ON_FAILURE%:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+function validateUser(string $email, string $userId, string $userName) : bool
+{
+    if (filter_var($email, FILTER_VALIDATE_EMAIL, *FILTER_NULL_ON_FAILURE*) === *null*) {
+        return false;
+    }
+    if (filter_var($userId, FILTER_VALIDATE_INT, *FILTER_NULL_ON_FAILURE*) === *null*) {
+        return false;
+    }
+    if (filter_var(
+        $userName, FILTER_VALIDATE_REGEXP,
+        ['options' => ['regexp' => '/^[a-z]+$/'], 'flags' => *FILTER_NULL_ON_FAILURE*]
+    ) === *null*) {
+        return false;
+    }
+    return true;
+}]]>
+</example>
+</div>
+
+<div effect="fade-in">
+<blurb>PHP 8.5: %filter_var% with %FILTER_THROW_ON_FAILURE%:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+function validateUser(string $email, string $userId, string $userName) : bool
+{
+    try {
+        filter_var($email, FILTER_VALIDATE_EMAIL, *FILTER_THROW_ON_FAILURE*);
+        filter_var($userId, FILTER_VALIDATE_INT, *FILTER_THROW_ON_FAILURE*);
+        filter_var(
+            $userName, FILTER_VALIDATE_REGEXP,
+            ['options' => ['regexp' => '/^[a-z]+$/'], 'flags' => *FILTER_THROW_ON_FAILURE*]
+        );
+        return true;
+    } catch (*\Filter\FilterFailedException* $e) {
+        return false;
+    }
+}]]>
+</example>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-final-property-promotion.xml b/slides/internals/php85-final-property-promotion.xml
new file mode 100644
index 00000000..92cf9291
--- /dev/null
+++ b/slides/internals/php85-final-property-promotion.xml
@@ -0,0 +1,72 @@
+<slide>
+<title>PHP 8.5: Final Property Promotion</title>
+
+<div effect="fade-out">
+<blurb>PHP 8.0 introduced property promotion:</blurb>
+<example inline="1">
+&amp;lt;?php
+class πŸ₯ƒwhisky 
+{
+    function __construct(
+        *public*    string $name,
+        *protected* Distillery $distillery,
+    ) {}
+}
+</example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>PHP 8.4 introduced property hooks:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+class User implements Named
+{
+    public function __construct(
+        private string $first,
+        private string $last,
+    ) {}
+
+    public string $fullName {
+        *get* *=>* $this->first . " " . $this->last;
+        *set {* [$this->first, $this->last] = explode(' ', $value); *}*
+    }
+}
+?>]]></example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>PHP 8.4 introduced property hooks and %final% properties:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+class User implements Named
+{
+    public function __construct(
+        private string $first,
+        private string $last,
+    ) {}
+
+    *final* public string $fullName {
+        get => $this->first . " " . $this->last;
+        set { [$this->first, $this->last] = explode(' ', $value); }
+    }
+}
+?>]]></example>
+</div>
+
+<div effect="fade-in-out">
+<blurb>PHP 8.5 adds %final% for promoted properties:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+class User implements Named
+{
+    public function __construct(
+        *final* ---private--- string $first,
+        *final* ---private--- string $last,
+    ) {}
+
+    final public string $fullName {
+        get => $this->first . " " . $this->last;
+        set { [$this->first, $this->last] = explode(' ', $value); }
+    }
+}
+?>]]></example>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-grapheme-add-locale.xml b/slides/internals/php85-grapheme-add-locale.xml
new file mode 100644
index 00000000..f307de74
--- /dev/null
+++ b/slides/internals/php85-grapheme-add-locale.xml
@@ -0,0 +1,16 @@
+<slide>
+<title>PHP 8.5: locale parameter for other grapheme_* functions</title>
+
+<blurb class="center">
+%grapheme_strpos%, %grapheme_stripos%, %grapheme_strrpos%, %grapheme_strripos%,
+%grapheme_substr%, %grapheme_strstr%, %grapheme_stristr%
+</blurb>
+
+<example result="1"><![CDATA[<?php
+$sentence  = 'Autobahnstraße';
+$word = 'ss';
+
+var_dump(grapheme_strpos($sentence, $word));
+var_dump(grapheme_strpos($sentence, $word, locale: 'de_DE-u-ks-level1'));
+?>]]></example>
+</slide>
diff --git a/slides/internals/php85-grapheme-levenstein3.xml b/slides/internals/php85-grapheme-levenstein3.xml
new file mode 100644
index 00000000..a9928d3f
--- /dev/null
+++ b/slides/internals/php85-grapheme-levenstein3.xml
@@ -0,0 +1,43 @@
+<slide>
+<title>PHP 8.5: grapheme_levenshtein</title>
+
+<div effect="fade-out">
+<example><![CDATA[<?php
+$normalCase = 'Hoffwn i ddΕ΅r';
+$upperCase  = 'HOFFWN I DDΕ΄R';
+
+echo grapheme_levenshtein($normalCase, $upperCase), "\n";
+?>]]></example>
+<example>
+10
+</example>
+
+<blurb>What if I would like it case-insensitively matched?</blurb>
+</div>
+
+<div effect="fade-in">
+<blurb>PHP 8.5 adds the %locale% argument:</blurb>
+<example inline="1"><![CDATA[&lt;?php
+$normalCase = 'Hoffwn i ddΕ΅r';
+$upperCase  = 'HOFFWN I DDΕ΄R';
+
+echo grapheme_levenshtein($normalCase, $upperCase, *locale: 'cy_GB-u-ks-level2'*), "\n";
+?>]]></example>
+<example>
+0
+</example>
+</div>
+
+<div effect="fade-in">
+<blurb>Locale descriptor:</blurb>
+<example>
+cy_GB-u-ks-level2
+┳━ ┳━ β”³ ━━━━┳━━━━
+┃  ┃  ┃     ┗━━━ ks: strength, with level 2
+┃  ┃  ┗━━━━━━━━━ flag separator
+┃  ┗━━━━━━━━━━━━ region code: GB (Great Britain)
+┗━━━━━━━━━━━━━━━ language code: CY (Cymreag)
+</example>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-opcache-required.xml b/slides/internals/php85-opcache-required.xml
new file mode 100644
index 00000000..d6f7eb2c
--- /dev/null
+++ b/slides/internals/php85-opcache-required.xml
@@ -0,0 +1,16 @@
+<slide>
+<title>OPcache is no longer optional</title>
+
+<break lines="3"/>
+<blurb class="center">
+OPcache is now always *loaded* as part of PHP as a static part.
+</blurb>
+
+<div effect="fade-in">
+<break lines="3"/>
+<blurb class="center">
+OPcache is *not* always *enabled*, you still need to make the right %php.ini% settings.
+</blurb>
+</div>
+
+</slide>
diff --git a/slides/internals/php85-pipes-debug.xml b/slides/internals/php85-pipes-debug.xml
new file mode 100644
index 00000000..bd204f9d
--- /dev/null
+++ b/slides/internals/php85-pipes-debug.xml
@@ -0,0 +1,7 @@
+<slide>
+<title>PHP 8.5: Pipe Operator β€” Debugging</title>
+
+<example><![CDATA[<?php
+?>]]></example>
+
+</slide>
diff --git a/slides/internals/php85-pipes-examples.xml b/slides/internals/php85-pipes-examples.xml
new file mode 100644
index 00000000..09d58423
--- /dev/null
+++ b/slides/internals/php85-pipes-examples.xml
@@ -0,0 +1,7 @@
+<slide>
+<title>PHP 8.5: Pipe Operator β€” Further Examples</title>
+
+<example><![CDATA[<?php
+?>]]></example>
+
+</slide>
diff --git a/slides/internals/php85-pipes.xml b/slides/internals/php85-pipes.xml
index aa5812d2..4959ef91 100644
--- a/slides/internals/php85-pipes.xml
+++ b/slides/internals/php85-pipes.xml
@@ -20,8 +20,4 @@ function slugger(string $input)
 var_dump(slugger('Hello, World!')); // string(11) "hello-world"
 ?>]]></example>
 
-<list>
-    <bullet>Still under consideration (but currently passing)</bullet>
-</list>
-
 </slide>
diff --git a/slides/internals/php85-url-parsing-api.xml b/slides/internals/php85-url-parsing-api.xml
new file mode 100644
index 00000000..9394a2ee
--- /dev/null
+++ b/slides/internals/php85-url-parsing-api.xml
@@ -0,0 +1,33 @@
+<slide>
+<title>PHP 8.5: URL API</title>
+
+<break lines="3"/>
+<div effect="fade-out">
+<blurb class="big-centre">%parse_url()% is πŸ’©</blurb>
+</div>
+
+<div effect="fade-in">
+<blurb class="big-centre">%Uri\WhatWg\Url::parse()%</blurb>
+<blurb class="center">embedding URLs into HTML</blurb>
+</div>
+
+<div effect="fade-in-out">
+<break lines="2"/>
+<blurb class="big-centre">%Uri\Rfc3986\Url::parse()%</blurb>
+<blurb class="center">"server to server" communication (e.g. DSN parsing or outgoing HTTP requests)</blurb>
+</div>
+
+<div effect="fade-in">
+<example result="1"><![CDATA[
+<?php
+$uri = new Uri\Rfc3986\Uri("https://www.example.com/elephpants?are=yes#great");
+echo $uri->getScheme(), "\n";
+echo $uri->getHost(), "\n";
+echo $uri->getPath(), "\n";
+echo $uri->getQuery(), "\n";
+echo $uri->getFragment(), "\n";
+]]>
+</example>
+</div>
+
+</slide>
diff --git a/slides/internals/pie.jpg b/slides/internals/pie.jpg
new file mode 100644
index 00000000..cef22741
Binary files /dev/null and b/slides/internals/pie.jpg differ
diff --git a/slides/internals/policy-adopt-pie.xml b/slides/internals/policy-adopt-pie.xml
new file mode 100644
index 00000000..8054e516
--- /dev/null
+++ b/slides/internals/policy-adopt-pie.xml
@@ -0,0 +1,12 @@
+<slide>
+<title>Policy: Adopting PIE</title>
+
+<image filename="pie.jpg"/>
+
+<break/>
+<blurb class="big-centre">A modern installer for PHP extensions</blurb>
+<div effect="fade-in">
+<blurb class="big-centre">to replace PECL.</blurb>
+</div>
+
+</slide>
diff --git a/slides/internals/policy-vote-abstain.xml b/slides/internals/policy-vote-abstain.xml
new file mode 100644
index 00000000..dce3bf41
--- /dev/null
+++ b/slides/internals/policy-vote-abstain.xml
@@ -0,0 +1,11 @@
+<slide>
+<title>Policy: Abstain</title>
+
+<image filename="abstain.png"/>
+
+<div effect="fade-in">
+<break lines="3"/>
+<blurb class="big-centre">It's now okay *not* to have an opinion πŸ˜€!</blurb>
+</div>
+
+</slide>
diff --git a/templates/default/ordered-list.ezt b/templates/default/ordered-list.ezt
new file mode 100644
index 00000000..78d95da6
--- /dev/null
+++ b/templates/default/ordered-list.ezt
@@ -0,0 +1,9 @@
+{use $node, $pres}
+{var $class = get_attribute( $node, 'class' )}
+<ol{if $class} class='{$class}'{/if}>
+{foreach $node->childNodes as $child}
+	{if is_set( $child->tagName )}
+		{include $child->tagName . '.ezt' send $child as $node, $pres}
+	{/if}
+{/foreach}
+</ol>