PHP Apachecon Dec.14, 2005. San Diego Rasmus Lerdorf - - PDF document

php
SMART_READER_LITE
LIVE PREVIEW

PHP Apachecon Dec.14, 2005. San Diego Rasmus Lerdorf - - PDF document

PHP Apachecon Dec.14, 2005. San Diego Rasmus Lerdorf <rasmus@php.net> http://talks.lerdorf.com/show/acon05 Slide 1/26 November 17 2005 Server-Side PHP is a Server-side language Even though it is embedded in HTML files much like the


slide-1
SLIDE 1

PHP

Apachecon Dec.14, 2005. San Diego Rasmus Lerdorf <rasmus@php.net> http://talks.lerdorf.com/show/acon05

slide-2
SLIDE 2

Slide 1/26 November 17 2005

Server-Side

PHP is a Server-side language

Even though it is embedded in HTML files much like the client-side Javascript language, PHP is server-side and all PHP tags will be replaced by the server before anything is sent to the web browser.

  • So if the HTML file contains:
  • <html>

<?php echo "Hello World" ?> </html>

What the end user would see with a "view source" in the browser would be:

  • <html>

Hello World </html>

  • 2 -
slide-3
SLIDE 3

Slide 2/26 November 17 2005

Idea

This is a C program that generates an HTML page with a table populated from an SQL query.

  • #include <stdio.h>

#include <stdlib.h> #include "mysql.h" MYSQL mysql; void err(void) { fprintf(stderr, "%s\n", mysql_error(&mysql) ); exit(1); } int gen_page() { puts("<html><body><table>"); if(!(mysql_connect(&mysql,"host","user","passwd"))) err(); if(mysql_select_db(&mysql,"my_db")) err(); if(mysql_query(&mysql,"SELECT * FROM my_table")) err(); if(!(res = mysql_store_result(&mysql))) err(); while((row = mysql_fetch_row(res))) { puts("<tr>"); for (i=0 ; i < mysql_num_fields(res); i++) printf("<td>%s</td>\n",row[i]); puts("</tr>"); } puts("</table></body></html>"); if (!mysql_eof(res)) err(); mysql_free_result(res); mysql_close(&mysql); }

Here is the same thing in PHP which produces the same output.

  • <html><body><table>

<?php mysql_connect('host','user','passwd') or die(mysql_error()); $res = mysql_db_query('my_db','SELECT * FROM my_table')

  • r die(mysql_error());

while(($row = mysql_fetch_row($res))) { echo '<tr>'; for ($i=0 ; $i < mysql_num_fields($res); $i++) echo "<td>{$row[$i]}</td>\n"; echo '</tr>'; } ?> </table></body></html>

  • 3 -
slide-4
SLIDE 4

Slide 3/26 November 17 2005

Fail Fast!

Fail Fast

  • Fail Cheap
  • Be Lazy
  • The greatest inefficiencies come from solving problems you will never have.
  • 4 -
slide-5
SLIDE 5

Slide 4/26 November 17 2005

What is Large?

  • 75 properties
  • 25 international portals
  • 15 Languages
  • Hundreds of millions of registered users
  • Literally billions of page views per day
  • Thousands of engineers spread around the world
  • Merger of 24 Web Companies
  • Make that 30 now with FareChase, MusicMatch, Flickr, DialPad, Konfabulator and Upcoming.org
  • 5 -
slide-6
SLIDE 6

Slide 5/26 November 17 2005

Why PHP?

  • Easy to learn and manage - well-documented
  • Works well on existing FreeBSD/Apache platform
  • Easy to integrate existing C/C++ code
  • Inherent HTML embedding
  • Outperformed technologies it replaced
  • 6 -
slide-7
SLIDE 7

Slide 6/26 November 17 2005

Scale

  • Share-nothing Architecture
  • Like HTTP, each request is distinct
  • Shared data is pushed down to the data-store layer
  • This gives us
  • Ability to load balance
  • Invisible failover from one datacenter to another
  • Better modularization of applications
  • Easier to develop and debug
  • 7 -
slide-8
SLIDE 8

Slide 7/26 November 17 2005

Scale

No Runtime Templating!

  • PHP is a templating system, we don't need another one
  • Ok, sometimes you might, but don't do it at runtime
  • System Calls - PHP 5.1 Code Changes
  • No more than 1 stat() per PHP file per request
  • Add a stat cache to PHP's expand_filepath code
  • Don't stat if Apache has already stat'ed the file
  • Get rid of excessive stats in the streams code
  • System Calls - Scripting Changes
  • Remove ./ from include_path and use relative path includes
  • Use just a single base dir in include_path
  • No open_basedir or safe_mode
  • 8 -
slide-9
SLIDE 9

Slide 8/26 November 17 2005

Scale

Watch those compiler flags

  • Use non-PIC Apache DSO (gcc -prefer-non-pic) by using --without-pic
  • Use platform-specific gcc flags
  • ./configure --disable-all
  • Other Changes
  • Plenty of custom extensions and limit RINIT
  • Filter all user data by default
  • No $_COOKIE nor $_ENV for you and use JIT population for $_SERVER
  • Careful use of the session extension
  • 9 -
slide-10
SLIDE 10

Slide 9/26 November 17 2005

Software Stack

  • FreeBSD 4.x/6.x
  • Apache 1.3.x
  • Plenty of homegrown C/C++ code
  • MySQL/Oracle
  • PHP/Perl
  • APC Opcode Cache
  • 10 -
slide-11
SLIDE 11

Slide 10/26 November 17 2005

People Architecture

  • 11 -
slide-12
SLIDE 12

Slide 11/26 November 17 2005

App Architecture

