Thursday

Cache it! Solve PHP Performance Problems

<p><strong>In the good old days when building web sites was as easy as knocking up a few <a href="http://www.sitepoint.com/glossary.php?q=H#term_75" class="glossary" title="HTML stands for HyperText Markup Language.">HTML</a> pages, the delivery of a web page to a browser was a simple matter of having the web server fetch a file. A site's visitors would see its small, text-only pages almost immediately, unless they were using particularly slow modems. Once the page was downloaded, the browser would <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">cache</a> it somewhere on the local computer so that, should the page be requested again, after performing a quick check with the server to ensure the page hadn't been updated, the browser could display the locally cached version. Pages were served as quickly and efficiently as possible, and everyone was happy. </strong></p> <p>Then dynamic web pages came along and spoiled the party by introducing two problems: </p> <ul><li>When a request for a dynamic web page is received by the server, some intermediate processing must be completed, such as the execution of scripts by the <a href="http://www.sitepoint.com/glossary.php?q=P#term_1" class="glossary" title="PHP, or Hypertext Preprocessor, is an open source, server-side programming language.">PHP</a> engine. This processing introduces a delay before the web server begins to deliver the output to the browser. This may not be a significant delay where simple PHP scripts are concerned, but for a more complex application, the PHP engine may have a lot of work to do before the page is finally ready for delivery. This extra work results in a noticeable time lag between the user's requests and the actual display of pages in the browser.</li><li>A typical web server, such as <a href="http://www.sitepoint.com/glossary.php?q=A#term_19" class="glossary" title="Apache is one of the world's most widely-used Web servers.">Apache</a>, uses the time of file modification to inform a web browser of a requested page's age, allowing the browser to take appropriate <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">caching</a> action. With dynamic web pages, the actual PHP script may change only occasionally; meanwhile, the content it displays, which is often fetched from a database, will change frequently. The web server has no way of discerning updates to the database, so it doesn't send a last modified date. If the client (that is, the user's browser) has no indication of how long the data will remain valid, it will take a guess. This is problematic if the browser decides to use a locally cached version of the page which is now out of date, or if the browser decides to request from the server a fresh copy of the page, which actually has no new content, making the request redundant. The web server will always respond with a freshly constructed version of the page, regardless of whether or not the data in the database has actually changed.</li></ul> <p>To avoid the possibility of a web site visitor viewing out-of-date content, most web developers use a meta tag or HTTP headers to tell the browser never to use a cached version of the page. However, this negates the web browser's natural ability to cache web pages, and entails some serious disadvantages. For example, the content delivered by a dynamic page may only change once a day, so there's certainly a benefit to be gained by having the browser cache a page--even if only for 24 hours. </p> <p>If you're working with a small PHP application, it's usually possible to live with both issues. But as your site increases in complexity--and attracts more traffic--you'll begin to run into performance problems. Both these issues can be solved, however: the first with server-side caching; the second, by taking control of <a href="http://www.sitepoint.com/glossary.php?q=C#term_15" class="glossary" title="Client-side code is sent to web browser and executed by the browser's rendering engine. ">client-side</a> caching from within your application. The exact approach you use to solve these problems will depend on your application, but in this chapter, we'll consider both PHP and a number of class libraries from <a href="http://www.sitepoint.com/glossary.php?q=P#term_50" class="glossary" title="The PHP Extension and Application Repository - a framework and distribution system for reusable PHP components">PEAR</a> as possible panaceas for your web page woes. </p> <p>Note that in this chapter's discussions of caching, we'll look at only those solutions that can be implemented in PHP. For a more general introduction, the definitive discussion of web caching is represented by <a class="sublink" href="http://www.mnot.net/cache_docs/">Mark Nottingham's tutorial</a>.
</p><p>Furthermore, the solutions in this chapter should not be confused with some of the script caching solutions that work on the basis of optimizing and caching compiled PHP scripts, such as <a class="sublink" href="http://www.zend.com/">Zend Accelerator</a> and <a class="sublink" href="http://www.php-accelerator.co.uk/">ionCube PHP Accelerator</a>.</p> <p>This chapter is excerpted from <a class="sublink" href="http://www.sitepoint.com/books/phpant2/"><em>The PHP Anthology: 101 Essential Tips, Tricks & Hacks, 2nd Edition</em></a>. <a class="sublink" href="http://www.sitepoint.com/article/caching-php-performance/www.sitepoint.com/launch/108ef2/2/120">Download this chapter plus two others, covering PDO and Databases, and Access Control</a>, in PDF format to read offline.</p> <h5>How do I prevent web browsers from caching a page?</h5> <p>If timely information is crucial to your web site and you wish to prevent out-of-date content from ever being visible, you need to understand how to prevent web browsers--and proxy servers--from caching pages in the first place. </p> <p><strong><em>Solutions</em></strong></p> <p>There are two possible approaches we could take to solving this problem: using HTML meta tags, and using HTTP headers.</p> <p><strong>Using HTML Meta Tags </strong></p> <p>The most basic approach to the prevention of page caching is one that utilizes HTML meta tags: </p> <p><code><meta equiv="expires" content="Mon, 26 Jul 1997 05:00:00 GMT">
<meta equiv="pragma" content="no-cache"></code></p> <p>The insertion of a date that's already passed into the <code>Expires</code> meta tag tells the browser that the cached copy of the page is always out of date. Upon encountering this tag, the browser usually won't cache the page. Although the <code>Pragma: no-cache</code> meta tag isn't guaranteed, it's a fairly well-supported convention that most web browsers follow. However, the two issues associated with this approach, which we'll discuss below, may prompt you to look at the alternative solution. </p> <p><strong>Using HTTP Headers</strong></p> <p>A better approach is to use the HTTP protocol itself, with the help of PHP's header function, to produce the equivalent of the two HTML meta tags above: </p> <p><code><?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Pragma: no-cache');
?></code></p> <p>We can go one step further than this, using the <code>Cache-Control</code> header that's supported by HTTP 1.1-capable browsers: </p> <p><code><?php
header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
header('Cache-Control: no-store, no-cache, must-revalidate');
header('Cache-Control: post-check=0, pre-check=0', FALSE);
header('Pragma: no-cache');
?></code></p> <p>For a precise description of HTTP 1.1 Cache-Control headers, have a look at <a class="sublink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9">the W3C's HTTP 1.1 RFC</a>. Another great source of information about HTTP headers, which can be applied readily to PHP, is <a class="sublink" href="http://perl.apache.org/docs/general/correct_headers/correct_headers.html">mod_perl's documentation on issuing correct headers</a>. </p> <p><strong><em>Discussion</em></strong></p> <p>Using the <code>Expires</code> meta tag sounds like a good approach, but two problems are associated with it: </p> <ul><li>The browser first has to download the page in order to read the meta tags. If a tag wasn't present when the page was first requested by a browser, the browser will remain blissfully ignorant and keep its cached copy of the original.</li><li>Proxy servers that cache web pages, such as those common to ISPs, generally won't read the HTML documents themselves. A web browser might know that it shouldn't cache the page, but the proxy server between the browser and the web server probably doesn't--it will continue to deliver the same out-of-date page to the client.</li></ul> <p>On the other hand, using the HTTP protocol to prevent page caching essentially guarantees that no web browser or intervening proxy server will cache the page, so visitors will always receive the latest content. In fact, the first header should accomplish this on its own; this is the best way to ensure a page is not cached. The <code>Cache-Control</code> and <code>Pragma</code> headers are added for some degree of insurance. Although they don't work on all browsers or proxies, the <code>Cache-Control</code> and <code>Pragma</code> headers will catch some cases in which the Expires header doesn't work as intended--if the client computer's date is set incorrectly, for example.</p> <p>Of course, to disallow caching entirely introduces the problems we discussed at the start of this chapter: it negates the web browser's natural ability to cache pages, and can create unnecessary overhead, as new versions of pages are always requested, even though those pages may not have been updated since the browser's last request. We'll look at the solution to these issues in just a moment. </p> <h5>How do I control client-side caching?</h5> <p>We addressed the task of disabling client-side caching in "How do I prevent web browsers from caching a page?", but disabling the cache is rarely the only (or best) option. </p> <p>Here we'll look at a mechanism that allows us to take advantage of client-side caches in a way that can be controlled from within a PHP script. </p> <p><em>Apache Required!</em>
<em>This approach will only work if you're running PHP as an Apache web server module, because it requires use of the function getallheaders--which only works with Apache--to fetch the HTTP headers sent by a web browser.</em></p> <p><strong><em>Solutions</em></strong></p> <p>In controlling client-side caching you have two alternatives. You can set a date on which the page will expire, or respond to the browser's request headers. Let's see how each of these tactics is executed. </p> <p><strong>Setting a Page Expiry Header</strong></p> <p>The header that's easiest to implement is the <code>Expires</code> header--we use it to set a date on which the page will expire, and until that time, web browsers are allowed to use a cached version of the page. Here's an example of this header at work: </p> <p><code>expires.php (excerpt)

<?php
function setExpires($expires) {
header(
'Expires: '.gmdate('D, d M Y H:i:s', time()+$expires).'GMT');
}
setExpires(10);
echo ( 'This page will self destruct in 10 seconds<br />' );
echo ( 'The GMT is now '.gmdate('H:i:s').'<br />' );
echo ( '<a href="'.$_SERVER['PHP_SELF'].'">View Again</a><br />' );
?></code></p> <p>In this example, we created a custom function called <code>setExpires</code> that sets the HTTP <code>Expires</code> header to a point in the future, defined in seconds. The output of the above example shows the current time in GMT, and provides a link that allows us to view the page again. If we follow this link, we'll notice the time updates only once every ten seconds. If you like, you can also experiment by using your browser's Refresh button to tell the browser to refresh the cache, and watching what happens to the displayed date.
</p><p><strong>Acting on the Browser's Request Headers</strong></p> <p>A more useful approach to <a href="http://www.sitepoint.com/glossary.php?q=C#term_15" class="glossary" title="Client-side code is sent to web browser and executed by the browser's rendering engine. ">client-side</a> <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">cache</a> control is to make use of the <code>Last-Modified</code> and <code>If-Modified-Since</code> headers, both of which are available in HTTP 1.0. This action is known technically as performing a conditional GET request; whether your script returns any content depends on the value of the incoming <code>If-Modified-Since</code> request header. </p> <p>If you use <a href="http://www.sitepoint.com/glossary.php?q=P#term_1" class="glossary" title="PHP, or Hypertext Preprocessor, is an open source, server-side programming language.">PHP</a> version 4.3.0 and above on <a href="http://www.sitepoint.com/glossary.php?q=A#term_19" class="glossary" title="Apache is one of the world's most widely-used Web servers.">Apache</a>, the HTTP headers are <a href="http://www.sitepoint.com/glossary.php?q=A#term_61" class="glossary" title="Accessibility deals with the issues of making online content available for experience, enjoyment, and use by all visitors, including those who do not fit the standard "Web user" mould.">accessible</a> with the functions <code>apache_request_headers</code> and <code>apache_response_headers</code>. Note that the function <code>getallheaders</code> has become an alias for the new <code>apache_request_headers</code> function.
</p><p>This approach requires that you send a <code>Last-Modified</code> header every time your PHP script is accessed. The next time the browser requests the page, it sends an <code>If-Modified-Since</code> header containing a time; your script can then identify whether the page has been updated since that time. If it hasn't, your script sends an HTTP 304 status code to indicate that the page hasn't been modified, and exits before sending the body of the page. </p> <p>Let's see these headers in action. The example below uses the modification date of a text file. To simulate updates, we first need to create a way to randomly write to the file: </p> <p><code>ifmodified.php (excerpt)

