Odebrání některých z dat ze sessions

aha009
Člen | 50
+
0
-

Ahoj mám na vás dotaz ohledně sessions. Možná na to jdu úplně špatně.

Ukládám si do sessions nějaká data z formuláře

//$values = (jabko => 1, hruska => 20);

$sessionSection->step1 = $values;

Je možné potom z toho smazat nějaká data nebo musím odstranit celou a poté ji zase uložit?

něco v tom to smyslu?

unset($sessionSection->step1['hruska']);

Děkuji

Polki
Člen | 553
+
-1
-

Ze session se maze tak, ze tu hodnotu co chces smazat nastavis na null takze

<?php
$sessionSection->step1['hruska'] = null;
?>
David Grudl
Nette Core | 8239
+
0
-

Můžeš data úplně normálně odstraňovat. Spíš teda tím unsetem, nastavit null uloží v tomto případě do ‚hruska‘ null.

aha009
Člen | 50
+
0
-

Děkuji za rady.
Zkoušel jsem to s tím unset, ale vrací to chybu
Undefined property: Nette\Utils\ArrayHash::$hruska

Řešení od @Polki funguje. Tak jsem na vážkách jestli ještě řešit ten unset.

Marek Bartoš
Nette Blogger | 1280
+
0
-

ArrayHash vychází z stdClass a ne z pole, takže unset jde volat jen pro existující hodnotu

if (isset($sessionSection->step1['hruska'])) {
  unset($sessionSection->step1['hruska']);
}

Edit: záleží na vnitřní implementaci interface ArrayAccess – vycházel jsem z předpokladu, že error undefined property vznikl díky tomu, že je implementace striktní. Není tomu tak, takže mi chyba nedává moc smysl

Editoval Mabar (13. 5. 2020 10:49)

Polki
Člen | 553
+
0
-

@aha009 Pokud máš uloženo ArrayHash, tak to má i metodu ->offsetUnset(‚name‘); tedy v tvém případě se dá místo

unset($sessionSection->step1['hruska']);

napsat

$sessionSection->step1->offsetUnset('hruska');

@Mabar právě otestováno Nette3/PHP7.3.12

$foo = new \Nette\Utils\ArrayHash();
unset($foo['bar']);
$foo->offsetUnset('bar2');

žádný error žeby ve vyšších verzích vyřešeno?

@DavidGrudl unset je dosti náročná operace (V případě, že offset existuje. Když ne, tak je to zhruba nastejno.). Záleží tedy na něm, jestli mu bude vadit zabráno v session v úložišti o pár bytů navíc, stejně jako když přistoupí k neexistující proměnné, nebo radši bude mít čistý disk ale práci se session pomalejší.
Jasně, pokud bude potřebovat pracovat se session tak, že ji bude procházet foreachem, tak asi nebude chtít pracovat s hodnotami, které tam ‚nejsou‘. V tomto případě by pak asi bylo lepší nastavit warnOnUndefined a hodnoty odstraňovat unsetem, čímž si zajistí, že tam opravdu nebudou. Otázka je, jestli se na to dá spoléhat a stejně nebude muset ověřovat v tom foreachi, jestli tam ty hodnoty, co tam nechce přeci jen nejsou (jestli je někdo nenastavil po tom co se unsetly, nebo jestli je nezapoměl unsetnout?). Netuším. Takto jsem se session ještě nepracoval :D

Pořád mi ale přijde lepší pracovat prostě:

// získání:
$products = $section->products;
if ($products === null) {
	return 'Košík je prázdný';
}
.....
// nastavení:
$section->products = $foo; // kde ve foo je buď pole produktů, nebo null

než třeba:

// získání:
if (!isset($section->products) || $section->products === null) {
	return 'Košík je prázdný';
}
$products = $section->products;
.....
// nastavení:
if ($foo === null) {
	unset($section->products);
} else {
	$section->products = $foo;
}

Editoval Polki (12. 5. 2020 19:36)

Martk
Člen | 661
+
0
-

@Polki Kde je zdroj informací, že je dosti náročná operace? Myslel jsem, že tato funkce označí pro GC, že se má v budoucím cyklu tato alokace uvolnit a na tom mi nepřijde nic náročného, možná jen o něco málo pomalejší než přiřazení hodnoty null.

Stejně musíš kontrolovat existenci přes isset, jinak si zaděláváš na problém v případě, že to tam neexistuje.

Mimochodem

