1

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?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
NulisDefo
  • 325
  • 2
  • 7
  • 19
  • Try `function(${$first}, ${$second})` – Mark Feb 20 '20 at 16:41
  • 2
    I guess I don't understand why you are trying to do this... what problem are you trying to solve? If it is just changing the sort order, call a separate function or pass in a use argument to denote the direction. – Alex Barker Feb 20 '20 at 16:47
  • @MarkOverton Same result. – NulisDefo Feb 20 '20 at 16:50
  • @AlexBarker Sure, same result can be achieved just by making function calls. I just came across variable variables and are trying to understand them better/see their limits (though I'm not sure what you mean by "use argument") – NulisDefo Feb 20 '20 at 16:50
  • @NulisDefo, I understand and that is a valid question. The only interesting stuff you can do with function args is the `...` (a.k.a. unpack operator) and variadic functions using similar notation. See: https://wiki.php.net/rfc/variadics & https://wiki.php.net/rfc/argument_unpacking On a related note, the variable variables are generally a bad idea due to unintended side effect type issues. Anytime you have execution of dynamic code, you should really stop and ask yourself if you can make a better design choice. – Alex Barker Feb 21 '20 at 02:30
  • @AlexBarker does your "dynamic code" include variable function names? Since I was playing around with a class which would have a single public method to which you provide some parameters (one of which would be sorting method) and then delegate the actual work to said method. Something along the lines of `foreach ($sort_criteria['methods'] as $method => $parameters) {$dataset = $this->$method($dataset, $parameters);}` – NulisDefo Feb 21 '20 at 09:32

3 Answers3

2

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?

ArSeN
  • 5,133
  • 3
  • 19
  • 26
1

I would like to expand a bit on @ArSeN's great answer to add a little more computer science context to why this simply isn't "a thing." If you take a look at how functions are implemented at the processor level (ASM Language) you quickly realize that the argument names passed to a function are not even used in the binary program. They are really only there to make your life as a programmer humaine. Most calling conventions implement function arguments on the stack frame as space between the functions. They are accessed at their respective memory offsets inside of the function. Most C books and courses will cover this concept in detail, but in simple english this means that the name of the argument doesn't matter as only the order of the argument is used by the eventual binary. This has been abstracted far, far away in PHP, however, it is still quite relevant and something you should understand if you intend to programming at a professional level.

Alex Barker
  • 4,316
  • 4
  • 28
  • 47
0

Let's back up the bus on this XY Problem. In virtually every use case, implementing variable variables is at worst an ill-considered antipattern and at best a symptom that data which should be array-typed is coded as a non-array data type.

Variable variables often confuse even high quality IDEs (the software that devs write their code in), so using these valid coding techniques can often trigger annoying false-positive warnings in otherwise extremely helpful code editors.

As for how you can avoid variable variables while calling usort() -- simply supply a conditionally positive or negative factor to affect the result of the 3-way comparison.

Code: (Demo)

$direction = 'asc';
$factor = $direction === 'desc' ? -1 : 1;
$c = 'columnName';

usort(
    $array,
    fn($a, $b) => $factor * ($a[$c] <=> $b[$c])
);

var_export($array);

Depending on specific circumstances, it may be less cryptic to, instead, call array_multisort(). (Demo)

$direction = 'desc';
$c = 'columnName';

array_multisort(
    array_column($array, $c),
    $direction === 'desc' ? SORT_DESC : SORT_ASC,
    $array
);

var_export($array);

Finally, if you are looking for a robust, general-use dynamic sorting function, you might be inspired by my answer to Sort array of associative arrays on multiple columns using specified sorting rules.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136