Advanced PHP Trickery

napísal , 22 Jan 2012

Viete čo sú v PHPčku anonymné funkcie, closures, ako fungujú statické premenné vo funkciách a načo je register_shutdown_function()? Ak nie, ste na správnom mieste, za 2 minúty to budete vedieť a budú z vás neporaziteľní mágovia PHP. Ak to už viete, môžte sa spokojne potľapkať po chrbte, znamená to, že ste asi s PHP strávili viac času, ako by bolo zdravé :)

Poďme si tieto 4 veci vysvetliť na krátkom príklade "zo života". Predstavte si, že máme triedu na odosielanie SMSiek. Ale predtým než začneme posielať SMSky, sa treba prostredníctvom triedy pripojiť k serveru a po doposielaní všetkých SMS sa treba odhlásiť.

My sa však nechceme zaoberať takými blbinami ako pripájanie a odpájanie. Cheme mať len globálnu funkciu send_sms(). Na to použijeme celkom elegantné riešenie, v ktorom budú obsiahnuté všetky spomínané advanced koncepty.

Statické premenné vo funkciách

function send_sms($number, $text)
{
	static $connection = false;

	if (!$connection)
	{
		require_once('extensions/SMS/connect.php');
		$connection = new SMSConnect();
		if (!$connection->create("login","heslo")) return false;

Statická premenná vo funkcii si zachováva hodnotu v rámci všetkých volaní danej funkcie. Pri prvom spustení bude obsahovať hodnotu false. Podmienka nižšie sa teda vykoná, do premennej $connection strčíme SMSkovú triedu a pripojíme sa k serveru. Pri ďalšom spustení funkcie bude v premennej SMSková trieda a celý tento blok "if" sa preskočí.

V OOP sa zvyknú používať statické premenné vo vnútri tried, každopádne je dobré vedieť, že sa dajú použiť aj vo vnútri funkcií. V manuále nájdete ďalšie príklady využitia.

		$callback = function() use($connection)
		{
			$connection->Logout();
		};

		register_shutdown_function($callback);

Na tomto čudnom kúsku sa nachádzajú zvyšné 3 koncepty. V skratke docielime, že keď dobehne PHP skript, zavolá sa odhlasovacia metóda SMSkovej triedy.

Achievement unlocked!

napísal , 25 Jan 2011

Awww, chudák náš úbohy blog, nejak sme ho v poslednej dobe ignorovali. Za to všetko môže evil škola! (a zajtra.sk ;) But we're back in business a povieme si ako v PHP spraviť nejakú riadnu sprostosť - napríklad achievement generator™.

Myšlienka za ním bola asi taká, že pokiaľ sa niekto zvykne velice chváliť nepodstatnými blbosťami a lezie s tým na nervy, pošlete mu takýto obrázok a možno to bude mať zaujímavý efekt. Pokiaľ ste za posledných pár rokov hrali nejakú novšiu hru, určite vašej pozornosti neuniklo, že vás zasypávajú kadejakými trofejami a achievementami pri každej príležitosti. (Niekoho to akože aj reálne teší? Podľa mňa je väčšina vývojárov dosť slabých psychológov, súdiac podľa toho aký majú achievementy na mňa efekt. :p)

Demo - brm achievement generator™!

Čo sa týka programovania, budeme chciet písať text na obrázok pomocou knižnice GD a odosielať ho ako PNG, plus využijeme nejaký ten URL shortener, aby vaša "obeť" nevidela čo jej to posielate. Všetko samozrejme pôjde cez AJAX.

PHP html/plaintext email

napísal , 12 Dec 2010

Čo to o posielaní mailov sme si povedali v našom článku Informácie o prehliadači v kontaktnom formulári (je to fakt užitočná vec, go check it out). Teraz prišiel čas podeliť sa o php funkciu, ktorá vie posielať html aj plaintext emaily. Naraz. Behold:

/**
 * odosiela multipart spravy - html aj plaintext pre hnilych klientov
 * linky pri plaintext mailoch su zamenene za samotne "href" hodnoty
 * html tagy a uderene charaktery su pri nich odstranene a "<br>" je zmenene na "\n"
 */
function send_mail($from, $subject, $body, $plaintext = NULL, $to = NULL)
{
	// ak neni zadany to, zvoli sa defaultny:
	if (!$to) $to = 'default@mail.sk';

	// zostavi html mail:
	$html  = "<html>\n";
	$html .= "<body style=\"font-family: Verdana, Geneva, sans-serif; font-size: 14px; color:#333;\">\n";
	$html .= $body;
	$html .= "</body>\n";
	$html .= "</html>\n";

	// ak neni zadany plaintext, zostavi sa z povodneho html mailu:
	if ( ! $plaintext)
	{
		// odstrani "\n" a potom nahradi "<br>" za "\n" pre plaintext:
		$body = preg_replace('/<br\\s*?\/??>/i', "\n", str_replace("\n", '', $body));

		// odstrani tagy (okrem <a>)
		$plaintext = strip_tags($body, '<a>');

		// matchne "<a>" tag a jeho "href" hodnotu:
		$pattern = '%<a\s[^>]*href=["/\'](.*)["/\']\s*>(?:.*)</a>%im';
		preg_match_all($pattern, $plaintext, $matches, PREG_SET_ORDER);

		// vymeni nazov linku za "href" hodnotu a na koniec prida medzeru 
		// (vraj to pomaha)
		foreach ($matches as $link)
		{
			$plaintext = str_replace($link[0], $link[1] . ' ', $plaintext);
		}

		// odstrani interpunkciu a prehodi html entities na text
		$plaintext = remove_accents(html_entity_decode($plaintext));

		// prida newline na konci plaintext mailu (bez toho to blbne):
		$plaintext .= "\n";
	}

	# -=-=-=- MIME BOUNDARY
	$mime_boundary = "----AWESOME----" . md5(time());

	# -=-=-=- MAIL HEADERS
	$headers  = "From: $from\n";
	$headers .= "Reply-To: $from\n";
	$headers .= "Return-Path: <default@mail.sk>\n";
	$headers .= "MIME-Version: 1.0\n";
	$headers .= "Content-Type: multipart/alternative; charset=UTF-8; boundary=\"$mime_boundary\"\n";

	# -=-=-=- TEXT EMAIL PART
	$message  = "--$mime_boundary\n";
	$message .= "Content-Type: text/plain; charset=UTF-8\n";
	$message .= "Content-Transfer-Encoding: 8bit\n\n";
	$message .= $plaintext;

	# -=-=-=- HTML EMAIL PART
	$message .= "--$mime_boundary\n";
	$message .= "Content-Type: text/html; charset=UTF-8\n";
	$message .= "Content-Transfer-Encoding: 8bit\n\n";
	$message .= $html;

	# -=-=-=- FINAL BOUNDARY
	$message .= "--$mime_boundary--\n\n";

	# -=-=-=- SEND MAIL
	$subject="=?UTF-8?B?".base64_encode($subject)."?=\n";
	$mail_sent = @mail( $to, $subject, $message, $headers );	
	
	return $mail_sent;
}

Kopírujte, prípadne si stiahnite súbor priamo: awesome_email.zip

Prečo? Nuž, HTML je v emailových klientoch podporované extrémne biedne. Niektoré ho nepodporujú vôbec, iné dokážu správu zhovadiť tak, že sa stane pre mnohých nepoužiteľná. Prudký geek z takého mailu linky vyťažiť dokáže, obyčajný Joe sa tým zaoberať nebude a váš business je ohrozený :P Niekedy to dokonca nemusí byť možné. Ak máte link v tvare <a href="http://awesome.bla/gule">haha link</a> a ono vám z toho náhodou spraví haha link? Useless. Táto funkcia pre plaintext časť emailu vytiahne adresu a zobrazí tú.

Pre väčšiu kompatibilitu odstráni aj interpunkciu a prehodí html entities na text. Keď už osekávame, tak poriadne ;) Na interpunkciu používa funkciu remove_accents, ktorú sme spomínali v článku Generovanie pekných URL. Je súčasťou toho .zipu tam hore.

$from mail, z ktorého bude správa poslaná (od koho)
$subject predmet mailu
$body text mailu
$plaintext ak chceme, môžeme plaintext pridať pridať sami, inak sa vygeneruje z $body
$to mail, na ktorý bude správa poslaná (komu)

Ak sa divíte, prečo sa mail "komu" zadáva ako posledný atribút... častejšie(?) budete chcieť, aby maily boli posielané vám. Prípadne klientom, ak robíte stránku pre klienta. Cez kontaktný formulár napríklad. A vtedy stačí napísať vašu adresu priamo do tela funkcie (namiesto default@mail.sk) a potom používať len prvé 3 atribúty.

Like this: send_mail($_POST['email'], 'Ahoj', 'Smrdí ti.');

Ak to nevyhovuje, nič vám nebráni prepísať si funkciu podľa seba :) Vráti TRUE ak sa mail podarilo odoslať, inak FALSE. Takže tak.

