Search code examples
phpusortfunction-parametervariable-variablesfunction-signature

Variable variables as parameters of custom function in usort()


I've been playing around and seeing if I can set up a more dynamic method in PHP.

usort(
    $dataset,
    function($a, $b){
        return strcasecmp($a[$this->parameters], $b[$this->parameters]);
    }
);

This line would sort array elements in alphabetically descending order. However, if we were to swap variables $a and $b whether in function($a, $b) or ($a[$this->parameters], $b[$this->parameters]) we will get the opposite effect (descending order).

As I've looked into the matter, there is such a thing as "variable variables" in PHP. An example of this coulde be:

$a="hello";
$$a="oops";
echo($hello);die;

However, if I similarly try to implement this within the code of line above I get an error.

$first = 'b';
$second = 'a';
usort($dataset, function($$first, $$second){ return strcasecmp($a[$this->parameters], $b[$this->parameters]); });

Error:Parse error: syntax error, unexpected '$', expecting variable (T_VARIABLE).

The idea is to be able to reverse the effect base on an iff statement result to redefine $first and $second variables. Otherwise one would need to duplicate almost identical code.

Am I missing something? Maybe some other way to achieve this?


Solution

  • Resolving dynamic names of variables is something that happens at runtime, rather than interpreter (aka compile) time. Therefore, you can not pass it as dynamic paramaters which you expected to be able to do.

    Given that, this is not possible for one simple reason: Even if you would manage to pass different names at compile time, it would only be true for one set of values to compare. What would keep the callback-call from passing a<b, then a>b, then a==b (imagine them as values)? Nothing, and exactly that would happen.

    That being said, you can try and validate which value is smaller before passing it to the final callback, but this only adds an extra layer rather than always sorting the same way (or even sorting at all):

    usort($dataset, function($a, $b)
    { 
        if ($a > $b) {
            return $b <=> $a;
        }
        return $a <=> $b;
    });
    
    var_dump($dataset);
    
    // output
    array(3) {
      [0]=>
      int(3)
      [1]=>
      int(1)
      [2]=>
      int(7)
    }
    

    I am fully aware that this does not solve your problem at all. I am just trying to demonstrate that it wont even work that way.

    I think the key fact here is that you define the sort mechanism in your callback, and hence you have to make sure that you sort it ascending or descending in that definition, since that is what it exists for!

    And on a side note I think sorting callbacks became really easy to create in PHP since the spaceship operator:

    // defines: sort ASC
    usort($dataset, function($a, $b) { return $a <=> $b; });
    // defines: sort DESC
    usort($dataset, function($a, $b) { return $b <=> $a; });
    

    And even more so with the arrow functions since PHP 7.4:

    // ASC
    usort($dataset, fn($a, $b) => $a <=> $b);
    // DESC
    usort($dataset, fn($a, $b) => $b <=> $a);
    

    In conclusion, I fully understand where you are coming from, but maybe you are trying to solve a problem that is not even really there?