A suggested architecture for a PHP application. The template layer should have as little business logic as possible. As you go down you have less presentation and more business logic.

  • 12 -
slide-13
SLIDE 13

Slide 12/26 November 17 2005

Large-Scale PHP

Scaling PHP is all about scaling your backend

  • Or bigger
  • Debugging and Profiling, look at xdebug (xdebug.org) and apd (pecl/apd)
  • 13 -
slide-14
SLIDE 14
  • 14 -
slide-15
SLIDE 15

Slide 13/26 November 17 2005

PHP 5

PHP 5

  • 15 -
slide-16
SLIDE 16

Slide 14/26 November 17 2005

XML

All XML handling based on libxml2

<?php $dom = new domDocument; $dom->load('menu.xml'); ?>

presentations/slides/intro/menu.xml

&lt;?xml&nbsp;version=&quot;1.0&quot;&nbsp;encoding=&quot;ISO-8859-1&quot;?&gt; &lt;breakfast_menu&gt; &nbsp;&lt;food&nbsp;itemno=&quot;1&quot;&gt; &nbsp;&nbsp;&lt;name&gt;Belgian&nbsp;Waffles&lt;/name&gt; &nbsp;&nbsp;&lt;price&gt;$5.95&lt;/price&gt; &nbsp;&nbsp;&lt;description&gt;two&nbsp;of&nbsp;our&nbsp;famous&nbsp;Belgian&nbsp;Waffles&nbsp;with&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;plenty&nbsp;of&nbsp;real&nbsp;maple&nbsp;syrup&lt;/description&gt; &nbsp;&nbsp;&lt;calories&gt;650&lt;/calories&gt; &nbsp;&lt;/food&gt; &nbsp;&lt;food&nbsp;itemno=&quot;2&quot;&gt; &nbsp;&nbsp;&lt;name&gt;Strawberry&nbsp;Belgian&nbsp;Waffles&lt;/name&gt; &nbsp;&nbsp;&lt;price&gt;$7.95&lt;/price&gt; &nbsp;&nbsp;&lt;description&gt;light&nbsp;Belgian&nbsp;waffles&nbsp;covered&nbsp;with&nbsp;strawberries &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;and&nbsp;whipped&nbsp;cream&lt;/description&gt; &nbsp;&nbsp;&lt;calories&gt;900&lt;/calories&gt; &nbsp;&lt;/food&gt; &nbsp;&lt;food&nbsp;itemno=&quot;3&quot;&gt; &nbsp;&nbsp;&lt;name&gt;Berry-Berry&nbsp;Belgian&nbsp;Waffles&lt;/name&gt; &nbsp;&nbsp;&lt;price&gt;$8.95&lt;/price&gt; &nbsp;&nbsp;&lt;description&gt;light&nbsp;Belgian&nbsp;waffles&nbsp;covered&nbsp;with&nbsp;an&nbsp;assortment&nbsp;of &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;fresh&nbsp;berries&nbsp;and&nbsp;whipped&nbsp;cream&lt;/description&gt; &nbsp;&nbsp;&lt;calories&gt;900&lt;/calories&gt; &nbsp;&lt;/food&gt; &nbsp;&lt;food&nbsp;itemno=&quot;4&quot;&gt; &nbsp;&nbsp;&lt;name&gt;French&nbsp;Toast&lt;/name&gt; &nbsp;&nbsp;&lt;price&gt;$4.50&lt;/price&gt; &nbsp;&nbsp;&lt;description&gt;thick&nbsp;slices&nbsp;made&nbsp;from&nbsp;our&nbsp;homemade&nbsp;sourdough&nbsp;bread&lt;/description&gt; &nbsp;&nbsp;&lt;calories&gt;600&lt;/calories&gt; &nbsp;&lt;/food&gt;

  • 16 -
slide-17
SLIDE 17

&nbsp;&lt;food&nbsp;itemno=&quot;5&quot;&gt; &nbsp;&nbsp;&lt;name&gt;Homestyle&nbsp;Breakfast&lt;/name&gt; &nbsp;&nbsp;&lt;price&gt;$6.95&lt;/price&gt; &nbsp;&nbsp;&lt;description&gt;two&nbsp;eggs,&nbsp;bacon&nbsp;or&nbsp;sausage,&nbsp;toast,&nbsp;and&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;our&nbsp;ever-popular&nbsp;hash&nbsp;browns&lt;/description&gt; &nbsp;&nbsp;&lt;calories&gt;950&lt;/calories&gt; &nbsp;&lt;/food&gt; &lt;/breakfast_menu&gt;

XPath

<?php $dom = domdocument::load('presentations/slides/intro/menu.xml'); $ctx = new domXPath($dom); $result = $ctx->query('/breakfast_menu/food[@itemno > 3]/price/text()'); foreach($result as $node) { echo $node->nodeValue."<br />\n"; } ?>

Output:

$4.50 $6.95

XSL

<?php $dom = domdocument::load('presentations/slides/intro/menu.xml'); $domxsl = domDocument::load('presentations/slides/intro/menu.xsl'); $proc = new xsltProcessor; $proc->importStyleSheet($domxsl); echo $proc->transformToXML($dom); ?>

Output:

Belgian Waffles

  • $5.95two of our famous Belgian Waffles with

plenty of real maple syrup (650 calories per serving) Strawberry Belgian Waffles

  • $7.95light Belgian waffles covered with strawberries

and whipped cream (900 calories per serving) Berry-Berry Belgian Waffles

  • $8.95light Belgian waffles covered with an assortment
  • f

fresh berries and whipped cream (900 calories per serving) French Toast

  • $4.50thick slices made from our homemade sourdough