Enjoy. :)

Jednoduchý export z PHP do Excelu

napísal , 9 Dec 2010

Je veľká šanca, že pokiaľ robíte webovú aplikáciu s akýmikoľvek tabuľkovými dátami, ktorá je určená pre ne-IT ľudí, budú od vás chcieť export údajov do Excelu. Ak nemajú veľmi obskúrne požiadavky (grafy, farebne odlíšené políčka, ...), môžete im pomerne jednoducho spraviť radosť - here's how:

V prvom rade nebudeme používať .xls, lebo to je škaredý binárny formát :), ale primitívny formát .csv (comma-separated values), ktorý vie Excel čítať a basically vyzerá takto:

Stlpec1,ďalší stĺpec,ešte stĺpec...
10,12,42
32,23,64
128,0,256

Na stránke asi používate UTF-8, takže samozrejme bude v Exceli problém s diakritikou a špeciálnymi znakmi. Problémy s enkódovaním sa dajú riešiť tak, že zakódujete súbor do Unicode a ako oddeľovače použijete taby (namiesto čiarok). Unicode sa dá vyjadriť veľa spôsobmi (UTF-8 je jedným z nich), ale Excel a Windowsovské veci vo všeobecnosti rozumejú iba UCS2/UTF-16 with BOM, čo znamená, že všetky znaky sú 16-bitové a súbor má na začiatku špeciálny znak (Byte Order Mark) udávajúci usporiadanie bitov (Big Endian alebo Little Endian - Windows používa ten druhý).

