PHP array_walk_recursive with closure

Using PHP’s new support for closures/anonymous functions is particularly helpful with array functions that otherwise would require a separate callback function. One recent example where I used this is to grab a simple array of user_id=value from a nested array. (Values shown below are somewhat simplified.)

It does seem to be true that this strategy is most useful when the callback function you define is used on multiple arrays and has some level of complexity. One advantage of this is that it does use a little less memory by only keeping the global callback function registered while it’s being used.

If it wasn’t a recursive array, you could simply use array_walk, or even a foreach loop. My guess is the real-world performance would be about the same in most cases, and while the example below is simplified, I would like to see some cases where a closure or anonymous function is a dramatic improvement over previous methods.

$people_ids = array();
$callback = function ($value, $key) use (& $people_ids) {
    if ($key == 'people_id') {
        $people_ids[] = $value;
    }
};
array_walk_recursive($people_list, $callback);
echo '<pre>$people_ids: '; print_r($people_ids); echo '</pre>';

see below for a before and after example of this.

view example

$people_list =array();
$people_list[0] = array(
	'people_info' => array('people_id' => 1),
	'first_name' => 'FirstName',
	'last_name' => 'LastName',
	'job_title' => 'Designer',
	'department_id' => 2
);
$people_list[1] = array(
	'people_info' => array('people_id' => 3),
	'first_name' => 'FirstTwo',
	'last_name' => 'LastTwo',
	'job_title' => 'JobTitle',
	'department_id' => 1
);
// result:
$people_ids Array
(
    [0] => 1
    [1] => 3
	...
)

also see here and for more about array_walk vs array_map, see here

PHP sanitizing filters

Pretty cool built-in PHP filters. I wouldn’t 100% rely on them for sensitive input and I’m not sure how expensive they are, but certainly makes a more consistent interface for dealing with input:

http://php.net/manual/en/filter.filters.sanitize.php

use with filter_var, filter_input, etc.

Redis vs Memcached + PHP Libs for Redis

I like the looks of Redis as a fast utility that falls somewhere between a Sql DB and memory cache.

Here is one comparison with Memcached, Redis vs Memcached
the intro: take memcached and add a disk backing, replication, virtual memory, and some cool additional data structures. Hashes, lists, sets, sorting, joining, transactions, yay! I instantly got a geek boner scanning the feature list… (His enthusiasm goes, um, down from there.)

And if you do want to use it with PHP, see Ori Pekelman’s blog:
Which PHP Library to use with Redis? The Benchmark
Basically comes down to using a pure-php library or one that uses a PHP-compiled module and the trade-offs of compatibility vs speed.

To use it with Yii, see: rediscache

init.d script for redis

Getting APC to work on OSX 10.6

If APC won’t compile manually or with PECL, complains of “pcre error: blah blah” or similar, it means there is a problem with the PCRE libs on your Mac. Either pcre.h is missing or not in your path or some other conflict.

I found this a while back and it still seems to work:

“While not a solution, by soft-linking to the alternate pcre.h location (e.g., “ln -s /opt/local/include/pcre.h /usr/include/php/ext/pcre/”), I was able to get it to compile.”

The “opt” location is the MacPorts version of PCRE. I also was able to get it to compile simply by copying the MacPorts version of the pcre header file (pcre.h) to /usr/include/php/ext/pcre/pcre.h (as there wasn’t one there) and ran the PECL install with no problems. On a prior install I linked to the macports pcre directory, see below, to compile from source.

After copying or soft-linking the pcre.h file:
Try PECL first:

pecl install APC

If compiling APC from source:

./configure --enable-apc --enable-mmap --with-pcre-regex=/opt/local/include \
--with-pcre-dir=/opt/local/include

Yii admin form drop-downs – with Ajax and other examples

Yii’s admin forms are great way to get a quick start on a project that requires admin tools. It would be great if there was some way to automatically create drop-downs for related tables, especially if they already have foreign keys. Perhaps someone has made an extension, but I haven’t found one.

In any case it’s pretty easy to add a drop down, below are some examples, including an Ajax form that updates another value via Ajax onchange.

Yii drop-down with custom sort:

<div class="row">
  <?php echo $form->labelEx($model,'table_name_id'); ?>
  <?php echo $form->dropDownList($model, 'table_name_id', 
    CHtml::listData(table_name::model()->findAll(
      array('order'=>'t.name_admin ASC')
    ), 'id', 'name_admin'),
    array('prompt'=>'Select Name...')
  ); ?>
  <?php echo CHtml::error($model,'table_name_id'); ?>
</div>

with ajax call onchange:
this example changes the entire contents of one drop-down (feature category) based on the language selected:

<div class="row">
  <?php echo $form->labelEx($model,'language_id'); ?>
  <?php echo $form->dropDownList('feature[language_id]', $lang_id,
    CHtml::listData(language::model()->findAll(
      array('select'=>'name, id','condition'=>'active = "y"')
    ), 'id', 'name'),
    array(
      'ajax' => array(
        'type'=>'POST', //request type
        'url'=>$this->createUrl('feature/herdcats'), //url to call
        'update'=>'#feature_category_id', //selector to update
        'data'=>array('feature[language_id]'=>'js:$(\'#feature_language_id\').val()'),
        //'data'=>'js:javascript statement' 
        //leave out the data key to pass all form values through
        // another option to update or replace (will supersede those):
        // 'success'=>' function(data) { $(\'#feature_category_id\').val(data) }',
      )//,'confirm' => 'foo',//, 'onchange' => 'alert("foo2")',
    )
  ); ?>
  <?php echo $form->error($model,'language_id'); ?>
</div>

controller action for above:

	public function actionHerdcats()
	{	
		// ajax function to retrieve categories by country:
		$lang_id = (!empty($_POST['feature']['language_id'])) ? $_POST['feature']['language_id'] : DEFAULT_LANG_ID;
 
		$data = category::model()->findAll('language_id=:parent_id', 
					  array(':parent_id'=>(int) $lang_id));
 
		$data = CHtml::listData($data,'id','name');
		if (!empty($data)) { echo '<option value="">Select Category...</option>'; }
		foreach($data as $value=>$name) {
			echo CHtml::tag('option',
				array('value'=>$value),CHtml::encode($name),true);
		}
	}

manual data drop-down:

<?php echo $form->dropDownList($model, 'status', 
  array(
    '1'=>'active',
    '0'=>'inactive'
  ) ); ?>

check for condition before running action in a controller:

protected function beforeAction($action)
{
  if (parent::beforeAction($action)) {
    //check GET parameters
    if (in_array($action->id, array('update','delete'))) {
      if (empty($_GET['id']))
        throw new CHttpException(404,'Not found');
     }
     return true;
  } else
    return false;
}

Next Entries »