bread (600 calories per serving) Homestyle Breakfast

  • 17 -
slide-18
SLIDE 18
  • $6.95two eggs, bacon or sausage, toast, and
  • ur ever-popular hash browns

(950 calories per serving)

SimpleXML

<?php $menu = simplexml_load_file('presentations/slides/intro/menu.xml'); foreach ($menu->food as $item) { echo $item['itemno'] . ") "; echo $item->price . " ... "; echo $item->name . "<br />\n"; } ?>

Output:

1) $5.95 ... Belgian Waffles 2) $7.95 ... Strawberry Belgian Waffles 3) $8.95 ... Berry-Berry Belgian Waffles 4) $4.50 ... French Toast 5) $6.95 ... Homestyle Breakfast

  • 18 -
slide-19
SLIDE 19

Slide 15/26 November 17 2005

Web 2.0

Web 2.0

  • The Programmable Web
  • 19 -
slide-20
SLIDE 20

Slide 16/26 November 17 2005

Ajax in 90 seconds

rpc.js

function createRequestObject() { var ro; ro = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP"); return ro; } var http = createRequestObject(); function sndReq(action) { http.open('get', 'rpc.php?action='+action); http.onreadystatechange = handleResponse; http.send(null); } function handleResponse() { if(http.readyState == 4){ var response = http.responseText; var update = new Array(); if(response.indexOf('|') != -1) { update = response.split('|'); document.getElementById(update[0]).innerHTML = update[1]; } } }

This creates a request object along with a send request and handle response function. So to actually use it, you could include this js in your page. Then to make one of these backend requests you would tie it to something. Like an onclick event or a straight href like this:

  • <a href="javascript:sndReq('foo')">[foo]</a>

That means that when someone clicks on that link what actually happens is that a backend request to rpc.php?action=foo will be sent.

  • rpc.php

<?php

  • 20 -
slide-21
SLIDE 21

switch($_REQUEST['action']) { case 'foo': / do something / echo "foo|foo done"; break; ... } ?>

Now, look at handleResponse. It parses the "foo|foo done" string and splits it on the '|' and uses whatever is before the '|' as the dom element id in your page and the part after as the new innerHTML

  • f that element. That means if you have a div tag like this in your page:
  • <div id="foo">

</div>

Once you click on that link, that will dynamically be changed to:

  • <div id="foo">

foo done </div>

Expanding this approach a bit to send multiple parameters in the request, for example, would be really

  • simple. Something like:
  • function sndReqArg(action,arg) {

http.open('get', 'rpc.php?action='+action+'&arg='+arg); http.onreadystatechange = handleResponse; http.send(null); }

  • 21 -
slide-22
SLIDE 22

Slide 17/26 November 17 2005

SOAP Server

<?php function Add($x,$y) { return $x+$y; } $server = new SoapServer(null,array('uri'=>"http://test-uri/")); $server->addFunction("Add"); $server->handle(); ?>

Or using a WSDL:

  • <?php

function Add($x,$y) { return $x+$y; } $server = new SoapServer("./add.wsdl")); $server->addFunction("Add"); $server->handle(); ?>

add.wsdl

<?xml version="1.0" ?> <definitions xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:si="http://soapinterop.org/xsd" xmlns:tns="http://localhost/~rasmus/pecl/soap/test.wsdl" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns="http://schemas.xmlsoap.org/wsdl/" targetNamespace="http://localhost/~rasmus/pecl/soap/test.wsdl"> <types> <xsd:schema targetNamespace="http://localhost/~rasmus/pecl/soap/test.wsdl"> <xsd:import namespace="http://schemas.xmlsoap.org/soap/encoding/" /> <xsd:import namespace="http://schemas.xmlsoap.org/wsdl/" /> </xsd:schema> </types> <message name="AddRequest"> <part name="x" type="xsd:double" /> <part name="y" type="xsd:double" /> </message> <message name="AddResponse"> <part name="result" type="xsd:double" /> </message> <portType name="TestServicePortType"> <operation name="Add"> <input message="tns:AddRequest" /> <output message="tns:AddResponse" /> </operation> </portType> <binding name="TestServiceBinding" type="tns:TestServicePortType"> <soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http" />

  • 22 -
slide-23
SLIDE 23

<operation name="Add"> <soap:operation soapAction="Add" style="rpc" /> <input> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </input> <output> <soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/> </output> </operation> </binding> <service name="TestService"> <port name="TestServicePort" binding="tns:TestServiceBinding"> <soap:address location="http://localhost/~rasmus/pecl/soap/soap_server.php"/> </port> </service> </definitions>

  • 23 -
slide-24
SLIDE 24

Slide 18/26 November 17 2005

Web Services

With PHP5's solid XML support and improved internal OOP support, Web Services are now a natural fit for PHP. Most people seem to think of SOAP when we say Web Services, so here is the

  • bligatory SOAP example:
  • <?php

$amazon_index = array( 'DVD', 'Photo', 'Electronics', 'OfficeProducts', 'HealthPersonalCare', 'Toys', 'Baby', 'VideoGames', 'MusicTracks', 'OutdoorLiving', 'Blended', 'MusicalInstruments', 'Magazines', 'DigitalMusic', 'Jewelry', 'Video', 'Tools', 'PCHardware', 'SportingGoods', 'Classical', 'Software', 'Books', 'VHS', 'Wireless', 'Restaurants', 'Music', 'GourmetFood', 'Miscellaneous', 'Kitchen', 'WirelessAccessories', 'Merchants', 'Beauty', 'Apparel' ); function amazon($index, $keywords, $timeout=7200) { $dest_file = "/tmp/aws_{$index}_".md5($keywords); if(file_exists($dest_file) && filemtime($dest_file) > (time()-$timeout)) { $result = unserialize(file_get_contents($dest_file)); } else { $aws = new SoapClient('http://webservices.amazon.com/'. 'AWSECommerceService/US/AWSECommerceService.wsdl', array("trace" => 1)); $result = $aws->ItemSearch(array( 'SubscriptionId'=>'XXXXXXXXXXXXXX', 'AssociateTag'=>'lerdorf-20', 'Request'=>array(array('SearchIndex'=>$index, 'Keywords'=>$keywords)) ) ); $tmpf = tempnam('/tmp','YWS'); file_put_contents($tmpf, serialize($result)); rename($tmpf, $dest_file); } return $result; } ?>

  • 24 -
