Najjednoduchší MVC framework v PHP (part #1)

23.07.2008 19:29 | blackhole

V tomto článku vám ukážem, ako si naprogramovať naozaj veľmi jednoduchý framework pre webové aplikácie v jazyku PHP. Medzi výhody frameworku patrí okrem iného, naozaj tiny veľkosť (samotné jadro je v šiestich malých .php súboroch), jednoduchá rozšíriteľnosť (pomocou vlastných tried), OOP a MVC prístup a jeho črevám porozumie aj úplný programátorský analfabet.

Prečo zase ďalší framework?
Pre nájdenie odpovede si musíme položiť otázku: „na čo je tento framework vhodný“. Predstavte si situáciu, že budete známym požiadaný o napísanie jednoduchej ankety alebo jednoduchej jednoúčelovej galérie. Mnoho začínajúcich PHP-čkarov siahne po metóde index.php ==> include($_GET), niektorí sa možno odvážia použiť aj nejaký šablónový systém. Stále však treba vymyslieť kód, ktorý bude generovať stránky podľa požiadaviek od klienta. Tie sú zväčša deklarované ako parametre v URL. Ak budete chcieť implementovať coolURI, pribudne ďalší kód. A čo takto podpora multi-language?

Áno - toto všetko framework rieši elegantným, krátkym kódom. Spolu s objektovým a MVC prístupom je rýchlosť dokončenia a manažovateľnosť aplikácie ďaleko vpredu pred hore spomenutou include() metódou. Jediné čo budete potrebovať naprogramovať, sú Controllery a Modely, čo sú .php súbory s typicky jednou triedou, určené na ovládanie aplikácie a dolovanie údajov.

Aké sú teda hlavné vlastnosti frameworku, ktorý som nazval príznačne sMVCfw (simplest MVC framework):
1. MVC prístup (vzhľad aplikácie je možné nechať na pleciach HTML kódera)
2. plne objektový kód
3. podpora coolURI
4. podora multijazyčnosti pomocou URL v tvare http://www.example.com/[jazyk]/ (+samodetekcia jazyka podľa UA)
5. vstavaný šablónovací systém (knižnica bTemplate - podporuje napr. loopy nad poliami alebo podmienky IF)
6. jednoduchá rozšíriteľnosť a modularita
7. jednoduchý je aj DEBUG, nakoľko je všetko pekne oddelené v samostatných súboroch.
8. je použiteľný aj na väčšie veci ako je blog alebo webový časopis.

K bodom 4 a 6 by som chcel niečo napísať.
- Na jednom známom web dizajnérskom fóre často vidím borcov, čo si informáciu o jazyku ukladajú do SESSION alebo COOKIE. Už som aj pár krát dohováral, že to nie je vhodné z hľadiska SEO. Môže sa totiž stať, že sa pod jednou URL bude skrývať niekoľko verzií stránky. Vďaka tomu je vyhľadávačmi indexovaná vždy iba jedna jazyková mutácia. Okrem toho sa duplicitný obsah na jednej URL u vyhľadávačov penalizuje. Informáciu o jazyku zásadne prenášajte v URL!

- Rozšíriteľnosť a modularita - nakoľko je kód umiestnený v súboroch s triedami, nie je problém jednoducho pridávať nové funkcie (aj cudzie, viď portál PHPclasses.) alebo napr. komplet vymeniť databázový layer za iný. Neskôr si ukážeme ako s pomocou tohto frameworku zostaviť jednoduché CMS-ko.

Na začiatku je vhodné upozorniť na licenčné podmienky. Zdrojové kódy, ktoré uverejním k stiahnutiu v druhej časti seriálu budú pod licenciou MIT. Táto licencia vám umožňuje využiť framework aj pre komerčné účely bez nutnosti otvoriť váš vlastný kód. Pod rovnakou licenciou je aj knižnica bTemplate.

Framework v akcii
Pred začiatkom práce je vhodné napísať, že celá aplikácia má jeden vstupný bod: súbor index.php. To je veľmi výhodné - predtým ako predáte kontrolu nad behom aplikácie do controllera, môžete vykonať rôzne iné operácie, napr. zalogovať si návštevu do databázy, alebo sledovať pohyb návštevníka po webe. Nech už si zobrazí akúkoľvek stránku, vždy pôjde jeho požiadavka najprv cez index.php.

Pripravme si pracovné prostredie. Odporúčam vytvoriť si nový virtuálny server, celú aplikáciu však môžete umiestniť aj do podadresára na webserveri. Ja som pre tutoriál vybral prvú možnosť - aplikácia priamo v koreni www_root.
Spustite teda virtuálny server, napr. s adresou http://smvcfw/. V adresári www_root si vytvorte adresáre (dodržujte veľkosť písmen!):

/www_root
  /controllers
  /lib
    /Controllers
    /Models
  /public
    /images
    /javascript
    /styles
  /templates
    /ErrorMessages
    /Subparts

Vďaka tejto schéme je možné framework nasadiť na každom hostingu. Existujú totiž frameworky, ktoré vyžadujú umiestniť svoje libky a ostatný kód mimo www_root. Našťastie to nie je tento prípad. Keďže sú adresáre s programovým kódom vo verejne prístupnej zložke, je treba adresáre ochrániť pred nepovoleným prístupom. Vytvorte súbory .htaccess s dole uvedeným kódom a umiestnite ich do adresárov controllers, lib a templates.

Deny From All

Do adresára public zase umiestnite súbor .htaccess s obsahom:

RewriteEngine Off
Options Indexes

Umožníte tak svojím návštevníkom sťahovať si verejne dostupné súbory (CSS štýlopisy, obrázky layoutu stránky). Akékoľvek súbory v adresári public budú priamo dostupné pomocou URL http://smvcfw/public/cesta/k/suboru

Posledný .htaccess fajl príde do koreňa www_root. Jeho obsah:

php_flag magic_quotes_gpc off
php_flag register_globals off
Options -Indexes
RewriteEngine on
# route all requests to index.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?route=$1 [L,QSA]

V tomto súbore trocha dotiahneme konfiguráciu PHP (vypneme nebezpečné a nepohodlné veci), zakážeme prechádzanie adresárov vo www_root (prechádzanie je ale povolené v adresári public, viď kód vyššie) a nastavíme konfiguráciu mod_rewrite.

mod_rewrite je nastavené tak, že najskôr hľadá súbor alebo adresár v menom podľa príchodzej URL. Ak napr. príde požiadavka s URL http://smvcfw/admin/ začne sa hľadať adresár alebo súbor s menom admin v koreni www_root, aby mu bola požiadavka predaná k spracovaniu.

Ak sa takáto položka nenájde, je všetko za TLD (najpravešia časť doménového mena) priradené ako hodnota premennej $_GET['route']. V našom malom príklade sa teda spracovanie predá do index.php a do $_GET['route'] sa zapíše string "admin/". Z toho vyplýva, že framework je možné prevádzkovať aj bez mod_rewrite, stačí aby aplikácia dodržiavala určitú schému pri generovaní <a href> linkov (konkrétne teda, linky musia mať formu index.php?route="CONTROLLER/ACTION/PARAMETER0/PARAMETER1/...").

V poslednej vete som naznačil ako framework spracuje požiadavku v závislosti od URL. Opäť použime modelovú situáciu s požiadavkou na stránku http://smvcfw/admin/.

Webový server najskôr hľadá súbor alebo adresár s menom admin, tak ako som to spomínal vyššie. V prípade neúspechu, je spracovanie predané súboru index.php. Ten sa spolu s ostatným kódom bude snažiť nájsť súbor s presne špecifikovaným menom, v tomto prípade AdminController.php. Ako je zrejmé, súbor sa bude hľadať v adresári controllers. V prípade úspechu sa súbor "natiahne" a riadiaci kód sa pokúsi v súbore nájsť triedu AdminController(). Ak sa kód triedy v súbore nachádza, zavolá sa metóda index(). Vo všetkých prípadoch, keď riadiaci kód nenájde požadované položky, vygeneruje sa jednoduchá ERROR404 stránka (riadenie sa predá na ErrorConroller).

Do celého procesu ešte vstupuje detekcia jazyka. Majme napr. takúto URL:
http://smvcfw/en/news/archive/

Riadiaci kód deteguje požiadavku, ktorú by mala obslúžiť trieda NewsController(). Nebude sa však volať metóda index() ako v predchádzajúcom prípade, ale na základe URL adresy sa zavolá metóda archive(). Zároveň si aplikácia uloží na globálne dostupné miesto požadovaný jazyk (uloží sa string hodnota "en"). Povolené sú len dvojznakové identifikátory jazyka (takže žiadne eng alebo ger ale pekne en resp. de).

Rozšírme predchádzajúci príklad takto:
http://smvcfw/en/news/archive/2008/june

Už vieme, že požiadavku bude obsluhovať trieda NewsController() a jej metóda archive(). Vieme, že sa niekde uloží hodnota s identifikátorom jazyka. Zvyšné dve položky z URL (2008/june) sa uložia na to isté globálne miesto, kde leží identifikátor jazyka ako hodnota array ([0] => '2008' [1] => 'june').

A keď sa dobre zamyslíme, zistíme, že máme všetky potrebné údaje a premenné, potrebné na vygenerovanie stránky. Zapojíme do práce naše triedy MODEL, vydolujeme z databázy potrebné údaje a pomocou VIEW ich vyrenderujeme do prehliadača návštevníka. Ideálna spolupráca jednotlivých komponentov MVC.

Píšeme kód
Poďme sa pustiť do písania kódu, ktorý toto všetko zabezpečuje. Začneme súborom index.php v koreni www_root. Zapíšte tento kód

index.php

<?php
error_reporting(E_ALL);
ini_set('display_errors', 'on');
date_default_timezone_set('Europe/Bratislava');
define('DS', DIRECTORY_SEPARATOR);
define('APPROOT', realpath(dirname(__FILE__)) . DS);
set_include_path(APPROOT . 'lib' . PATH_SEPARATOR . get_include_path());
require_once('Loader.php');

Všimnite si, že v súbore nie je ukončený blok PHP kódu. Takto budeme postupovať vo všetkých .php súboroch, ukončite ich iba novým riadkom (new-line) za posledným neprázdnym riadkom (tak to je zvykom v UNIXe a Linuxe - každý súbor so zdrojákom má na konci jeden prázdny riadok). Dôvod prečo nebudeme ukončovať blok PHP kódu, je ten, aby sa na výstup nedostali žiadne (biele) znaky ešte predtým ako budeme renderovať stránku. V prípade, že by ste napr. posielali nejaké cookies (session), objavila by sa známa chyba
Warning: Cannot modify header information - headers already sent.
Však to isto poznáte. Dodržiavaním tohto postupu, týmto nepríjemnostiam zabránite.

Malý popis zapísaného kódu:
Prvé dva riadky riadia úroveň chybových hlásení aplikácie. Horeuvedený zápis je vhodný pre vývoj. V prípade ostrej prevádzky nezabudnite chybové hlásenia úplne vypnúť (nastavením error_reporting na E_NONE a ini_set('display_errors') na "off")!

Okrem toho nastavujeme časovú zónu, aby aplikácia vedela, v akej časovej zóne beží - PHPčko to potrebuje vedieť, aby správne pracovali niektoré jeho funkcie.

Nasleduje vytvorenie dvoch užitočných konštánt. DS je skratkou pre pôvodne dlhý, neprakticky názov oddeľovača adresárov. APPROOT drží cestu k súboru index.php vo filesystéme servera. Cestu potrebujeme vedieť preto, aby sme vedeli dynamicky "naťahovať" súbory z adresára lib a aby aplikácia mohla bežať aj v podadresári. Cesta sa získava z magickej konštanty __FILE__, ktorá vždy obsahuje cestu k aktuálnemu súboru. Z tejto konštanty odstránime meno súboru (index.php) a expandujeme prípadné symbolické linky.

Hneď na následujúcom riadku novú konštantu APPROOT zužitkujeme a adresár lib vložíme do čela cesty, kde PHP vyhľadáva knižnice. Posledný príkaz bude preto súbor Loader.php hľadať práve tam. To je zatiaľ k súboru index.php všetko, poďme sa pozrieť na súbor Loader.php, ktorý je zodpovedný za "autoloading" knižníc.

lib/Loader.php

<?php
function __autoload($class)
{
    // replace all underscores by directory separators
    $file = str_replace('_', DS, $class) . '.php';
    if (!file_exists(APPROOT . 'lib' . DS . $file)) {
        die("File '$file' not found within 'lib' directory!");
    }
    include_once($file);
    if (!class_exists($class, false) && !interface_exists($class, false)) {
        die("Class '$class' not defined in file '$file'!");
    }
}

Tento súbor obsahuje definíciu ďalšej mágie v PHP - magickej funkcie __autoload(). Táto funkcia sa stará o automatické naťahovanie súborov, vždy keď PHP potrebuje definíciu novej triedy. Takže už žiadne include("lib/smarty/smarty.php"), preč s tým. Kód funkcie __autoload() pri požiadavke na triedu, ktorá ešte nebola natiahnutá, vyhľadá súbor s jej zdrojákom a "inkludne" ho do aplikácie.

Loader.php najskôr prevedie všetky podtržítka na oddeľovače adresárov. Následne zostaví cestu k súboru a opäť s pomocou APPROOT overí či je súbor dostupný. Ak teda niekde v aplikácii napíšeme:
$db = new Zend_Db_Table();

__autoload() bude hľadať súbor lib/Zend/Db/Table.php. Súbor Table.php musí obsahovať definíciu classy v tvare:

class Zend_Db_Table
...

Loader.php nám tak umožňuje prehľadné organizovanie knižníc v rámci nášho lib adresára. A áno hlavnú časť jeho kódu som odkukal z aktuálneho Zend Frameworku :) . Loader.php je týmto hotový, vráťme sa teraz k súboru index.php. Doplňte do neho kód:

<?php  // !!! tento riadok vymazať !!!
$registry = new Registry();
$view = new bTemplate(APPROOT . 'templates' . DS);
$registry['view'] = $view;
$registry['supportedLangs'] = array('sk', 'en');
define('DEFAULTLANG', 'sk');
$router = new Router($registry);
$router->setPath(APPROOT . 'controllers');
$registry['homelink'] = $router->getBaseUrl();
$registry['lang']  = $router->getClientLang();
switch ($registry['lang']) {
    case 'sk': $registry['sitename'] = 'nulovy projekt'; break;
    case 'en': $registry['sitename'] = 'null project'; break;
}
// setup database
  // TODO
$router->delegate();

Ešte som nenapísal, že súbor index.php predstavuje tzv. bootstrapper - súbor, ktorý vykonáva všetky dôležité činnosti, pretým než sa začne s vlastým spracovávaním požiadavky od klienta. Bootstrapper má za úlohu vytvoriť všetky dôležité objekty a pripraviť pracovné prostredie pre Controllery (často sa do neho zapisuje aj kód na pripojenie k databáze). Ako môžete vidieť vyššie, v index.php sa vytvára niekoľko nových objektov. Tým prvým je objekt $registry, ktorý slúži ako storage place pre globálne údaje. Používanie globálnych premenných ako majú vo zvyku PHP noobs je dnes (v dobe OOP) zastaralé a zaostalé. Objekt $registry nám slúži ako obálka pre naše globálne údaje. Veľkou výhodou je, že k nemu môžeme pristupovať ako ku poľu. Musíme však mať min. PHP v5.1! Definíciu tohto súboru tu nebudem uvádzať, prezrite si ho na službe pastebin a jeho obsah uložte do súboru lib/Registry.php.

