Kako postaviti pametne telefone i računala. Informativni portal
  • Dom
  • OS
  • Stvaranje objekta u php. Predmeti

Stvaranje objekta u php. Predmeti

Klase i objekti u PHP-u

Klasa je osnovni koncept u objektno orijentiranom programiranju (OOP). Klase čine sintaktičku osnovu OOP-a. Oni se mogu smatrati nekom vrstom "spremnika" za logički povezane podatke i funkcije (obično se nazivaju - vidi dolje). Pojednostavljeno rečeno, klasa je vrsta tipa podataka.

Instanca klase je objekt. Objekt je skup podataka () i funkcija (metoda) za njihovu obradu. Svojstva i metode se nazivaju. Općenito, objekt je sve što podržava enkapsulaciju.

Ako se klasa može smatrati tipom podataka, onda se objekt može smatrati varijablom (po analogiji). Skripta može istovremeno raditi s nekoliko objekata iste klase, kao i s nekoliko varijabli.

Unutar objekta podaci i kod (članovi klase) mogu biti javni ili ne. Javni podaci i članovi klase dostupni su drugim dijelovima programa koji nisu dio objekta. Ali privatni podaci i članovi klase dostupni su samo unutar ovog objekta.

Opisi klasa u PHP-u počinju funkcijskom riječi razreda:

klasa Ime klase (
// opis članova klase - svojstva i metode za njihovu obradu
}

Za deklaraciju objekta morate koristiti operator novi:

Objekt = novi naziv klase;

Podaci se opisuju pomoću funkcijske riječi var. Metoda je opisana na isti način kao i obična korisnički definirana funkcija. Metodi također možete proslijediti parametre.

Rezimirajmo: deklaracija klase mora započeti ključnom riječi razreda(slično kao što deklaracija funkcije počinje ključnom riječi funkcija). Ispred svake deklaracije svojstva sadržane u klasi mora stajati ključna riječ var. Svojstva mogu biti bilo koje vrste podataka koje PHP podržava i mogu se smatrati varijablama s manjim razlikama. Deklaracije svojstava slijede deklaracije metoda, koje su vrlo slične tipičnim deklaracijama korisnički definiranih funkcija.

Prema općeprihvaćenim pravilima nazivi OOP klasa počinju velikim slovom, a sve riječi u nazivima metoda, osim prve, počinju velikim slovom (prva riječ počinje malim slovom). Naravno, možete koristiti bilo koju notaciju koju smatrate zgodnom; glavno je odabrati standard i pridržavati ga se.

Primjer klase u PHP-u:

// Kreirajte novu Coor klasu:
klasa Coor (
// podaci (svojstva):
var$name;
var $addr;

// metode:
Naziv funkcije() (
jeka "

Ivan

" ;
}

}


$objekt = noviCoor;
?>

Pristup klasama i objektima u PHP-u

Pogledali smo kako se opisuju klase i stvaraju objekti. Sada moramo pristupiti članovima klase, za tu svrhu u PHP-u postoji operator -> . Evo primjera:

// Kreirajte novu Coor klasu:
klasa Coor (
// podaci (svojstva):
var$name;

// metode:
funkcija Getname() (
jeka "

Ivan

" ;
}

}

// Kreirajte objekt klase Coor:
$objekt = noviCoor;
// Pristupite članovima klase:
$objekt -> ime = "Alex" ;
echo $objekt -> ime ;
// Ispisuje "Alex"
// Sada pristupimo metodi klase (u stvari, funkciji unutar klase):
$objekt -> Getname();
// Ispisuje "Ivan" velikim slovima
?>

Za pristup članovima klase unutar klase, morate koristiti pokazivač $ovo, koji se uvijek odnosi na trenutni objekt. Modificirana metoda Getname():

funkcija Getname() (
echo $ovo->ime;
}

Na isti način možete napisati metodu Setname():

funkcija Setname($name) (
$this->name = $name;
}

Sada možete koristiti metodu za promjenu imena Setname():

$objekt->Setname("Petar");
$objekt->Getname();

A ovdje je cijeli popis kodova:

// Kreirajte novu Coor klasu:
klasa Coor (
// podaci (svojstva):
var$name;

// metode:
funkcija Getname() (
echo $ovo -> ime ;
}

funkcija Setname($name) (
$ovo -> ime = $ime ;
}

}

// Kreirajte objekt klase Coor:
$objekt = noviCoor;
// Sada za promjenu imena koristimo metodu Setname():
$objekt -> Setname("Nick");
// A za pristup, kao i prije, Getname():
$objekt -> Getname();
// Skripta ispisuje "Nick"
?>

Pokazivač $ovo također se može koristiti za pristup metodama, a ne samo za pristup podacima:

funkcija Setname($name) (
$this->name = $name;
$this->Getname();
}

Konstruktori

Vrlo često, kada stvarate objekt, morate postaviti vrijednosti nekih svojstava. Srećom, programeri OOP tehnologije uzeli su u obzir ovu okolnost i implementirali je u koncept. Konstruktor je metoda koja postavlja vrijednosti nekih svojstava (a može pozvati i druge metode). Konstruktori se pozivaju automatski kada se kreiraju novi objekti. Da bi to bilo moguće, naziv metode konstruktora mora odgovarati nazivu klase koja je sadrži. Primjer konstruktora:

Web stranica razreda(
var $bgcolor;
funkcija web stranica($color)(
$this -> bgcolor = $color ;
}
}

// Poziv konstruktora klase web stranice
$stranica = nova web stranica("smeđa");
?>

Prethodno su se stvaranje objekta i inicijalizacija svojstva izvodili odvojeno. Konstruktori vam omogućuju izvođenje ovih radnji u jednom koraku.

Zanimljiv detalj: ovisno o broju proslijeđenih parametara mogu se pozivati ​​različiti konstruktori. U razmatranom primjeru objekti klase Web stranica može se stvoriti na dva načina. Prvo, možete pozvati konstruktor koji jednostavno stvara objekt, ali ne inicijalizira njegova svojstva:

$stranica = nova web stranica;

Drugo, objekt se može stvoriti korištenjem konstruktora definiranog u klasi - u ovom slučaju stvarate objekt klase Web stranica i dodjeljujete vrijednost njegovom svojstvu bgcolor:

$stranica = nova web stranica("smeđa");

Destruktori

Ne postoji izravna podrška u PHP-u. Međutim, možete jednostavno simulirati destruktor pozivanjem PHP funkcije poništi(). Ova funkcija uništava sadržaj varijable i vraća resurse koje je zauzela sustavu. S predmetima poništi() radi na isti način kao i s varijablama. Recimo da radite s objektom $Web stranica. Nakon završetka rada s ovim konkretnim objektom, funkcija se poziva:

poništi($Webstranica);

Ova naredba briše sav sadržaj iz memorije $Web stranica. U duhu enkapsulacije, možete uputiti poziv poništi() u metodu pod nazivom uništiti() a zatim ga nazovite:

$Web stranica->uništi();

Potreba za pozivanjem destruktora javlja se samo kada se radi s objektima koji koriste veliku količinu resursa, jer se sve varijable i objekti automatski uništavaju kada skripta završi.

Inicijaliziranje objekata

Ponekad postoji potreba za inicijaliziranjem objekta - dodijeljivanjem njegovih svojstava početnim vrijednostima. Pretpostavimo da je ime klase Coor a sadrži dva svojstva: ime osobe i grad njenog prebivališta. Možete napisati metodu (funkciju) koja će inicijalizirati objekt, na primjer U tome():

// Kreirajte novu Coor klasu:
klasa Coor (
// podaci (svojstva):
var$name;
var $city;

// Metoda inicijalizacije:
funkcija Init($name) (
$ovo -> ime = $ime ;
$ ovo -> grad = "London" ;
}

}

// Kreirajte objekt klase Coor:
$objekt = noviCoor;
// Da biste inicijalizirali objekt, odmah pozovite metodu:
$objekt -> Init();
?>

Glavna stvar je ne zaboraviti pozvati funkciju odmah nakon stvaranja objekta ili pozvati neku metodu između stvaranja (operator novi) objekt i njegova inicijalizacija (pozivanje U tome).

Kako bi PHP znao da se određena metoda mora automatski pozvati kada se objekt kreira, treba joj dati isto ime kao klasi ( Coor):

funkcija Coord ($name)
$this->name = $name;
$this->city = "London";
}

Metoda koja inicijalizira objekt naziva se konstruktor. Međutim, PHP nema destruktore jer se resursi automatski oslobađaju kada skripte izađu.

Pristup elementima klase

Elementima klase se pristupa pomoću operatora :: "dvotočka" Pomoću "dvotočke" možete pristupiti metodama klase.

Kada pristupa metodama klasa, programer mora koristiti imena tih klasa.

klasa A(
primjer funkcije() (
jeka "Ovo je izvorna funkcija A::example().
"
;
}
}

Klasa B proširuje A (
primjer funkcije() (
jeka "Ovo je nadjačavanje B::example().
"
;
A::primjer();
}
}

// Nema potrebe za stvaranjem objekta klase A.
// Izvodi sljedeće:
// Ovo je izvorna funkcija A::example().
A::primjer();

// Kreirajte objekt klase B.
$b = novi forum B PHP portala. S.U.