slide-25
SLIDE 25

Slide 19/26 November 17 2005

No more SOAP!

SOAP makes my head hurt. Let's look at some stuff we can all actually understand instead.

  • Take a pinch of RSS

<?php $url = 'http://buzz.yahoo.com/feeds/buzzoverl.xml'; $xml = @simplexml_load_file($url); if($xml) { foreach($xml->channel->item as $item) { $ret[(string)$item->title] = (string)$item->link; } echo "<pre>"; print_r($ret); echo "</pre>"; } else echo "Can't get to Yahoo!"; ?>

Output:

Array ( [1. Eddie Guerrero] => http://search.yahoo.com/search?p=eddie+guerrero&cs=bz [2. WWE] => http://search.yahoo.com/bin/search?p=wwe [3. Brooke Burns] => http://search.yahoo.com/search?p=brooke+burns&cs=bz [4. NFL] => http://search.yahoo.com/search?p=national+football+league&cs=bz [5. Harry Potter and the Goblet of Fire] => http://search.yahoo.com/search?p=harry+potter&cs=bz [6. 50 Cent] => http://search.yahoo.com/search?p=50+cent&cs=bz [7. Mariah Carey] => http://search.yahoo.com/search?p=mariah+carey&cs=bz [8. Madonna] => http://search.yahoo.com/search?p=madonna&cs=bz [9. Green Day] => http://search.yahoo.com/search?p=green+day&cs=bz [10. Britney Spears] => http://search.yahoo.com/search?p=britney+spears&cs=bz [11. Oprah Winfrey] => http://search.yahoo.com/search?p=oprah+winfrey&cs=bz [12. Black Eyed Peas] => http://search.yahoo.com/search?p=black+eyed+peas&cs=bz [13. Nelly] => http://search.yahoo.com/search?p=nelly&cs=bz [14. Xbox 360] => http://search.yahoo.com/search?p=xbox+360&cs=bz [15. Eminem] => http://search.yahoo.com/search?p=eminem&cs=bz )

Add a spoonful of REST

<?php $srv='http://api.search.yahoo.com/ImageSearchService/V1/imageSearch'; foreach($ret as $key=>$link) { $url = $srv . "?query=$key&appid=RESTDemo"; $obj = simplexml_load_file($url); } ?>

A thimble of gradeschool math

x^2/a^2 + y^2/b^2 = 1

  • 25 -
slide-26
SLIDE 26

And we end up with something like this

  • http://buzz.progphp.com

By the way, throwing a cache layer between you and whatever remote service you are accessing tends to be a good idea. In our case we can do it like this:

  • <?php

$tmp = '/tmp/'.md5($q); if(!file_exists($tmp) || filemtime($tmp) < (time()-7200)) { $stream = fopen($url,'r'); $tmpf = tempnam('/tmp','YWS'); file_put_contents($tmpf, $stream); fclose($stream); rename($tmpf, $tmp); } $obj = simplexml_load_file($tmp); ?>

  • 26 -
slide-27
SLIDE 27

Slide 20/26 November 17 2005

REST services from Yahoo!

  • Web Search
  • Web Search with Context
  • MyWeb
  • Creative Commons Web Search
  • News Search
  • Image Search
  • Local Search
  • Audio Search
  • Video Search
  • Site Explorer
  • Events (upcoming.org)
  • Flickr
  • Maps
  • Geocoding
  • Context Extraction Service
  • Context Extraction Service
  • Parameter Value

Description appid string (required) The application ID. See Application IDs for more information. context string (required) The context to extract terms from (UTF-8 encoded). query string An optional query to help with the extraction process.

Example

<?php $context = <<< EOB PHP is a versatile scripting language that is mostly used to solve the web

  • problem. PHP is not a drug, and it is not a Phillipine

currency, but chicks dig it. Ok, perhaps chicks don't really dig it, and it is actually a currency, but it is still cool. EOB; $url = 'http://api.search.yahoo.com/ContentAnalysisService/V1/termExtraction'; $post = "query=tool&appid=YahooDemo&context=".rawurlencode($context); $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_POST, 1); curl_setopt($ch, CURLOPT_POSTFIELDS, $post); $xml = simplexml_load_string(curl_exec($ch)); curl_close($ch); $config = array('indent'=>TRUE, 'indent-attributes'=>TRUE, 'wrap-attributes'=>TRUE,'input-xml'=>TRUE, 'output-xml'=>TRUE); echo nl2br(str_replace(' ','&nbsp;',

  • 27 -
slide-28
SLIDE 28

htmlspecialchars(tidy_parse_string($xml->asXML(),$config)))); ?>

Output:

&lt;?xml&nbsp;version=&quot;1.0&quot;&nbsp;encoding=&quot;utf-8&quot;?&gt; &lt;ResultSet&nbsp;xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xmlns=&quot;urn:yahoo:cate&quot; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;xsi:schemaLocation=&quot;urn:yahoo:cate&nbsp;http://api.search.yahoo.com/ContentAnalysisService/V1/TermExtractionResponse.xsd&quot;&gt; &nbsp;&nbsp;&lt;Result&gt;scripting&nbsp;language&lt;/Result&gt; &nbsp;&nbsp;&lt;Result&gt;chicks&lt;/Result&gt; &nbsp;&nbsp;&lt;Result&gt;currency&lt;/Result&gt; &nbsp;&nbsp;&lt;Result&gt;php&lt;/Result&gt; &nbsp;&nbsp;&lt;Result&gt;web&nbsp;problem&lt;/Result&gt; &nbsp;&nbsp;&lt;Result&gt;phillipine&lt;/Result&gt; &lt;/ResultSet&gt; &lt;!--&nbsp;ws02.search.scd.yahoo.com&nbsp;uncompressed/chunked&nbsp;Thu&nbsp;Nov&nbsp;17&nbsp;10:10:46&nbsp;PST&nbsp;2005&nbsp;--&gt;

  • 28 -
slide-29
SLIDE 29

Slide 21/26 November 17 2005

Flickr API

Flickr also has a REST interface

  • REST Query to get public photos

http://flickr.com/services/rest/?method=flickr.people.getPublicPhotos&user_id=56053642@N00&api_key=...

Returned XML

<?xml version="1.0" encoding="utf-8" ?> <rsp stat="ok"> <photos page="1" pages="1" perpage="100" total="2"> <photo id="39006009" owner="56053642@N00" secret="f2086066d5" server="33" title="IMG_7564.JPG" ispublic="1" isfriend="0" isfamily="0" /> <photo id="39006000" owner="56053642@N00" secret="4ec57bd51f" server="28" title="IMG_7551.JPG" ispublic="1" isfriend="0" isfamily="0" /> </photos> </rsp>

We can parse it like this

<?php function photo_url($p, $size='s', $ext='jpg') { return "http://photos{$p['server']}.flickr.com/{$p['id']}{$p['secret']}{$size}.{$ext}"; } $url = "http://flickr.com/services/rest/?method=flickr.people.getPublicPhotos". "&user_id=56053642@N00&per_page=2&api_key=3aba8184848f9263b80795c95529bcd1"; $xml = @simplexml_load_file($url) or die("Unable to contact Flickr"); $perpage = $xml->photos['perpage']; $total = $xml->photos['total']; foreach($xml->photos->photo as $photo) { echo '<a href="'.photo_url($photo,'b').'" target="_blank">'; echo '<img src="'.photo_url($photo).'" height="75" width="75" />'; echo '<br />'.$photo['title'].'</a><br />'; } ?>

Output:

CassisIMG_0495.JPG

  • 29 -
slide-30
SLIDE 30

Slide 22/26 November 17 2005

Geocoding API

Finally, a free geocoding API

  • REST Query to look up an address

http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&location=94539

Returned XML

<ResultSet xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"> <Result precision="zip"> <Latitude>37.5173</Latitude> <Longitude>-121.9263</Longitude> <Address/> <City>FREMONT</City> <State>CA</State> <Zip>94539</Zip> <Country>US</Country> </Result> </ResultSet>

We can parse it like this

<?php $url = 'http://api.local.yahoo.com/MapsService/V1/geocode'; $url .= '?appid=YahooDemo&location=94539'; $xml = simplexml_load_file($url); $ret['precision'] = (string)$xml->Result['precision']; $ret['warning'] = (string)$xml->Result['warning']; foreach($xml->Result->children() as $key=>$val) { if(strlen($val)) $ret[(string)$key] = (string)$val; } echo "<pre>"; print_r($ret); echo "</pre>"; ?>

Output:

Array ( [precision] => zip [warning] => [Latitude] => 37.5173 [Longitude] => -121.9263 [City] => FREMONT [State] => CA [Zip] => 94539 [Country] => US )

Closest Match

http://api.local.yahoo.com/MapsService/V1/geocode?appid=YahooDemo&location=70120First,20Sunnyvale

Returned XML

<ResultSet xsi:schemaLocation="urn:yahoo:maps http://api.local.yahoo.com/MapsService/V1/GeocodeResponse.xsd"> <Result precision="address" warning="The exact location could not be found, here is the closest match: 701 First Ave, Sunnyvale, CA 94089">

  • 30 -
slide-31
SLIDE 31

<Latitude>37.416384</Latitude> <Longitude>-122.024853</Longitude> <Address>701 FIRST AVE</Address> <City>SUNNYVALE</City> <State>CA</State> <Zip>94089-1019</Zip> <Country>US</Country> </Result> </ResultSet>

Docs: http://developer.yahoo.net/maps/

  • 31 -
slide-32
SLIDE 32

Slide 23/26 November 17 2005

Map Tiles API

You can also ask for a single map tile of any location at any size.

  • REST Query to get a tile

http://api.local.yahoo.com/MapsService/V1/mapImage?appid=YahooDemo&location=94539

Returned XML

<Result> http://img.maps.yahoo.com/mapimage?MAPDATA=NE6uked6wXUvMKeeO65.zg9FRNGF36TLiC8QU14ROsDkbVumowh8Jm3JSJUKwebcVHHXcvWDLt8WExnWh59FAevHEV5ynmh6WyJ3zNdmrLJYh3S95w-- </Result>

Example