Ďalším objektom, ktorý bootstrapper vytvorí je objekt $view. Jedná sa o inštanciu šablónového stroja bTemplate. Tento šablónový stroj je oproti mamutovi Smarty mimoriadne malý (len jeden súbor s definíciou triedy) a je pomerne rýchly. Nevyznačuje sa síce featurami ako kompilovanie šablón alebo cachovanie, pre požiadavky tutoriálu je však dostatočný. Zdroják som oproti originálu mierne upravil, stiahnite si ho teda z tohto miesta, nie z domovskej stranky projektu! Stiahnutý archív rozbalte a súbory vložte do adresára lib. V bootstrapperi pri vytváraní objektu $view, nastavíme cestu ku šablónam. Cesta musí byť ukončená oddeľovačom adresárov! Hneď na to je objekt $view uložený do $registry.

Posledným a snáď naj dôležitejším objektom, ktorý v index.php vytvoríme je objekt $router. To on je zodpovedný za obsluhu URL, tak ako bola popísaná hore. Keďže definícia jeho triedy má viac ako 360 riadkov, opäť odkážem iba na jeho zdroják na pastebin. "Pastnite" jeho obsah do súboru lib/Router.php. Jeho kód si bližšie popíšeme neskôr, najskôr dokončím popis index.php. Všimnite si, že do konštruktora routra je priradený objekt $registry. Vďaka tomuto postupu je obsah registru dostupný v routri. Takto nejako je v OOP obsluhované nakladanie s globálnymi údajmi. Tento postup je možné ešte zjednodušiť, čo si ukážeme na pokusnom CMSku (zbavíme sa nepohodlného predávania objektu do konštruktorov).

Bootstraper predtým ako spustí aplikáciu, uloží do $registry ešte niekoľko elementov. Tým prvým je pole podporovaných jazykových mutácií aplikácie. Okrem toho vytvorime konštantu, ktorá obsahuje identifikátor primárnej jazykovej mutácie stránok. Táto mutácia bude servírovaná klientom, ktorý pri autodetekcii jazyka nebudú spadať do poľa podporovaných mutácií.

Do registru si uložíme aj URL, pod ktorou operuje index.php - link na indexovú stránku aplikácie. Tento link nájde primárne využitie pri odkazovaní na CSS a JS súbory v šablonách, pri generovaní navigácie a pod. Túto URL nám vráti router. Následne prebehne detekcia jazykovej mutácie. Tá je zisťovaná buď z URL (viď. vyššie), alebo ak identifikátor jazyka nie je v URL a klient vstúpil na indexovú stránku (IndexController/indexAction), prebehne autodetekcia z USER_AGENT. Obe získané informácie si uložíme do registra.

Nakoniec je na základe získanej jazykovej mutácie, nastavené pomenovanie aplikácie a toto je tiež uložené do registra. Následne je aplikácia spustená.

Spustenie aplikácie

v našom FW predstavuje započatie analýzy URL routrom pomocou jeho metody delegate(). Zatiaľ čo veľké FW routujú pomerne zložitým spôsobom, podporujú rôzne patterny a regulárne výrazy, náš router bude vykonávať iba jednoduché statické routovanie. Ako som už vyššie naznačil, schéma URL je takáto:
http://smvcfw/[jazyk]/[CONTROLLER]/[ACTION]/[parameter1]/[parameter2]...

