27. května bude sraz!

Materiály ke studiu PHP


# Kde hledat víc informací

• Především PHP manuál (php.net), anglicky. Reference jazyka, reference funkcí. Nevíte, jak funguje nějaká funkce? Určitě ji vyhledejte v manuálu, např. explode().

• Rozpracovaná učebnice PHP od členů djpw Základní kurz 1: Úvod.

Pokročilejší články a moderní trendy ve vývoji aplikací v PHP lze najít na Zdrojáku s nálepkou PHP (databáze).

• Kdysi dobrý, ale dnes zastaralý PHP tutoriál (linuxsoft.cz). Je psán pro PHP 4, v současnosti nebudou jeho scripty s defaultním nastavením fungovat. Inspirujte se, ale neberte vše doslova.

Mírně pokročilejší témata: PHP triky, phpFashion.

• Anglicky psané net.tutsplus.com/category/tutorials/php, phpmaster.com/category/php-tutorials a nikic's Blog

• MySQL: MySQL Documentation (anglicky), MySQL >= 4.1 a čeština

• SQLite: SQLite.org

• Slepice nebo vejce? Podobně věčná je i otázka jestli používat framework, než založite vlastní téma, přečtěte si Framework: Ano, či ne?, pokud se pro nějaký framework rozhodnete, s výběrem může pomoci Best PHP Framework for 2015 - SitePoint Survey Results.

Tematicky zaměřené odkazy


# PHP 7

Stručná prezentace novinek v PHP 7
Detailní přehled změn v PHP 7

I pokud vás nezajímají nové vlastnosti, měla by vás zajímat změna těch starších.
Throwable Exceptions and Errors in PHP 7 - Aaron Piotrowski
PHP: rfc:remove_deprecated_functionality_in_php7, zdůrazňuji konec ext/mysql.

# Výkonnost aplikace

Disproving the Single Quotes Performance Myth

# Escapování

Escapování - definitivní příručka

# Uvozovky


# Regulární výrazy

Online testování a stručný přehled
Podrobné shrnutí syntaxe, návody a hotová řešení
Seriál na rootu
V PHP používejte funkce z rodiny preg_, skupina ereg_ je deprecated (tj. zavržená, v dalších verzích bude zrušena).
Pro testování shody a získání výsledků preg_match() a preg_match_all().
Pro úpravy řetězce preg_replace().
Modifikátory v PHP manuálu.
Další odkazy:
Regulární výrazy na wikipedii
phpfashion.com/zradne-regularni-vyrazy-v-php

Triviální způsob includování obsahu stránky spolu s titulkem


# Cron

Úlohy v PHP na serveru Programujte.com

Řešení nejčastějších chyb

Vypsal vám PHP interpret některou z následujících hlášek? Nebo pozorujete některý z následujících problémů? Zde snad naleznete řešení.

# Parse error: syntax error, …

Chyba syntaxe znamená, že máte špatně napsaný skript. Zde dvojnásob platí: Zkuste to řešit samostatně předtím než pošlete dotaz do diskuse. I začátečník by měl znát základní syntaxi jazyka a být schopný si opravit překlep.

Jak na tuto chybu? Hláška vypadá nějak takto:

Parse error: syntax error, unexpected něco in /cesta/soubor.php on line číslo.
Česky to znamená zhruba: Chyba zpracování: Chyba syntaxe, neočekávané (něco) v souboru (cesta) na řádku (číslo).

Zvýrazněné části jsou název souboru a číslo řádku. Překontrolujte kód na daném řádku daného souboru a také o řádek výše (např. zapomenutý středník na konci předchozího příkazu).

Pokud přesto nic neobjevíte, pošlete dotaz do diskuse, přiložte několik řádků kódu kolem problémového místa (nekopírujte celé skripty s desítkami/stovkami řádků kódu!) a označte řádek, kam odkazuje chybová hláška.

Tip: Syntaktickým chybám lze předcházet používáním vhodného PHP editoru (respektive IDE), kde se syntaktické chyby zvýrazňují už během psaní.

# Warning mysql_*: supplied argument is not a valid MySQL result resource…

případně Warning: mysql_num_rows() expects parameter 1 to be resource, boolean given…

Znamená, že nejspíš máte chybu v SQL dotazu, jehož výsledek se snažíte přečíst.

Funkce mysql_query může krom výsledku dotazu vrátit i FALSE (Viz manuál) a ve skriptu byste s tím měli počítat.

Příklad:
$result = mysql_query($sql);
if(!$result){ /* Kód pro zalogování chyby a zobrazení nějaké chybové stránky uživateli. */ }
K jaké konkrétně chybě v SQL dotazu došlo zjistíte pomocí mysql_error()- k problémům projevujícím se výše uvedenou hláškou přikládejte rovnou i výsledek mysql_error()!

Poznámka: Stejně jako u všech ostatních chyb, ani tady není vhodné nechat chybové hlášky vypisovat na ostrém webu přímo do stránky, tedy např.: mysql_query($sql) or die(mysql_error()); je přípustné pro testovací prostředí, ale na ostrém webu by to nemělo být.