<html><head><title>Map Tile Demo</title> <script type="text/javascript"> <?php function request_cache($url, $dest_file, $timeout=43200) { if(!file_exists($dest_file) || filemtime($dest_file) < (time()-$timeout)) { $stream = fopen($url,'r'); $tmpf = tempnam('/tmp','YWS'); file_put_contents($tmpf, $stream); fclose($stream); rename($tmpf, $dest_file); } } $cities = array("San Francisco", "Los Angeles", "Seattle", "Atlanta", "Chicago", "Dallas", "Vancouver, Canada", "New York City", "Toronto", "Miami, FL", "Boston"); $width = 600; $height = 300; $zoom = 7; $url = "http://api.local.yahoo.com/MapsService/V1/mapImage?appid=YahooDemo"; $url .= "&image_height=$height&image_width=$width&zoom=$zoom"; $url .= "&location="; $i=0; foreach($cities as $city) { $q = $url.rawurlencode($city); $tmp = '/tmp/yws_tile_'.md5($q); request_cache($q, $tmp); $xml = simplexml_load_file($tmp); if($i==0) $first = (string)$xml; echo <<<EOB image$i = new Image(); image$i.src = "$xml"; EOB; $i++; } ?> </script> </head><body> <?php echo <<<EOB <div id="img_container" style="float:left; height:{$height}px; width:{$width}px; padding: 0px 10px 0px 10px;"> <img id="the_img" src="$first" height="$height" width="$width"/></div>

  • 32 -
slide-33
SLIDE 33

EOB; $i=0; foreach($cities as $city) { $q = rawurlencode($city); echo <<<EOB <a href="http://maps.yahoo.com/beta/index.php?#trf=0&q1=$q"

  • nmouseover="document.getElementById('the_img').src=image$i.src;

this.style.backgroundColor='white';"

  • nmouseout="this.style.backgroundColor='#eeee33';">$city</a><br

/> EOB; $i++; } ?> </body></html>

Output:

Map Tile Demo image0 = new Image(); image0.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=TObb2ud6wXUOwu3rBpP3X7uQY1CFYFaOi0GLJ_JAJ.ppidOuaul0nE3PMRYF_zCR5ojTf1uiUL1RSvRnq_RhT2.fYkvHjaIQrQgBU76uDiahQ1hTvpRV.B0-";image1 = new Image(); image1.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=r_pf9ud6wXVpMy6nZeaegikQIGpFcNMOXObKl.HNYwQrUVSyLsu_RszJJE5nvPunUUIU3Q._MMDSJpTei7M7amxBrtHWzKA6tavPshvzrumin3zjo42hnWNT6A--";image2 = new Image(); image2.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=zjOb5Od6wXVUs5XuW1tRQiAg9jQPgfwbY..w8NBnYghlA1O05tmZBbFRkhRETfn1Da64JK5lztn2NEi5iCokgfZhgqYyPBkoVAyLBVfxUEWCbWuwnoCl_PDftw--";image3 = new Image(); image3.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=XqZ_oOd6wXW1EkbxSGP7gTOaCuOyp5QuEXFWXdBD63N6xCwoGyuITSfSGieMDdIoeYqbzzfCBYWkFSFWh4j.I8wFlGyHTX2_ud.AtvB5l19Lc2Z1lgncgdFHiw--";image4 = new Image(); image4.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=riug9.d6wXVGBj0JGXa3h4NXBpYAvIavyp0m1fquhpX3paVo1_kda3MOjKEUyNPcXH03bU0CMGN3tI61p5am7pUZ5kG7QiyQ9bMKHa1vsYEpjHwie2PqrAgSIQ--";image5 = new Image(); image5.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=eQEBGud6wXUYoIteDaahjnOmlpbc0qULASmOB5bj3CditxvAHWhtTUiAbnRejt4xWXvrb07PJIn6tC7y9yhIwZDJM.342A1Navd6fTvHK4eO20zTQvVh.MA-";image6 = new Image(); image6.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=bGKft.d6wXVghGjtr5.QpIe7xdvXuohLLkq8jNBOEzMWfkoDt4GvijNgz5iLBRFzJmpK9yxBcKgeRjuZfCLuIpDQ00VbX8t4jL3PsBrR3eBuWEnawlmLs8j2CO5o";image7 = new Image(); image7.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=IkuPJed6wXVU1JEjDeiurHH0yNwgpTrNEJxZSyDXd_j82wLS67gphcDCidwH_QuQXnQ9dJDRjekl10bEdmromhD1kXevCDFGtrMxNFnHebmCPcj3_QfmOr8-";image8 = new Image(); image8.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=_IRkHud6wXWSAyUOnF6MyI3Ew1uqSxrqnKRw6E7rGsEyCSms0jfXl5yGv053CEp0lfTFRjdJNRoTmqBVm5qKE.Us9uv0s1x0FIBbP8tLzD_C3ocUdDFMmOGYvA--";image9 = new Image(); image9.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=NOiNPOd6wXWyElHYQW5LAYVnuzckDzXfky2VzbdnGxgAeeJCj9EZcvTyUc3w4Tb3vlBb8QUi2CXcDeJVwuQnjLcNmrm5dRzUMPvOEkWn7xPu_HrSLNqJXwh5sA--";image10 = new Image(); image10.src = "http://img.maps.yahoo.com/mapimage?MAPDATA=879zQOd6wXVet8_n8kHI0iFe4QKMyB8BaZlYPnPySZR8TYPBFWDPg_Sk0B3gFf3OKqyYlUwAf2sfH0EM84AyhVlpTACd.ABIJXCN4kQA2a2Qw56TdDBBCZka5w--"; San FranciscoLos AngelesSeattleAtlantaChicagoDallasVancouver, CanadaNew York CityTorontoMiami, FLBoston

  • 33 -
slide-34
SLIDE 34

Slide 24/26 November 17 2005

Embedded Map API

