Klasat dhe objektet në PHP

Klasat

Objektet janë tipi më kompleks i të dhënave në PHP. Për dallim nga variablat skalare të cilat mund të përmbajnë vetëm një vlerë të tipit të caktuar (integer, float, boolean, string ose NULL), dhe vargjeve të cilat mund të përmbajnë një listë të vlerave të tipeve të ndryshme, objektet mund të përmbajnë jo vetëm vlera por edhe funksione. Pra, objekti është një program në vete, i cili përbëhet nga vetitë (properties), respektivisht variablat, dhe nga metodat (methods), respektivisht funksionet.

Për definimin e vetive dhe metodave të klasës, përdoren klasat. Klasat janë kodi në PHP ku përpilohet logjika e veprimit të metodave të ndryshme si dhe lista e vetive me vlerat e tyre përkatëse fillestare. Me instancimin e klasës krijohet objekti. Thënë ndryshe, objekti është instancë e klasës. Objekti është një entitet që ekziston gjatë kohës së ekzekutimit të programit, ndërsa klasa është thjesht kodi në të cilin PHP bazohet gjatë krijimit të objektit. Për t’iu referuar objektit, përdorim një emërtim sikurse që veprojmë me variablat. Për t’iu qasur vetive dhe metodave të objektit përdoret shenja ->, e njohur si object operator.

Nga një klasë mund të krijohen një apo më tepër objekte. Secili prej objekteve, gjatë kohës së ekzekutimit të programit (run-time) vepron si njësi e pavarur programore, secili me mundësinë që t’i ndryshohen vetitë, respektivisht vlerat e variablave.

Metodat e një klase nuk janë asgjë më tepër se sa funksione, dhe si të tilla, ato mund të pranojnë parametra në formë të vlerave skalare, vargjeve apo edhe të objekteve të tjera. Pra, një objekt mund të përdoret si argument gjatë thirrjes së një metode të një klase, me ç’rast objekti i dërguar bëhet pjesë përbërëse e objektit të klasës që është instancuar.

Klasa formohet si vijon:

<?php
class Produkti {

}

Kjo aktualisht është një klasë e zbrazët, pa asnjë veti dhe asnjë metodë.

Krijimi i objektit

Sidoqoftë, ne mund ta instancojmë atë dhe ta krijojmë një objekt, në këtë rast një objekt të zbrazët:

<?php
class Produkti {

}
$o = new Produkti;

Shprehja në anën e djathtë të barazimin, e bën instancimin e klasës, respektivisht krijimin e objektit duke u bazuar në atë klasë, e pastaj e vendos në variablin $o, të cilit do t’i referohemi sa herë të na duhet ai objekt.

Efektin e njëjtë do ta arrimë edhe nëse kodin e shkruajmë si vijon:

<?php
class Produkti {

}
$o = new Produkti();

Këtu dallojmë se pas emrit të klasës janë shënuar edhe kllapat, të cilat do të jenë të rëndësishme kur do të dëshirojmë të bartim një numër të vlerave të caktuara siç bëjmë me funksionet, pra brenda kllapave mund të vendosim argumente të cilat do t’i nevojiten klasës gjatë procesit të krijimit të objektit.

Vetitë

Ta krijojmë një klasë që përmban vetëm veti:

<?php
class Produkti {
    public $cmimi;
    public $sasia;
}

$o = new Produkti();

Me këtë kemi deklaruar se klasa i përmban 2 veti: $cmimi dhe $sasia, të cilat mund të jenë variabla të cilitdo tip, por mund të jenë edhe vargje, resurse apo objekte. Këto dy variabla, apo në kontekst të OOP – veti, nuk përmbajnë vlera fillestare, respektivisht vlerat e $cmimi dhe $sasia janë NULL. Si të tilla, nuk do të jenë të dobishme deri në momentin kur ia caktojmë ndonjë vlerë më vonë gjatë rrjedhës së ekzekutimit të programit.

Vetia e klasës mund të ketë vlerë fillestare dhe atë mund t’ia caktojmë gjatë deklarimit:

<?php
class Produkti {
    public $cmimi = 0;
    public $sasia = 1;
}

$o = new Produkti();

$o->cmimi = 12;
echo "<p>Cmimi: ".$o->cmimi;
echo "<p>Sasia: ".$o->sasia;

