Wednesday, November 28, 2007

The Future of PHP

source : Marc's blog

Marc says :

Last Friday there was a rather spirited discussion at the office between Kaiton (a Ruby aficionado and ex-Panmedia employee currently at Microsoft), Nesta (a python aficionado who is forced to use PHP) and myself, (an un-biased PHP user who is forced to defend it ;-). The topic of course was PHP, its woes, its past, its present and its future.

According to Kaiton, PHP will be marginalised in 2008 by more literate programming languages like Ruby, Python and even Erlang. His view is that as the web becomes more important to business, backend programmers will start to get involved in the front end and web programming will be dominated by more experienced programmers who care more about the language they code with. According to Nesta, Drupal is the only good thing that ever came out of PHP. I think that 2007 will be the year of PHP.

By the end of the argument discussion, things were kinda starting to go around in circles so I decided to try and articulate my ideas in written format so that they could be dated, critiqued and openly ridiculed, and so that a year or two from now I can come back read the crazy things I was thinking way back in 2007. It will also serve as an official record in case Kaiton wants to put his money where his mouth is ;-)

Before I present my argument, let me get a few things straight.

There are definitely some dislikeable things about PHP. The language design (or lack thereof) is far from perfect. PHP’s roots and a laissez-faire approach to adding new features early on have scarred the language with inconsistent naming conventions and function behaviour. A late start in the object oriented game meant that it took them a little while to get that feature right. Its popularity has been its Achilles heel and prevented the developers from making radical sweeping changes to normalize the language.

On the flip side there are a few unfair assertions that people tend to make against PHP. I often hear people say “you can’t build large apps in PHP, it just gets messy!”. While there is a lot of bad PHP code out there, that doesn’t mean you can’t write clean code. The PHP language does not enforce a lot rules to prevent you from coding sloppily; whether or not a language should police coders is a separate matter of debate. The fact remains however that PHP provides you with everything you need to code your applications according to tried and true Object Oriented Design Patterns. Whether or not you choose to do so is up to you.

Just so it is clear, I am not saying that PHP deserves to prosper, I am not saying that I personally want it to prosper, I am simply saying that it will prosper. Here is why:

PHP is popular. This has a couple ramifications.

  • PHP 4 is available by default in just about any web-hosting plan you come across. And PHP 5 availability is rising steadily.

  • It has been widely and heavily tested against a multitude of different environments and configurations (Hardware, OS, Database, 3rd party software, etc).

  • You are much more likely to have somebody in-house who knows at lease some PHP than any Ruby or Python, and it is also much easier to hire a PHP developer.

PHP 5 is “good enough”.

Regardless of what you want to say about PHP’s language design (or complete lack thereof) as it currently stands at version 5.2:

  • It has sufficient object oriented features for coding according to enterprise patterns.

  • It has deprecated the magic functions (register globals, magic quotes, etc) that have been the source of many security issues in the past an scheduled them for complete removal in PHP 6.

  • The Internet’s most trafficked site is built using PHP. They are heavily invested its success and are one of the major driving forces behind the PHP 6’s upcoming Unicode support. They have a presentation online explaining their rationale for choosing PHP.

This meets my definition of good enough. Far from perfect, not necessarily the best, but good enough for web work of all sizes.

The PHP Collaboration Project makes PHP better.

  • The Zend Framework leads by example to help solve the sloppy PHP coder epidemic and give developers a solid basis for rapid application development.

  • The Zend Developer Zone plays in important role by engaging the community and providing example code.

  • However, to me, the most important part of the Collaboration project is the new Eclipse based IDE/Debugger. A well designed, fully integrated free and open source graphical coding/debugging solution for PHP will a major step forward for web application development. One year from now many of use will scratch our heads in amazement at the primitive methods we used to employ for debugging complex web apps. The difference will be as huge as the jump from debugging JavaScript using alert messages to using Firebug 1.0 (more on that in a later blog post).

Zend knows how to compete

From a developer point of view the entire PHP Collaboration Project is an attempt to compete with ASP.NET. The combination of a Developer Zone, an IDE and a Framework are straight out of the Microsoft playbook. They are even working with Microsoft to compete in the arena where Microsoft typically dominates… distribution to the windows desktop. Zend Core for Windows will be a simple download and install and I wouldn’t put it past them to bundle the framework in as well.

On the Framework level the Zend Framework competes with Ruby on Rails, Django and ASP.Net. It covers all the typical bases MVC, ORM, Caching, JSON, etc.

On the language level PHP 5’s object-oriented improvements and PHP 6’s upcoming Unicode support give the language some more “enterprise” features, but I don’t think Zend is actually trying to compete with Java. Zend and Sun’s collaborated on JSR-223 (Scripting for the Java Platform) which will allow scripting languages like PHP to access Java objects in the recently released Java 6 JRE. When you combine this with the Java language support in Zend Studio 5.5 it indicates to me that Zend wants to co-exist rather than compete with Java.

Zend is making inroads into the corporate world

Separate and apart from working with IBM, Intel, EBay and Amazon on the collaboration project, Zend has established partnerships with the major proprietary Database vendors and created a special set of PHP distributions just for them called Zend Core; this now includes Zend Core for Oracle, and Zend Core for IBM . Not only do these types of partnerships make companies feel more comfortable using PHP with the proprietary databases they have built their business on but they also offer a number of other benefits:

  • Improved drivers. By working directly with the database vendors, Zend has already made numerous improvements to the PHP extensions for Oracle and DB2.

  • Simplified distribution and setup for those who need to use proprietary drivers (since they aren’t distributed with the standard php.net download).

  • GUI based management. Each Zend Core distribution comes with a web based interface for viewing the server status and managing the PHP configuration. Zend Core also comes with an updater that allows you to easily apply and rollback PHP updates.

  • Zend Core provides a nice smooth on ramp to support. You can download the basic system for free and then choose your level of support later on. Support for PHP may not seem necessary to most developers but it is nice to have the option in case the company you are working for wants it.

Not to be left behind, rumour has it that early this year we will see a Zend Core for MySQL as well as a brand new MySQL driver developed specifically for PHP that provides added performance and reliability. When you consider this along with MySQL’s upcoming Falcon storage engine and their extremely useful GUI tools, 2007 may be the year of MySQL as well.

The train has left the station

I have a python book here on my desk that is number 2 or 3 in my reading queue right now, I would really like to learn Ruby (but don’t see it happening anytime soon). I have no doubt that I will find both these languages more pleasant to code with than PHP, but my personal tastes have little to do with what I think will happen in 2007/2008. The PHP train is a rollin’ and my learning or loving another language ain’t gonna stop it.

What do you think? Will 2007/2008 see PHP rise or fall?

Zend Framework - Power PHP

Zend Interview...

In July, Zend Technologies released version 1.0 of the Zend Framework, an open source application framework for PHP. Here, Huw Collingbourne talks to Mark de Visser, Zend’s Chief Marketing Officer, about Zend Framework, the future of PHP and the challenges of Ruby On Rails, Flex and Silverlight...

So What Is ‘Zend Framework’, Anyhow...?

We asked Mark de Visser to give us an overview of the main features of the Zend Framework and what they offer to developers. This is what he told us...


Zend Framework (ZF) is completely free for development and distribution, that is, is open-source and free to download & use and features the following:
- New BSD license: business-friendly – it’s easy to build your own commercial intellectual property on top of ZF
- An Apache-like Contributor License Agreement (CLA) process assures that the code is free of legal issues – important protection and peace-of-mind
- Fully object-oriented PHP 5 class library, with attention to best design practices
- Tested thoroughly with about 90,000 lines of test code for 140,000 lines of framework code, all contributors were asked to submit test code and documentation with every submission.
- Extreme simplicity, modular “use-at-will” architecture for easy migration and ability to share ZF code with your current applications and even with other frameworks if necessary.
That’s the high-level (above); more specific features and their benefits are here:

- Model/View/Controller architecture – an industry standard best practice for web apps Full database support (incl. MySQL, PostgreSQL, SQLite, Oracle, DB2, & MS SQL Server) – all major databases you may need, with the ease-of-use you expect from a framework
- Internationalization support necessary for most modern web applications – in a global economy providing localized versions in your users’ language is easy and straightforward
- Authentication & sessions – to make managing sign-on & users of your application easy.
- AJAX/JSON support – to help meet the ease-of-use your users now come to expect
- Search – native PHP port of industry-standard Lucence search engine
- Syndication (RSS & Atom supported) – don’t be left behind without the data formats & access your Web 2.0 users need
- Web services – Zend Framework aims to be the premier place to consume and publish web services with support for REST, Http, Google Gdata APIs, Amazon, Flickr, Yahoo!, del.ici.ous, StrikeIron services, and others – create mashups and other web 2.0 apps easily.
- Adobe PDF – support for PDF creation, reading, editing
- Mail – read and send e-mail, including support for MIME Many other framework services including support for safer and more secure applications with input filtering, authentication, & HTML escaping, as well as caching, filtering, logging, input validation, and more.

- Download Zend Framework: http://framework.zend.com/

HC: Let’s start by talking about the Zend Framework. There are already numerous PHP frameworks around - such as CakePHP, PEAR and CodeIgniter. Is the Zend Framework ‘in competition’ with those or can it be use in collaboration with them?

Mark de Visser: We would not say that Zend Framework competes per se, with any other PHP framework. Different frameworks have different aims and there really is no “one size fits all” in that regard.

Due to its modular “use-at-will” architecture, Zend Framework can be used with any/all of these frameworks. As a result, bolting on the Lucene search engine, adding PDF generation/editing, or even re-architecting your app to be a modern model-view-controller web app can all be done to greater or lesser degrees, depending on which other framework you are working with.

HC: What factors would determine which the choice of framework for a potential user?

Mark de Visser: We anticipate that larger enterprise developers will find Zend Framework an excellent choice to start any “green field” (new) project and that some of the factors they might use to choose a framework would be to carefully investigate: licensing (both of the framework itself and of any contributed code), how best practices are embodied in the framework, security support built-in, how thoroughly the code has been tested, how well it has been documented, how easy is it to migrate and whether or not you are ‘stuck’ in a particular way of doing things once migrated, as well as that it contains all the latest features one might want in a framework.

HC: Recently CodeGear launched a ‘visual’ PHP system, Delphi For PHP, which has its own open source class library and a drag-and-drop application designer. Is this the way forward for PHP development?

Mark de Visser: We are not familiar with CodeGear’s approach but subscribe to the idea of enabling framework classes so they can be used in visual designers.

HC: Over the past year or so, Ruby On Rails has emerged as an increasingly influential framework. If I were considering the pros and cons of developing a web application using Ruby On Rails or PHP and the Zend Framework, what things should I consider when coming to a decision?

Mark de Visser: We find that most programmers pick languages based on a number of criteria. We think that Zend Framework will be an important addition that makes PHP more attractive, just as Rails makes Ruby more attractive in others. .

Aside from that, by choosing Ruby on Rails a developer subscribes to a whole approach, including semantics, naming conventions, application architecture, etc. In contrast, Zend Framework lets you use as much or as little of the framework as you wish, making it very easy to adopt it gradually for existing code bases.

PHP is the technology used for many of the most scalable web applications, including Yahoo, Facebook and YouTube. These sites drive the decision for many commercial developers to PHP, and subsequently to Zend Framework.

HC: One of the features of the Ruby language is that it is object oriented from the ground up. With PHP, objects have been ‘bolted onto’ the language, haven’t they? Doesn’t that mean that PHP won’t take advantage of things like, for example, rigorous encapsulation and data hiding? Isn’t that something that goes in Ruby’s favour?

Mark de Visser: A key part of the move from PHP 4 to 5 was to make objects first-class citizens, including rigorous encapsulation and data hiding. So with PHP5 programming in PHP is really no different than programming in any other object oriented languages.

Zend Framework leverages this capability of PHP5 and to create a framework that is fully OO and rigorously encapsulates and hides data. By embodying object-oriented best practices Zend Framework helps a developer learn, and stay safe.

HC: Can you explain what part Zend plays in the development of the PHP language? Do you have full control over the design and implementation? Or is the future of PHP determined by the efforts of the open source community?

Mark de Visser: Zend does not have control over PHP, although some of its employees are active participants in the decisions of the open source community. Zend benefits greatly from the vibrant PHP ecosystem and tries to give back to this community whenever possible. It has many of the developers who work on maintenance and future versions of PHP on its payroll and also supports major open source projects like Zend Framework and the Eclipse PHP Development Tools (PDT).

HC: What is the state of development on PHP 6? What are the significant new features that will be added to the language and will they ‘break’ existing applications?

Mark de Visser: There is an active PHP open-source community at work on PHP 6. i18n with full unicode support for multi-byte characters and most likely namespaces seem to be the major features under consideration for PHP6.

HC: There is a great deal of activity in the realm of so-called ‘Rich Internet Applications’ at the moment - a field currently dominated by Adobe’s Flex but now being challenged by Microsoft’s Silverlight. One of the special features of Silverlight is that it will be programmable using Python, VB, Ruby or JavaScript. Are Flex and Silverlight going to squeeze out PHP? Can PHP find a way of working with them?

