blok Debugger::tryError(), Debugger::catchError() a možnost zanoření těchto bloků?

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

Nejsem si jistý jde-li o bug. Nebo o záměr a chyba je, že se snažím využít Debugger try/catch jinak než bylo zamýšleno.

Pro pársování většího množství stránek používám zhruba takovouto konstrukci:

foreach($pages as $page)
{
	Debugger::tryError();

	$this->parsePage($page);  // quite complex process

	$result = Debugger::catchError($e) ? -1 : 1;

	if ($result == -1) {
		$this->logError($e);
	}

	$this->updatePageResultFlag($result);
}

Záměr je, ať přestože během pársování jedné nebo více stránek dojde k jakékoli chybě, ať se chyba jen zapíše a skript pokračuje se dál.

Narazil jsem, ale na to, že se mi při chybě stále zobrazovala laděnka a nakonec jsem po vyloučení všech možností a postupném zakomentovávání různých částí kódů nakonec odhalil, že celý problém způsobilo volání funkce \Nette\Utils\Strings::replace ve které je tento blok na dvou místech použit.

Podle zdrojáků Debuggeru soudím, že se zanořením se nepočítá a proto pokud došlo k chybě až po volání funkce Strings::replace, tak se zachytávání chyb laděnkou obnovilo a můj blok try/catch už nefunguje.

Svůj problém jsem vyřešil že jsem místo Strings::replace použil čísté preg_replace.

Prosím o info.

Aurielle
Člen | 1281
+
0
-

try/catchError se využívá pro chytání PHP chyb. Pro odchytávání výjimek použij normálně try {…} catch($e) {…}. Strings::replace vyhazuje právě namísto PHP chyby výjimku.

LeonardoCA
Člen | 296
+
0
-

Díky za info. Myslel jsem si, že to využívám špatně.