Varijable koje su članovi klase nazivaju se "svojstva". Nazivaju se i drugim izrazima kao što su "atributi" ili "polja", ali za potrebe ove dokumentacije nazivat ćemo ih svojstvima. Definiraju se pomoću ključnih riječi javnost, zaštićen ili privatna, slijedeći pravila za ispravnu deklaraciju varijabli. Ova deklaracija može sadržavati inicijalizaciju, ali ta inicijalizacija mora biti konstantna vrijednost, to jest, vrijednost se mora izračunati u vrijeme kompajliranja i ne smije ovisiti o informacijama primljenim u vrijeme izvođenja da bi se izračunala.

Pseudo varijabla $this dostupna je unutar bilo koje metode klase kada se ta metoda pozove iz konteksta objekta. $this je referenca na objekt koji se poziva (obično objekt kojem metoda pripada, ali moguće drugi objekt ako se metoda statički poziva iz konteksta drugog objekta).

Primjer #1 Definiranje svojstava

klasa SimpleClass
{
public $var1 = "bok ". "svijet";
javni $var2 =<<Pozdrav svijete
EOD;
// ispravna definicija svojstva od PHP 5.6.0:
javni $var3 = 1 + 2 ;
// netočna definicija svojstva:
public $var4 = self::myStaticMethod();
public $var5 = $myVar ;

// ispravna definicija svojstva:
public $var6 = mojaKonstanta;
public $var7 = niz(true, false);

// ispravna definicija svojstva od PHP 5.3.0:
javni $var8 =<<<"EOD"
Pozdrav svijete
EOD;
}
?>

Komentar:

Od PHP 5.3.0 i sada se dokumenti mogu koristiti u bilo kojem kontekstu statičkih podataka, uključujući definiranje svojstava.

Primjer #2 Primjer korištenja nowdoc za inicijalizaciju svojstava

klasa foo (
// S PHP 5.3.0
javni $bar =<<<"EOT"
bar
EOT;
javni $baz =<<baz
EOT;
}
?>

Komentar:

Podrška za Nowdoc i Heredoc dodana je u PHP 5.3.0.

prije 7 godina

U slučaju da ovo ikome uštedi vrijeme, godinama sam proveo radeći na tome zašto sljedeće ne radi:

klasa MyClass
{
privatni $foo = FALSE;


{
$this->$foo = TRUE;

Echo($this->$foo);
}
}

$bar = new MyClass();

daje "Fatalna pogreška: Ne može se pristupiti praznom svojstvu u ...test_class.php na retku 8"

Suptilna promjena uklanjanja $ prije pristupa $foo popravlja ovo:

klasa MyClass
{
privatni $foo = FALSE;

Javna funkcija __construct()
{
$this->foo = TRUE;

Echo($this->foo);
}
}

$bar = new MyClass();

Pretpostavljam zato što $foo tretira kao varijablu u prvom primjeru, pa pokušava pozvati $this->FALSE (ili nešto u tom smislu) što nema smisla. Očito je kad shvatite, ali postoji na ovoj stranici nema primjera pristupa koji to pokazuju.

prije 4 godine

Možete pristupiti nazivima svojstava s crticama (na primjer, jer ste pretvorili XML datoteku u objekt) na sljedeći način:

$ref = nova StdClass();
$ref ->( "ref-type" ) = "Članak u časopisu" ;
var_dump ($ref);
?>

prije 8 godina

$ovo se može pretvoriti u niz. Ali kada to radi, nazivima svojstava/novim ključevima niza stavlja prefiks s određenim podacima ovisno o klasifikaciji svojstava. Imena javnih svojstava se ne mijenjaju. Zaštićena svojstva imaju prefiks "*" s razmakom. Privatna svojstva imaju prefiks s imenom klase s razmakom...

Razredni test
{
javni $var1 = 1;
zaštićeno $var2 = 2;
privatni $var3 = 3;
statički $var4 = 4;

Javna funkcija toArray()
{
povratak (niz) $this ;
}
}

$t = novi test ;
print_r($t -> toArray());

/* izlazi:

Niz
=> 1
[ * var2] => 2
[ test var3] => 3
)

*/
?>
Ovo je dokumentirano ponašanje prilikom pretvaranja bilo kojeg objekta u niz (pogledajtestranica priručnika za PHP). Sva svojstva, bez obzira na vidljivost, bit će prikazana prilikom pretvaranja objekta u polje (s iznimkom nekoliko ugrađenih objekata).

Da biste dobili niz sa svim imenima svojstava nepromijenjenim, upotrijebite funkciju "get_object_vars($this)" u bilo kojoj metodi unutar opsega klase za dohvaćanje niza svih svojstava bez obzira na vanjsku vidljivost ili "get_object_vars($object)" izvan opsega klase za dohvaćanje niza samo javnih svojstava (pogledajte:stranica priručnika za PHP).

prije 9 godina

Ne brkajte php-ovu verziju svojstava sa svojstvima u drugim jezicima (C++ na primjer). U php-u, svojstva su ista kao i atributi, jednostavne varijable bez funkcionalnosti. Trebaju se zvati atributi, a ne svojstva.

Svojstva imaju implicitnu funkciju pristupnika i mutatora. Stvorio sam apstraktnu klasu koja dopušta implicitnu funkcionalnost svojstava.

Apstraktna klasa PropertyObject
{
javna funkcija __get ($name)
{
if (metoda_postoji ($this, ($method = "get_" . $name )))
{
vrati $this -> $method ();
}
inače vratiti;
}

Javna funkcija __isset ($name)
{
if (metoda_postoji ($this , ($method = "isset_" . $name )))
{
vrati $this -> $method ();
}
inače vratiti;
}

Javna funkcija __set ($name, $value)
{
if (metoda_postoji ($this , ($method = "set_" . $name )))
{
$ovo -> $metoda ($vrijednost);
}
}

Javna funkcija __unset ($name)
{
if (metoda_postoji ($this, ($method = "unset_" . $name )))
{
$ovo -> $metoda();
}
}
}

?>

nakon proširenja ove klase, možete kreirati pristupnike i mutatore koji će se pozivati ​​automatski, koristeći php-ove čarobne metode, kada se pristupi odgovarajućem svojstvu.

prije 5 godina

Ažurirana metoda objectThis() u svojstva niza klase transtypage ili niz u stdClass.

Nadam se da će vam pomoći.

javna funkcija objectThis($array = null) (
if (!$array) (
foreach ($ovo kao $property_name => $property_values) (
if (is_array($property_values) && !empty($property_values)) (
$this->($property_name) = $this->objectThis($property_values);
) else if (je_niz($property_values) && prazno($property_values)) (
$this->($property_name) = nova stdClass();
}
}
) inače (
$objekt = nova stdClass();
foreach ($array kao $index => $values) (
if (is_array($values) && empty($values)) (
$objekt->($index) = nova stdKlasa();
) else if (is_array($values)) (
$objekt->($index) = $ovo->objektOvaj($vrijednosti);
) inače (
$objekt->($index) = $vrijednosti;
}
}
vratiti $objekt;
}
}

  • Prijevod

Danas se objekti koriste vrlo aktivno, iako je to bilo teško zamisliti nakon izlaska PHP 5 2005. godine. Tada sam još malo znao o mogućnostima ovog jezika. Peta verzija PHP-a uspoređena je s prethodnom, četvrtom, a glavna prednost novog izdanja bio je novi, vrlo moćan objektni model. I danas, deset godina kasnije, oko 90% svog PHP koda sadrži objekte koji se nisu mijenjali od PHP 5.0. Ovo snažno sugerira ulogu koju je imalo uvođenje objektnog modela, koji je više puta poboljšan tijekom sljedećih godina. U ovom postu želio bih govoriti o tome kako sve funkcionira "ispod haube". Tako da ljudi razumiju bit procesa - zašto je to učinjeno na ovaj način, a ne drugačije - i bolje, potpunije koriste mogućnosti jezika. Također ću se dotaknuti teme korištenja memorije od strane objekata, uključujući usporedbu s ekvivalentnim nizovima (kada je to moguće).

Govorit ću na primjeru PHP-a 5.4, a ono što opisujem vrijedi za 5.5 i 5.6, jer tamo struktura objektnog modela nije pretrpjela gotovo nikakve promjene. Imajte na umu da u verziji 5.3 stvari nisu tako dobre u smislu značajki i ukupne izvedbe.

U PHP 7, koji se još uvijek aktivno razvija, objektni model nije puno redizajniran, napravljene su samo manje izmjene. Jednostavno zato što ionako sve dobro funkcionira, a najbolje je neprijatelj dobrog. Dodane su značajke koje ne utječu na kernel, ali o tome se ovdje neće raspravljati.

Kao demonstraciju, počet ću sa sintetičkim referentnim vrijednostima:

Klasa Foo ( public $a = "foobarstring"; public $b; public $c = ["some", "values"]; ) za ($i=0; $i<1000; $i++) { $m = memory_get_usage(); ${"var".$i} = new Foo; echo memory_get_usage() - $m"\n"; }
Ovdje deklariramo jednostavnu klasu s tri atributa, a zatim stvaramo 1000 objekata te klase u petlji. Primijetite kako se memorija koristi u ovom primjeru: kada kreirate objekt klase Foo i varijablu za njegovo pohranjivanje, dodjeljuje se 262 bajta PHP dinamičke memorije.

Zamijenimo objekt s ekvivalentnim nizom:

Za ($i=0; $i<1000; $i++) { $m = memory_get_usage(); ${"var".$i} = [["some", "values"], null, "foobarstring"]; echo memory_get_usage() - $m . "\n"; }
U ovom slučaju koriste se isti elementi: sam niz, null i string varijabla foobarstring. Ali već je potrošeno 1160 bajtova memorije, što je 4,4 puta više.

Evo još jednog primjera:

$klasa =<<<"CL" class Foo { public $a = "foobarstring"; public $b; public $c = ["some", "values"]; } CL; echo memory_get_usage() . "\n"; eval($class); echo memory_get_usage() . "\n";
Budući da se klasa deklarira u vrijeme prevođenja, koristimo operator eval() za deklariranje i mjerenje korištene memorije (koristeći PHP upravitelj memorije). U ovom se slučaju u ovom kodu ne stvaraju objekti. Količina korištene memorije (diff memorija) je 2216 bajtova.

Sada pogledajmo kako sve ovo funkcionira u dubinama PHP-a, podupirući teoriju praktičnim opažanjima.

Sve počinje nastavom

Unutar PHP-a, klasa je predstavljena pomoću strukture zend_class_entry:

Struct _zend_class_entry ( char type; const char *name; zend_uint name_length; struct _zend_class_entry *parent; int refcount; zend_uint ce_flags; HashTable function_table; HashTable properties_info; zval **default_properties_table; zval **default_static_members_table; zval **static_member s_tablica; HashTable konstanta s_tablica; int default_properties_count; int default_static_members_count; union _zend_function *constructor; union _zend_function *destructor; union _zend_function *clone; union _zend_function *__get; union _zend_function *__set; union _zend_function *__unset; union _zend_function *__isset; union _zend_function * __poziv; unija _zend_funkcija *__poziv statički ; unija _zend_funkcija *__tostring; unija _zend_funkcija *serialize_func; unija _zend_function *unserialize_func; zend_class_iterator_funcs iterator_funcs; /* rukovatelji */ zend_object_value (*create_object)(zend_class_entry *class_type TSRMLS_DC); zend_object_iterator *(*get_iterator)(zend_class_ unos *ce, zval * objekt, int by_ref TSRMLS_DC); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* klasa implementira ovo sučelje */ union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC); /* povratni pozivi serijalizatora */ int (*serialize)(zval *object, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); int (*unserialize)(zval **object, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); zend_class_entry **sučelja; zend_uint broj_sučelja; zend_class_entry **osobine; zend_uint broj_osobina; zend_trait_alias **trait_aliasi; zend_trait_precedence **trait_precedence; unija ( struct ( const char *ime_datoteke; zend_uint line_start; zend_uint line_end; const char *doc_comment; zend_uint doc_comment_len; ) korisnik; struct ( const struct _zend_function_entry *builtin_functions; struct _zend_module_entry *module; ) interno; ) info; );
Veličina strukture, temeljena na modelu LP64, je 568 bajtova. To jest, svaki put kad PHP deklarira klasu, prisiljen je stvoriti zend_class_entry, koristeći više od pola kilobajta dinamičke memorije samo za ovu svrhu. Naravno, stvar nije ograničena na ovo: kao što ste primijetili, struktura sadrži mnogo pokazivača koje također treba smjestiti u memoriju. Odnosno, same klase troše puno više memorije nego svi objekti koji su naknadno kreirani iz njih.

Između ostalog, klase sadrže atribute (statičke i dinamičke) kao i metode. Sve to zahtijeva i pamćenje. Što se tiče metoda, teško je izračunati točan odnos, ali jedno je točno: što je veće tijelo metode, to je veći njen OPAarray, što znači da troši više memorije. Ovome dodajte statičke varijable koje se mogu deklarirati u metodi. Zatim dolaze atributi, kasnije će i oni biti smješteni u memoriju. Veličina ovisi o njihovim zadanim vrijednostima: cjelobrojni će zauzeti malo, ali veliki statički niz će pojesti puno memorije.

Važno je znati još jednu točku vezanu uz zend_class_entry - o PHP komentarima. Poznate su i kao bilješke. To su string varijable (u C-u, char* međuspremnici), koje također treba smjestiti u memoriju. Za jezik C, koji ne koristi Unicode, za razliku od PHP-a, pravilo je vrlo jednostavno: jedan znak = jedan bajt. Što više komentara imate u klasi, to će se više memorije koristiti nakon parsiranja.

Za zend_class_entry, polje doc_comment sadrži bilješke klase. Metode i atributi također imaju takvo polje.

Korisničke i unutarnje klase

Korisnička klasa je klasa definirana pomoću PHP-a, dok se unutarnja klasa definira ili ubacivanjem izvornog koda u sam PHP ili korištenjem proširenja. Najveća razlika između ove dvije vrste klasa je u tome što korisnički definirane klase rade na memoriji dodijeljenoj na zahtjev, dok interne klase rade na "postojanoj" memoriji.

To znači da kada PHP završi s obradom trenutnog HTTP zahtjeva, on čisti i uništava sve prilagođene klase u pripremi za obradu sljedećeg zahtjeva. Ovaj pristup je poznat kao "arhitektura dijeljenja ništa". Ovo je ugrađeno u PHP od samog početka i još nema planova da se to mijenja.

Dakle, svaki put kada se generira zahtjev i analiziraju klase, memorija im se dodjeljuje. Nakon korištenja klase, sve povezano s njom se uništava. Stoga budite sigurni da koristite sve deklarirane klase, inače će memorija biti izgubljena. Koristite automatske učitavače, oni odgađaju raščlanjivanje/deklaraciju tijekom izvođenja kada PHP treba koristiti klasu. Iako je izvođenje sporije, autoloader dobro iskorištava memoriju jer se neće pokrenuti dok klasa nije stvarno potrebna.

Drugačije je s unutarnjim klasama. Oni su trajno pohranjeni u memoriji, bez obzira koriste li se ili ne. Odnosno, uništavaju se tek kada sam PHP prestane raditi - nakon što su svi zahtjevi obrađeni (što znači web SAPI, na primjer, PHP-FPM). Stoga su unutarnje klase učinkovitije od prilagođenih (samo statički atributi se uništavaju na kraju zahtjeva, ništa drugo).

If (EG(full_tables_cleanup)) ( zend_hash_reverse_apply(EG(function_table), (apply_func_t) clean_non_persistent_function_full TSRMLS_CC); zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class_full TSRMLS_CC); ) else ( zend_ha sh_reverse_apply( EG(tablica_funkcija), (primjena_func_t) čista_nepostojana_funkcija TSRMLS_CC); zend_hash_reverse_apply(EG(class_table), (apply_func_t) clean_non_persistent_class TSRMLS_CC); ) static int clean_non_persistent_class(zend_class_entry **ce TSRMLS_DC) ( return ((*ce)->type == ZEND_INTERNAL_CLASS) ? ZEND_HASH_APPLY _STOP: ZEND_ HASH_APPLY_REMOVE; )
Imajte na umu da čak i kod predmemoriranja opcodea kao što je OPCache, klasa se stvara i uništava na svaki zahtjev, baš kao i kod prilagođenih klasa. OPCache jednostavno ubrzava oba ova procesa.

Kao što ste primijetili, ako aktivirate mnogo PHP ekstenzija, od kojih svaka deklarira mnogo klasa, ali koristite samo mali broj njih, tada se gubi memorija. Upamtite da PHP proširenja deklariraju klase u trenutku pokretanja PHP-a, čak i ako sljedeći zahtjevi neće koristiti te klase. Stoga se ne preporučuje da ekstenzije budu aktivne ako se trenutno ne koriste jer ćete u protivnom gubiti memoriju. Pogotovo ako ta proširenja deklariraju mnogo klasa - iako mogu začepiti memoriju nečim drugim.

Klase, sučelja ili osobine - nije važno

Za upravljanje klasama, sučeljima i značajkama u PHP-u koristi se ista struktura - zend_class_entry. I kao što ste već vidjeli, ova struktura je prilično glomazna. Ponekad programeri deklariraju sučelja u svom kodu tako da mogu koristiti njihova imena u catch blokovima. To vam omogućuje da uhvatite samo određenu vrstu iznimke. Na primjer, ovako:

Sučelje BarException ( ) class MyException extends Exception implementira BarException ( ) try ( $foo->bar(): ) catch (BarException $e) ( )
Nije baš dobro što se ovdje koristi 912 bajtova samo za deklaraciju BarException sučelja.

$klasa =<<<"CL" interface Bar { } CL; $m = memory_get_usage(); eval($class); echo memory_get_usage() - $m . "\n"; /* 912 bytes */
Ne želim reći da je ovo loše ili glupo, ne pokušavam nikoga i ništa kriviti. Samo vam skrećem pozornost na ovu točku. Sa stajališta unutarnje strukture PHP-a, klase, sučelja i značajke koriste se na potpuno isti način. Ne možete dodati atribute sučelju; parser ili kompajler vam to jednostavno neće dopustiti. Međutim, struktura zend_class_entry još uvijek postoji, samo što brojna polja, uključujući static_members_table, neće biti pokazivači dodijeljeni memoriji. Deklariranje klase, ekvivalentne osobine ili ekvivalentnog sučelja zahtijevat će istu količinu memorije budući da svi koriste istu strukturu.

Uvezivanje razreda

Mnogi programeri ne razmišljaju o klasnom povezivanju dok se ne počnu pitati kako stvari zapravo funkcioniraju. Vezanje klase može se opisati kao "proces kojim se sama klasa i svi njeni povezani podaci pripremaju za punu upotrebu od strane programera." Ovaj proces je vrlo jednostavan i ne zahtijeva puno resursa ako govorimo o jednoj klasi koja ne proširuje drugu, ne koristi značajke i ne implementira sučelje. Proces vezivanja za takve klase događa se u potpunosti u vrijeme kompajliranja, i nikakvi resursi se ne troše na to tijekom izvođenja. Imajte na umu da smo govorili o povezivanju klase koju je deklarirao korisnik. Za interne klase, isti se postupak provodi kada se klase registriraju s PHP jezgrom ili ekstenzijama, neposredno prije pokretanja korisničkih skripti - a to se radi samo jednom tijekom cijelog vremena izvođenja PHP-a.