$query = 'select * from tabulka where id = 5';
$result = mysql_query($query);
if (!$result) { # zde by šlo doplnit podmínku && DEBUG == true, ale začátečníci to asi řešit nebudou a pak použijí jiný db layer
  echo "<br>\n", htmlspecialchars($query, ENT_QUOTES), "<br>\n", mysql_error(), "<br>\n";
}
while ($item = mysql_fetch_assoc($result)) {
  // normální zpracování výsledku, 
  // stále je ale možné, že nebyly nalezeny žádné výsledky, to nekontrolujeme, viz mysql_num_rows()
}

# Headers already sent…

Pokud už byl generovaný nějaký výstup, tak už nelze používat funkce header(), setcookie() ani session_start(). Jejich volání skončí chybou.
Přesvědčete se, že před začátkem <?php ?> není žádný text (ani mezera) a že nebylo nic odesláno třeba funkcemi jako echo nebo print.

# Nefungují proměnné z formuláře nebo z URL (vypnuté register globals)

K hodnotě vstupního pole s názvem „nazev“ se dříve přistupovalo přes proměnnou $nazev. Z bezpečnostních důvodů je dnes tato možnost na většině serverů zakázaná.
K hodnotám zaslaným metodou POST lze přistupovat přes $_POST['nazev'], metodou GET přes $_GET['nazev']. K proměnným z cookies přes pole $_COOKIE.

Případně lze využít pole $_REQUEST, ve kterém jsou všechny proměnné zaslané metodou GET, POST i v cookies.
Podobně lze získávat hodnoty serverových proměnných z pole $_SERVER a session z $_SESSION.
Chceme-li uploadovat soubor, pracujeme s $_FILES.

Register globals lze zapnout (pokud je to povoleno) vložením následujícího kódu do .htaccess. Tento přístup ale nelze doporučit a navíc není od verze PHP 5.4* podporován.
php_flag register_globals on
*PHP 5.4 bylo vydáno v roce 2012, verze 5.3 a nižší jsou v dnešní době považovány za staré (tj. nepodporované) a register globals tudíž brzy nebude fungovat nikde.


# Lomítka ve vstupních hodnotách