<?php
$file = 'ifmodified.txt';
$random = <a href="http://www.sitepoint.com/glossary.php?q=%23#term_72" class="glossary" title="An array is a single variable with compartments, each of which can hold a value. ">array</a> (0,1,1);
shuffle($random);
if ( $random[0] == 0 ) {
$fp = fopen($file, 'w');
fwrite($fp, 'x');
fclose($fp);
}
$lastModified = filemtime($file);</code></p> <p>Our simple randomizer provides a one-in-three chance that the file will be updated each time the page is requested. We also use the <code>filemtime</code> function to obtain the last modified time of the file. </p> <p>Next, we send a <code>Last-Modified</code> header that uses the modification time of the text file. We need to send this header for every page we render, to cause visiting browsers to send us the <code>If-Modifed-Since</code> header upon every request: </p> <p><code>ifmodified.php (excerpt)

header('Last-Modified: ' .
gmdate('D, d M Y H:i:s', $lastModified) . ' GMT');</code> </p> <p>Our use of the <code>getallheaders</code> function ensures that PHP gives us all the incoming request headers as an array. We then need to check that the If-Modified-Since header actually exists; if it does, we have to deal with a special case caused by older <a href="http://www.sitepoint.com/glossary.php?q=M#term_31" class="glossary" title="Free, cross-platform open source Web browser and application framework.">Mozilla</a> browsers (earlier than version 6), which appended an illegal extra field to their <code>If-Modified-Since</code> headers. We use PHP's <code>strtotime</code> function to generate a timestamp from the date the browser sent us. If there's no such header, we set this timestamp to zero, which forces PHP to give the visitor an up-to-date copy of the page: </p> <p><code>ifmodified.php (excerpt)

$request = getallheaders();
if (isset($request['If-Modified-Since']))
{
$modifiedSince = explode(';', $request['If-Modified-Since']);
$modifiedSince = strtotime($modifiedSince[0]);
}
else
{
$modifiedSince = 0;
}</code></p> <p>Finally, we check to see whether or not the cache has been modified since the last time the visitor received this page. If it hasn't, we simply send a <code>304 Not Modified</code> response header and exit the script, saving <a href="http://www.sitepoint.com/glossary.php?q=B#term_56" class="glossary" title="Bandwidth is a measure of the amount of date that can be transferred between computers over the Internet.">bandwidth</a> and processing time by prompting the browser to display its cached copy of the page: </p> <p><code>ifmodified.php (excerpt)

if ($lastModified <= $modifiedSince)
{
header('HTTP/1.1 304 Not Modified');
exit();
}
echo ( 'The GMT is now '.gmdate('H:i:s').'<br />' );
echo ( '<a href="'.$_SERVER['PHP_SELF'].'">View Again</a><br />' );
?></code></p> <p>Remember to use the "View Again" link when you run this example (clicking the Refresh button usually clears your browser's cache). If you click on the link repeatedly, the cache will eventually be updated; your browser will throw out its cached version and fetch a new page from the server. </p> <p>If you combine the <code>Last-Modified</code> header approach with time values that are already available in your application--for example, the time of the most recent news article--you should be able to take advantage of web browser caches, saving bandwidth and improving your application's perceived performance in the process. </p> <p>Be very careful to test any <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">caching</a> performed in this manner, though; if you get it wrong, you may cause your visitors to consistently see out-of-date copies of your site.</p> <p><strong><em>Discussion</em></strong></p> <p>HTTP dates are always calculated relative to Greenwich Mean Time (GMT). The PHP function gmdate is exactly the same as the date function, except that it automatically offsets the time to GMT based on your server's system clock and regional settings. </p> <p>When a browser encounters an <code>Expires</code> header, it caches the page. All further requests for the page that are made before the specified expiry time use the cached version of the page--no request is sent to the web server. Of course, client-side caching is only truly effective if the system time on the computer is accurate. If the computer's time is out of sync with that of the web server, you run the risk of pages either being cached improperly, or never being updated. </p> <p>The <code>Expires</code> header has the advantage that it's easy to implement; in most cases, however, unless you're a highly organized person, you won't know exactly when a given page on your site will be updated. Since the browser will only contact the server after the page has expired, there's no way to tell browsers that the page they've cached is out of date. In addition, you also lose some knowledge of the traffic visiting your web site, since the browser will not make contact with the server when it requests a page that's been cached. </p> <h5>How do I examine HTTP headers in my browser?</h5> <p>How can you actually check that your application is running as expected, or debug your code, if you can't actually see the HTTP headers? It's worth knowing exactly which headers your script is sending, particularly when you're dealing with HTTP cache headers. </p> <p><strong><em>Solution</em></strong></p> <p>Several worthy tools are available to help you get a closer look at your HTTP headers: </p> <p><a class="sublink" href="http://livehttpheaders.mozdev.org/"><strong>LiveHTTPHeaders</strong></a>
This add-on to the <a href="http://www.sitepoint.com/glossary.php?q=F#term_45" class="glossary" title="Mozilla FireFox is a cross-platform Web browser.">Firefox</a> browser is a simple but very handy tool for examining request and response headers while you're browsing. </p> <p><a class="sublink" href="http://getfirebug.org/"><strong>Firebug</strong></a>
Another useful Firefox add-on, Firebug is a tool whose interface offers a dedicated tab for examining HTTP request information. </p> <p><a class="sublink" href="http://www.httpwatch.com/"><strong>HTTPWatch</strong></a>
This add-on to Internet Explorer for HTTP viewing and debugging is similar to LiveHTTPHeaders above. </p> <p><a class="sublink" href="http://getcharles.com/"><strong>Charles Web Debugging Proxy</strong></a>
Available for Windows, Mac OS X, and <a href="http://www.sitepoint.com/glossary.php?q=L#term_18" class="glossary" title="An Open Source computing platform based around the robust core of commercial Unix systems.">Linux</a> or Unix, the Charles Web Debugging Proxy is a proxy server that allows developers to see all the HTTP traffic between their browsers and the web servers to which they connect. </p> <p>Any of these tools will allow you to inspect the communication between the server and browser. </p> <h5>How do I cache file downloads with Internet Explorer?</h5> <p>If you're developing file download scripts for Internet Explorer users, you might notice a few issues with the download process. In particular, when you're serving a file download through a PHP script that uses headers such as <code>Content-Disposition: attachment, filename=myFile.pdf</code> or <code>Content-Disposition: inline, filename=myFile.pdf</code>, and that tells the browser not to cache pages, Internet Explorer won't deliver that file to the user. </p> <p><strong><em>Solutions</em></strong></p> <p>Internet Explorer handles downloads in a rather unusual manner: it makes two requests to the web site. The first request downloads the file and stores it in the cache before making a second request, the response to which is not stored. The second request invokes the process of delivering the file to the end user in accordance with the file's type--for instance, it starts Acrobat Reader if the file is a PDF document. Therefore, if you send the cache headers that instruct the browser not to cache the page, Internet Explorer will delete the file between the first and second requests, with the unfortunate result that the end user receives nothing! </p> <p>If the file you're serving through the PHP script won't change, one solution to this problem is simply to disable the "don't cache" headers, <code>pragma</code> and <code>cache-control</code>, which we discussed in "How do I prevent web browsers from caching a page?", for the download script. </p> <p>If the file download will change regularly, and you want the browser to download an up-to-date version of it, you'll need to use the <code>Last-Modified</code> header that we met in "How do I control client-side caching?", and ensure that the time of modification remains the same across the two consecutive requests. You should be able to achieve this goal without affecting users of browsers that handle downloads correctly. </p> <p>One final solution is to write the file to the file system of your web server and simply provide a link to it, leaving it to the web server to report the cache headers for you. Of course, this may not be a viable option if the file is supposed to be secured. </p> <h5>How do I use output buffering for server-side caching?</h5> <p>Server-side processing delay is one of the biggest bugbears of dynamic web pages. We can reduce server-side delay by caching output. The page is generated normally, performing database queries and so on with PHP; however, before sending it to the browser, we capture and store the finished page somewhere--in a file, for instance. The next time the page is requested, the PHP script first checks to see whether a cached version of the page exists. If it does, the script sends the cached version straight to the browser, avoiding the delay involved in rebuilding the page. </p> <p><strong><em>Solution</em></strong></p> <p>Here, we'll look at PHP's in-built caching mechanism, the output buffer, which can be used with whatever page rendering system you prefer (templates or no templates). Consider situations in which your script displays results using, for example, echo or print, rather than sending the data directly to the browser. In such cases, you can use PHP's output control functions to store the data in an in-memory buffer, which your PHP script has both access to and control over. </p> <p>Here's a simple example that demonstrates how the output buffer works: </p> <p><code>buffer.php (excerpt)

<?php
ob_start();
echo '1. Place this in the buffer<br />';
$buffer = ob_get_contents();
ob_end_clean();
echo '2. A normal echo<br />';
echo $buffer;
?></code></p> <p>The buffer itself stores the output as a string. So, in the above script, we commence buffering with the <code>ob_startfunction</code>, and use <code>echo</code> to display a piece of text which is stored in the output buffer automatically. We then use the <code>ob_get_contents</code> function to fetch the data the echo statement placed in the buffer, and store it in the <code>$buffer</code> variable. The <code>ob_end_clean</code> function stops the output buffer and empties the contents; the alternative approach is to use the <code>ob_end_flushfunction</code>, which displays the contents of the buffer. </p> <p>The above script displays the following output: </p> <p><code>2. A normal echo
1. Place this in the buffer</code></p> <p>In other words, we captured the output of the first echo, then sent it to the browser after the second echo. As this simple example suggests, output buffering can be a very powerful tool when it comes to building your site; it provides a solution for caching, as we'll see in a moment, and is also an excellent way to hide errors from your site's visitors, as is discussed in Chapter 9. Output buffering even provides a possible alternative to browser redirection in situations such as user authentication. </p> <p>In order to improve the performance of our site, we can store the output buffer contents in a file. We can then call on this file for the next request, rather than having to rebuild the output from scratch again. Let's look at a quick example of this technique. First, our example script checks for the presence of a cache file: </p> <p><code>sscache.php (excerpt)

<?php
if (file_exists('./cache/page.cache'))
{
readfile('./cache/page.cache');
exit();
}</code></p> <p>If the script finds the cache file, we simply output its contents and we're done! If the cache file is not found, we proceed to output the page using the output buffer:</p> <p><code>sscache.php (excerpt)

ob_start();
?>
<!DOCTYPE <a href="http://www.sitepoint.com/glossary.php?q=H#term_75" class="glossary" title="HTML stands for HyperText Markup Language.">html</a> public "-//<a href="http://www.sitepoint.com/glossary.php?q=W#term_49" class="glossary" title="The World Wide Web Consortium (W3C) - A consortium of industry leaders for drafting Web standards.">W3C</a>//DTD <a href="http://www.sitepoint.com/glossary.php?q=X#term_63" class="glossary" title="XHTML is a reformulation of HTML 4 as an XML 1.0 application.">XHTML</a> 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Cached Page</title>
</head>
<body>
This page was cached with PHP's
<a href="http://www.php<a href=" q="%23#term_2" class="glossary" title=".NET is an application framework from Microsoft.">.net</a>/outcontrol"
>Output Control Functions</a>
</body>
</html>
<?php
$buffer = ob_get_contents();
ob_end_flush();</code></p> <p>Before we flush the output buffer to display our page, we make sure to store the buffer contents in the <code>$buffer</code> variable. </p> <p>The final step is to store the saved buffer contents in a text file: </p> <p><code>sscache.php (excerpt)

$fp = fopen('./cache/page.cache','w');
fwrite($fp,$buffer);
fclose($fp);
?></code></p> <p>The <code>page.cache</code> file contents are exactly same as the HTML that was rendered by the script:</p> <p><code>cache/page.cache (excerpt)

<!DOCTYPE html public "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Cached Page</title>
</head>
<body>
This page was cached with PHP's
<a href="http://www.php.net/outcontrol"
>Output Control Functions</a>
</body>
</html>
</code></p><p><strong><em>Discussion</em></strong></p> <p>For an example that shows how to use <a href="http://www.sitepoint.com/glossary.php?q=P#term_1" class="glossary" title="PHP, or Hypertext Preprocessor, is an open source, server-side programming language.">PHP</a>'s output buffering capabilities to handle errors more elegantly, have a look at the PHP Freaks article <a class="sublink" href="http://www.phpfreaks.com/tutorials/59/0.php"><em>Introduction to Output Buffering</em>, by Derek Ford</a>.</p> <p><strong>What About Template <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">Caching</a>?
</strong></p><p>Template engines often include template caching features--<a class="sublink" href="http://smarty.php.net/">Smarty</a> is a case in point. Usually, these engines offer a built-in mechanism for storing a compiled version of a template (that is, the native PHP generated from the template), which prevents us developers from having to recompile the template every time a page is requested. </p> <p>This process should not be confused with output--or content--caching, which refers to the caching of the rendered <a href="http://www.sitepoint.com/glossary.php?q=H#term_75" class="glossary" title="HTML stands for HyperText Markup Language.">HTML</a> (or other output) that PHP sends to the browser. In addition to the content cache mechanisms discussed in this chapter, Smarty can cache the contents of the HTML page. Whether you use Smarty's content cache or one of the alternatives discussed in this chapter, you can successfully use both template and content caching together on the same site. </p> <p><strong>HTTP Headers and Output Buffering </strong></p> <p>Output buffering can help solve the most common problem associated with the <code>header</code> function, not to mention the issues surrounding <code>session_start</code> and <code>set_cookie</code>. Normally, if you call any of these functions after page output has begun, you'll get a nasty error message. When output buffering's turned on, the only output types that can escape the buffer are HTTP headers. If you use ob_start at the very beginning of your application's execution, you can send headers at whichever point you like, without encountering the usual errors. You can then write out the buffered page content all at once, when you're sure that no more HTTP headers are required. </p> <p><em>Use Output Buffering Responsibly
While output buffering can helpfully solve all our header problems, it should not be used solely for that reason. By ensuring that all output is generated after all the headers are sent, you'll save the time and resource overheads involved in using output buffers.</em> </p> <h5>How do I cache just the parts of a page that change infrequently?</h5> <p>Caching an entire page is a simplistic approach to output buffering. While it's easy to implement, that approach negates the real benefits presented by PHP's output control functions to improve your site's performance in a manner that's relevant to the varying lifetimes of your content. </p> <p>No doubt, some parts of the page that you send to visitors will change very rarely, such as the page's header, menus, and footer. But other parts--for example, the list of comments on your blog posts--may change quite often. Fortunately, PHP allows you to cache sections of the page separately. </p> <p><strong><em>Solution</em></strong></p> <p>Output buffering can be used to cache sections of a page in separate files. The page can then be rebuilt for output from these files. </p> <p>This technique eliminates the need to repeat database queries, while loops, and so on. You might consider assigning each block of the page an expiry date after which the cache file is recreated; alternatively, you may build into your application a mechanism that deletes the cache file every time the content it stores is changed. </p> <p>Let's work through an example that demonstrates the principle. Firstly, we'll create two helper functions, <code>writeCache</code> and <code>readCache</code>. Here's the <code>writeCache</code> function: </p> <p><code>smartcache.php (excerpt)

<?php
function writeCache($content, $filename)
{
$fp = fopen('./cache/' . $filename, 'w');
fwrite($fp, $content);
fclose($fp);
}</code></p> <p>The <code>writeCache</code> function is quite simple; it just writes the content of the first argument to a file with the name specified in the second argument, and saves that file to a location in the cache directory. We'll use this function to write our HTML to the cache files. </p> <p>The <code>readCache</code> function will return the contents of the cache file specified in the first argument if it has not expired--that is, the file's last modified time is not older than the current time minus the number of seconds specified in the second argument. If it has expired or the file does not exist, the function returns false: </p> <p><code>smartcache.php (excerpt)

function readCache($filename, $expiry)
{
if (file_exists('./cache/' . $filename))
{
if ((time() - $expiry) > filemtime('./cache/' . $filename))
{
return false;
}
$cache = file('./cache/' . $filename);
return implode('', $cache);
}
return false;
}</code></p> <p>For the purposes of demonstrating this concept, I've used a procedural approach. However, I wouldn't recommend doing this in practice, as it will result in very messy code and is likely to cause issues with file locking. For example, what happens when someone accesses the cache at the exact moment it's being updated? Better solutions will be explained later on in the chapter. </p> <p>Let's continue this example. After the output buffer is started, processing begins. First, the script calls <code>readCache</code> to see whether the file <code>header.cache</code> exists; this contains the top of the page--the HTML <code class="ref-term"><a title=""> tag in the SitePoint HTML Reference." href="http://reference.sitepoint.com/html/head"><head></a></code> tag and the start <code class="ref-term"><a title=""> tag in the SitePoint HTML Reference." href="http://reference.sitepoint.com/html/body"><body></a></code> tag. We've used PHP's date function to display the time at which the page was actually rendered, so you'll be able to see the different cache files at work when the page is displayed: </p> <p><code>smartcache.php (excerpt)

ob_start();
if (!$header = readCache('header.cache', 604800))
{
?>
<!DOCTYPE html public "-//<a href="http://www.sitepoint.com/glossary.php?q=W#term_49" class="glossary" title="The World Wide Web Consortium (W3C) - A consortium of industry leaders for drafting Web standards.">W3C</a>//DTD <a href="http://www.sitepoint.com/glossary.php?q=X#term_63" class="glossary" title="XHTML is a reformulation of HTML 4 as an XML 1.0 application.">XHTML</a> 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Chunked Cached Page</title>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1"/>
</head>
<body>
<p>The header time is now: <?php echo date('H:i:s'); ?></p>
<?php
$header = ob_get_contents();
ob_clean();
writeCache($header,'header.cache');
}</code></p> <p>Note what happens when a cache file isn't found: the header content is output and assigned to a variable, <code>$header</code>, with <code>ob_get_contents</code>, after which the <code>ob_clean</code> function is called to empty the buffer. This allows us to capture the output in "chunks" and assign them to individual cache files with the <code>writeCache</code> function. The header of the page is now stored as a file, which can be reused without our needing to rerender the page. Look back to the start of the if condition for a moment. When we called <code>readCache</code>, we gave it an expiry time of 604800 seconds (one week); <code>readCache</code> uses the file modification time of the cache file to determine whether the cache is still valid. </p> <p>For the body of the page, we'll use the same process as before. However, this time, when we call <code>readCache</code>, we'll use an expiry time of five seconds; the cache file will be updated whenever it's more than five seconds old: </p> <p><code>smartcache.php (excerpt)

if (!$body = readCache('body.cache', 5))
{
echo 'The body time is now: ' . date('H:i:s') . '<br />';
$body = ob_get_contents();
ob_clean();
writeCache($body, 'body.cache');
}</code></p> <p>The page footer is effectively the same as the header. After the footer, the output buffering is stopped and the contents of the three variables that hold the page data are displayed: </p> <p><code>smartcache.php (excerpt)

if (!$footer = readCache('footer.cache', 604800)) {
?>
<p>The footer time is now: <?php echo date('H:i:s'); ?></p>
</body>
</html>
<?php
$footer = ob_get_contents();
ob_clean();
writeCache($footer, 'footer.cache');
}
ob_end_clean();

echo $header . $body . $footer;
?></code></p> <p>The end result looks like this: </p> <p><code>The header time is now: 17:10:42
The body time is now: 18:07:40
The footer time is now: 17:10:42</code></p> <p>The header and footer are updated on a weekly basis, while the body is updated whenever it is more than five seconds old. If you keep refreshing the page, you'll see the body time updating. </p> <p><strong><em>Discussion</em></strong></p> <p>Note that if you have a page that builds content dynamically, based on a number of variables, you'll need to make adjustments to the way you handle your cache files. For example, you might have an online shopping catalog whose listing pages are defined by a URL such as: </p> <p><code>http://example.com/catalogue/view.php?category=1&page=2</code></p> <p>This URL should show page two of all items in category one; let's say this is the category for socks. But if we were to use the caching code above, the results of the first page of the first category we looked at would be cached, and shown for any request for any other page or category, until the cache expiry time elapsed. This would certainly confuse the next visitor who wanted to browse the category for shoes--that person would see the cached content for socks! </p> <p>To avoid this issue, you'll need to incorporate the category ID and page number in to the cache file name like so: </p> <p><code> $cache_filename = 'catalogue_' . $category_id . '_' .
$page . '.cache';
if (!$catalogue = readCache($cache_filename, 604800))
{
...display the category HTML...
}</code></p> <p>This way, the correct cached content can be retrieved for every request. </p> <p><em>Nesting Buffers</em>
<em>You can nest one buffer within another practically ad infinitum simply by calling ob_startmore than once. This can be useful if you have multiple operations that use the output buffer, such as one that catches the PHP error messages, and another that deals with caching. Care needs to be taken to make sure that <code>ob_end_flush</code> or <code>ob_end_clean</code> is called every time <code>ob_start</code> is used.
</em></p><h5>How do I use <code><a href="http://www.sitepoint.com/glossary.php?q=P#term_50" class="glossary" title="The PHP Extension and Application Repository - a framework and distribution system for reusable PHP components">PEAR</a>::Cache_Lite</code> for <a href="http://www.sitepoint.com/glossary.php?q=S#term_14" class="glossary" title="Server-side code is executed on the web server before being sent to the end user. ">server-side</a> <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">caching</a>?</h5> <p>The previous solution explored the ideas behind output buffering using the <a href="http://www.sitepoint.com/glossary.php?q=P#term_1" class="glossary" title="PHP, or Hypertext Preprocessor, is an open source, server-side programming language.">PHP</a> <code>ob_*</code> functions. Although we mentioned at the time, that approach probably isn't the best way to meet to dual goals of keeping your code maintainable and having a reliable caching mechanism. It's time to see how we can put a caching system into action in a manner that will be reliable and easy to maintain.</p> <p><strong><em>Solution</em></strong></p> <p>In the interests of keeping your code maintainable and having a reliable caching mechanism, it's a good idea to delegate the responsibility of caching logic to classes you trust. In this case, we'll use a little help from <code>PEAR::Cache_Lite</code> (version 1.7.2 is used in <a class="sublink" href="http://pear.php.net/package/Cache_Lite/">the examples here</a>). <code>Cache_Lite</code> provides a solid yet easy-to-use library for caching, and handles issues such as: file locking; creating, checking for, and deleting cache files; controlling the output buffer; and directly caching the results from function and class method calls. More to the point, <code>Cache_Lite</code> should be relatively easy to apply to an existing application, requiring only minor code modifications. </p> <p><code>Cache_Lite</code> has four main classes. First is the base class, <code>Cache_Lite</code>, which deals purely with creating and fetching cache files, but makes no use of output buffering. This class can be used alone for caching operations in which you have no need for output buffering, such as storing the contents of a template you've parsed with PHP. </p> <p>The examples here will not use <code>Cache_Lite</code> directly, but will instead focus on the three subclasses. <code>Cache_Lite_Function</code> can be used to call a function or class method and cache the result, which might prove useful for storing a <a href="http://www.sitepoint.com/glossary.php?q=M#term_12" class="glossary" title="MySQL is a free, fast, open source database.">MySQL</a> query result set, for example. The <code>Cache_Lite_Output</code> class uses PHP's output control functions to catch the output generated by your script and store it in cache files; it allows you to perform tasks such as those we completed in "How do I cache just the parts of a page that change infrequently?". The <code>Cache_Lite_File</code> class bases cache expiry on the timestamp of a master file, with any cache file being deemed to have expired if it is older than the timestamp. </p> <p>Let's work through an example that shows how you might use <code>Cache_Lite</code> to create a simple caching solution. When we're instantiating any child classes of <code>Cache_Lite</code>, we must first provide an <a href="http://www.sitepoint.com/glossary.php?q=%23#term_72" class="glossary" title="An array is a single variable with compartments, each of which can hold a value. ">array</a> of options that determine the behavior of <code>Cache_Lite</code> itself. We'll look at these options in detail in a moment. Note that the <code>cacheDir</code> directory we specify must be one to which the script has read and write access: </p> <p><code>cachelite.php (excerpt)

<?php
require_once 'Cache/Lite/Output.php';
$options = array(
'cacheDir' => './cache/',
'writeControl' => 'true',
'readControl' => 'true',
'fileNameProtection' => false,
'readControlType' => 'md5'
);
$cache = new Cache_Lite_Output($options);</code></p> <p>For each chunk of content that we want to cache, we need to set a lifetime (in seconds) for which the cache should live before it's refreshed. Next, we use the start method, available only in the <code>Cache_Lite_Output</code> class, to turn on output buffering. The two arguments passed to the start method are an identifying value for this particular cache file, and a cache group. The group is an identifier that allows a collection of cache files to be acted upon; it's possible to delete all cache files in a given group, for example (more on this in a moment). The start method will check to see if a valid cache file is available and, if so, it will begin outputting the cache contents. If a cache file is not available, start will return false and begin caching the following output. </p> <p>Once the output for this chunk has finished, we use the <code>end</code> method to stop buffering and store the content as a file: </p> <p><code>cachelite.php (excerpt)

$cache->setLifeTime(604800);
if (!$cache->start('header', 'Static')) {
?>
<!DOCTYPE <a href="http://www.sitepoint.com/glossary.php?q=H#term_75" class="glossary" title="HTML stands for HyperText Markup Language.">html</a> public "-//<a href="http://www.sitepoint.com/glossary.php?q=W#term_49" class="glossary" title="The World Wide Web Consortium (W3C) - A consortium of industry leaders for drafting Web standards.">W3C</a>//DTD <a href="http://www.sitepoint.com/glossary.php?q=X#term_63" class="glossary" title="XHTML is a reformulation of HTML 4 as an XML 1.0 application.">XHTML</a> 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>PEAR::Cache_Lite example</title>
<meta http-equiv="Content-Type"
content="text/html; charset=iso-8859-1"/>
</head>
<body>
<h2>PEAR::Cache_Lite example</h2>
<p>The header time is now: <?php echo date('H:i:s'); ?></p>
<?php
$cache->end();
}</code></p> <p>To cache the body and footer, we follow the same procedure we used for the header. Note that, again, we specify a five-second lifetime when caching the body:</p> <p><code>cachelite.php (excerpt)

$cache->setLifeTime(5);
if (!$cache->start('body', 'Dynamic')) {
echo 'The body time is now: ' . date('H:i:s') . '<br />';
$cache->end();
}

$cache->setLifeTime(604800);
if (!$cache->start('footer', 'Static')) {
?>
<p>The footer time is now: <?php echo date('H:i:s'); ?></p>
</body>
</html>
<?php
$cache->end();
}
?></code></p> <p>On viewing the page, <code>Cache_Lite</code> creates cache files in the cache directory. Because we've set the <code>fileNameProtection</code> option to false, <code>Cache_Lite</code> creates the files with these names: </p> <p><code>- ./cache/cache_Static_header
- ./cache/cache_Dynamic_body
- ./cache/cache_Static_footer</code></p> <p>You can read about the <code>fileNameProtection</code> option--and many more--in "What configuration options does <code>Cache_Lite</code> support?". When the same page is requested later, the code above will use the cached file if it is valid and has not expired. </p> <p><em>Protect your Cache Files</em>
<em>Make sure that the directory in which you place the cache files is not publicly available, or you may be offering your site's visitors access to more than you realize.</em></p> <h5>What configuration options does <code>Cache_Lite</code> support?</h5> <p>When instantiating <code>Cache_Lite</code> (or any of its subclasses, such as <code>Cache_Lite_Output</code>), you can use any of a number of approaches to controlling its behavior. These options should be placed in an array and passed to the constructor as shown below (and in the previous section): </p> <p><code>$options = array(
'cacheDir' => './cache/',
'writeControl' => true,
'readControl' => true,
'fileNameProtection' => false,
'readControlType' => 'md5'
);
$cache = new Cache_Lite_Output($options);</code></p> <p><strong><em>Solution</em></strong></p> <p>The options available in the current version of <code>Cache_Lite</code> (1.7.2) are: </p> <p><code>cacheDir</code>
This is the directory in which the cache files will be placed. It defaults to <code>/tmp/</code>. </p> <p><code>caching</code>
This option switches on and off the caching behavior of <code>Cache_Lite</code>. If you have numerous <code>Cache_Lite</code> calls in your code and want to disable the cache for debugging, for example, this option will be important. The default value is <code>true</code> (caching enabled). </p> <p><code>lifeTime</code>
This option represents the default lifetime (in seconds) of cache files. It can be changed using the <code>setLifeTime</code> method. The default value is <code>3600</code> (one hour), and if it's set to null, the cache files will never expire. </p> <p><code>fileNameProtection</code>
With this option activated, <code>Cache_Lite</code> uses an MD5 encryption hash to generate the filename for the cache file. This option protects you from error when you try to use IDs or group names containing characters that aren't valid for filenames; <code>fileNameProtection</code> must be turned on when you use <code>Cache_Lite_Function</code>. The default is <code>true</code> (enabled). </p> <p><code>fileLocking</code>
This option is used to switch the file locking mechanisms on and off. The default is <code>true</code> (enabled). </p> <p><code>writeControl</code>
This option checks that a cache file has been written correctly immediately after it has been created, and throws a PEAR::Error if it finds a problem. Obviously, this facility would allow your code to attempt to rewrite a cache file that was created incorrectly, but it comes at a cost in terms of performance. The default value is <code>true</code> (enabled). </p> <p><code>readControl</code>
This option checks any cache files that are being read to ensure they're not corrupt. Cache_Lite is able to place inside the file a value, such as the string length of the file, which can be used to confirm that the cache file isn't corrupt. There are three alternative mechanisms for checking that a file is valid, and they're specified using the <code>readControlType</code> option. These mechanisms come at the cost of performance, but should help to guarantee that your visitors aren't seeing scrambled pages. The default value is <code>true</code> (enabled). </p> <p><code>readControlType</code>
This option lets you specify the type of read control mechanism you want to use. The available mechanisms are a cyclic redundancy check (<code>crc32</code>, the default value) using PHP's <code>crc32</code> function, an MD5 hash using PHP's <code>md5</code> function (<code>md5</code>), or a simple and fast string length check (<code>strlen</code>). Note that this mechanism is not intended to provide security from people tampering with your cache files; it's just a way to spot corrupt files. </p> <p><code>pearErrorMode</code>
This option tells Cache_Lite how it should return PEAR errors to the calling script. The default is <code>CACHE_LITE_ERROR_RETURN</code>, which means Cache_Lite will return a PEAR::Error object. </p> <p><code>memoryCaching</code>
With memory caching enabled, every time a file is written to the cache, it is stored in an array in <code>Cache_Lite</code>. The <code>saveMemoryCachingState</code> and <code>getMemoryCachingState</code> methods can be used to store and access the memory cache data between requests. The advantage of this facility is that the complete set of cache files can be stored in a single file, reducing the number of disk read/write operations by reconstructing the cache files straight into an array to which your code has access. The <code>memoryCaching</code> option may be worth further investigation if you run a large site. The default value is <code>false</code> (disabled). </p> <p><code>onlyMemoryCaching</code>
If this option is enabled, only the memory caching mechanism will be used. The default value is <code>false</code> (disabled). </p> <p><code>memoryCachingLimit</code>
This option places a limit on the number of cache files that will be stored in the memory caching array. The more cache files you have, the more memory will be used up by memory caching, so it may be a good idea to enforce a limit that prevents your server from having to work too hard. Of course, this option places no restriction on the size of each cache file, so just one or two massive files may cause a problem. The default value is <code>1000</code>.</p> <p><code>automaticSerialization</code>
If enabled, this option will automatically serialize all data types. While this approach will slow down the caching system, it is useful for caching nonscalar data types such as objects and <a href="http://www.sitepoint.com/glossary.php?q=%23#term_72" class="glossary" title="An array is a single variable with compartments, each of which can hold a value. ">arrays</a>. For higher performance, you might consider serializing nonscalar data types yourself. The default value is <code>false</code> (disabled). </p> <p><code>automaticCleaningFactor</code>
This option will automatically clean old cache entries--on average, one in x cache writes, where x is the value set for this option. Therefore, setting this value to <code>0</code> will indicate no automatic cleaning, and a value of 1will cause cache clearing on every cache write. A value of <code>20</code> to <code>200</code> is the recommended starting point if you wish to enable this facility; it causes cache cleaning to happen, on average, 0.5% to 5% of the time. The default value is <code>0</code> (disabled). </p> <p><code>hashedDirectoryLevel</code>
When set to a nonzero value, this option will enable a hashed directory structure. A hashed directory structure will improve the performance of sites that have thousands of cache files. If you choose to use hashed directories, start by setting this value to <code>1</code>, and increasing it as you test for performance improvements. The default value is <code>0</code> (disabled). </p> <p><code>errorHandlingAPIBreak</code>
This option was added to enable backwards compatibility with code that uses the old API. When the old API was run in <code>CACHE_LITE_ERROR_RETURN</code> mode (see the <code>pearErrorMode</code> option earlier in this list), some functions would return a Boolean value to indicate success, rather than returning a <code>PEAR_Error</code> object. By setting this value to true, the <code>PEAR_Error</code> object will be returned instead. The default value is <code>false</code> (disable).
</p><h5>How do I purge the <code>Cache_Lite</code> <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">cache</a>?</h5> <p>The built-in lifetime mechanism for <code>Cache_Lite</code> cache files provides a good foundation for keeping your cache files up to date, but there will be some circumstances in which you need the files to be updated immediately. </p> <p><strong><em>Solution</em></strong></p> <p>In cases in which you need immediate updates, the methods remove and clean come in handy. The remove method is designed to delete a specific cache file; it takes as arguments the cache ID and group name of the file. To delete the page body cache file we created in "How do I use <a href="http://www.sitepoint.com/glossary.php?q=P#term_50" class="glossary" title="The PHP Extension and Application Repository - a framework and distribution system for reusable PHP components">PEAR</a>::Cache_Lite for <a href="http://www.sitepoint.com/glossary.php?q=S#term_14" class="glossary" title="Server-side code is executed on the web server before being sent to the end user. ">server-side</a> <a href="http://www.sitepoint.com/glossary.php?q=C#term_21" class="glossary" title="Cache, pronounced "cash", refes to a stored copy of (or pointers to) previously accessed data. ">caching</a>?", we'd use this code: </p> <p><code>$cache->remove('body', 'Dynamic');</code></p> <p>If we use the clean method, we can delete all the files in our cache directory simply by calling the method with no arguments; alternatively, we can specify a group of cache files to delete. If we wanted to delete both the header and footer cache files we created in "How do I use PEAR::Cache_Lite for server-side caching?", we could do so like this: </p> <p><code>$cache->clean('Static');</code></p> <p><strong><em>Discussion</em></strong></p> <p>The remove and clean methods should obviously be called in response to events that arise within an application. For example, if you have a discussion forum application, you probably want to remove the relevant cache files when a visitor posts a new message. </p> <p>Although it may seem like this solution entails a lot of code modifications, with some care it can be applied to your application in a global manner. If you have a central script that's included in every page, your script can simply watch for incoming events--for example, a variable like <code>$_GET['newPost']</code>--and respond by deleting the required cache files. This keeps the cache file removal mechanism central and easier to maintain. You might also consider using the <code><a href="http://www.sitepoint.com/glossary.php?q=P#term_1" class="glossary" title="PHP, or Hypertext Preprocessor, is an open source, server-side programming language.">php</a>.ini</code> setting <code>auto_prepend_file</code> to include this code in every PHP script. </p> <h5>How do I cache function calls?</h5> <p>Many web sites provide access to their data via web services such as SOAP and <a href="http://www.sitepoint.com/glossary.php?q=X#term_3" class="glossary" title="eXtensible Markup Language, or XML, is a text markup language designed for the easy sharing of data.">XML</a>-RPC. (You can read all about web services in Chapter 12.) As web services are accessed over a network, it's often a very good idea to cache results so that they can be fetched locally, rather than repeating the same slow request to the server multiple times. A simple approach might be to use PHP sessions, but as that solution operates on a per-visitor basis, the opening requests for each visitor will still be slow. </p> <p><strong><em>Solution</em></strong></p> <p>Let's assume you wish to create a web page that lists all the SitePoint books available on Amazon. The actual list is not likely to change from moment to moment, so why would we make the request to the Amazon web service every time the web page is displayed? We won't! Instead, we can take advantage of <code>Cache_Lite</code> by caching the results of the XML-RPC request. </p> <p><em>Requires PEAR::SOAP Version 0.11.0 </em>
<em>The following solution uses the PEAR::SOAP library version 0.11.0 to access the Amazon web service. You can find this package on the <a class="sublink" href="http://pear.php.net/package/soap/">PEAR web site</a>.</em> </p> <p>Here's some hypothetical code that fetches the data from the remote Amazon server: </p> <p><code>$results = $amazonClient->ManufacturerSearchRequest($params);</code></p> <p>Using <code>Cache_Lite_Function</code>, we can cache the results so the data returned from the service can be reused; this will avoid unnecessary network calls and significantly improve performance. </p> <p>The following example code focuses on the caching aspect to prevent us from getting bogged down in the details of using the Amazon web service. You can see the complete script if you download this book's code archive from the SitePoint web site. </p> <p>The <code>Cache_Lite_Function</code> requires the inclusion of the following file: </p> <p><code>cachefunction.php (excerpt)

