PHP a persistence.

Persistencí zde míním zachování stavu zobrazované stránky bez ohledu na skutečnost, že procházíme historií browseru, opustíme stránku a procházíme jinými stránkami, ovšem při návratu je nám opět zobrazena tak, jak jsme ji opustili.
Tato vlastnost může být někdy nežádoucí, ovšem při programování uživatelského rozhraní je důležitá. Pokud uživateli potřebujeme vnutit určitý postup, příkladem mohou být různí průvodci objednávkou zboží, přihlašováním a podobně, kde potřebujeme znát přesný stav uživatelova rozhraní, je tato vlastnost více nežli žádoucí.
Takovéto persistence je dosaženo ovlivňováním vzhledu stránky pomocí proměnných, do kterých ukládáme hodnoty, které je potřeba si zapamatovat. U bezstavového protokolu, jakým protokol HTTP je, je to bezesporu rozšířením jeho funkcionality. U PHP, ale i ostatních jazyků podporujících programování webových stránek, se k tomu používá indexace každého spojení klienta se serverem. Každé takové sezení (session) dostane webovým serverem přiděleno jedinečné označení - index, a tak může být i rozpoznatelné od sezení jiného. Uvedený index nám potom umožní ukložení potřebných proměnných spolu s jejich hodnotami, třeba do databáze, nebo do souboru, uloženého na disku příslušného serveru.
Pod tímto textem nalezneme textová pole, která nám umožní změnit hodnotu, která je v nich zobrazována. Horní pole umožňuje měnit hodnotu vloženou do persistentní proměnné, dolní potom je použito klasicky. Data jsou na server přenesena metodou GET, tj. spolu s hlavičkou HTTP protokolu, jako parametry v URL.
Pokud do obou polí vložíme nějakou hodnotu a potom stiskneme tlačítko 'Ulož' zůstane vložená hodnota viditelná v obou polích. Pokud si vynutíme opětovné překreslení stránky, zůstanou data opět beze změny. Pokud ovšem stránku znovu načteme, řekněme klepnutím na levou nabídku 'Php a persistence', zůstane dolní políčko prázdné, ovšem v horním je stále stejná hodnota. V horním políčku bude vložená hodnota uchována až do chvíle, nežli vyprší doba platnosti sezení, nastavená na webovém serveru, nebo pokud zavřeme okno s instancí browseru, tedy nejenom záložku.
Prohlédněme si dobře zdrojový kód stránky. Uvidíme, že název horního textového pole je poněkud podivný. Ponechme však textový editor s načteným zdrojovým kódem otevřený a vynuťme si překreslení stránky. Opět si nechte zobrazit zdrojový kód stránky a povšimněte si názvu horního textového pole. Porovnáním obsahů obou instancí textového editoru zjistíme, že jméno tohoto prvku je změněno. Přitom ovšem hodnota v textovém poli je stále stejná.
K čemu je dobré měnit názvy proměnných? Ale kvůli hackerům a různým zvědavcům, kteří se nám pokoušejí "nabourat" web, nebo databázi. Pokud si totiž tuto stránku uloží, něco změní a potom ji podstrčí jako kurentní webovému serveru, nezmění vůbec nic. Každý nový dotaz na server má za následek vrácení stránky s jinak pojmenovanými proměnnými. Tím je zmenšena možnost manipulace v případě odchytávání proměnných. Persistentní uložení proměnných na straně serveru nám umožní i poznamenat si čas, kdy byla data uživateli odeslána. Potom můžeme kontrolovat, kdy se nám vrátí uživatelova odpověď, a pokud vzniklý rozdíl bude větší, nežli jsme povolili, můžeme uživatelem odeslaná data odmítnout, a zaslat mu stránku s teď již jinými názvy proměnných a žádostí o nové uložení.
Proměnná "a" - persistentní:
Proměnná "b" - nepersistentní:

Celý postup dosažení persistence potřebných hodnot lze vysledovat v následujícím zdrojovém kódu části této stránky. Pro zjednošení operací s persistentními proměnými jsem si navrhl jednoduchou třídu. Vidíme, že celý postup sestává ze 4 kroků.
  1. vytvoříme instanci třídy, to se provede pouze jednou, stejne jako její definice.
  2. do naší proměnné ($a) vložíme hodnotu, kterou chceme mít persistentní.
  3. zrušíme veškeré persistentní proměnné.
  4. pomocí metody put() uložíme údaje o proměnné spolu s její hodnotou do pole $_SESSION a tak ji zajistíme persistenci. Návratovou hodnotou metody put() je index, čili jméno této proměnné ve formuláři.
