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?

6
  • Try function(${$first}, ${$second}) Commented Feb 20, 2020 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. Commented Feb 20, 2020 at 16:47
  • @MarkOverton Same result. Commented Feb 20, 2020 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") Commented Feb 20, 2020 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: wiki.php.net/rfc/variadics & 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. Commented Feb 21, 2020 at 2:30

3 Answers 3

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?

Sign up to request clarification or add additional context in comments.

Comments

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.

Comments

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.