require_once 'Cache/Lite/Function.php';</code></p> <p>We instantiate the <code>Cache_Lite_Function</code> class with some options: </p> <p><code>cachefunction.php (excerpt)

$options = <a href="http://www.sitepoint.com/glossary.php?q=%23#term_72" class="glossary" title="An array is a single variable with compartments, each of which can hold a value. ">array</a>(
'cacheDir' => './cache/',
'fileNameProtection' => true,
'writeControl' => true,
'readControl' => true,
'readControlType' => 'strlen',
'defaultGroup' => 'SOAP'
);
$cache = new Cache_Lite_Function($options);</code></p> <p>It's important that the <code>fileNameProtection</code> option is set to <code>true </code>(this is in fact the default value, but in this case I've set it manually to emphasize the point). If it were set to <code>false</code>, the filename would be invalid, so the data will not be cached. </p> <p>Here's how we make the calls to our SOAP client class: </p> <p><code>cachefunction.php (excerpt)

$results = $cache->call('amazonClient->ManufacturerSearchRequest',
$params);</code></p> <p>If the request is being made for the first time, <code>Cache_Lite_Function</code> will store the results as a serialized array or object in a cache file (not that you need to worry about this), and this file will be used for future requests until it expires. The <code>setLifeTime</code> method can again be used to specify how long the cache files should survive before they're refreshed; currently, the default value of 3600 seconds (one hour) is being used. You can then use the <code>$results</code> variable exactly as if you were calling the web service method directly. The output of our example script can be seen in Figure 11.1. </p> <div style="overflow: hidden;"><a href="http://i2.sitepoint.com/graphics/amazonbooks.thumb.png" class="beatbox"><em>SitePoint books at Amazon (click to view image)</em><img style="float: left;" src="http://i2.sitepoint.com/graphics/amazonbooks.thumb.png" alt="" height="344" width="400" /></a></div> <p> </p><h5>Summary</h5> <p>Caching is an important and often overlooked aspect of web site development. Many factors that affect the performance of today's web sites weren't a problem for their predecessors--from complex, dynamic page generation, to a reliance on third-party data over the network. In this chapter, we've examined <a href="http://www.sitepoint.com/glossary.php?q=H#term_75" class="glossary" title="HTML stands for HyperText Markup Language.">HTML</a> meta tags, HTTP headers, PHP output buffering and <code>PEAR::Cache_Lite</code>, and we've seen how you can use them to control the caching of your web site content and improve the site's reliability and performance. </p> <p>Implementing a caching system for your site might be simple, but ultimately, it depends on your requirements. If you have a busy and predominantly static web site--such as a blog--that's managed through a content management system, it will likely require little alteration, yet may benefit from huge performance improvements resulting from a small investment of your time. Setting up caching for a more complex site that generates content on a per-user basis, such as a portal or shopping cart system, will prove a little more tricky and time consuming, but the benefits are still clear.</p> <p>Regardless, I hope the information in this chapter has given you a good grasp of the options available, and will help you determine which techniques are most suitable for your application. Don't forget to <a class="sublink" href="http://www.sitepoint.com/launch/108ef2/2/120">download this chapter, plus two others</a> -- PDO and Databases, and Access Control -- to enjoy offline. For information on the contents of the book's other chapters, check out the <a class="sublink" href="http://www.sitepoint.com/books/phpant2/toc.php">full Table of Contents</a>.</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p><p>
</p>

