PHP

Mexico City

Nov. 16, 2013

http://talks.php.net/phpdaymx13

Rasmus Lerdorf
@rasmus

1993


#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

#define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && \
                   (x) <= 'f') || ((x) >= 'A' && (x) <= 'F'))

int htoi(char *s) {
	int     value;
	char    c;

	c = s[0];
	if(isupper(c)) c = tolower(c);
	value=(c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;

	c = s[1];
	if(isupper(c)) c = tolower(c);
	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;

	return(value);
}

void main(int argc, char *argv[]) {
	char *params, *data, *dest, *s, *tmp;
	char *name, *age;

	puts("Content-type: text/html\r\n");
	puts("<HTML><HEAD><TITLE>Form Example</TITLE></HEAD>");
	puts("<BODY><H1>My Example Form</H1>");
	puts("<FORM action=\"form.cgi\" method=\"GET\">");
	puts("Name: <INPUT type=\"text\" name=\"name\">");
	puts("Age: <INPUT type=\"text\" name=\"age\">");
	puts("<BR><INPUT type=\"submit\">");
	puts("</FORM>");

	data = getenv("QUERY_STRING");
	if(data && *data) {
		params = data; dest = data;
    	while(*data) {
			if(*data=='+') *dest=' ';
			else if(*data == '%' && ishex(*(data+1))&&ishex(*(data+2))) {
				*dest = (char) htoi(data + 1);
				data+=2;
			} else *dest = *data;
			data++;
			dest++;
		}
		*dest = '\0';
		s = strtok(params,"&");
		do {
			tmp = strchr(s,'=');
			if(tmp) {
				*tmp = '\0';
				if(!strcmp(s,"name")) name = tmp+1;
				else if(!strcmp(s,"age")) age = tmp+1;
			}
		} while(s=strtok(NULL,"&"));

		printf("Hi %s, you are %s years old\n",name,age);
	}
	puts("</BODY></HTML>");
}

1993


use CGI qw(:standard);
print header;
print start_html('Form Example'),
    h1('My Example Form'),
    start_form,
    "Name: ", textfield('name'),
    p,
    "Age: ", textfield('age'),
    p,
    submit,
    end_form;
if(param()) {
    print "Hi ",em(param('name')),
        "You are ",em(param('age')),
        " years old";
}
print end_html;

1994


<html><head><title>Form Example</title></head>
<body><h1>My Example Form</h1>
<form action="form.phtml" method="POST">
Name: <input type="text" name="name">
Age: <input type="text" name="age">
<br><input type="submit">
</form>
<?if($name):?>
Hi <?echo $name?>, you are <?echo $age?> years old
<?endif?>
</body></html>

Focus on the Ecosystem

  • LAMP wasn't an accident
  • Robustness, Performance and Security
  • shared hosting ISPs

Scale

  • Scaling up is expected
  • Scaling down is surprisingly hard
  • Doing both is rocket science

What was he thinking?

  • Case insensitive function names?
  • Naming inconsistencies?
  • What's with the $ signs?
  • Globals?
  • register_globals?
  • magic_quotes?

OMGWTFBBQ?

array_search($needle, $haystack);
strstr($haystack, $needle);
in_array($needle, $haystack);
substr_count($haystack, $needle);
array_key_exists($needle, $haystack);
strchr($haystack, $needle);

Performance

  • mod_php
  • shared-nothing perfect sandbox model

Robustness

  • SQL LIMIT clause
  • Promote Prefork shared-nothing model

Security

  • max_execution_time
  • memory_limit
  • safe mode

2013

PHP 5.4


Performance Improvements

  • FastCGI request handling
  • better memory handling
  • startup/shutdown
  • repeated run-time function binding
  • string constants
  • access to global constants
  • access to static properties
  • empty hashes
  • @ operator
  • unserialize()

✔ Built-in Web Server

✔ Traits aka Horizontal Code Reuse

        (Compiler-assisted Copy-and-Paste)

✔ Short Array Syntax

$a = [1, 2, 3];
$b = ['foo' => 'orange', 'bar' => 'apple'];

✔ Function Array Dereferencing

function fruits() {
  return array('apple', 'banana', 'orange');
}
echo fruits()[0]; // Outputs: apple
?>

✔ $this from current scope supported in Closures

class Foo {
  private $prop = "bar";
  public function getPrinter() {
    return function() { echo ucfirst($this->prop); };
  }
}

$a = new Foo;
$func = $a->getPrinter();
$func(); // Outputs: Bar
?>

✔ <?= is now always available

✔ New Session Object

✔ Callable Typehint

✔ Better support for asian chars in htmlspecialchars/htmlentities

✔ Multibyte support is now configurable at runtime

✔ JSON Improvements

  • JsonSerializable
class Foo implements JsonSerializable {
    private $data = 'Bar';
    public function jsonSerialize() {
        return array('data'=>$this->data);
    }
}
echo json_encode(new Foo); // Outputs: {"data":"Bar"}

✔ mysqlnd used by default everywhere

✔ iterator support added to mysqli (mysqli_result implements Traversable)

✔ Binary notation

$mask = 0b010101;

✔ AES Support added to OpenSSL

✔ Tokyo Cabinet and DB5 support added to dba

✔ Added stack frame count arg to debug_backtrace()

✔ $_SERVER['REQUEST_TIME_FLOAT'] added

✔ Apache 2.4 support on Windows

5.4 Performance


PHP 5.4 + Opcache vs. PHP 5.3 + APC

user cpu

system cpu

memory

latency

Watch out for...


✔ default charset is UTF-8 instead of ISO-8859-1

echo htmlspecialchars("abc".chr(0xE0)."def"); // 5.3 "abc�def"
echo htmlspecialchars("abc".chr(0xE0)."def", NULL, 'UTF-8'); // 5.3 ""
echo htmlspecialchars("abc".chr(0xE0)."def"); // 5.4 ""
echo htmlspecialchars("abc".chr(0xE0)."def", ENT_IGNORE); // 5.4 "abcdef"

✔ array to string conversion notice

echo [1,2,3];
// Notice: Array to string conversion in test.php code on line 1

array_diff([1,2,3], [1,2, [3] ]);
// Notice: Array to string conversion in test.php on line 2

✔ register_globals completely removed

✔ magic quotes completely removed

✔ removed variable break/continue

$var = 'label';
while(1) {
  break $var;
  continue $var;
}
label:

✔ max_input_vars (default 1000)

✔ "callable", "insteadof" and "trait" are now reserved words

✔ extending an abstract constructor must match the signature

abstract class Base {
    abstract public function __construct();
  }
  class Foo extends Base {
    public function __construct($bar) {}
// FATAL  Declaration of Foo::__construct() must be compatible 
//        with Base::__construct()
  }

✔ stream_select() preserves keys of array arg

✔ XSLT writes are disabled by default

✔ mcrypt_generic_end() removed in favour of mcrypt_generic_deinit()

✔ mysql_list_dbs() removed

✔ PDO needs at least mysql client lib 4.1 now

✔ Datetime ignores TZ env var and falls back to UTC if none set

✔ Tiger hash output fixed - See bug 61307

PHP 5.5


Performance Improvements

  • Nested calls
  • Call stack pre-allocated by compiler
  • Bundled opcode cache

✔ Generators

function xrange($start, $end) {
    for ($i = $start; $i <= $end; $i ++) {
        yield $i;
    }
}
foreach (xrange(0, 5) as $i) {
    echo $i, "\n";
}

✔ Coroutines

function logger($fileName) {
    $fileHandle = fopen($fileName, 'a');
    while (true) {
        fwrite($fileHandle, yield . "\n");
    }
}

$logger = logger(__DIR__ . '/log');
$logger->send('Foo');
$logger->send('Bar');

For an advanced explanation of coroutines, read this article by Nikita Popov

Cooperative multitasking using coroutines

✔ finally

$db = mysqli_connect();
try {
   call_some_function($db);
} finally {
   mysqli_close($db);
}

✔ list() in foreach

$names = [ ['John','Smith'], ['Fred','Johnson'] ];
foreach($names as list($first,$last)) { 
    echo $first,$last; 
}

✔ Const array/string Dereferencing

echo array(1, 2, 3)[0]; //output 1
echo "foobar"[3]; //output b
echo [1,3,4][2]; //output 4

✔ empty() support for functions/expressions

✔ curl upload functionality rewritten

✔ Simplified password hashing API

// Hash
$hash = password_hash("super secret",PASSWORD_BCRYPT);

// To validate $pwd against the stored hash
if (password_verify($pwd, $hash)) {
    echo 'Password is valid!';
} else {
    echo 'Invalid password.';
}
php.net/migration55

PHP 5.6


http://php.net/spec

✔ Variadic functions

class MySQL implements DB {
    public function query($query, ...$params) {
        $stmt = $this->pdo->prepare($query);
        $stmt->execute($params);
        return $stmt;
    }
}

$q = 'SELECT * FROM users WHERE id = ?';
$user = $db->query($q, $userID)->fetch();

✔ Argument Unpacking

// A better call_user_func_args
$args1 = [1, 2, 3];
$args2 = [4, 5, 6];
test(...$args1, ...$args2); // [1, 2, 3, 4, 5, 6]
test(1, 2, 3, ...$args2);   // [1, 2, 3, 4, 5, 6]
test(...$args1, 4, 5, 6);   // Fatal error: Cannot use positional argument after argument unpacking

✔ Constant scalar expressions

class Foo {
    const FOO = 1 + 1;
    const BAR = 1 << 1;
    const GREETING = "HELLO";
    const BAZ = self::GREETING." WORLD!"
}

✔ Add right-associative power operator **

echo 2 ** 3 ** 2; // 512 (not 64)
echo -3 ** 2; // -9 (not 9)
echo 1 - 3 ** 2; // -8
echo ~3 ** 2; // -10 (not 16)

✔ Internal operator overloading for internal features like GMP

echo 2**512;
echo "\n";
$n = gmp_init(2);
echo $n**512;
1.3407807929943E+154
13407807929942597099574024998205846127479365820592393377723561443721764030073546976801874298166903427690031858186486050853753882811946569946433649006084096
		

✔ use function and use const namespace imports

include 'template.inc';
include 'db.inc';
use function template\header, template\footer, db\query;

header('My Page');
query('select * from stuff');
footer();

✔ default_charset ini now applies to internal funcs

✔ SSL Peer verification by default

✔ openssl certificate fingerprints

✔ SAN x509 ext matching when verifying host names

✔ automatic DoS prevention of TLS renegotiation attacks

✔ and many more openssl-related options

✔ Asynchronous PostgreSQL database connections

✔ Non-blocking PostgreSQL queries

✔ New phpdbg SAPI

✔ Support for > 2GB file uploads

✔ The php://input stream is now re-usable

✔ Added hash_equals() for timing attack safe string comparison

✔ Added gost-crypto (CryptoPro S-box) hash algorithm

✔ FPM workers can now change their apparmor profile

✔ OCI8 Improvements

php.net/migration56

Deploying Web Apps


Are your deploys atomic?

Are you sure?

What happens to a request that is currently executing when you deploy?

Deploying Web Apps


Deploying Web Apps


✔ Don't copy files into current document root

✔ Let existing requests finish on old code

✔ New requests start on new code

✔ Avoid clearing your opcode cache

✔ Minimal impact on production traffic

Deploying Web Apps


Deploying Web Apps


Deploying Web Apps


2 Document Root directories

Symlink /var/www/site toggles between them

realpath() symlink at the web server level

Set Document Root to symlink realpath

Never hardcode the document root in code

Deploying Web Apps


Apache

http://github.com/etsy/mod_realdoc

nginx

You can use $realpath_root

eg. fastcgi_param DOCUMENT_ROOT $realpath_root;

PHP

http://github.com/etsy/incpath

Conclusion


  • Still running PHP 5.2 or 5.3? Upgrade now!
  • Test your code on PHP 5.5 and upgrade soon
  • Help us test PHP 5.6
  • Contribute!
Slides: http://talks.php.net/phpdaymx13

5.2->5.3: http://php.net/migration53

5.3->5.4: http://php.net/migration54

5.4->5.5: http://php.net/migration55

mysqlnd: http://php.net/mysqlnd

Wiki: http://wiki.php.net

Bugs: http://bugs.php.net