Tobiasz Cudnik

Posts Tagged ‘closures’

Extending QueryTemplates with closures

In QueryTemplates, phpQuery on 25.01.2009 at 14:24

Lastest changes in phpQuery allows us to extend it much more easily. One of the ways to do this is directly same as in jQuery, using phpQuery::extend() method. When you combine this with upcoming closures in PHP 5.3, you will feel like-almost-in-browser. Take a look at this:

$source = dirname(__FILE__).'/table.html';
$rows = array(
	array(
		'field-1' => 'foo1',
		'field-2' => 'foo2',
	),
	array(
		'field-1' => 'bar1',
		'field-2' => 'bar2',
	),
);
// this will work in PHP 5.3
// this code will evaluate on onLoad event, thus is cache firendly
$onload = function() {
  $tableFirstRowClass = function($self, $var, $classname = 'first') {
    return $self->addClassPHP("
      if ($$var == 0)
        print '$classname';
    ");
  };
  // here we pass new closure into extend method, just like in jQuery
  // compact() creates array for us
  phpQuery::extend('phpQueryObject', compact('tableFirstRowClass'));
};
require template('table')->parse($source)
  // just bind it
  ->bind('onload', $onload)
  // just fire it up ;)
  ->trigger('onload')
  ->find('table > tr, table > * > tr')
    ->loopOne('rows', 'i', 'row')
      ->varsToSelector('row', $rows[0])
      // now you can use this just-like-this
      ->tableFirstRowClass('i')
;

But for now to extend phpQuery on-the-fly we have to use create_function or delegate existing one.

We can also use new Scripts plugin feature, which does almost the same. Just like all Scripts’ plugin script files, newly attached function will have all available variables. Example below works in PHP < 5.3:

function onload() {
  // in PHP 5.3, this can be done simply like this
//  phpQuery::script(...
  // but for now we have to use fake phpQuery::$plugins namespace
  phpQuery::$plugins->script('tableFirstRowClass', create_function(
    '$self, $params, &$return, $config', '
    $className = ! $params[1] ? "first" : $params[1];
    $self->addClassPHP("
      if ($$params[0] == 0) print '$className';
    ");
  '));
};
$onload = 'onload';

One major difference between extending Scripts and phpQuery itself is way of using new functions. With our new script, we have to small change to main code.

// using phpQuery::extend()
//->tableFirstRowClass('i')
// using Scripts extend
->script('tableFirstRowClass', 'i')

Having fun using PHP 5.3 closures with phpQuery

In phpQuery on 08.01.2009 at 16:34

Some time ago i’ve wrote about new PHP 5.3 closures feature. Today i would like to show you it in action with phpQuery. If you’re using jQuery you will feel like in home :)

First example illustrates classic inline function, which is used to iterate over li nodes, incrementing each one’s content.

$markup = '
<ul>
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul>
';
$doc = phpQuery::newDocument($markup);
$doc['li']->each(function($node){
	pq($node)->text(
		pq($node)->text()+1
	);
});
print $doc;

Result will be someting like this (something because i’ve corrected indentation manually).

<ul>
	<li>2</li>
	<li>3</li>
	<li>4</li>
</ul>

Now more complicated stuff – scope inheritance. Scope inheritance means nothing else than ability to use variables declared outside code block (inline function in this case) inside this particular block. In JavaScript we have full inheritance right away. In PHP 5.3 we have to explicitly declare which variable we would like to inherit. It’s done by use keyword.

$markup = '
<div>
	<span>1</span>
	<span>2</span>
	<span>3</span></div>
<div>
	<span>1</span>
	<span>2</span>
	<span>3</span></div>
';
$doc = phpQuery::newDocument($markup);
$doc['div']->each(function($div){
	$div = pq($div);
	$div['span']->each(function($span) use ($div){
		pq($span)->insertBefore($div);
	});
});
print $doc;

We’ve nested one closure inside another. The inner one inherits $div variable from the outer. Lack of such feature for create_function was really problematic. Below you can see the result. Both divs are now empty.

<span>1</span>
<span>2</span>
<span>3</span>
<div></div>
<span>1</span>
<span>2</span>
<span>3</span>
<div></div>

Third example i want to show is about assigning inline function to a variable. It’s handful technique to avoid namespace collisions and of course allow easily pass closure thou the parts of code.

$markup = '
<div>1</div>
<div>2</div>
<div>3</div>
';
$callback = function($node){
	$node = pq($node);
	$node->text(
		'Callbacked: '.$node->text()
	);
};
print phpQuery::newDocument($markup)
	->find('div')->each($callback)->end();

Just how you suspect, every node’s content will be prefixed with “Callbacked: “.

<div>Callbacked: 1</div>
<div>Callbacked: 2</div>
<div>Callbacked: 3</div>

I think that this post clearly illustrates how closures work and that they are important for PHP as web development language.

If you would like to test code from this post and PHP 5.3 in general, download windows build from php.net or compile source yourself for other platform. CLI version will be enough, that means no apache module struggling.

PHP 5.3 introduces closures and inline functions

In The Net on 25.11.2008 at 0:31

I couldn’t believe it in first place, but there are ongoing works to bring closures power to PHP in upcoming 5.3 version! No more create_function() and backslashing backslashed quotes.

There are also some scope inheritance tries (using “use” keyword). Below you have some teasing snippet, after which head to wiki.php.net/rfc/closures for more…

function replace_in_array ($search, $replacement, $array) {
  $map = function ($text) use ($search, $replacement) {
    if (strpos ($text, $search) > 50) {
      return str_replace ($search, $replacement, $text);
    } else {
      return $text;
    }
  };
  return array_map ($map, $array);
}