Vygenerované .csv nakoniec treba browseru odoslať ako súbor (inak by sa namiesto download okna zobrazil obsah súboru v prehliadači), na čo stačí zmeniť HTTP hlavičky.

$export = "Stlpec\tĎalší stĺpec\tEšte stĺpec\n";
while ($o = mysql_fetch_object($res)) $export .= "$o->stlpec\t$o->dalsi\t$o->este\n";

header('Content-type: text/csv');
header('Content-Disposition: attachment; filename="drist.csv"');
echo chr(0xFF).chr(0xFE) // BOM pre Little Endian
     .mb_convert_encoding($export, "UCS-2LE", "UTF-8"); // konvertujeme z UTF-8 do UCS2/Little Endian

Pro tip: ak by sa v samotných hodnotách mali objaviť taby (čo to máte pre boha za údaje? :), treba celú hodnotu dať do úvodzoviek:

"I can has	tabs"	bla	dríst

Informácie o prehliadači v kontaktnom formulári

napísal , 14 Nov 2010

Kompatibilita prehliadačov je stále lepšia, ale ešte stále sa môže veľmi ľahko stať, že návštevníkom vašej stánky bude blbnúť nejaký prvok. Hlavne pokiaľ je určená netechnickým ľudom, ktorých absolútne netrápi, či majú nový prehliadač, prípadne čo to ten prehliadač vôbec je.

Pri troche šťastia sa vám pôjdu niektorí ľudia posťažovať, že im niečo nefunguje, napíšu napríklad cez kontaktný formulár a vy sa budete ako správny developer snažiť opraviť chybu. Pokiaľ vám všetko fungovalo, daný človek musí mať určite iný prehliadač. Ale aký? ..no to sa od netechnického človeka dozviete veľmi ťažko :p

A tu prichádza na rad tento malý tip.

Do kontaktného formulára (a v postate hocikde, kde je šanca, že sa používatelia môžu na vašu stránku sťažovať:) pridajte takéto skryté pole a ušetríte si kopu starostí:

<?php
	$dbg = (count($_COOKIE) ? 'cookies: on' : 'COOKIES: OFF!').', '
		.'browser: '.$_SERVER['HTTP_USER_AGENT'].', '
		.'ip: '.$_SERVER['REMOTE_ADDR'].', '
		.'js: OFF!';
?>
<input type="hidden" name="extra" id="extra" value="<?=$dbg?>">
<script>
$(function()
{
	$("#extra").val( 
		$("#extra").val().replace("JS: OFF!","JS: on")
		+"; resolution: " + screen.width+"x"+screen.height
		+", document: "+window.innerWidth+"x"+window.innerHeight 
	);
});
</script>

Vďaka tomuto políčku budete hneď po kontaktovaní vedieť aký ma človek prehliadač, IP adresu, rozlíšenie displaya, veľkosť okna browsera a či má zapnuté cookies a Javascript. Podľa potreby môžete pridať kontrolu Flashu, Javy, premenných zo $_SESSION a kadečoho iného. Kontrola cookies cez PHP funguje samozrejme iba v prípade, že ich používate. ..ale pokiaľ ich nepoužívate, asi vás ani nebude trápiť, či používateľovi fungujú :)

Pre lepšiu predstavu - výstup vyzera približne takto:

cookies: on, browser: Opera/9.80 (Windows NT 6.1; U; en) Presto/2.6.30 Version/10.63, IP: 78.98.13.37, JS: on; resolution: 1600x900, document: 1600x815

Aby sme neobišli bez nejakých tých zrád, tak Opera/9.80 je v skutočnosti 10.xx a Windows NT 6.1 je Windows 7 :)

Kontaktný formulár by mal aj niekoho kontaktovať, na čo sa dá použiť PHPčková funkcia mail(), ktorej ale treba trocha pomôcť, aby sa v maile zobrazoval správny odosielateľ a diakritika.

$header = "MIME-Version: 1.0\n"
	."Content-type: text/plain; charset=UTF-8\n"
	."From: vasa@stranka.sk\n"
	."Reply-to: ".$_POST['email']."\n";

$predmet = '=?UTF-8?B?'.base64_encode('[kontaktný formulár] správa od: '.$_POST['meno']).'?=';

$sprava = $_POST['sprava']."\n-------------------\n".$_POST['extra'];

mail("sem.chcem.dostavat@maily.sk", $predmet, $sprava, $header);

V takomto prípade v pohode stačí plaintextový mail. Ak ale chcete byť fancy, k dispozícií sú HTML maily o ktorých si povieme nabudúce. :)

Upload progressbar s PHP a jQuery

napísal , 1 Nov 2010

upload progressbar

Pri uploadovaní cez HTML formulár, hlavne ak sa jedná o veľké súbory, môže byť problém, že používateľ nevie aká časť zo súboru sa uploadla, či sa súbor vôbec uploaduje a prinajhoršom si myslí, že sa vôbec nič nedeje a nervózne kliká na submit. Ešte horšie ako zvýšená záťaž na server, ktorú generuje, je riziko, že sa na celú stránku vyserie, lebo si myslí, že nefunguje. :)

V tomto tutoriale si povieme ako za pomoci PHPčka a jQuery jednoducho vytvoriť progress bar, ktorý sa priebežne obnovuje podľa množstva prenesených dát z uploadovaných súborov a snáď pomôže predísť úniku netrpezlivých používateľov zo stránky.

Aby ste vedeli o čom je reč, tu si môžete pozrieť demo.

Generovanie pekných URL

napísal , 20 Oct 2010

Pre SEO sú veľmi dôležité vhodne formátované URL adresy. Taká dobrá URL by mala obsahovať viac menej len text úzko súvisiaci s obsahom, avšak bez diakritiky a podobnej chamrade, aby nevznikali problémy pri kopírovaní linkov a parsovaní adries vyhľadávačmi.

Chamraď sa dá buď nahradiť URL entitami (%20 je napríklad medzera), čo môže byť pri niektorých znakoch stále nespoľahlivé alebo sa z nej dá odstrániť diakritika a zvyšok nahradiť pomlčkami.

Druhý spôsob sa nám najviac osvedčil a používame naň túto funkciu:

function seo_url($str)
{
  $str = remove_accents($str);
  $str = preg_replace('/[^a-zA-Z0-9-]/', '-', $str);	
  return strtolower(trim(preg_replace('/-+/', '-', $str), "-"));
}

V prvom riadku sa odstáni z písmen diakritika:
Bŕm™ džžž DrÍST ブレイド!   ->   Brm™ dzzz DrIST ブレイド!

v druhom riadku sa nahradia všetky znaky okrem písmen a číslic pomlčkami:
Brm--dzzz-DrIST------

a v treťom sa všetky susediace pomlčky spakujú do jednej, odstránia sa pomlčky z krajov a všetky písmená sa premenia na malé:
Brm-dzzz-DrIST-   ->   Brm-dzzz-DrIST   ->   brm-dzzz-drist

Funkcia remove_accents() potom vyzerá nasledovne:

function remove_accents($str)
{
  return str_replace (
  array(
    'à','á','â','ã','ä','å','æ','ç','ć','č','ď','đ','ð','è','é','ê','ë','ę','ě','ì','í','î','ï','ĺ','ľ','ł','ň','ñ','ò','ó','ô','õ','ö','ő','ø','ŕ','ř','ś','š','ß','ť','þ','ù','ú','û','ü','ű','ů','ý','ÿ','ž',
    'À','Á','Â','Ã','Ä','Å','Æ','Ç','Ć','Č','Ď','Ð','È','É','Ê','Ë','Ę','Ě','Ì','Í','Î','Ï','Ĺ','Ľ','Ł','Ň','Ñ','Ò','Ó','Ô','Õ','Ö','Ő','Ø','Ŕ','Ř','Ś','Š','Ť','Þ','Ù','Ú','Û','Ü','Ű','Ů','Ý','Ÿ','Ž'
  ),
  array(
    'a','a','a','a','a','a','ae','c','c','c','d','d','d','e','e','e','e','e','e','i','i','i','i','l','l','l','n','n','o','o','o','o','o','o','o','r','r','s','s','ss','t','th','u','u','u','u','u','u','y','y','z',
    'A','A','A','A','A','A','AE','C','C','C','D','D','E','E','E','E','E','E','I','I','I','I','L','L','L','N','N','O','O','O','O','O','O','O','R','R','S','S','T','TH','U','U','U','U','U','U','Y','Y','Z'
  ), $str );
}

Takýto "brute force" spôsob je momentálne jediný spoľahlivý a obdobná funkcia by oficiálne mala byť až v PHP 6. Konkrétne táto funkcia je využiteľná viac menej vo všetkých krajinách používajúcich latinku okrem Vietnamu a možno pár inch krajín, ktoré majú fakt divoké znaky a nechcelo sa mi nimi zapodievať ^^. Ak by aj náhodou neodstránila diakritiku, jediné čo sa stane je, že z URL vypadne daný znak.

ǚ
evil u is evil
12345