Obvykle používám try, ale nefungovalo mi to, teď jsem přišel na důvod, začínám s namespaces a tak jsem se stále zkoušel různé varianty

} catch (Exception $e) {

a stačilo

} catch (\Exception $e) {

ještě ale jen tak zkouším simulovat chybu dělením nulou

$s = 1/0;

vyhodí to warning Division by zero

a to try blok nezachytí

Sice to v současném projektu nepotřebuji, ale v jiném to uplatním – znamená to, že pokud bych chybu dělení předpokládal, nebo by byla obecně nějaká možnost, že některá akce v bloku try vyhodí warning místo vyjímky, tak musí být každý takový řádek obalen blokem Debugger::tryError(); ?

Vyzkoušel jsem a výsledné řešení vypadá nějak takto:

foreach($pages as $page)
{
	$completed = 0;
	try
	{
		$this->parsePage($page);

		// possibility of warning "division by zero", must throw exception instead
		Debugger::tryError();
		$this->calculateSomeThing();
		if (Debugger::catchError($e)) throw $error;

		$completed = 1;
		// catch all errors, to be able to continue processing
	} catch (\Exception $e) {
		Debugger::log($e, Debugger::ERROR);
		$completed = -1;
	}

        $this->updatePageCompletedFlag($completed);
}

Editoval LeonardoCA (12. 5. 2012 18:03)

LeonardoCA
Člen | 296
+
0
-

Mám ještě jednu otázku.

Udělal jsem další pokusy, s vyhozením dvou chyb v daném bloku

1. Vyhodí až druhé warning dělení nulou jako výjimku

$result = 0;
try
{
	Debugger::tryError();
	file_get_contents('notExistingFile.txt');
	$s = 1/0;
	if (Debugger::catchError($error)) throw $error;

	$result = 1;
} catch (\Exception $e) {
	Debugger::log($e, Debugger::ERROR);
	$result = -1;
}

2. Vyhodí výjimku „file_get_contents(notExistingFile.txt): failed to open stream“ což je už lepší

$result = 0;
try
{
	Debugger::tryError();
	file_get_contents('notExistingFile.txt');
	if (Debugger::catchError($error)) throw $error;

	Debugger::tryError();
	$s = 1/0;
	if (Debugger::catchError($error)) throw $error;

	$result = 1;
} catch (\Exception $e) {
	Debugger::log($e, Debugger::ERROR);
	$result = -1;
}

Je možné nějak docílit toho, aby mi Debugger vrátil obě chyby v případě prvním?

Potřebuji to dořešit obecně pro skript volající nějakou akci definovanou přes interface, kde vůbec nebudu vědět jaká je implementace a kolik různých warning může funkce vyhodit. (a předpoklad je, že ji nebudu ani tvořit, takže nemůžu zajistit, aby byly warningy správně zachycené)

$result = 0;
try
{
	Debugger::tryError();
	$this->runAction();
	if (Debugger::catchError($error)) throw $error;

	$result = 1;
} catch (\Exception $e) {
	Debugger::log($e, Debugger::ERROR);
	$result = -1;
}

Takto v tuto chvíli dostanu až poslední warning. Potřeboval bych nejlépe všechny chyby a nebo alespoň první chybu a ne až poslední.

Editoval LeonardoCA (12. 5. 2012 18:11)

Aurielle
Člen | 1281
+
0
-

Volání tryError a catchError vůbec nemusíš obalovat bloky try…catch. Pokud v catchError zjistíš, že existuje chyba, vypořádej se s ní hned (zaloguj, přesměruj nebo tak něco).

LeonardoCA
Člen | 296
+
0
-

To je mi jasné, ale asi si nerozumíme o co mi jde,uvedu příklad i s výstupem.

Jde mi o to, že mám provést například 10 „akcí“ a musí se provést všechny i pokud v některé z nich dojde k libovolné chybě.

Funkce simulující chybové stavy

	private function action($i)
	{
		switch($i)
		{
			case 2:
				// single warning
				file_get_contents('notExistingFile.txt');
				break;
			case 4:
				// two warnings
				file_get_contents('notExistingFile.txt');
				$s = 1/0;
				break;
			case 5:
				// action uses Debug::catch() internally
				\Nette\Utils\Strings::replace('/-/', '?', 'nejaky-text');
				file_get_contents('notExistingFile.txt');
				$s = 1/0;
				break;
			case 6:
				// any standart exception
				throw new \Exception("Libovolná vyjímka");
				break;
			case 7:
				// everything at once
				\Nette\Utils\Strings::replace('/-/', '?', 'nejaky-text');
				file_get_contents('notExistingFile.txt');
				$s = 1/0;
				throw new \Exception("Libovolná vyjímka");
				break;
		}
	}

1. Použití try

		for($i=1; $i<=10; $i++)
		{
			$completed = 0;
			$resultMsg = "ok";
			try
			{
				$this->action($i);
				$completed = 1;
			} catch (\Exception $error) {
				$resultMsg = $error->getMessage();
				$completed = -1;
			}
			echo "$i : $completed - $resultMsg<br/>";
		}

/*
Výsledek:
1 : 1 - ok
2 : 1 - ok
3 : 1 - ok
4 : 1 - ok
5 : 1 - ok
6 : -1 - Libovolná vyjímka
7 : -1 - Libovolná vyjímka
8 : 1 - ok
9 : 1 - ok
10 : 1 - ok
*/

2. Použití Debugger::try()

		for($i=1; $i<=10; $i++)
		{
			$completed = 0;
			$resultMsg = "ok";
			Debugger::tryError();
			$this->action($i);
			$completed = 1;
			if (Debugger::catchError($error))
			{
				$resultMsg = $error->getMessage();
				$completed = -1;
			}
			echo "$i : $completed - $resultMsg<br/>";
		}

/*
Výsledek:
1 : 1 - ok
2 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
3 : 1 - ok
4 : -1 - Division by zero
5 : 1 - ok
Skript skončil s http response 500 protoze Debugger::try nefunguje kaskádovitě
*/

3. Použití try + Debugger::try()

		for($i=1; $i<=10; $i++)
		{
			$completed = 0;
			$resultMsg = "ok";
			try
			{
				Debugger::tryError();
				$this->action($i);
				if (Debugger::catchError($error)) throw $error;
				$completed = 1;
			} catch (\Exception $error) {
				$resultMsg = $error->getMessage();
				$completed = -1;
			}
			echo "$i : $completed - $resultMsg<br/>";
		}

/*
Výsledek:
1 : 1 - ok
2 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
3 : 1 - ok
4 : -1 - Division by zero
5 : 1 - ok
6 : -1 - Libovolná vyjímka
7 : -1 - Libovolná vyjímka
8 : 1 - ok
9 : 1 - ok
10 : 1 - ok
*/

Stále jsou dva problémy

  • v 5. volání – je ignorován warning
  • ve 4. a 7. volání – vrací poslední chybu, ne první chybu

výsledek má být

/*
1 : 1 - ok
2 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
3 : 1 - ok
4 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
5 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
6 : -1 - Libovolná vyjímka
7 : -1 - file_get_contents(notExistingFile.txt): failed to open stream: No such file or directory
8 : 1 - ok
9 : 1 - ok
10 : 1 - ok
*/

Jediné řešení podle mne je aby Debugger::try/catch

  • fungoval kaskádovitě, aby to vyřešilo případ 5.
  • alespoň s volitelným parametrem automaticky převáděl warnings případně notices na výjimky – aby se vracela první chyba a ne poslední, zároveň by to i správně ukončovalo běh vnořeného kódu hned po první chybě

Editoval LeonardoCA (12. 5. 2012 21:11)

juzna.cz
Člen | 248
+
0
-

Pokud chces vsechny chyby v PHP prevest na vyjimky, zapni si strictMode v debuggeru. Jakmile nastane chyba, Debugger ji prekonvertuje na vyjimku, a ty muzes zachytavat pomoci try/catch.

Filosofie PHP nechat chyby prochazet a „fungovat aspon nejak“. Tzn pokud chyba neni fatalni, tak na ni kasle a pokracuje dal. StrictModem to muzes zmenit.

LeonardoCA
Člen | 296
+
0
-

Testy jsem delal pri zapnutem strictMode. Ten pouzivam standartne.

Zkusim az budu mit chvili test nahodit na cisty skelton, at mam jistotu ve vsech podminkach, protoze jsem to jen narychlo zkousel v ramci uprav kodu, ktery neni muj. Takze to jeste proverim a pripadne nahodim nekde zdrojaky.

Filozofie PHP mne v tomto pripade nezajima, nejde mi o zobrazovani webu, kde se nejaky problem ztrati, ale o zpracovavani stovek tisic ruznych operaci cronem kdy jakoukoli chybu povazuji za fatalni z hlediska konzistence dat, ale zaroven vetsina chyb v ramci akce neni tak fatalni, aby se ukoncil beh skriptu.

Editoval LeonardoCA (13. 5. 2012 21:39)

David Grudl
Nette Core | 7445
+
0
-

Debugger::tryError() a catchError() existují pro vnitřní účely, nedoporučoval bych na nich stavět aplikace. Existují kvůli tomu, že PHP má naprosto dosr*nou správu chyb a používají se k ověření, zda PHP funkce skončila s chybou nebo ne.

Každopádně není možné je vnořovat. Protože Strings::replace() je sám používá, nelze volat replace() uvnitř tryError a catchError. Uvnitř bloku by se měla skutečně zavolat jen jedna PHP funkce.

To, co hledáš, je konvertování chyb na výjimky. Tohle Nette nedělá (vedlo se o tom jen mnoho diskusí), ani ve strict mode.