RSS
 

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

08 cze

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;
}
 
No Comments

Posted in PHP

 

Tags:

Leave a Reply