Archive for the 'PHP' Category

My two cents on Static Typing

Wednesday, May 16th, 2007

While I was wondering if using haXe or Java with my server, I’ve stumbled upon some discussion on Static Typing (Java) vs. Dynamic Typing (Ruby, Php).
In a really interesting blog post (read the comments too) I found this touching phrase:

“Static typing is Communist Bureaucracy”.

In short: since we (should) have pervasive testing, Static Typing is only more testing. However, a comment is enlighting:

I’m finding this logic hard to follow. Testing is good, coverage is good. Static typing is, among other things, a way to have the compiler test your code for type issues. Since we like testing, this makes static typing good, right?

But Static Typing not only prevent you to do mistakes, like typos, it allows your IDE to know what you are doing:

Dynamic Static Typing speeds up development because there is physically less to type since ctrl+space and ctrl+2 write the declarations for you.

Its seems obvious to me that Dynamic Typing is worth the catch only for a quick start or for learning.
Since almost all of the dynamically typed languages (like Actionscript, Perl, Php) have migrated to some kind of Static Typing (eg: Type Inference), there should be a reason.

Periodically, a new Dynamically Typed language spawn, attracting junior/tired programmers with the mirage of the simplicity and the less-to-type-ing, riding the hype until his young audience grows and start doing large projects hoping not to call a function with the wrong arguments.

Only then they realize the big truth: Static Typing is just more informations in the source code.
And more information means less wondering-what-object-was or what-was-the-method-name-and-parameters or who-instantiate-this-object, not to mention static analysis.

And hey, they have invented Javadoc-like documentation and Annotations just to add some more information, why remove one of the most important?

A better print_r: dBug

Friday, March 2nd, 2007

dBug is a php library that print a nice-formatted dump of a variable:

PHP:
  1. include_once("dBug.php");
  2.  
  3. $variable = array(
  4.     "first"=>"1",
  5.     "second",
  6.     "third"=>array(
  7.         "inner third 1",
  8.         "inner third 2"=>"yeah"),
  9.     "fourth");
  10.  
  11. new dBug($variable);

Will be formatted like this:
dBug

Variable types supported are: Arrays, Objects, Recordsets and XML Resources.

Simple CakeAMFPhp How To

Wednesday, November 8th, 2006