Në rreshtin e parafundit i referohemi objektit me $o e pastaj vetisë $cmimi me $o->cmimi dhe ia japim vlerën 12. Në rreshtin e fundit kërkojmë të tregohet vlera e $o->cmimi dhe do të shfaqet vlera 12. Ne mund t’ia ndryshojmë drejtpërsëdrejti vlerën e një vetie, vetëm nëse ajo veti është public. Gjatë deklarimit të një vetie, mund të përdorim:

  • public
  • protected
  • private

Vetëm atyre vetive public mund t’iu qasemi drejtpërsëdrejti jashtë klasës dhe po ashtu mund t’i ndryshojmë, gjersa atyre vetive që i deklarojmë si protected dhe private mund t’iu qasemi vetëm brenda klasës.

Ky objekt, që aktualisht i ka dy veti ($o->cmimi dhe $o->sasia), nuk është ende gjithaq i dobishëm, sepse mund të përdornim dy variabla të rëndomta, për ta arritur efektin e njëjtë:

<?php
$cmimi = 12;
$sasia = 1;

echo "<p>Cmimi: ".$cmimi;
echo "<p>Sasia: ".$sasia;
// Rezultati:
// Cmimi: 12
// Sasia: 1

Metodat

Një objekt do të jetë dobishëm tek kur përmban metoda, respektivisht funksione, të cilat definohen brenda klasës.

<?php
class Produkti {
    public $cmimi = 0;
    public $sasia = 1;

    public function vlera($c, $s) {
        $v = $c * $s;
        return $v;
    }
}

$o = new Produkti();

$o->cmimi = 12;
$o->sasia = 5;
echo "<p>Vlera: ".$o->vlera($o->cmimi, $o->sasia);
// Rezultati: 
// Vlera: 60

Në këtë shembull shohim se klasës ia kemi shtuar një metodë, që është një funksion që pranon dy parametra ($c dhe $s), të cilat më pas i shumëzon duke e vendosur rezultatin në variablin $v, dhe në fund e kthen vlerën e $v, e cila më pas shfaqet me echo.

Duke qenë se metoda vlera() gjendet brenda klasës së njëjtë me vetitë $cmimi dhe $sasia, në rastin konkret nuk kemi nevojë që këto dy veti t’i vendosim si argumente gjatë thirrjes së funksionit, sepse funksioni është në gjendje t’i “shohë” si anëtarë të klasës. Ta modifikojmë pak klasën:

<?php
class Produkti {
    public $cmimi = 0;
    public $sasia = 1;

    public function vlera() {
        $v = $this->cmimi * $this->sasia;
        return $v;
    }
}

$o = new Produkti();

$o->cmimi = 12;
$o->sasia = 5;
echo "<p>Vlera: ".$o->vlera();
// Rezultati: 
// Vlera: 60

Rezultati është krejtësisht i njëjtë, por kodi ynë dallon pak, sepse tash kur e thërrasim metodën $o->vlera() nuk i shënojmë argumentet e funksionit, sepse funksioni mund t’iu qaset atyre variablave drejtpërsëdrejti nga brendësia e klasës me $this->cmimi dhe $this->sasia.

Pra, $this është referencë ndaj instancës aktuale të klasës, pra objektit aktual, ku ai objekt në “brendësinë” e vet i ka edhe vetitë, edhe metodat.

Instancat

Nga një klasë mund të krijohen një e më tepër objekte. Secili objekt i krijuar do të jetë një tërësi në vete, me metoda të njëjta si objektet tjera të krijuara nga klasa e njëjtë, por me veti të ndryshme.

<?php
class Produkti {
    public $cmimi = 0;
    public $sasia = 1;

    public function vlera() {
        $v = $this->cmimi * $this->sasia;
        return $v;
    }
}

$o = new Produkti();
$o->cmimi = 12;
$o->sasia = 5;
echo "<p>Objekti 1, vlera: ".$o->vlera();
// Rezultati: 
// Objekti 1, vlera: 60

$p = new Produkti();
$p->cmimi = 17;
$p->sasia = 3;
echo "<p>Objekti 2, vlera: ".$p->vlera();
// Rezultati: 
// Objekti 2, vlera: 51

Në shembullin e mësipërm, objekti $o është objekt i pavarur nga objekti $p edhe pse të dy objektet janë krijuar si instanca të klasës së njëjtë. Ndryshimi i vetisë cmimi te njëri objekt, nuk ka fare ndikim në vlerën e vetisë cmimi të objektit tjetër. E njëjta vlen edhe për vetinë sasia. Pra, gjersa te objekti $o, vetia cmimi e ka vlerën 12, e njëjta veti te objekti $p e ka vlerën 17, dhe këto dy vlera ekzistojnë paralelisht, për dallim nga variablat ku ruhet vlera që është dhënë më vonë përgjatë rrjedhës së ekzekutimit të programit:

<?php
$cmimi = 12;
$cmimi = 17;
echo "<p>Cmimi: ".$cmimi;
// Rezultati: 
// Cmimi: 17;

Jetëgjatësia e objektit

Duhet të theksohet se jetëgjatësia e objektit është aq sa e skriptës brenda së cilës është krijuar. Në momentin e mbarimit të ekzekutimit të skriptës apo të ndërprerjes së ekzekutimit të saj, objekti automatikisht shkatërrohet dhe nuk ruhet askund. Nëse asaj skripte i bëjmë refresh në shfletues, objekti do të rikrijohet me vlerat iniciale dhe nuk do të ruhen vetitë e tij gjatë ekzekutimit paraprak.

Konstruktori

Gjatë instancimit të një klase, ne mund t’i japim klasës disa vlera që do të përdoren si vlera hyrëse, të cilat mund të jenë të ndryshme për çdo objekt të ri që krijojmë. Klasën mund ta thërrasim në të njëjtën mënyrë siç bëjmë me funksionet: duke shënuar një numër të argumenteve, të cilat më pas i përcillen një metode speciale e cila quhet konstruktor dhe në PHP emërtohet __construct().

<?php
class Produkti {
    public $cmimi;
    public $sasia;

    public function __construct($c, $s) {
        $this->cmimi = $c;
        $this->sasia = $s;
    }   
    public function vlera() {
        $v = $this->cmimi * $this->sasia;
        return $v;
    }
}

$o = new Produkti(12, 5);
echo "<p>Objekti 1, vlera: ".$o->vlera();
// Rezultati:
// Objekti 1, vlera 60

Metoda __construct() është metodë speciale, e cila thirret në momentin e krijimit të objektit, respektivisht të instancimit të klasës. Sikurse çdo funksioni tjetër, edhe këtij funksioni mund t’i përcjellim parametra, të cilat nevojiten gjatë krijimit të objektit. Në rastin konkret, janë bartur 2 vlera: numri 12 në variablin $c dhe numri 5 në variabli $s.

Variablat $c dhe $s janë variabla lokale dhe si të tilla mund të përdoren vetëm brenda funksionit __construct(), por jo edhe në funksionet/metodat e tjera të kësaj klase, për vetë faktin se janë variabla lokale dhe jetëgjatësia e tyre është aq sa e funksionit përkatës.

Nëse vlerat e bartura në konstruktor dëshirojmë t’i përdorim edhe tek funksionet/metodat e tjera të klasës së njëjtë, ato vlera duhet t’i ruajmë në variablat e deklaruara brenda klasës: $cmimi dhe $sasia, të cilave iu qasemi me $this->cmimi dhe $this->sasia:

    public function __construct($c, $s) {
        $this->cmimi = $c;
        $this->sasia = $s;
    }

Prej këtij momenti, cilado prej metodave të klasës mund t’iu qaset këtyre vlerave përgjatë gjithë kohës së ekzekutimit të skriptës, pra prej krijimit të objektit deri në momentin e shkatërrimit të tij.

Konstruktori përdoret për inicializimin e vlerave të vetive të ndryshme, si dhe për kryerjen e veprimeve përgatitëse gjatë procesit të krijimit të objektit, si p.sh. konektimi me MySQL serverin, leximi i vlerave të konfiguracionit, krijimi i objekteve nga klasat e tjera që i nevojiten kësaj klase, etj.

Destruktori

Destruktori është metodë speciale që aktivizohet para shkatërrimit të objektit. Përdoret për veprimet e nevojshme në fund të ekzekutimit të skriptës, siç mund të jenë: mbyllja e koneksionit me MySQL serverin, ruajtjen e objektit të serializuar në një medium persistent siç është sistemi i fajllave apo sistemi për menaxhimin e databazave, etj.

<?php
class Produkti {
    public $cmimi;
    public $sasia;

    public function __construct($c, $s) {
        $this->cmimi = $c;
        $this->sasia = $s;
    }   

    public function __destruct() {
        $obj = serialize($this);
        file_put_contents( __DIR__."/object.txt", $obj);
    }

    public function vlera() {
        $v = $this->cmimi * $this->sasia;
        return $v;
    }
}


$o = new Produkti(12, 5);
echo "<p>Objekti 1, vlera: ".$o->vlera();
// Rezultati:
// Objekti 1, vlera 60

