Pass extra parameters to usort callback

Solution 1:

I think this question deserves an update. I know the original question was for PHP version 5.2, but I came here looking for a solution and found one for newer versions of PHP and thought this might be useful for other people as well.

For PHP 5.3 and up, you can use the 'use' keyword to introduce local variables into the local scope of an anonymous function. So the following should work:

function sort_by_term_meta(&$terms, $meta) {
    usort($terms, function($a, $b) use ($meta) {
        $name_a = get_term_meta($a->term_id, 'artist_lastname', true);
        $name_b = get_term_meta($b->term_id, 'artist_lastname', true);
        return strcmp($name_a, $name_b);  
    });
}

Some more general code

If you want to sort an array just once and need an extra argument you can use an anonymous function like this:

usort($arrayToSort, function($a, $b) use ($myExtraArgument) {
    //$myExtraArgument is available in this scope
    //perform sorting, return -1, 0, 1
    return strcmp($a, $b);
});

If you need a reusable function to sort an array which needs an extra argument, you can always wrap the anonymous function, like for the original question:

function mySortFunction(&$arrayToSort, $myExtraArgument1, $myExtraArgument2) {
    usort($arrayToSort, function($a, $b) use ($myExtraArgument1, $myExtraArgument2) {
        //$myExtraArgument1 and 2 are available in this scope
        //perform sorting, return -1, 0, 1
        return strcmp($a, $b);
    });
}

Solution 2:

In PHP, one option for a callback is to pass a two-element array containing an object handle and a method name to call on the object. For example, if $obj was an instance of class MyCallable, and you want to call the method1 method of MyCallable on $obj, then you can pass array($obj, "method1") as a callback.

One solution using this supported callback type is to define a single-use class that essentially acts like a closure type:

function sort_by_term_meta( $terms, $meta ) 
{
    usort($terms, array(new TermMetaCmpClosure($meta), "call"));
}

function term_meta_cmp( $a, $b, $meta )
{
    $name_a = get_term_meta($a->term_id, $meta, true);
    $name_b = get_term_meta($b->term_id, $meta, true);
    return strcmp($name_a, $name_b); 
} 

class TermMetaCmpClosure
{
    private $meta;

    function __construct( $meta ) {
        $this->meta = $meta;
    }

    function call( $a, $b ) {
        return term_meta_cmp($a, $b, $this->meta);
    }
}

Solution 3:

Assuming you've access to objects and static (PHP 5 or greater), you can create an object and pass the arguments directly there, like so:

<?php
class SortWithMeta {
    private static $meta;

    static function sort(&$terms, $meta) {
       self::$meta = $meta;
       usort($terms, array("SortWithMeta", "cmp_method"));
    }

    static function cmp_method($a, $b) {
       $meta = self::$meta; //access meta data
       // do comparison here
    }

}

// then call it
SortWithMeta::sort($terms, array('hello'));

Assuming you don't have access to objects/static; you could just do a global:

$meta = array('hello'); //define meta in global

function term_meta_cmp($a, $b) {
   global $meta; //access meta data
   // do comparison here
}

usort($terms, 'term_meta_cmp');

Solution 4:

Warning This function has been DEPRECATED as of PHP 7.2.0. Relying on this function is highly discouraged.

The docs say that create_function() should work on PHP >= 4.0.1. Does this work?

function term_meta_cmp( $a, $b, $meta )  {
    echo "$a, $b, $meta<hr>"; // Debugging output
}
$terms = array("d","c","b","a");
usort($terms, create_function('$a, $b', 'return term_meta_cmp($a, $b, "some-meta");'));

Solution 5:

This won't help you at all with usort() but might be helpful nevertheless. You could sort the array using one of the other sorting functions, array_multisort().

The idea is to build an array of the values that you would be sorting on (the return values from get_term_meta()) and multisort that against your main $terms array.

function sort_by_term_meta(&$terms, $meta) 
{
    $sort_on = array();
    foreach ($terms as $term) {
        $sort_on[] = get_term_meta($term->term_id, $meta, true);
    }
    array_multisort($sort_on, SORT_ASC, SORT_STRING, $terms);
}