courtsy: Ben Balbo
sitepoint.com

Wednesday

FCKEDITOR table cell background Image implementation

Add

Add
<tr>
<td nowrap ><span >Backgrund Image</span>:</td>
<td><input id="txtUrl" style="WIDTH: 100%" type="text" onblur="UpdatePreview();"></td>
<td id="tdBrowse"><input id="btnBrowse" onclick="BrowseServer();" type="button" value="Browse Server" fckLang="DlgBtnBrowseServer"></td>
</tr>
code in fck_tablecell.html after
<tr>
<td nowrap><span fckLang="DlgCellBorderColor">Border Color</span>:</td>
<td> <input id="txtBorderColor" type="text" size="8" name="txtCellPadding"></td>
<td> <input type="button" fckLang="DlgCellBtnSelect" value="Select..." onclick="SelectColor( 'Border' )"></td>
</tr>

and add a js file for browsing and upload the image and the js file same as the
editor/dialog/fck_image/fck_image.js file name as editor/dialog/fck_image/fck_image_table.js and also i change some thing in this file ....

and the code is


/*******************/
/*
* FCKeditor - The text editor for internet
* Copyright (C) 2003-2005 Frederico Caldeira Knabben
*
* Licensed under the terms of the GNU Lesser General Public License:
* http://www.opensource.org/licenses/lgpl-license.php
*
* For further information visit:
* http://www.fckeditor.net/
*
* File Name: fck_image.js
* Scripts related to the Image dialog window (see fck_image.html).
*
* File Authors:
* Frederico Caldeira Knabben (fredck@fckeditor.net)
*/

