KEMBAR78
2019-03 PHP without PHP Architecture @ Confoo | PDF
PHP without PHP
The Philosophy of
Good Architecture
terry chay
2018-03-15T11:00+0500
Confoo, Montreal, Canada
http://bit.ly/confoo19-architecture
Expecting "Where Have
All My Servers Gone?"
Sorry, Last minute change, too late to
make the program.
(I guess the speaker found his servers.)
Funky Caching
Prologue #1
aka
ErrorDocument trick
Smarter Caching
… Rasmus’s Trick (Stig’s trick)
Go into apache.conf (or .htaccess) and
ErrorDocument 404 /error.php
$filepath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //or $_SERVER['REDIRECT_URL']
$basepath = dirname(__FILE__).DIR_SEP;
 
// Test to see if you can work with it
if (false) { //…EDIT…
    //output a 404 page
    include('404.html'); // see http://www.alistapart.com/articles/perfect404/ for tips
    return;
}
 
// Generate the file
// …EDIT…
$data = 'something';
 
// Don't send 404 back, send 200 OK because this is a pretty smart 404
// not a clueless one! http://www.urbandictionary.com/define.php?term=404
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
//Show the file
echo $data;
 
//Store the page to bypass PHP on the next request. Use a temp file with a
// link trick in order to avoid race conditions between concurrent PHP
// processes.
$tmpfile = tempnam($basepath.'tmp','fc');
$fp = fopen($tmpfile,'w'); //remove the "_" this is crashing my blog syntax hilighter
fputs($fp, $data);
fclose($fp);
@link($basepath.$filepath, $tmpfile); //suppress errors due to losing race
unlink($tmpfile);
Error.PHP
$filepath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //or $_SERVER['REDIRECT_URL']
$basepath = dirname(__FILE__).DIR_SEP;
 
// Test to see if you can work with it
if (false) { //…EDIT…
    //output a 404 page
    include('404.html'); // see http://www.alistapart.com/articles/perfect404/ for tips
    return;
}
 
// Generate the file
// …EDIT…
$data = 'something';
 
// Don't send 404 back, send 200 OK because this is a pretty smart 404
// not a clueless one! http://www.urbandictionary.com/define.php?term=404
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
//Show the file
echo $data;
 
//Store the page to bypass PHP on the next request. Use a temp file with a
// link trick in order to avoid race conditions between concurrent PHP
// processes.
$tmpfile = tempnam($basepath.'tmp','fc');
$fp = fopen($tmpfile,'w'); //remove the "_" this is crashing my blog syntax hilighter
fputs($fp, $data);
fclose($fp);
@link($basepath.$filepath, $tmpfile); //suppress errors due to losing race
unlink($tmpfile);
Error.PHP
$filepath = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH); //or $_SERVER['REDIRECT_URL']
$basepath = dirname(__FILE__).DIR_SEP;
 
// Test to see if you can work with it
if (false) { //…EDIT…
    //output a 404 page
    include('404.html'); // see http://www.alistapart.com/articles/perfect404/ for tips
    return;
}
 
// Generate the file
// …EDIT…
$data = 'something';
 
// Don't send 404 back, send 200 OK because this is a pretty smart 404
// not a clueless one! http://www.urbandictionary.com/define.php?term=404
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
//Show the file
echo $data;
 
