Most, hogy már van egy virtuális szerverünk, kellene kezdeni vele valami mókás dolgot. Lehetne mondjuk egy távolról is vezérelhető build rendszerünk (a "távolról" mondjuk ebben az esetben talán egy kicsit túlzás :)), amit a kedvenc szövegszerkesztőnk automatikusan tudna vezérelni. Az úgy kellőképp mókásan hangzik ahhoz, hogy belevágjunk. :)

Ahhoz, hogy legyen értelme a build rendszernek, nem árt, ha van mit build-elni. Tegyük fel mondjuk a példa kedvéért, hogy CSS helyett szeretnénk Sass-t, JavaScript helyett pedig CoffeeScript-et használni. Természetesen a rendszer képességei nem merülnek ki ennyiben, lehetne mondjuk olyat is csinálni, hogy a sablon fájlokat fordítsa le PHP-ra, vagy a konfigurációs XML-ekből gyártson tömböket, esetleg minden unit tesztet lefuttasson. A lehetőségek száma, ahogy mondani szokás, végtelen, de maradjunk az eredeti kettőnél, a többi a kedves olvasó fantáziájára van bízva. Kezdjük is rögtön a telepítéssel:

Sass

sudo apt-get install ruby rubygems
sudo gem install sass

A Sass [Syntactically Awesome Stylesheets] nevezetű előfeldolgozó néhány hasznos kiegészítéssel segít ráncba szedni az elvadult CSS kóddzsungelt.

CoffeeScript

sudo apt-get install nodejs npm
sudo npm install coffee-script

A CoffeeScript a funkcionális programozási nyelvekre és talán egy csöppet a Ruby-ra emlékeztető nyelv a JavaScript egy komolyabb újragondolása (a Sass-CSS viszonylathoz képest), de mentségére legyen mondva, hogy egy az egyben valid JavaScript-re fordul.

Építkezzünk

A build-elésben segítségünkre lesz a vicces nevű PHP-s build eszköz, a Phing. A következő módon érhetjük el, hogy feltelepüljön:

sudo apt-get install php-pear
sudo pear channel-discover pear.phing.info
sudo pear install phing/phing

A példához a következő minimális projekt könyvtárszerkezetet fogjuk használni:

  • assets/
    • coffee/
    • sass/
  • www/
    • css/
    • js/
  • build.xml

Az assets/ könyvtárban vannak a nyers fájlok, a www/ könytárba mennek a generált fájlok, amiket nem árt kívülről is elérni. A kulcs pedig a build.xml, ami a Phing számára tartalmaz utasításokat. Kezdetnek valami ilyesmi például megteszi:

<?xml version="1.0" encoding="UTF-8"?>
<project name="MyProject" default="assets">
    <property name="sass_dir" value="${project.basedir}/assets/sass/" />
    <property name="coffee_dir" value="${project.basedir}/assets/coffee/" />
    
    <property name="css_target" value="${project.basedir}/www/css/" />
    <property name="js_target" value="${project.basedir}/www/js/" />
    
    <target name="sass">
        <exec command="sass --no-cache --update &quot;${sass_dir}:${css_target}&quot;" />
    </target>
    
    <target name="coffee">
        <exec command="coffee -o &quot;${js_target}&quot; &quot;${coffee_dir}&quot;" />
    </target>
    
    <target name="assets">
        <phingcall target="sass" />
        <phingcall target="coffee" />
    </target>
</project>

A projekt könyvtárában (egy szinten a build.xml-lel) állva ki tudjuk adni a következő három parancsot, amivel a Sass-t, CoffeeScript-et és mindkettőt egyszerre lefordíthatjuk (ha nem adunk meg semmit a phing után, akkor a default assets fog lefutni):

phing sass
phing coffee
phing assets

Az alkalmazás

Már csak azt kellene elérni, hogy a fájlok módosítása után a megfelelő parancs automatikusan lefusson. Bár mind a Sass, mind a CoffeeScript rendelkezik megoldással a problémára (--watch), a későbbiekre is gondolva inkább valami általánosabb megoldásra lenne szükség. Mondjuk egy webes minialkalmazás, ami a kapott paraméterek függvényében meghívja a megfelelő parancsot. A kódszerkesztő alkalmazásunk pedig ezt a paraméterezett URL-t hívogathatná. Mondjuk valami ilyesmit:

http://builder.vbox/index.php?project=projektneve&target=target

Az a bizonyos index.php pedig valami ilyen kis egyszerű dolog lenne (az egyszerűség ára az, hogy élünk azzal a feltételezéssel, hogy az egyes projektek a /var/www/projektneve.vbox/ könyvtárban vannak és eltekintünk az olyan dolgoktól, mint például a target valódiságának az ellenőrzése):

$dir = '/var/www/' . get('project') . '.vbox/';
if (!file_exists($dir . 'build.xml')) {
    exit('invalid_project');
}

$target = get('target');
if (!$target) {
    exit('no_target');
}

chdir($dir);
shell_exec('phing ' . escapeshellcmd($target));
exit('ok');

function get($name) {
    if (empty($_GET[$name])) {
        return '';
    }
    return preg_replace('/[^a-z0-9\-\.]/i', '', $_GET[$name]);
}

A plugin

Ezzel meg is van a távolról is vezérelhető egyszerű kis build rendszerünk. A kedvenc szerkesztőnkhöz írhatunk egy plugin-t, ami mentésre meghívja a megfelelő URL-t, hogy legenerálódjanak a CSS/JS fájlok (vagy akár tetszőleges billentyűkombinációra tetszőleges build parancsot meghívhatunk). Sublime Text 2 esetén valahogy így nézne ki a dolog:

import sublime, sublime_plugin
import urllib, urllib2
import os, threading

class BuilderThread(threading.Thread):
    def __init__(self, project, target):
        threading.Thread.__init__(self)
        self.project = project
        self.target = target
    def run(self):
        try:
            data = urllib.urlencode({
                'project': self.project,
                'target': self.target,
            })
            request = urllib2.Request('http://builder.vbox/index.php?' + data)
            response = urllib2.urlopen(request).read()
            return
        except (urllib2.HTTPError) as (e):
            err = '%s: HTTP error %s contacting API' % (__name__, str(e.code))
        except (urllib2.URLError) as (e):
            err = '%s: URL error %s contacting API' % (__name__, str(e.reason)) 
        
        sublime.error_message(err)

class BuilderEventListener(sublime_plugin.EventListener):
    def __init__(self):
        sublime_plugin.EventListener.__init__(self)
    
    def on_post_save(self, view):
        name, ext = os.path.splitext(view.file_name())
        project = view.settings().get('builder_project_name', False)
        
        if (False == project):
            return
        
        if (ext == '.coffee'):
            BuilderThread(project, 'coffee').start()
        elif (ext == '.scss' or ext == '.sass'):
            BuilderThread(project, 'sass').start()

A plugin használ egy builder_project_name nevű beállítást, amit az éppen aktuális projekt .sublime-project fájljában érdemes megadni:

{
    "settings": {
        "builder_project_name": "test-project"
    }
}

Ezzel meg is volnánk, kellemes építkezést.

-----

Épp a sorozatos oldal második verziójának fejlesztésébe készülök komolyabban belevágni. Ennek kapcsán eszembe jutott, hogy milyen jó bejegyzést lehetne ebből kanyarítani. Mármint abból, hogy milyen módjai vannak a kényelmes otthoni projekt fejlesztésnek.

Összepiszkíthatnánk például a jelenlegi operációs rendszerünket mindenféle web- és egyéb szerverekkel, de ez nem egy szép megoldás. Telepíthetnénk egy extra operációs rendszert egy külön partícióra, de az, hogy a jelenlegiből ki kell lépni, oda pedig be... esélytelenné teszi a fejlesztést egy kellően lusta ember számára (én :)). A legjobb lenne egy külön gép, amit hálózaton keresztül elérünk. Ennek a legköltséghatékonyabb változata a virtuális gép, így ezen a vonalon fogunk tovább haladni. Hozzávalók egy személyre:

Pro tipp: az Ubuntu szerver iso-ját ne a VirtualBox telepítése közben töltsük le, mert a VirtualBox telepítés közben hajlamos időlegesen megkavarni az internet kapcsolatot és jól meg fog szakadni a letöltés.