var oEditor = window.parent.InnerDialogLoaded() ;
var FCK = oEditor.FCK ;
var FCKLang = oEditor.FCKLang ;
var FCKConfig = oEditor.FCKConfig ;

var bImageButton = ( document.location.search.length > 0 && document.location.search.substr(1) == 'ImageButton' ) ;


// Get the selected image (if available).
var oImage = FCK.Selection.GetSelectedElement() ;

if ( oImage && oImage.tagName != 'IMG' && !( oImage.tagName == 'INPUT' && oImage.type == 'image' ) )
oImage = null ;

// Get the active link.
var oLink = FCK.Selection.MoveToAncestorNode( 'A' ) ;

var oImageOriginal ;


window.onload = function()
{
// Translate the dialog box texts.
oEditor.FCKLanguageManager.TranslatePage(document) ;

GetE('btnLockSizes').title = FCKLang.DlgImgLockRatio ;
GetE('btnResetSize').title = FCKLang.DlgBtnResetSize ;

// Load the selected element information (if any).
LoadSelection() ;

// Show/Hide the "Browse Server" button.
GetE('tdBrowse').style.display = FCKConfig.ImageBrowser ? '' : 'none' ;
GetE('divLnkBrowseServer').style.display = FCKConfig.LinkBrowser ? '' : 'none' ;

UpdateOriginal() ;

// Set the actual uploader URL.
if ( FCKConfig.ImageUpload )
GetE('frmUpload').action = FCKConfig.ImageUploadURL ;

window.parent.SetAutoSize( true ) ;

// Activate the "OK" button.
window.parent.SetOkButton( true ) ;
}

function LoadSelection()
{
if ( ! oImage ) return ;

var sUrl = GetAttribute( oImage, 'src', '' ) ;

// TODO: Wait stable version and remove the following commented lines.
// if ( sUrl.startsWith( FCK.BaseUrl ) )
// sUrl = sUrl.remove( 0, FCK.BaseUrl.length ) ;

GetE('txtUrl').value = sUrl ;
GetE('txtAlt').value = GetAttribute( oImage, 'alt', '' ) ;
GetE('txtVSpace').value = GetAttribute( oImage, 'vspace', '' ) ;
GetE('txtHSpace').value = GetAttribute( oImage, 'hspace', '' ) ;
GetE('txtBorder').value = GetAttribute( oImage, 'border', '' ) ;
GetE('cmbAlign').value = GetAttribute( oImage, 'align', '' ) ;

if ( oImage.style.pixelWidth > 0 )
GetE('txtWidth').value = oImage.style.pixelWidth ;
else
GetE('txtWidth').value = GetAttribute( oImage, "width", '' ) ;

if ( oImage.style.pixelHeight > 0 )
GetE('txtHeight').value = oImage.style.pixelHeight ;
else
GetE('txtHeight').value = GetAttribute( oImage, "height", '' ) ;

// Get Advances Attributes
GetE('txtAttId').value = oImage.id ;
GetE('cmbAttLangDir').value = oImage.dir ;
GetE('txtAttLangCode').value = oImage.lang ;
GetE('txtAttTitle').value = oImage.title ;
GetE('txtAttClasses').value = oImage.getAttribute('class',2) || '' ;
GetE('txtLongDesc').value = oImage.longDesc ;

if ( oEditor.FCKBrowserInfo.IsIE )
GetE('txtAttStyle').value = oImage.style.cssText ;
else
GetE('txtAttStyle').value = oImage.getAttribute('style',2) ;

if ( oLink )
{
GetE('txtLnkUrl').value = oLink.getAttribute('href',2) ;
GetE('cmbLnkTarget').value = oLink.target ;
}

//UpdatePreview() ;
}


function UpdateImage( e, skipId )
{
e.src = GetE('txtUrl').value ;
SetAttribute( e, "alt" , GetE('txtAlt').value ) ;
SetAttribute( e, "width" , GetE('txtWidth').value ) ;
SetAttribute( e, "height", GetE('txtHeight').value ) ;
SetAttribute( e, "vspace", GetE('txtVSpace').value ) ;
SetAttribute( e, "hspace", GetE('txtHSpace').value ) ;
SetAttribute( e, "border", GetE('txtBorder').value ) ;
SetAttribute( e, "align" , GetE('cmbAlign').value ) ;

// Advances Attributes

if ( ! skipId )
SetAttribute( e, 'id', GetE('txtAttId').value ) ;

SetAttribute( e, 'dir' , GetE('cmbAttLangDir').value ) ;
SetAttribute( e, 'lang' , GetE('txtAttLangCode').value ) ;
SetAttribute( e, 'title' , GetE('txtAttTitle').value ) ;
SetAttribute( e, 'class' , GetE('txtAttClasses').value ) ;
SetAttribute( e, 'longDesc' , GetE('txtLongDesc').value ) ;

if ( oEditor.FCKBrowserInfo.IsIE )
e.style.cssText = GetE('txtAttStyle').value ;
else
SetAttribute( e, 'style', GetE('txtAttStyle').value ) ;
}

function BrowseServer()
{
OpenServerBrowser(
'Image',
FCKConfig.ImageBrowserURL,
FCKConfig.ImageBrowserWindowWidth,
FCKConfig.ImageBrowserWindowHeight ) ;
}

function OpenServerBrowser( type, url, width, height )
{
sActualBrowser = type ;

var iLeft = (screen.width - width) / 2 ;
var iTop = (screen.height - height) / 2 ;

var sOptions = "toolbar=no,status=no,resizable=yes,dependent=yes" ;
sOptions += ",width=" + width ;
sOptions += ",height=" + height ;
sOptions += ",left=" + iLeft ;
sOptions += ",top=" + iTop ;

var oWindow = window.open( url, "FCKBrowseWindow", sOptions ) ;
}

var sActualBrowser ;

function SetUrl( url, width, height, alt )
{

if ( sActualBrowser == 'Link' )
{
GetE('txtLnkUrl').value = url ;
// UpdatePreview() ;
}
else
{
GetE('txtUrl').value = url ;
GetE('txtWidth').value = width ? width : '' ;
GetE('txtHeight').value = height ? height : '' ;

if ( alt )
GetE('txtAlt').value = alt;

//UpdatePreview() ;
//UpdateOriginal( true ) ;
}

window.parent.SetSelectedTab( 'Info' ) ;
}


var oUploadAllowedExtRegex = new RegExp( FCKConfig.ImageUploadAllowedExtensions, 'i' ) ;
var oUploadDeniedExtRegex = new RegExp( FCKConfig.ImageUploadDeniedExtensions, 'i' ) ;

courtsy : Anindita Nandi

Sunday

Using PHP 4's DOM XML functions to create XML files from SQL data

Intended Audience

This tutorial is intended for developers who wish to extract data from a database and insert it into XML files so that it can be processed in some way, usually by transforming it into HTML using an XSL file.

This method completely splits the presentation layer (i.e. the generation of HTML documents) from the business layer (the application of business rules using a language such as PHP) so that any one of these layers can be modified without affecting the other.

In the samples below the code is generic in that no column names are ever hard coded. Data is retrieved from the database as an associative array (a series of 'name=value' pairs), and every element of the array is extracted and transferred to the XML file. The contents of the associative array are therefore governed entirely by the SQL 'select' statement.

In the samples below I will show how to deal with data from a single table, and then data from two tables with a one-to-many relationship. Finally I will show how to insert additional data in the form of XML attributes.

Prerequisites

The sample code requires that you have the DOM XML extension available in your PHP installation. It is also assumed that you know what an XML file is and what it can be used for.

Please note that this extension has been removed from PHP 5 and moved to the PECL repository. For details on how to use the DOM extension instead please refer to Using PHP 5's DOM functions to create XML files from SQL data

Multiple occurrences of a single table

The following code will take the contents of $dbresult (any number of rows, each of which contains a series of name=value pairs) and write it to a variable as an XML string. This can subsequently be written to a disk file or transformed into an HTML document using the Sablotron XSLT processor which is built into PHP. For details on how to use this extension please refer to Using PHP 4's Sablotron extension to perform XSL Transformations

This first piece of code simply connects to the database and performs a query:

$dbconnect = mysql_connect('localhost', 'user', 'pass')) {

echo "Connection failed to the host 'localhost'.";
exit;
} // if
if (!mysql_select_db('test')) {
echo "Cannot connect to database 'test'";
exit;
} // if

$table_id = 'some_table';
$query = "SELECT * FROM $table_id";
$dbresult = mysql_query($query, $dbconnect);

Now that we have our data we transfer it to an XML document. We start by creating a new DOM document. The following command will set the XML version number to '1.0' and return the object reference for the new document:

// create a new XML document

$doc = domxml_new_doc('1.0');

The first element we create in the XML document is known as the root element. Each XML document must have 1, and only 1, root element. In this example I have called it 'root', but you can use whatever name you like (such as the name of the PHP script which is executing). Note that you have to create the element and insert it into the document with two functions.

// create root node

$root = $doc->create_element('root');
$root = $doc->append_child($root);

