PHP Apachecon Dec.14, 2005. San Diego Rasmus Lerdorf - - PDF document
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/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 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 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 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 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 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 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 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 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 10/26 November 17 2005
People Architecture
- 11 -
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 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 -
- 14 -
Slide 13/26 November 17 2005
PHP 5
PHP 5
- 15 -
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
<?xml version="1.0" encoding="ISO-8859-1"?> <breakfast_menu> <food itemno="1"> <name>Belgian Waffles</name> <price>$5.95</price> <description>two of our famous Belgian Waffles with plenty of real maple syrup</description> <calories>650</calories> </food> <food itemno="2"> <name>Strawberry Belgian Waffles</name> <price>$7.95</price> <description>light Belgian waffles covered with strawberries and whipped cream</description> <calories>900</calories> </food> <food itemno="3"> <name>Berry-Berry Belgian Waffles</name> <price>$8.95</price> <description>light Belgian waffles covered with an assortment of fresh berries and whipped cream</description> <calories>900</calories> </food> <food itemno="4"> <name>French Toast</name> <price>$4.50</price> <description>thick slices made from our homemade sourdough bread</description> <calories>600</calories> </food>
- 16 -
<food itemno="5"> <name>Homestyle Breakfast</name> <price>$6.95</price> <description>two eggs, bacon or sausage, toast, and our ever-popular hash browns</description> <calories>950</calories> </food> </breakfast_menu>
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 -
- $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 15/26 November 17 2005
Web 2.0
Web 2.0
- The Programmable Web
- 19 -
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 -
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 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 -
<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 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 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 -
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 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(' ',' ',
- 27 -
htmlspecialchars(tidy_parse_string($xml->asXML(),$config)))); ?>
Output:
<?xml version="1.0" encoding="utf-8"?> <ResultSet xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="urn:yahoo:cate" xsi:schemaLocation="urn:yahoo:cate http://api.search.yahoo.com/ContentAnalysisService/V1/TermExtractionResponse.xsd"> <Result>scripting language</Result> <Result>chicks</Result> <Result>currency</Result> <Result>php</Result> <Result>web problem</Result> <Result>phillipine</Result> </ResultSet> <!-- ws02.search.scd.yahoo.com uncompressed/chunked Thu Nov 17 10:10:46 PST 2005 -->
- 28 -
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 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 -
<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 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 -
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 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 -
- 122.417068);
var jsmap = new YMap(document.getElementById('jsmapContainer')); jsmap.addPanControl(); jsmap.addZoomLong(); jsmap.drawZoomAndCenter(latlon, 6);
- 35 -
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 -
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 -
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 -
- 39 -
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 -
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