There are actually 4 different APIs. Actionscript-Flash, Flex-Flash, Javascript controlling a Flash map, and Javascript controlling an AJAX/DHTML map. I know nothing about Actionscript, nor Flex. Sorry.

  • Simple Embedded Flash map

<script type="text/javascript" src="http://api.maps.yahoo.com/v2.0/fl/javascript/apiloader.js"> </script> <div id="mapContainer" style="height: 400px; width: 930px;"></div> <script type="text/javascript"> var mymap = new Map("mapContainer", "YahooDemo", "San Francisco, CA", 6); mymap.addTool(new PanTool(), true); mymap.addWidget(new NavigatorWidget()); </script>

Output:

var mymap = new Map("mapContainer", "YahooDemo", "San Francisco, CA", 6); mymap.addTool(new PanTool(), true); mymap.addWidget(new NavigatorWidget());

It's actually a little bit disappointing how simple it is. We don't even need PHP here. Of course, chances are you are going to put things onto the map, in which case you are likely to need PHP to fetch data from a backend to plot.

  • Simple Embedded DHTML map

<script type="text/javascript" src="http://api.maps.yahoo.com/ajaxymap?v=2.0&appid=YahooDemo"> </script> <div id="jsmapContainer" style="height: 400px; width: 930px;"></div> <script type="text/javascript"> <?php $url = 'http://api.local.yahoo.com/MapsService/V1/geocode'; $url .= '?appid=YahooDemo&location=San%20Francisco,CA'; $xml = simplexml_load_file($url); ?> var latlon = new YGeoPoint(<?php echo $xml->Result->Latitude?>, <?php echo $xml->Result->Longitude?>); var jsmap = new YMap(document.getElementById('jsmapContainer')); jsmap.addPanControl(); jsmap.addZoomLong(); jsmap.drawZoomAndCenter(latlon, 6); </script>

Output:

var latlon = new YGeoPoint(37.7742,

  • 34 -
slide-35
SLIDE 35
  • 122.417068);

var jsmap = new YMap(document.getElementById('jsmapContainer')); jsmap.addPanControl(); jsmap.addZoomLong(); jsmap.drawZoomAndCenter(latlon, 6);

  • 35 -
slide-36
SLIDE 36

Slide 25/26 November 17 2005

Adding Markers

You can either add markers one by one by calculating a location and adding the marker, or in the Flash API you can use an overlay. There is a very handy GeoRSS overlay that you just feed a Geocoded RSS file to. Each entry needs to have this:

  • GeoRSS Sample Item

<item> <title>M 3.3, Southern California</title> <description>November 08, 2005 22:03:46 GMT</description> <link>http://earthquake.usgs.gov/recenteqsww/Quakes/ci14197132.htm</link> <geo:lat>33.5325</geo:lat> <geo:long>-116.6910</geo:long> <dc:subject>3</dc:subject> <dc:subject>pasthour</dc:subject> <dc:subject>11.60 km</dc:subject> </item>

Adding GeoRSS to the Flash map

<script type="text/javascript" src="http://api.maps.yahoo.com/v2.0/fl/javascript/apiloader.js"> </script> <div id="mapContainer" style="height: 500px; width: 930px;"></div> <script type="text/javascript"> var mymap = new Map("mapContainer", "YahooDemo", "San Francisco, CA", 12); mymap.addTool(new PanTool(), true); mymap.addWidget(new NavigatorWidget());

  • lay = new GeoRSSOverlay(

'http://earthquake.usgs.gov/recenteqsww/catalogs/eqs7day-M2.5.xml'); mymap.addOverlay(olay); </script>

Output:

var mymap = new Map("mapContainer", "YahooDemo", "San Francisco, CA", 12); mymap.addTool(new PanTool(), true); mymap.addWidget(new NavigatorWidget());

  • lay = new GeoRSSOverlay(

'http://earthquake.usgs.gov/recenteqsww/catalogs/eqs7day-M2.5.xml'); mymap.addOverlay(olay);

Ok, again, disappointing that we didn't need PHP. But to do the same on the DHTML map we need some PHP help

  • Simple Embedded DHTML map

<script type="text/javascript"

  • 36 -
slide-37
SLIDE 37

src="http://api.maps.yahoo.com/ajaxymap?v=2.0&appid=YahooDemo"> </script> <div id="jsmapContainer" style="height: 500px; width: 930px;"></div> <?php include 'simple_rss.php'; $url = 'http://earthquake.usgs.gov/recenteqsww/catalogs/eqs7day-M2.5.xml'; $feed = rss_request($url, $timeout=3600); ?> <script type="text/javascript"> <?php $url = 'http://api.local.yahoo.com/MapsService/V1/geocode'; $url .= '?appid=YahooDemo&location=San%20Francisco,CA'; $xml = simplexml_load_file($url); ?> var marker = new Array(); var latlon = new YGeoPoint(<?php echo $xml->Result->Latitude?>, <?php echo $xml->Result->Longitude?>); var jsmap = new YMap(document.getElementById('jsmapContainer')); jsmap.addPanControl(); jsmap.addZoomLong(); jsmap.drawZoomAndCenter(latlon, 12); <?php $i = 0; while(!empty($feed[$i])) { $info = $feed[$i]['title'][0]."<br />"; $info .= $feed[$i]['description'][0]."<br />"; $info .= '<a href="'.$feed[$i]['link'][0].'">more info</a>'; $info = addslashes($info); $lat = $feed[$i]['lat'][0]; $lon = $feed[$i]['long'][0]; echo <<<EOB marker[$i] = new YMarker(new YGeoPoint($lat,$lon)); marker[$i].addLabel("<b>$i</b>"); YEvent.Capture(marker[$i], EventsList.MouseClick, new Function("marker[$i].openSmartWindow('$info');")); jsmap.addOverlay(marker[$i]); EOB; flush(); $i++; if($i>9) break; } ?> </script>

Output:

var marker = new Array(); var latlon = new YGeoPoint(37.7742,

  • 122.417068);

var jsmap = new YMap(document.getElementById('jsmapContainer')); jsmap.addPanControl(); jsmap.addZoomLong(); jsmap.drawZoomAndCenter(latlon, 12); marker[0] = new YMarker(new YGeoPoint(53.9236,-163.8270)); marker[0].addLabel("0"); YEvent.Capture(marker[0], EventsList.MouseClick, new Function("marker[0].openSmartWindow('M 3.1, Unimak Island region, AlaskaNovember 17, 2005 17:05:16 GMTmore info');"));

  • 37 -
slide-38
SLIDE 38

jsmap.addOverlay(marker[0]); marker[1] = new YMarker(new YGeoPoint(64.2463,-149.9646)); marker[1].addLabel("1"); YEvent.Capture(marker[1], EventsList.MouseClick, new Function("marker[1].openSmartWindow('M 2.7, Central AlaskaNovember 17, 2005 14:46:08 GMTmore info');")); jsmap.addOverlay(marker[1]); marker[2] = new YMarker(new YGeoPoint(12.0783,-87.9075)); marker[2].addLabel("2"); YEvent.Capture(marker[2], EventsList.MouseClick, new Function("marker[2].openSmartWindow('M 4.5, near the coast of NicaraguaNovember 17, 2005 12:19:57 GMTmore info');")); jsmap.addOverlay(marker[2]); marker[3] = new YMarker(new YGeoPoint(61.6980,-150.7379)); marker[3].addLabel("3"); YEvent.Capture(marker[3], EventsList.MouseClick, new Function("marker[3].openSmartWindow('M 3.1, Southern AlaskaNovember 17, 2005 09:45:14 GMTmore info');")); jsmap.addOverlay(marker[3]); marker[4] = new YMarker(new YGeoPoint(38.8010,-122.7802)); marker[4].addLabel("4"); YEvent.Capture(marker[4], EventsList.MouseClick, new Function("marker[4].openSmartWindow('M 3.5, Northern CaliforniaNovember 17, 2005 08:56:27 GMTmore info');")); jsmap.addOverlay(marker[4]); marker[5] = new YMarker(new YGeoPoint(38.8137,-122.7822)); marker[5].addLabel("5"); YEvent.Capture(marker[5], EventsList.MouseClick, new Function("marker[5].openSmartWindow('M 4.1, Northern CaliforniaNovember 17, 2005 08:55:05 GMTmore info');")); jsmap.addOverlay(marker[5]); marker[6] = new YMarker(new YGeoPoint(62.3990,-150.5392)); marker[6].addLabel("6"); YEvent.Capture(marker[6], EventsList.MouseClick, new Function("marker[6].openSmartWindow('M 2.6, Central AlaskaNovember 17, 2005 07:38:34 GMTmore info');")); jsmap.addOverlay(marker[6]); marker[7] = new YMarker(new YGeoPoint(31.9538,-114.9526)); marker[7].addLabel("7"); YEvent.Capture(marker[7], EventsList.MouseClick, new Function("marker[7].openSmartWindow('M 2.9, Sonora, MexicoNovember 17, 2005 04:51:44 GMTmore info');")); jsmap.addOverlay(marker[7]); marker[8] = new YMarker(new YGeoPoint(19.5120,-155.5990)); marker[8].addLabel("8"); YEvent.Capture(marker[8], EventsList.MouseClick, new Function("marker[8].openSmartWindow('M 2.7, Island of Hawaii, HawaiiNovember 17, 2005 01:10:09 GMTmore info');")); jsmap.addOverlay(marker[8]); marker[9] = new YMarker(new YGeoPoint(19.3435,-155.4903)); marker[9].addLabel("9"); YEvent.Capture(marker[9], EventsList.MouseClick, new Function("marker[9].openSmartWindow('M 2.6, Island of Hawaii, HawaiiNovember 17, 2005 00:43:19 GMTmore info');")); jsmap.addOverlay(marker[9]);

  • 38 -
slide-39
SLIDE 39
  • 39 -
slide-40
SLIDE 40

Slide 26/26 November 17 2005

Resources

Slides: http://talks.lerdorf.com/show/acon05 Slides: http://xdebug.org APC: http://pecl.php.net/package/APC Yahoo: http://developer.yahoo.net/ Flickr: http://flickr.com/services/api/

  • 40 -
slide-41
SLIDE 41

Index

Server-Side ...................................................................................................... 2 Idea .................................................................................................................. 3 Fail Fast! ......................................................................................................... 4 What is Large? ................................................................................................ 5 Why PHP? ....................................................................................................... 6 Scale ................................................................................................................ 7 Scale ................................................................................................................ 8 Scale ................................................................................................................ 9 Software Stack ................................................................................................ 10 People Architecture ......................................................................................... 11 App Architecture ............................................................................................. 12 Large-Scale PHP ............................................................................................. 13 PHP 5 .............................................................................................................. 15 XML ................................................................................................................ 16 Web 2.0 ........................................................................................................... 19 Ajax in 90 seconds .......................................................................................... 20 SOAP Server ................................................................................................... 22 Web Services .................................................................................................. 24 No more SOAP! .............................................................................................. 25 REST services from Yahoo! ........................................................................... 27 Flickr API ....................................................................................................... 29 Geocoding API ................................................................................................ 30 Map Tiles API ................................................................................................. 32 Embedded Map API ........................................................................................ 34 Adding Markers .............................................................................................. 36 Resources ........................................................................................................ 40