Në shembullin e mësipërm, brenda metodës __destruct(), fillimisht bëhet serializimi i objektit, respektivisht konvertimi i objektit në një varg të vlerave të të gjitha vetive të objektit që më pas do të ruhet si string brenda një fajlli tekstual object.txt.

Përmbajtja e object.txt:

O:8:"Produkti":2:{s:5:"cmimi";i:12;s:5:"sasia";i:5;}

Kjo do të mundësojë që në një moment të mëvonshëm kohor të bëhet rikrijimi i objektit të shkatërruar, duke i lexuar vlerat e vetive të objektit nga fajlli tekstual object.txt.

Rikrijimi i objektit

Siç u cek më sipër, në PHP, në momentin e përfundimit të ekzekutimit të skriptës, t gjitha objektet, sikurse edhe variablat, shkatërrohen, që do të thotë se skriptat që ekzekutohen më vonë, përfshirë këtu edhe vetë skriptën ku është krijuara objekti, nuk do të kenë qasje në atë objekt. Për këtë arsye, nëse një objekt do të na duhet edhe në skriptat e tjera, fillimisht atë objekt e serializojmë dhe e ruajmë në një medium persistent, siç është sistemi i fajllave, sistemi i menaxhimit të databazave, sesionet, cookie, etj. Serializimin dhe ruajtjen e bëjmë te destruktori i klasës.

Në skriptën që ekzekutohet më pastaj, së pari duhet ta lexojmë objektin e serializuar, e më pas ta konvertojmë në objekt.

<?php
include('Produkti.php');
$us = file_get_contents( __DIR__."/object.txt");
$o = unserialize($us);
echo "<p>Vlera: ".$o->vlera();

Me funksionin unserialize() lexohen vetëm vlerat e vetive të objektit, sepse serialize() i ruan vetëm vetitë por jo edhe metodat. Prandaj, Për ta rikrijuar objektin dhe për të vazhduar më tej me përdorimin e tij, është e domosdoshme që fillimisht të inkludohet klasa, instancë e të cilës është objekti, pra klasa prej së cilës është krijuar objekti. Me inkludimin e klasës, pra mundësohet qasja në vetitë dhe metodat e objektit, dhe objekti i rikrijuar në tërësi i njëjtë me objektin i cili u shkatërrua në fund të ekzekutimit të skriptës paraprake. Objekti i ri është i njëjtë për nga vlerat e vetive, por nuk është objekt identik me objektin e shkatërruar sepse tash ka ID tjetër. ID-në e objektit e shohim me funksionin spl_object_hash():

$o = new Produkti(12, 5);
$p = new Produkti(34, 3);

echo "<p>ID e objektit o: ".spl_object_hash($o);
// Rezultati i mundshëm: 000000006a72b2a300000000248ccba9
echo "<p>ID e objektit o: ".spl_object_hash($p);
// Rezultati i mundshëm: 000000006a72b2a000000000248ccba9

Këtu shihet se dy objekte të ndryshme, qofshin edhe të klasës së njëjtë, do të kenë ID të ndryshme që PHP do t’i shfrytëzojë si tregues intern (internal handle). Gjatë rikrijimit të objektit nga të dhënat e serializuara, do të fitohet objekt me vlera të njëjta të vetive sikurse që i ka pasur objekti i ruajtur në momentin e shkatërrimit, si dhe listën e njëjtë të metodave, por nuk do të ketë ID të njëjtë.

Vërejtje: Shembulli i mësipërm është tepër rudimentar dhe shërben vetëm për ilustrimin e konceptit. Nuk preferohet të përdoret gjatë produksionit sepse në fajllin object.txt do të ruhet vetëm objekti i fundit që është shkatërruar, pa marrë parasysh numrin e objekteve të krijuara brenda skriptës dhe numrit të vizitorëve që në të njëjtën kohë janë duke e përdorur atë skriptë. Në produksion, ruajtjen e bëjmë ose në databazë, ose në sistemin për menaxhimin e sesioneve.

Vetitë statike

Në kuadër të një klase mund të definojmë veti statike, duke e shtuar fjalën static në këtë formë:

public static $shitorja;

Kjo veti do të jetë në dispozicion të të gjitha objekteve të krijuara si instanca të të njëjtës klasë. Përderisa vetitë $cmimi dhe $sasia mund të kenë vlera të ndryshme për objekte të ndryshme të të njëjtës klasë, vetia $shitorja do të jetë e njëjtë për të gjitha objektet e të njëjtës klasë. Me ndryshimin e vlerës së vetisë statike, ai ndryshim do të reflektohet tek të gjitha objektet e asaj klase. Vetive statike në PHP iu qasemi drejtpërsëdrejti duke iu referuar klasës, pa pasur nevojë që të instancohet klasa, si vijon:

Produkti::$shitorja = 3;

Siç shihet, njëherë shënohet emri i klasës, pastaj shenja ::, e më pas emri i vetisë me parashenjën $, dhe në fund vendoset vlera =3.

Shenja :: quhet “Scope Resolution Operator” (ose edhe “Paamayim Nekudotayim”).

Në rastin konkret, vlera 3 i jepet vetisë $shitorja të klasës Produkti, dhe të gjitha objektet e krijuara nga klasa Produkti do ta shohin atë vlerë të njëjtë. Në rast se atë vlerë e ndryshojmë më poshtë në rrjedhën e ekzekutimit të programit, ajo vlerë do të ndryshohet tek të gjitha objektet e klasës Produkti.

Vetitë statike mund t’i ndryshojmë edhe para, edhe pas instancimit të klasës. Në shembullin e mëposhtëm, njëherë e ndryshojmë para instancimit të klasës:

Produkti::$shitorja = 3;

e më pas e ndryshojmë pas instancimit të klasës:

Produkti::$shitorja = 5;

Kjo është e mundur për faktin se vetitë statike nuk varen fare nga instancimi i klasës, pra janë të pavarura nga instancat, sepse nuk janë vlera që lidhen për objektet e caktuara por për klasën në tërësi.

Shembull:

<?php
class Produkti {
    public $cmimi;
    public $sasia;
    public static $shitorja;

    public function __construct($c, $s) {
        $this->cmimi = $c;
        $this->sasia = $s;
    }
    public function vlera() {
        $v = $this->cmimi * $this->sasia;
        return $v;
    }
}

Produkti::$shitorja = 3;

$o = new Produkti(12, 5);
$p = new Produkti(8, 12);


echo "<p>Objekti 1, shitorja: ".$o::$shitorja;
echo "<p>Objekti 2, shitorja: ".$p::$shitorja;

Produkti::$shitorja = 5;
echo "<p>Objekti 1, shitorja: ".$o::$shitorja;
echo "<p>Objekti 2, shitorja: ".$p::$shitorja;
// Rezultati:
// Objekti 1, shitorja: 3
// Objekti 2, shitorja: 3
// Objekti 1, shitorja: 5
// Objekti 2, shitorja: 5

Metodat statike

Metodat statike veprojnş njëjtë si funksionet në programimin procedural; përmbajnë vetëm variabla lokale, vlerat e të cilave nuk ndërlidhen me instancën e klasës. Me fjalë të tjera, brenda metodës statike nuk mund t’iu qasemi anëtarëve të klasës (vetive dhe metodave) me $this.

Shembull:

<?php
class Produkti {

    public static function vlera($cmimi, $sasia) {
        $v = $cmimi * $sasia;
        return $v;
    }
}

echo Produkti::vlera(10,5);
// Rezultati:
// 50

Për t’iu referuar ndonjë vetie apo metode statike brenda klasës, përdoret self::.

Shembull:

<?php
class Produkti {
    public static $cmimi = 0;

​   public static function vlera($sasia) {
​       $v = self::cmimi * $sasia;
​       return $v;
​   }
}

Produkti::cmimi = 10;
echo Produkti::vlera(5);
// Rezultati:
// 50

Vizibiliteti

Në PHP, vetitë dhe metodat mund të jenë:

  • private,
  • protected (të mbrojtura), dhe
  • public (publike).

private

Vetive dhe metodave të mbrojtura mund t’iu qasen vetëm metodat brenda vetë klasës. Kjo është e dobishme për ato metoda të cilat nuk dëshirojmë t’ia ekpozojmë klient kodit, respektivisht kodit në të cilën inkludohet një klasë.

Në rastin e një klase që përdoret si kontroller tek arkitektura MVC, si metoda private dhe protected deklarohen ato metoda të cilave nuk u lejohet qasja direkte nga rutat.

protected

Vetive dhe metodave të mbrojtura mund t’iu qasen metodat brenda vetë klasës, si dhe metodat nga subklasat.

public

Vetive dhe metodave publike mund t’iu qasen metodat brenda vetë klasës, metodat nga subklasat, si dhe klient kodi. Klient kod konsiderohet kodi brenda të cilit bëhet instancimi i klasës, pra krijimi i objektit, ku edhe do të përdoret ai objekt.

Tahir Hoxha