Telepítés

A letöltések és a szokásos next-next-finish telepítés befejeztével hozzáadhatunk egy új Linux / Ubuntu típusú virtuális gépet és egy új virtuális merevlemezt a varázsló segítségével. Még mielőtt elindítanánk, a Settings / Shared Folders részben érdemes hozzáadni egy könyvtárat a saját gépünkről, amiben a projekteket fogjuk tárolni, hogy ne kelljen később Samba-t telepíteni. Az Auto-mount részt ne pipáljuk be, azt később kézzel beállítjuk majd. A Settings / Network részbe se árthat benézni és az Adapter 1-et ízlés szerint megpiszkálgatni. Nálam a Bridged Adapter volt a nyerő az Attached to részben. A VirtualBox elég okos és az első indításnál felajánlja, hogy etessünk meg a géppel egy CD-t. Ajánlatos itt beadagolni a korábban letöltött Ubuntu szerver iso-ját, mert az nagyban megkönnyíti a telepítést. :)

Az Ubuntu nem egy bonyolult jószág, így valószínűleg a telepítéssel se lesznek problémák. Amíg fut, tetszés szerint lehet fogyasztani a bespájzolt hideg élelmet illetve sört. Hogy, hogy nem előbb-utóbb eljutunk egy ilyen képernyőhöz:

Itt a LAMP server-t az Apache-PHP-MySQL szentháromság miatt erősen ajánlott bepipálni, mert valószínűleg szükségünk lesz rá. Én telepíteni szoktam OpenSSH server-t is, mert már annyira hozzászoktam a PuTTy használatához. :)
Ha ezzel megvagyunk, az első belépés után rögtön érdemes is nyomni egy frissítést:

sudo apt-get update
sudo apt-get upgrade

Valamint telepíteni a VirtualBox Guest csomagját a jobb támogatás érdekében:

sudo apt-get install virtualbox-guest-utils

A projekt könyvtárak beállítása

A fejlesztés a /var/www/ könyvtárban fog történni a virtuális gép nézőpontjából. Hogy kényelmes legyen, érdemes ezt kivezetni valahova a gazda gép merevlemezére. Ezért állítottuk be korábban a Shared Folders részt. A művelet elvégzéséhez a következő sorokat kell felvenni az /etc/fstab fájl végére (na jó, a komment nem kötelező :)):

# VirtualBox Shared Folder
projects /var/www vboxsf auto,rw,uid=1000,gid=33 0 0

A projects helyére a Shared Folder felvitelénél megadott nevet kell írni. Az uid=1000 a rendszer telepítése során létrehozott felhasználó, a gid=33 a www-data csoport. A módszernek az az előnye is megvan a Samba-féle megoldással szemben, hogy nem kell futnia a virtuális gépnek ahhoz, hogy a fájlokat módosítani tudjuk. Az uid és gid értékeként megadható számok után az /etc/passwd és /etc/group fájlokban lehet érdeklődni.

Szerver elérés

Ha minden jól ment, az ifconfig eth0 által kidobott ip címen el tudjuk érni a gépet. Ha nem, akkor a VirtualBox beállítások Network részénél érdemes a lehetőségeket végigpróbálgatni, hogy melyik jön be (ahány hálózat, annyi beállítás).
Az egyszerűséget szem előtt tartva érdemes valami egyedi, egyébként nem létező domain-el felruházni a gépet, hogy ne ip címeket kelljen mindig írogatni. Nálam a *.vbox domain-ek vannak erre fenntartva. A legegyszerűbb, ha a hosts fájlba felvesszük őket (Windows: c:\Windows\System32\drivers\etc\hosts, Linux: /etc/hosts), pl.:

192.168.1.117 default.vbox
192.168.1.117 projektneve.vbox
192.168.1.117 subdomain.projektneve.vbox

Apache, PHP, MySQL beállítás

Az egyes projektek a /var/www/projektneve.vbox/ könyvtárban kapnak helyet. Az alap Apache confignak csinálunk egy default.vbox nevet/könyvtárat:

mkdir /var/www/default.vbox
sudo mcedit /etc/apache2/sites-available/default

A megnyíló fájlban a DocmentRoot és a Directory résznél szereplő /var/www/ után kell még egy default.vbox/-ot írni. Aztán pedig

sudo service apache2 reload

A /etc/apache2/httpd.conf-ba esetleg még érdemes egy ServerName default.vbox sort beledobni és újraindítani az Apache-ot, hogy megszűnjön a "Could not reliably determine the server's fully qualified domain name" hibaüzenet.

A projekt VirtualHost beállításai a /etc/apache2/sites-available/projektneve.conf fájlba kerülnek. Egy egyszerű példa, hogy nagyjából mit is tartalmaz egy ilyen:

<VirtualHost *:80>
    ServerName projektneve.vbox
    ServerAlias www.projektneve.vbox

    DocumentRoot /var/www/projektneve.vbox/www
    <Directory />
        Options FollowSymLinks
        AllowOverride All
    </Directory>
    <Directory /var/www/projektneve.vbox/www/>
        Options FollowSymLinks
        AllowOverride All
    </Directory>

    ErrorLog /var/www/projektneve.vbox/log/apache_www_error.log
    CustomLog /var/www/projektneve.vbox/log/apache_www_access.log combined

    LogLevel warn
</VirtualHost>

A kész site beállításokat a következő parancsokkal tudjuk engedélyezni:

sudo a2ensite projektneve.conf
sudo service apache2 reload

A MySQL-en nincs sok beállítani való, telepítéskor elkérte a root felhasználójához az új jelszót, mást meg nem nagyon kell piszkálni. A vizuálisabb típusoknak esetleg egy phpmyadmin telepítése még nem árthat:

sudo apt-get install phpmyadmin

Ha kértük, a csomagból telepítés során be is köti magát az Apache-ba a /etc/apache2/conf.d/phpmyadmin.conf segítségével és a http://ipcím/phpmyadmin/ címen el is érhető az oldal. Ehelyett, ha kedvünk tartja létrehozhatunk neki egy saját VirtualHost-ot is akár.

A PHP beállításánál érdemes arra törekedni, hogy minél jobban hasonlítson az éles rendszerre, így ezzel kapcsolatban nem sokat tudok mondani.

Első körben ennyi, kellemes telepítgetést. :)

-----

Szóval van ez a Google+. Mindenki nagy örömére, most már lehet rajta Facebook Page-eket Google+ Page-eket is csinálni. És most már van a deadlime-nak is egy.

Jelenleg még üres, de a terv az, hogy leváltja az oldalsávban a "Linkek" rész alatt használt Google Reader-ből jövő megosztás listát és ezentúl ott lesznek megosztva az érdekesnek tűnő linkek.

Szóval lájkoljatok plusszoljatok! :)

+++++

Érdekes módon a deadlime kedvenc kódszerkesztő alkalmazása a Sublime. Egyrészt azért, mert a többszörös kijelölés az bizony hihetetlenül fantasztikus egy találmány. Másrészt pedig Python-ban lehet hozzá plugin-eket írni.

Kedves kollégám említette a minap, hogy egyik régebben használt szerkesztőjében volt valamiféle funkció, amivel a vágólap korábbi tartalmait lehetett újra felhasználni. Aztán elkezdtem gondolkodni rajta, hogy vajon lehetne-e egy ilyen plugin-t írni Sublime-hoz. Aztán megírtam. :)

Ha már így alakult, ragadjuk meg az alkalmat és nézzük meg kicsit, hogyan is néz ki egy Sublime plugin. A funkció meglepően kevés kódból megoldható: először is felül kell csapnunk a beépített copy és cut utasításokat sajáttal úgy, hogy megmaradjon az eredeti funkcionalitás is, de közbe mentsük egy tömbbe az adatokat.

class ClipboardHistoryCopyCommand(sublimeplugin.TextCommand):
    def run(self, view, args):
        saveSelections(view)
        view.runCommand('copy');

Egy kis XML-lel meg is mondjuk a proginak, hogy a mi parancsunkat használja a Ctrl+C nyomkodása esetén:

<bindings>
    <binding key="ctrl+c" command="clipboardHistoryCopy" />
</bindings>

A cut nem túl meglepő módon elég hasonlóan néz ki (csak a beépített cut parancsot hívjuk meg a végén). A saveSelections végzi a munka javát. Végigmegy az összes kijelölésen és amelyik nem üres, azt elpakolja egy tömbbe, aminél még arra is figyelünk, hogy ne lehessen túl nagy:

clipboardMaxSize = 100
clipboardData = []

def saveSelections(view):
    for region in view.sel():
        if region.begin() != region.end():
            clipboardData.insert(0, view.substr(region))
            if len(clipboardData) > clipboardMaxSize:
                clipboardData.pop()

Annyi maradt már csak, hogy valami kellően kézreálló billentyűkombinációra meg is jelenítse az előzményeket és tudjunk belőle kedvünkre válogatni. Legnagyobb szerencsénkre tudjuk használni a beépített lista ablakot, amiben még kereső is van (ugyanaz, ami a Ctrl+P vagy a Ctrl+R kombinációkra is bejön):

class ClipboardHistoryPasteCommand(sublimeplugin.TextCommand):
    def pasteSelected(self, view, index):
        for region in view.sel():
            view.insert(region.begin(), clipboardData[index])
    
    def run(self, view, args):
        view.window().showSelectPanel(
            clipboardData,
            functools.partial(self.pasteSelected, view),
            None,
            sublime.SELECT_PANEL_MONOSPACE_FONT
        );

És természetesen a hozzá tartozó kis XML részlet:

<binding key="alt+shift+q" command="clipboardHistoryPaste" />

Nem a legtökéletesebb megoldás, például plugin újratöltéseknél elveszik a clipboardData tömb tartalma (lehetne például fájlba menteni menet közben), de kezdetnek megteszi. A teljes csomagot innen le lehet rántani (érdekes módon ClipboardHistory.sublime-package fájlnévvel nem mentek volna a billentyűparancs felüldefiniálások, mivel az Sublime ABC sorrendben tölti be a plugineket és a Default később van... :)).

[a képet köszönjük ShutterBugChef-nek]

+++++

Bár nem tegnap történt, hogy az 5.3-as sorozat első verziója kijött, gondoltam kicsit körbejárom a névtelen függvények témakörét az ősidőktől napjainking. Az ősidő alatt itt természetesen a 4-es verziót értem, amikor még csak a call_user_func állt a rendelkezésünkre. Lássuk, hogyan is ment ez.

A create_function módszer (attól tekintsünk el, hogy a __FUNCTION__ varázskonstans csak a 4.3-as verzióban jelent meg):

$f = create_function('', 'echo __FUNCTION__ . "\n";');
call_user_func($f);

A függvény neve stringként módszer:

$f = 'myFunction';
call_user_func($f);

Az osztály és a statikus metódus neve tömbként:

$f = array('MyClass', 'myStaticFunction');
call_user_func($f);

Az objektum és a metódus neve tömbként:

$c = new MyClass();
$f = array($c, 'myFunction');
call_user_func($f);

Az osztály és a statikus metódus neve stringként (5.2.3-as PHP-tól):

$f = 'MyClass::myStaticFunction';
call_user_func($f);

Az objektum meghívása (5.3.0-s PHP-tól):

$c = new MyClass();
call_user_func($c);

És ezzel el is érkeztünk a jelenhez. Az 5.3.0-s verzióval megjelentek a closure-ök (az újfajta névtelen függvények), amolyan JavaScript-es definiálással:

$f = function () {
};

Természetesen a teljes visszafelé kompatibilitásról ne is álmodjunk ezért kezdjük is el végignézni az előző módokat, hogy mi működik továbbra is:

A create_function módszer megy továbbra is (emlékeim szerint 5.3 előtt is működött így, ezért ez nem túl meglepő):

$f = create_function('', 'echo __FUNCTION__ . "\n";');
$f();

A függvény neve stringként módszer (működik):

$f = 'myFunction';
$f();

