Slide 1

Slide 1 text

YOUR NEXT WEB SERVER WILL BE WRITTEN IN... PHP MIGHT PHP UK Conference 2017 London, England

Slide 2

Slide 2 text

David Zuelke

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Slide 5

Slide 5 text

@dzuelke

Slide 6

Slide 6 text

CGI

Slide 7

Slide 7 text

NCSA, 1993

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

RFC 3875, 1997-2004

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

/cgi-bin/counter.pl?site=8712

Slide 12

Slide 12 text

No content

Slide 13

Slide 13 text

No content

Slide 14

Slide 14 text

HOW CGI WORKS 1. Web server parses request from client 2. Web server sets request info into environment
 (PATH_INFO, REQUEST_METHOD, HTTP_ACCEPT, …) 3. Web server executes CGI script 4. CGI script echos status, headers, and body 5. Web server returns response to client

Slide 15

Slide 15 text

slow

Slide 16

Slide 16 text

(but simple)

Slide 17

Slide 17 text

#!/bin/bash cat < Hello World

Hello World

Your browser is ${HTTP_USER_AGENT:-unknown}

This page was served by $(hostname)

Slide 18

Slide 18 text

CGI VARIABLES (SEE $_SERVER) • Server info:
 SERVER_SOFTWARE, SERVER_NAME, GATEWAY_INTERFACE • Request info:
 SERVER_PROTOCOL, SERVER_PORT, REQUEST_METHOD, PATH_INFO, PATH_TRANSLATED, QUERY_STRING, REMOTE_ADDR, CONTENT_TYPE, CONTENT_LENGTH, … • Request headers:
 HTTP_HOST, HTTP_ACCEPT, HTTP_USER_AGENT, …

Slide 19

Slide 19 text

PHP's SAPIs
 & EXECUTION MODEL

Slide 20

Slide 20 text

a SAPI is the "gateway" to the PHP engine

Slide 21

Slide 21 text

marshals input and output from and to the interface

Slide 22

Slide 22 text

PHP WEB SAPI (SIMPLIFIED) • populate $_SERVER and $_ENV • parse QUERY_STRING into $_GET • " application/x-www-form-urlencoded into $_POST • " multipart/form-data into $_FILES • return header() data as headers, anything echo()d as content

Slide 23

Slide 23 text

PHP was built for web scripting, for CGI

Slide 24

Slide 24 text

execution model modeled around statelessness "blank slate"

Slide 25

Slide 25 text

PHP CGI EXECUTION LIFECYCLE
 (SIMPLIFIED) 1. core init, load extensions etc 2. MINIT for all modules (extension initialization etc) 3. SAPI ready for (one and only) request 4. RINIT for all modules (e.g. ext-session if session.auto_start) 5. script executes 6. RSHUTDOWN 7. engine cleanup (unset all variables and state) 8. MSHUTDOWN

Slide 26

Slide 26 text

all of that on each CGI request

Slide 27

Slide 27 text

NEIN NEIN NEIN NEIN DAS IST ZU LANGSAM

Slide 28

Slide 28 text

mod_php

Slide 29

Slide 29 text

embeds PHP into the Apache httpd process

Slide 30

Slide 30 text

mod_php EXECUTION LIFECYCLE
 (SIMPLIFIED) 1. SAPI ready for (next) request 2. RINIT for all modules (e.g. ext-session if session.auto_start) 3. script executes 4. RSHUTDOWN 5. engine cleanup (unset all variables and state)

Slide 31

Slide 31 text

but now PHP is in each httpd process

Slide 32

Slide 32 text

even when serving static files

Slide 33

Slide 33 text

