Object, closures a předávání pole odkazem

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
jasir
Člen | 746
+
0
-

S PHP5.3 se tak nějak pomalu sžívám.
Mám zajímavý(?) problém:

<?php
abstract class EntityPresenter extends BasePresenter
{
   public $onSave = array();

   public function handleSave($form) {
	$values = $form->values;
	$this->onSave($values);
	...
   }
}

public class ImagesPresenter extends EntityPresenter
{
	public function startup() {
		parent::startup();
		$this->onSave[] = function(&$values) {
			unset($values['image']);
			$values['size'] = ...
		}
        }
}
?>

Toto mi vyhodí chybu ‚Parameter 2 to Closure::__invoke() expected to be a reference, value given‘. Když se pokusím volání upravit na $this->onSave(&$values), dostanu
chybu ‚Call-time pass-by-reference has been deprecated‘ (mám zapnuté strictMode a vypínat ho nechci). Jak mám tedy dostat pole odkazem do closure, abych ho mohl ve funkci
upravit?

Co funguje, je poslat ArrayObject: $values = new ArrayObject($form->values), ale moc se mi to nelíbí.

Díky za nápady…

Editoval jasir (1. 4. 2010 0:52)

Majkl578
Moderator | 1364
+
0
-

Problém je ve třídě Nette\Callback. Tuhle chybu vyvolá použití funkce call_user_func_array na Closure.

Viz toto:

$data = range(1, 10);
$func = function (&$val) {
	unset($val[0]);
};

//zde je problém
call_user_func($func, $data);
//popř.
//call_user_func_array($func, array($data));
//pokud se místo toho volá toto, tak funguje jak má:
//$func($data);

var_dump($data);

„Warning: Parameter 1 to {closure}() expected to be a reference, value given“

Otázkou je, jestli je vyhození varování při volání Closure pomocí call_user_func(_array) správné, nebo jestli je to bug. Bug to ale pravděpodobně nebude, jelikož stejné chování nastane i při tomto:

$data = range(1, 10);
function x(&$val) {
	unset($val[0]);
};
call_user_func('x', $data);
var_dump($data);

Co funguje správně je tento kód (všimni si použitého & i v předání argumentu):

$data = range(1, 10);
$func = function (&$val) {
	unset($val[0]);
};
call_user_func_array($func, array(&$data));
var_dump($data);

Naopak ale použití call_user_func namísto call_user_func_array hodí E_DEPRECATED.

$data = range(1, 10);
$func = function (&$val) {
	unset($val[0]);
};
call_user_func($func, &$data);
var_dump($data);

Editoval Majkl578 (1. 4. 2010 2:40)

jasir
Člen | 746
+
0
-

Hmm… Moc díky za analýzu, to mě nedošlo. Takže zřejmě jediné řešení je posílat v tomto případě ArrayObject, protože úprava Nette je vlastně nemožná… Chápu to správně?

Majkl578
Moderator | 1364
+
0
-

Minimálně mě nenapadá jak to řešit obecně, aby to fungovalo pro všechny možné případy.

Matúš Matula
Člen | 257
+
0
-

Ahoj, mam rovnaky problem. Pri pokuse spustit callback hodi ladenka warning Parameter 1 to RssControl::a() expected to be a reference, value given . Nepomohlo ani pouzitie ArrayObject. Ako sa to da riesit?

<?php
	/** @var array */
	public $onPrepareProperties;

	public function __construct(/*Nette\*/IComponentContainer $parent = NULL, $name = NULL)
	{
		parent::__construct($parent, $name);

		$this->onPrepareProperties[] = callback($this, "a");
	}

	public function a(&$w) {
		echo '1';
	}

	public function render()
	{
		$properties = array(2);
//		$properties = new ArrayObject($properties);
		$this->onPrepareProperties($properties);
	}
?>