It's been a while since I last wrote, sorry! I've been busy with work, thesis and some other suffs (which I'll be posting about in the next days).
One of those is AMFPhp: a method to connect Flash with Php.
Why AMF instead of JSON or XML? Basically because AMF is more mature than JSON, offer a ready-made and really complete set of classes, many utilities like the connection debugger component, a great Php implementation, and, above all, CakeAMFPhp. (AMF is also a binary protocol on top of HTTP, so performance should be better than XML)
CakeAMFPhp is the integration of AMFPhp and Cake, a Rails-inspired php framework. With Cake I've quickly build a prototype with the Bake command-line utility. Then I've started implementing the methods for the AMF calls. Thanks to the great database abstraction, every method is only few lines long.

Since CakeAMFPhp is a beta, I've found some problems, for which I've found a quick fix that I'll explain shortly.

To start a CakeAMFPhp project, simply download the required libraries as explained in this tutorial. (If you are new to Cake you should also see this)
Then in your controller, for example GalleriesController, put this:

PHP:
  1. vendor("cakeamfphp/amf-core/util/MethodTable");
  2. vendor('cakeamfphp/amf-core/adapters/lib/Arrayft');
  3. vendor('cakeamfphp/amf-core/adapters/lib/Arrayf');
  4.  
  5. class GalleriesController extends AppController
  6. {
  7. ...
  8. function GalleriesController()
  9. {
  10.   //AMF:
  11.   $this->methodTable = MethodTable::create(__FILE__);
  12.   parent::__construct();
  13. }

In the line 11 we call the automatic method table constructor of AMFPhp: it read the javadoc-like comments in the file and do all the magic:

PHP:
  1. /**
  2. * @desc Return the list of public galleries
  3. * @access remote
  4. * @pagesize 25
  5. */
  6. function getGalleries($offset = 0, $limit = 25) {
  7.   $data = $this->Gallery->findAll(null, null, 'created DESC', $limit, $offset, 0);
  8.   return $this->_arrayft($data, 'Gallery');
  9. }
  10.  
  11. function getGalleries_count() {
  12.   return $this->Gallery->findCount();
  13. }

The @access remote tag specify that the getGalleries method is callable from flash:

Actionscript:
  1. var ser = new Service("http://localhost/cake_gateway.php", null, 'GalleriesController', null , null);
  2. var pc:PendingCall = ser.getGalleries();
  3. pc.responder = new RelayResponder(this, "handleResult", "handleError");

The getGalleries_count() method is needed for the pageable recordset:

Actionscript:
  1. function handleResult (re:ResultEvent) {
  2.   var rs = RecordSet(re.result);
  3.   rs.addEventListener('modelChanged', this); //Listen for updateItems
  4.   this.totalItems = rs.getLength();
  5.  
  6.   for(var i:Number=startFrom; i <l; i++) {
  7.     item = rs.getItemAt(i);
  8.     if(i <rs.getNumberAvailable()) { //It is already fetched
  9.     ... //Process a row
  10.   }
  11. }

getGalleries return only the first 25 row from the database. When the data is loaded in flash, rs.getLength() method return the value from getGalleries_count(). getItemAt return an item or a future, we check if the item is loaded in the line 8. If the item isn't from the ones loaded, it is automatically requested from the server when we call getItemAt. When it arrives, the modelChanged method is called:

Actionscript:
  1. function modelChanged (info:Object) {
  2.   var rs = this.rs;
  3.   var item:Object;
  4.   if(info.eventName == "updateItems") {
  5.     for(var i:Number=info.firstItem; i <= info.lastItem; i++) {
  6.       item = rs.getItemAt(i);
  7.       target.addItem(item);
  8.     }
  9.   }
  10. }

in the info object there are the rows received, you can configure Flash to get only the item required or the page containing it.
That's all, simply add the @access remote tag, a *methodname*_count method and it works.
You can find more information on pageable recordsets here.

The _arrayft is required for the issue I've talk about before: CakeAMFPhp 0.6 does not support sending recorset to flash, so this method is required to transform the data returned by the Cake database abstraction layer into a AMF-compatible recordset:

PHP:
  1. function _arrayft($data, $table) {
  2.   $r = array();
  3.   if(!empty($data[$table])) {
  4.     //Multi table query:
  5.     if(is_array($data[$table][0])) {
  6.       //Nested table:
  7.       foreach($data[$table] as $t) {
  8.         $r[] = $t;
  9.       }
  10.     }
  11.     else {
  12.     //Sigle record:
  13.       foreach($data[$table] as $t) {
  14.         $r[] = $t;
  15.       }
  16.     }
  17.   }
  18.   else {
  19.     foreach($data as $t) {
  20.       array_push($r, $t[$table]);
  21.     }
  22.   }
  23.   return new Arrayf($r, array_keys($r[0])  );
  24. }

For performance reason, it is suggested to use a 0 'recursive' value in the find(..) call.

Some useful tips on Flash, caching and Php

Wednesday, September 13th, 2006

Making the test for some strange flash cache behaviour I've learned some useful tips:

You can be sure a movie will be loaded appending a query string to the file name:

Actionscript:
  1. loadMovie("clip.swf?123", target);

 

You can force the user to reload a movie when you release a new version:

Actionscript:
  1. clipname= "cache"+VERSION+"-clip.swf";
  2. loadMovie(clipname, target); //Loaded
  3. //After the first load the movie is cached:
  4. loadMovie(clipname, target2); //Cached

And use a .htaccess file (ModRewrite required):

Apache:
  1. RewriteEngine on
  2. RewriteCond %{REQUEST_URI} cache([0-9]+)-(.*)\.swf(.*)$
  3. RewriteRule ^cache([0-9]+)-(.*)\.swf(.*)$ $2\.swf [L]

Only when you change the VERSION variable (pass it with flashVars) the movie will be reload, otherwise it will be loaded from the browser cache.
 

You can make cacheable a file downloaded through php:

PHP:
  1. header('Last-Modified: '.date( "D, j M Y G:i:s \G\M\T", filemtime($filename) ));

Be sure to set the correct content type:

PHP:
  1. $filename= "clip.swf";
  2. $fp = @fopen($filename,"r");
  3. if($fp){
  4.     header('Last-Modified: '.date( "D, j M Y G:i:s \G\M\T", filemtime($filename) ));
  5.     header('Content-Length: '.filesize($filename));
  6.     header('Connection: close');
  7.     header('Content-Type: application/x-shockwave-flash');
  8.  
  9.     while (!feof($fp)){
  10.         echo(fgets($fp, 4096));
  11.         ob_flush();
  12.     }
  13.     fclose($fp);
  14. }
  15. else
  16. {
  17.     header("HTTP/1.0 404 Not Found");
  18. }

Without Last-Modified or ETag headers flash doesn't cache the files.