Při určitém nastavení PHP se v hodnotách z formulářů samo přidá zpětné lomítko před znaky uvozovek ("), apostrofů (') a zpětných lomítek (\).
Pokud je to tak nastavené, funkce get_magic_quotes_gpc() vrací 1, jinak 0. Zařídit, aby $_GET['promenna'] nebyla takto olomítkována lze takto:
<?php
if (get_magic_quotes_gpc()) {
    $_GET['promenna'] = stripslashes($_GET['promenna']);
}
?>
Vypnutí magic_quotes_gpc


Jestliže se ve skriptu volá session_start(), je obvykle rozumné volání dát hned jako první příkaz, případně nejblíže začátku skriptu jak je možné.
Pokud je text v UTF-8, tím výstupem může být i tzv. BOM signatura, vizte výše zmíněný Divný znak (čtvereček).


# Kam zmizelo odřádkování za „?>

Pokud je hned za ukončením PHP ještě znak „\n“, tak je ignorován. Je tomu tak proto, že v tzv. unixových systémech je zvykem na konec každého souboru dát ještě odřádkování.


# Allowed memory size... (Překročení paměti)

Fatal error: Allowed memory size of 16777216 bytes exhausted (tried to allocate 9216 bytes) in …
Nejčastěji se tato chyba vyskytuje při úpravě velkých obrázků. Knihovna GD musí otevřít obrázek, který je mnohem větší než např. *.jpg soubor, a překročí maximální povolenou paměť.
Bez změny nastavení hostingu obtížně řešitelný problém, jedna z možností je použít např. ImageMagick (jím obsazená paměť nespadá do limitu pro php).
Potřebná velikost paměti pro zmenšení obrázku nesouvisí s datovou velikostí obrázku (jpg je komprimované), ale závisí na rozměrech obrázku, protože pro úpravu musí být původní kompripomavý obrázek rozbalen do bitmapy. Výslednou velikost obsazené paměti lze odhadnout z počtu pixelů a barevného hloubky s korekčním koeficientem pro režii. Také nezapomeňte, že se do paměti musí vejít původní i nový obrázek. Kalkulátor pro výpočet nalezneta na www.dotsamazing.com/en/labs/phpmemorylimit.

Jak něco udělat


# Získaní IP adresy

IP adresu lze najít v poli $_SERVER, viz register globals

echo 'Vaše IP adresa je: '.$_SERVER['REMOTE_ADDR'];

# Jak předat proměnnou vkládanému souboru

Souboru vkládaném pomocí include/require nelze předat proměnnou tímto způsobem:
<?php include 'soubor.php?e=mc2'; // NELZE !!! ?>
Jednoduše proto, že nejde o komunikaci HTTP protokolem, ale o obyčejné načtení souboru. Program ve vloženém souboru ale může využívat všech proměnných jako program v místě vložení, takže je možné „předat“ proměnnou například takto:
<?php
$promenna = 'hodnota';
include 'soubor.php';
?>

Předání title z includovaného souboru do indexu

# Co dávat a co nedávat do uvozovek

Do uvozovek či apostrofů se dávají pouze řetězce. Nepište uvozovky (ani apostrofy) kolem hodnot jiných datových typů, ani kolem proměnných:
$text = "Ahoj!"; // správně
$druhy = "$text"; // špatně, zde uvozovky být nemají
$cislo = "10"; // špatně, čísla pro matematické výpočty se nepíší do uvozovek
$cislo = 10; // správně
$logicka = "false"; // velmi špatně, logické hodnoty nikdy nedávejte do uvozovek, protože "false" se převede na true!
$logicka = false; // správně
$telefon = "123456789"; // správně, sice je to telefonní „číslo“, ale obvykle s ním nechcete dělat matematické výpočty a v PHP ho používáte jako řetězec
Poznámka, jak demonstruje poslední ukázka, někdy máte hodnotu, která je například číslo, ale v PHP ji používáte jako řetězec. Pak samozřejmě do uvozovek patří. Stejně jako když třeba chcete text "true" nebo "false" a ne logickou hodnotu, například:
// zde chceme řetězec "true" a ne logickou hodnotu true, proto použijeme uvozovky
$pozice = strpos("Slovo true je obsaženo v tomto řetězci", "true"); // vyhledáváme v řetězci slovo "true"

# Práce s XML

Metody parsování XML
Problémy s e-maily

Jednoduché použití funkce mail() se 3 povinnými parametry často nestačí.

# Kódování e-mailu, aneb abychom neposílali otazníky.

Text musíme posílat s uvedeným kódováním. Dříve se doporučovalo ISO-889-2, dnes bych neměl strach z UTF-8 a vzhledem k předpokládanému vývoji ho doporučuji.

Kódování předmětu (a dalších hlaviček) a zprávy samotné se zadávají samostatně.
Předmět zakódovat musíme, zakódováním těla zprávy nic nezkazíme.
Hlavičky by mohly vypadat nějak takhle:
$head = "MIME-Version: 1.0".PHP_EOL;

Zde je zadané kódování zprávy. Když budeme posílat html mail, zde změníme na text/html.
$head .= "Content-Type: text/plain; charset=\"utf-8\"".PHP_EOL;

Tuto hlavičku uvedeme, pokud kódujeme i tělo zprávy.
$head .= "Content-Transfer-Encoding: base64".PHP_EOL;


To by ze základních hlaviček mohlo být vše. Teď kódování předmětu:
$predmet = "=?utf-8?B?".base64_encode(autoUTF($predmet))."?=";

Všimněte si, že pracujeme s utf-8 a používáme funkci autoUTF(), viz následující.
Pokud budeme chtít použít jméno, poskládáme hlavičku takto:
"From: =?UTF-8?B?".base64_encode(autoUTF("Moje Jméno"))."?=<ja@email.cz>".PHP_EOL;


Už zbývá jen zakódování zprávy samotné, opět použijeme base64_encode()
$zprava = base64_encode(autoUTF($zprava));


Abychom bez námahy získali text v utf, použijeme k tomu funkci od Davida Grudla. Dokážete-li získat data ve správném kódování (např. máte vše v utf-8), lze tento krok vynechat.
To je vše, vypadá to trochu složitě, ale vše můžeme naskládat do vlastní funkce.


<?php
/* *************** diskuse.jakpsatweb.cz *** PHP FAQ ********************** */
function autoUTF($s)
{
    if (preg_match('#[\x80-\x{1FF}\x{2000}-\x{3FFF}]#u', $s)) // detect UTF-8
    {
        return $s;
    }
    elseif (preg_match('#[\x7F-\x9F\xBC]#', $s)) // detect WINDOWS-1250
    {
        return iconv('WINDOWS-1250', 'UTF-8', $s);
    }
    else // assume ISO-8859-2
    {
        return iconv('ISO-8859-2', 'UTF-8', $s);
    }
}

function cs_mail($to, $predmet, $zprava, $head = "")
{
    $predmet = "=?utf-8?B?".base64_encode(autoUTF($predmet))."?=";
    $head .= "MIME-Version: 1.0".PHP_EOL;
    $head .= "Content-Type: text/plain; charset=\"utf-8\"".PHP_EOL;
    $head .= "Content-Transfer-Encoding: base64".PHP_EOL;
    $zprava = wordwrap(base64_encode(autoUTF($zprava)), 78, PHP_EOL, true);
    return mail($to, $predmet, $zprava, $head);
}
/* ********************************************************************** **  */


Jak to použít? Zkopírujte si do stránky výše uvedený kód a pak stačí tuto funkci zavolat.
Ukázka použití:
<?php
$mail = 'jmeno@email.cz';
$predmet = 'ěščřžýáíé46';
$zprava = "Test, \r\n ěščřžýáíé123456789";

if (cs_mail($mail, $predmet, $zprava, "From: vas@web.cz".PHP_EOL))
{
    echo 'E-mail byl úspěšně odeslán.<br>';
}
else
{
    echo 'E-mail se bohužel nepodařilo odeslat.<br>';
}

To je vše, už jen vysvětlit. $mail obsahuje e-mailovou adresu příjemce, $predmet předmět e-mailu a $zprava zprávu samotnou. To je asi jasné. 4. parametr obsahuje další hlavičky, oddělené koncem řádku.

# Odeslání více položek

Často máme obsáhlejší formulář a potřebujeme z něj odeslat všechny jeho položky, řešení je jednoduché. Stačí všechny proměnné spojit do $zprava.
Pokud posíláme mail jako html, je vhodné ošetřit vstupy funkcí htmlspecialchars($s, ENT_QUOTES).
$zprava = "Jméno: {$_POST['jmeno']}".PHP_EOL;
$zprava .= "Příjmení: {$_POST['prijmeni']}".PHP_EOL;
$zprava .= "Telefon: {$_POST['telefon']}".PHP_EOL;
$zprava .= "Ulice: {$_POST['ulice']}".PHP_EOL;
$zprava .= "Žádost: {$_POST['zadost']}".PHP_EOL;
$zprava .= "Poznámka: {$_POST['poznamka']}".PHP_EOL;

Teď stačí $zprava vložit jako třetí parametr funkce mail.

Pro složitější věci (např. přílohy) doporučuji PHPMailer nebo Nette mail.

# Rekurzivní zipování

Skript pro rekurzivní zipování souborů

# Registrace uživatelů


Zde původně uváděný script již neodpovídá současnému stylu programování a nechci ho nadále doporučovat. Než se shodneme na nové verzi, doporučuji přihlašovací script gist.github.com/JanTvrdik/8d813cfc7a4a8e0db2c7 od Jana Tvrdíka.

# Solený hash

Stručné vysvětlení principu soli od Jokera
Podrobnější informace o bezpečnosti hesel, nejen solení. Taktéž od Jokera.

# Výpis výsledků z databáze (nebo z jiného zdroje) do tabulky o více sloupcích

Testovcí tabulka obsahuje 7 dní.
CREATE TABLE `dny` (
  `den` varchar(10) default NULL
);

INSERT INTO `dny` VALUES ('Pondělí'), ('Úterý'), ('Středa'), ('Čtvrtek'), ('Pátek'), ('Sobota'), ('Neděle');


Řazení po řádcích
Výsledek vypadá takhle:
1. Pondělí    2. Úterý    3. Středa
4. Čtvrtek    5. Pátek    6. Sobota
7. Neděle

Kód, který to zajistí:
$result = mysql_query("select den from dny");

define ("COLS", 3);  // počet sloupců

echo "<table>\n";
for ($i = 0; $zaznam = mysql_fetch_assoc($result); $i++)
{
  if ($i % COLS == 0) echo "<tr>";
  echo "<td>".($i+1).". ".$zaznam['den']."</td>";
  if ($i % COLS == COLS - 1) echo "</tr>\n";
}
if ($i % COLS != 0)
{
  while ($i++ % COLS != 0)
  {
    echo "<td>&nbsp;</td>";
  }
  echo "</tr>\n";
}
echo "</table>\n";


Řazení po sloupcích
Tentokrát to vypadá takhle:
1. Pondělí    4. Čtvrtek    7. Neděle
2. Úterý      5. Pátek    
3. Středa     6. Sobota

A kód, kterým to vytvoříme:
$result = mysql_query("select den from dny");

while ($line = mysql_fetch_assoc ($result))
  $zaznamy[] = $line;

define ("COLS", 3);

$pocetRadku = ceil(count ($zaznamy) / COLS);
echo "<table>\n";
for ($i = 0; $i < $pocetRadku; $i++)
{
  echo "<tr>";
  for ($j = 0; $j < COLS; $j++)
  {
    echo "<td>";
    if (isset($zaznamy[$j*$pocetRadku + $i]))
      echo ($j*$pocetRadku + $i + 1).". ".$zaznamy[$j*$pocetRadku + $i]['den'];
    else
      echo "&nbsp;";
    echo "</td>";
  }
  echo "</tr>\n";
}
echo "</table>\n";

# Odlišení lichých a sudých řádků

Při výpisu dat (nejen z databáze) často potřebujeme odlišit liché a sudé řádky.

Vytvoříme si dvě CSS třídy.
.licha {font-weight: bold; color: #00f}
.suda {font-style: italic; color: #063}

V praxi bychom asi měnili jen pozadí, ale to nemohu v této diskusi demonstrovat.

Teď již zbývá jen vypsat data a odlišit jednotlivé řádky. Využijeme tabulku z předchozího přikladu.

$result = mysql_query("select den from dny");  // výběr dat z databáze

echo "<table>";
$m = 0; // pomocná proměnná, můžeme ji zároveň použít pro číslování řádků
while ($line = mysql_fetch_assoc($result)) // data vypisujeme v cyklu
{
  echo "<tr class=\"".(++$m % 2 ? "licha" : "suda")."\">";  // tady se za pomoci modula a ternárního operátoru vypíše vhodná třída
  echo "<td>$m. {$line['den']}</td>"; // zde je $m využita jako číslo řádku
  echo "</tr>\n";
}
echo "</table>";

Jak to funguje?
Hodnota proměnné $m se při každém průchodu cyklem zvyšuje o 1. Využitím matematické funkce modulo (zbytek po celočíselném dělení) určíme, jestli je daný řádek lichý nebo sudý, a pomocí ternárního operátoru vložíme správnou třídu.
Jak to vypadá?
Výhodou tohoto řešení je možnost využít $m k číslování řádků.
1. Pondělí
2. Úterý
3. Středa
4. Čtvrtek
5. Pátek
6. Sobota
7. Neděle

Další možností (a tentokrát zapsanou přehedněji) je neustálé prohazovaní hodnot true a false v jedné proměnné.
echo "<ul>";
$suda = false;
while ($line = mysql_fetch_assoc($result))
{
  $trida = $suda ? "suda" : "licha"; // stejné jako minule
  $suda = !$suda; // ! neguje současnou hodnotu a negaci ukládá pro příští iteraci
  echo "<li class=\"$trida\">{$line['den']}</li>";
}
echo "</ul>";

Tentokrát ukázka generuje seznam.
Pondělí
Úterý
Středa
Čtvrtek
Pátek
Sobota
Neděle

# Upload souborů

Upload a download souborů na Linuxsoftu
PHP triky – Zabezpečený upload souborů
PHP triky – Velikost uploadovaných souborů
PHP triky – Zmenšení obrázků

# Vypsání náhodného obrázku

<?php
$obrazky = array(  // pole s adresami obrázků
  "img/9.jpg",
  "img/21.jpg",
  "img/e02.jpg",
);
shuffle($obrazky);
echo "<img src=\"$obrazky[0]\">";  // jeden náhodný
echo "<img src=\"$obrazky[1]\">";  // druhý náhodný
?>


Jestli chceme vybírat ze všech .jpg souborů v adresáři bez jejich vypisování, stačí předchozí script upravit na:
<?php
$path = "img/";
$obrazky = glob("$path*.jpg");
shuffle($obrazky);
echo "<img src=\"$obrazky[0]\">";  // jeden náhodný
echo "<img src=\"$obrazky[1]\">";  // druhý náhodný



Pokud jsou obrázky v databázi, lze je jednoduše vybrat dotazem:
select obrazek from obrazky order by rand() limit 2

Podstatné je order by rand(), zbytek si upravte dle potřeby.

# Nadpis skupiny nejen při výpisu z databáze

A
anicka
andulka
B
bolek
boris

Viz Nadpis skupiny.

# Jak získat zpět text zakódovaný přes MD5 (SHA-1, SHA-256, …)?

Nijak, nejde to.
V první řadě je nutné si uvědomit, že MD5 (SHA-1, SHA-256, atp.) je otisk, ne zašifrování ani zakódování.
Samotný otisk nedává dost informací, aby z něj šlo rekonstruovat vstup, ze kterého byl vytvořen. Logicky když např. MD5 otisk má vždy 32 bajtů a vstup může být téměř libovolně velký, musejí být různé vstupy dávající stejné otisky. Ze samotného otisku pak nelze poznat, který z možných vstupů k němu vedl.


# Jak na funkci „zapomenuté heslo“, když mám jen otisk (hash) hesla?

(častá otázka po zjištění, že hash nejde „dekódovat“)
Toto se obvykle řeší tak, že se uživateli nastaví nové náhodně vygenerované heslo a to se mu pošle. Lepší řešení je poslat uživateli výzvu s odkazem na stránku, kde si může heslo změnit. Zabráníme tak případnému měnění hesel útočníkem (který heslo sice nezíská, ale obtěžuje naše uživatele).
Schopnost zaslat uživateli jeho původní heslo lze považovat za příznak vážné bezpečnostní chyby. Znamená to totiž, že vůbec existuje možnost heslo získat. A když to dokáže aplikace, dokáže to i případný útočník, který by se do ní naboural.
Viz Poslání zapomenutého hesla.

# Zkrácení článku pro výpis perexu

Často je třeba vypsat v seznamu článků jen jejich krátkou ukázku, tzv. perex. Pokud článek neobsahuje html značky, lze použít jednoduché
select substring_index(`obsah`, ' ', 10) perex from `clanky`


Tento dotaz vrátí text zkrácený na počet slov (oddělených mezerou) specifikovaný ve třetím parametru.

Jednoduchou úpravou
select concat(substring_index(`obsah`, ' ', 10), '&hellip;') perex from `clanky`
můžeme doplnit na konec doplnit tři tečky (budou doplněny ale vždy, i když se text nezkracuje).
Pokud zkracujeme na pevnou délku (nehledáme mezery) lze upravit tak, aby se &hellip; doplnil jen když se zkracuje:
select concat(substr(`obsah`, 1, 200), if(length(`obsah`) > 200, '&hellip;', '')) perex from `clanky`

Na úrovni PHP lze zkrácení textu dosáhnout funkcí substr(), případně mb_substr().
echo substr("Lorem ipsum dolor sit amet, consectetur adipiscing elit.", 0, 17)."&hellip;";

Složitější úpravy, např. vyřešení problémů s neuzavřenými html značkami, se obvykle řeší v PHP.
Často stačí, když všechny značky odstraníme, tj. použijeme funkci strip_tags(), a následně zkrátíme (druhý script v Zkrácení textu s XHTML značkami).
Pokud je nutné značky zachovat, viz Zkrácení textu s XHTML značkami.

V obou případech je u delších článků vhodné z databáze vybrat určitý nadlimitní počet znaků, který se v PHP dále zkrátí. Není nutné načítat celé články.

Pokud používáte nějaký šablonovací systém, postará se o konečnou fázi sám.
Latte filter v Nette:
<a href="…">{$linkText|truncate:20}</a>

Smarty modifikátor truncate:
<h1>{$articleTitle|truncate:30:"…":true}</h1>

# Testování mailů na localhostu

Běžná instalace vývojového prostředí obvykle nezahrnuji zajištění a konfiguraci SMTP serveru. Důsledkem je, že nám na localhostu nefunguje funkce mail(). Správné nastavení nebývá vždy jednoduché, viz např. možnosti posílání emailů přes gmail SMTP.

Pokud ale emaily nepotřebujete skutečně odesílat a stačí vám je testovat, doporučuji vynikající Test Mail Server Tool.

# Záloha databáze

Návod na zálohování databáze pomocí Clevis/DatabaseBackup

Komunikace mezi javascriptem a PHP


# Jak přenést proměnnou z javascriptu do PHP

Mám tento kód, ale nefunguje mi dle očekávání:
<script>
var i = confirm('blablbla');
</script>
 
<?php
   echo $i;

Proč to nemůže fungovat? Protože PHP se nejdříve zpracuje na serveru, teprve výsledná html stránka se odešle klientovi a ten začne vyhodnocovat javascript. V této chvíli sice může vyvolat dialogové okno a zeptat se uživatele na informace, ale již vygenerovanou stránku nelze zpětně upravit.

Jak to udělat jinak? Je důležité si uvědomit, že proměnné naplněné v js lze do php odeslat nejdříve při dalším požadavku na server. Pak máme několik možností.
Přesměrování stránky na sebe sama a doplnění informací do url – výhodou je možnost okamžitého překreslení nové stránky již na serveru, nevýhodou je velká prodleva, než se uživateli nová stránka načte a ztráta všechny neuložených věcí (u rozepsaného formuláře).
• Odeslání skrytého formuláře na server (např. do iframe), možnost použít kromě dat v url i post data – výhodou je možnost odeslání post dat nebo třeba i upload obrázku na pozadí, přenáší se pouze užitečná data, stránka se znovu nenačítá. Nevýhodou je obtížnější získání odpovědi serveru.
• Vyvolat jiný požadavek na server, např. v js vytvořit objekt obrázku. Nevýhodou je, že nezískáme odezvu serveru, data lze tedy přenášet jen jedním směrem.
Vytvořit cookies, která se při dalším požadavku na server odešle automaticky. Tento způsob je také v dané chvíli jednosměrný a je zde riziko, že uživatel stránku opustí a data se na server už nedostanou. Je to ale použitelné např. pro jednoduchou anktetu.
• Využít AJAX – hlavní výhodou je, že lehce najdete hotové řešení, i když ne vždy optimální (jednoduchá ukázka, řešení s jQuery – jen na tohle je framework zbytečně velký, ale jestli už ho stejně načítáte, proč ho nepoužít). Tato metoda je univerzální a častou používaná. Počítá se zde s okamžitou odpovědí serveru, která vyvolá předem určenou js funkci (která může reagovat na odpověď např. změnou stránky). Komunikace se serverem je asynchronní, tj. zpracování dalšího javascriptu nečeká na dokončení předchozí požadavku.

Tato řešení jsou bohužel závislá na funkčním javascriptu, když základ řešení necháte na html formuláři, bude to fungovat vždy (myslete i na mobilní zařízení).

# Jak přenést proměnnou z PHP do javascriptu

Tento směr je značně jednodušší, protože PHP část se vykonává dříve.
var vekUzivatele = <?php echo $vekUzivatele; ?>; // kdyby se předával string, musí se použít uvozovky a správné escapování
(escapování)

Pokud přenášíte více hodnot, mohl by se vám hodit JSON. Umí s ním pracovat php i javascript.

# Rozdíl ve viditelnosti cookies při vytvoření javascriptem a PHP

Data obsažená v cookies se mezi javascriptem a PHP předávají v http hlavičce. V době jejich platnosti jsou ukládána a spravována prohlížečem klienta. Pokud tedy v js vytvoříme cookies, je odezva okamžitá a hned na dalším řádku kódu ji můžeme přečíst. Jakmile ale zapojíme PHP (vytvoření cookie pomocí funkce setcookie), musíme počítat s čekáním na další požadavek (načtení stránky). Teprve s ním prohlížeč vloží do své žádosti o novou stránku aktuální data z cookies a serverový script je uvidí.

Cookies v PHP fungují tak, že při na začátku zpracování requestu (počáteční fáze zpracování stránky) je vytvořeno superglobální pole $_COOKIE, které obsahuje přijatá data. Toto pole potom již v průběhu běhu scriptu zůstává samo o sobě neměnné. Nezmění ho ani funkce setcookie(), která pouze přidá do nové hlavičky odesílané klientovi nová data. Z toho plyne, že PHP cookies uvidí až po znovunačtení stránky.

Pokud chceme, můžeme si sami modifikovat pole $_COOKIE. Tato změna se ale sama o sobě, na rozdíl od změny $_SESSION, nepřenese na další stránky.

# Konfigurace PHP

PHP interpreta globálně nastavujeme editací souboru php.ini. Tuto možnost využijeme především na vlastním počítači, protože změněná nastavení se nám projeví (po restartu PHP (obvykle restartujeme Apache)) ve všech scriptech.
Někdy chceme změnit nastavení jenom pro určitý script, pak použijeme ini_set(), jehož volba v aktuálním scriptu přepíše globální hodnotu v php.ini.
Podobně můžeme nastavení změnit prostřednictvím souboru .htaccess (pokud pracujeme na Apache), detaily syntaxe jsou níže. Tato varianta úpravy konfigurace se hodí zvláště na sdíleném hostingu.

Byly uvedeny 3 způsoby nastavení běhového prostředí, jejich použití ale není ekvivalentní. Zatímco v php.ini můžeme změnit cokoliv, pokud k němu máme přístup, funkce ini_set() i nastavení v .htaccess lze omezit, s tím se setkáme hlavně na sdíleném.

Navíc ani z technických důvodů není možné vždy využít všech variant, v manuálovém přehledu možných nastavení je uveden sloupec Changeable, jehož hodnoty oznamují, kde lze danou hodnotu nastavení změnit. Zvláště upozorňuji, že nastavení upload_max_filesize s changeable hodnotou PHP_INI_PERDIR není možné změnit funkcí ini_set() (protože se daný script začne zpracovávat až po dokončení uploadu).

# Nalezení správného php.ini souboru

Častou chybou je editace špatného souboru. Ten správný naleznete ve výpisu phpinfo() pod klíčem Loaded Configuration File .

# Konfigurace PHP pomocí .htaccess

Nejdřív je potřeba zjistit v přehledu konfiguračních direktiv, jestli se dané nastavení vůbec dá změnit pomocí .htaccess. Pokud ano, musí tam být u něj napsané "PHP_INI_ALL" nebo "PHP_INI_PERDIR". Potom si kliknutím na název direktivy zobrazíme její podrobnější popis - tady nás zajímá hlavně datový typ (je uvedený hned za názvem direktivy).

# takto nastavujeme logické hodnoty (boolean)
php_flag název On
#nebo 
php_flag název Off

# všechny ostatní typy nastavujeme pomocí php_value. Příklady:
php_value iconv.internal_encoding "UTF-8"  # pokud řetězec neobsahuje mezeru, uvozovky jsou nepovinné. 
php_value upload_max_filesize 5M           # zkrácený zápis datové velikosti, viz dále
php_value error_reporting 30719            # odpovídá hodnotě E_ALL v PHP 5.3. Na rozdíl od php.ini nemůžeme používat konstanty. 
php_value název none                       # klíčové slovo "none" zruší předchozí nastavení
Pokud se u direktivy zadává datová velikost (např. omezení velikosti uploadovaných souborů), je možné ji zadat buď v bajtech jako číslo, nebo zkráceným zápisem.

# Zapnutí výpisu všech chyb a varování


1) V globálním konfiguračním souboru php.ini nebo od PHP 5.3 v souboru .user.ini
display_errors = On 
error_reporting = E_ALL

2) V souboru .htaccess (pokud PHP běží jako modul mod_php5 v Apache)
<IfModule mod_php5.c>
    php_flag display_errors On
    php_value error_reporting 30719
</IfModule>

3) Na začátku skriptu (neukáže např. syntaktické chyby, ke kterým dojde před spuštěním skriptu)
<?php
    ini_set('display_errors', 'On');
    error_reporting(E_ALL);