//Store the page to bypass PHP on the next request. Use a temp file with a
// link trick in order to avoid race conditions between concurrent PHP
// processes.
$tmpfile = tempnam($basepath.'tmp','fc');
$fp = fopen($tmpfile,'w'); //remove the "_" this is crashing my blog syntax hilighter
fputs($fp, $data);
fclose($fp);
@link($basepath.$filepath, $tmpfile); //suppress errors due to losing race
unlink($tmpfile);
Error.PHP
PHP without PHP
Paradigms
Prologue #2
Code Complete
The metaphor of Code as
construction comes from this
book…
W e n o w k n o w t h i s i s
fundamentally wrong…
Mythical Man Month
“man-month” is a term from
construction work
The premise is that man and
months are interchangeable.
This means that in order to
reach a deadline I simply add
more people to the project.
F i r s t c o n s i d e r s o m e t h i n g l i k e
painting a fence: everything is
partionable (man-month).
…add a constant time for training.
…add communication cost: n(n-1)/2.
Compare to the unpartitionable
(single man)
Adding people to a late project
makes it later!
paritionable
with training
traning + communication
unpartitionable
Engineer and
Architect
1 Fallingwater
organic, democratic, plasticity, continuity
…including me
Hatchway Staircase View at the main (living room) level, from the
bridge (from east)
Beyond the glass Fallingwater: Living room terraces and glass
walls (from east).
Fall Foliage View from lookout, downstream.
Fall Foliage View from lookout, downstream.
No barriers Detail: corner window at the guest house,
from southeast.
Existing tree The trellis over the driveway is built to
accommodate a tree.
Local quarry Living room, west (downstream) side, from
southeast
Existing boulder Living room fireplace and hearth, looking
from kitchen door to south windows
Philosophy
organic,
democratic,
plasticity,
continuity.
Why on PHP?
Harmony with Environment
Apache web server: ErrorDocument
Customer-centric: Performance
paramount
Relational Database: Slow
persistence
Harmony with PHP itself…
Architecture of PHP Modern web architecture
PHP is “Cheap”
“A project done in Java will cost 5 times
as much, take twice as long, and be
harder to maintain than a project done
in a scripting language such as PHP or
Perl.”
—Phillip Greenspun
PHP is “Scalable”
“That a Java servlet performs better
than a PHP script, under optimal
conditions [has] nothing to do with
scalability. The point is can your
application continue to deliver
consistent performance as volume
increases. PHP delegates all the ‘hard
stuff’ to other systems.”
—Harry Fuecks
PHP is “Pragmatic”
“PHP is not about purity in CS
principles or architecture; it is about
solving the ugly web problem with an
admittedly ugly, but extremely
functional and convenient solution. If
you are looking for purity, you are in
the wrong boat. Get out now before
you get hit by a wet cat!”
—Rasmus Lerdorf
Not PHP? A framework?
Your language is a set of different
choices.
Some choices were made for you (by a
framework).
Choices create a different
environment.
Leverage those choices to be in
harmony with that environment!
2 Bellefield Towers
Design Hubris
Starting where my mom worked…
Gothic Romanesque architecture The current Bellefield Church (two blocks
away)
Other city towers Allegheny Courthouse Main Tower & Four
Tower Types at Jail (and other towers)
Tower of
Learning
Within spitting distance of
Bellefield Towers
Carnegie Library Most iconic moment in baseball history
Why is Bellefield
Towers so Ugly?
Design Hubris?
“I'm a developer, I can make software
conform to my needs.”
Trying to “lord over the environment
with an isolated man-made imposition.”
“But what I mean is it’s all man-made in
software, there is no environment.”
Language, infrastructure, team, etc.
it's all the environment
…at Tagged: the literal environment
add slideshow
…at Tagged
Cubes
2007: 20M users, 20M pages/
day
crashing
2009: 80M users, 250M pages/
day, 3rd largest
40% one app
Friendster & Design
Hubris
Good Architecture
Considers Environment
Bad Architecture Doesn't
3 Golden Gate Bridge
The Design Pattern
design build

quote of goldne gate bridge
the other bridge
quote from emperor norton
quotes of sf
Same problem,
different pattern
Original design was hybrid
cantilever-suspension.
Replaced by pure suspension
Art Deco
Bridge Tower, Lighting,
pedestrial walkway
quote of art deco movemebnt
International
Orange
Rust colored like the
environment it lives in … and
safe.
Part of the
whole
Design Patterns
Defined
“Each pattern describes a
problem which occurs over
a n d o v e r a g a i n i n o u r
e n v i r o n m e n t , a n d t h e n
describes the core of the
solution to that problem, in
such a way that you can use
this solution a million times
over, without ever doing it
the same way twice.”
shorten quote
Certainly iconic Me in front of both icons
Never the same way twice How do you know which one?
How do you know which way?
blend transition in stages
Funky Caching again
“search for the closest matching valid
URL and redirect, and use attempted
url text as a DB keyword lookup”
—Rasmus Lerdorf
cleanup text
Javascript and CSS
compiling & caching
put a graphic or some text
<?php
$watermark = '3129080702_c4e76f71d7_o.png';
$dead_url = 'http://example.com/dead_image.png';
 
