<?xml version="1.0" encoding="utf-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/">
<channel>
	
	<title>RSS 2.0 Článků kategorie "PHP: Základy"</title>
	<atom:link href="https://mike.treba.cz/rss/kategorie/php-zaklady-hotova-reseni/" rel="self" type="application/rss+xml" />
	<link>https://mike.treba.cz/</link>
	<description>RSS 2.0 Článků kategorie "PHP: Základy"</description>
	<lastBuildDate>Wed, 27 Apr 2016 21:49:25 +1100</lastBuildDate>
	<language>cs</language>
	<generator>Abstract CMS</generator>
	<sy:updatePeriod>daily</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>

		
		<item>
			<title>PHP Třída na detekci zařízení</title>
			<link>https://mike.treba.cz/php-trida-na-detekci-zarizeni/</link>
			<pubDate>Wed, 27 Apr 2016 21:49:25 +1100</pubDate> 
			<comments>https://mike.treba.cz/php-trida-na-detekci-zarizeni/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>V sérii předchozích článků na téma YouTube videa jsem několikrát referoval na správnou detekci zařízení. Nic totiž nelze cílit se stoprocentní přesností jenom pomocí @media query, proto je dobré nasadit na responzivní weby i silnější kalibr. Tím samozřejmě myslím PHP knihovnu, která správně určí vstupní zařízení, na kterém se momentálně uživatel nachází. A jedno takové řešení vám dnes představím.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/php-trida-na-detekci-zarizeni/</guid>
			<content:encoded><![CDATA[<p>V sérii předchozích článků na téma YouTube videa jsem několikrát referoval na správnou detekci zařízení. Nic totiž nelze cílit se stoprocentní přesností jenom pomocí @media query, proto je dobré nasadit na responzivní weby i silnější kalibr. Tím samozřejmě myslím PHP knihovnu, která správně určí vstupní zařízení, na kterém se momentálně uživatel nachází. A jedno takové řešení vám dnes představím.</p> <p>Než jsem našel tu správnou knihovnu, strávil jsem nějaký čas na Googlu. Nakonec se ukázalo, že žádná jiná pořádně ani neexistuje. Tedy pokud chceme kvalitní produkt, jehož vývoj stále pokračuje. Autor knihovnu průběžně aktualizuje, což je v případě detekce zařízení nejdůležitější aspekt. Dovolte mi, abych vám představil Mobile Detect Library.</p>
<blockquote>
<p><strong>Mobile Detect Library</strong><br /><a href="http://mobiledetect.net" target="_blank">mobiledetect.net</a></p>
</blockquote>
<p>Nasazení si můžete nastudovat na uvedeném odkazu, pro snazší porozumění uvedu příklad i zde.</p>
<pre><code class="php">include_once('libs/detect/detect.php');
if (Detect::isTablet()) {
	$device = 'tablet';
} elseif (Detect::isMobile()) {
	$device = 'mobile';
} else {
	$device = 'desktop';
}</code></pre>
<pre><code class="html">&lt;body class="&lt;?=$device;?&gt;"&gt;
</code></pre>
<p>Je to opravdu takhle jednoduché. Jenom na jeden malý zádrhel si musíme dávat pozor: metoda Detect::isMobile vrací true i v případě tabletu, proto je nutné dodržet dané pořadí, chceme-li tato dvě zařízení odlišit. Je to vlastně uvedené i na hlavní stránce projektu.</p>
<p>Pak už stačí jen vytisknout proměnnou do třídy &lt;html&gt; nebo &lt;body&gt; a ve stylech můžeme krásně cílit zařízení. Stejně tak v JavaScriptu, kde stačí otestovat element na přítomnost třídy "mobile" respektive "tablet".</p>
<pre><code class="php">if ($('body.mobile').length) {
	// tento blok proběhne pouze na telefonu
}</code></pre>
<p>Třída funguje krásně, nedostatkem může být nejpspíš jen špatná rychlost jednotlivých metod. Pořád je to regulár za regulárem, což chvíli trvá a urychlit to příliš nelze. U mě na webu detekce vezme nějakých 0,02 sekundy, což se nemusí zdát až tak moc, ale pro srovnání - stejně dlouho mi trvá hlavní SQL dotaz na články, ve kterém spojuji víc jak 4 tabulky dohromady...</p>
<p>Každopádně nic lepšího asi neexistuje, a tak si musíme vystačit s tím, co máme zadarmo. A bez práce. Mně detekce slouží dobře: k čemu přesně ji využívám, si řekneme v dalším dílu seriálu "<em>Co jsem dnes udělal pro svůj web</em>".</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/devices.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Návštěvní kniha v PHP s reakcí na příspěvky</title>
			<link>https://mike.treba.cz/navstevni-kniha-v-php-s-reakci-na-prispevky/</link>
			<pubDate>Thu, 14 Jan 2016 13:58:39 +1100</pubDate> 
			<comments>https://mike.treba.cz/navstevni-kniha-v-php-s-reakci-na-prispevky/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Také v novém roce bych rád pokračoval s články v oboru. Mám tu ještě nějaké resty u starších textů, které potřebují revizi. Návštěvní kniha v PHP a MySQL s reakcí na příspěvky byla velmi navštěvovaným článkem, a tak je více než vhodné ji malinko předělat. Začaly se mi tu množit dotazy, proč to nyní nefunguje... </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/navstevni-kniha-v-php-s-reakci-na-prispevky/</guid>
			<content:encoded><![CDATA[<p>Také v novém roce bych rád pokračoval s články v oboru. Mám tu ještě nějaké resty u starších textů, které potřebují revizi. Návštěvní kniha v PHP a MySQL s reakcí na příspěvky byla velmi navštěvovaným článkem, a tak je více než vhodné ji malinko předělat. Začaly se mi tu množit dotazy, proč to nyní nefunguje... </p> <p>Knížku jsem totiž psal v době, kdy PHP5 rozhodně nebylo samozřejmostí. Bylo potřeba dodržovat nějakou zpětnou kompatibilitu, která už je ale v tuto chvíli bezpředmětná. Celou miniaplikaci jsem tedy aktualizoval a stejně jako naposledy si i dnes ukážeme, jak ji rozběhnout na vlastním webu. Také si projdeme některé nové funkce a vychytávky, které jsem zachoval, přidal či zrušil.</p>
<blockquote>
<p>Prosím berte v potaz, že původní článek je z roku <strong>2007</strong>. Hodně se za tu dobu změnilo, a tak návod nemusí odpovídat dnešním standardům. Ukázka ale funguje na <strong>PHP5</strong> a můžete ji využít jako první krok k lepšímu porozumnění PHP.</p>
</blockquote>
<h2>1. Co všechno návštěvní kniha umí</h2>
<p>Než si ukážeme, jak knížku nainstalovat, projdeme si, co všechno vlastně umí. Aplikace se skládá ze dvou skriptů (<em>frontend a backend</em>), instalačního souboru, configu, souboru s funkcemi a tří knihoven. Části kódu jsem se snažil okomentovat tak, aby bylo jasné, k čemu přesně slouží. Co vás tedy zajímá, jsou souboru "<strong>guestbook.php</strong>", "<strong>guestbook-admin.php</strong>", "<strong>config.php</strong>" a případně i "<strong>functions.php</strong>".</p>
<p><strong>Funkce:</strong></p>
<ul>
<li>jednoúrovňová reakce na příspěvky: žádná složitá strukturovaná diskuse, pouze první úroveň odpovědí</li>
<li>základní formátování písma, smajlíci</li>
<li>několik vrstev antispamu</li>
<li>administrace</li>
<li>notifikace na e-mail</li>
<li>ošetření dlouhých řetězců</li>
</ul>
<h2>2. Přístup do databáze</h2>
<p>Tímto začneme. Po rozbalení zipky, ještě před samotnou instalací musíme nastavit přístupové údaje do databáze. Otevřeme soubor "<strong>config.php</strong>" a hned na začátku upravíme:</p>
<pre><code class="php">$config['db']['host'] = 'localhost';	// db server
$config['db']['user'] = 'root';		// uzivatel
$config['db']['pass'] = '';		// heslo
$config['db']['name'] = 'mysql';	// jmeno databaze</code></pre>
<h2>3. Instalace: vytvoření tabulky v databázi</h2>
<p>Tabulku můžeme vytvořit buď manuálně nebo spuštěním skriptu "<strong>guestbook-install.php</strong>". Na první pohled se možná zarazíte, proč se datum ukládá do varcharu namísto jiného, přijatelnějšího formátu: je to pouze kvůli zpětné kompatibilitě. Tak, aby to stále fungovalo, když si vezmete čistě skripty a přepíšete ty původní.</p>
<pre><code class="sql">CREATE TABLE `knizka_2` (
	`id` int(10) unsigned NOT NULL   auto_increment,
	`jmeno` varchar(255),
	`text` text,
	`datum` varchar(255),
	`email` varchar(255),
	`web` varchar(255),
	`addr` varchar(255),
	`r` int(11) DEFAULT '0',
	PRIMARY KEY  (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;</code></pre>
<h2>4. Nastavení</h2>
<p>Tabulka byla úspěšně vytvořena, nyní se můžeme pustit do rozšířeného nastavení guestbooku a podívat se, co všechno umí. Vrátíme se do souboru "<strong>config.php</strong>", kde je uvedeno další nastavení.</p>
<pre><code class="php">// vase IP adresa, pro odliseni komentaru (jmeno bude jinou barvou)
$config['guestbook']['myip'] = '127.0.0.1'; 

// po kolika budeme strankovat (reakce nejsou zapocitany)
$config['guestbook']['per_page'] = 10; 

// po kolika budeme strankovat v administraci
$config['guestbook']['per_page_admin'] = 20; 

// heslo do administrace - NEZAPOMENTE ZMENIT
$config['guestbook']['admin_password'] = '123'; 

// IP adresy, z nichz nebude povoleno zanechavat vzkazy
$config['guestbook']['blacklist'] = array(
    '127.0.0.2',
    '127.0.0.3',
);

// dalsi ochrana proti spamu - pokud bude v poli zprava nektere ze zadanych slov, 
// prispevek se neodesle. (piste malymi pismeny)
$config['guestbook']['spamwords'] = 'anatrim website [url] [/url]';</code></pre>
<p>Takto jsou jednotlivé direktivy okomentovány přímo v souboru. Pokud máte pevnou IP adresu, můžete odlišit vlastní příspěvky jinou barvou. Následuje limit stránkování pro frontend i administraci spolu s heslem do administrace. Poslední 2 proměnné jsou pro vlastní ochranu knížky: můžeme zabanovat otravné spammery a definovat seznam slov, které zamezí uložení příspěvku.</p>
<p><strong>Notifikace na e-mail</strong></p>
<pre><code class="php">// budou chodit notifikace na email
$config['notif']['enable'] = true;

// kam budou chodit notifikace, mozno vice oddeleno strednikem
$config['notif']['to'] = 'vas@email.cz';

// odesilatel
$config['notif']['from_name'] = 'Guestbook - nový komentář';

// vzor predmetu
$config['notif']['subject'] = 'Nový komentář od %s';

// vzor pro telo emailu
$config['notif']['body'] = "
	Od: %s
	
	Zpráva: %s 
	
	Odkaz: %s
";</code></pre>
<p>Nová verze knížky vám nyní pošle e-mail s každou novou zprávou. Nastavení je poměrně intuitivní, jediné, co stačí nastavit, je vaše e-mailová adresa. Pokud notifikace nechcete, stačí řádky zakomentovat. Nebo nastavit první proměnnou na <strong>false</strong>. Nebo prázdný řetězec do adresy. Testování probíhá přes konstrukt !<strong>empty</strong>, takže blok kódu se pak prostě přeskočí. Pro posílání e-mailu používám vlastní jednoduchou knihovnu, její náhradu za PHPMailera by měl zvládnout každý začátečník.</p>
<h3>5. Změny oproti předchozí verzi</h3>
<p>Celou knížku jsem dost překopal. Tabulka ale zůstala nezměněna, takže když starý guestbook smažete a nahradíte novým, o uloženou diskusi nepřijdete. Jak už jsem zmiňoval na začátku, hlavní důvod k nové verzi byla zpětná kompatibilita, která v nových verzích PHP už vyhazuje chyby. Co se tedy změnilo dále?</p>
<ul>
<li>Třídy ve stylech jsou trochu rozumněji pojmenované, aby to případně nic nepřebíjelo.</li>
<li>Nasazena <a href="objekty-v-php5-navrh-databazove-tridy-dil-i-uvod-cile/">databázová třída</a> a <a href="pokrocile-strankovani-php/">stránkovací knihovna</a> - přesně ty, o kterých už jsem psal. Odkazy na články najdete znovu níže jako "<strong>Související</strong>". Až totiž soudruzi z NDR jednoho dne zakážou mysql_ funkce, změna bude pohodlnější.</li>
<li>Změnilo se ukládání data, nyní už ve formátu datetime: 2016-01-12. Kvůli zpětné kompatibilitě se ale stále ukládá do sloupce varchar. Každopádně není problém sloupec přetypovat.</li>
<li>Diskuse už nepřevádí odkazy, protože toto chování považuji za nevyžádané. Stejně tak už se nezobrazuje email - ten uvidíme pouze v administraci.</li>
<li>Chybové hlášky jsou méně "nasí*ací"</li>
<li>Systém reakcí je trochu upraven: už nesměruje na novou url, při reakci na reakci se akorát do příspěvku vloží jméno.</li>
<li>JavaScriptové funkce jsou psány na frameworku jQuery.</li>
<li>Formátovací značky už se vkládají na pozici vybraného textu (není-li vybraný, pak nakonec).</li>
</ul>
<h3>6. Ukázka a link ke stažení</h3>
<p>Ukázka v tuto chvíli není k dispozici. Skript je psaný pro PHP5 a na webu mám nasazené PHP7. Ale ZIPku si stáhnout můžete. </p>
<p class="download-links">ZIP archív ke <strong>stažení</strong>: <a href="https://mike.treba.cz/doc/2016/gb/guestbook.zip" target="_blank">guestbook.zip</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2016/www/guestbook_new.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Formuláře v PHP - ošetření odesílaných dat: díl II</title>
			<link>https://mike.treba.cz/formulare-v-php-osetreni-odesilanych-dat/</link>
			<pubDate>Wed, 4 Feb 2015 21:14:30 +1100</pubDate> 
			<comments>https://mike.treba.cz/formulare-v-php-osetreni-odesilanych-dat/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Další z článků, který po letech potřebuje oprášit jsou Formuláře v PHP - ošetření odesílaných dat. Byť samotné kontroly zůstávají pořád stejné, způsob jejich realizace se po letech vyvinul. Dříve jsme zobrazovali chybová hlášení všechna po kupě někde nad formulářem, pomocí JavaScriptového alertu nebo v případě začátečnické realizace samostatně na externí stránce. </p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/formulare-v-php-osetreni-odesilanych-dat/</guid>
			<content:encoded><![CDATA[<p>Další z článků, který po letech potřebuje oprášit jsou Formuláře v PHP - ošetření odesílaných dat. Byť samotné kontroly zůstávají pořád stejné, způsob jejich realizace se po letech vyvinul. Dříve jsme zobrazovali chybová hlášení všechna po kupě někde nad formulářem, pomocí JavaScriptového alertu nebo v případě začátečnické realizace samostatně na externí stránce. </p> <p>Dnes uživatelé vyžadují větší pohodlí a intuitivnější chování webových aplikací, a tak zobrazujeme chybová hlášení většinou hned vedle daného políčka. Jak jsem už ale zmínil, princip kontrol není třeba měnit. Článek "<a href="je-cas-udelat-wordpressu-papa/">Je čas udělat Wordpressu pápá</a>" se datuje na 1. 10. 2007, tehdy jsem přešel na vlastní redakční systém, kde používám úplně stejné kontroly v komentářích. A od té doby jsem nezaznamenal jediný spam - tedy spam od neživého návštěvníka. Základní prvky ochrany tedy fungují stále dobře a je na čase si je připomenout.</p>
<blockquote>
<p>Prosím berte v potaz, že původní článek je z roku <strong>2007</strong>. Hodně se za tu dobu změnilo, a tak návod nemusí odpovídat dnešním standardům. Ukázka ale funguje <span>na </span><strong>PHP5</strong> a můžete ji využít jako první krok k lepšímu porozumnění PHP.</p>
</blockquote>
<h2>Ukázkový formulář</h2>
<pre><code class="html">&lt;? if(!empty($_GET['sent'])){ ?&gt;
	&lt;p class="success"&gt;Formulář byl úspěšně odeslán&lt;/p&gt;
&lt;? } ?&gt;

&lt;form action="&lt;?=$_SERVER['REQUEST_URI'];?&gt;" method="post"&gt;
	&lt;fieldset&gt;
		&lt;legend&gt;Přidej komentář&lt;/legend&gt;
		
		&lt;label&gt;Jméno &lt;b&gt;*&lt;/b&gt;&lt;/label&gt;&lt;br /&gt;
		&lt;input type="text" name="jmeno" value="&lt;?=(isset($_jmeno) ? $_jmeno : '');?&gt;" /&gt;&lt;br /&gt;
		&lt;?=(!empty($error_messages['jmeno']) ? '&lt;div class="error"&gt;'.$error_messages['jmeno'].'&lt;/div&gt;' : ''); ?&gt;
		
		&lt;label&gt;E-mail &lt;b&gt;*&lt;/b&gt;&lt;/label&gt;&lt;br /&gt;
		&lt;input type="text" name="web" value="&lt;?=(isset($_email) ? $_email : '');?&gt;" /&gt;&lt;br /&gt;
		&lt;?=(!empty($error_messages['email']) ? '&lt;div class="error"&gt;'.$error_messages['email'].'&lt;/div&gt;' : ''); ?&gt;
		
		&lt;label&gt;Web&lt;/label&gt;&lt;br /&gt;
		&lt;input type="text" name="email" value="&lt;?=(isset($_web) ? $_web : '');?&gt;" /&gt;&lt;br /&gt;
		&lt;?=(!empty($error_messages['web']) ? '&lt;div class="error"&gt;'.$error_messages['web'].'&lt;/div&gt;' : ''); ?&gt;
		
		&lt;label&gt;Vaše zpráva &lt;b&gt;*&lt;/b&gt;&lt;/label&gt;&lt;br /&gt;
		&lt;textarea name="text" rows="4" cols="10"&gt;&lt;?=(isset($_text) ? $_text : '');?&gt;&lt;/textarea&gt;&lt;br /&gt;
		&lt;?=(!empty($error_messages['text']) ? '&lt;div class="error"&gt;'.$error_messages['text'].'&lt;/div&gt;' : ''); ?&gt;
		
		&lt;span class="schovany"&gt;
			&lt;label&gt;Tohle políčko ponechte prázdné&lt;/label&gt;&lt;br /&gt;
			&lt;input type="text" name="city" /&gt;&lt;br /&gt;
			&lt;?=(!empty($error_messages['check1']) ? '&lt;div class="error"&gt;'.$error_messages['check1'].'&lt;/div&gt;' : ''); ?&gt;
		&lt;/span&gt;
		
		&lt;label&gt;Kontrola: Opište číslici pět &lt;b&gt;*&lt;/b&gt;&lt;/label&gt;&lt;br /&gt;
		&lt;input type="text" name="skype" /&gt;&lt;br /&gt;
		&lt;?=(!empty($error_messages['check2']) ? '&lt;div class="error"&gt;'.$error_messages['check2'].'&lt;/div&gt;' : ''); ?&gt;
		
		&lt;button name="submit" type="submit"&gt;Odeslat&lt;/button&gt;&lt;br /&gt;
	&lt;/fieldset&gt;
&lt;/form&gt;</code></pre>
<p>Takhle nějak by mohla vypadat základní kostra formuláře. Doporučuji samozřejmě i atributy FOR a ID pro labely a inputy (<em>popřípadě obalit input labelem</em>). Třídu "<em>schovany</em>" nastylujte na <em>display:none</em>. Pokud se na kód podíváte blíže, můžete si všimnout hned <strong>tří úrovní</strong> ochrany proti spamu, které za chvíli rozeberu. Další dvě úrovně ochrany pak budou schované už v obsluze odesílacího skriptu.</p>
<h2>Zpracování dat po odeslání, krok 1: Základní ošetření</h2>
<p>Formulář budeme posílat na stejnou url, na které je, takže zpracování odeslaných dat musí proběhnout ještě před zobrazením jakéhokoli HTML kódu. V případě jednoduchého formuláře, na kterém kontrolu předvádím můžeme začít například takto:</p>
<pre><code class="php">&lt;?php
if(isset($_POST['submit'])){
	$_jmeno = htmlspecialchars(trim($_POST['jmeno']));
	$_email = htmlspecialchars(trim($_POST['web']));
	$_web   = htmlspecialchars(trim($_POST['email']));
	$_text  = htmlspecialchars(trim($_POST['text']));
	
	$_check1 = $_POST['city'];
	$_check2 = htmlspecialchars(trim($_POST['skype']));</code></pre>
<p><strong>htmlspecialchars()</strong> a <strong>trim() </strong>je vše, co k ošetření vstupu potřebujete. Při ukládání do databáze je samozřejmě potřeba myslet na escape uvozovek, ale o databázi se v dnešním článku bavit nebudeme. U větších formulářů pak tato kontrola bude napsaná úplně jinak, ale pro začátek je tato ukázka víc než dostatečná. Ukončující závorku záměrně neuvádím, protože v bloku kódu budeme pokračovat.</p>
<h2>Zpracování dat po odeslání, krok 2: Antispam</h2>
<p>Na pořadí kroku 2 a 3 samozřejmě nezáleží, já zvolil jako první antispam. Jak nahoře ve formuláři, tak zde v prvních pár řádcích PHP jste si mohli všimnout, že jména pole <strong>e-mail</strong> a <strong>web </strong>jsou prohozená. To je první past na roboty. Pokud v poli email bude zavináč, vypíšeme chybu a formulář nezpracujeme. Dále zkontrolujeme odpověď na kontrolní otázku a hodnotu skrytého pole: pokud je vyplněno, opět se jedná o robota. Roboti totiž zpravidla vyplní každé pole formuláře nějakými bláboly - a my si proto řekneme, že právě jedno z polí musí být prázdné. Robot ho vyplní a skončil.</p>
<p>Skrytému poli je také dobré dát nějaký výstižný název, na který se roboti nachytají. V mém případě "city", můžete ale uvést i názvy "email_here" nebo cokoli jiného. I kontrolní pole samo o sobě má název, který láká na vyplnění všeho jiného jen ne jednomístného čísla.</p>
<p>Prohození názvů polí samozřejmě můžeme vynechat. V případě, kdy generujeme celý formulář z databáze a atribut name má hodnoty ve tvaru "input_25", "input_26" a podobně, budou nám bohatě stačit dvě antispamová pole pojmenovaná "email", "skype" či v podobném duchu.</p>
<p>Dále provedeme kontrolu zakázaných slov a počet hypertextových odkazů v těle zprávy. Zakázaná slova můžeme mít uložená v databázi nebo jenom staticky v proměnné, na tom nezáleží. Na čem ale záleží je uvedení těch správných frází, které se často vyskytují ve spamu. Pokud takové slovo vloží sám uživatel, dostane chybové hlášení, slovo smaže a příspěvek může odeslat. Robot ale bude ztracen.</p>
<pre><code class="php">	// antispam
	$evil_words = array('[url', 'viagra', 'website');
	foreach($evil_words as $word){
		if(strpos($_text, $word) !== false){
			$error_messages['text'] = 'Zpráva obsahuje některé ze zakázaných slov: '.implode(', ', $evil_words);
		}
	}
	
	if(substr_count($_text, 'http://') &gt; 5 ){
  		$error_messages['text'] = 'Více než pět hypertextových odkazů není povoleno.';
  	}
  	
  	if(strpos($_web, '@') !== false){
		$error_messages['web'] = 'Jste spam, prosím nebuďte spam.';
	}
	
	if(!empty($_check1)){
		$error_messages['check1'] = 'První kontrolní pole ponechte prázdné.';
	}
	
	if($_check2 != 5){
		$error_messages['check2'] = 'Chybně opsaný kontrolní kód.';
	}</code></pre>
<h2>Zpracování dat po odeslání, krok 2: Kontrola uživatelských dat</h2>
<p>Po prvním bloku kontrol následuje druhý, kde už jenom zkontrolujeme, zda-li jsou vyplněna všechna pole a jestli mají správný formát. Abychom ošetřili samotné tělo zprávy proti dlouhým řetězcům bez mezer, přidáme poslední blok kódu, které rozdělí slova delší než 50 znaků. Pokud je vše v pořádku (<em>proměnná $error_messages nebyla ani jednou naplněna</em>), můžeme provést uložení formuláře a <strong>redirect</strong>.</p>
<pre><code class="php">	// kontrola uzivatelskych dat
	if(empty($_jmeno)){
		$error_messages['jmeno'] = 'Pole Jméno je povinné.';
	}
	
	if(!preg_match('/^[a-zA-Z0-9_.&plus;-]&plus;@[a-zA-Z0-9-]&plus;\.[a-zA-Z0-9-.]&plus;$/', $_email)){
		$error_messages['email'] = 'Pole E-mail má chybný formát.';
	}

	if(empty($_email)){
		$error_messages['email'] = 'Pole E-mail je povinné.';
	}
	
	if(empty($_text)){
		$error_messages['text'] = 'Pole Vaše zpráva je povinné.';
	}else{
		$text_array  = explode(' ', $_text);
		$text_count  = count($text_array);
		$text_return = NULL;
		
		for($i = 0; $i &lt;= $text_count-1; $i&plus;&plus;){
		    $text_array[$i] = wordwrap($text_array[$i], 50, ' ', 1); 
		    $text_return .= $text_array[$i].' '; 
		}
		
		$_text = $text_return;
	}
	
	// uložení dat do DB, odeslání na email &plus; přesměrování
	if(empty($error_messages)){
		$current_url = $_SERVER['REQUEST_URI'];
		$current_url.= (strpos($current_url, '?') === false ? '?sent=1' : '&amp;sent=1');
		
		header('Location: '.$current_url);
		echo '&lt;meta http-equiv="refresh" content="1;url='.$current_url.'" /&gt;';
		exit;
	}
} // konec if(isset($_POST['submit']))</code></pre>
<h2>Vypsání chyb zpět do formuláře</h2>
<p>Jak jste si mohli všimnout v ukázce výchozího HTML, formulář už je na chystaný na zpětný výpis chyb. Pokud bude naplněn některý z klíčů proměnné k tomu určené, na daném místě se vypíše. Pokud bude u některého pole zaznamenáno více chyb, vypíše se vždy jen poslední zaznamenaná: není nutné vypisovat všechny, uživatel formulář odešle a dostane nové chybová hlášení. Když například zapomenu vyplnit e-mail, stačí napsat, že e-mail není vyplněn. Nemusím ještě číst další řádek o tom, že pole neodpovídá regulárnímu výrazu.</p>
<h2>Bonus: GEO IP ochrana</h2>
<p>Ochrana před přístupy z některých vyjmenovaných zemí je sice věc, která by měla být řešená na úrovni PHP (<em>abychom mohli návštěvníkovi vypsat alespoň hlášení, že je ve špatné lokalitě a celé to mohlo být administrovatelné</em>), ovšem pro malé weby cílené na menší region je tohle vcelku zbytečné. IP adresy některé exotických zemí bývají často využíváné spammery, takže zákazem přístupu z dané země můžeme velice jednoduše přidat další úroveň ochrany. Stačí do souboru <strong>.htaccess</strong> vložit následující řádky, popřípadě je rozšířit o další domény.</p>
<pre><code># soubor .htaccess
Deny from .ru # rusko
Deny from .ua # ukrajina
Deny from .cn # cina
Deny from .in # indie
Deny from .sa # saudska arabie</code></pre>
<h2>Ukázka</h2>
<p class="download-links"><a href="https://mike.treba.cz/doc/2015/form/form_example.php" target="_blank">Formulář</a><br /><a href="https://mike.treba.cz/doc/2015/form/form_example.html" target="_blank">Zdrojový kód</a></p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/design-art.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>PHP: Chytré funkce - získání url</title>
			<link>https://mike.treba.cz/zjisteni-url/</link>
			<pubDate>Sun, 28 Feb 2010 15:20:05 +1100</pubDate> 
			<comments>https://mike.treba.cz/zjisteni-url/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>Potřeba stále psát a drobný nedostatek nápadů mě přivedl k myšlence otevření nového miniseriálu. Půjde vždy o takové chytré funkce, kterými si sám usnadňuji programování, které mi ušetří pár řádků kódu. V prvním dílu začnu něčím jednodušším, a to univerzálním zjištěním <strong>aktuální url adresy</strong>. Není to totiž tak triviální, jak by se mohlo na první pohled zdát.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/zjisteni-url/</guid>
			<content:encoded><![CDATA[<p>Potřeba stále psát a drobný nedostatek nápadů mě přivedl k myšlence otevření nového miniseriálu. Půjde vždy o takové chytré funkce, kterými si sám usnadňuji programování, které mi ušetří pár řádků kódu. V prvním dílu začnu něčím jednodušším, a to univerzálním zjištěním <strong>aktuální url adresy</strong>. Není to totiž tak triviální, jak by se mohlo na první pohled zdát.</p> <p>Pro získání aktuální url (například v šabloně pro vypsání odkazu či v kontroleru pro zaslání headeru) je potřeba myslet na spoustu věcí. Kromě <em>REQUEST_URI</em> musíme znát rozdíl mezi proměnnými <em>HTTP_HOST</em> a <em>SERVER_NAME</em>, stejně tak, jako testovat https a případný port.</p>
<p><em>SERVER_NAME</em> obsahuje aktuální doménu včetně subdomén. Pokud je adresa zadaná s www, v proměnné je najdeme, pokud ne, <em>SERVER_NAME</em> je obsahovat nebude. Oproti tomu <em>HTTP_HOST</em> www nikdy neobsahuje, ovšem pokud je v adrese definovaný <strong>port</strong>, v <em>HTTP_HOST</em> ho narozdíl od <em>SERVER_NAME</em> obsahovat bude. V naší funkci využijeme obou proměnných.</p>
<p>Dále funkci doplníme o nepovinný parametr, který rozliší, zda-li má být aktuální adresa vrácena ve <strong>formě pro HTML</strong> nebo ve formě pro header. <strong>Statickou proměnnou</strong> využijeme pro uložení hodnoty v rámci jedné instance a ošetření vícenásobného provádění akce.</p>
<pre><code class="php">function get_url($header = false) {
	static $pure_url = null;
	static $html_url = null;
	
	if (!$pure_url) {
		$url = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://');
		$url .= $_SERVER['SERVER_NAME'];
		$port = explode(':', $_SERVER['HTTP_HOST']);
		if (!empty($port[1])) {
			$url .= ':'.$port[1];
		}
		$url .= $_SERVER['REQUEST_URI'];
		$pure_url = $url;
		$html_url = str_replace('&amp;', '&amp;amp;', $pure_url);
	}
	
	return $header ? $pure_url : $html_url;
}
</code></pre>
<p>Výše uvedená funkce slouží opravdu jen pro zjištění řetězce v adresním řádku. Jestliže potřebujeme dynamicky získávat aktuální umístění projektu, je nám to k ničemu... Vlastně to ani nelze. Pokud je totiž kořenový adresář rewritován, dostaneme jeho skutečné umístění, nikoli to změněné přes mod_rewrite. V takovém případě je nutné mít url projektu vždy uloženou v <strong>globální proměnné</strong> či v databázi.</p>
<p>Další akce, kterou provádím poměrně častěji je vypsání url spolu s přidaným parametrem. Tam je vždy nutné testovat, zda-li aktuální url již obsahuje ampersand nebo otazník a podle toho vypsat patřičný znak pro přidání nového parametru. Za tím účelem jsem si definoval malou funkci <em>get_url_separator</em>, která tento problém vyřeší za mě. V prvním nepovinném parametru mohu předat řetězec, který bude na výskyt otazníku respektive ampersandu testován, v druhém pak opět příznak, jenž rozliší, zda-li bude návratová hodnota použita pro výpis nebo pro <strong>přesměrování</strong>. Ve funkci využiji výše deklarované <em>get_url</em>.</p>
<pre><code class="php">function get_url_separator($url = null, $header = false) {
	if (!$url) {
		$url = get_url();
	}
	
	if (strpos($url, '?') !== false || strpos($url, '&amp;') !== false) {
		return $header ? '&amp;' : '&amp;amp;';
	} else {
		return '?';
	}
}
</code></pre>
<p>To by bylo zatím vše, v pokračování se budu věnovat rewritingu jediné proměnné, jejímu <strong>rozparsování</strong> a následnému využití při <strong>lokalizaci</strong> projektu.</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/keybord.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>Přihlašování a registrace uživatelů pomocí sessions</title>
			<link>https://mike.treba.cz/prihlasovani-a-registrace-uzivatelu-pomoci-sessions/</link>
			<pubDate>Thu, 28 Feb 2008 17:10:11 +1100</pubDate> 
			<comments>https://mike.treba.cz/prihlasovani-a-registrace-uzivatelu-pomoci-sessions/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>...snadno a jednoduše. Doufám, že tento můj článek nebude nošením dříví do lesa. K jeho napsání jsem se rozhodl po přečtení mnoha tutoriálů ohledně registrace a přihlašování uživatelů, které rozhodně nebyly z mého pohledu dostačující. Málokde je zmíněna funkce <a href="http://cz.php.net/session_regenerate_id" target="_blank">session_regenerate_id</a> či možnost předávání SESSID pouze v cookies.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/prihlasovani-a-registrace-uzivatelu-pomoci-sessions/</guid>
			<content:encoded><![CDATA[<p>...snadno a jednoduše. Doufám, že tento můj článek nebude nošením dříví do lesa. K jeho napsání jsem se rozhodl po přečtení mnoha tutoriálů ohledně registrace a přihlašování uživatelů, které rozhodně nebyly z mého pohledu dostačující. Málokde je zmíněna funkce <a href="http://cz.php.net/session_regenerate_id" target="_blank">session_regenerate_id</a> či možnost předávání SESSID pouze v cookies.</p> <p>Na první záblesky lepších časů jsem narazil až na <a href="http://php.vrana.cz/zabezpeceni-session-promennych.php" target="_blank">php.vrana.cz</a> <small>(Mimochodem doporučuji přidat do RSS čtečky)</small>, a tak jsem se vlastně dostal k napsání tohoto svého návodu. A jelikož v jednoduchosti je síla, nebudeme například potřebovat žádná zbytečná volání funkcí htmlspecialchars či nesmyslného includování souborů až po odeslání formuláře, a s pomocí pár základních pravidel nakonec snadno vytvoříme přihlašovací systém a registraci uživatelů.</p>
<blockquote>
<p>Prosím berte v potaz, že článek je z roku <strong>2008</strong>. Hodně se za tu dobu změnilo, a tak návod nemusí odpovídat dnešním standardům. Ukázka ale funguje a můžete ji využít jako první krok k lepšímu porozumnění PHP.</p>
</blockquote>
<p>Ale dost řečí okolo, pojďme na to. Jako základ je samozřejmě potřeba vytvořit SQL tabulku pro uživatele.</p>
<h2>SQL tabulka</h2>
<pre><code class="sql">CREATE TABLE `uzivatele` (
    `id` int(10) unsigned NOT NULL   auto_increment,
    `jmeno` varchar(255)  COLLATE utf8_czech_ci,   
    `heslo` varchar(255) NOT NULL,
    `email` varchar(255)  COLLATE utf8_czech_ci,
    `prava` int(10) unsigned  DEFAULT '1', 
    `aktivni` int(10) unsigned  DEFAULT '1',
    PRIMARY KEY  (`id`),
    UNIQUE (`jmeno`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_czech_ci AUTO_INCREMENT=0 ;</code></pre>
<p>O uživateli budeme ukládat přihlašovací jméno, heslo, registrační e-mail a jeho práva. Sloupec<samp>`aktivni`</samp>bude sloužit k pozdějšímu využití, pro implementaci možnosti schválení registrace administrátorem.</p>
<h2>.htaccess</h2>
<p>Ještě před napsáním prvních řádků skriptu se pustíme do nastavení základních direktiv .htaccessu pro práci se <strong>sessions</strong>. Zde zakážeme předávání <strong>SESSID </strong>v url, nastavíme ukládání pouze do cookies a nakonec jeho platnost. Stejného efektu lze docílit i funkcí <a href="http://cz.php.net/ini_set" target="_blank">ini_set</a>, záleží jen na nastavení hostingu, co vám povolí. Nezapomeneme ani na další pojistku proti SQL injection a zapneme escapovaní uvozovek.</p>
<pre><code>php_flag session.use_only_cookies 1
php_flag session.use_trans_sid 0
php_value session.cookie_lifetime 10800

php_flag magic_quotes_gpc on
php_flag magic_quotes_sybase off
php_flag magic_quotes_runtime off</code></pre>
<p>První dva řádky mimo jiné zabrání i generování invalidního inputu do formulářů. Dále máme na pořadu dne konfigurační soubor. V něm nastavíme údaje pro připojení k databázi, připojíme se k ní a nastartujeme session.</p>
<h2>Soubor config.php</h2>
<pre><code class="php">&lt;?php

error_reporting(E_ALL);

define('DBHOST', 'localhost');		// databazovy server
define('DBNAME', 'mysql');		// jmeno databaze
define('DBUSER', 'root');		// uzivatelske jmeno
define('DBPASS', '');			// heslo k databazi

mysql_connect(DBHOST, DBUSER, DBPASS) or die(mysql_error());
mysql_select_db(DBNAME) or die(mysql_error());

session_start();

?&gt;</code></pre>
<p>Nyní už si můžeme napsat registrační skript. Soubor <strong>register.php</strong> zpracuje data odeslaná z formuláře, zkontroluje a uloží do databáze. Jméno a heslo namísto projedeme navíc i regulárním výrazem, který žádné zlobivé znaky nepustí dál. Povolíme pouze alfanumerické znaky, pomlčku, podtržítko, tečku a e-mail (<em>pokud je někdo zvyklý uvádět e-mail jako login</em>). Kvůli potencionálním chybám v kódování zakážeme i diakritiku.</p>
<h2>Registrace: soubor register.php</h2>
<pre><code class="php">&lt;?php

require_once('config.php');

if(isset($_POST['submit'])){
	$message = array();
	
	$_jmeno = trim(htmlspecialchars($_POST['jmeno']));
	$_email = trim(htmlspecialchars($_POST['email']));
	$_heslo = trim(htmlspecialchars($_POST['heslo']));
	
	// kontrola jmena na prazdnotu a regularnim vyrazem
	if(empty($_jmeno)){
		$message[] = 'Pole jméno je prázdné';
	}else{
		if(!preg_match("/^[_a-z0-9\.\-@]*$/", $_POST['jmeno'])){
			$message[] = 'Neplatné znaky v poli jméno.';
		}
	}
	
	// kontrola hesla na prazdnotu a regularnim vyrazem
	if(empty($_email)){
		$message[] = 'Pole e-mail je prázdné';
	}else{
		if(!preg_match('/^[_a-zA-Z0-9\.\-]&plus;@[_a-zA-Z0-9\.\-]&plus;\.[a-zA-Z]{2,4}$/', $_POST['email'])){ 
			$message[] = 'Zadejte platný e-mail.';
		}
	}
	
	// kontrola hesla
	if(empty($_heslo)){
		$message[] = 'Pole heslo je prázdné';
	}else{
		if($_POST['heslo'] != $_POST['heslo2']) { 
			$message[] = 'Hesla se neshodují';
		}
	}
	
	// zkontrolujeme, zda-li v databazi uz nemame stejneho uzivatele
	$select = mysql_query("SELECT `jmeno` FROM `uzivatele` WHERE `jmeno`='".mysql_real_escape_string($_jmeno)."'");
	if(mysql_num_rows($select)&gt;0) { 
		$message[] = 'Zvolte jiné uživatelské jméno.';
	} 
	
	// ulozime udaje, kdyz neni zadna chyba
	// prava i aktivni nastavime na 1
	if(empty($message)){
		$_heslo = md5($_POST['heslo']);
		$_jmeno = mysql_real_escape_string($_jmeno);
		$_email = mysql_real_escape_string($_heslo);
		
		mysql_query("INSERT INTO `uzivatele` VALUES('','{$_jmeno}','{$_heslo}','{$_email}',1,1)");
		header('Location: ./register.php?ok=1');
	}
}

?&gt;</code></pre>
<p>Dále už nám zbývá jen registrační formulář, HTML část souboru register.php. Nad něj vytiskneme případné chybové hlášky, uvedeme také informace o znacích, které jsou ve jménu povoleny.</p>
<pre><code class="html">&lt;!DOCTYPE html&gt; 
&lt;html lang="cs"&gt; 
&lt;head&gt;
	&lt;meta charset="utf-8" /&gt;
	&lt;title&gt;Registrace uživatele&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;?php if(!empty($message)){?&gt;
	&lt;ul style="color:red;"&gt;
		&lt;?php foreach((array)$message as $item){?&gt;
		&lt;li&gt;&lt;?php echo $item;?&gt;&lt;/li&gt;
		&lt;?php }?&gt;
	&lt;/ul&gt;
&lt;?php }?&gt;

&lt;?php if(isset($_GET['ok'])){ ?&gt;
	&lt;h2&gt;Registrace proběhla úspěšně&lt;/h2&gt;
&lt;?php }else{ ?&gt;
	&lt;ul&gt;
		&lt;li&gt;Jméno a heslo se smí skládat pouze z alfanumerických znaků, pomlčky, podtržítka, tečky a zavináče.&lt;/li&gt; 
		&lt;li&gt;Nepoužívejte diakritiku.&lt;/li&gt;
	&lt;/ul&gt;
	&lt;form action="&lt;?php echo $_SERVER['PHP_SELF']?&gt;" method="post"&gt;
		&lt;fieldset&gt;
			&lt;legend&gt;Registrace uživatele&lt;/legend&gt;
			&lt;p&gt;&lt;input name="jmeno" size="20" tabindex="1" type="text" value="&lt;?php echo (!empty($_POST['jmeno']) ? htmlspecialchars($_POST['jmeno']) : ''); ?&gt;" /&gt; &lt;label&gt;Jméno *&lt;/label&gt;&lt;/p&gt;
			&lt;p&gt;&lt;input name="email" size="20" tabindex="2" type="text" value="&lt;?php echo (!empty($_POST['email']) ? htmlspecialchars($_POST['email']) : ''); ?&gt;" /&gt; &lt;label&gt;E-mail *&lt;/label&gt;&lt;/p&gt;
			&lt;p&gt;&lt;input name="heslo" size="20" tabindex="3" type="password" /&gt; &lt;label&gt;Heslo *&lt;/label&gt;&lt;/p&gt;
			&lt;p&gt;&lt;input name="heslo2" size="20" tabindex="4" type="password" /&gt; &lt;label&gt;Heslo znovu *&lt;/label&gt;&lt;/p&gt;
			&lt;input name="submit" type="submit" tabindex="5" value=" registrovat " /&gt;&lt;br /&gt;
		&lt;/fieldset&gt;
	&lt;/form&gt;
&lt;?php }?&gt;

&lt;p&gt;&lt;a href="login.php"&gt;Přihlásit&lt;/a&gt;&lt;/p&gt;
  
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Nyní k přihlašovacímu skriptu a ověření uživatele. Pro ošetření dat z formuláře použijeme funkci mysql_real_escape_string <small>(mysleme na SQL injection)</small>, provedeme SQL dotaz, při kladném výsledku nastavíme sessiony a přesměrujeme do administrace, při výsledku nulovém zašleme zobrazíme chybové hlášení. Pod parametrem v GETu zde také budeme provádět odhlášení.</p>
<h2>Přihlášení: soubor login.php</h2>
<pre><code class="php">&lt;?php 

require_once('config.php');

if(isset($_POST['submit'])){
	$_jmeno = mysql_real_escape_string(trim($_POST['jmeno']));
	$_heslo = md5(trim($_POST['heslo']));
	
	$select = mysql_query("SELECT * FROM `uzivatele` WHERE `jmeno`='{$_jmeno}' AND `heslo`='{$_heslo}'") or die(mysql_error());
	$udaje  = mysql_fetch_assoc($select);
	
	// pokud je zadano platne jmeno a heslo
	if(mysql_num_rows($select)==1){
		session_regenerate_id(); // osetreni session stealing
		$_SESSION['jmeno'] = $_jmeno; // nastavime sessiony
		$_SESSION['heslo'] = $_heslo;
		$_SESSION['prava'] = $udaje['prava'];
		header("Location: admin.php");
	}else{
		$error_message = 'Chybné přihlašovací údaje';
	}
}
	
// odhlasime se
if(isset($_GET['logout'])){
	$_SESSION['jmeno'] = '';
	$_SESSION['heslo'] = '';
	$_SESSION['prava'] = '';
	unset($_SESSION['jmeno']);
	unset($_SESSION['heslo']);
	unset($_SESSION['prava']);
}

?&gt;</code></pre>
<p>Nakonec už jen vlastní formulář k loginu a můžeme se vesele přihlašovat.</p>
<pre><code class="html">?&gt;
&lt;!DOCTYPE html&gt; 
&lt;html lang="cs"&gt; 
&lt;head&gt;
	&lt;meta charset="utf-8" /&gt;
	&lt;title&gt;Přihlášení&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;

&lt;?php if(isset($_GET['logout'])){ ?&gt; 
	&lt;p&gt;Byli jste odhlášeni ze systému.&lt;/p&gt;
&lt;?php }?&gt;

&lt;?php if(!empty($error_message)){?&gt;
	&lt;ul style="color:red;"&gt;
		&lt;li&gt;Chybné přihlašovací údaje&lt;/li&gt;
	&lt;/ul&gt;
&lt;?php }?&gt;

&lt;form action="&lt;?php echo $_SERVER['PHP_SELF']?&gt;" method="post"&gt;
	&lt;fieldset&gt;
		&lt;legend&gt;Přihlášení&lt;/legend&gt;
		&lt;p&gt;&lt;input name="jmeno" size="20" tabindex="1" type="text" /&gt; &lt;label&gt;Jméno&lt;/label&gt;&lt;/p&gt;
		&lt;p&gt;&lt;input name="heslo" size="20" tabindex="2" type="password" /&gt; &lt;label&gt;Heslo&lt;/label&gt;&lt;/p&gt;
		&lt;p&gt;&lt;input name="submit" type="submit" tabindex="3" value=" přihlásit " /&gt;&lt;/p&gt;
	&lt;/fieldset&gt; 
&lt;/form&gt;
  
&lt;p&gt;&lt;a href="register.php"&gt;Zaregistrovat&lt;/a&gt;&lt;/p&gt;
  
&lt;/body&gt;
&lt;/html&gt;</code></pre>
<p>Na závěr bych ještě krátce zmínil kontrolu uživatelů přímo v administraci. Jelikož jsme paranoidní, budeme při každém obnovení stránky tahat údaje z databáze a kontrolovat je s hodnotou SESSION proměnných. Funkci samozřejmě doporučuji includovat z externího souboru a v každém skriptu ji pouze volat. Ta se postará o kontrolu, zda-li je přihlášen skutečný uživatel, pokud ne, přesměruje na přihlašovací skript s hlavičkou 401. Potom je samozřejmě nutné myslet na to, že v případném formuláři na editaci profilu se musí přenastavit session, změní-li se heslo.</p>
<p>Před vlastním HTML výstupem by tedy měly být následující řádky. Samotné odhlášení bude pouze odkaz na <strong>login.php</strong> s parametrem.</p>
<h2>Zaheslovaná sekce: soubor admin.php</h2>
<pre><code class="php">&lt;?php

require_once('config.php');

// pouze presmerovani
function unauth_header(){ 
    header('Location: login.php?code=401',401);
    exit;
}

// kontrola uzivatele
// na kazdy refresh probehne kontrola cloveka
function check_user(){
	if(isset($_SESSION['jmeno'],$_SESSION['heslo'],$_SESSION['prava'])){
		$select = mysql_query("SELECT `id` FROM `uzivatele` WHERE `jmeno`='{$_SESSION['jmeno']}' AND `heslo`='{$_SESSION['heslo']}'") or die(mysql_error());
		$udaje = mysql_fetch_assoc($select);
		if(mysql_num_rows($select) != 1){
			unauth_header();
		}
	}else{
		unauth_header();
	}
}

check_user();

?&gt;</code></pre>
<p class="download-links">Všechny soubory si můžete <strong>stáhnout</strong> zde: <a href="doc/clanky/prihlasovani/prihlasovani.zip">prihlasovani.zip</a></p>
<p class="color-grey-paragraph">Edit 19.3.2008: Na připomínky čtenářů jsem přidal další direktivy do souboru .htaccess a také otrimování hesla při přihlašování.</p>
<p class="color-grey-paragraph">Edit 21. 1. 2016: Skripty byly upraveny tak, aby nepoužívaly deprecated funkce. Systém kontroly registračního formuláře malinko upraven tak, aby lépe vracel chybové hlášky.</p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2023/renewed/php.jpg</url>
			</image>
		</item>
		
		
		<item>
			<title>PHP od začátku: dynamické skládání stránek funkcí include</title>
			<link>https://mike.treba.cz/php-od-zacatku-dynamicke-skladani-stranek-funkci-include/</link>
			<pubDate>Sun, 27 Jan 2008 14:10:25 +1100</pubDate> 
			<comments>https://mike.treba.cz/php-od-zacatku-dynamicke-skladani-stranek-funkci-include/#comments</comments>
			<dc:creator></dc:creator>
			<description><![CDATA[<p>V dnešním článku bych odbočil od středně pokročilých PHP skriptů a návodů, a vrátil se na chvíli k samotným začátkům většiny z nás, a to prvnímu použití funkce <a href="http://cz.php.net/include/" target="_blank">include</a>. Existuje mnoho konstrukcí, kterými můžeme stránky poskládat z více souborů, a já vám popíši všechny jejich výhody a nevýhody. Pokusím se jít úplně od začátku, od prvního neandrtálského řešení až po šikovnou sekvenci podmínek s ošetřením všech potencionálních chyb a nedostatků.</p>]]></description>   
			<guid isPermaLink="false">https://mike.treba.cz/php-od-zacatku-dynamicke-skladani-stranek-funkci-include/</guid>
			<content:encoded><![CDATA[<p>V dnešním článku bych odbočil od středně pokročilých PHP skriptů a návodů, a vrátil se na chvíli k samotným začátkům většiny z nás, a to prvnímu použití funkce <a href="http://cz.php.net/include/" target="_blank">include</a>. Existuje mnoho konstrukcí, kterými můžeme stránky poskládat z více souborů, a já vám popíši všechny jejich výhody a nevýhody. Pokusím se jít úplně od začátku, od prvního neandrtálského řešení až po šikovnou sekvenci podmínek s ošetřením všech potencionálních chyb a nedostatků.</p> <p>Předpokládejme includování stránek podle proměnné <var>$_GET['page']</var> a odkazy například ve tvaru<samp>index.php?page=kontakt</samp>. Jako nejjednodušší řešení lze vytvořit posloupnost několika podmínek:</p>
<pre><code class="php">if ($_GET['page'] == 'kontakt') {
	include 'inc/kontakt.php';
} else if ($_GET['page'] == 'o-nas') {
	include 'inc/o-nas.php';
} else {
	include 'inc/uvod.php';
}</code></pre>
<p>Jako první nedostatek bych zmínil absenci chybové stránky. Při zavolání adresy například <samp>index.php?page=tohle-neexistuje </samp>je načtena úvodní stránka. Lepší, než kdyby se zhroutil celý vesmír, ovšem my se chceme posunout dál. Proto můžeme vytvořit podmínku, která bude hned jako první, postará se o načtení úvodu při nulové hodnotě proměnné a chybovou stránku 404 zavolá až pod else. Nesprávně bývá uváděno porovnání <samp>if ($_GET['page'] == '') </samp>předcházející dalšímu testování proměnné <var>$_GET['page']</var>, a právě zde narážíme na další chybu: nedefinovaná proměnná totiž nemá hodnotu <strong>empty</strong> nýbrž <strong>null</strong>, tudíž musíme testovat na not null. Upravený zápis by mohl vypadat nějak takto:</p>
<pre><code class="php">if (!isset($_GET['page'])) { // špatně : if ($_GET['page'] == '') 
	include 'inc/uvod.php';
} elseif ($_GET['page'] == 'kontakt') {
	include 'inc/kontakt.php';
} elseif ($_GET['page'] == 'o-nas') {
	include 'inc/o-nas.php';
} else {
	include 'inc/404.php';
}</code></pre>
<p>Tohle řešení je ovšem stále <strong>čiré zlo</strong>. Jestliže máme například 20 podstránek, neustálé opakování if / elseif … je cesta do pekel. A zde přichází konstrukce switch, kterou implementujeme namísto zbytečných podmínek. Nezapomeneme ani na hodnotu null a chybová stránka 404 zůstane pod default. </p>
<pre><code class="php">switch($_GET['page']) {
	case null :
		include 'inc/uvod.php';
	break;
	case 'kontakt' :
		include 'inc/kontakt.php';
	break;
	case 'o-nas' :
		include 'inc/o-nas.php';
	break;
	default:
		include 'inc/404.php';
	break;
}</code></pre>
<p>Mnohem lepší řešení než předchozí uvedené, ale to stále nestačí. Zde zmíním další nedostatek, který první a třetí zápis obsahuje: jestliže nastavíme úroveň výpisu chybových hlášek  <samp>error_reporting(E_ALL)</samp>, zmíněné konstrukce vypisují chybu typu notice. Navíc stejně jako sekvence podmínek musíme i ve switchi neustále přidávat další řádky, tři s každou novou podstránkou. Naštěstí ale přichází v pravou chvíli spásná funkce <a href="http://cz.php.net/file_exists/">file_exists</a>, díky které zkrouhneme desítky zbytečných řádků do jednoho if / else. Hodnotě proměnné přidáme koncovku *.php a zkontrolujeme, zdali daný soubor existuje. Pokud ano, includujeme. </p>
<pre><code class="php">if (file_exists('inc/' . $_GET['page'] . '.php')) {
	include 'inc/' . $_GET['page'] . '.php';
} else {
	include 'inc/uvod.php';
}
</code></pre>
<p>Jako základ stačí, ale my se přece nebudeme spokojení s žádným ořezaným řešením. Výše uvedená konstrukce opět nemyslí na chybová hlášení typu <strong>notice: undefined index</strong>, stejně tak na error 404 při nesmyslné hodnotě proměnné. </p>
<p>Nejdříve otestujeme proměnnou funkcí <a href="http://cz.php.net/isset/" target="_blank">isset</a>, v případě hodnoty false načteme úvodní stránku. Až uvnitř bude kontrola přes file_exists, načteme požadovaný soubor nebo 404ku. Dynamické načítání stránek může nyní vypadat nějak takto:</p>
<pre><code class="php">if (isset($_GET['page'])) {
	if (file_exists('inc/' . $_GET['page'] . '.php')) {
		include 'inc/' . $_GET['page'] . '.php';
	} else {
		include 'inc/404.php';
	}
} else {
	include 'inc/uvod.php';
}</code></pre>
<h2>Závěrečná ošetření</h2>
<p>Problém může nastat ve chvíli, kdy uživatel odhadne naši adresářovou strukturu a vyzkouší, co se stane, když napíše od URL například "index.php?page=../index". V takovém případě nás totiž cesta zavede zpět na spouštěcí soubor, a ten bude includovat sám sebe až do zabití procesu. Nejsnazší řešení je použít "include_once" namísto "include", ovšem tady bychom dostali bílou stránkou. A my chceme zobrazit 404 šablonu. Navíc ošetříme notice, kdyby proměnná page náhodou byla array. Include_once necháme, protože tím člověk nic nezkazí. </p>
<pre><code class="php">if (isset($_GET['page']) &amp;&amp; !is_array($_GET['page'])) {
	$page = str_replace(array('/', '\\'), '', $page);
} else {
	$page = null;
}

if (isset($page)) {
	if (file_exists('inc/' . $page . '.php')) {
		include_once 'inc/' . $page . '.php';
	} else {
		include_once 'inc/404.php';
	}
} else {
	include_once 'inc/uvod.php';
}</code></pre>
<h2>Unikátní titulek pro každou stránku</h2>
<p>Bohužel, dynamická změna obsahu nestačí, je také vhodné tisknout každé podstránce unikátní titulek, keywords či description. Tuto funkcionalitu ale už nebudeme montovat do indexu, ale zanoříme ji k ostatním šablonám. <strong>Tedy výše uvedený blok kódu může být celý index.php</strong>. </p>
<p>Ošetření dalších includů uvnitř šablon lze realizovat několika způsoby. Podadresář. Jiný adresář v rootu. Jiná přípona. Já zvolil jinou příponu, protože šablony patří k sobě a měly by u sebe zůstat. </p>
<p>Nyní k samotnému titulku: z každé šablony "úvod", "o-nas" a "kontakt" vyjmeme hlavičku a patičku, vložíme ji jedinkrát do dalšího souboru a tam vyechujeme nastavenou proměnnou. Toť vše. Keywords a description můžeme vyřešit stejně.</p>
<h4>uvod.php</h4>
<pre><code class="php">&lt;?php
	$title = 'Úvod'; 
	include_once '_header.phtml'; 
?&gt;

	&lt;h1&gt;Úvodní stránka&lt;/h1&gt;

&lt;?php include_once '_footer.phtml'; ?&gt;</code></pre>
<h4>_header.phtml</h4>
<pre><code class="php">&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
	&lt;meta charset="utf-8"&gt;
	&lt;title&gt;&lt;?php echo (!empty($title) ? $title : ''); ?&gt; | Můj web&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
	
	&lt;header&gt;
		Header
	&lt;/header&gt;
</code></pre>
<p>To by bylo snad vše, co jsem na srdci měl, a pro ty z vás, kteří by si chtěli stáhnout těch pár souborů, na kterých jsem toto testoval, nabízím ukázku <strong>ke stažení</strong>.</p>
<p class="download-links"><a href="doc/clanky/include/include.zip">ZIP archív</a></p>
<p class="color-grey-paragraph">Edit 26. 4. 2020: Článek byl zrevidován za účelem vhodnější struktury samotných šablon a lepšího ošetření vstupu. </p>]]></content:encoded>
			<image>
    				<url>https://mike.treba.cz/img/2020/web/woman-working-at-home-using-her-laptop-4050290.jpg</url>
			</image>
		</item>
		
	
</channel>
</rss>