Mark de Visser: PHP being a server-side language works just fine with Silverlight & Flex. Flex and PHP work together beautifully. There is an article on the Zend Developer Zone HERE that explains this well - and is written by an Adobe employee. Additionally, Microsoft (one of our partners) has released an Atlas-based Ajax toolkit for PHP (some links you might see are on the main library site that Microsoft supports; also see HERE, HERE and HERE, which further strengthens the usage of PHP and other client-side technologies like Silverlight and Flex/Flash.

Unrelated to those two, but a connector between client & server side is the WebORB project that you can find HERE; they are also interested in building their code into Zend Framework—and they already have a free open-source PHP kit available now.

HC: Finally, PHP has been one of the driving forces of ‘Web 2’. Looking ahead, five or ten years from now, how will the face of the web have changed and what part will PHP play in that?

Mark de Visser: Zend finds predictions of that sort difficult at best. But as Andi Gutmans, Zend CTO for Product Strategy and co-founder has said in a recent ComputerWorld profile of him:

“One trend that I think is going to be critical to the IT industry in the coming years is the decentralization of IT. It is being enabled by a few factors. The first is dynamic languages like PHP, which are making it increasingly easy for lightweight developers to become productive. The second is the rise of service-oriented architecture, which is leading to the end of monolithic application architectures. Third, the shift toward Web applications that are making it extremely easy to deploy applications inside the enterprise, not requiring a central function to test, deploy and manage those applications. This change is going to allow companies to become far more productive and agile, significantly decreasing the traditional backlog of business applications IT has in its queue. It will also enable an increasing number of such applications to leverage the organization’s existing assets by integrating the SOA-enabled infrastructure.”

As a follow-up to Andi’s prognostications we’ll also mention that IBM has built a management dashboard ‘mash-up’ tool of sorts to do just that, that is, leverage an organization’s existing assets—and though it’s just getting released, it’s already a fairly high profile project at IBM gaining them many SOA-enabled customers—and is called QEDWiki (Quickly & Easily Done Wiki) which is built entirely on Zend Framework. You can find out more about this project here: http://services.alphaworks.ibm.com/qedwiki/

You also may want to view this demo here: http://www.alphaworks.ibm.com/demo/flash/display/qedwiki4


Zend Technologies, Inc., the PHP company (http://www.zend.com/), is the leading provider of products and services for developing, deploying and managing business-critical PHP applications. Zend’s founders are the architects of PHP, which is used by more than 22 million Web sites.

Mark de Visser joined Zend in December 2005. Previously he worked for Agitar Software, a supplier of tools for Java software developers. Before Agitar, de Visser was vice president of marketing at Red Hat Inc. where he was a member of the team that created the company’s market-leading position with the introduction of Red Hat Enterprise Linux. He also worked for twelve years at Borland International in international and product marketing roles, and as vice president of corporate marketing.

Wednesday, November 21, 2007

Caching in PHP using the filesystem, APC and Memcached

By Evert on 2006-11-01 02:14:18

Caching is very important and really pays off in big internet applications. When you cache the data you're fetching from the database, in a lot of cases the load on your servers can be reduced enormously.


One way of caching, is simply storing the results of your database queries in files.. Opening a file and unserializing is often a lot faster than doing an expensive SELECT query with multiple joins.


Here's a simple file-based caching engine.


<?php



// Our class

class  FileCache  {



// This is the function you store information with

function  store ( $key , $data , $ttl ) {



// Opening the file

$h  =  fopen ( $this -> getFileName ( $key ), 'w' );

     if (! $h ) throw new  Exception ( 'Could not write to cache' );

// Serializing along with the TTL

$data  =  serialize (array( time ()+ $ttl , $data ));

     if ( fwrite ( $h , $data )=== false ) {

       throw new  Exception ( 'Could not write to cache' );

     }

fclose ( $h );



   }



// General function to find the filename for a certain key

private function  getFileName ( $key ) {



       return  '/tmp/s_cache'  .  md5 ( $key );



   }



// The function to fetch data returns false on failure

function  fetch ( $key ) {



$filename  =  $this -> getFileName ( $key );

       if (! file_exists ( $filename ) || ! is_readable ( $filename )) return  false ;



$data  =  file_get_contents ( $filename );



$data  = @ unserialize ( $data );

       if (! $data ) {



// Unlinking the file when unserializing failed

unlink ( $filename );

          return  false ;



       }



// checking if the data was expired

if ( time () >  $data [ 0 ]) {



// Unlinking

unlink ( $filename );

          return  false ;



       }

       return  $data [ 1 ];

     }



}



?>

Key strategies


All the data is identified by a key. Your keys have to be unique system wide; it is therefore a good idea to namespace your keys. My personal preference is to name the key by the class thats storing the data, combined with for example an id.


example


Your user-management class is called My_Auth, and all users are identified by an id. A sample key for cached user-data would then be " My_Auth:users:1234 ". '1234' is here the user id.


Some reasoning behind this code


I chose 4096 bytes per chunk, because this is often the default inode size in linux and this or a multiple of this is generally the fastest. Much later I found out file_get_contents is actually faster.


Lots of caching engines based on files actually don't specify the TTL (the time it takes before the cache expires) at the time of storing data in the cache, but while fetching it from the cache. This has one big advantage; you can check if a file is valid before actually opening the file, using the last modified time ( filemtime() ).


The reason I did not go with this approach is because most non-file based cache systems do specify the TTL on storing the data, and as you will see later in the article we want to keep things compatible. Another advantage of storing the TTL in the data, is that we can create a cleanup script later that will delete expired cache files.


Usage of this class


The number one place in web applications where caching is a good idea is on database queries. MySQL and others usually have a built-in cache, but it is far from optimal, mainly because they have no awareness of the logic of you application (and they shouldn't have), and the cache is usually flushed whenever there's an update on a table. Here is a sample function that fetches user data and caches the result for 10 minutes.


<?php



// constructing our cache engine

$cache  = new  FileCache ();



  function  getUsers () {



     global  $cache ;



// A somewhat unique key

$key  =  'getUsers:selectAll' ;



// check if the data is not in the cache already

if (! $data  =  $cache -> fetch ( $key )) {

// there was no cache version, we are fetching fresh data



        // assuming there is a database connection

$result  =  mysql_query ( "SELECT * FROM users" );

$data  = array();



// fetching all the data and putting it in an array

while( $row  =  mysql_fetch_assoc ( $result )) {  $data [] =  $row ; }



// Storing the data in the cache for 10 minutes

$cache -> store ( $key , $data , 600 );

     }

     return  $data ;

}



$users  =  getUsers ();



?>

The reason i picked the mysql_ set of functions here, is because most of the readers will probably know these.. Personally I prefer PDO or another abstraction library. This example assumes there's a database connection, a users table and other issues.


Problems with the library


The first problem is simple, the library will only work on linux, because it uses the /tmp folder. Luckily we can use the php.ini setting 'session.save_path'.


<?php



private function  getFileName ( $key ) {



       return  ini_get ( 'session.save_path' ) .  '/s_cache'  .  md5 ( $key );



   }



?>

The next problem is a little bit more complex. In the case where one of our cache files is being read, and in the same time being written by another process, you can get really unusual results. Caching bugs can be hard to find because they only occur in really specific circumstances, therefore you might never really see this issue happening yourself, somewhere out there your user will.


PHP can lock files with flock() . Flock operates on an open file handle (opened by fopen) and either locks a file for reading (shared lock, everybody can read the file) or writing (exclusive lock, everybody waits till the writing is done and the lock is released). Because file_get_contents is the most efficient, and we can only use flock on filehandles, we'll use a combination of both.


The updated store and fetch methods will look like this


<?php

// This is the function you store information with

function  store ( $key , $data , $ttl ) {



// Opening the file in read/write mode

$h  =  fopen ( $this -> getFileName ( $key ), 'a+' );

     if (! $h ) throw new  Exception ( 'Could not write to cache' );



flock ( $h , LOCK_EX );  // exclusive lock, will get released when the file is closed



fseek ( $h , 0 );  // go to the beginning of the file



     // truncate the file

ftruncate ( $h , 0 );



// Serializing along with the TTL

$data  =  serialize (array( time ()+ $ttl , $data ));

     if ( fwrite ( $h , $data )=== false ) {

       throw new  Exception ( 'Could not write to cache' );

     }

fclose ( $h );



   }



   function  fetch ( $key ) {



$filename  =  $this -> getFileName ( $key );

       if (! file_exists ( $filename )) return  false ;

$h  =  fopen ( $filename , 'r' );



       if (! $h ) return  false ;



// Getting a shared lock 

flock ( $h , LOCK_SH );



$data  =  file_get_contents ( $filename );

fclose ( $h );



$data  = @ unserialize ( $data );

       if (! $data ) {



// If unserializing somehow didn't work out, we'll delete the file

unlink ( $filename );

          return  false ;



       }



       if ( time () >  $data [ 0 ]) {



// Unlinking when the file was expired

unlink ( $filename );

          return  false ;



       }

       return  $data [ 1 ];

    }



?>

Well that actually wasn't too hard.. Only 3 new lines.. The next issue we're facing is updates of data. When somebody updates, say, a page in the cms; they usually expect the respecting page to update instantly.. In those cases you can update the data using store(), but in some cases it is simply more convenient to flush the cache.. So we need a delete method.


<?php



function  delete (  $key  ) {



$filename  =  $this -> getFileName ( $key );

         if ( file_exists ( $filename )) {

             return  unlink ( $filename );

         } else {

             return  false ;

         }



     }



?>

Abstracting the code


This cache class is pretty straight-forward. The only methods in there are delete, store and fetch.. We can easily abstract that into the following base class. I'm also giving it a proper prefix (I tend to prefix everything with Sabre, name yours whatever you want..). A good reason to prefix all your classes, is that they will never collide with other classnames if you need to include other code. The PEAR project made a stupid mistake by naming one of their classes 'Date', by doing this and refusing to change this they actually prevented an internal PHP-date class to be named Date.


<?php



abstract class  Sabre_Cache_Abstract  {



         abstract function  fetch ( $key );

         abstract function  store ( $key , $data , $ttl );

         abstract function  delete ( $key );



     }



?>

The resulting FileCache (which I'l rename to Filesystem) is:


<?php



class  Sabre_Cache_Filesystem  extends  Sabre_Cache_Abstract  {



// This is the function you store information with

function  store ( $key , $data , $ttl ) {



// Opening the file in read/write mode

$h  =  fopen ( $this -> getFileName ( $key ), 'a+' );

     if (! $h ) throw new  Exception ( 'Could not write to cache' );



flock ( $h , LOCK_EX );  // exclusive lock, will get released when the file is closed



fseek ( $h , 0 );  // go to the start of the file



     // truncate the file

ftruncate ( $h , 0 );



// Serializing along with the TTL

$data  =  serialize (array( time ()+ $ttl , $data ));

     if ( fwrite ( $h , $data )=== false ) {

       throw new  Exception ( 'Could not write to cache' );

     }

fclose ( $h );



   }



// The function to fetch data returns false on failure

function  fetch ( $key ) {



$filename  =  $this -> getFileName ( $key );

       if (! file_exists ( $filename )) return  false ;

$h  =  fopen ( $filename , 'r' );



       if (! $h ) return  false ;



// Getting a shared lock 

flock ( $h , LOCK_SH );



$data  =  file_get_contents ( $filename );

fclose ( $h );



$data  = @ unserialize ( $data );

       if (! $data ) {



// If unserializing somehow didn't work out, we'll delete the file

unlink ( $filename );

          return  false ;



       }



       if ( time () >  $data [ 0 ]) {



// Unlinking when the file was expired

unlink ( $filename );

          return  false ;



       }

       return  $data [ 1 ];

    }



    function  delete (  $key  ) {



$filename  =  $this -> getFileName ( $key );

       if ( file_exists ( $filename )) {

           return  unlink ( $filename );

       } else {

           return  false ;

       }



    }



   private function  getFileName ( $key ) {



       return  ini_get ( 'session.save_path' ) .  '/s_cache'  .  md5 ( $key );



   }



}



?>

There you go, a complete, proper OOP, file-based caching class... I hope I explained things well.


Memory based caching through APC


If files aren't fast enough for you, and you have enough memory to spare.. Memory-based caching might be the solution. Obviously, storing and retrieving stuff from memory is a lot faster. The APC extension not only does opcode cache (speeds up your php scripts by caching the parsed php script), but it also provides a simple mechanism to store data in shared memory.


Using shared memory in APC is extremely simple, I'm not even going to explain it, the code should tell enough.


<?php



class  Sabre_Cache_APC  extends  Sabre_Cache_Abstract  {



         function  fetch ( $key ) {

             return  apc_fetch ( $key );

         }



         function  store ( $key , $data , $ttl ) {



             return  apc_store ( $key , $data , $ttl );



         }



         function  delete ( $key ) {



             return  apc_delete ( $key );



         }



     }



?>

My personal problem with APC that it tends to break my code.. So if you want to use it.. give it a testrun.. I have to admit that I haven't checked it anymore since they fixed 'my' bug . . This bug is now fixed, APC is amazing for single-server applications and for the really often used data.


Memcached


Problems start when you are dealing with more than one webserver. Since there is no shared cache between the servers situations can occur where data is updated on one server and it takes a while before the other server is up to date.. It can be really useful to have a really high TTL on your data and simply replace or delete the cache whenever there is an actual update. When you are dealing with multiple webservers this scheme is simply not possible with the previous caching methods.


Introducing memcached . Memcached is a cache server originally developed by the LiveJournal people and now being used by sites like Digg , Facebook , Slashdot and Wikipedia .


How it works



  • Memcached consists of a server and a client part.. The server is a standalone program that runs on your servers and the client is in this case a PHP extension.

  • If you have 3 webservers which all run Memcached, all webservers connect to all 3 memcached servers. The 3 memcache servers are all in the same 'pool'.

  • The cache servers all only contain part of the cache. Meaning, the cache is not replicated between the memcached servers.

  • To find the server where the cache is stored (or should be stored) a so-called hashing algorithm is used. This way the 'right' server is always picked.

  • Every memcached server has a memory limit. It will never consume more memory than the limit. If the limit is exceeded, older cache is automatically thrown out (if the TTL is exceed or not).

  • This means it cannot be used as a place to simply store data.. The database does that part. Don't confuse the purpose of the two!

  • Memcached runs the fastest (like many other applications) on a Linux 2.6 kernel.

  • By default, memcached is completely open.. Be sure to have a firewall in place to lock out outside ip's, because this can be a huge security risk.


Installing


When you are on debian/ubuntu, installing is easy:


apt-get install memcached

You are stuck with a version though.. Debian tends to be slow in updates. Other distributions might also have a pre-build package for you. In any other case you might need to download Memcached from the site and compile it with the usual:


./configure

make

make install

There's probably a README in the package with better instructions.


After installation, you need the Pecl extension . All you need to do for that (usually) is..


pecl install Memcache

You also need the zlib development library. For debian, you can get this by entering:


apt-get install zlib1g-dev

However, 99% of the times automatic pecl installation fails for me. Here's the alternative installation instructions.


pecl download Memcache

tar xfvz Memcache-2.1.0.tgz #version might be changed

cd Memcache-2.1.0

phpize

./configure

make

make install

Don't forget to enable the extension in php.ini by adding the line extension=memcache.so and restarting the webserver.


The good stuff


After the Memcached server is installed, running and you have PHP running with the Memcache extension, you're off.. Here's the Memcached class.


<?php



class  Sabre_Cache_MemCache  extends  Sabre_Cache_Abstract  {



// Memcache object

public  $connection ;



         function  __construct () {



$this -> connection  = new  MemCache ;



         }



         function  store ( $key ,  $data ,  $ttl ) {



             return  $this -> connection -> set ( $key , $data , 0 , $ttl );



         }



         function  fetch ( $key ) {



             return  $this -> connection -> get ( $key );



         }



         function  delete ( $key ) {



             return  $this -> connection -> delete ( $key );



         }



         function  addServer ( $host , $port  =  11211 ,  $weight  =  10 ) {



$this -> connection -> addServer ( $host , $port , true , $weight );



         }



     }



?>

Now, the only thing you have to do in order to use this class, is add servers. Add servers consistently! Meaning that every server should add the exact same memcache servers so the keys will distributed in the same way from every webserver.


If a server has double the memory available for memcached, you can double the weight. The chance that data will be stored on that specific server will also be doubled.


Example


<?php



     $cache  = new  Sabre_Cache_MemCache ();

$cache -> addServer ( 'www1' );

$cache -> addServer ( 'www2' , 11211 , 20 );  // this server has double the memory, and gets double the weight

$cache -> addServer ( 'www3' , 11211 );



// Store some data in the cache for 10 minutes

$cache -> store ( 'my_key' , 'foobar' , 600 );



// Get it out of the cache again

echo( $cache -> fetch ( 'my_key' ));



?>

Some final tips



  • Be sure to check out the docs for Memcache and APC to and try to determine whats right for you.

  • Caching can help everywhere SQL queries are done.. You'd be surprised how big the difference can be in terms of speed..

  • In some cases you might want the cross-server abilities of memcached, but you don't want to use up your memory or have your items automatically get flushed out.. Wikipedia came across this problem and traded in fast memory caching for virtually infinite size file-based caching by creating a memcached-compatible engine, called Tugela Cache , so you can still use the Pecl Memcache client with this, so it should be pretty easy. I don't have experience with this or know how stable it is.

  • If you have different requirements for different parts of your cache, you can always consider using the different types alongside.

Create a searchable Google map

Google's Mark Lucokvsky shows you how to add search functionality to maps using the GSmapSearchControl Solution


The GSmapSearchControl Solution is a simple to use application of the Google AJAX Search API that is designed to let you easily add a searchable map to your pages, sites and blogs. The rest of this page discusses this topic in detail - but if you just want to cut and paste some code, the “hello world” of the Map Search Control solution is available at the end of this article .


This basic sample demonstrates the following features:



  • A search control that allows you to look up addresses and business listings, plotting the results on a map.

  • The search control has a programmable center point, and at this center point you can specify a custom title and url.

  • The search control takes a small amount of verticle space while inactive, the inactive view is a small map with a white center point.

  • Once a search completes, the map expands and the search result cursors allow you to scroll through the set of search results.

  • Each search result contains a link to a details page for the result as well as a link to get driving directions from the center point of the map to the current search result.

  • The clear button in the center of the cursor controls clears the current set of results and sets the control into the idle state.


The solution is designed for extreme ease of use. As a site designer you are able to control the center point location as well as a custom title and url. In addition, you can easily program a collection of “search links” that you can place on your page that when clicked, will drive the search control.


New Functionality Update


If you are already familiar with this solution, you might find the following new functionality helpful and useful.



Instructions for adding this to your site


Adding the solution is a simple three step process.


Step 1 - Load AJAX Search API, the Google Maps API, and the Map Search Solution

First you need to load the Google AJAX Search API, the Google Maps API, and the Map Search solution into your application. If you don't already have a Google AJAX Search API key, your first step is to sign up for a key . With your key in hand, add the following five lines of code to your page or blog template.


<!-- maps api, ajax search api, map search solution code --> <script src="http://maps.google.com/maps?file=api&v=2&key=YOUR-KEY" type="text/javascript"></script> <script src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=YOUR-KEY" type="text/javascript"></script> <script src="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.js" type="text/javascript"></script> <!-- ajax search stylesheet, map search stylesheet --> <link href="http://www.google.com/uds/css/gsearch.css" rel="stylesheet" type="text/css"/> <link href="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.css" rel="stylesheet" type="text/css"/>
Step 2 - Define a location on your page for the Map Search Control

The next step is to define a place on your page for the search control (note: you can have more than one). This is typically done by defining a named <div> element as we have shown below:


<div id="mapsearch">Loading...</div>

You might want to set some styling attributes on this element to constrain the width of the control, set a border or margin, etc. For example, a style rule like this might be useful.


#mapsearch { width : 400px; margin : 10px; padding : 4px; border : 1px solid #f9f9f9; }

In addition to this base style, the height of the idle state map and the active state map is easily modified.


/* set height of idle state map */ #mapsearch .gsmsc-idleMapDiv { height : 200px; } /* set height of active state map */ #mapsearch .gsmsc-mapDiv { height : 300px; }
Step 3 - Create a GSmapSearchControl and bind it to your page

The final step involves creating and configuring the control. The control is very powerful and offers several options. In this section, we will describe the simplest case of adding a control to your page or blog followed several examples that demonstrate more advanced concepts.


The first step is to actually get your code to run. The easiest way to do this is to either extend an existing body onload handler, or create a new one. Find the <body> element on your page and add an onload handler.


<body onload="OnLoad()">

Now write your onload handler by adding some code to an existing <script> block, or by creating a new one. Find the <body> element on your page and add an onload handler. The code snippet below demonstrates how to create a new map search control that will appear on your page inside the element named “mapsearch”. The map will center itself on Google corporate headquarters located at “1600 Amphitheatre Parkway, Mountain View, CA.”


<script type="text/javascript"> function OnLoad() { new GSmapSearchControl( document.getElementById("mapsearch"), // container "1600 Amphitheatre Parkway, Mountain View, CA", // center point null // options ); } </script>

Options


Notice in the above example, the value null is passed as the options argument, a Javascript object which is typically specified in object literal form. The following sections describe the possible values for options . These values support the following features:



  • Setting a special title and url for the map center point.

  • Specifying a set of hot spots on your page that will trigger searches


Setting a special title and url for the map center point

A special title and url can be programmed into the control and bound to the white center point marker of an active map. A typical use case for this feature might be to specify a conference name and conference url, or a company name and url. In order to request this functionality, the title and url properties of the options argument must be set. The following code snippet demonstrates this.


<script type="text/javascript"> function OnLoad() { // set title to the Googleplex and the link to // the Google corporate information page var options = { title : "Googleplex", url : "http://www.google.com/corporate/index.html" } // create the map search control new GSmapSearchControl( document.getElementById("mapsearch"), "1600 Amphitheatre Parkway, Mountain View, CA", options ); } </script>
Setting up hot spots

A common use case for this control is program hot spots on to your page thatwhen clicked will drive the search control. These hot spots might take the form of a list of recommendations, hotels, restaurants, etc. The search control supports this by allowing you to program a list of HTML elements and associated queries. When your users click on the element, the search control is activated with the specified query.


The options argument contains an optional hotspot property. This property is an array of objects, where each object contains an element property which is a reference to an HTML element on your page, as well as a query property which specifies a query to execute when the element is clicked.


The following code fragment demonstrates the use of this feature including the CSS declarations,HTML fragments, and options programming. If you prefer programming this on your own, perhaps by using <a> tags with javascript: urls, that's a perfectly reasonable way to proceed. We include this capability in the solution because for some of our users, this is a simpler mechanism that requires less of their time to develop. In the snippet that follows we are going to show sample CSS, HTML, and search control initialization logic.


/* define the CSS used to style the hotspots */ h4.hotspot { font-size : 100%; font-weight : normal; color : rgb(9, 122, 182); margin-left : 8px; margin-top : 0px; margin-bottom : 2px; font-style : normal; cursor : pointer; } h4.hotspot:hover { color : rgb(237, 92, 11); text-decoration : underline; } <!-- Define the HTML that places the hotspots on your page. Note, you can use any HTML element you like including li, div, etc. --> <div id="mapsearch">Loading...</div> <h3 class="hotspotheader">Recommendations</h3> <h4 id="hs01" class="hotspot">Caffeine</h4> <h4 id="hs02" class="hotspot">Thai Food</h4> <h4 id="hs03" class="hotspot">Pizza</h4> <h4 id="hs04" class="hotspot">Gym</h4> <h4 id="hs05" class="hotspot">Hotel Avante</h4> <h4 id="hs06" class="hotspot">Residence Inn</h4> <h4 id="hs07" class="hotspot">The Four Seasons</h4> <h4 id="hs08" class="hotspot">The Westin, Palo Alto</h4> <script type="text/javascript"> function OnLoad() { // Create an array of hotspots. Each entry contains and html element // from your page, and the query to execute when that element is clicked var hotspotsList = [ { element : document.getElementById("hs01"), query : "Starbucks" }, { element : document.getElementById("hs02"), query : "Amarin Thai" }, { element : document.getElementById("hs03"), query : "Frankie Johnnie & Luigi" }, { element : document.getElementById("hs04"), query : "Hotel Avante" }, { element : document.getElementById("hs05"), query : "Residence Inn" }, { element : document.getElementById("hs06"), query : "Four Seasons Palo Alto" }, { element : document.getElementById("hs07"), query : "Westin Palo Alto" } ]; // set title to the Googleplex and the link to // the Google corporate information page // set the hotspot list to the list above var options = { title : "Googleplex", url : "http://www.google.com/corporate/index.html", hotspots : hotspotsList } // create the map search control new GSmapSearchControl( document.getElementById("mapsearch"), "1600 Amphitheatre Parkway, Mountain View, CA", options ); } </script>
New! Setting Custom Center Icon

Applications can either accept the default center location icon, or they can supply their own GIcon and the system will use that Icon to mark the center point of the map.


The options argument contains an optional centerIcon property. When supplied, property must specify a valid GIcon . When specified, this icon is used as the center point marker for both the idle and active map.


The following code fragment demonstrates the use of this feature. It demonstrates the use of both a hand built icon, as well as the G_DEFAULT_ICON two calls:


// use the ridefinder, small yellow icon as the center point marker var ci = new GIcon(); ci.image = "http://labs.google.com/ridefinder/images/mm_20_yellow.png"; ... (rest of initialization omitted) new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", centerIcon : ci } ); // use the G_DEFAULT_ICON as the center point marker new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", centerIcon : G_DEFAULT_ICON } );
New! Setting Custom Selected and Unselected Icons

Applications are able to specify the icons for the currently selected search result as well as for all unselected search results using a similar model.


The options argument contains an optional selectedIcon property. When supplied, property must specify a valid GIcon . When specified, this icon is used as the marker for the selected search result.


The options argument contains an optional unselectedIcon property. When supplied, property must specify a valid GIcon . When specified, this icon is used as the marker for all other search results.


The following code fragment demonstrates the use of this feature:


// use the G_DEFAULT_ICON as the for selected and unselected search results new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", selectedIcon : G_DEFAULT_ICON, unselectedIcon : G_DEFAULT_ICON } );
Controlling the Zoom Level

Applications can control the Zoom Level for the idle map (displayed when no searches are active) as well as for the active map (displayed when a map is showing results). The system defines two constants that are helpful when configuring this aspect of the control:



  • GSmapSearchControl.IDLE_MAP_ZOOM - the default zoom level for the idle map

  • GSmapSearchControl.ACTIVE_MAP_ZOOM - the default zoom level for the active map


The options argument contains an optional idleMapZoom property. When supplied, this property specifies the zoom level for the idle map. A common use of this property is to have the idle map's zoom level match the default zoom level for the active map. In order to do this, use the constants described above to set idleMapZoom to GSmapSearchControl.ACTIVE_MAP_ZOOM .


The zoom level for the active map is controlled in a similar manner. The options argument contains an optional activeMapZoom property. When supplied, this property specifies the zoom level for the active map. Note: Applications can use these properties independently of each other.


The following code fragment demonstrates the use of this feature.


// set the idle map zoom so that it matches the // default active map zoom level new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", idleMapZoom : GSmapSearchControl.ACTIVE_MAP_ZOOM } ); // set the active map zoom level up by 1 new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", activeMapZoom : GSmapSearchControl.ACTIVE_MAP_ZOOM+1 } );
New! Enabling the Map Type Control

The default behavior of the maps created by this control is for them to not contain the GMapTypeControl , the control that allows switching between map, hybrid and satellite mode. This behavior is programmable allowing applications to cause the control to build maps with the GMapTypeControl available on all maps, or just on the active map.


The options argument contains an optional mapTypeControl property. When supplied, this property specifies which maps should be programmed to include the GMapTypeControl .



  • GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE - When this value is specified for the mapTypeControl property, the active map will contain a GMapTypeControl

  • GSmapSearchControl.MAP_TYPE_ENABLE_ALL - When this value is specified for the mapTypeControl property, both the active map and the idle map will contain a GMapTypeControl


The following code fragment demonstrates the use of this feature.


// enable a GMapTypeControl on the active map new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", mapTypeControl : GSmapSearchControl.MAP_TYPE_ENABLE_ACTIVE } ); // enable a GMapTypeControl on the all maps new GSmapSearchControl(container, "1600 Amphitheatre Parkway, Mountain View, CA", { title : "Googleplex", url : "http://www.google.com/press/factorytour.html", mapTypeControl : GSmapSearchControl.MAP_TYPE_ENABLE_ALL } );

The “Hello World” of GSmapSearchControl


The following page demonstrates a complete page which uses the GSmapSearchControl solution. You can start with this simple page, change internal to the value of your key and be up and running in seconds.


<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <title>GSmapSearchControl Sample</title> <!-- Note: Make sure to replace the &key=internal with &key=YOUR-KEY in both the maps API script load and in the Ajax Search API script load statements --> <!-- maps api, ajax search api, map search solution code --> <script src="http://maps.google.com/maps?file=api&v=2&key=internal" type="text/javascript"></script> <script src="http://www.google.com/uds/api?file=uds.js&v=1.0&key=internal" type="text/javascript"></script> <script src="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.js" type="text/javascript"></script> <!-- ajax search stylesheet, map search stylesheet --> <link href="http://www.google.com/uds/css/gsearch.css" rel="stylesheet" type="text/css"/> <link href="http://www.google.com/uds/solutions/mapsearch/gsmapsearch.css" rel="stylesheet" type="text/css"/> <style type="text/css"> body, table, p{ background-color: white; font-family: Arial, sans-serif; font-size: 13px; } #mapsearch { width : 400px; margin-left: 10px; padding: 4px; border : 1px solid #f9f9f9; } </style> <script type="text/javascript"> function OnLoad() { var options = { title : "Googleplex", url : "http://www.google.com/corporate/index.html" } new GSmapSearchControl( document.getElementById("mapsearch"), "1600 Amphitheatre Parkway, Mountain View, CA", options ); } </script> </head> <body onload="OnLoad()"> <div id="mapsearch">Loading...</div> </body> </html>

Examples


15 Things you can do with Yahoo! UI

write by :Dustin Diaz dustindiaz.com

Slicken up your web apps with these tips and tricks using the Yahoo! User Interface library

I will admit straight up front - I don’t like writing long articles. There, I said it. I passed College English convincing my professors that I should be rewarded more for my creativity rather than the length of my prose. I’m the kind of guy that spent more time writing short stories than huge complicated essays. Eventually I went on to write for the school Newspaper, a.k.a. The State Hornet which gave me the freedom to keep my stories to a minimum of 500 words, or just a couple of paragraphs.

Today that legacy still stands and has transferred into my love for making widgets. No, not those widgets. I’m talking about those little goofy things you do on a Sunday afternoon from when you had that idea on Saturday night which left you frustrated on Friday after work. Yes, you’re not the only one with crazy ideas.

I’m talking about sliders, yoyos, tooltips, shakers, better looking code, faster queries, and random jubilees of DOM hoopla. It’s those small things that some of us live to create and find it rewarding even if it’s one line of code. And although these little works of ‘code art’ don’t always work on all browsers while having labels of ‘experimental’ written all over them, don’t feel ashamed of them. They come from you and you should embrace your work. You are, a Web Professional.

Segue

In this article I’m going to share some of my short stories, poems, and random sonnets of affectionate escapades I’ve had with JavaScript during the last few months, which I’ve built using the Yahoo UI utilities. Some are rather embarrassing, others useful, and yet others I feel are just downright sexy. If you see something you like, feel free to take it, tweak it, and make it your own. This is what some poetry afficionados of secret societies would call a poetry slam. So for want of a better phrase, this can be a .

Why YUI?

The YUI utilities give me the expressive freedom to do what I want. And although comparing libraries is generally silly, the best illustration I can give is the Script.aculo.us Effects library vs the YUI Animation utility. Script.aculo.us offers you some pretty sweet looking effects like Fade, SlideUp, BlindDown, Shake, etc.. You can even add Parallel effects (way to go Thomas). On the flip side, the animation util doesn’t aim to give you these canned effects to package up with your web app, but rather it lets you make up your own effects by giving you the finely crafted tools (like a utility knife) to make what you want.

Another reason I like YUI is that it isn’t out to change the language itself, but rather it solves many of the cross-browser incompatibilities that we run into on a daily basis. Beyond that, it does quite a few other sexy things that I think you might like.

Code

Below is a compilation of fifteen things that I have either developed over the last few months, or have been inspired by a friend (who will receive full credit on the original idea). Enjoy!

DOM + The getElementBy’s

These days, when you’re working with the DOM, it’s all about getting the elements you want, with conditions applied. Thankfully the Dom util doesn’t go all out and try to do all the guesswork for you resulting in a bulky library with large sets of unwanted code. Instead it gives you a handy method for you to work with called getElementsBy.

YAHOO.util.Dom.getElementsByClassName

Let’s kick off our slam with the classic getElementsByClassName (which is actually in the utility by default). I like to think of it as our token example by the YUI folks showing off how getElementsBy can work natively within your functions. See Demo

getElementsByAttribute

This method is not in the Dom util by default, but demonstrates exactly how we can add it in if we wanted. See Demo

getElementsByExternal & getElementsByInternal

This function allows you to get outbound or inbound links returned to you as an HTMLElement collection. At that point it’s up to you what you want to do with your array of elements (eg. bind event listeners or run a method against them). Currently this is untested on IP address URIs, but I presume that won’t be an issue for most situations. See Demo

Random Effects of Love

Because everyone enjoys California cheese.

Pagination Slide Pattern

Paginating through large data sets and keeping someone’s attention can be a tedious task. Sliding results within a container is one way to keep a person entertained. See Demo

BlindUp & BlindDown (Yoyo)

These are classic effects that almost any animation lover would want in their toolkit. For the sake of making them a macro, here they are. See Demo

BlindOut

A demonstration of animating a box from any given corner. See Demo

Faux Mutation Events with CustomEvent

Most folk don’t know what mutation events are, mainly because they don’t work in IE6. These are events just like regular DOM events like ‘click’,'mouseover’, or ‘mouseout’ that you can listen for. Just to name a few there are ‘DOMNodeInserted’, ‘DOMNodeRemoved’, and ‘DOMNodeInsertedIntoDocument’. For a better reference you can see Wikipedia’s entry on DOM events and search for the Mutation events within the event table. Anyway, here’s how you can simulate some of those events using the CustomEvent class.

DOMNodeInserted

Create some elements, listen for the event, then make them draggable. See Demo

DOMNodeRemoved

Create some elements, then remove them to update an information box. See Demo

More with CustomEvents

There really is an endless amount of Custom Events that you can create. So we’ll keep this to a minimum. Here are three places that the CustomEvent can come in useful:

Extending the Drag and Drop

Currently, the Drag and Drop utility is packaged with quite a few custom events you can override such as startDrag, onDrag, or endDrag. However the problem is that you can’t subscribe to any of these, only override. Here is one trick you can use to make them subscribable. See Demo

onMenuOpen & onMenuCollapse

This is a combination of animation and custom events where we show menu items sliding into view and firing off subscribable events. See Demo

General Recipes

None of these have any rhyme or reason. But I tend to think that in some place or another, they can solve a problem.

Sweet Titles (YUI Style)

Here we have an OO version of Sweet Titles that allows us to declare multiple instances of tooltips on separate elements with some optional configuration parameters you can pass into the constructor object to get that exact look and feel you want out of a tooltip. See Demo

Faux Lightbox Effect

For the sake of doing something that’s already been done, here’s a faux light box effect that can be obtained using some core tools. See Demo

Photo Batching

YAHOO.util.Dom.batch alone may convince you of how cool the DOM Collection utility can be. This shows you how you can batch an HTML Element collection to a method. (Thanks Matt Sweeny) See Demo

Unobtrusive new windows links

For some reason or another, this always seems to create some noise regardless of its controversial nature. Here is a very simple way to do it with Event, DOM, and some of the tools we’ve defined in this very article. See Demo

Layout Switching with Grids

This is a combination of using the CSS Grids toolkit and swapping out the templates using JavaScript animation See Demo

That’s All Folks

At least for this article’s sake. There are plenty of other things you can making using the Yahoo! UI utilities and this has only scratched the surface. I hope it was as fun for you as it was for me.

Tuesday, November 20, 2007

Application logic versus template logic

By Quentin Zervaas, 1 May 2006


This article takes a look at separating application logic from template logic. We will use the Smarty Template Engine as the basis for the article and its examples.


What this is basically saying is that anything relating to the final output should be determined in the template, while any data handling or processing should be done in your PHP files and classes. There's a little more to it than though, which we'll soon see.


An important aspect of this is that it works both ways. Often people focus on making sure their templates don't do anything application related, but then make the mistake of making their PHP scripts (or classes) doing work that their templates should be doing. We will look at both situations and common issues that arise.


What is application logic?


Application logic (also called business logic) is any code that deals with processing of data. That is, the processing behind the scenes. Common examples of this include:



  • Processing the input from a web form

  • Saving data to a database

  • Debiting money from a credit card



  • Reading or writing cookies on the client's computer

    This even includes instantiating the Smarty class and displaying a template:


    Highlight: PHP


    <?php $smarty = new Smarty () ; $smarty -> display ( ' index.tpl ' ) ; ?>

    It also includes generating data that may be used in your template logic, but we'll look at that in a little while.


    What is template logic?


    Template logic (also called display logic) is any code in your templates that determines how things are displayed. For instance, if you output a certain message if a string is empty, or the string if it isn't empty, then this is template logic. Here's an example:


    Highlight: Smarty Template


    {if $name|strlen == 0) < p > You didn't enter your name! < p > {else} < p > Hello {$name} </ p > {/if}

    Formatting of a string is also template logic (although there may be exceptions, depending on the data). For example:


    Highlight: Smarty Template


    < p > Title: {$title|strtoupper} Summary: {$summary|truncate} </ p >

    We'll look at some more examples of application logic and template logic shortly, for now, we'll look at things that aren't application logic and aren't template logic.


    Application logic in templates—bad!


    Smarty allows developers to directly embed PHP code by using the {php} {/php} tag. Personally, while it's good to have the functionality available, I think it's almost always a bad idea to ever use this. It is also possible to use any declared PHP function (builtin or user defined) as a modifier (unless you have template security turned on).


    An example would be to directly access some data from a form and sanitize it in the template:


    Highlight: Smarty Template


    < p > This is bad, but you entered {$smarty.get.name|strip_tags|trim}. </ p >

    The way this should be done is done in the PHP file, and then passing the sanitized value to the template:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; $name = trim ( strip_tags ( $_GET [ ' name ' ])) ; $smarty = new Smarty () ; $smarty -> assign ( ' name ' , $name ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

    And then the template would look like:


    Highlight: Smarty Template


    < p > This is good. You entered {$name}. </ p >

    Examples of acceptable logic in templates


    So now that I've covered a few bad things, what kinds of things are acceptable? Here are some examples.


    Checking a string value or boolean flag (or string length, etc.) to determine what to output:


    Highlight: Smarty Template


    {if $name|strlen > 8} < p > Your name is long! </ p > {/if} {if $male} < img src = " male.gif " /> {else} < img src = " female.gif " /> {/if}

    Alternating background colours


    Actually, this is made easy in Smarty with the {cycle} function:


    Highlight: Smarty Template


    < table > < tr > < th > Field 1 </ th > < th > Field 2 </ th > </ tr > {foreach from=$data item=row} < tr style = " background: {cycle values='#ccc,#ddd'} " > < td > {$row.field1} </ td > < td > {$row.field2} </ td > </ tr > {/foreach} </ table >

    Displaying a running count


    This is also made easy in Smarty, using the {counter} function.


    Highlight: Smarty Template


    {foreach from=$items row=item} < p > Item number {counter}. {$item} </ p > {/foreach}

    Including another template


    This will be required frequently, and as we point out next, the alternative to doing this is quite bad!


    Highlight: Smarty Template


    {include file='header.tpl'} < p > Example of including a header and a footer. </ p > {include file='footer.tpl'}

    Template logic in applications


    There are several situations where you may incorrectly end up using template logic in your application without even realising.


    Case 1: displaying multiple template files


    Take a look at this PHP code to output a single page:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> display ( ' header.tpl ' ) ; $smarty -> display ( ' example.tpl ' ) ; $smarty -> display ( ' footer.tpl ' ) ; ?>

    This is extremely bad! You have essentially hardcoded in your application that your templates are built by displaying a header, then the main content, and then the footer. The fact that your templates are split into 3 templates like this is a function of the templates, not of the application.


    The correct way to do this is:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> display ( ' example.tpl ' ) ; ?>

    And then the example.tpl file would like:


    Highlight: Smarty Template


    {include file='header.tpl'} < p > Example of including a header and a footer. </ p > {include file='footer.tpl'}

    Case 2: Determining a style or an image in code and then assigning it to a template


    Let's assume you're outputting a document and you want to show an image that says ‘new' if it was submitted today, or an image that says ‘old' if it was submitted earlier.


    It would be bad to specify this image in the code like so:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; if ( $article -> isNew ) $image = ' new.gif ' ; else $image = ' old.gif ' ; $smarty = new Smarty () ; $smarty -> assign ( ' image ' , $image ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

    Even worse would be:


    Highlight: PHP


    <?php if ( $article -> isNew ) $image = ' <img src="new.gif" alt="New" /> ' ; else $image = ' <img src="old.gif" alt="Old" /> ' ; ?>

    It seems obvious, but I have seen it done. The correct way would be to pass the flag to the template and in the template determine which image to use:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> assign ( ' isNew ' , $article -> isNew ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

    And the template:


    Highlight: Smarty Template


    {if $isNew} < img src = " new.gif " alt = " New " /> {else} < img src = " old.gif " alt = " New " /> {/if}

    Afterall, the image is tied to display. You might change template sets or image sets, resulting a new filename. You should only have to change your templates, not your application code.


    Exceptions


    There are always going to be exceptions where you may need to put application logic in a template, or some circumstances that sit right on the fence. Here's an example:


    Highlight: Smarty Template


    < input type = " text " name = " title " value = " {$title|escape} " />

    In this situation, to deal with the fact a user might enter a quotation mark (”), we must escape the data. You could argue that this is application logic, but personally, I think this is directly related to the output of your HTML document, and so should be escaped when it is output.


    Another exception I can think of would be if you wanted to list news articles in your template, but you're not sure how many. That is, the number of articles shown is a function of the template. In one layout you might have room to fit three articles, but if you change templates, you might have room for 5. So how many articles do you assign?


    The only way to cover all bases would be to get every single article and assign them all to the template. This seems somewhat excessive. The way I have dealt with this in the past is to use a function call with the number of articles. This function directly calls the database, which as we have said is therefore application logic, but sometimes there's no easier way around things:


    Highlight: Smarty Template


    {foreach from=$news->getArticles(5) item=article} < h2 > {$article.headline} </ h2 > {/foreach}

    Common sense


    You must use your own judgement when determining what should be in code and what should be in template. Take a look at this example of checking for an empty array:


    Highlight: Smarty Template


    {assign var='numElements' value=$myArray|@count} {if $numElements == 0} < p > The array is empty. </ p > {else} < p > There are {$numElements} elements. </ p > {/if}

    Strictly speaking, you should determine the count in PHP, and perhaps even a flag to check if it's empty:


    Highlight: PHP


    <?php require_once ( ' Smarty.class.php ' ) ; $myArray = array ( 1 , 2 , 3 ) ; $numElements = count ( $myArray ) ; $isEmpty = $numElements == 0 ; $smarty = new Smarty () ; $smarty -> assign ( ' myArray ' , $myArray ) ; $smarty -> assign ( ' numElements ' , $numElements ) ; $smarty -> assign ( ' isEmpty ' , $isEmpty ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

    And the example.tpl file:


    Highlight: Smarty Template


    {if $isEmpty} < p > The array is empty. </ p > {else} < p > There are {$numElements} elements. </ p > {/if}

    Personally, I think the second method is overkill and I would always use the first method. The additional problem is that we don't necessarily know in the script what data the template requires.


    The fact that we do something in the template for an empty array is display specific, not something that needs to be known in the application.


    Conclusion


    In this article I explained the difference between application logic and template logic when using Smarty. Hopefully you found some simple ways to improve your template design, but also recognised that some complexities can arise when doing this and sometimes comprimises must be made.


    One (simplified) way to look at it is like this:


    You should be able to completely change templates, including all image files and CSS files, without having to touch any of your PHP files.


    All fundamental data required in your templates should be fetched or determined ahead of time and assigned to your templates (this doesn't include meta data, such as the length of a string, etc.).


    Further reading



  • Smarty Template Engine


URL Rewriting with ASP.NET 2.0

Introduction

In this article, you will learn about URL Rewriting in ASP.NET 2.0. URL Rewriting was originally introduced by Apache as an extensions called mod_rewrite. The concept of URL rewriting is simple. It allows you to rewrite URL from those ugly URL into a better URL and hence it will perform better in SEO.

Most Search Engines will ignore those dynamic URL such as the one ended with querystring
e.g http://www.worldofasp.net/displayproduct.aspx?ID=10
Therefore if you like to have more hits and traffic from search engine, consider of rewriting all those querystring url into normal URL.

If you rewrite the URL above, you can actually rewrite it into more readable format
e.g http://www.worldofasp.net/product10.aspx.
or you can rewrite it to http://www.worldofasp.net/product/10.aspx.

Search Engine robot will think that your dynamic page is a normal page and therefore it will crawl your page and your page will have a better search results.
If you check all the page in Worldofasp.net or CodeProject.com, you can see that the web developer is using URL rewriting techniques.

Main

Microsoft .NET Framework 2.0 come with limited URL Rewriting Library support and you have to write your own URL Rewriting engine if you need complex URL rewriting for your website.

The simplest URL Rewriting that you can achieve in seconds is by copy and paste the code below to your global.asax file.

void Application_BeginRequest(Object sender, EventArgs e)
{
String strCurrentPath;
String strCustomPath;
strCurrentPath = Request.Path.ToLower();

if (strCurrentPath.IndexOf("ID") >= 0)
{
strCustomPath = "/Product10.aspx";
// rewrite the URL
Context.RewritePath( strCustomPath );
}
}

As you can see from the code above, the URL Redirection is only limited to one rules. It will always redirect to one page called product10.aspx if it detects that your URL contains ID keyword.
Of course this one will not work if you want different Query String ID to redirect to different page or if you want to have different page redirection for different query string type.

To have a more complex URL Rewriting Rules, we can use Regular Expressions for implementing different redirection techniques.

Now, lets start writing some rules for your Redirection.



product/(.*)\.aspx
displayProduct.aspx?ID=$1


Items/Mouse/(.*)\.aspx
ViewItem.aspx?ID=$1




As you can see from the first rules above, It will redirect the URL from product/(Number).aspx into displayProduct.aspx?ID=(Number).
Second rules will redirect the URL if it Contains Items/Mouse/(Number).aspx into ViewItem.aspx?ID=(Number). You can add as many rules as you like by just adding the xml node above

void Application_BeginRequest(Object sender, EventArgs e)
{
string sPath = Context.Request.Path;
//To overcome Postback issues, stored the real URL.
Context.Items["VirtualURL"] = sPath;
Regex oReg;
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Server.MapPath("/XML/Rule.xml"));
_oRules = xmlDoc.DocumentElement;

foreach (XmlNode oNode in _oRules.SelectNodes("rule"))
{
oReg = new Regex(oNode.SelectSingleNode("url/text()").Value);
Match oMatch = oReg.Match(sPath);

if (oMatch.Success)
{
sPath = oReg.Replace(sPath, oNode.SelectSingleNode("rewrite/text()").Value);
break;
}
}
Context.RewritePath(sPath);
}

The code above is self explanatory. It will search from the rules in XML file and if it match, then it will rewrite the path.
Thats all you need to do to implement URL Rewriting. However after you rewrite the URL, all your page postbacks will not work. The reason is because, ASP.NET will automatically by default output the "action" attribute of the markup to point back to the page it is on. The problem when using URL-Rewriting is that the URL that the page renders is not the original URL of the request . This means that when you do a postback to the server, the URL will not be your nice clean one.

To overcome this problem, you need to add this code on every page that you want to do postbacks.

protected override void Render(HtmlTextWriter writer)
{
if (HttpContext.Current.Items["VirtualURL"] != null)
{
string sVirURL= HttpContext.Current.Items["VirtualURL"].ToString()
RewriteFormHtmlTextWriter oWriter = new RewriteFormHtmlTextWriter(writer,sVirURL);
base.Render(oWriter);
}

}

Source Code for RewriteFormHtmlTextWriter

public class RewriteFormHtmlTextWriter : HtmlTextWriter
{
private bool inForm;
private string _formAction;

public RewriteFormHtmlTextWriter(System.IO.TextWriter writer):base(writer)
{

}
public override void RenderBeginTag(string tagName)
{
if (tagName.ToString().IndexOf("form") >= 0)
{
base.RenderBeginTag(tagName);
}
}
public RewriteFormHtmlTextWriter(System.IO.TextWriter writer, string action)
: base(writer)
{

this._formAction = action;

}
public override void WriteAttribute(string name, string value, bool fEncode)
{
if (name == "action")
{
value = _formAction;
}
base.WriteAttribute(name, value, fEncode);
}

}

That's all you need to implement a complex and fully extensible URL Rewriting. You can create unlimited rules into your Rules.xml file and redirect to the page you want.

Conclusion

In this article, you can see how easy it is to implement URL Rewriting for your website. You can do enhancements and improve the workflow of the code above. For faster execution of your rules, you can also stores the Rules.xml files as Caching objects so it will save lots of time compare to open and close the xml files everytime the rewrite happens.

About Me

Ordinary People that spend much time in the box
Powered By Blogger