// {{{ start_image($filename, &$data)
/**
* Creates a gd handle for a valid file
* @param $filename string the file to get
* @param $data array the imagesize
* @return resource GD handle
*/
function start_image($filename, &$data) {
    $data = @getimagesize($filename);
    if (empty($data)) { return null; }
    $data['ratio'] = $data[0]/$data[1];
    switch($data[2]) {
        case IMG_GIF: return imagecreatefromgif($filename);
        case 3: //problem where IMG_PNG is not bound correctly for my install
        case IMG_PNG: return imagecreatefrompng($filename);
        case IMG_JPG: return imagecreatefromjpeg($filename);
        case IMG_WBMP: return imagecreatefromwbmp($filename);
        case IMG_XPM: return imagecreatefromxbm($filename);
    }
    return null;
}   
// }}}
$requestimg = $_SERVER['REDIRECT_URL'];
if (!$_SERVER['QUERY_STRING']) {
    // redirect user to invalid image
    tag_http::redirect($dead_url);
    return '';
}
// grab image to temp {{{
$ch = curl_init($_SERVER['QUERY_STRING']);
$tempfile = tempnam('/tmp', 'prod_remote_');
$fp = f_open($tempfile, 'w'); //again delete the "_"
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec_($ch); //delete the final "_"
curl_close($ch);
fclose($fp);
// }}}
expand callouts
<?php
$watermark = '3129080702_c4e76f71d7_o.png';
$dead_url = 'http://example.com/dead_image.png';
 
// {{{ start_image($filename, &$data)
/**
* Creates a gd handle for a valid file
* @param $filename string the file to get
* @param $data array the imagesize
* @return resource GD handle
*/
function start_image($filename, &$data) {
    $data = @getimagesize($filename);
    if (empty($data)) { return null; }
    $data['ratio'] = $data[0]/$data[1];
    switch($data[2]) {
        case IMG_GIF: return imagecreatefromgif($filename);
        case 3: //problem where IMG_PNG is not bound correctly for my install
        case IMG_PNG: return imagecreatefrompng($filename);
        case IMG_JPG: return imagecreatefromjpeg($filename);
        case IMG_WBMP: return imagecreatefromwbmp($filename);
        case IMG_XPM: return imagecreatefromxbm($filename);
    }
    return null;
}   
// }}}
$requestimg = $_SERVER['REDIRECT_URL'];
if (!$_SERVER['QUERY_STRING']) {
    // redirect user to invalid image
    tag_http::redirect($dead_url);
    return '';
}
// grab image to temp {{{
$ch = curl_init($_SERVER['QUERY_STRING']);
$tempfile = tempnam('/tmp', 'prod_remote_');
$fp = f_open($tempfile, 'w'); //again delete the "_"
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec_($ch); //delete the final "_"
curl_close($ch);
fclose($fp);
// }}}
<?php
$watermark = '3129080702_c4e76f71d7_o.png';
$dead_url = 'http://example.com/dead_image.png';
 
// {{{ start_image($filename, &$data)
/**
* Creates a gd handle for a valid file
* @param $filename string the file to get
* @param $data array the imagesize
* @return resource GD handle
*/
function start_image($filename, &$data) {
    $data = @getimagesize($filename);
    if (empty($data)) { return null; }
    $data['ratio'] = $data[0]/$data[1];
    switch($data[2]) {
        case IMG_GIF: return imagecreatefromgif($filename);
        case 3: //problem where IMG_PNG is not bound correctly for my install
        case IMG_PNG: return imagecreatefrompng($filename);
        case IMG_JPG: return imagecreatefromjpeg($filename);
        case IMG_WBMP: return imagecreatefromwbmp($filename);
        case IMG_XPM: return imagecreatefromxbm($filename);
    }
    return null;
}   
// }}}
$requestimg = $_SERVER['REDIRECT_URL'];
if (!$_SERVER['QUERY_STRING']) {
    // redirect user to invalid image
    tag_http::redirect($dead_url);
    return '';
}
// grab image to temp {{{
$ch = curl_init($_SERVER['QUERY_STRING']);
$tempfile = tempnam('/tmp', 'prod_remote_');
$fp = f_open($tempfile, 'w'); //again delete the "_"
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec_($ch); //delete the final "_"
curl_close($ch);
fclose($fp);
// }}}
<?php
$watermark = '3129080702_c4e76f71d7_o.png';
$dead_url = 'http://example.com/dead_image.png';
 