Všechny typy chyb jsou popsány v dokumentaci PHP - Predefined Constants.

# Více verzí PHP na jednom počítači

Pokročilejší uživatelé mají PHP pro testování nainstalováno ve více verzích. Pro jejich pohodlné testování lze využít PhpVersionSwitcher – nástroj na přepínání verzí PHP od Jana Tvrdíka.

# Správné kódování v PHP


# Jaké kódování použít pro pro stránku anebo pro ukládání do databáze?

a Ať nastavím stránce/skriptu/databázi jakékoliv kódování, některé (všechny) texty se zobrazují špatně.
Co se týká výběru kódování: Použijte UTF-8, nemáte-li důvod použít nějaké jiné kódování. (Např. při úpravě existující aplikace v nějakém kódování použijte stávající kódování té aplikace.)

Pro správné zobrazení textů jsou důležité tyto věci:
• Kódování souboru, ve kterém je stránka fyzicky uložená (tj. kódování nastavené v editoru při ukládání souboru).
• Pokud do stránky vkládáte jiné soubory (např. přes include), kódování, v jakém jsou (opět fyzicky) uložené tyto soubory.
• Jaké kódování stránka řekne prohlížeči. To lze nastavit HTTP hlavičkou content-type (v PHP přes funkci header), nebo v HTML kódu <meta> značkou charset nebo content-type.
• Pokud používáte databázi, kódování pro komunikaci s databází. Standardní SQL na to má příkaz SET NAMES kódování (najdete v manuálu k příslušné databázi). Poznámka, například MySQL umožňuje také nastavit kódování pro konkrétní sloupec, což je něco jiného, podstatné je nastavit komunikační kódování.
Poznámka: Z databázových systémů běžně používaných s PHP by mělo SET NAMES fungovat v MySQL a PostgreSQL. V MS-SQL by mělo kódování jít nastavit přes konfigurační položku mssql.charset, SQLite má zdá se pevně dané kódování UTF-8.