Stvari se jako kompliciraju kada se radi o implementaciji sučelja ili nasljeđivanju klasa. Zatim, tijekom vezanja klasa, apsolutno sve se kopira iz nadređenih i podređenih objekata (bilo da su klase ili sučelja).

/* Jedna klasa */ case ZEND_DECLARE_CLASS: if (do_bind_class(CG(active_op_array), opline, CG(class_table), 1 TSRMLS_CC) == NULL) ( return; ) table = CG(class_table); pauza;
U slučaju jednostavne deklaracije klase, pokrećemo do_bind_class() . Ova funkcija jednostavno registrira potpuno definiranu klasu u tablici klasa za kasniju upotrebu tijekom izvođenja, a također provjerava moguće apstraktne metode:

Void zend_verify_abstract_class(zend_class_entry *ce TSRMLS_DC) ( zend_abstract_info ai; if ((ce->ce_flags & ZEND_ACC_IMPLICIT_ABSTRACT_CLASS) && !(ce->ce_flags & ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) ( memset(&ai, 0, sizeof(ai) )); z end_hash_apply_with_argument(&ce - >function_table, (apply_func_arg_t) zend_verify_abstract_class_function, &ai TSRMLS_CC); if (ai.cnt) ( zend_error(E_ERROR, "Klasa %s sadrži %d apstraktnu metodu%s i stoga se mora proglasiti apstraktnom ili implementirati preostale metode (" MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT MAX_ABSTRACT_INFO_FMT ")", ce->name, ai.cnt, ai.cnt > 1 ? "s" : "", DISPLAY_ABSTRACT_FN(0), DISPLAY_ABSTRACT_FN(1), DISPLAY_ABSTRACT_FN(2)); ) ) )
Nema se tu što dodati, jednostavan slučaj.

Kada vežete klasu koja implementira sučelje, trebate učiniti sljedeće:

  • Provjerite je li sučelje već deklarirano.
  • Provjerite je li tražena klasa stvarno klasa, a ne samo sučelje (kao što je gore spomenuto, s točke gledišta unutarnje strukture strukturirani su isto).
  • Kopirajte konstante iz sučelja u klasu, provjeravajući moguće kolizije.
  • Kopirajte metode iz sučelja u klasu, provjeravajući moguće kolizije i nedosljednosti u deklaraciji (na primjer, pretvaranje metoda sučelja u statične u klasi dijete).
  • Dodajte sučelje i sva moguća matična sučelja na popis sučelja implementiranih od strane klase.
Pod "kopiranjem" ne mislimo na potpunu duboku kopiju. Za konstante, atribute i funkcije provodi se ponovni izračun kako bi se utvrdilo koliko ih entiteta u memoriji koristi.

ZEND_API void zend_do_implement_interface(zend_class_entry *ce, zend_class_entry *iface TSRMLS_DC) ( /* ... ... */ ) else ( if (ce->num_interfaces >= current_iface_num) ( if (ce->type == ZEND_INTERNAL_CLASS) ( ce ->sučelja = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num)); ) else ( ce->interfaces = (zend_class_entry **) erealloc(ce->interfaces, sizeof (zend_class_entry *) * (++current_iface_num)); ) ) ce->interfaces = iface; zend_hash_merge_ex(&ce->constants_table, &iface->constants_table, (copy_ctor_func_t) zval_add_ref, sizeof(zval *), (merge_checker_func_t) do_inherit_constant_check, iface ); zend_hash_merge_ex(&ce->function_table, &iface->function_table, (copy_ctor_func_t) do_inherit_method, sizeof(zend_function), (merge_checker_func_t) do_inherit_method_check, ce); do_implement_interface(ce, iface TSRMLS_CC); zend_do_inherit_interfaces(ce, iface TS RMLS_CC); ) )
Obratite pažnju na razliku između internih i korisničkih klasa. Prvi će koristiti realloc() za dodjelu memorije, drugi će koristiti erealloc(). realloc() dodjeljuje "trajnu" memoriju, dok erealloc() radi s memorijom "na zahtjev".

Možete vidjeti da kada se spoje dvije konstantne tablice (interface-1 i class-1), one to čine pomoću povratnog poziva zval_add_ref. Ne kopira konstante iz jedne tablice u drugu, već dijeli njihove pokazivače, jednostavno dodajući broj referenci.

Za svaku od tablica funkcija (metoda) koristi se do_inherit_method:

Static void do_inherit_method(zend_function *function) ( function_add_ref(function); ) ZEND_API void function_add_ref(zend_function *function) ( if (function->type == ZEND_USER_FUNCTION) ( zend_op_array *op_array = &function->op_array; (*op_array->refcount) )++; if (op_array->static_variables) ( HashTable *statičke_varijable = op_array->static_variables; zval *tmp_zval; ALLOC_HASHTABLE(op_array->static_variables); zend_hash_init(op_array->static_variables, zend_hash_num_elements(statičke_varijable), NULL, ZVAL _PTR_DTOR, 0 ); zend_hash_copy(op_array->static_variables, static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *)); ) op_array->run_time_cache = NULL; ) )
Refcount se dodaje u OPAarray funkcije, a sve moguće statičke varijable deklarirane u funkciji (ovdje metoda) kopiraju se pomoću zval_add_ref. Dakle, cijeli proces kopiranja zahtijeva puno računalnih resursa jer je uključeno mnogo petlji i provjera. Ali koristi se malo memorije. Nažalost, vezanje sučelja danas je potpuno procurilo tijekom izvođenja i to ćete osjetiti na svakom zahtjevu. Možda će programeri to uskoro promijeniti.

Što se tiče nasljeđivanja, ovdje je, u načelu, sve isto kao kod implementacije sučelja. Samo je uključeno više "sudionika". Ali želim napomenuti da ako PHP već zna za klasu, onda se povezivanje vrši u vrijeme kompajliranja, a ako ne zna, onda u vrijeme izvođenja. Stoga je bolje deklarirati ga ovako:

/* dobro */ klasa A ( ) klasa B proširuje A ( )
umjesto:

/* loše */ klasa B proširuje A ( ) klasa A ( )
Usput, rutinska procedura vezanja klase može dovesti do vrlo čudnog ponašanja:

/* ovo radi */ klasa B proširuje A ( ) klasa A ( )

/* ali ovo nije */ Fatalna pogreška: klasa "B" nije pronađena */ klasa C proširuje B ( ) klasa B proširuje A ( ) klasa A ( )

U prvoj opciji, vezanje klase B je odgođeno do vremena izvođenja, jer kada prevodilac dođe do deklaracije ove klase, još ne zna ništa o klasi A. Kada započne izvođenje, vezanje klase A se događa bez pitanja, jer je već kompiliran, budući da je jednostruka klasa. U drugom slučaju sve je drugačije. Vezanje klase C odgođeno je tijekom izvođenja jer prevodilac još ne zna ništa o B kada ga pokušava prevesti. Ali kada je klasa C vezana za vrijeme izvođenja, ona traži B, koji ne postoji jer nije kompajliran jer je B komplementaran. Pojavljuje se poruka "Klasa B ne postoji".

Predmeti

Sada znamo da:
  • Nastava zauzima puno memorije.
  • Unutarnje klase su puno bolje optimizirane od korisničkih klasa jer se potonje moraju kreirati i uništiti na svaki zahtjev. Unutarnje klase postoje cijelo vrijeme.
  • Klase, sučelja i značajke koriste istu strukturu i procedure, razlike su vrlo male.
  • Tijekom nasljeđivanja ili deklaracije, proces vezivanja oduzima puno CPU vremena, ali koristi malo memorije jer se mnoge stvari dijele, a ne dupliciraju. Također, bolje je pokrenuti vezanje klase tijekom kompilacije.

Sada razgovarajmo o objektima. Prvo poglavlje pokazuje da je stvaranje "klasičnog" objekta ("klasične" korisničke klase) zahtijevalo vrlo malo memorije, oko 200 bajtova. Sve je u razredu. Daljnja kompilacija klase također troši memoriju, ali to je bolje jer je potrebno manje bajtova za stvaranje jednog objekta. U biti, objekt je sićušna zbirka sićušnih struktura.

Metode upravljanja objektima

Na razini motora, metode i funkcije su ista stvar - struktura zend_function_structure. Samo se imena razlikuju. Metode su kompilirane i dodane u atribut table_function u zend_class_entry. Stoga je tijekom izvođenja svaka metoda prezentirana, samo je stvar prijenosa pokazivača na izvođenje.

Typedef unija _zend_funkcija ( zend_uchar tip; struktura ( zend_uchar tip; const char *funkcija_name; zend_class_entry *scope; zend_uint fn_flags; unija _zend_function *prototype; zend_uint num_args; zend_uint required_num_args; zend_arg_info *arg_info; ) common; zend_op_array op_niz; zend_unutarnja_funkcija unutarnja_funkcija; ) zend_funkcija ;
Kada objekt pokuša pozvati metodu, motor prema zadanim postavkama gleda u tablicu funkcija klase objekta. Ako metoda ne postoji, poziva se __call(). Također se provjerava vidljivost - javno/zaštićeno/privatno - ovisno o tome koje su sljedeće radnje poduzete:

Statička unija _zend_function *zend_std_get_method(zval **object_ptr, char *metod_name, int method_len, const zend_literal *key TSRMLS_DC) ( zend_function *fbc; zval *object = *object_ptr; zend_object *zobj = Z_OBJ_P(object); ulong hash_value; char * lc_method_name; ALLOCA_FLAG(use_heap) if (EXPECTED(key != NULL)) ( lc_method_name = Z_STRVAL(key->constant); hash_value = key->hash_value; ) else ( lc_method_name = do_alloca(method_len+1, use_heap); zend_str_tolower_copy( lc_method_name, method_name, method_len); hash_value = zend_hash_func(lc_method_name, method_len+1); ) /* Ako metoda nije pronađena */ if (NEOČEKIVANO(zend_hash_quick_find(&zobj->ce->function_table, lc_method_name, method_len+1, hash_value) , (void **)&fbc) == FAILURE)) ( if (UNEXPECTED(!key)) ( free_alloca(lc_method_name, use_heap); ) if (zobj->ce->__call) ( /* ako klasa ima __call() rukovatelj */ return zend_get_user_call_function(zobj->ce, method_name, method_len); /* poziva __call() rukovatelj */ ) else ( return NULL; /* else vraća NULL, što će vjerojatno dovesti do fatalne pogreške: metoda nije pronađena */ ) ) /* Provjerite razinu pristupa */ if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) ( zend_function *updated_fbc; updated_fbc = zend_check_private_int(fbc , Z_OBJ_HANDLER_P(objekt, get_class_entry)(objekt TSRMLS_CC), lc_method_name, method_len, hash_value TSRMLS_CC); if (EXPECTED(updated_fbc != NULL)) ( fbc = updated_fbc; ) else ( if (zobj->ce->__call) ( fbc = zend_get_user_call_function(zobj->ce, method_name, method_len); ) else ( zend_error_noreturn(E_ERROR, "Poziv %s metode %s::%s() iz konteksta "%s"", zend_visibility_string(fbc->common.fn_flags ), ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope) ? EG(scope)->name: ""); ) ) ) else ( /* ... ... */ )
Možda ste primijetili zanimljivu stvar, pogledajte prve retke:

If (EXPECTED(key != NULL)) ( lc_method_name = Z_STRVAL(key->constant); hash_value = key->hash_value; ) else ( lc_method_name = do_alloca(method_len+1, use_heap); /* Kreirajte zend_copy_str_tolower(dest, src, src_length); */ zend_str_tolower_copy(lc_method_name, method_name, method_len); hash_value = zend_hash_func(lc_method_name, method_len+1); )
Ovo je manifestacija PHP-ove neosjetljivosti na velika i mala slova. Sustav svaku funkciju prvo mora napisati malim slovima (zend_str_tolower_copy()) prije nego što je pozove. Ne baš sve, ali one gdje je prisutna naredba if. Varijabla ključ sprječava izvršavanje funkcije koja pretvara u mala slova (dio else) - ovo je dio PHP optimizacije implementirane u verziji 5.4. Ako poziv metode nije dinamičan, tada je kompajler već izračunao ključ i manje resursa se troši tijekom izvođenja.

Klasa Foo ( javna funkcija BAR() ( ) ) $a = novi Foo; $b = "bar"; $a->bar(); /* statički poziv: dobar */ $a->$b(); /* dinamički poziv: loš */
Kada se funkcija/metoda prevede, odmah se pretvara u mala slova. Gornju funkciju BAR() prevodilac pretvara u bar() kada dodaje metodu u tablicu klasa i funkcija.

U gornjem primjeru, prvi poziv je statičan: prevodilac je izračunao ključ za niz "traka", a kada dođe vrijeme za pozivanje metode, ima manje posla. Drugi poziv je već dinamičan, kompajler ne zna ništa o “$b” i ne može izračunati ključ za pozivanje metode. Zatim, tijekom izvođenja, moramo konvertirati string u mala slova i izračunati njegov hash (zend_hash_func()), što nema najbolji utjecaj na performanse.

Što se tiče __call() , ne smanjuje toliko performanse. Međutim, ova metoda troši više resursa nego pozivanje postojeće funkcije.

Upravljanje atributima objekta

Evo što se događa:

Kao što možete vidjeti, kada se stvori više objekata iste klase, mehanizam preusmjerava svaki atribut na isti pokazivač kao u slučaju atributa klase. Tijekom svog života klasa pohranjuje ne samo vlastite statičke atribute, već i atribute objekata. U slučaju internih klasa - tijekom cijelog vremena rada PHP-a. Stvaranje objekta ne uključuje stvaranje njegovih atributa, tako da je ovo prilično brz i isplativ pristup. Samo kada objekt treba promijeniti jedan od svojih atributa, motor stvara novi za njega, pod pretpostavkom da mijenjate $a atribut Foo #2:

Dakle, kada stvaramo objekt, mi "samo" stvaramo strukturu zend_object od 32 bajta:

Typedef struct _zend_object ( zend_class_entry *ce; HashTable *properties; zval **properties_table; HashTable *guards; /* štiti od __get/__set ... rekurzije */ ) zend_object;
Ova se struktura dodaje u pohranu objekata. A ovo je pak struktura zend_object_store. Ovo je Zendov globalni registar objekata - mjesto gdje se svi objekti prikupljaju i pohranjuju u jednoj instanci:

ZEND_API zend_object_value zend_objects_new(zend_object **object, zend_class_entry *class_type TSRMLS_DC) ( zend_object_value retval; *object = emalloc(sizeof(zend_object)); (*object)->ce = class_type; (*object)->properties = NULL; ( *object)->properties_table = NULL; (*object)->guards = NULL; /* Dodajte objekt u pohranu */ retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_free_object_storage_t) zend_o bjects_free_object_stor starost, NULL TSRMLS_CC); retval.handlers = &std_object_handlers; return retval; )
Zatim, motor stvara vektor značajki za naš objekt:

ZEND_API void object_properties_init(zend_object *object, zend_class_entry *class_type) ( int i; if (class_type->default_properties_count) ( object->properties_table = emalloc(sizeof(zval*) * class_type->default_properties_count); for (i = 0; i< class_type->broj zadanih_svojstava; i++) ( object->properties_table[i] = class_type->default_properties_table[i]; if (class_type->default_properties_table[i]) ( #if ZTS ALLOC_ZVAL(object->properties_table[i]); MAKE_COPY_ZVAL(&class_type->default_properties_table [i], object->properties_table[i]); #else Z_ADDREF_P(object->properties_table[i]); #endif ) ) object->properties = NULL; ) )
Kao što vidite, dodijelili smo tablicu/vektor (kao u C) memoriju za zval* na temelju deklariranih svojstava klase objekta. U slučaju non-thread-safe PHP-a, jednostavno dodajemo refcount atributu, a ako se koristi thread-safe Zend (ZTS, Zend thread safety), tada moramo u potpunosti kopirati zval. Ovo je jedan od mnogih primjera koji potvrđuju niske performanse i veliki intenzitet resursa ZTS načina u usporedbi s PHP-om koji nije ZTS.

Vjerojatno imate dva pitanja:

  • Koja je razlika između properties_table i svojstava u strukturi zend_object?
  • Ako stavimo atribute našeg objekta u C-vektor, kako ih možemo vratiti? Pregledati vektor svaki put (što smanjuje izvedbu)?

Odgovor na oba pitanja donosi zend_property_info.

Typedef struct _zend_property_info ( zend_uint zastavice; const char *name; int name_length; ulong h; int offset; const char *doc_comment; int doc_comment_len; zend_class_entry *ce; ) zend_property_info;
Svaki deklarirano atribut (svojstvo) našeg objekta ima odgovarajuće informacije o svojstvu dodane u polje property_info u zend_class_entry. To se radi tijekom kompilacije atributa deklariranih u klasi:

Klasa Foo ( public $a = "foo"; protected $b; private $c; ) struct _zend_class_entry ( /* ... ... */ HashTable function_table; HashTable properties_info; /* ovdje su informacije o svojstvima o $a, $b i $c */ zval **default_properties_table; /* i ovdje ćemo pronaći $a, $b i $c s njihovim zadanim vrijednostima */ int default_properties_count; /* ovo će imati vrijednost 3: 3 nekretnine */ /* ... ... */
Properties_infos je tablica koja govori objektu postoji li traženi atribut. A ako postoji, tada prosljeđuje svoj indeksni broj u polju object->properties. Zatim provjeravamo vidljivost i pristup opsegu (javno/zaštićeno/privatno).

Ako atribut ne postoji i moramo pisati u njega, tada možemo pokušati pozvati __set(). U slučaju neuspjeha, stvaramo dinamički atribut koji će biti pohranjen u polje object->property_table.

Property_info = zend_get_property_info_quick(zobj->ce, member, (zobj->ce->__set != NULL), ključ TSRMLS_CC); if (EXPECTED(property_info != NULL) && ((EXPECTED((property_info->flags & ZEND_ACC_STATIC) == 0) && property_info->offset >= 0) ? (zobj->properties ? ((variable_ptr = (zval**) )zobj->properties_table) != NULL) : (*(variable_ptr = &zobj->properties_table) != NULL)) : (EXPECTED(zobj->properties != NULL) && EXPECTED(zend_hash_quick_find(zobj->properties, property_info- >name, property_info->name_length+1, property_info->h, (void **) &variable_ptr) == SUCCESS)))) ( /* ... ... */ ) else ( zend_guard *guard = NULL; if (zobj->ce->__set && /* klasa ima __set() ? */ zend_get_property_guard(zobj, property_info, member, &guard) == USPJEH && !guard->in_set) ( Z_ADDREF_P(objekt); if (PZVAL_IS_REF( objekt)) ( SEPARATE_ZVAL(&object); ) guard->in_set = 1; /* spriječi kružno postavljanje */ if (zend_std_call_setter(objekt, član, vrijednost TSRMLS_CC) != USPJEH) ( /* poziv __set() */ ) čuvar ->in_set = 0; zval_ptr_dtor(&object); /* ... ... */
Sve dok ne pišete u objekt, njegova potrošnja memorije se ne mijenja. Nakon snimanja zauzima više prostora (dok se ne uništi), budući da sadrži sve atribute koji su u njega upisani.

Objekti koji se ponašaju kao reference zahvaljujući pohranjivanju objekata

Objekti nisu reference. Ovo je prikazano u maloj skripti:

Funkcija foo($var) ( $var = 42; ) $o = new MyClass; foo($o); var_dump($o); /* ovo je još uvijek objekt, a ne cijeli broj 42 */
Sada će svi reći da su "u PHP 5 objekti poveznice", čak i službeni priručnik to spominje. Tehnički ovo je potpuno netočno. Međutim, objekti se mogu ponašati na isti način kao reference. Na primjer, kada varijablu koja je objekt proslijedite funkciji, ta funkcija može modificirati taj isti objekt.

To je zato što zval proslijeđen kao funkcija ne prosljeđuje sam objekt, već njegov jedinstveni identifikator, koji se koristi za pretraživanje dijeljene pohrane objekata. A rezultat je isti. Moguće je dodijeliti tri različita zval-a u memoriju, a svi mogu sadržavati isti rukovatelj objektom.

Object(MyClass)#1 (0) ( ) /* #1 je ručica objekta (broj), jedinstvena je */

Zend_object_store omogućuje jednokratno pohranjivanje objekata u memoriju. Jedini način pisanja u pohranu je stvaranje novog objekta s ključnom riječi new, unserialize() funkcijom, API-jem refleksije ili ključnom riječi clone. Niti jedna druga operacija vam neće omogućiti dupliciranje ili stvaranje novog objekta u trgovini.

Typedef struct _zend_objects_store ( zend_object_store_bucket *object_buckets; zend_uint vrh; zend_uint veličina; int free_list_head; ) zend_objects_store; typedef struct _zend_object_store_bucket ( zend_bool destructor_ called; zend_bool valid; zend_uchar apply_count; union _store_bucket ( struct _store_object ( void *object; zend_objects_store_dtor_t dtor; zend_objects_free_object_storage_t free_storage; zend_object s_store_clone_t klon; const zend_object_handlers *handleri; zend_uint refcount; gc_root_buffer *buffered; ) obj; struct ( int next; ) free_list; ) bucket; ) zend_object_store_bucket;

Što je ovo?

Razumijevanje $this nije toliko teško, ali postoje dijelovi koda povezani s ovim alatom na nekoliko mjesta u stroju: u kompajleru, u kodu za dobivanje varijabli tijekom izvođenja, itd. $ovo se pojavljuje i nestaje po potrebi, automatski se pridružujući trenutnom objektu - općenito, "čarobna" stvar. A interni kod savršeno omogućuje upravljanje njime.

Prvo, kompajler neće dopustiti pisanje u $this. Da bi to učinio, provjerit će svaku dodjelu koju napravite, i ako pronađe dodjelu za $this, izbacit će fatalnu pogrešku.

/* ... ... */ if (opline_is_fetch_this(last_op TSRMLS_CC)) ( zend_error(E_COMPILE_ERROR, "Cannot re-assign $this"); ) /* ... ... */ static zend_bool opline_is_fetch_this(const zend_op *opline TSRMLS_DC) ( if ((opline->opcode == ZEND_FETCH_W) && (opline->op1_type == IS_CONST) && (Z_TYPE(CONSTANT(opline->op1.constant)) == IS_STRING) && ((opline-> extended_value & ZEND_FETCH_STATIC_MEMBER) != ZEND_FETCH_STATIC_MEMBER) && (Z_HASH_P(&CONSTANT(opline->op1.constant)) == THIS_HASHVAL) && (Z_STRLEN(CONSTANT(opline->op1.constant)) == (sizeof("this")- 1)) && !memcmp(Z_STRVAL(CONSTANT(opline->op1.constant)), "this", sizeof("this"))) ( return 1; ) else ( return 0; ) )
Kako se $ovo upravlja? Njegova je upotreba moguća samo unutar metode, tijekom čijeg poziva kompajler generira OPCode INIT_METHOD_CALL. Motor zna tko poziva metodu, u slučaju $a->foo() to je $a. Tada se vrijednost $a dohvaća i pohranjuje u zajednički prostor. Zatim se metoda poziva koristeći OPCode DO_FCALL. U ovoj točki, pohranjena vrijednost se ponovno dohvaća (objekt poziva metodu) i dodjeljuje se globalnom internom $this pokazivaču - EG(This) .

If (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) ( should_change_scope = 1; EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_ called_scope) = EG( called_scope); EG(This) = EX(object); /* dohvati objekt pripremljen u prethodnom kodu INIT_METHOD i utječe na njega u EG(This) */ EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX (objekt)) ? fbc->common.scope: NULL; EG(nazvani_opseg) = EX(poziv)->nazvani_opseg; )
Sada, kada se metoda pozove, ako u njenom tijelu koristite $this za djelovanje na varijablu ili pozivanje metode (npr. $this->a = 8), tada će to rezultirati OPCodeom ZEND_ASSIGN_OBJ, koji će zauzvrat dohvatiti $ ovo natrag iz EG(This) .

Statički zend_always_inline zval **_get_obj_zval_ptr_ptr_unused(TSRMLS_D) ( if (EXPECTED(EG(This) != NULL)) ( return &EG(This); ) else ( zend_error_noreturn(E_ERROR, "Upotreba $this kada nije u kontekstu objekta"); return NULL;))
U slučaju da ste upotrijebili $this za pozivanje metode (na primjer, $this->foo()) ili proslijedili drugi poziv funkcije ($this->foo($this);), tada će motor pokušati izdvojiti $this iz trenutne tablice simbola kao što to čini za svaku standardnu ​​varijablu. Ali ovdje se provodi posebna priprema tijekom stvaranja okvira stoga trenutne funkcije:

If (op_array->this_var != -1 && EG(This)) ( Z_ADDREF_P(EG(This)); if (!EG(active_symbol_table)) ( EX_CV(op_array->this_var) = (zval **) EX_CV_NUM(execute_data , op_array->last_var + op_array->this_var); *EX_CV(op_array->this_var) = EG(This); ) else (if (zend_hash_add(EG(active_symbol_table), "this", sizeof("this"), &EG (This), sizeof(zval *), (void **) EX_CV_NUM(execute_data, op_array->this_var))==FAILURE) ( Z_DELREF_P(EG(This)); ) ) )
Kada pozovemo metodu, motor mijenja opseg:

If (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) ( /* ... ... */ EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX(objekt)) fbc->common.scope: NULL; /* ... ... */ )
EG(scope) je tipa zend_class_entry. Ovo je klasa koja posjeduje metodu koju tražite. I koristit će se za sve operacije na objektu koje ćete izvesti u tijelu metode nakon provjere vidljivosti motora:

Static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) ( switch (property_info->flags & ZEND_ACC_PPP_MASK) ( case ZEND_ACC_PUBLIC: return 1; case ZEND_ACC_PROTECTED: return zend_check_protected(pro) perty _info->ce, EG(scope)); slučaj ZEND_ACC_PRIVATE : if ((ce==EG(scope) || property_info->ce == EG(scope)) && EG(scope)) ( return 1; ) else ( return 0; ) break; ) return 0; )
Ovako možete pristupiti privatnim članovima objekata koji vam ne pripadaju, ali su djeca vašeg trenutnog opsega:

Klasa A ( privatni $a; javna funkcija foo(A $obj) ( $this->a = "foo"; $obj->a = "bar"; /* da, ovo je moguće */ ) ) $a = novi A; $b = novo A; $a->foo($b);
Ova je značajka uzrokovala veliki broj izvješća o programskim pogreškama od strane programera. Ali ovo je način na koji objektni model funkcionira u PHP-u - zapravo, mi postavljamo opseg na temelju ne objekta, već klase. U slučaju naše klase "Foo", možete raditi s bilo kojim privatnim Foo ili bilo kojim drugim Fooom, kao što je prikazano gore.

O destruktoru

Destruktori su opasni, nemojte se oslanjati na njih jer ih PHP ne poziva čak ni u slučaju fatalne pogreške:

Klasa Foo ( javna funkcija __destruct() ( echo "byebye foo"; ) ) $f = new Foo; ova funkcija ne postoji(); /* kobna pogreška, funkcija nije pronađena, Fooov destruktor NIJE pokrenut */
Što je s redoslijedom kojim se destruktori pozivaju ako se ipak pozovu? Odgovor je jasno vidljiv u kodu:

Void shutdown_destructors(TSRMLS_D) ( zend_try ( int symbols; do ( symbols = zend_hash_num_elements(&EG(symbol_table)); zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC); ) while (symbols != zend_hash_num_elements (&EG(tablica_simbola) ) ); zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC); ) zend_catch ( /* ako nismo mogli čisto uništiti, svejedno označimo sve objekte kao uništene */ zend_objects_store_mark_destructed(&EG(objects_store) TSRMLS_CC); ) zend_end_try(); ) static int z val_destruktor_poziva (zval **zv TSRMLS_DC) (if (Z_TYPE_PP(zv) == IS_OBJECT && Z_REFCOUNT_PP(zv) == 1) ( vrati ZEND_HASH_APPLY_REMOVE; ) else ( vrati ZEND_HASH_APPLY_KEEP; ) )
Ovdje su prikazane tri faze pozivanja destruktora:

  • Prođite kroz tablicu globalnih simbola unatrag i pozovite destruktore za objekte koji imaju refcount = 1.
  • Zatim se smjer cikličnosti mijenja, a destruktori se pozivaju za sve ostale objekte, s brojem ref > 1.
  • Ako se problem pojavi u jednoj od prethodnih faza, poziv preostalim destruktorima se prekida.