Now we are ready to start adding the data we have retrieved from the database. Note that I am returning each row as an associative array which provides me with a list of 'name=value' pairs. This makes all subsequent processing far easier.

// process one row at a time

while($row = mysql_fetch_assoc($dbresult)) {

The first task I must perform for each row is to add a new element to the XML document. Here I create a new element using the table name, then I insert it into the document as a child of the root element.

  // add node for each row

$occ = $doc->create_element($table_id);
$occ = $root->append_child($occ);

Now I loop through each column in the current row, and insert the fieldname and corresponding value. You will see how having an associative array makes life easy. I need not concern myself with how many columns have been returned from the database query, nor with the order in which they are presented, as every column in the array gets written out.

  // add a child node for each field

foreach ($row as $fieldname => $fieldvalue) {

Note that here I create a new element for the field and then insert it as a child to the current database row, as identified in $occ.

    $child = $doc->create_element($fieldname);

$child = $occ->append_child($child);

Now I must add the field value as a text node, then insert it as a child element to the current field node, as identified in $child.

    $value = $doc->create_text_node($fieldvalue);

$value = $child->append_child($value);

These loops do not terminate until they have processed every column of every row which has been retrieved from the database.

  } // foreach

} // while

This next function returns the completed XML document as a string.

// get completed xml document

$xml_string = $doc->dump_mem(true);

Here I am simply outputting the results to the client browser, but I could just as easily perform some additional processing such as passing it to an XSLT processor for transformation into another document, such as an HTML document, using the contents of a separate XSL file.

echo $xml_string;

?>

The contents of the XML file produced with this code will look something like the following, starting with the XML declaration, with the version number, and immediately followed by the root node.

"1.0"?>


For each database row there will be an element, as a child to the root node, which contains the table name Each row element will have a separate child element for each column within that row. Note that each column element contains a text node for its value, while the row element does not have a text node. This grouping will be repeated for each column within each row. After the last column, notice the closing tag for the current row element, after which the row/column group must be repeated for each additional row that was extracted from the database and transferred to the XML file.

  

value1
value2
............
valueX


............

The last line in an XML file is there to close the root node.


Note that each element within the XML document has an opening and a closing tag in the format .... This identifies the node name within the document tree. Everything between these two tags is a child node to that element. This child node may be a text node or another element.

You may sometimes see an element in an XML document shown as . This signifies that the element is empty. When an element is empty, XML allows the opening and closing tags to be merged into a single self-closing tag.

A One-to-Many relationship

In the following example the XML string will contain data from two tables arranged in a One-to-Many (or parent-to-child or outer-to-inner) relationship. In the following code two query results are produced: $resouter for the parent table and $resinner for the child table. I shall only comment this code where there are differences.

$dbconnect = mysql_connect('localhost', 'user', 'pass')) {

echo "Connection failed to the host 'localhost'.";
exit;
} // if
if (!mysql_select_db('test')) {
echo "Cannot connect to database 'test'";
exit;
} // if

Here, for example, are separate database queries for each of the two tables:

$outer_table = 'parent_table';

$query = "SELECT * FROM $outer_table WHERE column='value'";
$resouter = mysql_query($query, $dbconnect);

$inner_table = 'child_table';
$query = "SELECT * FROM $inner_table WHERE column='value'";
$resinner = mysql_query($query, $dbconnect);

Here we create a new DOM document and add the root node:

// create a new XML document

$doc = domxml_new_doc('1.0');

// add root node
$root = $doc->create_element('root');
$root = $doc->append_child($root);

Here we add a node for the single row obtained from the parent table:

// add node for parent/outer table

$outer = $doc->create_element($outer_table);
$outer = $root->append_child($outer);

We must not forget to add each column value as a child element to the $outer node.

// take only one row from parent/outer table

$row = mysql_fetch_assoc($resouter);

// add a child node for each parent field
foreach ($row as $fieldname => $fieldvalue) {
$child = $doc->create_element($fieldname);
$child = $outer->append_child($child);
$value = $doc->create_text_node($fieldvalue);
$value = $child->append_child($value);
} // foreach

Here we add a node for each row obtained from the child table. Note that each of these rows is inserted as a child node to the $outer node, not the $root node. Each $inner node will have its column values inserted as its children.

// process all rows of the inner/many/child table

while($row = mysql_fetch_assoc($resinner)) {
// add node for each record
$inner = $doc->create_element($inner_table);
$inner = $outer->append_child($inner);
// add a child node for each field
foreach ($row as $fieldname => $fieldvalue) {
$child = $doc->create_element($fieldname);
$child = $inner->append_child($child);
$value = $doc->create_text_node($fieldvalue);
$value = $child->append_child($value);
} // foreach
} // while

Finally, get the completed XML document and send it to the client's browser.

// get completed xml document

$xml_string = $doc->dump_mem(true);
echo $xml_string;
?>

The above code will produce an XML file with the following structure:

"1.0"?>



value1
value2
............
valueX

value1
value2
............
valueX


............



This has the structure to to . The has child nodes which are its column values as well as multiple occurrences of .

Adding optional attributes

It may sometimes be necessary to include additional information for an element with the XML data, and this can be done in the form of attributes. An attribute has a name and a value, and any number of attributes can be added to an element. This must be done by using the '->set_attribute' method immediately after the '->append_child' method and before any '->create_text_node' method, as shown in the following code snippet:

$child = $doc->create_element($fieldname);

$child = $outer->append_child($child);
$child->set_attribute('attr1', 'attrval1');
$child->set_attribute('attr2', 'attrval2');
$value = $doc->create_text_node($fieldvalue);
$value = $child->append_child($value);

These attribute values will then appear within the element's start tag, as follows:

"1.0"?>



"attrval1" attr2="attrval2">value1
"attrval1" attr2="attrval2">value2
"attrval1" attr2="attrval2">value3


Note that you can insert attributes for row elements as well as column elements.

In my own application I use attribute values to specify the size of each column, so that it does not have to be hard-coded within the XSL file. For multi-line columns I pass values for both 'rows' and 'cols'.

I also use attributes to include any error messages. All error messages get inserted to an array called $errors where the key is the fieldname and the value is the message. The code to insert the error message into the XML document as an attribute of the field which generated the error is as simple as this:

if (isset($errors[$fieldname])) {

$child->set_attribute("error", $errors[$fieldname]);
} // if

Using Multi-Byte Characters

I had a slight problem recently when the data I output to my XML file contained characters with accents (as in à, è, í, ö and û). These were coming out all garbled until I discovered the reason why. It turns out that the internal encoding for libxml when storing the document is UTF-8, so you need to convert non UTF-8 encoded strings into UTF-8 when setting content. This requires the following changes to the code samples:

(1) Convert from default character set (refer to default_charset in file php.ini) to UTF-8 by inserting a single line as follows:

  foreach ($row as $fieldname => $fieldvalue) {

$child = $doc->create_element($fieldname);
$child = $inner->append_child($child);
$value = mb_convert_encoding($value,'UTF-8','ISO-8859-1'); <<-- new line $value = $doc->create_text_node($fieldvalue);
$value = $child->append_child($value);
} // foreach

(2) To output the document correctly you must convert back to the default character set by amending the following line:

$xml_string = $doc->dump_mem(true, 'ISO-8859-1');

NOTE: In order for this to work you must enable the Multi-Byte String functions in PHP.

Conclusion

By using this method I have been able to develop a generic mechanism for creating XML files based on the relationship of the database tables concerned. All I need do is specify the table names(s) and the selection criteria, and whatever comes out of the database will be transferred to an XML file for subsequent transformation into HTML using an XSL file.
By Tony Marston

Quercus for PHP

Quercus is Caucho Technology's 100% Java implementation of PHP 5 released under the Open Source GPL license. Quercus comes with many PHP modules and extensions like PDF, PDO, MySQL, and JSON. Quercus allows for tight integration of Java services with PHP scripts, so using PHP with JMS or Grails is a quick and painless endeavor.

With Quercus, PHP applications automatically take advantage of Java application server features just as connection pooling and clustered sessions.

Quercus implements PHP 5 and a growing list of PHP extensions including APC, iconv, GD, gettext, JSON, MySQL, Oracle, PDF, and Postgres. Many popular PHP application will run as well as, if not better, than the standard PHP interpreter straight out of the box. The growing list of PHP software certified running on Quercus includes DokuWiki, Drupal, Gallery2, Joomla, Mambo, Mantis, MediaWiki, Phorum, phpBB, phpMyAdmin, PHP-Nuke, Wordpress and XOOPS.

Quercus presents a new mixed Java/PHP approach to web applications and services where Java and PHP tightly integrate with each other. PHP applications can choose to use Java libraries and technologies like JMS, EJB, SOA frameworks, Hibernate, and Spring. This revolutionary capability is made possible because 1) PHP code is interpreted/compiled into Java and 2) Quercus and its libraries are written entirely in Java. This architecture allows PHP applications and Java libraries to talk directly with one another at the program level. To facilitate this new Java/PHP architecture, Quercus provides and API and interface to expose Java libraries to PHP.

The Quercus .war file can be run on Java application servers such as Glassfish, i.e. it can be run outside of Resin. This .war file includes the Quercus interpreter and the PHP libraries.

If you are new to Quercus, please check out:

Private cloud networks are the future of corporate IT

The future of corporate IT is in private clouds, flexible computing networks modeled after public providers such as Google and Amazon yet built and managed internally for each business's users, the analyst firm Gartner says.

Cloud computing hype centers largely around the outsourcing of IT needs to cloud services available over the Internet. While this trend is expected to accelerate, Gartner predicts it will also become standard for large companies to build their own highly automated private cloud networks in which all resources can be managed from a single point and assigned to applications or services as needed. "Our belief is the future of internal IT is very much a private cloud," says Gartner analyst Thomas Bittman."Our clients want to know 'what is Google's secret? What is Microsoft's secret?' There is huge interest in being able to get learnings from the cloud."
Bittman discussed Gartner's predictions in an interview with Network World, and will detail them again next month at the analyst firm's annual Data Center Conference in Las Vegas in a presentation titled "The Future of Infrastructure and Operations: The Engine of Cloud Computing."

While Bittman says it will take years for private clouds to develop, some early adopters are already "Google-izing" their own data centers. Bechtel, for instance, is using the software-as-a-service computing model internally to provide IT services to 30,000 users, in a project that relies heavily on server and storage virtualization. (Compare storage products.)

Server virtualization is key to building internal as well as external clouds, Bittman says, noting that Amazon hosts applications in Xen virtual machines. But server virtualization is only one of several necessary layers.

