Objekty

Zdravím.

Jaký máte na OOP v PHP názor vy ?
Já je nepoužívám, protože s nimi pracovat neumím. Prý zlehčují práci, ale vůbec mě nenapadá, jak bych mohl své scripty vylepšit.
Mohl by mi někdo ukázat nějaký jednoduchý script s objekty využitý v praxi ? Nechci nic velkého (jako třeba zdrojové kódy frameworků).

Popřípadě jestli by někdo nemohl vytvořit například registraci uživatelského účtu (pokud je nejlepší řešení právě přes OOP) a zaslat mi kód.

Pracovat s objekty bych se naučit chtěl, ale asi jsem málo sebejistý, ale nevěřím si, že bych to zvládl sám od sebe. Možná je to věkem, možná neznalostí, kdo ví.

Občas použiji objekt v JS, když odesílám POST pomocí jQuery funkce $.post, ale to je tak vše.
Objekt je zjednodušeně nějaká skupina proměnných (atributů) a k nim je přidáno pár funkcí, které s tou skupinou umí pracovat. Když jsou objekty vhodně navrženy, výrazně to zjednoduší aplikaci.

Pro správné objektové programování je nutné změnit programátorské myšlení. Ostatně to platí i pro ostatní programovací paradigmata.

Čím mladší programátor, tím snáze pochopí objektové programování. Možná by to chtělo nějakou učebnici.
Jak píše Kit, objekt je taková obalená směsice funkcí a atributů. Což mu umožňuje snadné použití. Vytáhnu jen objekt a zavolám funkci. Nemusím pak řešit programový kód.
Analogicky k sekvenčnímu programování by se to dalo představit tak, že objekt je něco jako samostatný PHP soubor, který v sobě obsahuje různé funkce. Ty pak jen tento soubor includuješ a zavoláš funkci. U objektového programování tohle všechno máš jen obalené do objektu a jen zavoláš objekt a v něm funkci.

Objekty usnadňuji tvorbu. Na začátku si vytvoříš objekt nebo najdeš na internetu, a pak ho jen používáš kolikrát chceš.

Pro představu co to ten objekt je a jak může pomoci.

Budeme pracovat s autem. Takže vytvoříme objekt Auto. Toto auto má nějaké své parametry (atributy) - barva, typ, značka, rozměry, rychlost apod. A má nějaké funkce start(), zastavit(), otevři(), zapni_světla(), ukaž_rychlost() apod.

Mám tedy nové auto, které je tak nějak prázdné. Tak ho nastavím.

$auto = new Auto();
$auto->značka = "Škoda"; // mám škodovku
$auto->typ = "Superb"; // extra třídu ;)
$auto->barva = "bílá"; // chci bílou barvu

// Nyní s ním vyjedu na cestu
$auto->otevři_dveře();
$auto->zavři_dveře();
$auto->nastartuj();
$auto->jeď();
$auto->ukaž_rychlost();
..

Jednotlivé funkce si prostě předem naprogramuješ a v aplikaci pak jen zavoláš tyto funkce.
Výhodou objektu je, že je můžeš použít vícekrát. Takže si mohu pořídit další auto:

$auto2 = new Auto();
$auto2->značka = "Ferrari"; // mám Ferrari
$auto2->typ = "F1"; // formulku ;)
$auto2->barva = "červená";

A mezi těmito auty pak třeba soutěžit :)

$auto->start();
$auto2->start();


Ve tvém případě registrace může být použita v OOP, i když v jednoduchém případě to asi nebude nutné.
Prostě máš objekt Registrace. Ten má nějaké atributy: login, heslo, email a funkce: registruj($data), existuje_login($login), kontroluj_heslo($heslo1, $heslo2), odesli_email() atd podle toho, co chceš s registraci dělat.

Jde jen o to pochopit co to je OOP, jak se s ním pracuje a co přináší. Chce to cvika.
Doplním Tomíka, že includovat není třeba, pokud si nakonfiguruješ autoloader, např. podle tohoto:
http://www.nabito.net/autoload-v-php/

Pro upřesnění: "Auto" není objekt, ale třída (nebo chceš-li kategorie objektů). $auto je v daném případě objekt. Spíš bych ty objekty pojmenoval konkrétněji:

$mojeauto=new Auto();
$sousedovoauto=new Auto();

Když si ve třídě nadefinuješ vhodnou funkci __construct($znacka,$typ,$barva), tak se vytváření objektů zjednoduší:

$mojeauto=new Auto("Škoda","Superb","bílá");
$sousedovoauto=new Auto("Ferrari","F1","červená");

Program pak jen v objektech volá různé metody tak, aby pokud možno přímo nepracoval s proměnnými objektu ale pouze s jeho metodami.

Ve tvém případě by to mohlo být třeba takto:

$hrac=new Hrac($_SESSION['nick'])); // najde si v databázi údaje o hráči a vytvoří z nich objekt
$hrac->kupArtefakt('Pohár života',500); // provede u hráče transakci

Veškerá práce kolem této transakce se pak děje v metodě kupArtefakt() objektu $hrac třídy Hrac, kterou budeš mít uloženu třeba v souboru "class.Hrac.php", viz autoloader.

Navíc u takto definovaných modulů zpravidla nemusíš řešit, zda do něj někdo leze ilegálním způsobem. Takovému útočníkovi se pouze nadefinuje třída, která zatím nic neudělala, nic víc. Objekt se nevytvoří.
Pěkný den.

Nejspíš vím, jak to asi tak práci zjednodušší, ale nejsem si jist, jestli to zvládnu udělat tak, aby mi to opravdu práci zjednodušilo a ne naopak. Každopádně ne vše se umí hned, tak to budu zkoušet na nějakých jednodušších scriptech a pokusím se to ovládat.
Díky, že máte takové strpení. :-)
Když použiješ ten autoloader, tak se zbavíš skoro všech includů a aplikace se docela zajímavě vyčistí. Přijdeš na to, že stávající kód máš do těch includů rozdělený nesmyslně.

Při té příležitosti jsem zkusil stránku, kterou jsem odkazoval výše. Našel jsem v tom skriptu chybu, která se v mé aplikaci projevila docela nepříjemně jako fatal error. Zde je opravená verze:

function __autoload($class_name) {
if (!class_exists($class_name, false) || !interface_exists($class_name, false)) {
if(file_exists('class.'.$class_name . '.php')) {
require_once('class.'.$class_name . '.php');
}
}
}

Když si ten kód dáš na začátek skriptu index.php a přepíšeš aplikaci do objektů, můžeš zapomenout na další include i require. Ten kousek skriptu se o to postará sám.

Chyba se projevovala tak, že když jsem se ve funkci ptal na přítomnost systémové třídy, která přítomna nebyla, snažil se skript includovat tuto třídu z mého neexistujícího souboru a na tom zkolaboval.

Konkrétně jsem se ve skriptu ptal na přítomnost třídy MySQLi, která na WZ není, ale jinak je běžná.
Pomoci __autoload ti sice odpadne povinnost používat include nebo require, protože to ti obstará tato metoda. Ale tímto pro začátečníka vznikne nepříjemnost. Pokud nebude mít pevný řád, tak se může dočkat chybových hlášek, že třída neexistuje. Je to dáno tím, že třídu umísti tam, kde to autloader nemá definovaný. Takže je třeba v __autoload myslet i na to, kam tyto třídy umístit a odkud očekávat, že se třída bude načítat.

Kitův příklad předpokládá, že všechny třídy jsou umístěny na stejné úrovni jako skript a zároveň používají prefix "class" (třeba classAuto.php).
oprava: .. používají prefix "class." (třeba class.Auto.php).
Pokud budu mít více času, určitě autoloader vyzkouším. Díval jsem se na zdrojový kód nějaké hry v C++, kde se OOP používá vlastně pořád, tak mi to dalo motivaci... ale trápí mě ten čas kvůli škole. :(
Každopádně i ten autoloader si mohu trochu upravit, třeba ty názvy souborů - změním prefix class. na něco jiného a poběží to tak, jak si to nastavím.
C++ a OOP? To je nějaký nový vtip? :-)

Podle mne je C++ spíš pro masochisty, kteří potřebují ušetřit každou mikrosekundu a nevadí jim, když program při vývoji neustále padá.

Co takhle Lua? Ta je hezky objektová a je docela i rychlá.

Tomík: Fantazii se meze nekladou. Můžeš to změnit třeba na
'class/'.$class_name . '.php'
a hned to máš v podadresáři dle přání.
Ten řád se i začátečník velmi rychle naučí.

Těch tříd nemívám mnoho, proto mi plochá struktura nevadí, ale naopak vyhovuje. Jak roste projekt a počet tříd by překročil určitý limit, např. 2000, tak se samozřejmě dá rozčlenit do podadresářů s patřičnou úpravou autoloaderu. Ovšem na projekty začátečníka mi to připadá jako zbytečný luxus, který k přehlednosti nepřispívá.
Tak jsem se očividně spletl, ale Lua tam určitě byla také.

Kolik asi tak lidí dělá v nějaké firmě na nějakém projektu, když myslím pouze programátory ? Myslím, že to přesahuje pár desítek, je možné, že i pár stovek? :)
Jsou to obvykle malé skupinky 3-30 lidí podle rozsahu projektu. Velké skupiny se špatně koordinují. Nejmenší použitelná skupina jsou 2 lidé: Jeden programuje a druhý se stará o veškerý styk se zákazníky, provádí analytickou činnost a testování. Programátor pak stačí jeden.
Tak záleží na různých okolnostech. Hlavním faktorem je mohutnost projektu a dostupné prostředky firmy (počet programátorů, analytiků, testerů, koordinátorů apod). Na začátku bývá vždy analýza, odkud se zjistí náročnost, dostupný počet a čas.
Na projektu může klidně pracovat jeden, ale projeví se to na delším čase. Naopak několik desítek programátorů na drobný projekt také nebude pravý ořechoví. Tam může docházet k vzájemným kolizím a komunikačnímu šumu. Pokud jde o dosti velký projekt, tak počet programátorů se může určit na základě rozštěpenosti projektu. Třeba máme deset modulů/části, takže můžeme nasadit i deset programátorů, kteří budou dělat jen daný modul. Tím nedochází k vzájemným kolizím. A zde také vidíš jednu z výhodných vlastností objektů. Každý pracuje pouze na svém objektu a v konečné fázi se to jen vzájemně propojí.

Menší firmy, kde je jen několik programátorů (třeba 5), tak je počet od jedné do max. Ideálně tak, aby to pokrylo všechny projekty.
Mega korporace (Microsoft, Adobe apod) mají tisíce programátorů. Takže na jedné aplikaci může pracovat stovky programátorů. Ale tam je to stejně řešeno na části, takže i tak je to jen skupinka jednotek programátorů, kteří řeší pouze jednu drobnost (ve Windows třeba jen tlačítko start :)).
Nerad se zapojuju do delších diskusí, když se neúčastním hned od začátku, ale nedá mi to. Je sice fajn, že všichni jdeme cestou defenzivního programování (ono se to v netypovaném php ani jinak dělat nedá), ale ukázkový kód pro autoloader je opravdu zbytečný. Měl by se zkrátit pouze na:

function __autoload($class_name) {
if(file_exists('class.'.$class_name . '.php')) {
require_once('class.'.$class_name . '.php');
}
}

Porovnávání jestli třída existuje je totiž irelevantní. Pokud třída existuje, autoloader se nezavolá. Pokud naopak neexistuje, tak projde i přes tuto podmínku. Mimochodem s využitím této znalosti lze udělat i autoloaderovou prasárnu, kdy se napíše více tříd do souboru pojmenovaného po první třídě (a první se volá třída podle které je pojmenovaný soubor) a další hledání souborů už se neprovádí.

Protože je však operace file_exists jedna z nejpomalejších operací vůbec (z častých operací ji předbíhá snad jenom filesize, fileinfo, filemtime,..), stojí za zvážení další zkrácení autoloaderu:

function __autoload($class_name) {
require_once('class.'.$class_name . '.php');
}

Při vývoji stejně potřebujeme vědět, že soubor neexistuje (a tudíž nám fatal error nevadí) a ve veřejné verzi už zase máme všechny skripty zkontrolované, aby se používaly pouze existující třídy (a tudíž fatal error nehrozí).

___
Jinak osobně mám raději pojmenování Class_name.class.php, protože se tak v adresáři dá jednoduše vyhledávt podle prvních písmen ;)
function __autoload($class_name) {
require_once('class.'.$class_name . '.php');
}

Právě podobný kód mi způsoboval fatal error:
if(class_exists('MySQLi')) {
// kód, pokud systémová třída MySQLi existuje
}

Potřeboval jsem jeden kód pro 5 různých serverů s různou konfigurací.
Takové případy, je podle mě často lepší je řešit přes dummy soubor (tzn. správné pojmenování, ale bez obsahu). Pokud už ale dělám jakoukoliv aplikaci, která má běžet na více nastaveních, snažím se optimalizovat tak, abych takovéto kontroly (existenci systémových knihoven) dělal jenom při vytváření konfigurace, nikoli při každém spuštění skriptu. V takovém případě jeden prázdný dummy soubor (který se nenačte, pokud systém knihovnu má a nebo načte pouze jednou při vytváření konfigurace) prakticky nejlepší řešení ;)
K tem tridam, mi je to celkem neprijemne vytvaret, ale zjistil jsem tu krasnou vec, ze o to lip se to pouziva. urcite vlastnosti zkloubis do uzavreneho objektu a pak je nemusis hledat mezi funkcemi mimo tento objekt. Problem vzdycky mam jen, co dat dovnitr a co dat ven a jak tridy provazat vzajemne?

Mozna by se to dalo pojmout jako dotaz.

Treba mam funkci isRequest, ktera mi vytahuje poslane udaje z formulare pro name ze session, post, get, ... ve vybranem poradi, napr get, session, post. Tuto funkci vedu jako externi, protoze se zapis uvnitr objekt zjednodusi. Mohl bych ji uzavrit do objektu, treba neco jako class_main, ale pak to musim nejak napojit do objektu, jak?

class Main {...}
class Jina {
public function aaa()
{
global $class_main;
$a = $class_main->nejakafunkce();
}
}
$class_main = new Main;
$class_jina = new Jina;

To mi prijde jako dost zmateny zapis, protoze ta class je jednak zavisla na pritomnosti jine a tez se to da spatne zjistit, protoze je to nekde uprostred kodu. A v podstate se to zjisti az errorem.

Resit to pres construct a nacpat do vnitrni promenne mi prijde taky takove kostrbate, ale asi nejlepsi. Jenze potom pri kazdem vytvareni tam musite pridavat nazev externiho class.

class Jina {
public $class_main;
public function __construct($class_main)
{
$this->class_main = $class_main;
}
public function aaa()
{
$a = $this->class_main->nejakafunkce();
}
}

A jeste me napadlo toto, ale tez se mi to nelibi, prilis.
class Jina {
public $class_main;
public function __construct()
{
global $class_main;
$this->class_main = $class_main;
}
public function aaa()
{
$a = $this->class_main->nejakafunkce();
}
}

Cili v ramci zjednoduseni zapisu to resim takto:
class Jina {
public function aaa()
{
$a = nejakafunkce(); //nejakafunkce= isRequest()
}
}
Coz je tez na houby, byt zavisly na externi funkci, ale asi lepsi nez ji klonovat do objektu duplicitne, ne?
Freeze: Vytváření dummy objektů je IMHO nešťastné řešení. Kompromisem by mohlo být nahrazení funkce require_once() funkcí include_once() - bez kritizovaného ověřování existence souboru, ale s vyhozením výjimky, pokud soubor neexistuje:

function __autoload($class_name) {
if(!@include_once("$class_name.class.php"))
throw new Exception("Chybí soubor $class_name.class.php");
}
Mohl bych se ještě zeptat, proč se před název píší dvě podtržítka?
Dvě podtržítka se píší před všemi speciálními metodami objektů. Je to proto, aby se nepletly s ostatními metodami. Kdyby sis chtěl napsat vlastní metodu autoload(), která by dělala něco jiného, došlo by ke kolizi.

Zjednodušeně řečeno: Všechny metody začínající dvěma podtržítky jsou nebo někdy v budoucnu mohou být vedeny jako systémové (magické). Názvy vlastních metod by nikdy dvěma podtržítky neměly začínat.

Další zajímavé názvy magických metod:
http://cz.php.net/manual/en/language.oop5.magic.php

Jednou z mých oblíbených magických metod je __toString(). Hodí se zejména pokud používáš stringy typu heredoc.
Kit: Když už jsme u těch require_once a include_once, tak jsem předtím zapomněl na další optimalizaci, která spočívá v nepoužití _once funkcí, ale klasických require/include. Z důvodu, který jsem už napsal o pár příspěvků zpátky (tzn. autoload se volá, jenom když je opravdu potřeba), je kontrola, jestli už se soubor nenačítal, zbytečná. Kvůli takové kontrole jsou navíc _once funkce pomalejší.

Na zmíněný problém (kontrola existence systémové třídy) podle mě neexistuje (při použití autoloadu) univerzální řešení. Postup, který zpomalí načítání dalších tříd, bych si ale nechal opravdu jako záložní a snažil se přijít s čímkoliv lepším.. :)

___
Čas, který je potřeba na include/require ale mnohem víc ovlivňuje nastavení include_path (projeví se zejména ve chvíli, kdy je stroj dost zatížený).
Freeze: Dobrá, tím se ten autoloader ještě o pár znaků zkrátí. Když to dám do kupy, tak můj autoloader vypadá teď takto:

function __autoload($class_name) {
if(!@include("$class_name.class.php"))
throw new Exception("Chybí soubor $class_name.class.php");
}

Ošetření výjimky už nechávám na aplikaci.
Zkusil jsem si jen tak narychlo udělat jednoduchou třídu a chtěl bych se zeptat, jestli je vhodné nějaké takovéto užití objektů.

http://konecny.php5.cz/oop/priklad.txt

Samozřejmě by v daných funkcí byly různé podmínky, databázové příkazy atd.
Pro začátek to vypadá velmi slušně. Pokračuj.
Jen taková drobnost. V třídě by se nemělo objevovat něco, co vyvolá IO výstup (echo, print, ..). Třída je od toho, aby se to dalo použít kdekoli. A pokud třídu použije neznalý (nevidí dovnitř), tak bude překvapen, že to něco vyplivlo na obrazovku a to bez jeho zvolení. Pokud už má metoda vyplivnout nějaký výstup, tak jedině přes návratovou hodnotu.

class hrac
{
function odesli_zpravu($prijemce)
{
return "Hráč ".$this -> user ." napsal zprávu hráči ".$prijemce;
}
}

echo $user->odesli_zpravu("Jméno jiného hráče");


Pro testování nebo naučení echo klidně můžeš použít, abys věděl, co to dělá. Ale nemělo by to být pravidlem.

Pro začátek dobrý.