Do čega to dovodi:

Klasa Foo ( javna funkcija __destruct() ( var_dump("uništen Foo"); ) )
klasa Bar ( javna funkcija __destruct() ( var_dump("uništena traka"); ))

Primjer jedan:

$a = novi Foo; $b = nova traka; "uništio Bar" "uništio Foo"
Isti primjer:

$a = nova traka; $b = novi Foo; "uništio Foo" "uništio Bar"
Drugi primjer:

$a = nova traka; $b = novi Foo; $c = $b; /* povećava $b's object refcount */ "destroyed Bar" "destroyed Foo"
Primjer tri:

Klasa Foo ( javna funkcija __destruct() ( var_dump("destroyed Foo"); die();) ) /* primijetite die() ovdje */ class Bar ( javna funkcija __destruct() ( var_dump("destroyed Bar"); ) ) $a = novi Foo; $a2 = $a; $b = nova traka; $b2 = $b; uništio Foo
Ovaj postupak je odabran s razlogom. Ali ako vam to ne odgovara, onda je bolje da sami uništite svoje predmete. Ovo je jedini način za kontrolu poziva __destruct() . Ako dopustite PHP-u da to učini umjesto vas, nemojte se uzrujati zbog rezultata. Uvijek imate mogućnost ručno uništiti svoje objekte kako biste imali potpunu kontrolu nad redoslijedom.

PHP ne poziva destruktore u slučaju bilo kakve fatalne pogreške. Činjenica je da je u ovom slučaju Zend nestabilan, a pozivanje destruktora dovodi do izvršavanja korisničkog koda, koji može pristupiti pogrešnim pokazivačima i, kao rezultat, srušiti PHP. Bolje je održavati stabilnost sustava - zato je pozivanje destruktora blokirano. Možda će se nešto promijeniti u PHP 7.

Što se tiče rekurzija, one su u PHP-u slabo zaštićene, a to se odnosi samo na __get() i __set() . Ako uništite svoj objekt negdje u okviru steka destruktora, završit ćete u beskonačnoj rekurzivnoj petlji koja će pojesti sve resurse steka vašeg procesa (obično 8 kB, ulimit -s) i pokvariti PHP.

Klasa Foo ( javna funkcija __destruct() ( new Foo; ) /* srušit ćete se */ )
Ukratko, ne vjerujte destruktorima s kritičnim kodom, kao što je kontrola mehanizma zaključavanja, jer PHP možda neće pozvati destruktor ili ga može pozvati u nekontroliranom nizu. Ako se važan kod i dalje obrađuje destruktorom, onda barem sami kontrolirajte životni ciklus objekata. PHP će pozvati destruktor kada refcount vašeg objekta padne na nulu, što znači da se objekt više ne koristi i može se sigurno uništiti.

Zaključak

Nadam se da vam je sada puno toga postalo jasnije u svakodnevnom radu s predmetima. Ne troše puno memorije, a njihova implementacija na razini motora je dobro optimizirana. Pokušajte koristiti dobro dizajniran autoloader kako biste poboljšali korištenje memorije. Deklarirajte klase prema redoslijedu logičkog nasljeđivanja, a ako najsloženije od njih pretvorite u C ekstenzije, možete optimizirati mnoge procese, pa čak i dodatno povećati ukupnu izvedbu takvih klasa.

U ovom ćete vodiču naučiti osnove objektno orijentiranog programiranja u PHP-u. Naučit ćete općenito o načelima OOP-a i naučiti kako pisati jednostavne skripte u PHP-u.

Dobrodošli u prvu u nizu lekcija o OOP-u u PHP-u! Dovršavajući sve lekcije u ovoj seriji, naučit ćete o osnovnim principima i konceptima OOP-a i naučiti kako brzo i jednostavno kreirati korisne aplikacije u PHP-u.

U ovom tutorijalu počet ću vas upoznavati i govoriti o osnovnim konceptima OOP-a. Naučit ćeš:

  • što je OOP
  • kako će vam OOP pomoći da stvorite bolje PHP skripte
  • neke osnovne koncepte kao što su klase, objekti, metode, varijable klase
  • gdje početi pisati PHP skriptu

Jeste li spremni uroniti u svijet PHP objekata? Onda samo naprijed!

Što je objektno orijentirano programiranje?

Ako ste ikada stvorili i koristili prilagođene funkcije u PHP-u, koristili ste proceduralni stil programiranja. U proceduralnom programiranju obično stvarate strukture podataka - brojeve, nizove, nizove itd. - pohraniti neke podatke, a zatim obraditi te strukture posebnim funkcijama koje manipuliraju tim podacima.

Objektno orijentirano programiranje, ili OOP, napredovalo je jer ovdje pohranjujemo strukture podataka i funkcije koje ih obrađuju u jednu cjelinu koja se zove objekt. Umjesto obrade podataka nekom funkcijom, te podatke učitavate u objekt, a zatim pozivate njegove metode da njima manipulirate i dobijete željeni rezultat.

Najčešće, objekti stvoreni pomoću OOP-a odražavaju stvarne entitete. Na primjer, ako stvarate forum za svoju stranicu, izradili biste objekt Član koji će pohranjivati ​​podatke o svakom članu foruma (ime, prijavu, e-poštu, lozinku itd.), kao i metode koje će obraditi te informacije ( registracija, autorizacija, odjava, zabrana itd.).

Zašto koristiti OOP?

Proceduralni i objektno orijentirani dva su različita načina da se radi ista stvar. To ne znači da je jedan od njih bolji od drugog - svatko piše kako mu se sviđa, pa čak možete lako kombinirati ova dva pristupa u jednom scenariju.

Međutim, evo nekih prednosti OOP-a za programere:

  • Lakše je odražavati stvarne situacije: kao što sam gore napomenuo, objekti odražavaju stvarne entitete - ljude, proizvode, kartice, članke na blogu itd. Ovo uvelike pojednostavljuje zadatak kada tek počinjete dizajnirati svoju aplikaciju, budući da je svrha svakog objekta je kao cilj odnosi između objekata će biti jasni i razumljivi.
  • Lakše je pisati modularne programe: OOP uključuje pisanje modula. Podjelom koda na module bit će vam lakše upravljati njime, ispravljati pogreške i proširivati ​​ga.
  • Lakše je napisati kod koji će se koristiti mnogo puta: Pisanje koda koji se može koristiti više puta uštedjet će vrijeme pri pisanju aplikacije, a s vremenom čak možete stvoriti cijelu biblioteku ovih vrsta modula koje možete koristiti u mnogim aplikacije. S OOP-om postaje relativno lakše napisati takav kod jer su podatkovne strukture i funkcije kapsulirane u jedan objekt koji se može koristiti neograničeni broj puta.

Neki osnovni pojmovi

Prije nego počnete pisati skripte, morate dobro razumjeti koncepte klase, objekta, varijable klase i metode.

Nastava

Klasa je okvir za objekt. Ovo je dio koda koji definira:

  • Tipovi podataka koje će kreirani objekti klase sadržavati
  • Funkcije koje će sadržavati te objekte.

Kada kreirate OOP aplikaciju, obično ćete stvoriti nekoliko klasa koje će predstavljati različite vrste entiteta u vašoj aplikaciji. Na primjer, da biste stvorili forum, možete stvoriti klase Forum, Tema, Post i Član.

Predmeti

Objekt je posebna vrsta varijable koja se stvara kroz klasu. Sadrži stvarne podatke i funkcije za rukovanje njima. Iz jedne klase možete stvoriti onoliko objekata koliko želite. Svaka funkcija objekta neovisna je o drugom objektu, čak i ako su stvoreni iz iste klase.

Za usporedbu sa stvarnim entitetima:

  • Klasa je okvir za automobil: ona definira kako će automobil izgledati i ponašati se, ali je još uvijek apstraktan entitet
  • Objekt je pravi automobil stvoren od žičanog okvira: ima stvarna svojstva (kao što je brzina) i ponašanje (kao što je ubrzanje ili kočenje).

Napomena: Objekt se često naziva bit klase, a proces stvaranja objekta klase naziva se implementacija.

Varijable klase

Vrijednosti podataka koje su pohranjene u određenom objektu zapisuju se u posebne varijable koje se nazivaju varijablama klase. Varijable klase usko su povezane s objektom. Iako svi objekti klase imaju iste varijable, njihove vrijednosti se mogu razlikovati.

Metode

Funkcije definirane u klasi i korištene na objektima te klase nazivaju se metode. Ne razlikuju se mnogo od uobičajenih funkcija - možete im proslijediti vrijednosti, mogu sadržavati lokalne varijable i vraćati vrijednosti. Međutim, metode češće rade s objektnim varijablama. Na primjer, metoda login() za prijavu korisnika na vaš forum može postaviti varijablu klase loggedIn na true.