Itt sajnos meg is állhatunk. A tömbös rendszerek (PHP Fatal error: Function name must be a string) és az osztály+statikus metódus neve stringként (PHP Fatal error: Call to undefined function MyClass::myStaticFunction()) nem működik.

Maradt még az objektum direkt meghívása, ami működik, de ez sem túl nagy meglepetés, mivel az 5.3.0-s verzióbal vezették be hozzá az új varázsmetódust:

$c = new MyClass();
$c();

Ilyenkor (és a call_user_func-os esetben is) az osztály __invoke() metódusa hívódik meg.

Ezzel azt hiszem nagyjából ki is veséztük a különböző callback típusok és névtelen függvények hívhatóságát. Maradt még némi scope kérdés, de az házi feladat (annyit megsúgok, hogy a use a kulcsszó). Egy kis segítség hozzá:

+++++

Az úgy volt, hogy elgondolkoztam azon, hogy vajon miért jó az, ha a kiszolgálás folyamatát több részre bontják. Ha egyáltalán jó az. Offline, bolti kiszolgálásról van szó, mielőtt bárki - a blog témáját figyelembe véve - jogosan másra gondolna.

Ha vesszük az értékesítő-pénztáros-árukiadó modellt, érzésre úgy tűnik, hogy a több sor végigjárása miatt többet is várunk. De a sorbanállás pszichológiája fura egy dolog. Jó példa erre a hipermarket, ahol N pénztárnak van N sora azért, mert az N pénztár 1 sor felállás zavaró lehet az embereknek a hosszú sor miatt, holott a várakozási idő úgy kevesebb lenne.

(tovább...)
++++-

Úgy tűnik tartom magamat az elmúlt években már megszokott kontent mennyiséghez. Most is csak azért írok, hogy hírt adjak arról, hogy összedobtam egy kis oldalt, aminek segítségével nyomon lehet követni ismerőseink és saját magunk sorozatnézési szokásait. Használjátok egészséggel. :)

Ja... a cím: s01e01.hu

+++++

Már lassan egy éve nem volt új bejegyzés, gondoltam ideje írni valamit. :) Régebben már volt szó a PHP objektum-orientált adatbázis elérési felületéről, a PDO-ról. Mivel utóbb inkább csak a használatára tértem ki, nem lett megemlítve, hogy ezekből az osztályokból származtathatjuk saját osztályainkat is, ami alapvetően egy jó dolog tud lenni.

Vegyünk is egy egyszerű példát. Tegyük fel azt, hogy már annyira jók vagyunk, hogy az SQL szerverrel is csak l33t nyelven vagyunk hajlandóak kommunikálni. Persze a mysqld (vagy egyéb tetszőleges daemon) nem ilyen megértő, és folyamatosan panaszkodik, hogy nem ért meg minket. A probléma elsimítása érdekében származtatunk a beépített PDO osztályból egy sajátot:

class L33tPDO extends PDO {
	private static $from = array(
		's3l3ct', 'fr0m', 'wh3r3', 's3t', '1ns3rt', '1nt0', 'upd4t3', '0rd3r', '4sc', 'd3sc', 'l3ft', 'r1ght', '1nn3r', 'j01n'
	);
	private static $to = array(
		'SELECT', 'FROM', 'WHERE', 'SET', 'INSERT', 'INTO', 'UPDATE', 'ORDER', 'ASC', 'DESC', 'LEFT', 'RIGHT', 'INNER', 'JOIN'
	);
	public function query($statement, $type = false, $param1 = false, $param2 = false) {
		$statement = $this->convertStatement($statement);
		
		if ($type && $param1 && $param2) {
			return parent::query($statement, $type, $param1, $param2);
		}
		if ($type && $param1) {
			return parent::query($statement, $type, $param1);
		}
		if ($type) {
			return parent::query($statement, $type);
		}
		
		return parent::query($statement);
	}
	public function prepare($statement, $driver_options = array()) {
		$statement = $this->convertStatement($statement);
		parent::prepare($statement, $driver_options);
	}
	private function convertStatement($statement) {
		return str_ireplace(self::$from, self::$to, $statement);
	}
}

Hogy takarékoskodjunk a hellyel, a kulcsszavak listája nem teljes. Nézzünk is egy példát a használatra:

$conn = new L33tPDO('pgsql:host=localhost port=5432 dbname=test user=test password=test');

$stmt = $conn->query("s3l3ct * fr0m users wh3r3 id > 1 0rd3r by login_name 4sc" );

var_dump($stmt->fetchAll());

Bár a felhozott példa nem igényli, a PDOStatement osztályból is származtathatunk sajátot, csak meg kell mondanunk a saját PDO osztályunknak, hogy az általunk írt PDOStatement-tet adja vissza a megfelelő függvényei. Ehhez bővítsük ki a L33tPDO osztályunkat egy konstruktorral:

function __construct($dsn, $username = null, $password = null, $driver_options = array()) {
	parent::__construct($dsn, $username, $password, $driver_options);
	
	$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('L33tPDOStatement', array($this)));
}

És egy hozzá passzoló statement osztály:

class L33tPDOStatement extends PDOStatement {
	private $dbh;
	protected function __construct($dbh) {
		$this -> dbh = $dbh;
	}
}

Ennyit mára. További kellemes kódolást.

+++--

Talán nem túlzás azt állítani, hogy a Firebug a legjobb dolgok közé tartozik, ami manapság egy JavaScript fejlesztővel történhetnek. A több mint 10 millió letöltés, valamint a tény, hogy - bár maga is kiegészítő - kiegészítőket fejlesztenek hozzá elég bizonyítékul is szolgálhat ennek alátámasztására. Egy ilyen kiegészítőbe futottam bele még valamikor a múlt héten, ami a FirePHP nevet viseli (a nevével ellentétben bármely más szerver oldali nyelvvel használható, ami képes header-öket kiküldeni). Nincs is másra szükségünk csak egy Firefox-ra (ez remélhetőleg már mindenkinek van :)), egy Firebug-ra (ez is esélyes lehet, hogy akad a közelben) és a FirePHP-ra. Meg egy kis szerveroldali scriptre, ami szintén a FirePHP oldaláról tölthető le.

FirePHP

A kiterjesztés mögött rejlő ötlet rettentően egyszerű (és talán éppen ezért olyan fantasztikus): küldjünk ki pár speciális fejlécet valamiféle JSON tartalommal, amit kliens oldalon a kiegészítő feldolgoz és a Firebug segítségével szépen meg is jelenít. Ezek lehetnek több prioritásba sorolható egyszerű szöveges üzenetek (hasonlóak a Firebug beépített üzeneteihez), táblázatok, szépen formázott backtrace-ek vagy akár print_r()-hez hasonló információk változókról.

(tovább...)
++++-

A szokásos "napi" php.net böngészgetésem során bukkantam rá a lentebb olvasható kódrészletre (bár nem egy bonyolult dolog, de mégsem jutott volna magamtól eszembe), amit szerintem kötelezővé kellene tenni minden egyes PHP alapú alkalmazás fejlesztési időszaka alatt. A kód nem csinál mást, mint hogy minden hibát kivétellé alakít át, így ha nincsenek elkapva, akkor egy egyszerű E_NOTICE-tól is lehal az oldal fatal error-ral, az error_reporting beállítástól függetlenül. :)

function error_handler($errno, $errstr, $errfile, $errline)
{
    throw new ErrorException(
        $errstr, 0, $errno,
        $errfile, $errline
    );
}
set_error_handler('error_handler');

Egyéni ízlés kérdése, de én még az alábbi kóddal kiegészíteném, hogy fatal error-ok helyett inkább valami olvasható dolgot kapjak:

function exception_handler($ex)
{
	print('<pre>'.$ex.'</pre>');
}
set_exception_handler('exception_handler');

És hogy miért is lenne hasznos? Elég sokan kikapcsolják az E_NOTICE-ok jelzését még fejlesztés alatt is, mondván hogy az nem számít. Pedig elég sok kisebb hibára (pl. változó név vagy tömb kulcs elgépelésekre) hívhatná fel a figyelmet, amik így csak a tesztelési időszakban derülnek ki (ha kiderülnek). Emellett biztonsági haszna is van, mint az a php.net-en is olvasható.

+++++