APACHE PROCESS MODELS • mpm_prefork creates worker processes
 (each with a PHP) • mpm_worker uses threads
 (so you need ZTS, and stuff will generally crash a lot) • mpm_event uses an event loop
 (best, but you can't embed something like PHP at all)

Slide 34

Slide 34 text

so... what do we do?

Slide 35

Slide 35 text

¯\_(ϑ)_/¯

Slide 36

Slide 36 text

"let's just use Nginx!"

Slide 37

Slide 37 text

FastCGI

Slide 38

Slide 38 text

protocol similar to CGI, but over a socket

Slide 39

Slide 39 text

persistent server process

Slide 40

Slide 40 text

old fcgi SAPI: web server manages FastCGI child processes

Slide 41

Slide 41 text

newer FPM: PHP manages its own child processes' lifecycle

Slide 42

Slide 42 text

no overhead in web server for static content

Slide 43

Slide 43 text

web server can use threading or whatever

Slide 44

Slide 44 text

still re-executes from ~RINIT for each request

Slide 45

Slide 45 text

still bootstraps Symfony/Laravel/… on each request

Slide 46

Slide 46 text

RUBY & PYTHON

Slide 47

Slide 47 text

Rack & WSGI

Slide 48

Slide 48 text

RUBY/RACK app = Proc.new do |env| ['200', {'Content-Type' => 'text/plain'}, ['Hello World']] end def application(environ, start_response): start_response('200 OK', [('Content-Type', 'text/plain')]) yield 'Hello World\n' PYTHON/WSGI

Slide 49

Slide 49 text

THE RACK/WSGI STACKS 1. Web server:
 Unicorn, Gunicorn, Puma, Tornado, Phusion Passenger, mod_wsgi, ... 2. Middlewares:
 Routing, authentication, filtering, post-processing, ... 3. Application/framework:
 Rails, Django, Sinatra, Flask, ...

Slide 50

Slide 50 text

NATIVE PHP WEB SERVERS

Slide 51

Slide 51 text

PHP IS NOW READY • PHP 7+ performance is amazing • Almost all engine errors are catchable since PHP 7 • Signal handling without ticks in PHP 7.1 • Concurrency frameworks and event lib extensions

Slide 52

Slide 52 text

FRAMEWORKS FOR EVENT- DRIVEN NON-BLOCKING I/O • http://reactphp.org • https://icicle.io • http://amphp.org

Slide 53

Slide 53 text

IT'S ALL A REACTOR echo "-- before run()\n"; Amp\run(function() { Amp\repeat(function() { echo "tick\n"; }, $msInterval = 1000); Amp\once("Amp\stop", $msDelay = 5000); }); echo "-- after run()\n";

Slide 54

Slide 54 text

SIMPLE WEB SERVER, ReactPHP $app = function ($request, $response) { $response->writeHead(200, array('Content-Type' => 'text/plain')); $response->end("Hello World\n"); }; $loop = React\EventLoop\Factory::create(); $socket = new React\Socket\Server($loop); $http = new React\Http\Server($socket, $loop); $http->on('request', $app); $socket->listen(1337); $loop->run();

Slide 55

Slide 55 text

https://github.com/M6Web/PhpProcessManagerBundle & https://github.com/php-pm/php-pm

Slide 56

Slide 56 text

$kernel = new AppKernel('prod', false); $reactHttp->on('request', function ($request, $response) use ($kernel) { $headers = $request->getHeaders(); if (in_array(strtoupper($request->getMethod()), ['POST','PUT','DELETE','PATCH']) && isset($headers['Content-Type']) && (0 === strpos($headers['Content-Type'], 'application/x-www-form-urlencoded')) ) parse_str($request->getBody(), $post); $sfRequest = new Symfony\Component\HttpFoundation\Request( $request->getQuery(), $post ?? [], array(), array(), $request->getFiles(), array(), $request->getBody() ); $sfRequest->setMethod($request->getMethod()); $sfRequest->headers->replace($headers); $sfRequest->server->set('REQUEST_URI', $request->getPath()); if (isset($headers['Host'])) $sfRequest->server->set('SERVER_NAME', explode(':', $headers['Host'])[0]); $sfResponse = $kernel->handle($sfRequest); $response->writeHead($sfResponse->getStatusCode(), $sfResponse->headers->all()); $response->end($sfResponse->getContent()); $kernel->terminate($request, $response); });

Slide 57

Slide 57 text

WHAT BECOMES POSSIBLE

Slide 58

Slide 58 text

speeeeeeeeeed :)

Slide 59

Slide 59 text

http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html

Slide 60

Slide 60 text

processing request data while it's still uploading

Slide 61

Slide 61 text

handling Web Sockets in the same process

Slide 62

Slide 62 text

git clone project && cd project && composer install && php server.php

Slide 63

Slide 63 text

WHAT BECOMES IMPOSSIBLE

Slide 64

Slide 64 text

native session handling

Slide 65

Slide 65 text

ignoring memory leaks

Slide 66

Slide 66 text

MAYBE, IN A BRIGHT FUTURE...

Slide 67

Slide 67 text

PSR-7 (HTTP Message Interface) + PSR-15 (HTTP Middlewares) = ultimate interop :)

Slide 68

Slide 68 text

a universe of useful middlewares :)

Slide 69

Slide 69 text

competition between different native web servers :)

Slide 70

Slide 70 text

"legacy" server that runs in FPM SAPI and translates a request :)

Slide 71

Slide 71 text

"legacy" middleware that runs in new server and populates $_GET and friends :)

Slide 72

Slide 72 text

READING MATERIAL • http://blog.kelunik.com/2015/09/20/getting-started-with-amp.html • http://blog.kelunik.com/2015/10/21/getting-started-with-aerys.html • http://blog.kelunik.com/2015/10/20/getting-started-with-aerys- websockets.html • http://marcjschmidt.de/blog/2014/02/08/php-high-performance.html • http://marcjschmidt.de/blog/2016/04/16/php-high-performance-reactphp- jarves-symfony-follow-up.html • https://gnugat.github.io/2016/04/13/super-speed-sf-react-php.html

Slide 73

Slide 73 text

The End

Slide 74

Slide 74 text

THANK YOU FOR LISTENING! Questions? Ask me: @dzuelke & [email protected]