Kako napraviti klasu u PHP-u?

Sada kada već znate što su klase, metode, varijable klase i objekti, vrijeme je da stvorite nekoliko klasa i objekata u PHP kodu.

Prvo, pogledajmo kako zapravo stvoriti klasu. U osnovi, skripta za kreiranje klase izgleda ovako:

Klasa ClassName ( // (definicija klase) )

Na primjer, ako stvarate klasu članova za svoj forum, napisali biste ovo:

Član klase ( // (definicija klase) )

Sasvim je jednostavno. Naravno, ova klasa neće učiniti ništa dok joj ne dodate varijable i metode. Međutim, gornji kôd stvara valjanu PHP klasu koja se može koristiti.

Dobro pravilo: svaku klasu smjestite u zasebnu datoteku s istim imenom kao i ime klase. Na primjer, stavite klasu Member u datoteku Member.php i pohranite je u mapu, recimo, classes.

Kako kreirati objekte u PHP-u?

Možete stvoriti objekt koristeći novu ključnu riječ:

Novi ClassName()

Ovaj kod će stvoriti objekt klase ClassName. Kasnije ćete morati koristiti ovaj objekt, pa ga morate pohraniti u varijablu. Na primjer, stvorimo objekt klase Member i pohranimo ga u varijablu $member:

$član = novi član();

Također možemo stvoriti drugi objekt iste klase:

$član2 = novi član();

Iako smo ova dva objekta stvorili iz iste klase, varijable $member i $member2 neovisne su jedna o drugoj.

Stvorite varijable klase

Sada kada već znamo kako stvoriti klase i objekte klase, pogledajmo kako stvoriti varijable klase. Postoje 3 pristupnika za varijable klase koji se mogu dodati klasi:

  • Javne varijable klase (public): dostupne - tj. mogu se čitati i/ili mijenjati - bilo gdje u skripti, bez obzira gdje se ovaj kod nalazi - unutar klase ili izvan nje
  • Privatne varijable klase (private): dostupne samo metodama klase. Najbolje je varijable klase učiniti privatnima kako biste odvojili objekte od ostatka koda.
  • Zaštićene varijable klase: Dostupne metodama vaše vlastite klase, kao i metodama naslijeđenih klasa (o nasljeđivanju ćemo govoriti kasnije).

Da biste stvorili varijablu klase, napišite ključnu riječ public, private ili protected, a zatim unesite naziv varijable:

Klasa ClassName ( javno $propertyName; privatno $propertyName; zaštićeno $propertyName; )

Dodajmo javnu varijablu klase našoj klasi Član za pohranu korisničkog imena:

Član razreda ( public $username = ""; )

Primijetite da smo inicijalizirali našu varijablu klase, njena vrijednost je prazan niz, “”. To znači da će, kada se stvori novi korisnik, korisničko ime prema zadanim postavkama biti prazan niz. Baš kao i u slučaju običnih varijabli u PHP-u, varijable klase ne moraju se inicijalizirati, ali bolje je ne biti lijen. Ako ne inicijalizirate varijablu klase, njezina zadana vrijednost je null.

Pristup varijablama klase

Za pristup varijabli objekta upotrijebite operator ->:

$objekt->naziv svojstva

Pokušajmo. Napišimo skriptu koja deklarira klasu Member i varijablu klase, stvara objekt te klase, a zatim postavlja vrijednost varijable klase i prikazuje je na ekranu:

korisničko ime = "Fred"; echo $član->korisničko ime; // Ispis "Fred" ?>

Pokrenite ovaj kod, on će prikazati niz "Fred", vrijednost varijable klase $member->username. Kao što vidite, varijablom objekta upravljate baš kao i običnom varijablom - možete je postaviti na vrijednost i pročitati je.

Dodavanje metoda u klasu

Što je s metodama stvaranja? Kao što sam ranije spomenuo, metode su samo regularne funkcije koje su dio klase. Stoga se možda nećete iznenaditi što su stvorene pomoću iste ključne riječi funkcije. Jedina razlika u odnosu na stvaranje običnih funkcija je da možete dodati jedan od identifikatora pristupa (javno, privatno, zaštićeno) u njegovu deklaraciju. Na ovaj način, metode su slične varijablama klase:

Klasa ClassName ( javna funkcija methodName() ( // (code) ) privatna funkcija methodName() ( // (code) ) zaštićena funkcija methodName() ( // (code) ) )

Napomena: baš kao u slučaju varijabli klase, javne metode mogu se pozvati s bilo kojeg mjesta, privatne metode mogu se pozvati samo unutar klase, a zaštićene metode mogu se pozvati iz same klase i njezinog potomka.

Pokušajmo dodati neke metode i varijable klase našoj klasi:

  • privatna varijabla klase $loggedIn za identifikaciju korisnika, tj. bio on ušao ili ne
  • login(), koja će se prijaviti na forum postavljanjem varijable klase $loggedIn na true,
  • metodu logout(), koja će se odjaviti s foruma postavljanjem varijable klase $loggedIn na false,
  • metodu isLoggedIn(), koja će vratiti vrijednost varijable klase $loggedIn.

Evo našeg koda:

loggedIn = istina; ) javna funkcija odjava() ( $this->loggedIn = false; ) javna funkcija isLoggedIn() ( return $this->loggedIn; ) ) ?>

Možda ste primijetili da smo upotrijebili novu ključnu riječ $this. U kontekstu metoda objekta, posebna varijabla $this se odnosi na sam objekt. Korištenjem $this u metodi objekta, metoda može pristupiti bilo kojoj varijabli klase i metodi objekta.

Na primjer, metoda login() može pristupiti varijabli klase $loggedIn objekta preko $this->loggedIn.

Usput, naša varijabla klase je privatna, pa se ne može pozvati iz bilo kojeg dijela skripte, već samo iz metoda login(), logout() i isLoggedIn(). Ovo je dobar pristup jer je unutrašnjost objekta (kao što je kako točno bilježi je li korisnik prijavljen ili ne) odvojena od ostatka koda. Kad god je moguće, pokušajte koristiti varijable privatne klase kako bi vaši objekti bili autonomni, mobilni i zaštićeni.

Napomena: varijabla klase $username u našem primjeru je javna. Učinio sam ovo samo da pokažem kako možete pristupiti varijablama klase objekta. U stvarnim projektima radije biste ovu varijablu učinili privatnom i izradili posebne javne varijable klase za postavljanje vrijednosti korisničkog imena ako je potrebno.

Korištenje metoda

Za pozivanje metode na objektu koristite operator -> s kojim ste se već upoznali.

$objekt->metodName()

Ovo funkcionira kao pozivanje obične funkcije. Argumente možete proslijediti u zagradama (naravno, pod pretpostavkom da su potrebni argumenti), a poziv metode također može vratiti određene vrijednosti koje zatim možete koristiti.

loggedIn = istina; ) javna funkcija odjava() ( $this->loggedIn = false; ) javna funkcija isLoggedIn() ( return $this->loggedIn; ) ) $member = new Member(); $member->username = "Fred"; echo $member->username . "je". ($član->
"; $member->login(); echo $member->username . " je " . ($member->isLoggedIn() ? "prijavljen" : "odjavljen") . "
"; $member->logout(); echo $member->username . " je " . ($member->isLoggedIn() ? "prijavljen" : "odjavljen") . "
"; ?>

Ova skripta će prikazati sljedeće:

Fred je odjavljen Fred je prijavljen Fred je odjavljen

Evo kako to funkcionira:

  1. Nakon što smo opisali klasu Member, kreirali smo njen objekt i pohranili ga u varijablu $member. Također smo varijabli klase $username ovog objekta dali vrijednost “Fred”.
  2. Zatim smo pozvali metodu $member->isLoggedIn() kako bismo utvrdili je li korisnik prijavljen ili ne. Ova metoda jednostavno vraća vrijednost varijable klase $loggedIn. Budući da je zadana vrijednost ove varijable klase false, rezultat pozivanja $member->isLoggedIn() bit će false, pa će se prikazati poruka "Fred je odjavljen".
  3. Zatim pozivamo metodu login(). Postavit će varijablu klase $loggedIn na true.
  4. Sada, prilikom pozivanja metode $member->isLoggedIn(), ona će vratiti true i prikazati poruku "Fred je prijavljen".
  5. Pozovimo metodu logout(), koja vrijednost svojstva $loggedIn postavlja na false.
  6. Pozovimo metodu $member->isLoggedIn() treći put. Sada će vratiti false jer je svojstvo $loggedIn ponovno postavljeno na false. Dakle, ponovno će se prikazati poruka "Fred je odjavljen".

Napomena: u slučaju da ste ovo prvi vidjeli: ?: je ternarni operator. Ovo je pojednostavljena verzija if...else blokova. Možete naučiti o ovim vrstama operatora.

zaključke

U ovom vodiču naučili ste osnove OOP-a u PHP-u. Naučili ste o stvarima poput:

  • Što je OOP i zašto je koristan?
  • koncepti klasa, objekata, varijabli klase i metoda
  • kako kreirati klase i objekte
  • kako stvoriti i koristiti varijable klase
  • koncepti javnih, privatnih, zaštićenih identifikatora pristupa
  • kako stvoriti i koristiti metode klase

Već ste naučili puno o tome, a naučit ćete puno više u sljedećim lekcijama. Međutim, ako ste proradili kroz sve primjere koje sam dao, imate čvrst temelj. Možete početi stvarati aplikacije pomoću OOP-a.

Najbolji članci na temu