Pořadí zde popsaných kroků je potřeba dodržet. Až do použití metody destroy() pracujeme se "starými" proměnnými, po metodě destroy() nastavujeme proměnné nové. Je to z důvodu práce se stále se měnícími názvy proměnných, odesílaných uživateli do formuláře!

<?php

$_SESSION["perCounter"] = 0;

class Persist {
 
 var $counter = 0;
 
 function Persist() {
  global $_SESSION;
  $this->counter = $_SESSION["perCounter"] + 1;
  $_SESSION["perCounter"] = $this->counter;
 }

 function GETS($gets) {
  global $_SESSION;
  if (is_array($gets)) {
   foreach ($gets as $idx => $val) {
    if (isset($_SESSION["perIdx"][$idx])) {
     $vn = split("=",$_SESSION["perIdx"][$idx]);
     $_SESSION[trim($vn[0])] = substr(strstr($_SESSION["perIdx"][$idx],"="),1);
    }
   }
  }
 }

 function POSTS($gets) {
  global $_SESSION;
  if (is_array($gets)) {
   foreach ($gets as $idx => $val) {
    if (isset($_SESSION["perIdx"][$idx])) {
     $vn = split("=",$_SESSION["perIdx"][$idx]);
     $vn = trim($vn[0]);
     $_SESSION[$vn] = $this->check($vn, $val);
    }
   }
  }
 }
 
 function getIndex() {
  global $_SESSION;
  $arrLength = count($_SESSION["perIdx"]);
  $timeIndex = substr(microtime(),5,4);
  return "i".MD5("$arrLength{$this->counter}$timeIndex");
 }

 function putVar($varName, $varValue) {
  global $_SESSION;
  $idx = $this->getIndex();
  $_SESSION["perIdx"][$idx] = "$varName=$varValue";
  return $idx;
 }
 
 function setVar($varName,$varVal) {
  global $_SESSION, $$varName;
  $_SESSION[$varName] = $$varName = $varVal;
 }

 function getVar($varName) {
  global $_SESSION;
  if (is_array($_SESSION)) {
   if (isset($_SESSION[$varName])) {
    return $_SESSION[$varName];
   }
  }
  return "";
 }
 
 function getVars() {
  global $_SESSION;
  if (is_array($_SESSION)) {
   foreach ($_SESSION as $key => $val) {
    global $$key; $$key = $val;
   }
  }
 }

 function destroy() {
  global $_SESSION;
  $_SESSION["perIdx"] = array();
 }

 function unsetVars($varNames) {
  global $_SESSION;
  if (is_string($varNames)) {
   $varNames = array($varNames);
  }
  if (is_array($varNames)) {
   foreach ($varNames as $key => $varName) {
    global $$varName; unset($_SESSION[$varName],$$varName);
   }
  }
 }

 function check($varName, $varVal) {
  $fileName = "$varName.php";
  if (file_exists($fileName)) {
   require($fileName);
  }
  return $varVal;
 }

}

// 1. krok 
$per = new Persist();

// 2. krok
$per->POSTS($_GET);

/*
if (count($_GET) > 0) {
 header("Location: cz_persistencePhp.php");
}
*/

// 3. krok
$per->destroy();

// 4. krok
$idx = $per->putVar("a",$a);

if (isset($_GET["b"])) {
 $b = $_GET["b"];
}

?>

<center>
<form method="GET" action="../cz/cz_persistencePhp.php" target="body">
<input type="text" name="<?php echo $idx; ?>" value="<?php echo $a; ?>">
<input type="submit" name="uloz" value="ULOŽ">
</form>
</center>

Povšimněte si ještě, že jsem ponechal zakomentovány řádky, které umožňují odstranění proměnných, přidaných k URL, viz úvaha "Nechtěný insert". Je to úmyslně, neboť tato metoda se nedá použít bez skutečné persistence proměnných, ať už s pomocí databáze, či session souboru na disku. Přišli bychom tak o pozorování chování proměnné bez persistence.
© 2007 Djordje Zurovac, všechna práva vyhrazena.