usort function in a class

Php

Php Problem Overview


I have a function which sorts data in a multidimensional array, as shown below:

<?php  
$data = array();  
$data[] = array("name" => "James");  
$data[] = array("name" => "andrew");  
$data[] = array("name" => "Fred");  

function cmp($a, $b)  
{  
    return strcasecmp($a["name"], $b["name"]);  
}  

usort($data, "cmp");  

var_dump($data);  
?>  

When i run this, it works as expected, returning the data ordered by name, ascending. However, I need to use this in a class.

<?php
class myClass  
{  
    function getData()  
    {  
        // gets all data
        $this -> changeOrder($data);
    }  

    function changeOrder(&$data)  
    {  
         usort($data, "order_new");
    }

    function order_new($a, $b)  
    {  
        return strcasecmp($a["name"], $b["name"]);  
    }
}
?>

When I use this, i get the following warning: Warning: usort() expects parameter 2 to be a valid callback, function 'order_new' not found or invalid function name.

When i put the order_new function in the changeOrder function it works fine, but I have problems with Fatal error: Cannot redeclare order_new(), so i cannot use that. Any suggestions?

Php Solutions


Solution 1 - Php

order_new is a class method not a global function. As the PHP-Manual suggest you can use in this case

usort($data, array($this, "order_new")); 

or declare order_new static and use

usort($data, array("myClass", "order_new")); 

Solution 2 - Php

Change to usort($data, array($this, "order_new"));

Solution 3 - Php

 usort($data, array($this,"order_new"));

is what you want when referring to a function in your class instance. See callable

> A method of an instantiated object is passed as an array containing an object at index 0 and the method name at index 1.

Solution 4 - Php

One thing to consider is that if the variable is passed to your method by reference, usort() appears to execute in the context of the referenced variable and not your current method. Therefore, you need to provide the full namespace in your class reference like so:

usort($data, ['My\NameSpace\MyClass', 'order_new']); 

Solution 5 - Php

I know this is an old thread, but I ran into another instance today that might benefit from another approach. I had a case where I wanted to use a specific key so I could abstract the actual support function. This led to finding a version using a wrapper function to return an anonymous function utilizing the 'use' command word to supply the name of the key used to sort in my class. (in my case, it's a key in a property on a [nested] class that is assigned to one of the class properties where I'm doing the sorting - i.e. I'm sorting instances based on a property in a 'dimensions' instance assigned to a property of 'items' and want to be able to sort them by width, height, length or weight for example). Then to get your function for the callback, you simply call this wrapper function with the key name you want to use to sort the result:

/**
 * list of items
 *
 * @var Item[]
 */
public $items;

/**
 * @param string $key
 * @return \Closure
 */
private static function keySort($key) {
    return function ($ia, $ib) use ($key) {
        if($ia->dimensions->$key == $ib->dimensions->$key) return 0;
        return ($ia->dimensions->$key < $ib->dimensions->$key) ? -1 : 1;
    };
}

/**
 * return the list of items in the items array sorted by the value
 *   of the property specified by $key
 *
 * @param string $key
 * @return Item[]
 * @throws \Exception
 */
public function sortItemsByKey($key)
{
    if(in_array($key, array( 'width', 'length', 'height', 'weight', ))) {
        return usort($this->items, static::keySort($key));
    } else
        throw new \Exception(__METHOD__ . ' invalid sort key!');
}

This allows me to call it local using either static:: or self:: (conceivably, I could even wrap it as a non-static function in this case since the only thing I'm worried about is getting the call but function returned) Another benefit I soon discovered is my dimensions object also has some 'calculated' fields such as girth, volume and dimensional weight. But one problem is that dimensional weight varies depending on if you are shipping an item domestically or internationally so I need to tell my 'calculateDimensionalWeight' function if it should use the value for international shipping or not. Well, using this method, I can do that by just passing an additional parameter to the wrapper function and adding the additional parameter to the use variables. Since I also need to make sure these values have been calculated prior to doing any comparisons, I can trigger that in my function based on the key:

/**
 * @param string $key
 * @param bool $intl // only used for dimensional weight
 * @return \Closure
 */
private static function keySort($key,$intl=false) {
    return function ($ia, $ib) use ($key,$intl) {
        switch($key) {
            case 'girth':
                $ia->dimensions->calculateGirth();
                $ib->dimensions->calculateGirth();
                break;
            case 'dimweight':
                $ia->dimensions->calculateDimensionalWeight($intl);
                $ib->dimensions->calculateDimensionalWeight($intl);
                break;
            case 'volume':
                $ia->dimensions->calculateVolume();
                $ib->dimensions->calculateVolume();
                break;
        }
        if($ia->dimensions->$key == $ib->dimensions->$key) return 0;
        return ($ia->dimensions->$key < $ib->dimensions->$key) ? -1 : 1;
    };
}

/**
 * return the list of items in the items array sorted by the value
 *
 * @param string $key
 * @param bool $intl  (only used for dimensional weight sorts on international shipments)
 * @return Item[]
 * @throws \Exception
 */
public function sortItemsByKey($key,$intl=false)
{
    if(in_array($key, array('value','collect', 'width', 'length', 'height', 'weight', 'girth', 'dimweight', 'volume'))) {
        return usort($this->items, static::keySort($key,$intl));
    } else
        throw new \Exception(__METHOD__ . ' invalid sort key!');
}

NOTE: calculating values this way creates overhead as all but the first and last items in any list will technically be calculated twice which is redundant, but in this instance my lists are not long and in some cases, I need to compare two items when doing bin-sorting, so it's still preferable. For large datasets, it would probably be wiser to pre-calculate values external to the sort method.

Solution 6 - Php

// If order_new is a NORMAL function 
usort($data, [$this, 'order_new']);      // self ref object, function name  

// If order_new is a STATIC function 
usort($data, [__CLASS__, 'order_new']);  // string, function name 

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMarinusView Question on Stackoverflow
Solution 1 - PhpworengaView Answer on Stackoverflow
Solution 2 - PhpxdazzView Answer on Stackoverflow
Solution 3 - PhpHarald BrinkhofView Answer on Stackoverflow
Solution 4 - PhpbeltashazzarView Answer on Stackoverflow
Solution 5 - PhpScottView Answer on Stackoverflow
Solution 6 - PhpJpsyView Answer on Stackoverflow