Všechny výše uvedené odrážky musejí být nastavené na totéž kódování.
Jestliže používáte databázi, opravili jste komunikační kódování (takže teď už máte všechny odrážky správně) a přesto texty z databáze jsou špatně, pravděpodobně máte špatně uložená už data v databázi. Bude potřeba stará data překonvertovat a uložit znovu a ujistit se, že se komunikační kódování nastavuje i při ukládání dat.


# Divný znak (čtvereček) na začátku includovaného souboru v UTF-8

Soubor v kódování UTF-8 a UTF-16 může začínat takzvanou BOM signaturou. V případě UTF-8 je sice zcela zbytečná, přesto ji některé editory vkládají.
Řešením je ve svém editoru nastavit, aby BOM nevkládal (v PSPadu Nastavení » Nastavení Programu » Program 2), nebo to nějakým způsobem odmazat. V některých editorech to nelze.
A stručný popis UTF-8

Někdy lze BOM poznat dle prázdného místa.

# Zastaralé (deprecated) funkce

# Co to je?

PHP se jako ostatní jazyky postupně vyvíjí. Většinou je přidávána nová funkčnost, avšak někdy dojde naopak k odebrání zastaralých prvků, zpravidla funkcí. S cílem usnadnění práce vývojářům je před definitivním odebráním daných funkcí zařazena přípravná fáze, ve které tyto funkce generují chybové hlášení o tom, že použitá funkce je zastaralá a brzy bude zrušena.
Deprecated: Function ereg() is deprecated in ...
V této chvíli ještě vše funguje a ignorování chybové hlášky „vyřeší problém“. Může ale nastat okamžik, kdy admini na serveru aktualizují PHP a aplikace přestane fungovat. (Podobně jako když se změnila vychozí volba register globals na off a internetové diskuse začaly plnit dotazy, proč najednou přestal fungovat web, když „teď jsem s ním nic nedělal“.)