// {{{ start_image($filename, &$data)
/**
* Creates a gd handle for a valid file
* @param $filename string the file to get
* @param $data array the imagesize
* @return resource GD handle
*/
function start_image($filename, &$data) {
    $data = @getimagesize($filename);
    if (empty($data)) { return null; }
    $data['ratio'] = $data[0]/$data[1];
    switch($data[2]) {
        case IMG_GIF: return imagecreatefromgif($filename);
        case 3: //problem where IMG_PNG is not bound correctly for my install
        case IMG_PNG: return imagecreatefrompng($filename);
        case IMG_JPG: return imagecreatefromjpeg($filename);
        case IMG_WBMP: return imagecreatefromwbmp($filename);
        case IMG_XPM: return imagecreatefromxbm($filename);
    }
    return null;
}   
// }}}
$requestimg = $_SERVER['REDIRECT_URL'];
if (!$_SERVER['QUERY_STRING']) {
    // redirect user to invalid image
    tag_http::redirect($dead_url);
    return '';
}
// grab image to temp {{{
$ch = curl_init($_SERVER['QUERY_STRING']);
$tempfile = tempnam('/tmp', 'prod_remote_');
$fp = f_open($tempfile, 'w'); //again delete the "_"
curl_setopt($ch, CURLOPT_FILE, $fp);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_exec_($ch); //delete the final "_"
curl_close($ch);
fclose($fp);
// }}}
// configure image and dimensions {{{
$size_data = array();
$im = start_image($tempfile, $size_data);
if (!$im) {
    unlink($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// get watermark information {{{
$wm_data = array();
$wm = start_image($watermark, $wm_data);
if (!$wm) {
    unlink ($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// add watermark {{{
if ($size_data['ratio']> $wm_data['ratio']) {
    // image is wider format than the watermark
    $new_smaller_dim = $wm_data[0] * ($size_data[1]/$wm_data[1]);
    $dst_x = ($size_data[0] - $new_smaller_dim)/2;
    $dst_y = 0;
    $dst_w = $new_smaller_dim;
    $dst_h = $size_data[1];
} else {
    // image is taller format than the watermark
    $new_smaller_dim = $wm_data[1] * ($size_data[0]/$wm_data[0]);
    $dst_x = 0;
    $dst_y = ($size_data[1] - $new_smaller_dim)/2;
    $dst_w = $size_data[0];
    $dst_h = $new_smaller_dim;;
}
imagecopyresized($im, $wm, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $wm_data[0], $wm_data[1]);
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
header(sprintf('Content-type: %s',$size_data['mime']));
// }}}
switch ($size_data[2]) {
    case IMG_GIF: imagegif($im); break;
    case 3: case IMG_PNG: imagepng($im); break;
    case IMG_JPG: imagejpeg($im); break;
    case IMG_WBMP: imagewbmp($im); break;
    case IMG_XPM: imagexbm($im); break;
}
imagedestroy($wm);
imagedestroy($im);
unlink($tempfile);
// configure image and dimensions {{{
$size_data = array();
$im = start_image($tempfile, $size_data);
if (!$im) {
    unlink($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// get watermark information {{{
$wm_data = array();
$wm = start_image($watermark, $wm_data);
if (!$wm) {
    unlink ($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// add watermark {{{
if ($size_data['ratio']> $wm_data['ratio']) {
    // image is wider format than the watermark
    $new_smaller_dim = $wm_data[0] * ($size_data[1]/$wm_data[1]);
    $dst_x = ($size_data[0] - $new_smaller_dim)/2;
    $dst_y = 0;
    $dst_w = $new_smaller_dim;
    $dst_h = $size_data[1];
} else {
    // image is taller format than the watermark
    $new_smaller_dim = $wm_data[1] * ($size_data[0]/$wm_data[0]);
    $dst_x = 0;
    $dst_y = ($size_data[1] - $new_smaller_dim)/2;
    $dst_w = $size_data[0];
    $dst_h = $new_smaller_dim;;
}
imagecopyresized($im, $wm, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $wm_data[0], $wm_data[1]);
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
header(sprintf('Content-type: %s',$size_data['mime']));
// }}}
switch ($size_data[2]) {
    case IMG_GIF: imagegif($im); break;
    case 3: case IMG_PNG: imagepng($im); break;
    case IMG_JPG: imagejpeg($im); break;
    case IMG_WBMP: imagewbmp($im); break;
    case IMG_XPM: imagexbm($im); break;
}
imagedestroy($wm);
imagedestroy($im);
unlink($tempfile);
// configure image and dimensions {{{
$size_data = array();
$im = start_image($tempfile, $size_data);
if (!$im) {
    unlink($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// get watermark information {{{
$wm_data = array();
$wm = start_image($watermark, $wm_data);
if (!$wm) {
    unlink ($tempfile);
    tag_http::redirect($dead_url);
    return;
}
// }}}
// add watermark {{{
if ($size_data['ratio']> $wm_data['ratio']) {
    // image is wider format than the watermark
    $new_smaller_dim = $wm_data[0] * ($size_data[1]/$wm_data[1]);
    $dst_x = ($size_data[0] - $new_smaller_dim)/2;
    $dst_y = 0;
    $dst_w = $new_smaller_dim;
    $dst_h = $size_data[1];
} else {
    // image is taller format than the watermark
    $new_smaller_dim = $wm_data[1] * ($size_data[0]/$wm_data[0]);
    $dst_x = 0;
    $dst_y = ($size_data[1] - $new_smaller_dim)/2;
    $dst_w = $size_data[0];
    $dst_h = $new_smaller_dim;;
}
imagecopyresized($im, $wm, $dst_x, $dst_y, 0, 0, $dst_w, $dst_h, $wm_data[0], $wm_data[1]);
header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));
header(sprintf('Content-type: %s',$size_data['mime']));
// }}}
switch ($size_data[2]) {
    case IMG_GIF: imagegif($im); break;
    case 3: case IMG_PNG: imagepng($im); break;
    case IMG_JPG: imagejpeg($im); break;
    case IMG_WBMP: imagewbmp($im); break;
    case IMG_XPM: imagexbm($im); break;
}
imagedestroy($wm);
imagedestroy($im);
unlink($tempfile);
San Francisco
Looking around you for
inspiration
(Organic, Democratic,
Plasticity, Continuity)
Organic
“Form and function are one”
think beyond funky-caching!
Funky-caching is just a sleight-of-
hand
…actually a design pattern you've
seen before and used many times.
Cache-Aside Pattern
Cache-Aside Pattern
Cache-Aside Pattern
Web server PHP
Cache-Aside Pattern
Instead of dynamic page generation
maybe it's a database query or remote
web query
Instead cache in file system, the cache
could be Memcache or Redis
instead of bypassing app server, bypass
your entire website (reverse proxy or
Content Distribution Network)
Whether to use it an how is ALWAYS
determined by environment
ORGANIC: Form and function are one
San Francisco
Looking around you for
inspiration
(Organic, Democratic,
Plasticity, Continuity)
Democratic
“design should accommodate needs.”
Me
"Ruby on Rails is a rounded rectangle.”
“PHP is a Ball of Nails.”
let me build a bridge to the ruby
world…
mod_ruby and AWS
mod_ruby shares app space
Ruby on Rails could not be used in a
shared environment, PHP could
2006: Amazon adds EC2 to AWS
Ruby world adopted it in droves
because of a NEED.
Ruby gave us modern devops
Puppet/Chef
GitHub
Heroku/Engineyard
vagrant
Ruby was Democratic
Ruby and cloud computing was design
accommodating need
Democratic: Design should
accommodate need
In the PHP world we adopt those
tools (php is scalable by being
"shared none")
(other worlds like Python and Go,
adopt and extend to give us things like
Ansible and docker)
San Francisco
Looking around you for
inspiration
(Organic, Democratic,
Plasticity, Continuity)
Plasticity
“physical element should remove
barriers.”
JavaScript started as a marketing
portmanteau
Hypertext & Hypercard
onmouseover
JavaScript hits big time
Code richer libaries: JQuery,
Underscores, BackboneJS…
barrier to install: npm
Compiling those libraries into single
file: webpack
webpack means we have transpiler:
Coffeescript, TypeScript,…
Facebook's php mentality: "Practical":
webpack: ES5 (webpack) into
javascript, performance
NPM, Webpack & Plasticity
Facebook's PHP mentality:
"Practical": ReactJS
javascript framework focus on performance
(vdom)
we have webpack, why not write it in
ECMAScript6 and transpile it to browser code?
PHP's PHP mentality:
"Cheap"
“npm is great, let's copy it! (including its
package manafest)” -> composer
All things that remove barriers: Plasticity
San Francisco
Looking around you for
inspiration
(Organic, Democratic,
Plasticity, Continuity)
Continuity
“An element should be part of
nature.”
threw Ruby a bone, what about
Rounded rectangles?
Meet Bill atkinson
folklore.org
San Francisco
Looking around you for
inspiration
(Organic, Democratic,
Plasticity, Continuity)
Thanks!
tychay@php.net @tychay terrychay.com
(say we met at #confoo)
http://bit.ly/confoo19-architecture

2019-03 PHP without PHP Architecture @ Confoo