if (!isset($section->products) || $section->products === null) {

lze zapsat jako

if (!isset($section->products))

protože

$test = [null];

var_dump(isset($test[0]));

ti dumpne false

Editoval Martk (12. 5. 2020 20:56)

Polki
Člen | 553
+
0
-

@Martk Už z logiky věci. Všechny pole a proměnné jsou v PHP uloženy ve struktuře, která je takovou hashovací tabulkou a seznamem v jednom. Každé pole (nehledě na to, jestli je index číselný, nebo řetězec) má pořadí prvků, v jakém se tam vložili, má záznam v hashovací tabulce atd. Zde všude je nutno nějak ošetřit, že se daná proměnná smazala. (Přeskočit pořadí ve foreachi, znepřístupnit přes přímý přístup skrze hashtable atd.) Toto z toho dělá náročnější operaci, než jednoduché přiřazení null.

dále z knihy o PHP:
unset(variable)
The unset function (listing 11.18) destroys a variable, causing all memory associated with the variable to be freed. You may accomplish the same effect by setting the variable to NULL.

Larger applications that make use of many variables may consume larger chunks of memory, and conserving memory may become an issue. In this context, PHP needs help identyfiing variables you no longer need. To accomplish this, you have two methods: set the variable to NULL or use the unset function.

If you set variable to NULL, the variable itself remains, but it does not point to any memory. PHP uses small amount of memory itself to maintain the variable, but the memory consumed isn't enough ot be a concern.
The unset function completely removes the variable from the memory. This saves the overhead PHP needs for any variable, and any read of the variable generates the notice.

After using either method, you can test whether a variable contains a value with the isset and empty functions.

Tedy jak sám vidíš, tak jak jsem psal při přiřazení null se prostě na místo v paměti, kde je proměnná prostě uloží místo pointru na data pointer na null, zatímco v případě unset se musí opravit záznamy v hash tabulce, seznamu a navíc ještě uvolňovat samotná data té proměnné datového typu ‚bucket‘. Takže jak jsem psal, unset je náročná operace. Bohužel nezmínil jsem jak náročná :D Tedy oprava: Přiřazení null je náročné na paměť, unset je náročný na čas. Vzhledem ke větě ‚but the memory consumed isn't enough ot be a concern‘ si myslím, že to, že používám přiřazení null místo unsetu je okay.

Mimochodem

if (!isset($section->products) || $section->products === null) {
lze zapsat jako

if (!isset($section->products))

Díky, to mi nedošlo.

Stejně musíš kontrolovat existenci přes isset, jinak si zaděláváš na problém v případě, že to tam neexistuje.

Normálně jo. U Nette sessiony ale ne, jelikož přístup k proměnné ze sessiony, která neexistuje tuto proměnnou vytvoří a přiřadí ji NULL, takže s tím pracuješ pak úplně stejně, jako když ji pomocí NULL vymažeš a isset není nutný. Možná is_null, nebo jak jsem to udělal výše. Vzhledem k tomu ale, že isset pracuje pro nenastavené a pro null stejně, tak by to bylo fajn sjednotit na isset. Jen se mi u isset nelíbí to, že musím k proměnné přistupovat 2× tedy že ji nemůžu získat a pak ověřit její dostupnost. U null to jde. To je pro mě dost výhoda, jelikož nemusím pro ni 2× sahat. Kdyby isset vracelo null, nebo obsah proměnné, bylo by to boží.
Naštěstí v PHP 7 máme
$products = $section->products ?? null;
což tento problém řeší a díky tomu můžeš pak s úplně vším pracovat prostě tak, že víš, že v tom je buď něco, nebo NULL, což je i přívětivější pro datové typy. Rychlý krátký zápis, který vlastně řeší obě dvě čtení, co tam mám napsané.

Jen to nastavování sessiony je pořád to, že jednoduché $section->products = $foo; mi přijde hezčí, než nějaké ifování s unsetem, který mě ještě stojí systémový čas a když potom k té proměnné přistoupím, tak ji Nette stejně vytvoří a to mi přijde jako padlé na hlavu, abych se nějak štval s unsetem, když by se pak dělo to, že unsetem ji smažu, dotazem na existenci ji vytvořím, unsetem ji zase smažu, pak zase vytvořím, to je takový časově náročný ping-pong. Zatímco když přiřazuju null, tak easy prostě jednou se vytvoří a pak je po celou dobu nastavena na null, tedy probíhá jen čtení a zápis a ten overhead neustálým tvořením a mazáním zmizel.

Editoval Polki (12. 5. 2020 22:51)

David Grudl
Nette Core | 8239
+
0
-

Děcka, píšete tu dezinformace…

unset() jde použít i na neexistující prvky a jeho náročnost je nulová

(Vykonání všech unset ve všech aplikacích, které za celý život napíšete, zabere méně času, než přečtení tohoto vlákna).

Unsetnutí zmenší velikost dat v session, což je v praxi asi zanedbatelné, ale pokud bych chtěl mazat tisíc položek, už by to zanedbatelné úplně nebylo.

A nakonec: u session nikdy nevíš, co je jejím obsahem. Mohla být vytvořena před rokem a v té době $session->product nemuselo vůbec existovat. Proto není dobré se na existenci prvků vůbec spoléhat.

Polki
Člen | 553
+
-4
-
C:\wamp64\www\test>php -n ./index.php
Unset took: 0.043650150299072 time and freed 76.29 mb
Set null took: 0.031754970550537 time and freed 76.29 mb
Set null is about 27.251177068199 % faster
Unset is about 0 % less memory consuming
Array with one milion elements setted to null took 0 b of memory

unset() jde použít i na neexistující prvky

nikdo neřekl, že nejde…

a jeho náročnost je nulová

očividně není…

(Vykonání všech unset ve všech aplikacích, které za celý život napíšete, zabere méně času, než přečtení tohoto vlákna).

jistě, pokud někdo nikdy nikam nejel autem, tak taky jeho čas, který kdy strávil na cestách v autě bude menší, než čas přečtení tohoto vlákna. A pokud to myslíš, že kdyby se použily na všech aplikacích psaných v PHP přiřazení null místo unset, tak by součet časů, co by to za celý náš život urychlilo bylo kratší, než přečtení tohoto vlákna, tak nesouhlasím.

ale pokud bych chtěl mazat tisíc položek, už by to zanedbatelné úplně nebylo.

Tak tady asi záleží na způsobu uložení sessiony. Pokud se ukládá do souboru, kde se všechny položky vypisují, tak to zanedbatelné není. Jinak díky peckovní vlastnosti PHP nealokovat paměť, dokud není třeba v tom nevidím problém. No a v případě souboru by pak asi nebylo na škodu zvážit, jestli je nutné při vypnutém warnOnUndefined do toho úložiště vůbec nějaké proměnné ukládat…

A nakonec: u session nikdy nevíš, co je jejím obsahem. Mohla být vytvořena před rokem a v té době $session->product nemuselo vůbec existovat. Proto není dobré se na existenci prvků vůbec spoléhat.

What? Však když prvek neexistuje, tak se sessiona chová, jako kdyby existoval. (Vytvoří jej a naplní jej hodnotou null, kterou pak vrátí.) Takže je mi snad úplně jedno, jestli v sessioně taková proměnná existovala nebo neexistovala ne? A je mi taky úplně jedno, kdy byla session vytvořená. To na tu práci nemá přece vliv…

A pokud ti dělá nepředstavitelný problém Davide uvěřit, že rozdíl ve vykonávání, který je u rychlého 0.9ns, nebo pomalého 1.1ns, tak už na světě byli takoví, kteří si taky říkali, že to je zanedbatelné…

kvůli 2ns, ale spoustě použití bylo zpomalení o 600ms a to můžou být rádi. Viděl jsem už génie, kteří to neřešili, protože proč taky, když počítače jsou rychlé a zákazníci pak byli zoufalí, že jak se zvedl objem dat, tak najednou se stránka načítala přes 30 vteřin… Na to, že třeba Amazon uvádí, že každé zpomalení stránky o 100ms je propad na zisku o 1%, je 600ms docela dost. Já si zakládám na tom, že odezva serveru od mnou vytvořených webů nesmí být nikdy vyšší, než 200ms. A to už je hodně velký strop. Běžně se vlezeme pod 100…

Editoval Polki (13. 5. 2020 1:10)

David Grudl
Nette Core | 8239
+
+5
-

:facepalm:

Šaman
Člen | 2667
+
+3
-

@Polki: Za prvé – induktivní argumentační klam. „Protože jedna z největších internetových služeb zjistila, že optimalizací CSS může nahnat až 600ms“ a „protože největší internetový obchod Amazon napsal, že odezva 100ms představuje průměrnou ztrátu 1% zisku“ a „rozdíl v rychlosti zpracovaní miliónu cyklů je 12ms“, tak bychom měli (běžně) nastavovat null místo unsetovat. Imho srovnáváš nesrovnatelné.

Pokud ti jde o ultimátní rychlost – proč vůbec děláš PHP a ne nějaký kompilovaný nízkoúrovňový jazyk? Tam se dostaneš na o řád kratši časy zpracování (pokud bude kód optimalizovaný).

A jestli nepracuješ s obrovskými datovými poli, pak deset miliónů cyklů (kde by mohlo být reálné zpoždění 120ms) je nejspíš ukázka špatně navrženého algoritmu – nechtěná rekurze apod.

Editoval Šaman (13. 5. 2020 22:50)