A meta operating system -- similar to VMware's recently developed Virtual Datacenter Operating System -- will be necessary to manage an enterprise's distributed resources as one computing pool, Bittman adds.

Specifically, the meta operating system is "a virtualization layer between applications and distributed computing resources … that utilizes distributed computing resources to perform scheduling, loading, initiating, supervising applications and error handling."

But the meta operating system only provides the muscles of a distributed environment, Bittman says. Another layer, which Gartner calls a service governor, will have to provide the brains, making decisions about where to allocate computing resources.
Say you have five business units and 100 applications -- some need ultra-fast performance and others don't. The service governor will decide which application gets what.

The technology "is evolving," Bittman says."This is not something that is just going to turn on."

Private clouds will take shape over the next few years, but perhaps only in large enterprises."Over time, a small business will not have economies of scale to make it worth staying in the IT business," Bittman says. Within five years, a huge percentage of small businesses will get most of their computing resources from external cloud providers, he predicts.

That's not to say enterprises with their own private clouds will shun cloud offerings that provide instant access to processing power and storage. Each company will manage a fixed capacity in-house and have access to external capacity from public providers when they need it -- sort of like overdraft protection in a bank account, Bittman says.

If a company experiences a sudden spike in demand, the meta operating system and service governor will arrange for extra capacity to be secured from outside sources. Users won't have any idea which server they are using or whether computing capacity is coming from inside or outside the enterprise, Bittman says. Layers of abstraction will permeate through the data center and users will be presented only with a services-oriented interface.

As public clouds evolve, enterprises will have many more choices, Bittman says. Today's cloud computing services boast of elasticity, the ability to scale resources up and down as needed at any time. Amazon's cloud computing service is called the Elastic Compute Cloud (EC2), for example.

But these services aren't truly elastic, in Bittman's view. Amazon charges a certain price for each virtual server and "if I want a slightly larger server, I have to buy another one. … I have to do it in chunks," he says.

Cloud vendors should move toward providing computing capacity in any increment customers want, he says. But that's just one of many hurdles to be overcome in the building of private and public clouds.

"It's going to take a long time for clouds to mature in all areas and have viable offerings that fit all needs," Bittman says.
by Jon Brodkin
courtsy: http://www.networkworld.com




Wednesday

NASA Open Source Software

NASA conducts research and development in software and software technology as an essential response to the needs of NASA missions. Under the NASA Software Release policy, NASA has several options for the release of NASA developed software technologies. These options now include Open Source software release. This option is under the NASA Open Source Agreement "NOSA".

The motivations for NASA to distribute software codes Open Source are:

  • To increase NASA software quality via community peer review
  • To accelerate software development via community contributions
  • To maximize the awareness and impact of NASA research
  • To increase dissemination of NASA software in support of NASA's education mission

Projects

BigView

BigView allows for interactive panning and zooming of images of arbitrary size on desktop PCs running linux. Additionally, it can work in a multi-screen environment where multiple PCs cooperate to ...

CODE

CODE is a software framework for control and observation in distributed environments. The basic functionality of the framework allows a user to observe a distributed set of resources, services, and ...

ECHO

The concept of ECHO has been many years in the making. The initial charter and Plan development began in June of 1998 and was called the Independent Information Management System ...

Geometry Manipulation Protocol (GMP)

The Geometry Manipulation Protocol (GMP) is a library which serializes datatypes between XML and ANSI C data structures to support CFD applications. This library currently provides a description of geometric ...

Growler

Growler is a C++-based distributed object and event architecture. It is written in C++, and supports serialization of C++ objects as part of its Remote Method Invocation, Event Channels, and ...

IND: Creation and Manipulation of Decision Trees from Data

IND is applicable to most data sets consisting of independent instances, each described by a fixed length vector of attribute values. An attribute value may be a number, one of ...

IPG Execution Service

The Execution Service allows users to submit, monitor, and cancel complex jobs. Each job consists of a set of tasks that perform actions such as executing applications and managing data. ...

JavaGenes

JavaGenes is a fairly general purpose evolutionary software system written in Java. It implements several versions of the genetic algorithm, simulated annealing, stochastic hill climbing and other search techniques. JavaGenes ...

Libibvpp

Libibvpp is a C++ wrapper around libibverbs, which is part of the OpenFabrics software suite (www.openfabrics.org). For the most part, Libibvpp provides a minimalistic C++ wrapper interface to libibverbs, while ...

Livingstone2

Livingstone2 is a reusable artificial intelligence (AI) software system designed to assist spacecraft, life support systems, chemical plants or other complex systems in operating robustly with minimal human supervision, even ...

Mariana

Mariana is an algorithm that efficiently optimizes the hyperparameters for Support Vector Machines for regression and classification. It currently uses Simulated Annealing for optimization but can be extended to use ...

Mesh

Mesh is a secure, lightweight grid middleware that is based on the addition of a single sign-on capability to the built-in public key authentication mechanism of SSH using system call ...

Mission Simulation ToolKit

The Mission Simulation Toolkit (MST) is a flexible software system for autonomy research. The MST was developed as part of the Mission Simulation Facility (MSF) project, which was started in ...

NodeMon

NodeMon is a resource utilization monitor tailored to the Altix architecture, but is applicable to any Linux system or cluster. It allows distributed resource monitoring via the Growler software infrastructure. ...

Pour

Pour is a framework for Periodic, On-Demand, and User-Specified Information Reconciliation that accepts periodic information updates, collects information on-demand as needed, and accepts user-specified information while presenting a single unified ...

ROBUS-2

The ROBUS-2 Protocol Processor (RPP) is a custom-designed hardware component implementing the functionality of the ROBUS-2 fault-tolerant communication system. The Reliable Optical Bus (ROBUS) is the core communication system of ...

sequenceMiner

sequenceMiner was developed to address the problem of detecting and describing anomalies in large sets of high-dimensional symbol sequences. sequenceMiner works by performing unsupervised clustering (grouping) of sequences using the ...

SLAB Spatial Audio Renderer

SLAB is a software-based, real-time virtual acoustic environment rendering system being developed as a tool for the study of spatial hearing. SLAB is designed to work in the personal computer ...

Surfer

Surfer is an extensible framework designed to select and rank grid resources where a resource is defined to be anything that may need selecting such as compute resources, storage resources, ...

Swim

Swim is a Software Information Metacatalog that gathers detailed information about the software components and packages installed on each grid resource. Information is currently gathered for Executable and Linking Format ...

Vision Workbench

The NASA Vision Workbench (VW) is a modular, extensible, cross-platform computer vision software framework written in C++. It was designed to support a variety of space exploration tasks, including automated ...

World Wind

World Wind allows any user to zoom from satellite altitude into any place on Earth, leveraging high resolution LandSat imagery and SRTM elevation data to experience Earth in visually rich ...
courtsy: nasa.gov

Tuesday

NASA Successfully Tests First Deep Space Internet

NASA has successfully tested the first deep space communications network modeled on the Internet.
Working as part of a NASA-wide team, engineers from NASA's Jet Propulsion Laboratory in Pasadena, Calif., used software called Disruption-Tolerant Networking, or DTN, to transmit dozens of space images to and from a NASA science spacecraft located about 20 million miles from Earth.

"This is the first step in creating a totally new space communications capability, an interplanetary Internet," said Adrian Hooke, team lead and manager of space-networking architecture, technology and standards at NASA Headquarters in Washington.

NASA and Vint Cerf, a vice president at Google Inc., in Mountain View, Calif., partnered 10 years ago to develop this software protocol. The DTN sends information using a method that differs from the normal Internet's Transmission-Control Protocol/Internet Protocol, or TCP/IP, communication suite, which Cerf co-designed.

The Interplanetary Internet must be robust to withstand delays, disruptions and disconnections in space. Glitches can happen when a spacecraft moves behind a planet, or when solar storms and long communication delays occur. The delay in sending or receiving data from Mars takes between three-and-a-half to 20 minutes at the speed of light.

Unlike TCP/IP on Earth, the DTN does not assume a continuous end-to-end connection. In its design, if a destination path cannot be found, the data packets are not discarded. Instead, each network node keeps the information as long as necessary until it can communicate safely with another node. This store-and-forward method, similar to basketball players safely passing the ball to the player nearest the basket means information does not get lost when no immediate path to the destination exists. Eventually, the information is delivered to the end user.

"In space today, an operations team must manually schedule each link and generate all the commands to specify which data to send, when to send it, and where to send it," said Leigh Torgerson, manager of the DTN Experiment Operations Center at JPL. "With standardized DTN, this can all be done automatically."

Engineers began a month-long series of DTN demonstrations in October. Data were transmitted using NASA's Deep Space Network in demonstrations occurring twice a week. Engineers use NASA's Epoxi spacecraft as a Mars data-relay orbiter. Epoxi is on a mission to encounter Comet Hartley 2 in two years. There are 10 nodes on this early interplanetary network. One is the Epoxi spacecraft itself and the other nine, which are on the ground at JPL, simulate Mars landers, orbiters and ground mission-operations centers.

This month-long experiment is the first in a series of planned demonstrations to qualify the technology for use on a variety of upcoming space missions. In the next round of testing, a NASA-wide demonstration using new DTN software loaded on board the International Space Station is scheduled to begin next summer.

In the next few years, the Interplanetary Internet could enable many new types of space missions. Complex missions involving multiple landed, mobile and orbiting spacecraft will be far easier to support through the use of the Interplanetary Internet. It also could ensure reliable communications for astronauts on the surface of the moon.

The Deep Impact Networking Experiment is sponsored by the Space Communications and Navigation Office in NASA's Space Operations Mission Directorate in Washington. NASA's Science Mission Directorate and Discovery Program in Washington provided experimental access to the Epoxi spacecraft. The Epoxi mission team provided critical support throughout development and operations.
courtsy: nasa.gov

Wednesday

Switch from file get contents to curl

Introduction

file_get_contents() is deprecated in favor of using the CURL libraries. You will occasionally run accross old code that uses the file_get_contents() that you want to use on servers with the file_get_contents functionality disabled. This shows how to convert from that function to the curl functions.
[edit]
file_get_contents code

$data = file_get_contents($remoteurl);
[edit]
curl code

//Initialize the Curl session
$ch = curl_init();

//Set curl to return the data instead of printing it to the browser.
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
//Set the URL
curl_setopt($ch, CURLOPT_URL, $URL);
//Execute the fetch
$data = curl_exec($ch);
//Close the connection
curl_close($ch);

//$data now contains the contents of $URL

ML self-service pipeline that abstracts Kubernetes complexity

  To successfully bridge the gap between machine learning engineering and cluster operations, you need to build a self-service pipeline that...