Nejen kvůli tomu doporučuji na vývojovém serveru zobrazovat všechna chybová hlášení a vhodně ho aktualizovat. Vždy je lepší se o problému dozvědět dříve, než se rozbije produkční verze.

# Mám kód přesně podle návodu a přesto vidím tuto hlášku

Problémem je, že značná část návodů pro PHP (zvláště v češtině) je buď příliš stará, nebo jejich autoři (zvláště blogy menšího rozsahu) ignorují, že se s některými funkcemi do budoucna nepočítá. Např. oblíbený seriál o PHP na linuxsoftu vycházel v roce 2004 a je psán pro PHP 4, ve stejném roce (2004) vyšlo PHP 5. Ano, je to už opravdu dlouho a hodně věcí se změnilo.

[h2#deprecated-functionsJakých funkcí se to týká?[/h2]
Nejlepší přehled získáte pravidelným sledováním PHP changelogu, ten je více než dlouhý (ale je možné tam v prohlížeči vyhledat výrazy „deprecated“). Běžně stačí pročíst články, které se vždy objeví při vydání významných změn, a občas kouknout na fórum (to by měl programátor stejně dělat běžně).

• Aktuálně nejčastěji zřejmě narazíte na zastaralost mysql_ extenze pro přístup k databázi. Např. u funkce mysql_connect() vidíme, že je označena jako deprecated a vývojáři nám doporučují použít MySQLi nebo PDO. Detailněji si o tom můžete přečíst na Fisirovo doupě: Proč nepoužívat funkce mysql_*?. Alternativně lze použít databázovou vrstvu třetí strany, např. dibiphp.com/cs/quick-start, které dokáže pracovat s různými extenzemi a jejich výběr závisí jen na konfiguraci.
• Další v pořadí je celá rodina funkcí ereg_, místo které je doporučeno používat preg_ funkce.

# Kdy přestanou fungovat?

Tohle se obecně přesně neví, manuál mluví o příštích verzích. Konkrétně např. mysql extenze a mnohé další (wiki.php.net/rfc/remove_deprecated_functionality_in_php7) označované od 5.x jako deprecated nebudou součástí PHP 7, která má vyjít na konci roku 2015. Vydání nové verze však neznamená, že na ni váš hosting hned přejde.

Nemusíte se bát, že to bude za měsíc. Zvláště free hostingy obvykle s aktualizací nikam nespěchají (a zvláště po této změně nebudou), takže to může fungovat ještě několik let.
U nyní vyvíjených projektů byste se však měli těmto funkcí vyhnout. Všechny mají (lepší) náhradu, takže technicky to není problém. Nenechte se zmást tím, že na webu na některé zastaralé konstrukce narazíte na každém kroku.
Toto téma je uzamčeno. Odpověď nelze zaslat.