PHP 5.3 – wywołanie array_multisort za pośrednictwem call_user_func_array

Podziel się z innymi!

    Chcąc dostosować swój kod do wersji PHP 5.3, tak aby wykonywał się bezproblemowo z uwzględnieniem błędów STRICT natrafiłem na taki drobny szczegół. Swego czasu dodałem do manuala funkcji array_multisort na php.net taką drobną [notkę zawierającą implementację funkcji ułatwiającej sortowanie wyników zapytań sql-owych] (http://usphp.com/manual/en/function.array-multisort.php#87268).

    Chcąc ją dzisiaj użyć dostałem na twarz warning-a „Parameter 2 to array_multisort() expected to be a reference, value given”.

    Załóżmy, że miałem następujące dane.

    $data = array(
    	0 => array (
    		'id' => '1',
    		'name' => 'Rodzaje',
    	),
    	1 =>
    	array (
    		'id' => '2',
    		'name' => 'Kategorie',
    	),
    	2 =>
    	array (
    		'id' => '3',
    		'name' => 'Statusy',
    	),
    	3 =>
    	array (
    		'id' => '4',
    		'name' => 'Kolory',
    	),
    );

    Aby posortować tę tablicę według kolumny „name” wywołałem wspomnianą funkcję w następujący sposób

    sortDbResult($data, 'name', SORT_ASC, SORT_STRING);

    Przyjrzałem się problematycznej linii i zobaczyłem, że wywołuję funkcję array_multisort przy pomocy funkcji call_user_func_array, do której parametry przekazuję za pośrednictwem tablicy.

    call_user_func_array('array_multisort', $_params);

    W moim przypadku zmienna $_params miała następującą zawartość

    array(
    0 => &array (
    	0 => 'Rodzaje',
    	1 => 'Kategorie',
    	2 => 'Statusy',
    	3 => 'Kolory',
    ),
    1 => 2,
    2 => 4,
    3 => array(
    	0 => array (
    		'id' => '1',
    		'name' => 'Rodzaje',
    	),
    	1 =>
    	array (
    		'id' => '2',
    		'name' => 'Kategorie',
    	),
    	2 =>
    	array (
    		'id' => '3',
    		'name' => 'Statusy',
    	),
    	3 =>
    	array (
    		'id' => '4',
    		'name' => 'Kolory',
    	),
    ));

    Pomyślałem sobie o co chodzi z tym błędem w końcu drugi parametr funkcji array_multisort to w moim przypadku wartość stałej SORT_ASC czyli 2. Przeprowadziłem mały eksperyment i wywołałem funkcję array_multisort bezpośrednio.

    $data_1 = array (
    	0 => 'Rodzaje',
    	1 => 'Kategorie',
    	2 => 'Statusy',
    	3 => 'Kolory',
    );
     
    $data_2 = array(
    	0 => array (
    		'id' => '1',
    		'name' => 'Rodzaje',
    	),
    	1 =>
    	array (
    		'id' => '2',
    		'name' => 'Kategorie',
    	),
    	2 =>
    	array (
    		'id' => '3',
    		'name' => 'Statusy',
    	),
    	3 =>
    	array (
    		'id' => '4',
    		'name' => 'Kolory',
    	),
    );
    array_multisort($data_1, SORT_ASC, SORT_STRING, $data_2);
     
    var_dump($data_2);

    Zadziałało. Tablica $_data2 została posortowana prawidłowo.

    call_user_func_array('array_multisort',  array(&$data_1, SORT_ASC, SORT_STRING, &$data_2));

    Powyższy kod również wykonał się nie zwracając żadnych komunikatów błędów.

    $params = array(&$data_1, SORT_ASC, SORT_STRING, &$data_2);
    call_user_func_array('array_multisort',  $params);

    Wykonanie powyższego kodu kończy się już niestety warningiem. Aby się go pozbyć trzeba użyć małego obejścia i przypisać wartości stałych do zmiennych a następnie przekazać je do tablicy przy pomocy referencji.

    $o = SORT_ASC;
    $v = SORT_STRING;
    $params = array(&$data_1, &$o, &$v, &$data_2);
    call_user_func_array('array_multisort', $params);

    W tym wypadku nie działa ukrywanie błędów. Funkcja array_multisort po prostu nie zadziała prawidłowo.

    Poprawiona funkcja sortDbResult wygląda następująco:

    /**
     * Sort DB result
     *
     * @param array $data Result of sql query as associative array
     *
     * Rest of parameters are optional
     * [, string $name  [, mixed $name or $order  [, mixed $name or $mode]]]
     * $name string - column name i database table
     * $order integer - sorting direction ascending (SORT_ASC) or descending (SORT_DESC)
     * $mode integer - sorting mode (SORT_REGULAR, SORT_STRING, SORT_NUMERIC)
     *
     * 
     *  $i,
     *                      'first_name' => sprintf('first_name_%s', rand(1, 9)),
     *                      'last_name' => sprintf('last_name_%s', rand(1, 9)),
     *                      'date' => date('Y-m-d', rand(0, time()))
     *                  );
     * }
     * $data = sortDbResult($data, 'date', SORT_DESC, SORT_NUMERIC, 'id');
     * var_dump($data);
     * $data = sortDbResult($data, 'last_name', SORT_ASC, SORT_STRING, 'first_name', SORT_ASC, SORT_STRING);    
     * var_dump($data);
     * ?>
     * 
     *
     * @return array $data - Sorted data
     */
    
    function sortDbResult(array $data /*$name, $order, $mode*/) {
    	$_argList = func_get_args();
    	$_data = array_shift($_argList);
    	if (empty($_data)) {
    		return $_data;
    	}
    	$_max = count($_argList);
    	$_params = array();
    	$_cols = array();
    	$_rules = array();
    	for ($_i = 0; $_i < $_max; $_i += 3)
    	{
    		$_name = (string) $_argList[$_i];
    		if (!in_array($_name, array_keys(current($_data)))) {
    			continue;
    		}
    		if (!isset($_argList[($_i + 1)]) || is_string($_argList[($_i + 1)])) {
    			$_order = SORT_ASC;
    			$_mode = SORT_REGULAR;
    			$_i -= 2;
    		} else if (3 > $_argList[($_i + 1)]) {
    			$_order = SORT_ASC;
    			$_mode = $_argList[($_i + 1)];
    			$_i--;
    		} else {
    			$_order = $_argList[($_i + 1)] == SORT_ASC ? SORT_ASC : SORT_DESC;
    			if (!isset($_argList[($_i + 2)]) || is_string($_argList[($_i + 2)])) {
    				$_mode = SORT_REGULAR;
    				$_i--;
    			} else {
    				$_mode = $_argList[($_i + 2)];
    			}
    		}
    		$_mode = $_mode != SORT_NUMERIC
    					? $_argList[($_i + 2)] != SORT_STRING ? SORT_REGULAR : SORT_STRING
    					: SORT_NUMERIC;
    		// references below required from PHP 5.3
    		$_rules[] = array('name' => $_name, 'order' => &$_order, 'mode' => &$_mode);
    	}
    	foreach ($_data as $_k => $_row) {
    		foreach ($_rules as $_rule) {
    			if (!isset($_cols[$_rule['name']])) {
    				$_cols[$_rule['name']] = array();
    				$_params[] = &$_cols[$_rule['name']];
    				// references below required from PHP 5.3
    				$_params[] = &$_rule['order'];
    				$_params[] = &$_rule['mode'];
    			}
    			$_cols[$_rule['name']][$_k] = $_row[$_rule['name']];
    		}
    	}
    	$_params[] = &$_data;
    	call_user_func_array('array_multisort', $_params);
    	return $_data;
    }
    Podziel się z innymi!

      Posted in PHP by Zbigniew Heintze · Tag: