Viewed   69 times

I have looked at the php documentation, tutorials online and none of them how usort is actually working. I have an example i was playing with below.

$data = array(

    array('msg' => 'some text','month' => 11,'level' => 10),

    array('msg' => 'some text','month' => 5,'level' => 10),

    array('msg' => 'some text','month' => 8,'level' => 10),

    array('msg' => 'some text','month' => 12,'level' => 10),

    array('msg' => 'some text','month' => 2,'level' => 10),

    array('msg' => 'some text','month' => 3,'level' => 10),

    array('msg' => 'some text','month' => 4,'level' => 10),

    array('msg' => 'some text','month' => 7,'level' => 10),

    array('msg' => 'some text','month' => 10,'level' => 10),

    array('msg' => 'some text','month' => 1,'level' => 10),

    array('msg' => 'some text','month' => 6,'level' => 10),

    array('msg' => 'some text','month' => 9,'level' => 10)

);

I wanted to be able to sort the months from 12 to 1 (since their unorganized) through some help this was the solution

function cmp($a, $b)
{
    if ($a["month"] == $b["month"]) 
    {
       return 0;
    }
    return ($a["month"] < $b["month"]) ? -1 : 1;
}

usort($data, "cmp");

but i dont understand how the function cmp sorts the array. i tried printing out each variable $a and $b like this:

function cmp($a, $b)
{
   echo "a: ".$a['month']."<br/>";
   echo " b: ".$b['month']."<br/>";
   echo "<br/><br/>";
}

and the output was

a: 3
b: 5

a: 9
b: 3

a: 3
b: 8

a: 6
b: 3

a: 3
b: 12

a: 1
b: 3

a: 3
b: 2

a: 10
b: 3

a: 3
b: 11

a: 7
b: 3

a: 4
b: 3

a: 12
b: 2

a: 5
b: 12

a: 12
b: 11

a: 8
b: 12

a: 5
b: 8

a: 2
b: 11

a: 6
b: 9

a: 7
b: 6

a: 6
b: 4

a: 10
b: 6

a: 1
b: 6

a: 9
b: 4

a: 7
b: 1

a: 10
b: 7

it makes no sense to how the sort is working and why cmp($a, $b) is used. i have tried to print out all its processes as you can see but have not come to any solution to how it all works..

thanks

 Answers

4

The function cmp itself doesn't do the sorting. It just tells usort if a value is smaller, equal or greater than another value. E.g. if $a = 5 and $b = 9 it will return 1 to indicate that the value in $b is greater than the one in $a.

Sorting is done by usort.

Wednesday, October 26, 2022
5
$str="abcdefg foo() hijklmopqrst";
function foo() {return "bar";}

$replaced = preg_replace_callback("~([a-z]+)()~", 
     function ($m){
          return $m[1]();
     }, $str);

output:

$replaced == 'abcdefg bar hijklmopqrst';

This will allow any lower-case letters as function name. If you need any other symbols, add them to the pattern, i.e. [a-zA-Z_].

Be VERY careful which functions you allow to be called. You should at least check if $m[1] contains a whitelisted function to not allow remote code injection attacks.

$allowedFunctions = array("foo", "bar" /*, ...*/);

$replaced = preg_replace_callback("~([a-z]+)()~", 
     function ($m) use ($allowedFunctions) {
          if (!in_array($m[1], $allowedFunctions))
              return $m[0]; // Don't replace and maybe add some errors.

          return $m[1]();
     }, $str);

Testrun on "abcdefg foo() bat() hijklmopqrst" outputs "abcdefg bar bat() hijklmopqrst".

Optimisation for whitelisting approach (building pattern dynamically from allowed function names, i.e. (foo|bar).

$allowedFunctions = array("foo", "bar");

$replaced = preg_replace_callback("~(".implode("|",$allowedFunctions).")()~", 
     function ($m) {
          return $m[1]();
     }, $str);
Monday, November 21, 2022
 
5

To preserve the keys, you just need to use uasort().

I recommend the spaceship operator to make the 3-way comparisons (available from PHP7 and higher).

$result in your code is only going to return true/false so that is no use to you. The sort()'ing functions aren't to be assigned to a variable; they directly modify the input array.

When offering array data to the spaceship operator, the first two elements will be compared. If there is a difference, then 1 or -1 will be returned. If there is a tie (comparison evaluates as 0, then the next two elements will be evaluated. This behavior continues until a non-zero evaluation occurs or there are no more elements to iterate. This is a rather clean/readable syntax to implement.

To sort in a descending direction, write the $b value on the left and the $a value on the right.

Code: (Demo)

$user_scores = [
    82 => ['score' => 1, 'time' => 6.442],
    34 => ['score' => 1, 'time' => 5.646],
    66 => ['score' => 3, 'time' => 1.554],
    7  => ['score' => 2, 'time' => 4.442],
    99 => ['score' => 4, 'time' => 3.646],
    55 => ['score' => 1, 'time' => 2.554]
];

uasort($user_scores, function($a, $b) {
    return [$b['score'], $a['time']] <=> [$a['score'], $b['time']];
});
var_export($user_scores);

Output:

array (
  99 => 
  array (
    'score' => 4,
    'time' => 3.646,
  ),
  66 => 
  array (
    'score' => 3,
    'time' => 1.554,
  ),
  7 => 
  array (
    'score' => 2,
    'time' => 4.442,
  ),
  55 => 
  array (
    'score' => 1,
    'time' => 2.554,
  ),
  34 => 
  array (
    'score' => 1,
    'time' => 5.646,
  ),
  82 => 
  array (
    'score' => 1,
    'time' => 6.442,
  ),
)
Thursday, November 17, 2022
 
3

No, it's not possible. Ordering means that you can compare two elements and determine which order they should have. Your query fails at that when you have two 1 elements.

However, that doesn't mean that it can't be done algorithmically, only that sorting or ordering is not the right tool for the job.

Sunday, December 25, 2022
 
amp
 
amp
3

Ok, I found the answer to my question in this related question: Are PDO prepared statements sufficient to prevent SQL injection?

Thanks to Haim for pointing this Q to me.

In non technical terms, here is how prepared statements protect from injection:

When a query is sent to a data base, it's typically sent as a string. The db engine will try to parse the string and separate the data from the instructions, relying on quote marks and syntax. So if you send "SELECT * WHERE 'user submitted data' EQUALS 'table row name', the engine will be able to parse the instruction.

If you allow a user to enter what will be sent inside 'user submitted data', then they can include in this something like '..."OR IF 1=1 ERASE DATABASE'. The db engine will have trouble parsing this and will take the above as an instruction rather than a meaningless string.

The way PDO works is that it sends separately the instruction (prepare("INSERT INTO ...)) and the data. The data is sent separately, clearly understood as being data and data only. The db engine doesn't even try to analyze the content of the data string to see if it contains instructions, and any potentially damaging code snipet is not considered.

Tuesday, October 11, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :