PHPUK Conference 2013

Ako ste kojim slučajem jedan od mojih followera na Twitteru, sigurno ste već upoznati da sam prethodnih nedelju dana boravio u Londonu. Razlog nisu bile Londonske znamenitosti niti daleko bilo engleska hrana, glavni razlog mog putešestvija bila je PHP UK konferencija koja se ove godine održava već osmi put!

PHP UK je jedna od najvećih konferencija posvećenih PHP-u, konferencija koju sam dugo godina zaobilazio jednostavno zbog prevelikih troškova ne samo registracije (Earl Bird ticket košta 320 Funti), već i zbog dodatnih troškova boravka u Londonu koji je jedan od najskupljih gradova na svetu. Sama konferencija traje dva dana, a osim uvodnog keynote-a u svakom trenutku postoje 3 sesije predavanja, tako da praktično morate odabrati samo jedno od ponuđena tri što zna da bude tricky. Ponekad odaberete temu koja vas više interesuje na osnovu abstrakta predavanja a kasnije se ispostavi da se autor tek pomalo dotakao teme, ili još gore da ima veoma jak francuski akcenat tako da vas njegovo predavanje uspava bolje nego 3 pinte najboljeg irskog piva :)

phpuk

Pored sesija sa predavanjima, konferencija je imala i 3 Social Eventa, Hackathon kao i Unconference – deo gde su delegati mogli sami da se prijave i da održe neko predavanje – korisno za one koji imaju šta da kažu ali su previše stidljivi da bi izdržali pojavljivanje na glavnim sesijama.

Ako ne računamo uvodni Social koji je održan u Četvrak uveče (i koji sam naravno propustio), glavna konferencija počela je u Petak ujutru uvodnim keynote-om koji je održao Aral Balkan. Iako je čovek pričao o dizajnu i to na PHP konferenciji, po mom skromnom mišljenju ovo je bilo možda i najbolje predavanje, jedno od onih koje vas nagna na razmišljanje i koje može promeniti način na koji gledate stvari oko sebe.

aral

Aral je naime kroz brojne primere lošeg dizajna iz stvarnog života pokušao da nam ukaže na stvari kojima možemo promeniti svet uz malobrojne primere onih zaista dobro odrađenih stvari. Inače juče sam iskopao da je ovo zapravo bilo predavanje koje je Aral održao još 2012 godine i zove se A Happy Grain Of Sand – jedno od najboljih predavanja u 2012 i dostupno je na linku ovde (predavanje broj 7.).

Nakon uvodnog keynote-a i malog kašnjenja, sva predavanja su se održavala po veoma preciznoj satnici dostupnoj na sajtu konferencije. Dole su ukratko opisana predavanja koja sam ja odabrao da posetim zajedno sa joind linkom u naslovu (gde možete naći komentare, slajdove i sl). Joind stranu celog događaja možete naći ovde, trenutno su samo dostupni komentari i slajdovi, a za par nedelja očekujem da organizatori izbace i video snimke kompletnih predavanja!

Day 1

PHP Traits, Threat or Treat?
Predavanje koje je trebalo biti o Traits-ovima, no čovek je naširoko pričao o statistikama korišćenja raznih verzija PHP-a i zašto bi trebali preći na PHP 5.4, a nakon dugačkog uvoda bez ikakve uvodne priče o Traitsima skočio je pravo u neke “specijalne slučajeve” (npr kada dva Traita imaju metod sa istim imenom). Uz sve ovo dodajte očajan engleski tako da sam ovo predavanje odlično iskoristio da malo bolje pogledam satnicu i predavače kako ne bih ponovo napravio sličan fail.

Looking Ahead: PHP 5.5
Lep uvod jednog od Core PHP developera u ono što nam donosi PHP 5.5. U najkraćem, Generatori, finally u try/catch, Passwords API, empty funkcija koja radi direktno sa funkcijama itd. Takođe ext/mysql je (konačno) deprecated a najavljen je i kraj podrške za PHP 5.3 (što uključuje i security ispravke) koji bi trebao biti u 2014 godini, što ukratko znači da će svi korisnici nakon toga biti primorani da se upgraduju na minimum PHP 5.4.

API Design: It’s Not Rocket Surgery
Dosta generalne priče o API dizajnu, REST principima bez nekih konkretnih primera, još jedno ne tako interesantno predavanje, barem za mene lično. Što bi rekao kolega @kornrunner “Izgleda da smo setovali pogresan request header” :)

Help, I’m Running Out of Memory!
Još jedan govornik sa jakim francuskim akcentom, no ovog puta tema je bila mnogo interesantnija. Čovek je jednostavno objasnio sve oko načina na koji PHP interno koristi memoriju, počev od alokacije na Unix sistemima pa sve do Garbage Kolektora. Ne toliko korisno za svakodnevni rad, ali svakako veoma interesantno za one koji poput mene vole da znaju više od “crne kutije” kako stvari zapravo funkcionišu.

Bottleneck Analysis
Jedno od najboljih predavanja autora koga sam i ranije bio u prilici da gledam na konferencijama. Ilija (inače takođe deo Core PHP ekipe, autor barem jedne sjajne knjige) pričao je o analizi potencijalnih uskih grla u aplikaciji ali sa strane jednog korisnika gde je detaljno opisao alate koje možete koristiti. Za mene nekoliko veoma korisnih informacija (npr ta da procesiranje security sertifikata može trajati i do jedne cele sekunde!). Obavezno skinite slajdove, verujem da će i bez videa biti interesantni.

ilia

Frameworks: The Good, The Bad and The Ugly
Keynote Panel sa Twitter wall-om i tri lika koji cirkaju pivo i mlate o frejmorcima. Obzirom da je ovo bilo poslednje predavanje prvog dana, zaključili smo da London ima mnogo više toga da ponudi od slušanja gluposti, tako da smo ga napustili. Vratili smo se nešto kasnije na Social Event koji je bio takav fail da je bolje da ne trošim preterano reči na to. Uostalom, zamislite Social koji organizujete u noćnom klubu punom pijanih Engleza i njihovih isto tako urađenih devojaka, uz glasnu muziku i par zaplašenih developera sa kojima bi jelte trebali da razmenjujete iskustva, preko IM-a valjda pošto od glasne muzike ništa drugo ne možete čuti. No comment.

Day 2

Drugi dan počeo je još jednim keynoteom – Diabolic Developer koji je trebao biti osveženje nakon sinoćnje lumperajke na social eventu (haha). Lik je sa izvesnom dozom sarkazma pričao o tome kako nikada ne treba testirati, pisati smislene komentare, nazvao je govornike na konferencijama luzerima koji nisu sposobni ni da programiraju niti da pišu blogove itd. Nažalost na joindu nema slajdova, tako da me mrzi da skupljam citate na Twitteru ali bilo je na momente urnebesno. Najgore od svega je što su ovog lika koji je pokušao da bude duhovit mnogi ozbiljno shvatili (pogotovo ovaj deo oko govornika po konferencijama) :)

The future of the PHP Development Environment
Pomalo monotono predavanje o podešavanju PHP okruženja koristeći Vagrant. Do tančina opisano u najsitnije detalje, pomalo overkill za onoga poput mene koji Vagrant još uvek ne koristi i očekivao je samo friendly introduction. :)

Ten Commandments of a Software Engineer
Jedno od najboljih predavanja na konferenciji posvećeno raznim aspektima razvoja software-a od testiranja od pisanja pravilnih komentara prilikom komitova. Teško ga je prepričati tako da vam toplo preporučujem da sačekate video snimak a u međuvremenu možete baciti oko i na slajdove.

Expert Troubleshooting: Resolving MySQL Problems quickly
Jedno od predavanja od kojih sam dosta očekivao, možda i zbog toga što je bilo bukvalno jedino posvećeno MySQL-u na čitavoj konferenciji. Nažalost iako je naveo neke korisne tehnike optimizacije, lik je uspeo da upropasti sopstveno predavanje time što je bacao majice u publiku za svako pitanje (kojih je onda naravno bilo jaaako puno), a zatim u pokušaju da ubaci celo predavanje u propisanih 45′ preskakao i zbrzao tako da je nažalost malo toga korisnog na kraju rečeno.

A Hands-On Introduction to Writing Unit Tests Using PHPUnit
Odličan uvod u Unit Testing sa PHP Unitom, jedno od onih predavanja kada autor pred vama nešto iskodira što kod mene lično uvek izaziva veliki respect. Iako je predavanje bilo samo uvod u pisanje Unit Testova, bilo je veoma interesantno slušati ga, pitanja i diskusiju koja je usledila takođe.

Nakon ovoga usledio je još jedan panel koji smo poučeni iskustvom sa prvog dana konferencije rešili da preskočimo. Isto je naravno važilo za nekakav najavljeni Social valjda u noćnom klubu odmah tu pored (no comment), pa smo rešili da pijane engleze i cure potražimo na nekom drugom mestu.

stage_setup

Da saberemo utiske …

Bila su to dva dana posvećena PHP-u a nekako sam stekao utisak da je PHPa bilo premalo za jednu konferenciju koja nosi PHP u nazivu. Organizacija je svakako bila dobra, mesto je bilo sjajno, klopa je bila okay (ako ste Englez valjda) ali nekako sam ipak očekivao više obzirom na uloženi novac. Napominjem da ovo nije bila moja prva PHP konferencija, pre nje dva puta sam bio na PHP Barcelona eventu a prošle godine i na Amsterdamskoj DPC konferenciji. Moj lični utisak je da je ova po utiscima možda i najlošija od sve tri, a barem po ceni trebala je biti apsolutno najbolja.

I dalje ostaje utisak da je PHP Barcelona Conf imala ubedljivo najbolji “value for money”, pošto ste za cenu od svega 100-nak Eura za registraciju dobijali dva dana odličnih predavanja uz odličnu organizaciju u fenomenalnoj i ne tako skupoj Barceloni. Nažalost prošle godine zbog krize u Španiji taj konf je otkazan i sva je prilika po aktivnostima na Twiter profilu da je više nažalost neće ni biti.

Kad podvučemo crtu, sledi uvek teško pitanje da li je vredelo uloženog novca (truda), i na to pitanje je veoma teško odgovoriti. Za one koji eventualno traže posao u UK verovatno vredi doći, odgledati predavanja, promuvati se po social eventima i pokušati da napravite neki deal u ne tako lakim uslovima za komuniciranje. Za ostale verujem da se ne isplati, tim pre što je ovo jedna od retkih konferencija koje samo par nedelja nakon kraja izbace snimke kompletnih predavanja na YouTube-u.

Sve u svemu, za nas iz Srbije preostaju stvari blizu nama, a jedna od njih može biti konferencija u Veroni koja je relativno blizu a opet relativno jeftina u ne tako skupoj Italiji (pogotovo u poređenju sa Londonom).

MySQL – NULL polja i sortiranje

Vrlo često imamo situaciju da želimo sortiranje po nekom određenom polju u tebeli ali tako da se NULL polja nikada ne pojavljuju na početku. Tipičan primer je recimo frontend koji prikazuje podatke iz neke tabele, gde se klikom na header kolone obavlja sortiranje po rastućem (asc) ili opadajućem (desc) poretku.

Obzirom da se ja u poslednje vreme dosta bavim domenima, kreirao sam jednu tabelu sa par svojih domena, čisto kao demonstraciju koncepta.

mysql> select * from domains;
+----+-------------------+-------------+
| id | domain            | expire_date |
+----+-------------------+-------------+
|  1 | dinke.net         | 2010-01-17  | 
|  2 | lampix.net        | 2009-12-26  | 
|  3 | blogodak.com      | 2010-09-08  | 
|  4 | maestrodesert.com | 2009-09-11  | 
|  5 | nepostojeci.com   | NULL        | 
+----+-------------------+-------------+
5 rows in set (0.00 sec)

Dakle problem, želim sortiranje po expire_date polju ali tako da se NULL polje (recimo domen koji još nije regovan ili je istekao) uvek pojavljuje na kraju. Po defaultu NULL se javlja na početku ako sortiramo u rastućem (ASC) orderu odnosno na kraju ako sortiramo po opadajućem (desc) orderu.

mysql> select * from domains 
order by expire_date asc;
+----+-------------------+-------------+
| id | domain            | expire_date |
+----+-------------------+-------------+
|  5 | nepostojeci.com   | NULL        | 
|  4 | maestrodesert.com | 2009-09-11  | 
|  2 | lampix.net        | 2009-12-26  | 
|  1 | dinke.net         | 2010-01-17  | 
|  3 | blogodak.com      | 2010-09-08  | 
+----+-------------------+-------------+
5 rows in set (0.00 sec)

mysql> select * from domains 
order by expire_date desc;
+----+-------------------+-------------+
| id | domain            | expire_date |
+----+-------------------+-------------+
|  3 | blogodak.com      | 2010-09-08  | 
|  1 | dinke.net         | 2010-01-17  | 
|  2 | lampix.net        | 2009-12-26  | 
|  4 | maestrodesert.com | 2009-09-11  | 
|  5 | nepostojeci.com   | NULL        | 
+----+-------------------+-------------+
5 rows in set (0.00 sec)

Problem sortiranja ćemo rešiti korišćenjem MySQL-ove IF f-je, a rešenje je:

mysql> select * from domains 
order by if(expire_date is null, 1, 0), expire_date asc;
+----+-------------------+-------------+
| id | domain            | expire_date |
+----+-------------------+-------------+
|  4 | maestrodesert.com | 2009-09-11  | 
|  2 | lampix.net        | 2009-12-26  | 
|  1 | dinke.net         | 2010-01-17  | 
|  3 | blogodak.com      | 2010-09-08  | 
|  5 | nepostojeci.com   | NULL        | 
+----+-------------------+-------------+
5 rows in set (0.00 sec)

MySQL-ova IF f-ja slična je ternarnom operatoru, tj. vraća prvi argument ako je iskaz tačan odnosno drugi u slučaju da nije, dakle u ovom slučaju vraća 1 za null vrednosti odnosno 0 za ostale, čime dobijamo upravo prikaz koji želimo tj. NULL polje na kraju liste.

Znam da ovo može delovati pomalo konfuzno pa ću otići još jedan korak dalje i dodati još jedno polje u našoj tabeli čisto radi razjašnjenja šta se ovde tačno događa:

mysql> alter table domains 
add column nullorder tinyint not null;
Query OK, 5 rows affected (0.01 sec)
Records: 5  Duplicates: 0  Warnings: 0

a zatim i update-ovati vrednosti nullorder polja tako da sadrže vrednost IF iskaza odozgo:

mysql> update domains 
set nullorder = if(expire_date is null, 1, 0);
Query OK, 1 row affected (0.00 sec)
Rows matched: 5  Changed: 1  Warnings: 0

mysql> select * from domains;
+----+-------------------+-------------+-----------+
| id | domain            | expire_date | nullorder |
+----+-------------------+-------------+-----------+
|  1 | dinke.net         | 2010-01-17  |         0 | 
|  2 | lampix.net        | 2009-12-26  |         0 | 
|  3 | blogodak.com      | 2010-09-08  |         0 | 
|  4 | maestrodesert.com | 2009-09-11  |         0 | 
|  5 | nepostojeci.com   | NULL        |         1 | 
+----+-------------------+-------------+-----------+
5 rows in set (0.00 sec)

Sve u svemu naš gornji query iz rešenja problema:

select * from domains 
order by if(expire_date is null, 1, 0), expire_date asc;

Potpuno je isto što i ovaj query:

mysql> select * from domains 
order by nullorder, expire_date;
+----+-------------------+-------------+-----------+
| id | domain            | expire_date | nullorder |
+----+-------------------+-------------+-----------+
|  4 | maestrodesert.com | 2009-09-11  |         0 | 
|  2 | lampix.net        | 2009-12-26  |         0 | 
|  1 | dinke.net         | 2010-01-17  |         0 | 
|  3 | blogodak.com      | 2010-09-08  |         0 | 
|  5 | nepostojeci.com   | NULL        |         1 | 
+----+-------------------+-------------+-----------+
5 rows in set (0.00 sec)

osim što naravno nullorder polje nismo morali da kreiramo.

Naravno na sličan način možemo dobiti NULL polja na početku u desc prikazu (za slučaj da je to ikome potrebno).

Browser class by Bluesman

Moj prijatelj i kolega Goran Pilipović (u zajednici poznatiji kao Bluesman) nedavno mi je poslao svoju verziju moje Browser Detection klase.

Praktično radi se o potpuno novom kodu jer za razliku od moje poprilično jednostave klase Blues koristi značajno veći broj metoda. Evo primera korišćenja:

require_once "class.Browser.php";

Browser::get();

echo "<pre>";
echo "<br />User Agent:      ".Browser::ua();
echo "<br />Browser Id:      ".Browser::id();
echo "<br />Browser Name:    ".Browser::name();
echo "<br />Browser Version: ".Browser::version();
echo "<br />OS:              ".Browser::os();
echo "<br />Device:          ".Browser::device();
echo "<br />Platform:        ".Browser::platform();
echo "<br />Is PC:           ".yesno(Browser::isPc());
echo "<br />Is Windows:      ".yesno(Browser::isWindows());
echo "<br />Is Mac:          ".yesno(Browser::isMac());
echo "<br />Is Linux:        ".yesno(Browser::isLinux());
echo "<br />Is Symbian:      ".yesno(Browser::isSymbian());
echo "<br />Is IE:           ".yesno(Browser::isIe());
echo "<br />Is Safari:       ".yesno(Browser::isSafari());
echo "<br />Is Firefox:      ".yesno(Browser::isFirefox());
echo "<br />Is Chrome:       ".yesno(Browser::isChrome());
echo "<br />Is Opera:        ".yesno(Browser::isOpera());
echo "<br />Is iPhone:       ".yesno(Browser::isIphone());
echo "<br />Is Handlheld:    ".yesno(Browser::isHandheld());
echo "<br />Is Phone:        ".yesno(Browser::isPhone());
echo "<br />Is Console:      ".yesno(Browser::isConsole());
echo "<br />Is Terminal:     ".yesno(Browser::isTerminal());
echo "</pre>";

Kompletnu klasu kao i test fajl sa primerima možete skinuti ovde. U slučaju da pronađete neki bug možete ga prijaviti ovde (samo ostavite komentar sa opisom problema).

Koji programski jezik učiti

Pitanje iz naslova jedno je od najčešće postavljenih pitanja svršenih diplomaca ili ljudi koji ulaze u svet programiranja. Pitanje nije trivijalno iz jednostavog razloga što dobro odabrana tehnologija znači veće mogućnosti za pronalaženje posla, bolje plaćen posao, … u najkraćem – svetliju budućnost.

Sve do juče na pitanje iz naslova bez mnogo razmišljanja odgovarao bih – PHP. Jednostavno, PHP poslova uvek ima, PHP se relativno brzo nauči, tako da za relativno kratko vreme možete doći do posla. Čak i za one bez ikakvog iskustva, otvorenost Web platforme omogućava početnicima da naprave sopstveni sajt, sajt komšijine prodavnice, prijatelja … whatever … i tako za kratko vreme steknu kakve takve reference i iskustvo – stvar koju tako mnogo tražimo mi koji tragamo za kvalitetnim developerima.

Elem, pre nekoliko dana kolega iz FBM-a zamolio me je da otvorim novu poziciju za iPhone developere, nakon čega sam nažalost jako brzo saznao da se broj iPhone Developera u Srbiji može nabrojati “na prste” jedne ruke.

Međutim situacija u svetu a naročito u USA je dramatično drugačija. Broj downloada iPhone aplikacija na Apple Application Store-u bliži se cifri sa 9 nula, a trenutno na listi najprodavanijih knjiga na Amazonu u kategoriji Računari i Internet možete naći samo nekoliko programerskih knjiga, od kojih su apsolutno sve vezane za iPhone. Ista priča je i kada odete i na kategoriju Programming gde su tri od prvih pet naslova knjige o razvoju iPhone aplikacija!

iPhone Development Books
iPhone Application Develpment Books

Nažalost cene Apple računara i nedostatak zvanične iPhone podrške u Srbiji i dalje je veliki problem u ovoj priči. No, za one za koje taj problem nije nepremostiv, današnji odgovor na pitanje iz naslova definitivno bi bio Objective C. Nisam siguran da ću čak i ja odoleti iskušenju …

Browser Detection Update

Danas sam sticajem okolnosti bio u prilici da nakon duzeg vremena updatujem svoju klasu za detekciju browsera. Radi se o klasi koja omogucava detektovanje tacne verzije Browsera/OS-a na osnovu user agenta browsera.

Npr: Mozilla Firefox 3.0.4 / Mac OS X

Može biti od koristi u slučaju da želite da korisnike određenih browsera redirektujete na posebne strane, kod pravljenja sopstvene statistike korišćenosti browsera itd.

Update sadrži dodatu podršku za Google Chrome, iPhone i Windows Vista OS.

Kompletan kod sa primerima možete downloadovati ovde

Izlaz scripta na ekranu i fajlu

Zbog prirode svog posla gotovo svakodnevno se bakćem sa raznoraznim php cli scriptovima. Često su u pitanju programi koji rade jako dugo (satima, neki čak i danima), ponekad praveći veliku količinu outputa, te zbog toga praktikujem da iste startujem unutar screen sesije, kako bih uvek mogao da se ponovo ulogujem u sesiju i bacim pogled kako stoje stvari.

Veoma je dobro logovati kompletan output skripta u fajl, za slučaj da je nešto pošlo naopako, ali nekako sam uvek voleo da onako “live” vidim na ekranu šta se dešava, umesto da mi ceo izlaz stoji u nekom tamo log fajlu. No, zahvaljući unix tee komandi i ovom postu, danas sam naučio da mogu imati obadva.

./script.php | tee out.log

Ovo će kreirati fajl out.log i usmeriti kompletan output scripta u njega, ali će isti biti prikazan i na ekranu (STDOUT). U slučaju da želite samo da dodate (append) output u postojeći log, nema problema:

./script.php | tee -a out.log

MySQL Jokeri

Pitanje za milion dolara u nekom popularnom kvizu. Imate samo 15 sekundi za odgovor. Srecni ste jer ste izvukli pitanje iz vase omiljene oblasti :)

Kako obrisati sve korisnike ciji nick pocinje sa donjom crtom (‘_’).

I vas odgovor je naravno:

delete from users where username like '_%';

Vec mastate o raskalasnom zivotu daleko od kompjutera, mozda i neka jahtica pride (i sve sto ide u to lol). Glas voditelja vraca vas u surovu realnost. “Zao mi je, ovo nije tacan odgovor”. U neverici gledate vas query i milione koji su otisli u nepovrat.

Verovali ili ne, slicnu nevericu iskusio sam na svojoj kozi pre nekoliko veceri, tacnije 8. aprila. U tabeli sa nekoliko miliona generisanih domena uocen je bug (mala greska u regularnom izrazu) i nekako su generisani i domeni koji pocinju sa znakom “_”. Underscore (donja crta) naravno nije dozvoljen kod domena, bug je ispravljen ali treba obrisati i te nevalidne domene iz baze. Nista lakse, jedan brzi query i sve ce ubrzo biti pocisceno. Otvaram mysql klijent i bez mnogo razmisljanja kucam:

mysql> delete from result_domains where domain like '_%';
Query OK, 9035782 rows affected (9 min 57.35 sec)

WTF!?? Query je obrisao sve domene iz tabele. Ali kaaakooo???

I onda se setim. Donja crta – ‘_’ je poput ‘%’, takodje joker karakter koji za razliku od ‘%’ (koji menja ‘nula ili vise’ karaktera), ‘_’ menja tacno jedan karakter. Ne secam se kada sam ga poslednji put koristio (ako sam ga uopste koristio), ali znam da mi je on dosao glave.

Cimam admina na ICQ … treba mi backup, server taj i taj, tabela ta i ta, poslednji koji imamo … ASAP! Posle 30-tak sekundi admin se javlja “ok, poslednji koji imamo je od 8 marta. Gde da ti stavim?”. FUCK!!!

FUCK!!! FUCK!!! FUCK!!! FUUUUCK!!!

Ustajem od kompjutera i besno setam kroz sobu. Na postoji nacin da objasnim klijentu da sam jednim jedinim kverijem sjebao sate i sate mukotrpnog rada, podatke vredne verovatno hiljade dolara. Da imam utoku verovatno bih pao u iskusenje da pucam sebi u glavu. Mozda pre toga da sredim i admina? Kako god, sta je tu je, ne preostaje nista drugo nego da napisem email i objasnim svom klijentu sta se desilo.

Sedam ponovo za comp, ali umesto da otvorim thunderbird, cimam ponovo admina.

– Ja: “Jebote … kako se desilo da je poslednji backup star mesec dana???”
– Admin: “Kako to mislis? Ovo je backup od sinoc!?”
– Ja: “Rekao si 8 mart???”
– Admin: “Ups. Sorry, 8. April. Sad gledam, kreiran je pre samo par sati. Moze?”

I tako, my ass has been saved. Ali moglo je i biti drugacije. Mnogo drugacije.

Pouka price:

1) Budite ekstremno oprezni kada kucate nesto unutar mysql klijenta (ili phpmyadmina) koji barata sa live podacima
2) Ako morate da brisete/update-ujete nesto, uvek uradite prvo count nad istim podacima i istom where klauzom kako bi ste se uverili da je to bas ono sto ste zeleli.

Npr:

mysql> select count(*) from result_domains where domain like '_%';
+----------+
| count(*) |
+----------+
|  9035782 |
+----------+
1 row in set (5.27 sec)

(ups nesto ne valja, ovo ce obrisati sve domene koje imamo)

mysql> select count(*) from result_domains where domain like '\_%';
+----------+
| count(*) |
+----------+
|        0 |
+----------+
1 row in set (4.18 sec)

Aha, sada je sve ok :)

3) Uvek pravite redovan backup. Ako to za vas rade admini postarajte se da rade svoj posao kako treba. Cak i tada nije losa ideja da s vremena na vreme napravite sopstveni backup.

P.S. Problem sa pocetka price (matchovanje stringa koji pocinje sa ‘_’) resava se jednostavnim escapovanjem specijalnog karaktera. Dakle:

select foo from footable where somefield like '\_%';

ili ako ste ljubitelj regularnih izraza:

select foo from footable where somefield regexp '^_';

PHP 4 End of Life Announcement

From php.net

[13-Jul-2007]

Today it is exactly three years ago since PHP 5 has been released. In those three years it has seen many improvements over PHP 4. PHP 5 is fast, stable & production-ready and as PHP 6 is on the way, PHP 4 will be discontinued.

The PHP development team hereby announces that support for PHP 4 will continue until the end of this year only. After 2007-12-31 there will be no more releases of PHP 4.4. We will continue to make critical security fixes available on a case-by-case basis until 2008-08-08. Please use the rest of this year to make your application suitable to run on PHP 5.

For documentation on migration for PHP 4 to PHP 5, we would like to point you to our migration guide. There is additional information available in the PHP 5.0 to PHP 5.1 and PHP 5.1 to PHP 5.2 migration guides as well.

Amen to that. Finally this will move the rest of people to PHP5, so we can finally start using all those great PHP5 features without worrying that such code cannot be used on most of client servers. Looking forward to PHP6 now :)

MySQL – Backup of Big MyISAM Tables

If you ever dealt with backup of MySQL tables, you probably used mysqldump utility, which allows you to dump all data into some mytables.sql file, which you can backup somewhere, import to other MySQL server etc. I used this procedure for a thousands times so far, and generally it goes as follows:

export:

mysqldump -udinke -pmojpass -hhostname.of.server1 dbname table1 table2 tableN > dump_file.sql
mysql -udinke -pmojpass -hhostname.of.server2 dbname  < dump_file.sql

Sometimes there is a problem when you move data from new version of MySQL to old, and in that case you have to specify proper compability flag when running mysql dump (--compatible=name where name can be mysql323, mysql40, postgresql, oracle etc.).

Anyway, few days ago in order to move data from one MySQL to another, I had to dump some ... let say big mysql tables (about 10 tables, where each contained about 10 millions of records). After long lasted procedure (dump to file, gzip, scp to other server) I finally started import. However, after 3 hours (yes, three hours) instead of Linux prompt I got this:

[dinke@um-917 ~/public_html]$ mysql -udinke -p325ewfwt23rasf
keyword_discovery < es_miner_data.sql
ERROR 1582 (23000) at line 163833: Duplicate entry '1167548' for key
'PRIMARY'

WTF? I moved data from old server to new, which means import should run without compability problems. It could be indexes on old table were damaged, but instead to wait like 2 more hours in order to complete check & repair procedure, I've decided to create dump file, this time with ignore option, so all insert queries in dump file are "insert ignore", so in case of error like previous one, errors will be ignored. Not very smart, but those data are not really high sensitive, and I can afford to lose few records but can't afford to lose 10 hours for import!

So, dump, gzip, scp, import again ... which lasted long... loooooong .... so fucking long that after 3 hours after I started import I started to think about other solutions. And solution was dumb but effective. We simple moved all MySQL data files (*.MYI, *.MYD i *.frm) from one server to another, and then we run myisamchk in order to fix those tables because ... we didn't shutdown MySQL server during copy procedure which generally could cause some problems with data.

All in all, this procedure went very fast (the longest was actual copy from one host to another), and in less than half hour I got everything settled down.

At the end I came with conclusion that in case of really big tables, using tool like mysqldump is unaccepted as backup solution because it takes literally hours to complete. One of solution to that problem can be to copy MySQL data files like I did. That shouldn't be a problem, because tables are "platform safe" meaning, binary file created on one platform (ie *.MYI file on Linux) will work without problem when moved to other problem (ie. on Windows). The only one real problem is that if you copy data wihout MySQL shutdown, table files there are in some kind of "state of flux" which can be a problem, especialy if they are highly used in moment when you do copy. That's why you need to do myisamchk on it.

MySQL 5.x – Konačno unapređen klijent

Listajući svoje omiljene WebDev feed-ove, za oko mi je zapao jedan post na sjajnom MySQL Perfomance Blogu:

…if you press CTRL-C MySQL Command Line Client will not exit but will terminate query being executed.

Drugim rečima, u dosadašnjim verzijama MySQL klijenta, kada bi ste ukucali neki query i pritiskom na CTRL-C pokušali da prekinete njegovo izvršavanje, CTRL-C bi zapravo ubio MySQL klijent, ali query nastavlja da se izvršava u pozadini! Rešenje u takvim slučajevima je traženje id-a tog query-a na listi procesa upitom “show full processlist”, a zatim njegovo “ubijanje” upitom “kill 12345” gde je 12345 u ovom slučaju id procesa koji želimo da ubijemo. Dakle, recimo nešto ovako:

mysql> select * from odm_result_keywords where keyword like '%foo%joe%';
^CAborted
bash-2.05b$ mysql -A --enable-local-infile -udinke -ppass mydb
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 1512 to server version: 4.1.18-log

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql> show full processlist;
+------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+
| Id   | User  | Host                       | db                | Command | Time | State        | Info                                                               |
+------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+
| 1486 | dinke | localhost                  | mydb | Query   |    3 | Sending data | select * from odm_result_keywords where keyword like '%foo%joe.cl' |
+------+-------+----------------------------+-------------------+---------+------+--------------+--------------------------------------------------------------------+
1 row in set (0.00 sec)

mysql> kill 1486;
mysql>

Zahvaljujući izmenama u MySQL klijentu, sada je dovoljno da stisnete CTRL-C, i query će biti odmah prekinut:

mysql> select domain from odm_result_keywords_de where whois_status is null and domain like '%.%.%';
Query aborted by Ctrl+C
ERROR 1317 (70100): Query execution was interrupted
mysql>

Više informacija o ovoj, kao i drugim izmenama u pomenutoj verziji (5.0.25) možete naći ovde.

MySQL – Backup velikih MyISAM tabela

Ako ste ikada morali da backup-ujete ili prebacujete podatke iz jedne ili više MySQL tabela, znate da se za to koristi mysqldump utility, pomoću kojeg jednostavno podatke iz MySQL-a “dumpujete” u neki mojetabele.sql fajl, koji zatim možete sačuvati kao backup, prebaciti i importovati na neki drugi MySQL server, itd. Ovu proceduru koristio sam milion puta do sada, i ona se generalno svodi na izvršavanje sledećih komandi u linuxu:

export:

mysqldump -udinke -pmojpass -hhostname.prvog.servera ime_baze tabela1 tabela 2 tabelan > dump_fajl.sql

import:

mysql -udinke -pmojpass -hhostname.drugog.servera ime_baze < dump_fajl.sql

Tu i tamo pojavi se problem kada prebacujete dump sa novije verzije MySQL-a na stariju, i tada je poželjno specifirati i odgovarajući compability flag prilikom izvršavanja mysqldump komande (--compatible=name gde name može biti mysql323, mysql40, postgresql, oracle itd.).

Elem, pre nekoliko dana zbog prebacivanja MySQL servera sa jedne lokacije na drugu, morao sam da odradim dump solidno velikih MySQL tabela (10-tak tabela gde je svaka imala preko 2 miliona slogova). Nakon užasavajuće duge procedure dumpa, gzipovanja i scpovanja na drugi server, konačno sam startovao import. Međutim, nakon 3 sata (tri sata) umesto standardne "no news is a good news" poruke, kada sam bacio pogled na status dočekala me je sledeća poruka ...:

[dinke@um-917 ~/public_html]$ mysql -udinke -p325ewfwt23rasf
keyword_discovery < es_miner_data.sql
ERROR 1582 (23000) at line 163833: Duplicate entry '1167548' for key
'PRIMARY'

WTF? Prebacivao sam podatke sa starije verzije na noviju, što znali da import mora proći glatko! Ovo bi trebalo da znači da je MySQL negde nešto pobrljavio sa indexima, i umesto da čekam još 2 sata da se odradi check & repair, odlučujem se da napravim ponovo dump fajl, ovaj put sa ignore opcijom, kako bi svi inserti u dump fajlu bili "insert ignore" tako da u slučaju ovakve greške ne bi došlo do pucanja. Ne preterano pametno, ali podaci nisu extremno osetljivi i mogu da dozvolim luksuz da izgubim desetak slogova, ali ne i desetak sati za import!

Naravno, novi dump, zip, scp ... to je traajalooo ... toliko dugo da sam posle par sati nakon startovanja importa krenuo da mozgam sa administratorom (kuki) oko alternativnih rešenja. A rešenje je bilo jednostavno ali efikasno. Prekopirali smo sve MySQL data fajlove sa jednog servera na drugi (*.MYI, *.MYD i *.frm fajlove), a zatim odradili myisamchk kako bi sredili pomenute tabele, jer nismo smeli da spuštamo server prilikom backup-a što generalno može da prouzrokuje dosta problema (tipa oštećene tabele) ako se na ovaj način backupuju fajlovi.

Sve u svemu, ova operacija prošla je jako brzo (najduže je trajalo kopiranje fajlova sa jednog hosta na drugi), i za manje od pola sata cela procedura bila je završena.

Zaključak do koga smo došli je da je kod velikih tabela zbog dužine trajanja operacije dumpovanje praktično neprihvatljivo kao backup rešenje, i da se samim tim kao jedino rešenje nameće kopiranje MySQL data fajlova. Kod MyISAM tabela to nije nikakav problem, jer su one "platform safe", tj. binarni fajl kreiran na jednoj platformi (recimo *.MYI fajl na linuxu) radiće bez ikakvih problema na drugoj platformi (recimo isti *.MYI fajl na Windowsu).

Client size resizovanje slika

Radeći na servisu Blogodak, nedavno sam se susreo sa problemom kod slika velikih dimenzija koje neki korisnici uključuju u svojim feedovima. Naime, kako Blogodak “vuče” feed-ove sa raznih domaćih blogova, a sa njima i slike koje se u njima nalaze, dešavalo se da one dimenzija većih od 560px uredno “skrljaju” layout blogotka, obzirom da to e == “Microsoft Internet Explorer”)
{prevazilazi predviđenu veličinu containera, tako da u najboljem slučaju dolazi do pojave horizonatalnog skrol bara. Kako nemamo nikakav uticaj na slike koje se vuku sa servera gde su hostovane, server side varijante(npr. resize korišćenjem GD liba ili Image Magicka) nisu primenjive, jedino rešenje je da se dimenzije slike smanje direktno u browseru – Client Side.

CSS Rešenje
Za browsere koji imaju potpunu podršku za css2, dovoljno je staviti nešto tipa:

.container img{
  max-width:560px;
}

u stil strane, tako da će sve slike koje se nalaze unutar nekog <div idclass=”container”>…</div> elementa biti ograničene na max 560 piksela, tj. biće automatski resizovane na odgovarajuću veličinu. Naravno, ovo ne radi u IE-u (verzije < 7) tako da je potreban hack :) IE Hack (CSS verzija)

.container img{
  max-width:560px;
  /*hack for IE*/
  width: expression(this.width > 560 ? 560: true);
}

Ovaj css hack je validan samo u IE-u, koristi se neka vrsta “ternarnog operatora”, a sam hack skinut je sa ovog bloga. Rado bih vam rekao da više informacija potražite tamo, ali i sam autor priznaje da na razume mnogo oko toga “kako to radi”, ali jednostavno radi. Za ljubitelje standardnijih rešenja, sledi JS verzija.

IE Hack (JS verzija)

Obzirom da mi se nije svidela ideja da koristim nevalidan css kod koji uz to i ne razumem u potpunosti(na stranu što je pravio i neke nekoegzistentne probleme sa IE-om za koji je i namenjen), odlučio sam se za JavaScript rešenje koje sledi.

function fixImages()
{
  //fix images for ie only
  if(navigator.appName == "Microsoft Internet Explorer")
  {
    for(i=0; i<document.images.length; i++)
    {
      //if image is bigger than 560
      if(document.images[i].width > 560)
      {
        imgRatio = document.images[i].width/document.images[i].height;
        document.images[i].width = 560;
        document.images[i].height = 560 / imgRatio;

        //hack neophodan da bi se uklonio skroler zbog prvobitne velicine slike
        //mainContent je ime div kontejnera koji drzi sadrzaj strane				
        divid = document.getElementById('mainContent');
        content = divid.innerHTML;
        divid.innerHTML = '';
        divid.innerHTML = content;
      }
    }
  }
}

Ova funkcija se poziva nakon učitavanja stane(onLoad). U slučaju da je u pitanju IE, proveravaju se dimenzije svih slika na strani(parsuje se niz document.images), i u slučaju da dimenzije prevazilaze 560 piksela, setuju se na manje, uz očuvanje proporcija slike. Naravno, ovaj metod ima manu, jer je neophodno da se sve slike prvo učitaju, pa tek onda dolazi do resizovanja. Kod IE-a i pored resizovanja slike na “prihvatljive” dimenzije, bilo je potrebno nekako mu i staviti do znanja da je došlo do promene dimenzija(samo resizovanje nije dovoljno da nestane horizontalni skroler), tako da je bilo neophodno ručno ili skriptabilno rezisovati i prozor browsera. Srećom posle manjeg “prčkanja” sa alternativama prošlo je i jednostavnije rešenje sa setovanjem kontejnera cele strane na prazan string i vraćanjem na prvobitno stanje korišćenjem innerHTML-a.

Nadam se da će ovo nekome biti od koristi :)

Kako Prepoznati Srbina

Sećate li se mog prijatelja Manu-a, amerikanca koji prati isključivo fudbal(i to ne američki) ?

Manu       15.12.20 20:34 usa stole a serb footballer

Manu       15.12.20 20:35 http://chivas.usa.mlsnet.com/players/bio.jsp?team
                               t120&player=kljestan_s&playerId=kle326299&statTyp
                               =current

dinke      15.12.20 20:35 Sacha Kljestan ? Serb ?

Manu       15.12.20 20:36 lol

Manu       15.12.20 20:36 you should know

Manu       15.12.20 20:36 that is serb name

Manu       15.12.20 20:36 that is not serb name

Manu       15.12.20 20:36 that is serb name

Manu       15.12.20 20:36 that is not serb name

Manu       15.12.20 20:36 to be or not to be a serb

Manu       15.12.20 20:36 that is the question

Nakon što mi je sinoć po n-ti put(gde n–>00) servirao vest o nekom mom “zemljaku”, morao sam da potrošim vreme i objasnim mu kako da prepozna Srbina u vestima(tim pre što u našem multinacionalnom timu imamo dvocifren broj ovdašnjih programera):

function isSerb($lastname)
{
	$found = preg_match("/^[\pL]+ić$/u", $lastname);
	
	if($found)
	{
		return true;
	}
	else
	{
		return false;
	}
}

Uvod u GeoIP

Verovatno ste već bili u prilici da koristite Google Analytics alat, gde između ostalog možete na mapi sveta videti odakle tačno dolaze posetioci vašeg sajta, ili ste tu i tamo posetili sajt koji bi Vam između ostalog izbacio podatke o Vašoj trenutnoj lokaciji. Naravno, nije u pitanju nikakva magija, tačna lokacija posetioca definisana je na osnovu njegove IP adrese, a tehnologija koja se koristi prilikom “lociranja” korisnika opšte je poznata pod nazivom GeoIP.

Danas ćemo pričati o tome kako “locirati” posetioca pomoću PHP-a i Max Mind-ove GeoIP baze. U primerima koji slede koristicemo besplatne(lite) verzije GeoIP baza, obzirom da se za pune verzije plaća $50USD + $12USD za update (GeoIP Country baza) i $370USD + $90USD za update (GeoIP City baza). Mana lite verzija je što nisu uvek 100% ažurne, ali će odlično poslužiti za naš tutorijal, a iz ličnog iskustva tvrdim da su upotrebljive i u većini live projekata.

MaxMind obezbeđuje API za nekoliko popularnih programskih jezika, (kompletna lista dostupna je ovde), a detalji o PHP API-u dostupni su ovde. Pored takozvanog “Pure PHP API-a” koji ćemo ovde koristiti, postoje i PECL ektstenzija kao i apache modul(mod_geoip), koji pružaju bolje perfomanse ali i komplikovaniji setup.

Za početak neophodno je da skinete sve fajlove koji se nalaze na http://www.maxmind.com/download/geoip/api/php/ i snimite ih negde unutar vašeg Web stabla(recimo /htdocs/geoip). Za korišćenje GeoIP Country treba skinuti lite bazu odavde, a za city GeoLiteCity bazu odavde. Radi jednostavnosti korišćenja, obe baze ćemo takođe raspakovati u isti direktorijum gde smo i snimili fajlove iz PHP API-a (/htdocs/geoip).

GeoIP Country
——————————–

Idemo sa primerom detekcije zemlje posetioca:

<?php
/**
 * Primer Koriscenja GeoIP Country Baze
 * 
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoip.inc");

$gi = geoip_open("GeoIP.dat", GEOIP_STANDARD);

$ip = $_SERVER['REMOTE_ADDR'];
//ako testirate u lokalu koristite ovaj ip radi testa
//posto ce $_SERVER['SERVER_ADDR'] biti 127.0.0.1
//$ip = "89.216.226.174";

$country_name = geoip_country_name_by_addr($gi, $ip);
$country_code = geoip_country_code_by_addr($gi, $ip);
if($country_name)
{
	echo "Zemlja iz koje nas posecujete je: $country_name <br />";
	echo "Skracena Oznaka: $country_code <br />";
}
else 
{
	echo "Nazalost, nismo bili u mogucnosti da vas lociramo.";
}

geoip_close($gi);
?>

Dakle, na početku uključujemo geoip.inc koji sadrži sve f-je potrebne za korišćenje GeoIP County baze, zatim kreiramo novu instancu GeoIP klase pomoću geoip_open f-je, i na kraju pozivamo odgovarajuće f-je (geoip_country_name_by_addr i geoip_country_code_by_addr) da bi smo dobili ime/kod zemlje u kojoj se nalazi ip adresa posetioca(u slučaju da testirate u lokalu nemojte koristiti $_SERVER[‘REMOTE_ADDR’]).

Kao izlaz skripta, trebalo bi da dobijemo nešto poput:

Zemlja iz koje nas posecujete je: Serbia and Montenegro 
Skracena Oznaka: CS

F-je koje smo koristili da bi dobili podatke o zemlji posetioca, samo su neke od f-ja koje su dostupne u API-u. Ostatak možete i sami pronaći jednostavnom analizom PHP sourca geoip.inc fajla.

GeoIP City
—————————-

A sada da proširimo podatke o zemlji sa tačnom lokacijom (grad, poštanski kod itd).

<?php
/**
 * Primer Koriscenja GeoIP City Baze
 * 
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoipcity.inc");

$gi = geoip_open("GeoLiteCity.dat", GEOIP_STANDARD);

$ip = $_SERVER['REMOTE_ADDR'];
//ako testirate u lokalu koristite ovaj ip radi testa
//posto ce $_SERVER['SERVER_ADDR'] biti 127.0.0.1
//$ip = "89.216.226.174";

$record = geoip_record_by_addr($gi, $ip);

if(!$record)
{
	echo "Nazalost, nismo bili u mogucnosti da vas lociramo.";
}
else
{
	echo "Zemlja: " .$record->country_name . "<br />";
	echo "Skracena Oznaka: " . $record->country_code . "<br />";
	echo "Skracena Oznaka2: " . $record->country_code3 . "<br />";
	echo "Region: " .$record->region . "<br />";
	echo "Grad: " .$record->city . "<br />";
	echo "Postanski Kod: " .$record->postal_code . "<br />";
	echo "Geog. Sirina: " .$record->latitude . "<br />";
	echo "Geog. Duzina: " .$record->longitude . "<br />";
}

geoip_close($gi);
?>

Kao što vidite, PHP kod je sličan kodu za detekciju zemlje, s tim što smo koristili geoipcity.inc kao i GeoLiteCity.dat bazu. F-ja geoip_record_by_addr($gi, $ip) vraća instancu klase ‘geoiprecord’ koja sadrži kao promenljive(osobine) podatke o lokaciji koje koristimo u gornjem kodu. Nakon pokretanja skripta trebalo bi da dobijemo nešto poput:

Zemlja: Serbia and Montenegro
Skracena Oznaka: CS
Skracena Oznaka2: SCG
Region: 02
Grad: Beograd
Postanski Kod: 
Geog. Sirina: 44.8186
Geog. Duzina: 20.4681

Napominjem da je GeoIP baza najažurnija kada su u pitanju gradovi sa severnoameričkog dela planete, dok je njena preciznost znatno manja kada se dođe do “egzotike” u koju nažalost spada i Srbija.

CaseStudy – Redirekcija na osnovu IP adrese
————————————————————–
Za kraj znanje stečeno ovde iskoristićemo u jednom pravom projektu. Naime cilj je da se na dvojezičnom sajtu(blogu) korisnici koji dolaze iz Srbije usmere na srpsku verziju sajta, dok će se svi ostali usmeriti na englesku verziju. Evo kako to izgleda:

<?php
/**
 * Case Study - Redirekcija na osnovu lokacije
 * 
 * @version $Id$
 * @package geoip
 * @copyright © 2006 Lampix.net
 * @author Dragan Dinic <dinke@lampix.net>
 */

require_once("geoip/geoip.inc");

$gi = geoip_open("geoip/GeoIP.dat",GEOIP_STANDARD);

$country_code = geoip_country_code_by_addr($gi, $_SERVER['REMOTE_ADDR']);

geoip_close($gi);

if($country_code == 'CS')
{
        header("HTTP/1.1 301 Moved Permanently");
        header('Location: http://www.dinke.net/blog/sr/');
}
else
{
        header("HTTP/1.1 301 Moved Permanently");
        header('Location: http://www.dinke.net/blog/en/');
}
?>

Primer koji vidite gore koristi se upravo na ovom blogu, kako bi sve korisnike koji ne dolaze iz Srbije automatski preusmerio na englesku verziju bloga. Slanje custom 301 redirection headera je važno kako bi botovi (Google i sl.) indeksirali strane na odgovarajući način.

PHP 5.2 upload progress meter

Yesterday I’ve spent considerable amount of time in order to find out more about the most interesting new PHP 5.2 feature – hook for upload progress meter. Except for this link I haven’t found anything else, no php source code example how to make one.

However, after I looked at internal php mailing list archive, I’ve found this thread, still no php data found, but Rasmus mentioned link with an example at http://progphp.com/progress.php. I immediately tried upload and it looked very cool. Then I looked at html source code and noticed one unusual thing there: APC_UPLOAD_PROGRESS hidden field inside of html source. I knew it must be important so I’ve googled for it, and insteresting enough first result was source code of Rasmus example above :)

So, I took complete source (I figured out later it is upload meter example made by Rasmus Lerdorf) and quickly tried to make it working under my fresh new installed PHP 5.2.0. Unfortunately, it didn’t work since it needed apc stuff installed. After I looked at apc documentation, I found that I need to grab it from list of pecl dll’s for windows php 5.2 version. Unfortunately, after I’ve downloaded it from here I’ve noticed apc dll is missing there ?!!

Again, I had to google for php_apc.dll and after a while found needed dll available at http://pecl4win.php.net/ext.php/php_apc.dll. In order to make it working, you have to save dll file under php/ext dir (i.e. c:\php\ext on windows) and put this to php.ini:

extension=php_apc.dll

Unfortunately, it still didn’t work, so I’ve looked at apc docs further. Finally on this page I’ve found apc have new “special feature” which is directly related to our new upload feature.

    apc.rfc1867             RFC1867 File Upload Progress hook handler is only available
                            if you compiled APC against PHP 5.2.0 or later.  When enabled
                            any file uploads which includes a field called 
                            APC_UPLOAD_PROGRESS before the file field in an upload form
                            will cause APC to automatically create an upload_
                            user cache entry where  is the value of the 
                            APC_UPLOAD_PROGRESS form entry.
                            (Default: 0)

After I figured out on my phpinfo page apc.rfc1867 setting is turned off, I’ve added

apc.rfc1867 = on 

in php.ini, and after restart was finally able to enjoy new fancy upload progress meter :)

upload progress meter

Btw, upload also depend of json turned on as well, but it was already turned on so I didn’t have any more problems.

About the code Rasmus used in his example, I am tired to analyze it more now, but obviously it use Yahoo! User Interface Library to create progress bar and json/apc to control it from php during file upload.

I hope this will be helpful for someone. Enjoy ;)

PHP 5.2.0 Released

New release of PHP 5.2.0 is available for download.

Along with plenty of bugfixes, new release bring us some interesting features, the most interesting among them are hooks for tracking file upload progress , as well as new ZIP extension for creating and editing zip files.

More informations about this release can be found in the release announcement 5.2.0.

Izašao PHP 5.2.0

Nova verzija PHP-a, 5.2.0 od juče je dostupna za download.

Pored gomile ispravljenih bagova, nova verzija donosi i neke interesantne mogućnosti od kojih su meni najinteresantnije podrška za “progres bar” prilikom uploada, kao i podrška za zip fajlove. Više informacija o tome šta nam novo donosi PHP 5.2 možete pronaći ovde.

MySQL – Kako ukloniti duplikate

Ovaj post je donekle inspirisan problemom koji je bluesman opisao na svom blogu. Dva problema koji nemaju veze jedan sa drugim, ali odlično pokazuju zašto poslodavci insistiraju na x godina iskustva (gde x > 2) :)

Naime, danas sam “u minut do dvanaest” dobio fajl sa par stotina hiljada keyworda koje treba procesirati ASAP (oh kako volim ovu reč). Naravno, u žurbi nisam mnogo gledao u fajl, provalio sam da je format uobičajen (1 keyword po liniji), importovao u bazu sa load data local infile … , startovao procesiranje i to bi bilo to.

Par minuta nakon toga startuje me kolega koji je zaboravio da mi kaže da se kod nekih keyworda “potkralo” par domena tipa “foo.eu” gde ono “.eu” samo treba izbaciti. Sve je to lepo, ali svi su već importovani u tabelu i samo što nisu pokupljeni. Rešen da brzo delam, odlučim da je najjednostavnije odraditi “search/replace hack” direktno u tabeli, ali …

mysql> update odm_master_keywords 
       set keyword = replace(keyword, '.eu','');
ERROR 1062 (23000): Duplicate entry 'academia' for key 2

Shit. Posto je keyword polje unique, neki od tih “.eu” je nakon search/replace-a napravio problem sa istim identičnim keywordom. Ok, obrisaću ručno te duplikate, valjda ih nema mnogo …

mysql> delete from odm_master_keywords_cl where keyword='academia';
Query OK, 1 row affected (0.02 sec)

mysql> update odm_master_keywords set keyword = replace(keyword, '.eu','');
ERROR 1062 (23000): Duplicate entry 'academic' for key 2
mysql> delete from odm_master_keywords_cl where keyword='academic';
Query OK, 1 row affected (0.02 sec)

mysql> update odm_master_keywords set keyword = replace(keyword, '.eu','');
ERROR 1062 (23000): Duplicate entry 'actriz' for key 2
mysql> delete from odm_master_keywords where keyword='actriz';
Query OK, 1 row affected (0.02 sec)

mysql> update odm_master_keywords set keyword = replace(keyword, '.eu','');
ERROR 1062 (23000): Duplicate entry 'foo' for key 2
mysql> delete from odm_master_keywords where keyword='foo';
Query OK, 1 row affected (0.48 sec)
...

I tako, mogao bih ovako do prekosutra … Mora da postoji nešto pametnije :)

Nakon kraćeg razmišljanja, došao sam do ovog rešenja. Privremeno skloniti “unique” index, odraditi update i vratiti index natrag, ali sa ignore opcijom koja će “ubiti” višak duplikata. Dakle:

mysql> alter table odm_master_keywords drop key keyword;
Query OK, 338565 rows affected (13.79 sec)
Records: 338565  Duplicates: 0  Warnings: 0

mysql> update odm_master_keywords set keyword = replace(keyword, '.eu','');
Query OK, 378 rows affected (4.95 sec)
Rows matched: 338565  Changed: 378  Warnings: 0

mysql> alter ignore table odm_master_keywords add unique keyword(keyword);
Query OK, 338565 rows affected (46.45 sec)
Records: 338565  Duplicates: 233  Warnings: 0

Obratite pažnju na ignore deo u poslednjem alteru. On je ključan ovde, jer da ga nismo koristili ponovo bi došlo do greške(duplicate entry …) prilikom update-a.

Inače, poslednja dva query-a su uobičajen “pattern” kada se želite osloboditi duplikata u nekom polju – jednostavno dodajte unique index na polje i višak će biti obrisan iz tabele.

Stigle knjizice

Da vam malo pravim zazubice :)

MySQL Full-Text Searches

Danas vam predstavljam treci, ujedno i poslednji zapis iz serije tutorijala o “Mogucnostima MySQL-a koje developeri retko koriste”.

U prethodna dva pricali smo transakcijama i referencijalnom integritetu – mogucnostima koje su dostupne samo u InnoDB i BDB tabelama. Danas vam predstavljam “Full-Text Search”, koji je za razliku od gore opisanih feature-a dostupan samo u MyISAM tabelama (što je MySQL-ov default).

Upravo ste kreirali još jedan database driven sajt (Forum, Blog, CMS, šta vec) i došao je red na pretragu. Bez mnogo razmišljanja, dolazite do uobicajenog rešenja:

select * from moja_tabela 
where textpolje like '%text_iz_search_polja%'

koje po svoj prilici završava posao. Malo varijacije na temu ako je potrebno ukljuciti više polja u pretragu, par logickih operatora koje dinamicki generišete u vašem scriptu i to je to? Hmmm ne baš.

(more…)

MySQL::Spoljni Ključevi i Referencijalni Integritet

Danas nastavljamo sa započetom serijom tutorijala o retko korišćenim mogućnostima MySQL-a. U prošlom zapisu detaljno sam predstavio kako se u MySQL-u koriste transakcije. Danas ćemo se usresrediti na korišćenje “Spoljnih Ključeva i Referencijalnog Integriteta”.

Kao i kod transakcija, podrška za referencijalni integritet nije dostupna u standardnim MyISAM tabelama, već se moraju koristiti InnoDB tabele. Malo developera zna da je ova mogućnost dostupna još od verzije 3.23.44, koja se pojavila sada već davne 2001 godine. Za početak idemo malo sa teorijom, a posle ćemo preći na praktične primere.

(more…)

MySQL i Transakcije

Kao što sam u prvom ovogodišnjem zapisu i najavio, od danas krećem sa serijom tutorijala o retko korišćenim mogućnostima MySQL-a kao što su transakcije, referencijalni integritet, fulltext search itd. Developeri ove mogućnosti MySQL-a retko koriste. Mnogi od njih čak i ne znaju da one postoje, pa se uobičajeno, bez puno argumenata, na raznim advocacy raspravama MySQL naziva nekompletnom bazom jer “ne podržava” ništa od gore navedenih mogućnosti. Malo ljudi zna da MySQL recimo podržava transakcije još od verzije 3.23.15 (izašla još maja sada već davne 2000-te godine). Slična je priča i sa ostalim mogućnostima.

Ovaj tekst kao i nastavci koji slede ima za cilj da razbije neke predrasude o MySQL-u kao i da podstakne developere da počnu sa korišćenjem naprednijih mogućnosti koje im njihova baza pruža.

Danas krećemo sa opisom transakcija.

(more…)

MySQL – Prebacivanja tabele iz jedne baze u drugu

Pre neko veče sam morao da prebacim nekoliko tabela iz jedne baze (na istom MySQL serveru) u drugu. Pošto nisam imao vremena da se smaram sa dump-om, posle kraćeg razmišljanja došao sam do sledećeg rešenja:

use prvabaza;
create table foo like drugabaza.foo;
insert into foo select * from drugabaza.foo;

gde je prvabaza baza u koju prebacujemo tabele iz drugebaze. create table like … kreira tabelu identično originalnoj uključujući i indexe, a drugi iskaz kopira podatke iz jedne u drugu. I tako za svaku tabelu posebno (srećom nije ih bilo mnogo).

Danas sam listajući MySQL knjigu došao do znatno jednostavnijeg rešenja :)

alter table drugabaza.foo rename prvabaza.foo;

Jednostavno, brzo, efektno! Nadam se da će nekome ovo koristiti.

PHP 5.1 released

Nakon nekoliko meseci od najave i desetak nezvaničnih “release candidata”, nova verzija PHP-a konačno je ugledala svetlost dana.

PHP 5.1 po nekim testovima donosi značajna unapređenja u perfomansama u poređenju sa prethodnim verzijama. Tu su i već pominjani PDO (PHP Data Object – DB persistant layer) kao i kontroverzne promene u date funkcijama.

Koliko će nova verzija uticati na značajniji proboj “petice” na serverima, ostaje da se vidi. Po onome što sam do sada video, nažalost moram da priznam da sam vrlo skeptičan.

Browser Detection – Revisited

Pre nekoliko nedelja postavio sam na blog jednostavnu klasu koja služi za detekciju browsera. Kao svoj doprinos open source-u, istu sam submitovao na phpclasses.org sajt (pod BSD licencom) i moram da priznam da me je priličo iznenadio njen uspeh.
U prvoj nedelji od postavljanja, klasa se nalazi na trecem mestu po popularnosti, što je moram da priznam bilo iznenađenje za mene. No, ne mogu poreci da me je njen uspeh obradovao, tim pre sto se radi o “projektu” koji je nastao kao rezultat jednog lenjog i dosadnog nedeljnog popodneva :).

Zend Studio 5.0 is out

Kompanija Zend danas je objavila petu verziju Zend Studia, bez premca najboljeg okruženje za razvoj PHP aplikacija, koje će očigledno Zend i PHP još više pribiližiti velikim “Enterprise” korisnicima.

Zend Studio 5 donosi niz poboljšanja od kojih posebno izdvajam podršku za subversion – sve popularniji open source version control software, naslednik CVS-a.Od ostalih novosti tu je podrška za Web servise, SSL FTP kao i takozvani “Case Folding”. Više informacija o novoj verziji možete pronaći ovde.

Browser Detection

I needed a simple PHP class that detect browser and OS based on user agent string. I’ve tried some free available classes on net like PEAR::Net_UserAgent_Detect, but none worked for me. They were either too big or didn’t worked well with most recent browsers (like Opera 8.5 or Netscape 8).

After I spent some time by looking to code that suit my needs, I’ve decided to write my own class which should be working fine with most recent browsers. It recognize most popular browsers (like IE, Firefox, Opera …) as well as it’s version and subversion number (like Opera 8.50, Firefox 1.0.6, etc.). I didn’t care to add support for ancient and no longer used browsers like Netscape 4.x etc.

Here is the class code. You can use it in your projects for free as long as you don’t change header information.

(more…)

Čitanje Excel fajlova iz PHP-a

Verujem da je svako od developera bar jednom u svojoj karijeri imao potrebu da importuje podatke iz nekog Excel fajla. Bez obzira da li se radi o cenovniku ili listi leadova koji morate iz Excela importovati u bazu, postupak je gotovo uvek isti. Prvo što radite je export Excel fajla u običan txt fajl (csv, xml, …) a zatim parsovanje podataka (radi importa u bazu i sl.). Prilično zamoran postupak, pogotovo ako ga morate raditi često, recimo za update cenovnika i sl.

Zahvaljujući besplatnoj phpexcelreader klasi, gore opisan postupak može biti prošlost. Jednostavno, sa phpexcelreader klasom podacima u Excelu možete pristupiti direktno referencijanjem indeksa reda odnosno kolone. Što je najlepše od svega, ova klasa podržava UTF-8 encoding, tako da neće biti problema sa našim slovima. Evo kratkog primera kako to u praksi izgleda.
(more…)

PHP FAQ

Posle otkrića da je elitesecurity forum osetljiv na XSS (cross site scripting) hakeri su očigledno iskoristivši pomenuti propust napali ES i tom prilikom preuzeli privilegije pojedinih super modova tamo. Tom prilikom obrisali su gomilu tema, a nažalost i dosta top tema koje sam svojevremeno kao moderator postavljao na PHP forum.

Srećom, možda najvrednija od svih koje sam tamo pisao (barem za početnike) PHP FAQ je preživela. Da ne bih čekao novu sramotu EliteSecurity-a i dopustio da i ova tema ode u nepovrat skinuo sam ceo tekst i rešio da ga postavim ovde. Verujem da će tekst biti od koristi svima koji počinju da se bave PHP-om.

(more…)

php5 mysqli on windows

Za neupućene, mysqli (skraćeno od MySQL Improved) je nova php ekstenzija dostupna od verzije 5. MySQL Improved donosi novi objektno orijentisan api koji u potpunosti iskorišćava nove mogućnosti MySQL-a 4.1.

Dokumentacija mysql extenzije dostupna je na http://www.php.net/mysqli. Ono što ovoj dokumentaciji nedostaje jeste postupak za instalaciju mysql ekstenzije na Windows-u (u manualu je opisan samo postupak za *nix servere).

Na Windows-u php ekstenzije uobičajeno se instaliraju tako što se “unkomentuje” odgovarajuća linija u “Dynamic Extensions” sekciji fajla php.ini. Problem sa mysqli-om je – linija koja sadrži ime mysqli ektenzije ne postoji. Od mysql ekstenzija tu je samo :

extension=php_mysql.dll

ali to je ekstenzija koja sadrži samo standardne mysql biblioteke.

Brzo “googlanje” i pretraga PHP dokumentacije nisu bili od pomoći. Nakon kopanja po listi extenzija u ext dir-u, shvatio sam da mysqli extenzija ipak postoji ali nekim čudom nisu je stavili u php.ini.

Dakle, za instalaciju mysqli-a treba dodati sledeću liniju u “Dynamic Extensions” sekciju fajla php.ini:

extension=php_mysqli.dll

Napominjem da (barem na Windowsu) mysqli može raditi zajedno sa starim mysql bibliotekama, tj. da bi uključili mysqli nije neophodno da isključite mysql extenziju. Ovo je zgodno ako imate neke druge projekte na lokalnom serveru koji koriste stare biblioteke, a želite da koristite i nove (recimo u novim projektima). Na linuxu se isto preporučuje samo ako se koristtite iste biblioteke (one koje dolaze uz sam mysql).

Nakon izmene php.ini fajla, restartujte vaš Web server i uživajte u mogućnostima nove mysql biblioteke.

Next Page »