Cum se configurează smartphone-uri și PC-uri. Portal informativ
  • Acasă
  • OS
  • Crearea unui obiect în php. Obiecte

Crearea unui obiect în php. Obiecte

Clase și obiecte în PHP

Clasă este un concept de bază în programarea orientată pe obiecte (OOP). Clasele formează baza sintactică a OOP. Ele pot fi considerate ca un fel de „containere” pentru date și funcții legate logic (denumite în mod obișnuit - vezi mai jos). Pentru a spune simplu, o clasă este un fel de tip de date.

O instanță a unei clase este un obiect. Un obiect este o colecție de date () și funcții (metode) pentru prelucrarea acestora. Sunt numite proprietăți și metode. În general, un obiect este orice lucru care acceptă încapsularea.

Dacă o clasă poate fi considerată ca un tip de date, atunci un obiect poate fi considerat ca o variabilă (prin analogie). Scriptul poate lucra simultan cu mai multe obiecte din aceeași clasă, ca și cu mai multe variabile.

În cadrul unui obiect, datele și codul (membrii clasei) pot fi fie publice, fie nu. Datele publice și membrii clasei sunt accesibile altor părți ale programului care nu fac parte din obiect. Dar datele private și membrii clasei sunt disponibile numai în interiorul acestui obiect.

Descrierile claselor în PHP încep cu un cuvânt funcție clasă:

clasa ClassName (
// descrierea membrilor clasei - proprietăți și metode de procesare a acestora
}

Pentru a declara un obiect trebuie să utilizați operatorul nou:

Obiect = nou ClassName;

Datele sunt descrise folosind un cuvânt funcțional var. Metoda este descrisă în același mod ca o funcție obișnuită definită de utilizator. De asemenea, puteți transmite parametri metodei.

Să rezumam: declarația de clasă trebuie să înceapă cu cuvântul cheie clasă(similar cu modul în care o declarație de funcție începe cu cuvântul cheie funcţie). Fiecare declarație de proprietate conținută într-o clasă trebuie să fie precedată de un cuvânt cheie var. Proprietățile pot fi orice tip de date acceptate de PHP și pot fi considerate variabile cu diferențe minore. Declarațiile de proprietăți sunt urmate de declarații de metodă, care sunt foarte asemănătoare cu declarațiile tipice de funcții definite de utilizator.

Conform regulilor general acceptate, numele claselor OOP încep cu o literă majusculă, iar toate cuvintele din numele metodelor, cu excepția primului, încep cu litere mari (primul cuvânt începe cu o literă mică). Desigur, puteți folosi orice notație considerați convenabilă; principalul lucru este să alegeți un standard și să vă respectați.

Exemplu de clasă în PHP:

// Creați o nouă clasă Coor:
clasa Coor (
// date (proprietăți):
var$nume;
var $addr;

// metode:
funcția Nume() (
ecou"

Ioan

" ;
}

}


$obiect = newCoor;
?>

Accesarea claselor și obiectelor în PHP

Ne-am uitat la modul în care sunt descrise clasele și cum sunt create obiectele. Acum trebuie să accesăm membrii clasei, în acest scop operatorul este furnizat în PHP -> . Iată un exemplu:

// Creați o nouă clasă Coor:
clasa Coor (
// date (proprietăți):
var$nume;

// metode:
funcția Getname() (
ecou"

Ioan

" ;
}

}

// Creați un obiect din clasa Coor:
$obiect = newCoor;
// Obțineți acces la membrii clasei:
$ obiect -> nume = "Alex" ;
echo $obiect -> nume ;
// Tipărește „Alex”
// Acum să accesăm metoda clasei (de fapt, o funcție în interiorul clasei):
$obiect -> Getname();
// Tipărește „John” cu majuscule
?>

Pentru a accesa membrii clasei din cadrul unei clase, trebuie să utilizați un pointer $aceasta, care se referă întotdeauna la obiectul curent. Metoda modificată Getname():

funcția Getname() (
echo $this->name;
}

În același mod, puteți scrie o metodă Pune un nume():

funcția Setname($nume) (
$acest->nume = $nume;
}

Acum puteți folosi metoda pentru a schimba numele Pune un nume():

$obiect->Setname(„Petru”);
$obiect->Getname();

Și iată lista completă a codurilor:

// Creați o nouă clasă Coor:
clasa Coor (
// date (proprietăți):
var$nume;

// metode:
funcția Getname() (
echo $this -> nume ;
}

funcția Setname($nume) (
$acest -> nume = $nume ;
}

}

// Creați un obiect din clasa Coor:
$obiect = newCoor;
// Acum pentru a schimba numele folosim metoda Setname():
$obiect -> Setname("Nick");
// Și pentru acces, ca și înainte, Getname():
$obiect -> Getname();
// Scenariul afișează „Nick”
?>

Indicator $aceasta poate fi folosit și pentru a accesa metode, nu doar acces la date:

funcția Setname($nume) (
$acest->nume = $nume;
$this->Getname();
}

Constructorii

Destul de des, atunci când creați un obiect, trebuie să setați valorile unor proprietăți. Din fericire, dezvoltatorii tehnologiei OOP au luat în considerare această circumstanță și au implementat-o ​​în concept. Un constructor este o metodă care stabilește valorile unor proprietăți (și poate apela și alte metode). Constructorii sunt apelați automat atunci când sunt create obiecte noi. Pentru a face acest lucru posibil, numele metodei constructorului trebuie să se potrivească cu numele clasei care o conține. Exemplu de constructor:

pagina web a clasei (
var $bgcolor;
funcția Pagina web($culoare)(
$this -> bgcolor = $culoare ;
}
}

// Apelați constructorul clasei Webpage
$pagină = pagină web nouă(„maro”);
?>

Anterior, crearea obiectelor și inițializarea proprietăților erau efectuate separat. Designerii vă permit să efectuați aceste acțiuni într-un singur pas.

Un detaliu interesant: în funcție de numărul de parametri trecuți, pot fi apelați diferiți constructori. În exemplul considerat, obiectele clasei Pagină web poate fi creat în două moduri. În primul rând, puteți apela un constructor, care pur și simplu creează obiectul, dar nu își inițializează proprietățile:

$pagină = pagină web nouă;

În al doilea rând, un obiect poate fi creat folosind un constructor definit într-o clasă - în acest caz, creați un obiect al clasei Webpage și atribuiți o valoare proprietății sale bgcolor:

$pagină = pagină web nouă(„maro”);

Distructori

Nu există suport direct în PHP. Cu toate acestea, puteți simula cu ușurință un destructor apelând funcția PHP unset(). Această funcție distruge conținutul unei variabile și returnează resursele pe care le-a ocupat sistemului. Cu obiecte unset() funcționează la fel ca și cu variabilele. Să presupunem că lucrezi cu un obiect $Pagina web. După terminarea lucrului cu acest obiect special, funcția se numește:

unset($Webpage);

Această comandă șterge tot conținutul din memorie $Pagina web. În spiritul încapsulării, puteți efectua un apel unset()într-o metodă numită distruge()și apoi numiți-o:

$Website->distruge();

Necesitatea de a apela destructori apare doar atunci când lucrați cu obiecte care folosesc o cantitate mare de resurse, deoarece toate variabilele și obiectele sunt distruse automat la finalizarea scriptului.

Inițializarea obiectelor

Uneori este nevoie de a inițializa un obiect - de a atribui proprietățile sale valorilor inițiale. Să presupunem că numele clasei este Coorși conține două proprietăți: numele persoanei și orașul reședinței sale. Puteți scrie o metodă (funcție) care va inițializa un obiect, de exemplu Init():

// Creați o nouă clasă Coor:
clasa Coor (
// date (proprietăți):
var$nume;
var $oras;

// Metoda de inițializare:
funcția Init($nume) (
$acest -> nume = $nume ;
$ this -> city = "Londra" ;
}

}

// Creați un obiect din clasa Coor:
$obiect = newCoor;
// Pentru a inițializa obiectul, apelați imediat metoda:
$obiect -> Init();
?>

Principalul lucru este să nu uitați să apelați funcția imediat după crearea obiectului sau să apelați o metodă între creare (operator nou) obiect și inițializarea acestuia (apel Init).

Pentru ca PHP să știe că o anumită metodă trebuie apelată automat atunci când este creat un obiect, trebuie să i se dea același nume ca și clasa ( Coor):

funcția Coord ($nume)
$acest->nume = $nume;
$this->city = "Londra";
}

Metoda care inițializează un obiect se numește constructor. Cu toate acestea, PHP nu are destructori, deoarece resursele sunt eliberate automat la iesirea scripturilor.

Accesarea elementelor de clasă

Elementele de clasă sunt accesate cu ajutorul operatorului :: „colon dublu” Folosind „punul dublu” puteți accesa metodele de clasă.

La accesarea metodelor de clase, programatorul trebuie să folosească numele acestor clase.

clasa a (
exemplu de funcție() (
ecou „Aceasta este funcția originală A::example().
"
;
}
}

Clasa B se extinde pe A (
exemplu de funcție() (
ecou „Aceasta este o înlocuire a lui B::example().
"
;
A::exemplu();
}
}

// Nu este nevoie să creați un obiect din clasa A.
// Afișează următoarele:
// Aceasta este funcția originală A::example().
A::exemplu();

// Creați un obiect din clasa B.
$b = forum nou portal B PHP. S.U.

Variabilele care sunt membre ale unei clase se numesc „proprietăți”. Ele sunt denumite și folosind alți termeni precum „atribute” sau „câmpuri”, dar în scopul acestei documentații, ne vom referi la ei drept proprietăți. Ele sunt definite folosind cuvinte cheie public, protejat sau privat, urmând regulile pentru declararea corectă a variabilelor. Această declarație poate conține o inițializare, dar această inițializare trebuie să fie o valoare constantă, adică valoarea trebuie calculată în momentul compilării și nu trebuie să depindă de informațiile primite în timpul execuției pentru a le calcula.

$this pseudo variabilă este disponibilă în interiorul oricărei metode de clasă atunci când acea metodă este apelată dintr-un context de obiect. $aceasta este o referință la obiectul care este apelat (de obicei, obiectul căruia îi aparține metoda, dar eventual un alt obiect dacă metoda este apelată static din contextul unui al doilea obiect).

Exemplul #1 Definirea proprietăților

clasa SimpleClass
{
public $var1 = "bună ziua " . "lume";
public $var2 =<<Salut Lume
EOD;
// definiția corectă a proprietății începând cu PHP 5.6.0:
public $var3 = 1 + 2 ;
// definiție incorectă a proprietății:
public $var4 = self::myStaticMethod();
public $var5 = $myVar ;

// definirea corectă a proprietăților:
public $var6 = myConstant ;
public $var7 = array(true, false);

// definiția corectă a proprietății începând cu PHP 5.3.0:
public $var8 =<<<"EOD"
Salut Lume
EOD;
}
?>

cometariu:

Deoarece PHP 5.3.0 și nowdocs pot fi utilizate în orice context de date statice, inclusiv definirea proprietăților.

Exemplul #2 Exemplu de utilizare a nowdoc pentru a inițializa proprietăți

clasa foo (
// Cu PHP 5.3.0
public $bar =<<<"EOT"
bar
EOT;
public $baz =<<baz
EOT;
}
?>

cometariu:

Suportul Nowdoc și Heredoc a fost adăugat în PHP 5.3.0.

acum 7 ani

În cazul în care acest lucru salvează pe cineva vreodată, am petrecut o mulțime de ani determinând de ce următoarele nu au funcționat:

clasa MyClass
{
private $foo = FALSE;


{
$this->$foo = TRUE;

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

$bar = new MyClass();

dând „Eroare fatală: Nu se poate accesa proprietatea goală în ...test_class.php pe linia 8”

Modificarea subtilă a eliminării $ înainte de accesarea $foo remediază acest lucru:

clasa MyClass
{
private $foo = FALSE;

Funcția publică __construct()
{
$this->foo = TRUE;

Echo($this->foo);
}
}

$bar = new MyClass();

Presupun că pentru că tratează $foo ca pe o variabilă în primul exemplu, așa că încerc să apelezi $this->FALSE (sau ceva de genul acesta), ceea ce nu are sens. Este evident odată ce ți-ai dat seama, dar acolo nu există exemple de accesare pe această pagină care să arate asta.

acum 4 ani

Puteți accesa numele proprietăților cu liniuțe în ele (de exemplu, deoarece ați convertit un fișier XML într-un obiect) în felul următor:

$ref = new StdClass();
$ref ->( "ref-type" ) = "Articol de jurnal" ;
var_dump ($ref);
?>

acum 8 ani

$this poate fi turnat în matrice. Dar când face acest lucru, prefixează numele proprietăților/cheile de matrice noi cu anumite date în funcție de clasificarea proprietăților. Numele proprietăților publice nu sunt modificate. Proprietățile protejate sunt prefixate cu un „*” cu spațiu. Proprietățile private sunt prefixate cu numele clasei cu spațiu...

Test de clasă
{
public $var1 = 1 ;
protejat $var2 = 2 ;
private $var3 = 3 ;
static $var4 = 4 ;

Funcția publică toArray()
{
return (matrice) $this ;
}
}

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

/* iese:

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

*/
?>
Acesta este un comportament documentat la conversia oricărui obiect într-o matrice (vezipagina de manual PHP). Toate proprietățile, indiferent de vizibilitate, vor fi afișate atunci când proiectați un obiect în matrice (cu excepția câtorva obiecte încorporate).

Pentru a obține o matrice cu toate numele proprietăților nemodificate, utilizați funcția „get_object_vars($this)” în orice metodă din domeniul de aplicare al clasei pentru a prelua o matrice cu toate proprietățile, indiferent de vizibilitatea externă, sau „get_object_vars($object)” în afara domeniului clasei pentru preluați o serie de numai proprietăți publice (vezi:pagina de manual PHP).

acum 9 ani

Nu confundați versiunea php a proprietăților cu proprietăți în alte limbi (de exemplu C++). În php, proprietățile sunt aceleași cu atributele, variabile simple fără funcționalitate. Ar trebui să fie numite atribute, nu proprietăți.

Proprietățile au funcționalitate implicită accesor și mutator. Am creat o clasă abstractă care permite funcționalitatea proprietăților implicite.

Clasa abstractă PropertyObject
{
funcția publică __get ($nume)
{
dacă (metoda_există ($aceasta , ($metoda = "get_" . $nume )))
{
returnează $aceasta -> $metodă ();
}
altfel reveni;
}

Funcția publică __isset ($nume)
{
dacă (metoda_există ($aceasta , ($metoda = "isset_" . $nume )))
{
returnează $aceasta -> $metodă ();
}
altfel reveni;
}

Funcția publică __set ($nume, $valoare)
{
dacă (metoda_există ($aceasta , ($metoda = "set_" . $nume )))
{
$aceasta -> $metoda ($valoare );
}
}

Funcția publică __unset ($nume)
{
dacă (metoda_există ($aceasta , ($metoda = "unset_" . $nume )))
{
$aceasta -> $metoda();
}
}
}

?>

după extinderea acestei clase, puteți crea accesori și mutatori care vor fi apelați automat, folosind metodele magice php, atunci când este accesată proprietatea corespunzătoare.

acum 5 ani

Metoda objectThis() actualizată la proprietățile matricei clasei transtypage sau matricea la stdClass.

Sper că te ajută.

funcția publică obiectAcest($array = null) (
dacă (!$array) (
foreach ($this ca $property_name => $property_values) (
if (este_matrice($property_values) && !empty($property_values)) (
$this->($property_name) = $this->objectThis($property_values);
) altfel if (este_matrice($property_values) && empty($property_values)) (
$this->($property_name) = new stdClass();
}
}
) altfel (
$obiect = new stdClass();
foreach ($array ca $index => $valori) (
if (este_matrice($valori) && gol($valori)) (
$obiect->($index) = new stdClass();
) else if (este_matrice($valori)) (
$obiect->($index) = $this->objectThis($valori);
) altfel (
$obiect->($index) = $valori;
}
}
returnează $obiect;
}
}

  • Traducere

Astăzi obiectele sunt folosite foarte activ, deși acest lucru a fost greu de imaginat după lansarea PHP 5 în 2005. Pe vremea aceea, știam încă puține despre capacitățile acestui limbaj. A cincea versiune de PHP a fost comparată cu anterioară, a patra, iar principalul avantaj al noii versiuni a fost un model de obiect nou, foarte puternic. Și astăzi, zece ani mai târziu, aproximativ 90% din tot codul PHP conține obiecte care nu s-au schimbat de la PHP 5.0. Acest lucru sugerează cu tărie rolul jucat de introducerea modelului obiect, care a fost îmbunătățit în mod repetat în anii următori. În această postare aș vrea să vorbesc despre cum funcționează totul „sub capotă”. Pentru ca oamenii să înțeleagă esența proceselor - de ce s-a făcut așa și nu altfel - și mai bine, să folosească mai pe deplin capacitățile limbajului. Voi aborda, de asemenea, subiectul utilizării memoriei de către obiecte, inclusiv în comparație cu matrice echivalente (când este posibil).

Voi vorbi folosind exemplul PHP 5.4, iar lucrurile pe care le descriu sunt valabile pentru 5.5 și 5.6, deoarece structura modelului obiect de acolo nu a suferit aproape nicio modificare. Vă rugăm să rețineți că în versiunea 5.3 lucrurile nu sunt la fel de bune în ceea ce privește caracteristicile și performanța generală.

În PHP 7, care este încă în curs de dezvoltare activ, modelul obiect nu a fost prea mult reproiectat, s-au făcut doar modificări minore. Pur și simplu pentru că oricum totul funcționează bine, iar cel mai bun este dușmanul binelui. Au fost adăugate caracteristici care nu afectează nucleul, dar acest lucru nu va fi discutat aici.

Ca o demonstrație, voi începe cu repere sintetice:

Clasa Foo ( public $a = "foobarstring"; public $b; public $c = ["unele", "valori"]; ) pentru ($i=0; $i<1000; $i++) { $m = memory_get_usage(); ${"var".$i} = new Foo; echo memory_get_usage() - $m"\n"; }
Aici declarăm o clasă simplă cu trei atribute, apoi creăm 1000 de obiecte din această clasă într-o buclă. Observați cum se folosește memoria în acest exemplu: când creați un obiect din clasa Foo și o variabilă pentru a-l stoca, sunt alocați 262 de octeți de memorie dinamică PHP.

Să înlocuim obiectul cu o matrice echivalentă:

Pentru ($i=0; $i<1000; $i++) { $m = memory_get_usage(); ${"var".$i} = [["some", "values"], null, "foobarstring"]; echo memory_get_usage() - $m . "\n"; }
În acest caz, se folosesc aceleași elemente: matricea în sine, null și variabila șir foobarstring . Dar 1160 de octeți de memorie sunt deja consumați, adică de 4,4 ori mai mult.

Iată un alt exemplu:

$clasa =<<<"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";
Deoarece clasa este declarată în momentul compilării, folosim operatorul eval() pentru a declara și măsura memoria utilizată (folosind managerul de memorie PHP). În acest caz, nu sunt create obiecte în acest cod. Cantitatea de memorie utilizată (memorie diferită) este de 2216 octeți.

Acum să ne uităm la cum funcționează toate acestea în profunzimea PHP, susținând teoria cu observații practice.

Totul începe cu cursurile

În interiorul PHP, o clasă este reprezentată folosind structura zend_class_entry:

Struct _zend_class_entry ( tipul de caractere; const char *nume; 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_static;_membersaulttable **; s_table; default_properties_count; union _zend_function *constructor union _zend_function *__isset _zend_function *__tostring *serialize_func_func_cs; ject_value(*create_object)(zend_class_entry *class_type TSRMLS_DC); *ce, zval *obiect, int by_ref TSRMLS_DC); int (*interface_gets_implemented)(zend_class_entry *iface, zend_class_entry *class_type TSRMLS_DC); /* o clasă implementează această interfață */ union _zend_function *(*get_static_method)(zend_class_entry *ce, char* method, int method_len TSRMLS_DC); /* callbacks serializator */ int (*serialize)(zval *obiect, unsigned char **buffer, zend_uint *buf_len, zend_serialize_data *data TSRMLS_DC); int (*unserialize)(zval **obiect, zend_class_entry *ce, const unsigned char *buf, zend_uint buf_len, zend_unserialize_data *data TSRMLS_DC); zend_class_entry **interfețe; zend_uint num_interfaces; zend_class_entry **trăsături; zend_uint num_traits; zend_trait_alias **trait_aliases; zend_trait_precedence **trait_precedences; union ( struct ( const char *nume fișier; zend_uint line_start; zend_uint line_end; const char *doc_comment; zend_uint doc_comment_len; ) utilizator; struct ( const struct _zend_function_entry *builtin_functions; struct _zend_module;) info intern *;module_entry; );
Dimensiunea structurii, bazată pe modelul LP64, este 568 de octeți. Adică, de fiecare dată când PHP declară o clasă, este forțat să creeze un zend_class_entry , folosind mai mult de jumătate de kilobyte de memorie dinamică numai în acest scop. Desigur, problema nu se limitează la asta: după cum ați observat, structura conține mulți indicatori care trebuie de asemenea plasați în memorie. Adică, clasele în sine consumă mult mai multă memorie decât toate obiectele create ulterior din ele.

Printre altele, clasele conțin atribute (statice și dinamice), precum și metode. Toate acestea necesită și memorie. În ceea ce privește metodele, este dificil de calculat relația exactă, dar un lucru este adevărat: cu cât corpul metodei este mai mare, cu atât OPArray-ul său este mai mare, ceea ce înseamnă cu atât mai multă memorie consumă. Adăugați la aceasta variabile statice care pot fi declarate într-o metodă. Urmează atributele, mai târziu vor fi și ele plasate în memorie. Mărimea depinde de valorile lor implicite: cele întregi vor ocupa puțin, dar o matrice statică mare va consuma multă memorie.

Este important să știți despre încă un lucru legat de zend_class_entry - comentariile PHP. Sunt cunoscute și sub denumirea de adnotări. Acestea sunt variabile de tip șir (în C, buffer-uri char*), care trebuie de asemenea plasate în memorie. Pentru limbajul C, care nu folosește Unicode, spre deosebire de PHP, regula este foarte simplă: un caracter = un octet. Cu cât aveți mai multe adnotări într-o clasă, cu atât mai multă memorie va fi folosită după parsare.

Pentru zend_class_entry, câmpul doc_comment conține adnotări de clasă. Metodele și atributele au și ele un astfel de câmp.

Utilizator și clase interne

O clasă de utilizator este o clasă definită folosind PHP, în timp ce o clasă interioară este definită fie prin injectarea codului sursă în PHP, fie prin utilizarea unei extensii. Cea mai mare diferență dintre aceste două tipuri de clase este că clasele definite de utilizator funcționează pe memorie alocată la cerere, în timp ce clasele interne operează pe memorie „persistentă”.

Aceasta înseamnă că, atunci când PHP termină de procesare a cererii HTTP curente, elimină și distruge toate clasele personalizate din memorie în vederea procesării următoarei solicitări. Această abordare este cunoscută sub denumirea de „arhitectura „share nothing”. Acesta a fost încorporat în PHP de la bun început și încă nu există planuri de schimbare.

Deci, de fiecare dată când este generată o cerere și clasele sunt analizate, li se alocă memorie. După utilizarea unei clase, tot ceea ce este asociat cu aceasta este distrus. Prin urmare, asigurați-vă că utilizați toate clasele declarate, altfel memoria va fi irosită. Folosiți încărcătoare automate, acestea întârzie analizarea/declararea în timpul rulării atunci când PHP trebuie să folosească clasa. Deși execuția este mai lentă, încărcătorul automat folosește bine memoria, deoarece nu va rula până când clasa este cu adevărat necesară.

Este diferit cu clasele interioare. Sunt stocate permanent în memorie, indiferent dacă sunt folosite sau nu. Adică, ele sunt distruse numai atunci când PHP-ul în sine încetează să funcționeze - după ce toate cererile au fost procesate (adică web SAPI, de exemplu, PHP-FPM). Prin urmare, clasele interne sunt mai eficiente decât cele personalizate (numai atributele statice sunt distruse la sfârșitul solicitării, nimic altceva).

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_CCC); ly (EG(function_table), (apply_func_t) clean_non_persistent_function 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_PASS_H_APPLY) APPLY_REMOVE;
Rețineți că, chiar și cu opcode cache precum OPCache, clasa este creată și distrusă la fiecare solicitare, la fel ca în cazul claselor personalizate. OPCache pur și simplu accelerează ambele procese.

După cum ați observat, dacă activați multe extensii PHP, fiecare dintre ele declarând multe clase, dar folosiți doar un număr mic dintre ele, memoria este irosită. Amintiți-vă că extensiile PHP declară clase la momentul pornirii PHP, chiar dacă solicitările ulterioare nu vor folosi acele clase. Prin urmare, nu este recomandat să păstrați extensiile active dacă nu sunt utilizate în prezent, altfel veți irosi memorie. Mai ales dacă aceste extensii declară multe clase - deși pot înfunda memoria cu altceva.

Clase, interfețe sau trăsături - nu contează

Pentru a gestiona clase, interfețe și trăsături în PHP, se folosește aceeași structură - zend_class_entry. Și după cum ați văzut deja, această structură este destul de greoaie. Uneori, dezvoltatorii declară interfețe în codul lor, astfel încât să își poată folosi numele în blocurile catch. Acest lucru vă permite să prindeți doar un anumit tip de excepție. De exemplu, așa:

Interfață BarException ( ) clasa MyException extinde Exception implementează BarException ( ) try ( $foo->bar(): ) catch (BarException $e) ( )
Nu este foarte bine că 912 octeți sunt folosiți aici doar pentru a declara interfața BarException.

$clasa =<<<"CL" interface Bar { } CL; $m = memory_get_usage(); eval($class); echo memory_get_usage() - $m . "\n"; /* 912 bytes */
Nu vreau să spun că acest lucru este rău sau stupid, nu încerc să dau vina pe nimeni sau nimic. Vă atrag atenția doar asupra acestui punct. Din punct de vedere al structurii interne a PHP, clasele, interfețele și trăsăturile sunt folosite exact la fel. Nu puteți adăuga atribute la interfață; analizatorul sau compilatorul pur și simplu nu vă vor permite să faceți acest lucru. Cu toate acestea, structura zend_class_entry este încă acolo, doar că un număr de câmpuri, inclusiv static_members_table , nu vor fi pointeri alocați de memorie. Declararea unei clase, a unei trăsături echivalente sau a unei interfețe echivalente va necesita aceeași cantitate de memorie, deoarece toate folosesc aceeași structură.

Legarea de clasă

Mulți dezvoltatori nu se gândesc la legarea de clasă până când nu încep să întrebe cum funcționează de fapt lucrurile. Legarea de clasă poate fi descrisă ca „procesul prin care clasa în sine și toate datele asociate acesteia sunt pregătite pentru utilizare completă de către dezvoltator”. Acest proces este foarte simplu și nu necesită multe resurse dacă vorbim de o clasă care nu extinde alta, nu folosește trăsături și nu implementează o interfață. Procesul de legare pentru astfel de clase are loc în întregime în timpul compilării și nu se irosesc resurse pentru aceasta în timpul execuției. Vă rugăm să rețineți că vorbeam despre legarea unei clase declarate de utilizator. Pentru clasele interne, același proces se efectuează atunci când clasele sunt înregistrate cu nucleul sau extensiile PHP, chiar înainte ca scripturile utilizatorului să fie rulate - și acest lucru se face o singură dată în timpul întregului runtime PHP.

Lucrurile se complică foarte mult când vine vorba de implementarea interfețelor sau a moștenirii claselor. Apoi, în timpul legării clasei, absolut totul este copiat din obiectele părinte și fii (fie clase sau interfețe).

/* Clasă unică */ caz ZEND_DECLARE_CLASS: if (do_bind_class(CG(active_op_array), opline, CG(class_table), 1 TSRMLS_CC) == NULL) ( return; ) table = CG(class_table); pauză;
În cazul unei declarații simple de clasă, rulăm do_bind_class() . Această funcție înregistrează pur și simplu o clasă complet definită în tabelul de clase pentru o utilizare ulterioară în timpul execuției și, de asemenea, verifică posibilele metode abstracte:

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) (memset (memset,&ABSTRACT)) ; 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, „Clasa %s conține %d metodă abstractă%s și, prin urmare, trebuie să fie declarată abstractă sau să implementeze metodele rămase („ MAX_INFO_ABSTRACT_MAX_ABSTRACT_MAX_ABSTRACT_MAX_ _FMT ")", ce->name, ai.cnt, ai.cnt > 1 ? "s" : "", DISPLAY_ABSTRACT_FN(0), DISPLAY_ABSTRACT_FN(1), DISPLAY_ABSTRACT_FN(2) )
Nu este nimic de adăugat aici, este un caz simplu.

Când legați o clasă care implementează o interfață, trebuie să faceți următoarele:

  • Verificați dacă interfața a fost deja declarată.
  • Verificați dacă clasa necesară este într-adevăr o clasă și nu o interfață în sine (după cum am menționat mai sus, din punct de vedere al structurii interne sunt structurate la fel).
  • Copiați constantele din interfață în clasă, verificând posibilele coliziuni.
  • Copiați metodele din interfață în clasă, verificând posibilele coliziuni și inconsecvențe în declarație (de exemplu, transformarea metodelor de interfață în unele statice în clasa copil).
  • Adăugați interfața și toate interfețele mamă posibile la lista de interfețe implementate de clasă.
Prin „copiere” nu înțelegem o copie profundă completă. Pentru constante, atribute și funcții, se efectuează pe rând o recalculare pentru a determina câte entități din memorie le folosesc.

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 ->interfețe = (zend_class_entry **) realloc(ce->interfaces, sizeof(zend_class_entry *) * (++current_iface_num) ) else ( ce->interfaces = (zend_class_entry **) erealloc(ce->interfețe, sizeof (zend_class_entry) *) * (++current_iface_num) ) ) ce->interfaces = iface; ( &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_CC);
Observați diferența dintre clasele interne și cele de utilizator. Primul va folosi realloc() pentru a aloca memorie, al doilea va folosi erealloc(). realloc() alocă memorie „persistentă”, în timp ce erealloc() operează pe memorie „la cerere”.

Puteți vedea că atunci când două tabele constante (interfața-1 și clasa-1) sunt unite, ele fac acest lucru folosind callback-ul zval_add_ref. Nu copiază constante dintr-un tabel în altul, ci le împărtășește pointerii, adăugând pur și simplu numărul de referințe.

Pentru fiecare dintre tabelele cu funcții (metode), se folosește 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-> count_array; )++; if (op_array->static_variables) ( HashTable *static_variables = op_array->static_variables; zval *tmp_zval; ALLOC_HASHTABLE(op_array->static_variables); zend_hash_init(op_array->static_variables, zend_hash_num_variables_P, zend_hash, static_variables_D), ); zend_hash_copy(op_array->static_variables, (copy_ctor_func_t) zval_add_ref, (void *) &tmp_zval, sizeof(zval *));
Un refcount este adăugat la OPArray al funcției și toate variabilele statice posibile declarate în funcție (aici o metodă) sunt copiate folosind zval_add_ref. Deci, întregul proces de copiere necesită o mulțime de resurse de calcul, deoarece sunt implicate o mulțime de bucle și verificări. Dar se folosește puțină memorie. Din păcate, legarea interfeței astăzi este complet scursă în timpul rulării și o veți simți la fiecare solicitare. Poate că dezvoltatorii vor schimba acest lucru în curând.

Cât despre moștenire, aici, în principiu, totul este la fel ca la implementarea unei interfețe. Sunt implicați doar mai mulți „participanți”. Dar vreau să remarc că, dacă PHP știe deja despre clasă, atunci legarea se face în timpul compilării, iar dacă nu știe, atunci în timpul execuției. Deci este mai bine să o declarați astfel:

/* bun */ clasa A ( ) clasa B extinde A ( )
în loc de:

/* rău */ clasa B extinde A ( ) clasa A ( )
Apropo, procedura de rutină de legare a clasei poate duce la un comportament foarte ciudat:

/* funcționează */ clasa B extinde A ( ) clasa A ( )

/* dar aceasta nu este */ Eroare fatală: clasa „B” nu a fost găsită */ clasa C se extinde pe B ( ) clasa B se extinde pe A ( ) clasa A ( )

În prima variantă, legarea clasei B este amânată până la momentul execuției, deoarece atunci când compilatorul ajunge la declarația acestei clase, încă nu știe nimic despre clasa A. Când începe execuția, legarea clasei A are loc fără îndoială, deoarece este deja compilat, fiind o clasă singleton. În al doilea caz, totul este diferit. Legarea clasei C este amânată în timpul execuției, deoarece compilatorul nu știe încă nimic despre B când încearcă să-l compileze. Dar când clasa C este legată în timpul execuției, caută B, care nu există pentru că nu este compilată deoarece B este complementar. Apare mesajul „Clasa B nu există”.

Obiecte

Deci acum știm că:
  • Cursurile ocupă multă memorie.
  • Clasele interne sunt mult mai bine optimizate decât clasele de utilizatori, deoarece acestea din urmă trebuie create și distruse la fiecare solicitare. Clasele interioare există tot timpul.
  • Clasele, interfețele și trăsăturile folosesc aceeași structură și proceduri, diferențele sunt foarte mici.
  • În timpul moștenirii sau declarației, procesul de legare durează mult timp CPU, dar folosește puțină memorie deoarece multe lucruri sunt partajate mai degrabă decât duplicate. De asemenea, este mai bine să rulați legarea clasei în timpul compilării.

Acum să vorbim despre obiecte. Primul capitol arată că crearea unui obiect „clasic” (o clasă de utilizator „clasică”) a necesitat foarte puțină memorie, aproximativ 200 de octeți. Totul tine de clasa. Compilarea ulterioară a clasei consumă și memorie, dar acest lucru este în bine, deoarece sunt necesari mai puțini octeți pentru a crea un singur obiect. În esență, un obiect este o mică colecție de structuri minuscule.

Gestionarea metodelor obiectelor

La nivel de motor, metodele și funcțiile sunt același lucru - structura zend_function_structure. Doar numele diferă. Metodele sunt compilate și adăugate atributului function_table din zend_class_entry . Prin urmare, la runtime fiecare metodă este prezentată, este doar o chestiune de transfer al pointerului la execuție.

Typedef union _zend_function ( zend_uchar tip; struct ( zend_uchar tip; const char *function_name; zend_class_entry *scope; zend_uint fn_flags; union _zend_function *prototype; zend_uint num_args; * zend_uint required; zend_uint_info;_zendar_arg_op;) y op_array ; zend_internal_function internal_function ) zend_function ;
Când un obiect încearcă să apeleze o metodă, motorul caută implicit în tabelul de funcții al clasei obiectului. Dacă metoda nu există, atunci se apelează __call(). Se verifică și vizibilitatea - public/protejat/privat - în funcție de care se întreprind următoarele acțiuni:

Unire statică _zend_function *zend_std_get_method(zval **object_ptr, char *method_name, int method_len, const zend_literal *key TSRMLS_DC) ( zend_function *fbc; zval *object = *object_ptr; zend_object = ZOBject_); uj_P = Zobject_; 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_helen_to_1, use_copy) lc_method_name, method_name, method_len); hash_value = zend_hash_func(lc_method_name, method_len+1 ) /* Dacă metoda nu este găsită */ if (UNEXPECTED(zend_hash_quick_find(&zobj->ce->function_table, lc_metho_valud, ha_metho_valud, void **)&fbc) == ESEARE)) ( if (NEAȘTEPTAT(!key)) ( free_alloca(lc_method_name, use_heap); ) if (zobj->ce->__call) ( /* dacă clasa are un __call( ) handler */ return zend_get_user_call_function(zobj->ce, method_name, method_len) /* apelează handler-ul __call() */ ) else ( return NULL; /* altfel returnează NULL, ceea ce va duce probabil la o eroare fatală: metoda nu a fost găsită */ ) ) /* Verificați nivelul de acces */ if (fbc->op_array.fn_flags & ZEND_ACC_PRIVATE) ( zend_function *updated_fbc; updated_fbc = zend_check_private_int(fbc) , Z_OBJ_HANDLER_P(obiect, get_class_entry)(obiect TSRMLS_CC), lc_method_name, method_len, hash_value TSRMLS_CC if (EXPECTED(updated_fbc != NULL)) ( fbc = updated_fbc; ) else (fbc) (->b_j-call) (->b_c); = zend_get_user_call_function(zobj->ce, method_name, method_len else ( zend_error_noreturn(E_ERROR, "Apelați la metoda %s %s::%s() din contextul "%s"", zend_visibility_string(fbc->common.fn_flags )); , ZEND_FN_SCOPE_NAME(fbc), method_name, EG(scope)->name: "" ) else ( /* ... */ )
Poate ați observat un lucru interesant, uitați-vă la primele rânduri:

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); /* Creați un zend_copy_str_tolower src, src_length); */ zend_str_tolower_copy(lc_method_name, method_name, method_len = zend_hash_func(lc_method_name, method_len+1);
Aceasta este o manifestare a insensibilității la majuscule și minuscule a PHP. Sistemul trebuie mai întâi să fie cu litere mici fiecare funcție (zend_str_tolower_copy()) înainte de a o apela. Nu tocmai toate, ci cele în care este prezentă declarația if. Variabila cheie împiedică execuția funcției care se convertește în litere mici (partea else) - aceasta face parte din optimizarea PHP implementată în versiunea 5.4. Dacă apelul de metodă nu este dinamic, atunci compilatorul a calculat deja cheia și mai puține resurse sunt irosite la runtime.

Clasa Foo (funcția publică BAR() ( ) ) $a = new Foo; $b = „bară”; $a->bar(); /* apel static: bun */ $a->$b(); /* apel dinamic: rău */
Când funcția/metoda este compilată, aceasta este imediat convertită în litere mici. Funcția BAR() de mai sus este transformată în bar() de către compilator atunci când adaugă o metodă la tabelul de clase și funcții.

În exemplul de mai sus, primul apel este static: compilatorul a calculat cheia pentru șirul „bară”, iar când vine timpul să apeleze metoda, trebuie să lucreze mai puțin. Al doilea apel este deja dinamic, compilatorul nu știe nimic despre „$b” și nu poate calcula cheia pentru apelarea metodei. Apoi, în timpul execuției, trebuie să convertim șirul în litere mici și să-i calculăm hash (zend_hash_func()), care nu are cel mai bun impact asupra performanței.

În ceea ce privește __call() , nu reduce atât de mult performanța. Cu toate acestea, această metodă irosește mai multe resurse decât apelarea unei funcții existente.

Gestionarea atributelor obiectului

Iată ce se întâmplă:

După cum puteți vedea, atunci când sunt create mai multe obiecte din aceeași clasă, motorul redirecționează fiecare atribut către același pointer ca și în cazul atributelor de clasă. De-a lungul vieții sale, o clasă stochează nu numai propriile atribute statice, ci și atributele obiectelor. În cazul claselor interne - pe toată durata de rulare a PHP. Crearea unui obiect nu implică crearea atributelor acestuia, așa că aceasta este o abordare destul de rapidă și rentabilă. Numai când un obiect este pe cale să-și schimbe unul dintre atributele, motorul creează unul nou pentru el, presupunând că modificați atributul $a al lui Foo #2:

Deci, atunci când creăm un obiect, „doar” creăm o structură zend_object de 32 de octeți:

Typedef struct _zend_object ( zend_class_entry *ce; HashTable *properties; zval **properties_table; HashTable *protejează; /* protejează de __get/__set ... recursion */ ) zend_object;
Această structură este adăugată la depozitul de obiecte. Și aceasta, la rândul său, este structura zend_object_store. Acesta este registrul global de obiecte al motorului Zend - un loc în care toate obiectele sunt colectate și stocate într-o singură instanță:

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 =-ULL;; (*object) *object)->properties_table = NULL (*object)->guards = NULL /* Adăugați obiectul în magazin */ retval.handle = zend_objects_store_put(*object, (zend_objects_store_dtor_t) zend_objects_destroy_object, (zend_objects_object_object_storage) NUL TSRMLS_CC ); retval.handlers = &std_object_handlers;
Apoi, motorul creează un vector caracteristic pentru obiectul nostru:

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); i (i (i)< class_type->număr_proprietăți_implicit; 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_table [i], object->properties_table[i]); #else Z_ADDREF_P(object->properties_table[i]) ) obiect->properties = NULL; ) )
După cum puteți vedea, am alocat o memorie de tabel/vector (ca în C) pentru zval* pe baza proprietăților declarate ale clasei de obiecte. În cazul PHP non-thread-safe, adăugăm pur și simplu refcount la atribut, iar dacă este folosit Zend thread-safe (ZTS, Zend thread safety), atunci trebuie să copiem complet zval . Acesta este unul dintre multele exemple care confirmă performanța scăzută și intensitatea ridicată a resurselor a modului ZTS în comparație cu PHP non-ZTS.

Probabil că ai două întrebări:

  • Care este diferența dintre properties_table și proprietățile din structura zend_object?
  • Dacă punem atributele obiectului nostru în vectorul C, cum le putem recupera? Răsfoiți vectorul de fiecare dată (ceea ce reduce performanța)?

Răspunsul la ambele întrebări este oferit de zend_property_info .

Typedef struct _zend_property_info ( steaguri zend_uint; const char *nume; int name_length; ulong h; int offset; const char *doc_comment; int doc_comment_len; zend_class_entry *ce; ) zend_property_info;
Fiecare declarat un atribut (proprietate) al obiectului nostru are informații de proprietate corespunzătoare adăugate la câmpul property_info din zend_class_entry . Acest lucru se face în timpul compilării atributelor declarate în clasă:

Clasa Foo ( public $a = "foo"; protected $b; private $c; ) struct _zend_class_entry ( /* ... ... */ HashTable function_table; HashTable properties_info; /* aici sunt informațiile despre proprietăți despre $a, $b și $c */ zval **default_properties_table /* și aici, vom găsi $a, $b și $c cu valorile lor implicite */ int default_properties_count; /* aceasta va avea valoarea 3: 3 proprietăți */ /* ... ... */
Properties_infos este un tabel care spune unui obiect dacă atributul solicitat există. Și dacă există, atunci își trece numărul de index în tabloul object->properties. Apoi verificăm vizibilitatea și accesul la domeniu (public/protejat/privat).

Dacă atributul nu există și trebuie să-l scriem, atunci putem încerca să apelăm __set() . În caz de eșec, creăm un atribut dinamic care va fi stocat în câmpul object->property_table.

Property_info = zend_get_property_info_quick(zobj->ce, membru, (zobj->ce->__set != NULL), cheie TSRMLS_CC); if (EXPECTED(informații_proprietății!= NULL) && ((EXPECTED((informații_proprietății->steaguri și ZEND_ACC_STATIC) == 0) && informații_proprietății->offset >= 0) ? (zobj->proprietăți ? ((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; dacă (zobj->ce->__set && /* clasa are un __set() ? */ zend_get_property_guard(zobj, property_info, member, &guard) == SUCCES && !guard->in_set) ( Z_ADDREF_P(obiect); if (PZVAL_IS_REF( obiect)) ( SEPARATE_ZVAL(&object); ) guard->in_set = 1 /* împiedică setarea circulară */ if (zend_std_call_setter(obiect, membru, valoare TSRMLS_CC) != SUCCESS) ( /* call __set() */ ) guard ->in_set = zval_ptr_dtor(&object);
Atâta timp cât nu scrieți la un obiect, consumul de memorie al acestuia nu se modifică. După înregistrare, ocupă mai mult spațiu (până când este distrus), deoarece conține toate atributele scrise în el.

Obiectele care se comportă ca referințe datorită stocării obiectelor

Obiectele nu sunt referințe. Acest lucru este demonstrat într-un mic script:

Funcția foo($var) ( $var = 42; ) $o = new MyClass; foo($o); var_dump($o); /* acesta este încă un obiect, nu întregul 42 */
Toată lumea va spune acum că „în PHP 5, obiectele sunt legături”, chiar și manualul oficial menționează acest lucru. Din punct de vedere tehnic, acest lucru este complet fals. Cu toate acestea, obiectele se pot comporta în același mod ca referințele. De exemplu, atunci când treceți o variabilă care este un obiect unei funcții, acea funcție poate modifica același obiect.

Acest lucru se datorează faptului că zval-ul transmis ca funcție nu trece obiectul în sine, ci identificatorul său unic, care este folosit pentru a căuta în depozitul de obiecte partajat. Și rezultatul este același. Este posibil să alocați trei zval-uri diferite în memorie și toate pot conține același mâner de obiect.

Object(MyClass)#1 (0) ( ) /* #1 este mânerul obiectului (numărul), este unic */

Zend_object_store permite ca obiectele să fie stocate în memorie o singură dată. Singura modalitate de a scrie în magazin este să creați un nou obiect cu noul cuvânt cheie, funcția unserialize(), API-ul reflection sau cuvântul cheie clone. Nicio altă operațiune nu vă va permite să duplicați sau să creați un nou obiect în magazin.

Typedef struct _zend_objects_store ( zend_object_store_bucket *object_buckets; zend_uint top; zend_uint size; 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_sto_ject_free) _t clone t zend_object_buffer *buffered; ) găleată;

Ce este $asta?

Înțelegerea $this nu este chiar atât de dificilă, dar există bucăți de cod asociate cu acest instrument în mai multe locuri din motor: în compilator, în cod pentru obținerea variabilelor în timpul execuției etc. $this apare și dispare după cum este necesar, atribuindu-se automat obiectului curent - în general, un lucru „magic”. Iar codul intern permite perfect gestionarea acestuia.

În primul rând, compilatorul nu va permite scrierea în $this . Pentru a face acest lucru, va verifica fiecare misiune pe care o faceți și, dacă găsește o misiune pentru $this , va genera o eroare fatală.

/* ... ... */ if (opline_is_fetch_this(last_op TSRMLS_CC)) ( zend_error(E_COMPILE_ERROR, "Nu se poate reatribui $this"); ) /* ... ... */ static zend_bool opline_is_fetch_this(const zend_op *opline TSRMLS_DC) ( dacă ((opline->opcode == ZEND_FETCH_W) && (opline->op1_type == IS_CONST) && (Z_TYPE(CONSTANT(opline->op1.constant)) == IS_STRING) && ((opline-> extinsed_value & ZEND_FETCH_STATIC_MEMBER) != ZEND_FETCH_STATIC_MEMBER) && (Z_HASH_P(&CONSTANT(opline->op1.constant)) == THIS_HASHVAL) && (Z_STRLEN(CONSTANT(opline->op1.constant-of)("=this->op1.constant)) 1)) && !memcmp(Z_STRVAL(CONSTANT(opline->op1.constant)), „acest”, sizeof(„acest”))) ( returnează 1; ) else ( returnează 0; ) )
Cum este gestionat $this? Utilizarea sa este posibilă numai în interiorul unei metode, în timpul apelului căreia compilatorul generează OPCode INIT_METHOD_CALL . Motorul știe cine apelează metoda, în cazul lui $a->foo() este $a . Apoi valoarea $a este preluată și stocată în spațiul partajat. Apoi, metoda este numită folosind OPCode DO_FCALL. În această etapă, valoarea stocată este din nou preluată (obiectul apelează metoda) și atribuită indicatorului intern global $this - EG(This) .

Dacă (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) ( ar trebui_schimbat_sfera = 1; EX(current_this) = EG(This); EX(current_scope) = EG(scope); EX(current_called_scope) = EG( call_scope); EG(This) = EX(obiect); /* preia obiectul pregătit în codul operațional INIT_METHOD anterior și îl afectează pe EG(This) */ EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX (obiect)) ? fbc->common.scope: NULL;
Acum, când o metodă este apelată, dacă în corpul ei utilizați $this pentru a acționa asupra unei variabile sau apelați o metodă (de exemplu, $this->a = 8), atunci aceasta va avea ca rezultat OPCode ZEND_ASSIGN_OBJ , care la rândul său va prelua $ asta înapoi de la EG(Acest) .

Static 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, „Folosirea obiectului $this”); NUL; ) )
În cazul în care ați folosit $this pentru a apela o metodă (de exemplu, $this->foo()) sau ați trecut un alt apel de funcție ($this->foo($this);), atunci motorul va încerca să extragă $this din tabelele de simbol curent așa cum face el pentru fiecare variabilă standard. Dar aici se efectuează pregătirea specială în timpul creării cadrului de stivă al funcției curente:

Dacă (op_array->this_var != -1 && EG(This)) ( Z_ADDREF_P(EG(This)); dacă (!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)); ) ) )
Când numim o metodă, motorul schimbă domeniul de aplicare:

Dacă (fbc->type == ZEND_USER_FUNCTION || fbc->common.scope) ( /* ... ... */ EG(scope) = (fbc->type == ZEND_USER_FUNCTION || !EX (obiect)) ? fbc->common.scope: NULL;
EG(scope) este de tip zend_class_entry . Aceasta este clasa care deține metoda pe care o solicitați. Și va fi folosit pentru orice operație asupra obiectului pe care o veți efectua în corpul metodei după verificarea vizibilității motorului:

Static zend_always_inline int zend_verify_property_access(zend_property_info *property_info, zend_class_entry *ce TSRMLS_DC) ( comutați (property_info->flags & ZEND_ACC_PPP_MASK) (case ZEND_ACC_PUBLIC: return 1Zend_Property: return 1 _info->ce, EG(sfera)); ZEND_ACC_PRIVATE : if ((ce==EG(sfera) || property_info->ce == EG(sfera)) && EG(sfera)) ( return 1; ) else ( return 0; ) break ) return 0;
Iată cum puteți accesa membrii privați ai obiectelor care nu vă aparțin, dar sunt copii ale domeniului dvs. actual:

Clasa A ( private $a; public function foo(A $obj) ( $this->a = "foo"; $obj->a = "bar"; /* da, acest lucru este posibil */ ) ) $a = A nou; $b = A nou; $a->foo($b);
Această caracteristică a provocat un număr mare de rapoarte de erori de la dezvoltatori. Dar așa funcționează modelul obiect în PHP - de fapt, setăm domeniul de aplicare nu pe baza obiectului, ci pe clasă. În cazul clasei noastre „Foo”, puteți lucra cu orice Foo privat al oricărui alt Foo, așa cum se arată mai sus.

Despre destructor

Destructorii sunt periculoși, nu vă bazați pe ei, deoarece PHP nu îi cheamă nici măcar în cazul unei erori fatale:

Clasa Foo (funcția publică __destruct() ( echo "bybye foo"; ) ) $f = new Foo; această funcție nu există (); /* eroare fatală, funcția nu a fost găsită, destructorul Foo NU este rulat */
Dar ordinea în care sunt chemați destructorii dacă sunt chemați oricum? Răspunsul este clar vizibil în cod:

Void shutdown_destructors(TSRMLS_D) ( zend_try ( simboluri int; do ( simboluri = zend_hash_num_elements(&EG(symbol_table)); zend_hash_reverse_apply(&EG(symbol_table), (apply_func_t) zval_call_destructor TSRMLS_CC(&EGs_symbol) ; mbol_table )) ); zend_objects_store_call_destructors(&EG(objects_store) TSRMLS_CC ( /* dacă nu am putut distruge în mod curat, marcați totuși toate obiectele ca fiind distruse */ zend_objects_store_mark_destructed(&EG(objects_store) TS_RMD_CC); instructor ( zval **zv TSRMLS_DC) ( dacă (Z_TYPE_PP(zv) == IS_OBJECT && Z_REFCOUNT_PP(zv) == 1) ( returnează ZEND_HASH_APPLY_REMOVE; ) else ( returnează ZEND_HASH_APPLY_KEEP; ) )
Cele trei etape ale apelării unui destructor sunt demonstrate aici:

  • Buclă prin tabelul global de simboluri înapoi și apelați destructori pentru obiectele care au refcount = 1.
  • Apoi direcția ciclicității se schimbă și destructorii sunt chemați pentru toate celelalte obiecte, cu refcount > 1.
  • Dacă apare o problemă într-una din etapele anterioare, apelul către destructorii rămași este anulat.
La ce duce asta:

Clasa Foo (funcția publică __destruct() ( var_dump(„Foo distrus”); ) )
Bara de clasă (funcția publică __destruct() ( var_dump("Bară distrusă"); ) )

Exemplul unu:

$a = nou Foo; $b = bar nou; „Distrus Bar” „Distrus Foo”
Același exemplu:

$a = bar nou; $b = nou Foo; „distrus Foo” „distrus Bar”
Exemplul doi:

$a = bar nou; $b = nou Foo; $c = $b; /* incrementează numărul refcount al obiectului $b */ "distrus Bar" "distrus Foo"
Exemplul trei:

Clasa Foo ( function public __destruct() ( var_dump("distrus Foo"); die();) ) /* observați die() aici */ class Bar (funcție publică __destruct() ( var_dump("distrus Bar"); ) ) $a = nou Foo; $a2 = $a; $b = bar nou; $b2 = $b; l-a distrus pe Foo
Această procedură a fost aleasă dintr-un motiv. Dar dacă nu ți se potrivește, atunci este mai bine să-ți distrugi singur obiectele. Acesta este singurul mod de a controla apelurile către __destruct() . Dacă lași PHP să facă acest lucru pentru tine, nu fi revoltat de rezultate. Aveți întotdeauna opțiunea de a vă distruge obiectele manual pentru a avea control complet asupra comenzii.

PHP nu apelează destructori în cazul oricărei erori fatale. Cert este că, în acest caz, Zend este instabil, iar apelarea destructorilor duce la executarea codului utilizatorului, care poate accesa pointeri eronați și, ca urmare, poate bloca PHP. Este mai bine să mențineți stabilitatea sistemului - de aceea apelarea destructorilor este blocată. Poate că ceva se va schimba în PHP 7.

În ceea ce privește recursiunile, acestea sunt slab protejate în PHP, iar acest lucru se aplică numai pentru __get() și __set() . Dacă vă distrugeți obiectul undeva în cadrul stivei destructorului, veți ajunge într-o buclă recursivă infinită care va consuma toate resursele stivei procesului dvs. (de obicei 8 kB, ulimit -s) și va rupe PHP.

Clasa Foo (funcția publică __destruct() (nou Foo; ) /* vei prăbuși */ )
Pentru a rezuma cele de mai sus: nu aveți încredere în destructori cu cod critic, cum ar fi gestionarea mecanismului de blocare, deoarece PHP poate să nu apeleze destructorul sau să-l apeleze într-o secvență necontrolată. Dacă un cod important este încă procesat de un destructor, atunci cel puțin controlați singur ciclul de viață al obiectelor. PHP va apela destructorul atunci când refcount-ul obiectului tău scade la zero, ceea ce înseamnă că obiectul nu mai este în uz și poate fi distrus în siguranță.

Concluzie

Sper că acum multe ți-au devenit mai clare în munca de zi cu zi cu obiecte. Nu consumă multă memorie, iar implementarea lor la nivel de motor este bine optimizată. Încercați să utilizați un autoloader bine conceput pentru a îmbunătăți utilizarea memoriei. Declarați clase în ordinea moștenirii logică și, dacă transformați cele mai complexe dintre ele în extensii C, puteți optimiza multe procese și chiar și mai mult să creșteți performanța generală a unor astfel de clase.

În acest tutorial veți învăța elementele de bază ale programării orientate pe obiecte în PHP. Veți afla despre principiile OOP în general și veți învăța cum să scrieți scripturi simple în PHP.

Bun venit la prima dintr-o serie de lecții despre POO în PHP! După finalizarea tuturor lecțiilor din această serie, veți afla despre principiile și conceptele de bază ale POO și veți afla cum să creați rapid și ușor aplicații utile în PHP.

În acest tutorial voi începe să vă pun la curent și să vă spun despre conceptele de bază ale OOP. O sa inveti:

  • ce este OOP
  • cum vă va ajuta OOP să creați scripturi PHP mai bune
  • câteva concepte de bază precum clase, obiecte, metode, variabile de clasă
  • de unde să începeți să scrieți un script PHP

Sunteți gata să vă scufundați în lumea obiectelor PHP? Atunci dă-i drumul!

Ce este programarea orientată pe obiecte?

Dacă ați creat și folosit vreodată funcții personalizate în PHP, ați folosit un stil de programare procedurală. În programarea procedurală, de obicei creați structuri de date - numere, șiruri, matrice etc. - să stocheze unele date, iar apoi să proceseze aceste structuri cu funcții speciale care manipulează aceste date.

Programarea orientată pe obiecte, sau OOP, a avansat deoarece aici stocăm structurile de date și funcțiile care le procesează într-o singură entitate numită obiect. În loc să procesați datele cu o anumită funcție, încărcați acele date într-un obiect și apoi apelați metodele acestuia pentru a le manipula și a obține rezultatul dorit.

Cel mai adesea, obiectele create folosind POO reflectă entități reale. De exemplu, dacă creați un forum pentru site-ul dvs., veți crea un obiect Membru care va stoca informații despre fiecare membru al forumului (nume, autentificare, e-mail, parolă etc.), precum și metode care vor procesa aceste informații ( înregistrare, autorizare, deconectare, interzicere etc.).

De ce să folosiți OOP?

Procedural și orientat pe obiecte sunt două moduri diferite de a face același lucru. Acest lucru nu înseamnă că una dintre ele este mai bună decât cealaltă - fiecare scrie așa cum vrea, așa că puteți chiar să combinați cu ușurință aceste două abordări într-un singur scenariu.

Cu toate acestea, iată câteva beneficii ale OOP pentru dezvoltatori:

  • Este mai ușor să reflectați situații reale: așa cum am menționat mai sus, obiectele reflectă entități reale - oameni, produse, carduri, articole de blog etc. Acest lucru simplifică foarte mult sarcina atunci când abia începeți să vă proiectați aplicația, deoarece scopul fiecărui obiect. este ca un scop relațiile dintre obiecte vor fi clare și de înțeles.
  • Este mai ușor să scrieți programe modulare: OOP implică scrierea de module. Împărțind codul în module, vă va fi mai ușor să îl gestionați, să îl depanați și să îl extindeți.
  • Este mai ușor să scrieți cod care va fi folosit de mai multe ori: scrierea unui cod care poate fi folosit de mai multe ori va economisi timp la scrierea unei aplicații și, în timp, puteți chiar să creați o bibliotecă întreagă din aceste tipuri de module pe care le puteți utiliza în multe aplicatii. Cu OOP, devine relativ mai ușor să scrieți un astfel de cod, deoarece structurile și funcțiile de date sunt încapsulate într-un singur obiect care poate fi folosit de orice număr.

Câteva concepte de bază

Înainte de a începe să scrieți scripturi, trebuie să înțelegeți bine conceptele de clasă, obiect, variabilă de clasă și metodă.

Clase

O clasă este un cadru pentru un obiect. Aceasta este o bucată de cod care definește:

  • Tipurile de date pe care le vor conține obiectele de clasă create
  • Funcții care vor conține aceste obiecte.

Când creați o aplicație OOP, de obicei veți crea mai multe clase care vor reprezenta diferitele tipuri de entități din aplicația dvs. De exemplu, pentru a crea un forum, puteți crea clasele Forum, Topic, Post și Member.

Obiecte

Un obiect este un tip special de variabilă care este creat printr-o clasă. Conține date reale și funcții de manipulare. Puteți crea câte obiecte doriți dintr-o singură clasă. Fiecare funcție a unui obiect este independentă de alt obiect, chiar dacă sunt create din aceeași clasă.

Pentru comparație cu entități reale:

  • O clasă este cadrul pentru o mașină: definește cum va arăta și acționa mașina, dar este totuși o entitate abstractă
  • Un obiect este o mașină reală creată dintr-un cadru: are proprietăți reale (cum ar fi viteza) și comportament (cum ar fi accelerația sau frânarea).

Notă: Un obiect este adesea numit esența unei clase, iar procesul de creare a unui obiect al unei clase se numește implementare.

Variabile de clasă

Valorile datelor care sunt stocate într-un anumit obiect sunt scrise în variabile speciale numite variabile de clasă. Variabilele de clasă sunt strâns asociate cu obiectul său. Chiar dacă toate obiectele unei clase au aceleași variabile, valorile lor pot diferi.

Metode

Funcțiile definite într-o clasă și utilizate pe obiectele acelei clase sunt numite metode. Nu sunt foarte diferite de funcțiile obișnuite - le puteți transmite valori, pot conține variabile locale și pot returna valori. Cu toate acestea, metodele funcționează mai des cu variabile obiect. De exemplu, metoda login() pentru conectarea utilizatorilor în forumul dvs. ar putea seta variabila de clasă loggedIn la true.

Cum se creează o clasă în PHP?

Acum că știți deja ce sunt clasele, metodele, variabilele de clasă și obiectele, este timpul să creați câteva clase și obiecte în cod PHP.

În primul rând, să vedem cum să creăm de fapt o clasă. Practic, scriptul pentru crearea unei clase arată astfel:

Class ClassName ( // (definiția clasei) )

De exemplu, dacă creați o clasă de membru pentru forumul dvs., ați scrie asta:

Membru al clasei ( // (definiția clasei) )

Este destul de simplu. Desigur, această clasă nu va face nimic până când nu adăugați variabile și metode la ea. Cu toate acestea, codul de mai sus creează o clasă PHP validă care poate fi utilizată.

O regulă generală bună: plasați fiecare clasă într-un fișier separat cu același nume cu numele clasei. De exemplu, puneți clasa Member în fișierul Member.php și stocați-o într-un folder, să zicem, clase.

Cum se creează obiecte în PHP?

Puteți crea un obiect folosind cuvântul cheie nou:

Nou ClassName()

Acest cod va crea un obiect din clasa ClassName. Va trebui să utilizați acest obiect mai târziu, așa că trebuie să îl stocați într-o variabilă. De exemplu, să creăm un obiect al clasei Member și să-l stocăm în variabila $member:

$member = membru nou();

De asemenea, putem crea un alt obiect din aceeași clasă:

$member2 = membru nou();

Chiar dacă am creat aceste două obiecte din aceeași clasă, variabilele $member și $member2 sunt independente una de cealaltă.

Creați variabile de clasă

Acum că știm deja cum să creăm clase și obiecte de clasă, să ne uităm la cum să creăm variabile de clasă. Există 3 accesorii pentru variabilele de clasă care pot fi adăugate la o clasă:

  • Variabile de clasă publică (publice): accesibile - i.e. pot fi citite și/sau modificate - oriunde în script, indiferent de locul în care se află acest cod - în interiorul clasei sau în afara acesteia
  • Variabile de clasă privată (private): accesibile numai metodelor de clasă. Cel mai bine este să faceți variabilele de clasă private pentru a separa obiectele de restul codului.
  • Variabile de clasă protejată: disponibile pentru metodele propriei clase, precum și pentru metodele claselor moștenite (vom vorbi mai târziu despre moștenire).

Pentru a crea o variabilă de clasă, scrieți cuvântul cheie public, private sau protected, apoi introduceți numele variabilei:

Clasa ClassName ( public $propertyName; privat $propertyName; protejat $propertyName; )

Să adăugăm o variabilă de clasă publică la clasa noastră Member pentru a stoca numele de utilizator:

Membru al clasei ( public $username = ""; )

Observați că am inițializat variabila noastră de clasă, valoarea acesteia este șirul gol, „”. Aceasta înseamnă că atunci când un utilizator nou este creat, numele de utilizator va fi implicit șirul gol. La fel ca și în cazul variabilelor obișnuite în PHP, variabilele de clasă nu trebuie să fie inițializate, dar este mai bine să nu fii leneș. Dacă nu inițializați o variabilă de clasă, valoarea implicită a acesteia este nulă.

Accesarea variabilelor de clasă

Pentru a obține acces la o variabilă a unui obiect, utilizați operatorul ->:

$object->propertyName

Sa incercam. Să scriem un script care declară o clasă Membru și o variabilă de clasă, creează un obiect al acestei clase și apoi setează valoarea variabilei de clasă și o afișează pe ecran:

nume utilizator = "Fred"; echo $member->nume utilizator; // Tipăriți „Fred” ?>

Rulați acest cod, acesta va afișa șirul „Fred”, valoarea variabilei de clasă $member->username. După cum puteți vedea, operați pe o variabilă obiect la fel ca o variabilă obișnuită - o puteți seta la o valoare și o puteți citi.

Adăugarea de metode la o clasă

Dar crearea de metode? După cum am menționat mai devreme, metodele sunt doar funcții obișnuite care fac parte dintr-o clasă. Deci s-ar putea să nu fiți surprins că acestea sunt create folosind același cuvânt cheie pentru funcție. Singura diferență față de crearea de funcții obișnuite este că puteți adăuga și unul dintre identificatorii de acces (public, privat, protejat) la declarația acestuia. În acest fel, metodele sunt similare cu variabilele de clasă:

Clasa ClassName (funcție publică methodName() ( // (cod) ) funcție privată methodName() ( // (cod) ) funcție protejată methodName() ( // (cod) ) )

Notă: la fel ca și în cazul variabilelor de clasă, metodele publice pot fi apelate de oriunde, metodele private pot fi apelate doar în cadrul clasei, iar metodele protejate pot fi apelate din clasa însăși și descendenții acesteia.

Să încercăm să adăugăm câteva metode și variabile de clasă la clasa noastră:

  • variabilă privată de clasă $loggedIn pentru a identifica utilizatorul, de ex. indiferent dacă a intrat sau nu,
  • metoda login(), care se va conecta pe forum prin setarea variabilei clasei $loggedIn la true,
  • metoda logout(), care se va deconecta din forum prin setarea variabilei clasei $loggedIn la false,
  • isLoggedIn(), care va returna valoarea variabilei clasei $loggedIn.

Iată codul nostru:

loggedIn = adevărat; ) funcția publică logout() ( $this->loggedIn = fals; ) public function isLoggedIn() ( returnează $this->loggedIn; ) ) ?>

Poate ați observat că am folosit un nou cuvânt cheie $this. În contextul metodelor unui obiect, variabila specială $this se referă la obiectul însuși. Folosind $this într-o metodă obiect, metoda poate accesa orice variabilă de clasă și metodă a obiectului.

De exemplu, metoda login() poate accesa variabila clasa $loggedIn a obiectului prin $this->loggedIn.

Apropo, variabila noastră de clasă este privată, deci nu poate fi apelată din nicio parte a scriptului, ci doar din metodele login(), logout() și isLoggedIn(). Aceasta este o abordare bună, deoarece elementele interne ale obiectului (cum ar fi modul în care înregistrează exact dacă utilizatorul este conectat sau nu) este separat de restul codului. Ori de câte ori este posibil, încercați să utilizați variabile de clasă privată, astfel încât obiectele dvs. să fie autonome, mobile și protejate.

Notă: variabila clasa $username din exemplul nostru este publică. Am făcut asta doar pentru a demonstra cum puteți accesa variabilele clasei unui obiect. În proiectele reale, preferați să faceți această variabilă privată și să creați variabile speciale de clasă publică pentru a seta valorile numelui de utilizator dacă este necesar.

Utilizarea Metodelor

Pentru a apela o metodă pe un obiect, utilizați operatorul ->, cu care v-ați familiarizat deja.

$object->methodName()

Acest lucru funcționează la fel ca apelarea unei funcții obișnuite. Puteți trece argumente în paranteze (presupunând că este nevoie de orice argument, desigur), iar apelul la metodă poate returna și valori specifice pe care apoi le puteți utiliza.

loggedIn = adevărat; ) funcția publică logout() ( $this->loggedIn = false; ) public function isLoggedIn() ( return $this->loggedIn; ) ) $member = new Member(); $member->username = "Fred"; echo $member->nume utilizator . "este". ($membru->
"; $member->login(); echo $member->nume utilizator . " este " . ($member->isLoggedIn() ? "conectat": "deconectat") ."
"; $member->logout(); echo $member->nume utilizator . " este " . ($member->isLoggedIn() ? "conectat": "deconectat") ."
"; ?>

Acest script va afișa următoarele:

Fred este deconectat Fred este conectat Fred este deconectat

Iată cum funcționează:

  1. După ce am descris clasa Member, am creat obiectul acesteia și l-am stocat în variabila $member. De asemenea, am dat variabilei de clasă $username a acestui obiect valoarea „Fred”.
  2. Apoi am apelat metoda $member->isLoggedIn() pentru a determina dacă utilizatorul este conectat sau nu. Această metodă returnează pur și simplu valoarea variabilei clasei $loggedIn. Deoarece valoarea implicită a acestei variabile de clasă este falsă, rezultatul apelării $member->isLoggedIn() va fi fals, așa că va fi afișat mesajul „Fred este deconectat”.
  3. Apoi numim metoda login(). Acesta va seta variabila de clasă $loggedIn la adevărat.
  4. Acum, când apelați metoda $member->isLoggedIn(), va returna true și va afișa mesajul „Fred este conectat”.
  5. Să apelăm metoda logout(), care setează valoarea proprietății $loggedIn la false.
  6. Să apelăm metoda $member->isLoggedIn() a treia oară. Acum va returna false deoarece proprietatea $loggedIn este setată din nou la false. Deci, mesajul „Fred este deconectat” va fi afișat din nou.

Notă: în cazul în care ați văzut asta mai întâi: ?: este un operator ternar. Aceasta este o versiune simplificată a blocurilor if...else. Puteți afla despre aceste tipuri de operatori.

concluzii

În acest tutorial ați învățat elementele de bază ale OOP în PHP. Ați învățat despre lucruri precum:

  • Ce este OOP și de ce este util?
  • concepte de clase, obiecte, variabile de clasă și metode
  • cum se creează clase și obiecte
  • cum să creați și să utilizați variabilele de clasă
  • concepte de identificatori de acces public, privat, protejat
  • cum să creați și să utilizați metodele de clasă

Ați învățat deja multe despre el și veți învăța multe mai multe în lecțiile următoare. Cu toate acestea, dacă ați analizat toate exemplele pe care le-am dat, aveți o bază solidă. Puteți începe să creați aplicații folosind OOP.

Cele mai bune articole pe această temă