Poďme sa pozrieť ako analýza URL prebieha. Otvorte si súbor so zdrojovým kódom routra (nebudem relevantný kód vypisovať na tejto stránke, nakoľko zvýrazňovanie syntaxe silno zaťažuje BH server). Na riadku 169. sa nachádza definícia metódy delegate(). Tá hneď volá ďalšiu metódu triedy Router - parseUrl(). Ako vidíte, metóda predáva 5 parametrov. Nakoľko sú tieto v hlavičke parseUrl() deklarované ako referencie, výsledky práce parseUrl() sa nebudú vracať pomocou štandardného príkazu return(), ale práve cez tieto premenné. Metóda delegate() zavolá parseUrl() s prázdnymi premennými a tá ich naplní požadovanými hodnotami. delegate() potom takto naplnené premenné použije k svojej práci. Prejdime teda k detailnému popisu metódy parseUrl() (jej kód začína na riadku 87.).

Na začiatku metóda parseUrl() získa údaje z URL, z premennej $_GET['route']. Meno tejto premennej musí súhlasiť so zápisom RewriteRule v hlavnom .htacces súbore. Údaje z premennej sú ďalej očistené o prebytočné lomítka vpredu aj vzadu funkciou trim(). Takto očistená premenná je "rozsekaná" do poľa pomocou funkcie explode(). Ako oddeľovače jednotlivých elementov poľa sa využijú lomítka z URL adresy.

Keď zoberieme náš predošlý príklad s adresou http://smvcfw/en/news/archive/2008/june, výsledné pole bude mať takúto schému:

array (
  [0] => en
  [1] => news
  [2] => archive
  [3] => 2008
  [4] => june
)

Jednotlivé elementy poľa sú následne podrobené analýze. Najskôr prebehne analýza toho, či prvý prvok URL obsahuje identifikátor jazykovej mutácie. Testuje sa jednoduchým regulárnym výrazom. Ako vidno, identifikátor musí mať presnú formu - dva znaky z malej abecedy. Ak je toto splnené (v našom príklade je) je identifikátor hneď zapísaný do referencovanej premennej. Zároveň je pole skrátené a testovaný element je z poľa vyradený, aby už nevstupoval do nasledujúcej etapy analýzy.
Ak nie je zistená zhoda s regulárnym výrazom, je analyzovaný element vrátený späť do horeuvedeného poľa.

Nasleduje najzaujímavejšia časť routovania. Jednotlivé elementy poľa sú prechádzané v slučke a pri každom prechode sa testuje, či hodnota elementu odpovedá názvu nejakého existujúceho controllera, ktorý máme uložený v našom controller adresári. Tento kód však obsahuje zaujímavé rozšírenie. Najskôr sa totiž testuje, či hodnota elementu poľa neukazuje na nejaký podadresár v controllers adresári. Ak je toto splnené, je aktuálna iterácia ukončená a controller sa bude hľadať v tomto podadresári.

K čomu je to dobré? Umožňuje to hierarchicky usporiadať vaše controllery. Napr. môžete všetky contollery, ktoré súvisia s administráciou uložiť do podadresára controllers/admin a tieto budú volané zadaním URL http://smvcfw/admin/.... Toto opäť zvyšuje vašu produktivitu a manažovateľnosť aplikácie.

Držme sa však našej modelovej URL http://mvcdemo/en/news/archive/2008/june a plochej štruktúry adresára controllers.

Jazyk teda máme vyextrahovaný a žiadny podadresár news v štruktúre adresára controllers nie je. Z testovaného elementu poľa sa teda poskladá nový názov súboru - controllers/NewsController.php. Následne sa testuje prítomnosť tohto súboru. Predpokladajme, že tento súbor existuje. Hodnota elementu sa vloží do premennej pre identifikátor názvu controllera (priradí sa do príslušnej referencovanej premennej) a detekcia controllera sa ukončí (ukončí sa slučka analýzy).

Keďže pole je počas testu pri každej iterácii skrátené o aktuálny element a keďže už máme názov controlleru (iterácia bola ukončená príkazom break;), na konci cyklu má pole takúto štruktúru:

array (
  [0] => archive
  [1] => 2008
  [2] => june
)

Hodnota nultého prvku je v ďalšom kroku uložená do premennej pre identifikátor názvu akcie (ACTION) a zvyšné prvky poľa sú exportované z metódy ako argumenty URL.

Toto všetko sa odohrá, ak router obdrží predpokladané parametre - na server príde požiadavka s predpokladanou URL. Zlí chlapci však môžu na server poslať URL so zámerne modifikovanými parametrami URL, alebo proste návštevník môže skúšať rôzne náhodne formy URL. Router sa s tým musí vysporiadať, preto sú do metódy parseUrl() implementované rozličné podvetvy kódu. Prejdime si ich.

Predpokladajme, že záškodník skúša modifikovať URL cca takto:
http://mvcdemo/asdasda/dasdas12313

Úvodná detekcia jazykovej mutácie neprejde, analýza teda postupuje ďalej. Testujeme prítomnosť adresára controllers/asdasda. Opať false výsledok, analýza vstupuje do finále, poskladá si názov súboru controllers/AsdasdaController.php a snaží sa overiť jeho prítomnosť. Ďalšie zlyhanie a router už vie, že na jeho vstup došla nezmyselná URL. Vykoná sa teda alternatívna vetva ktorá nastaví identifikátor názvu controllera na ErrorController(), identifikátor ACTION sa nastaví na index. Všetky nezmyselné výrazy URL sú uložené do premennej $args.

Inou situáciou je prípad URL bez parametrov:
http://smvcfw/

Slučka analýzy nemá nad čím iterovať (pole $parts je prázdne), preto ostáva premenná $controller prádzna. Tento stav je znakom, že kontrolu nad aplikáciou preberie IndexController() a jeho metóda index().

Posledným stavom, ktorý môže nastať je takáto URL:
http://mvcdemo/news/

Controller je nájdený, ale nemáme označenie akcie (pole malo jeden prvok, ktorý sme počas prvej iterácie analýzy skrátili na nulovú veľkosť). parseUrl() opäť situáciu odhalí a nastaví defaultnú ACTION index().

Za zmienku ešte stojí, že v názve akcie sú všetky pomlčky prevedené na podtržítka. Dôvodom je to, že v PHP nie je možné mať v názve metódy (funkcie) pomlčku. Ak teda router obdrží URL http://smvcfw/forum/post-delete, bude sa volať kód ForumController::post_delete().

----------------------------------

To by bolo z popisu na dnes všetko. Snažil som sa kód obkecať čo najpodrobnejšie a zároveň čo najstručnejšie (to druhé sa asi moc nevydarilo :o) ), verte kód nie je taký rozvlečený, ako vypadá v článku. Tú vatu okolo snáď niekto ocení. Nabudúce dokončíme popis kódu routra, vytvoríme si nejaké controllery, ukážeme si ako vyrenderovať prvé demo s využitím šablón. Sami uvidíte, že keď bude kostra frameworku hotová, následné písanie aplikácie už bude brnkačka (sľubované CMS-sko bude v tretej časti seriálu). Každopádne >> don't hesitate, feedback me :-)

Ešte malý súhrn toho ako by mal váš projekt po dnešnom diely vypadať:

pokračovanie nabudúce...

    • Re: Najjednoduchší MVC framework v PHP (part #1) 30.07.2008 | 19:13
      Avatar blackhole   Návštevník

      Chcem sa opytat tak trochu mimo, takze keby vas to znechutilo, tak preskocte moj prispevok.

      Môže sa totiž stať, že sa pod jednou URL bude skrývať niekoľko verzií stránky, za čo sa u vyhľadávačov penalizuje.
      Plati to len pre rozne jazyky?
      Vzdy ina verzia pri navsteve stranky je kazdy krat aj tu ako frontpage BH, ked sem Google Bot chodi raz za tyzden. Bolo by z hladiska SEO (ak by neslo o navstevnikov) vyhodnejsie pouzit odkaz na blogy a novinky, takze by bola uvodna stranka staticka?
      ____________________________________________________________
      Ked niecim nie som takmer uplne presvedceny, nepisem to. Vzdy uvadzajte vecne a najdolezitejsie argumenty, inak ma nepresvedcite. Ked sa mylim, opravte ma; rad sa poucim.

      • Re: Najjednoduchší MVC framework v PHP (part #1) 31.07.2008 | 14:22
        Avatar pa3k   Používateľ

        Ide o duplicitný obsah stránky. To znamená, že obsah na jednej URL je rôzny v rovnakom čase. SEO roboti často pristupujú v webom z rôznych serverov a môže sa stať, že každý server "vidí" iný obsah. Prípadne ak SEO bot nie je schopný prijať cookie, zaindexuje len jednu jazykovú verziu (defaultnú) a zvyšok webu nebude zaindexovaný vôbec. Viem, že minimálne Google Bot niekedy porovnáva obsah webu s obsahom získaným emuláciou bežného UA, čím predchádza podvrhovaniu obsahu čisto pre SEO botov. Sú totiž špekulanti, ktorý vygenerujú podľa UA identifikácie pre SEO bota stránku v vyšším počtom kľúčových slov, za čo bývajú penalizácie, pretože SE sa tomu pochopiteľne snažia brániť.

        http://imgs.xkcd.com/comics/exploits_of_a_mom.png
    • Re: Najjednoduchší MVC framework v PHP (part #1) 05.08.2008 | 17:17
      Avatar blackhole   Návštevník

      skvely clanok, framework sa mi paci, tesim sa na pokracovanie

      • Re: Najjednoduchší MVC framework v PHP (part #1) 06.08.2008 | 00:30
        Avatar blackhole_ventYl   Používateľ

        ja si naopak myslim, ze aj ked clanok je po formalnej stranke dobry, obsahovo nic moc extremne neprinasa. frameword bezducho a viacmenej dost bezucelne vyuziva objekty tam, kde by sa bez akejkolvek straty funkcnosti dal pouzit funkcionalny pristup (ako konieckoncov ~90% aplikacii objektov v PHP /hej, aj ta tvoja s velkou pravdepodobnostou/). navyse je model MVC tak, ako je pouzity tu, dost skostnately, pretoze nedovoluje rozne kombinovanie sablon / subcasti krizom krazom (trebars akcia edit nad jednym objektom v pohlade cez iny objekt, co je sice dost perverzne, ale niekedy sa to moze kriticky hodit). bud sa sticknem k tomu, ze budem vsade vyuzivat uplne rovnaku strukturu dokumentu, alebo sa uprogramujem k smrti. ako som niekde cital, je to "flexible little less than rocky mountains".

        ale budem rad, ak ma autor vyvedie z omylu.

        ---
        Cuchat s nadchou, to je ako sniffovat bez promiscu.

        --- Cuchat s nadchou, to je ako sniffovat bez promiscu.
        • Re: Najjednoduchší MVC framework v PHP (part #1) 06.08.2008 | 15:32
          Avatar blackhole   Návštevník

          "frameword bezducho a viacmenej dost bezucelne vyuziva objekty tam, kde by sa bez akejkolvek straty funkcnosti dal pouzit funkcionalny pristup"

          Ano samozrjeme, uz sa vsetci tesime na tvoj PHP framework, ktory vyriesi vsetky nase problemy a bude najlepsi :)

          m_ax

          • Re: Najjednoduchší MVC framework v PHP (part #1) 07.08.2008 | 12:14
            Avatar blackhole_ventYl   Používateľ

            srigiho odpoved je rozumna, svoje invektivy si mozes strcit za klobuk.

            ---
            Cuchat s nadchou, to je ako sniffovat bez promiscu.

            --- Cuchat s nadchou, to je ako sniffovat bez promiscu.
        • Re: Najjednoduchší MVC framework v PHP (part #1) 07.08.2008 | 09:37
          Avatar blackhole   Návštevník

          Tento framework skutocne plni ulohu byt velmi jednoduchy a v podstate je urceny ako nahrada za prapôvodnu include() metodu pisania webov. Jeho routovanie je naozaj velmi obmedzene, neumoznuje vobec definovat vlastne routy, zato praca so sablonovacim systemom je velmi jednoducha (co predvediem v druhom diely). Sice sa nedodrzuje DRY pristup (don't repeat yourself) pri programovani conrollerov, ale zase to vedie k pochopitelnemu kodu, ktory neskryva pod kapotou nejake čudesne čary.

          BTW pokracovanie asi az cez vikend.

          ----------------------
          Ja len v dobrom.

        • Re: Najjednoduchší MVC framework v PHP (part #1) 13.09.2008 | 22:51
          Avatar blackhole   Návštevník

          Janse ze vsetci budu programovat ve struktorovanem PHP a nebudou vyuzivat vsetky moznosti OOP si uhodl :D, pomoci strukurovaneho kodu nikdy nedosahnes to co v OOP, si otem neco precti, aby ses vyvedl z omylu

          • Re: Najjednoduchší MVC framework v PHP (part #1) 14.09.2008 | 20:21
            Avatar pa3k   Používateľ

            Jasné pomocou štrukturálneho/procedurálneho programovania nedosiahnem oproti OOP veľa vecí. Mimo iného nekompatibilitu, chaos a problémy v aplikácii medzi jednotlivými relasmi PHP ;-) Overloading v 5.2.0. alebo chovanie Reflection sú veci, kvôli ktorým sa OOP v PHP fakt oplatilo! :) Mám zásadu - čo sa dá v PHP spraviť jednoducho a bez OOP robím jednoducho a bez OOP. Mimochodom PHP ako jednoducjý skriptovací jazyk začal byť "hnusný" práve snahou o zavedenie OOP. Nemám nič proti OOP, no ak je implementované len čiastočne a problematicky, nehrozí porovnanie s OOP jazykmi, ktoré boli ako OO už navrhnuté.

            http://imgs.xkcd.com/comics/exploits_of_a_mom.png
    • Re: Najjednoduchší MVC framework v PHP (part #1) 16.08.2008 | 15:48
      Avatar blackhole_karci   Používateľ

      Dôvodom prečo nebudeme ukončovať blok PHP kódu, je ten, aby sa na výstup nedostali žiadne (biele) znaky ešte predtým ako budeme renderovať stránku. V prípade, že by ste napr. posielali nejaké cookies (session), objavila by sa známa chyba
      Warning: Cannot modify header information - headers already sent.

      A co takto tam hodit ob_start(); a na konci suboru ob_end_flush(); ? To (dokial ma pamat a skusenosti neklamu) pomaha, a aspon si nemusime davat pozor ze ci sme nieco omylom uz neposlali

      ______________
      if it moves, compile it! [:. Gentoo rulz .:]

      ______________ if it moves, compile it! [:. Gentoo rulz .:]
      • Re: Najjednoduchší MVC framework v PHP (part #1) 17.08.2008 | 21:41
        Avatar blackhole_thomm   Používateľ

        no, predpokladam, ze neukoncit znacku je jednoducho menej pracnejsie...:) na co robit hentaky workaround?

        • Re: Najjednoduchší MVC framework v PHP (part #1) 18.08.2008 | 10:17
          Avatar pa3k   Používateľ

          Neviem či je to až o toľko menej pracné, ale má to výhody hlavne v tom, že môžeš ešte pred odoslaním hlavičiek echovať do nemoty a veselo posielať na výstup čokoľvek (výstup ide len do bufferu). Kedykoľvek môžeš posielať hlavičky bez straty funkčnosti. V tomto zmysle to JE oveľa menej pracné, pretože vôbec neriešiš poradie výstupu a hlavičiek.

          Osobne ale buffer takýmto spôsobom nepoužívam, jedine v jednom väčšom tímovom projekte. Rovnako ale nepoužívam ani to neukončovanie php parsera značkou. Proste sú to veci na ktoré som si už zvykol dávať pozor a viac by ma to mýlilo ako pomáhalo :-D Hold starého psa novým kúskom... je železná košeľa. ;-)

          http://imgs.xkcd.com/comics/exploits_of_a_mom.png
          • Re: Najjednoduchší MVC framework v PHP (part #1) 09.09.2008 | 21:00
            Avatar blackhole   Návštevník

            Nuz to je ale v informatike dost blbe, prave tu viac ako kdekolvek (co ma prave napada) inde je nutne sa ucit stale novym a novym "kuskom".
            ==
            Things are only impossible until they're not.

            • Re: Najjednoduchší MVC framework v PHP (part #1) 10.09.2008 | 10:33
              Avatar pa3k   Používateľ

              Samozrejme, učenie nových vecí je v IT nutnosť, ale mne nešlo o postupy a znalosti ako také. Narážal som na zažité konvencie z hľadiska dodržiavania "štábnej kultúry" zdrojového kódu. Ak mám dané postupy zabehnuté inak a som zvyknutý ich tak používať, nič ma neprinúti svoje zvyky meniť, ak mi to niečo významné neprinesie. Samozrejme, iné je to v tímovej práci, kde je dôležitá jednotnosť.

              http://imgs.xkcd.com/comics/exploits_of_a_mom.png
      • Re: Najjednoduchší MVC framework v PHP (part #1) 13.09.2008 | 22:54
        Avatar blackhole   Návštevník

        si to tam kazdy velmi lahko moze doplnit ako aj plno dalsich funkcii

    • Re: Najjednoduchší MVC framework v PHP (part #1) 09.09.2008 | 16:55
      Avatar blackhole   Návštevník

      A kde jsou vsechny zdrojaky ? Velmi rad bych si tento clanek precetl. Pls muzete podoplnotat chybjejici obsah ? Na netu je o MVC pramalo clanku v nasem jazyce.

      Dekuji. Alebo mi muzete calnek poslat i na maila.

    • Re: Najjednoduchší MVC framework v PHP (part #1) 13.09.2008 | 22:58
      Avatar blackhole   Návštevník

      Chcel by som podakovat za skvely clanok a naozaj sa tesiiim na dalsie pokracovanie, nech uz je uverejnene ;)

      Iba by som chcel podotknut, ze mena adresarov v adresary Controller nemozu mat rovnaky nazov ako mena controlerov ze zdrojaku jsou duvody zrejme

    • Re: Najjednoduchší MVC framework v PHP (part #1) 16.09.2008 | 14:24
      Avatar pa3k   Používateľ

      Jedna faktická k mod_rewrite a cool uri. Na cool uri mi trochu vadí nemožnosť relatívneho adresovania. Možno predbieham, ale rád by som vedel, ako riešite generovanie odkazov? Používate nejkú funkciu na konverziu odkazov na globálny tvar, alebo prefix webrootu, prípadne používate v HTML šablóne base href? Ako riešite odkazy na kotvy dokumentu?

      http://imgs.xkcd.com/comics/exploits_of_a_mom.png
      • Re: Najjednoduchší MVC framework v PHP (part #1) 21.09.2008 | 10:17
        Avatar blackhole   Návštevník

        Ja som to povodne hackoval kade-tade, ze som si vyratal a vsade posuval absolutnu cestu k prave zobrazovanej "veci".
        Potom som zistil, ze existuje <base href="/" /> , od coho sa odvadzaju relativne url...

        Ale pri pouzivani mod_rewrite na cool uri sa predsa da adresovat relativne.
        ../ , subdir/ , ../sibling/ - to vsetko funguje.
        ==
        Things are only impossible until they're not.