24. Odesílání zpráv na email

Pro odesílání zpráv na email (např. pro kontaktní formuláře) existuje mnoho způsobů. Můžeme např. využít funkci PHP - mb_send_mail:

<?php
   mb_internal_encoding("UTF-8");
   $hlaska = '';
    if (isset($_GET['uspech']))
        echo 'Email byl úspěšně odeslán, brzy vám odpovíme.';
    if ($_POST) // V poli _POST něco je, odeslal se formulář
    {
        if (isset($_POST['jmeno']) && $_POST['jmeno'] &&
            isset($_POST['email']) && $_POST['email'] &&
            isset($_POST['zprava']) && $_POST['zprava'] &&
            isset($_POST['rok']) && $_POST['rok'] == date('Y'))
        {
            $hlavicka = 'From:' . $_POST['email'];
            $hlavicka .= "\nMIME-Version: 1.0\n";
            $hlavicka .= "Content-Type: text/html; charset=\"utf-8\"\n";
            $adresa = 'vase-adresa[zavináč]neco.cz';
            $predmet = 'Nová zpráva z mailformu';
            $uspech = mb_send_mail($adresa, $predmet, $_POST['zprava'], $hlavicka);
            if ($uspech)
            {
                header('Location: kontakt.php?uspech=ano');
                exit;
            }
            else
                $hlaska = 'Email se nepodařilo odeslat. Zkontrolujte adresu.';
        }
        else 
            $hlaska = 'Formulář není správně vyplněný!';
    }
?>
        <p>Můžete nás kontaktovat pomocí formuláře níže.</p>
        <?php 
            if ($hlaska)
                echo('<p>' . htmlspecialchars($hlaska) . '</p>');
        
            $jmeno = (isset($_POST['jmeno'])) ? $_POST['jmeno'] : '';
            $email = (isset($_POST['email'])) ? $_POST['email'] : '';
            $zprava = (isset($_POST['zprava'])) ? $_POST['zprava'] : '';
        ?>
                
        <form method="POST">
            <table>
                <tr>
                    <td>Vaše jméno</td>
                    <td><input name="jmeno" type="text" value="<?= htmlspecialchars($jmeno) ?>"/></td>
                </tr>
                <tr>
                    <td>Váš email</td>
                    <td><input name="email" type="email" value="<?= htmlspecialchars($email) ?>"/></td>
                </tr>
                <tr>
                    <td>Aktuální rok</td>
                    <td><input name="rok" type="number" /></td>
                </tr>
            </table>
            <textarea name="zprava"><?= htmlspecialchars($zprava) ?></textarea>
            <br />
        
            <input type="submit" value="Odeslat" />
        </form>

20. Session

Protokol HTTP, který slouží ke komunikaci mezi www serverem a prohlížečem je bezstavový. Tzn., že mezi jednotlivými přechody stránek se neudržuje žádné spojení. Když kliknete na odkaz, pouze se spojí klient se serverem, server pošle stránku a spojení se ukončí. Pokud ale potřebujete znát obsah hodnoty proměnné, kterou uživatel odeslal formulářem asi tak o 3 stránky dříve, bez session se neobejdeme.

Jaká je hlavní činnost session:

  1. identifikaci uživatele
  2. uchovávání obsahu proměnných

Pro identifikaci uživatele stačí správně identifikovat opětovné přístupy od téhož uživatele. Samotná data přiřazená ke konkrétnímu uživateli již není třeba mezi stránkami sdílet, ale lze je ukládat na server a odkazovat se na ně.
Na tomto principu jsou založeny sessions. Při jejich vytváření je uživateli vygenerován unikátní identifikátor sloužící k jeho rozpoznání a umožňující přistupovat ke konkrétnímu datovému souboru. Pro sdílení mezi jednotlivými stránkami tento identifikátor uložíme do cookie, kterou prohlížeč na server odesílá při každém načítání stránky. Data samotná jsou defaultně uložena v serializované podobě v souboru uloženém na serveru a pojmenovaném podle uvedeného identifikátoru.

Příklad http requestu (vyžádání nějaké URL adresy):

GET /wiki/Wikipedie HTTP/1.1
Host: cs.wikipedia.org
Accept-Charset: UTF-8,*

Zde vidíme zcela běžnou obecnou hlavičku, kde není spuštěn příkaz (funkce session) a server odpovídá anonymnímu uživateli.

GET /programujeme-v-php/sessions?rev=1 HTTP/1.1
Host: pehapko.cz
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en,cs;q=0.8
Cookie: PHPSESSID=9g0jg1mgs26c1i0e4hok7rgbd1; nette-browser=spx2kdfq2y

V druhém případě již hlavička obsahuje záznamy o cookies, vidíme tady hned dvě. Cookie s název PHPSESSID je klíč k identifikaci uživatele. Cookie nette-browser není tak důležitý. Identifikuje prohlížeč.

Pokud není na serveru nastavený session.auto_start (což ve výchozím stavu není), zahájíme session relaci funkcí session_start(). Kvůli nastavování cookie musíme tuto funkci volat před jakýmkoliv výstupem, typicky zcela na začátku souboru (až na výjimky...). V této chvíli již můžeme začít pracovat se superglobálním polem $_SESSION (dříve $HTTP_SESSION_VARS).

Hodnotu do session přiřadíme běžným operátorem přiřazení $_SESSION['time'] = time(). Přečíst (s vypsáním) ji můžeme standardní konstrukcí echo $_SESSION['time']. Obecně lze říci, že se session pracujeme jako s běžným polem, můžeme používat in_array, isset, unset a podobně všechny funkce pro práci s poli, jak byly uvedeny v díle o polích. Jediný rozdíl při práci mezi session a běžnými poli je jejich superglobálnost, tj. jsou viditelé v libovolné funkci.
Pozn. Na rozdíl od nastavení cookie pomocí setcookie() se změna session projeví ještě na téže stránce (změněná cookie až po reloadu, protože obvykle neupravujeme přímo $_COOKIE).

Příklad základní práce se session – čas strávený na stránce a další statistiky

<?php
session_start();

$firstVisit = false;
if (!isset($_SESSION['firstVisit']))
{
    $_SESSION['firstVisit'] = new \Datetime;
    $_SESSION['lastVisit'] = new \Datetime;
    $_SESSION['count'] = 0;
    $firstVisit = true;
}
$timeSum = (new \Datetime)->diff($_SESSION['firstVisit']);
$timeLast = (new \Datetime)->diff($_SESSION['lastVisit']);
$lastVisit = $_SESSION['lastVisit'];
$_SESSION['lastVisit'] = new \Datetime;
$_SESSION['count']++;

echo ($firstVisit ? 'Vítejte, jste u nás poprvé.' : 'Děkujeme, že se k nám vracíte.') . PHP_EOL;
echo '<br>Čas první návštěvy: ' . $_SESSION['firstVisit']->format('H:i:s - d.m.Y') . PHP_EOL;
echo '<br>Celkový čas strávený na našich stránkách: '.  $timeSum->format('%H:%i:%s') . PHP_EOL;
echo '<br>Čas předchozí návštěvy: ' . $lastVisit->format('H:i:s') . PHP_EOL;
echo '<br>Čas od předchozí návštěvy: ' . $timeLast->format('%H:%i:%s') . PHP_EOL;
echo '<br>Počet návštěv: ' . $_SESSION['count'] . PHP_EOL;
?>

Vyzkoušejte si tento příklad takto:

  • Aktualizacemi téže stránky se budou měnit časové údaje. Aplikace si pamatuje svoji historii.
  • Otevřete si stránku ve dvou různých prohlížečích (nebo v jednom + anonymním režimu) a paralelně jednotlivé stránky aktualizujte. Ověřte, že se jednotlivé relace neovlivňuji.
  • Naopak otevřete stránku ve dvou běžných oknech prohlížeče (sdílejících cookies). Zjistíte, že nyní se relace vzájemně ovlivňují.
  • Můžete vytvořit několik (volitelně odkazy provázaných) stránek a includovat do nich výše uvedený kód. Zjistíte, že se session sdílí mezi stránkami (session_start() musí být na každé stránce). 
  • Můžete si měnit jednotlivé cookie, když je smažete, bude vaše návštěva označena jako první. Zakážete-li ve svém prohlížeči přijímat cookies, pokaždé bude návštěva označena jako první.
  • Výchozí nastavení pamatování si session bývá 24 minut (1440 sekund), nechte relaci delší dobu neaktivní a pak zkuste aktualizovat. Zřejmě uvidíte, že se v relaci pokračuje a nic se nestalo, mazání starých záznamů je totiž pravděpodobnostní. Existence po dobu 24 minut od poslední aktualizace je zaručena. Smazání po této době nikoliv, to závisí na aktivitě garbage collectoru, která je při výchozím nastavení 0.01, tj. s pravděpodobností 1 % při každé inicializaci smaže staré session záznamy. **Na některých systémech (Debian, Ubuntu) bývá gc zcela neaktivní a mazání periodicky provádí cronová úloha.

Demo k testování.

Zdroj: http://www.pehapko.cz/programujeme-v-php/sessions

22. Administrační zóna

Administrační zónu můžeme vytvořit mnoha způsoby. Zde uvedený způsob je čerpán ze stránek - https://www.allphptricks.com/simple-user-registration-login-script-in-php-and-mysqli/

Byly však upraveny některé části souborů pro naše účely. Nicméně skript není připraven pro ostrý provoz, má bezpečnostní mezery a chybí např. ochrana proti SPAMu.

V první části vytvoříme v databázi tabulku, do které se budou registrovaní uživatelé ukládat. Tady je SQL skript:

CREATE TABLE IF NOT EXISTS uzivatele (
 id int(11) NOT NULL AUTO_INCREMENT,
 uzivatel varchar(50) NOT NULL,
 email varchar(50) NOT NULL,
 heslo varchar(50) NOT NULL,
 datum_reg datetime NOT NULL,
 PRIMARY KEY (id)
 );

Dále bude potřeba mít ve stejném adresáři soubor s přístupovými údaji do databáze - db.php (viz předcházející kapitoly)

Nyní si vytvoříme registrační formulář pro neregistrované (soubor registrace.php). Tento soubor nemusíte vytvářet, pokud nechcete, aby se noví uživatelé mohli registrovat:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Registrace</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<?php
// na začátku si načteme připojení k databázi, které musí existovat
require('db.php');
// Pokud je ve formuláři vloženo jméno uživatele, zašle jej do databáze.
if (isset($_REQUEST['uzivatel'])){
        // příkaz stripslashes odstraní případné zpětné lomítko ve jménu
	$username = stripslashes($_REQUEST['uzivatel']);
        // následující příkaz odstraní případné speciální znaky
	$username = mysqli_real_escape_string($spojeni,$username); 
	$email = stripslashes($_REQUEST['email']);
	$email = mysqli_real_escape_string($spojeni,$email);
	$password = stripslashes($_REQUEST['heslo']);
	$password = mysqli_real_escape_string($spojeni,$password);
	$datum_reg = date("Y-m-d H:i:s");
        $query = "INSERT into uzivatele (uzivatel, heslo, email, datum_reg)
VALUES ('$username', '".md5($password)."', '$email', '$datum_reg')";
        $result = mysqli_query($spojeni,$query);
        if($result){
            echo "<div class='form'>
<h3>Vaše registrace byla úspěšně dokončena.</h3>
<br/><a href='login.php'>Přihlášení</a></div>";
        }
    }
else{
?>
<div class="form">
<h1>Registrace</h1>
<form name="registrace" action="" method="post">
<input type="text" name="uzivatel" placeholder="Uživatelské jméno" required />
<input type="email" name="email" placeholder="Email" required />
<input type="password" name="heslo" placeholder="Heslo" required />
<input type="submit" name="submit" value="Registrovat" />
</form>
</div>
<?php } ?>
</body>
</html>

Dále to bude soubor pro přihlašování - login.php:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Přihlášení</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<?php
require('db.php');
session_start();
// Pokud je ve formuláři něco zadáno, zapíše se to do databáze.
if (isset($_POST['uzivatel'])){
	// příkaz stripslashes odstraní případné zpětné lomítko ve jménu
	$username = stripslashes($_REQUEST['uzivatel']);
        // následující příkaz odstraní případné speciální znaky
        $username = mysqli_real_escape_string($spojeni,$username);
	$password = stripslashes($_REQUEST['heslo']);
	$password = mysqli_real_escape_string($spojeni,$password);
	// následně se kontroluje, zda je zadán jak uživatel i heslo
        $query = "SELECT * FROM uzivatele WHERE uzivatel='$username'
and heslo='".md5($password)."'";
	$result = mysqli_query($spojeni,$query) or die(mysql_error());
	$rows = mysqli_num_rows($result);
        if($rows==1){
	    $_SESSION['uzivatel'] = $username;
            // přesměrování na index.php
	    header("Location: index.php");
         }else{
	echo "<div class='form'>
<h3>Uživatelské jméno nebo heslo je neplatné.</h3>
<br/><a href='login.php'>Přihlášení</a></div>";
	}
    }else{
?>
<div class="form">
<h1>Přihlášení</h1>
<form action="" method="post" name="login">
<input type="text" name="uzivatel" placeholder="Uživatelské jméno" required />
<input type="password" name="heslo" placeholder="Heslo" required />
<input name="submit" type="submit" value="Přihlásit" />
</form>
<p>Nejste zaregistrováni? <a href='registrace.php'>Registrujte se zde</a></p>
<!-- Tento odkaz nevkládejte, pokud nechcete, aby se noví uživatelé mohli registrovat -->
</div>
<?php } ?>
</body>
</html>

Dále je potřeba zajistit autentizaci přihlášeného uživatele přes session - soubor auth.php:

<?php
session_start();
if(!isset($_SESSION["uzivatel"])){
header("Location: login.php");
exit(); }
?>

Nyní si vytvoříme startovací stránku k přihlášení do administrace - index.php:

<?php
include("auth.php");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Vítejte</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="form">
<p>Je přihlášen uživatel - <?php echo $_SESSION['uzivatel']; ?>!</p>
<p>Toto je zabezpečená část webu.</p>
<p><a href="administrace.php">Administrace</a></p>
<a href="logout.php">Odhlásit</a>
</div>
</body>
</html>

Následuje stránka určená pro administraci webu - administrace.php:

<?php
require('db.php');
include("auth.php");
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Administrační zóna</title>
<link rel="stylesheet" href="css/style.css" />
</head>
<body>
<div class="form">
<p>Toto je administrační část webu.</p>
<p><a href="index.php">Titulní strana</a></p>
<a href="logout.php">Odhlášení</a>
</div>
</body>
</html>

Dále potřebujeme stránku pro odhlášení - logout.php:

<?php
session_start();
// vymazání celé session
if(session_destroy())
{
// přesměrování na stránku s přihlášením
header("Location: login.php");
}
?>

Tady je ještě kaskádový styl - soubor style.css:

body {
     font-family:Arial, Sans-Serif;
}
.clearfix:before, .clearfix:after{
     content: "";
     display: table;
}
.clearfix:after{
     clear: both;
}
a{
     color:#0067ab;
     text-decoration:none;
}
a:hover{
     text-decoration:underline;
}
.form{
     width: 300px;
     margin: 0 auto;
}
input[type='text'], input[type='email'],
input[type='password'] {
     width: 200px;
     border-radius: 2px;
     border: 1px solid #CCC;
     padding: 10px;
     color: #333;
     font-size: 14px;
     margin-top: 10px;
}
input[type='submit']{
     padding: 10px 25px 8px;
     color: #fff;
     background-color: #0067ab;
     text-shadow: rgba(0,0,0,0.24) 0 1px 0;
     font-size: 16px;
     box-shadow: rgba(255,255,255,0.24) 0 2px 0 0 inset,#fff 0 1px 0 0;
     border: 1px solid #0164a5;
     border-radius: 2px;
     margin-top: 10px;
     cursor:pointer;
}
input[type='submit']:hover {
     background-color: #024978;
}

Zde najdete demo

21. Počítadlo přístupů

Toto jednoduché počítadlo přístupů v PHP se skládá z několika částí.

V první části příkazem session_start() zajistíme, aby se započítal z naší adresy pouze jeden přístup (tedy alespoň co se týče defaultní doby, po kterou si nás server pamatuje)

Počet přístupů se zapisuje do textového souboru, umístěného např. ve stejné složce, jako samotné počítadlo. V tomto případě se jedná o soubor pocitadlo.txt, který je potřeba si vytvořit.

V druhé části se právě příkazem fopen tento soubor "otevírá" a parametrem "r" zajistíme, aby z něj skript mohl číst (více o funkci fopen).

Dále funkcí fgets přečteme data souboru pocitadlo.txt. Číslice 1000 je maximální počet znaků. Pokud tedy budeme předpokládat vyšší počet, je potřeba číslo zvětšit (více o funkci fgets).

Po přečtení je vhodné soubor zase zavřít - fclose (více o funkci fclose).

Nyní je v paměti tedy uloženo číslo, které reprezentuje posledního návštěvníka a následně jej potřebujeme zvětšit o jedno (+1) a nechat vypsat.

V poslední části je kontrola stavu session a v případě, že se jedná o novou návštěvu, je tento stav opět za pomoci otevření souboru pocitadlo.txt s parametrem "w" (právo zápisu) zapsán a následně je soubor uzavřen.

<?php
 session_start();

// Otevření souboru pocitadlo.txt ke čtení
$data = fopen("pocitadlo.txt","r");
$pocet = fgets($data,1000);
fclose($data);
$pocet=$pocet + 1 ;
echo "$pocet" ;
echo " přístupů" ;
echo "\n" ;

if(!isset($_SESSION['hasVisited'])){
$_SESSION['hasVisited']="yes";
// Otevření souboru pocitadlo.txt k zápisu
$data = fopen("pocitadlo.txt","w");
fwrite($data, $pocitadlo);
fclose($data);
}
?>

Demo počítadla si můžete vyzkoušet.

23. Datum a čas v PHP česky

Pokud potřebujeme, aby se datumové údaje vypisovaly česky, nestačí nám pouze vestavěná funkce PHP Date.

Lze to vyřešit např. pomocí polí:

<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Datum a čas v PHP</title>
</head>
<body bgcolor="#FFFFFF" text="#000000">
<center><font face="Arial CE, Arial" size="5">
<?php
$mesice = array ("ledna", "února", "března", "dubna", "května", "června", "července", "srpna", "září", "října", "listopadu", "prosince");

$den = array("neděle", "pondělí", "úterý", "středa", "čtvrtek", "pátek", "sobota");

echo "Datum a čas pro pokročilé: <br> Je " . $den[Date ("w")]. ", " . Date ("d") . ". ". $mesice[Date ("n") - 1] . " " . Date ("Y") . ", " . Date ("H:i:s") . "<br>";
?>
</font></center>
</body>
</html>