Upload
terry-chay
View
2.699
Download
4
Embed Size (px)
DESCRIPTION
PDF version of my slide deck for "PHP Without PHP" given at International PHP Conference in Berlin, Germany on May 2009 Abstract: An obscure but ubiquitous design pattern in PHP development is known as Funky Caching. Using real architectural examples as a lens to look at this one simple PHP design pattern, we see how we can design web architectures that are "organic, democratic, and lasting."
Citation preview
PHP without PHPThe Philosophy of Good
Architecture
terry chay2009-05-27T14:15+0200
International PHP ConferenceBerlin, Germany
Funky CachingPrologue #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 itif (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=404header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));//Show the fileecho $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 hilighterfputs($fp, $data);fclose($fp);@link($basepath.$filepath, $tmpfile); //suppress errors due to losing raceunlink($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 itif (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=404header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));//Show the fileecho $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 hilighterfputs($fp, $data);fclose($fp);@link($basepath.$filepath, $tmpfile); //suppress errors due to losing raceunlink($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 itif (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=404header(sprintf('%s 200', $_SERVER['SERVER_PROTOCOL']));//Show the fileecho $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 hilighterfputs($fp, $data);fclose($fp);@link($basepath.$filepath, $tmpfile); //suppress errors due to losing raceunlink($tmpfile);
Error.PHP
PHP without PHP
ParadigmsPrologue #2
Code CompleteThe 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!
5
10
15
20 Tim
e t
o C
Om
pl
et
e T
itl
e
Number of People
paritionablewith trainingtraning + communicationunpartitionable
Engineer and Architect
1 Fallingwaterorganic, 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
Philosophyorganic,
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
2 Bellefield TowersDesign Hubris
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?Frameworks (almost by definition)
Develop in the PHP community?
Take over an existing project?
Limitations of Site Operations? Hosting? or Virtual hosting?
Business needs trump programmer desire?
Dealt with a user request?
…at Tagged
add slideshow
…at TaggedCubes
Waterfall process Oracle RAC
JavaPHP4
Zend Accelerator
Solutions Consider Environment
3 Golden Gate BridgeThe Design Pattern
the other bridge
Same problem, different pattern
Original design was hybrid cantilever-suspension.
Replaced by purse suspension
Art DecoBridge Tower, Lighting,
pedestrial walkway
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.”
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?
Funky Caching again
“search for the closest matching valid URL and redirect, and use attempted url text as a DB keyword lookup”
—Rasmus Lerdorf
Javascript and CSS compiling & caching
<?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);// }}}
<?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);
<?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);
<?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);
Beyond Funky Caching
San FranciscoLooking around you for
inspiration