PHP dhe MySQL për fillestarë
PHP dhe MySQL për fillestarë
Tahir Hoxha
Buy on Leanpub

Table of Contents

Parathënie

Ky manual është përmbledhje e materialeve të ndryshme të përdorura në kurset e PHP/MySQL në NSH “ORBIS” (Prizren), Innovation Centre Kosova (Prishtinë) dhe SmartCode (Prishtinë).

Materiali është ende i pasistemuar dhe i pakompletuar sepse libri është në procesin e përgatitjes, ndërkohë që është vështirë të parashikohet se kur libri mund ta marrë formën e vet finale.

Janë të mundshme gabime të ndryshme si në tekst, ashtu edhe në kod, por do të përpiqem që ato t’i eliminoj apo t’i sjell në minimum.

Vështirësia më e madhe me një libër për PHP është mungesa e terminologjisë për termet nga IT, në veçanti të atyre specifike për Web. Prandaj, në raste të caktuara janë përdorur fjalë në gjuhën angleze, të cilat do të zëvendësohen me ato shqipe kur të gjendet përkthimi i përshtatshëm i tyre.

Libri i dedikohet fillestarëve, por aty-këtu temat janë diç më të avancuara, gjë që nuk do të duhej të konsiderohej si mangësi sepse mund të hyjnë në punë kur lexuesi ta ketë arritur një nivel më të lartë të njohurive në PHP programim.

Libri është duke u përgatitur me përdorimin e Markdown, që e bën formatimin e faqeve më pak të sofistikuara se ato në ndonjë tekst procesor. Ky kufizim është i imponuar nga shtëpia botuese “Leanpub”, për shkak se prej momentit të ruajtjes së dokumenteve deri në publikim në Web, i gjithë procesi është i automatizuar. Për shkak të automatizimit të procesit, mund të ndodhë që ndonjë pjesë e ndonjë faqeje të mos dalë e formatuar në mënyrë korrekte në momentin e publikimit, por gjithsesi do të bëj përpjekje që edhe ky lloj i gabimeve të eliminohet.

Në këtë fazë, libri ofrohet gratis dhe mund të shpëndahet lirisht.

I mirëpres këshillat, sugjerimet, kritikat dhe korigjimet e gabimeve nëpër tekst.

Autori

Kontakti:

  • Email: tahir.hoxha@gmail.com
  • Facebook: /hoxha.tahir
  • Twitter: @Alkimisti

Hyrje në PHP

## Arkitektura klient/server

Arkitektura klient/server është një model që vepron si aplikacion i shpëndarë (distributed application), i cili bën ndarjen e detyrave ndërmjet ofruesve të resurseve apo shërbimeve (serverët) dhe kërkuesve të atyre shërbimeve (klientët).

Klientët dhe serverët komunikojnë nëpërmjet rrjetit kompjuterik në pajisje të ndryshme harduerike.

Serveri është host në të cilin ekzekutohen një apo më tepër programe speciale, që kanë për detyrë t’i ndajnë resurset me klientët.

Klienti e inicon sesionin e komunikimit me serverin, i cili është në pritje të kërkesave nga klientët.

Web serverët

Dedikim parësor i Web serverëve është distribuimi i Web faqeve me kërkesën e klientëve, duke u bazuar në protokolin HTTP (Hypertext Transfer Protocol).

Kjo përfshin dërgimin e dokumenteve të tipeve të ndryshme, imazheve, audio/video fajllave, etj.

Shfletuesi (browser, user agent) e inicon komunikimin duke dërguar kërkesë për një resurs specifik duke përdorur HTTP. Në këtë serveri i përgjigjet duke ia dërguar përmbajtjen e kërkuar apo duke kthyer raportin e gabimit, nëse ai resurs nuk është gjetur.

Resursi i kthyer mund të jetë fajll real që ekziston në diskun e serverit, por edhe mund të jenë të dhëna të gjeneruara në mënyrë dinamike.

Përveç dërgimit të të dhënave, serveri është në gjendje edhe të pranojë të dhëna nga klientët.

Kjo rëndomë ka të bëjë me pranimin e përmbajtjes së ndonjë formulari, apo të fajllave të atashuar.

Shumë Web serverë, përkrahin gjuhët skriptuese siç janë PHP, ASP, Perl, Python, etj.

Këto më tepër përdoren për krijimin e HTML dokumenteve dinamike, që gjenerohen aty për aty (on-the-fly).

HTTP

HTTP funksionet janë protokole kërkesë-përgjigje (request-response) në modelin klient-server.

Klienti ia dërgon serverit kërkesën në formë të HTTP mesazhit kërkues (HTTP request message).

Serveri nga ana e vet, çdo përgjigjeje të kthyer ia shoqëron edhe raportin (response message) që i dërgohet klientit.

Ai raport përmban statusin mbi realizimin e kërkesës, dmth nëse është kompletuar apo jo dhe çka mund të jetë arsyeja e gabimit. Raporti se gjithçka ka vajtur në rregull:

1 HTTP/1.1 200 OK

1. Ç’është PHP?

PHP është shkurtesë nga Personal Home Page. I krijuar në vitin 1994 nga Rasmus Lerdorf, me qëllim të përcjelljes së vizitave në Web faqen që e përmbante rezymenë e tij. Kuptimi që i jepet sot është PHP: Hypertext Preprocessor.

Është gjuhë programore, e dedikuar në veçanti për zhvillimin e Web aplikacioneve. Duke qenë gjuhë për përpilimin e skriptave që ekzekutohen në server, programet e shkruara në PHP nuk kompajlohen, por interpretohen gjatë ekzekutimit. Efekti kryesor negativ i kësaj qasjeje është se aplikacionet, gjegjësisht skriptat, ekzekutohen më ngadalë se ato të kompajluara, siç janë aplikacionet e shkruara në gjuhët programore C apo C++. Në këtë aspekt, PHP është i krahasueshëm me JavaScript, me një dallim thelbësor se JavaScript ekzekutohet brenda shfletuesit, ndërsa PHP ekzekutohet ekskluzivisht në server.

Meqenëse shfletuesit i njohin vetëm kodet e përpiluara në HTML, CSS dhe JavaScript, edhe rezultati që gjenerohet nga një PHP skriptë duhet të jetë në njërën nga gjuhët e përmendura. Kodi i PHP mund të futet brenda kodit HTML, ku është në gjendje t’i shtojë faqes ekzistuese kodin HTML të gjeneruar në mënyrë programore.

Pra, përdorimi i PHP është shumë specifik: gjenerimi i HTML kodit që insertohet në një HTML faqe ekzistuese. Me PHP nuk mund të shkruhen aplikacione të pavarura që rezultojnë në një .exe aplikacion.

Kodi në PHP ekzekutohet në server, jo tek klienti (shfletuesi). Për dallim nga JavaScript, ekzekutimi i kodit në PHP nuk varet nga modeli apo versioni i shfletuesit të caktuar.

Skripta ruhet në server dhe shfletuesit i dërgohet vetëm rezultati i ekzekutimit të kodit, në formë të dokumentit të formatuar në HTML/CSS.

Për këtë shkak, skripta në PHP nuk mund të shihet në shfletues me "View Source", gjë që është avantazh në aspektin e mbrojtjes së pronës intelektuale, por edhe në aspektin e sigurisë së aplikacionit.

Sipas Bacon J. (2007), PHP ofron një gjuhë programuese solide dhe mirë të definuar që përfshin përkrahjen për programim të orientuar në objekte (object-orientated programming - OOP), strukturat logjike, manipulimet me fajlla, aritmetikë, e shumëçka tjetër.

Gjuha PHP është e ngjashme semantikisht me atë të gjuhëve skriptuese kombinuar me lehtësitë që ofron gjuha C.

  • Me burim të hapur (open source)
  • Pa pagesë
  • I instaluar në miliona Web serverë
  • Platformë për edhe më tepër Web sajte
  • I ngjashëm me C për nga sintaksa
  • Mund të zgjerohet me biblioteka në gjuhët e tjera

Performanca

Skriptat e shkruara në PHP tregojnë peformanca më të mira se ato në gjuhët tjera, në radhë të parë për nga shpejtësia e ekzekutimit.

Versioni 5.0 i PHP është rishkruar nga zeroja, me çka është përmirësuar dukshëm në aspektin e shpejtësisë.

Janë në dispozicion akceleratorë të ndryshëm për t’ia rritur performancat e përgjithshme.

Portabiliteti

PHP-në e gjejmë në shumë sisteme operative: UNIX, Microsoft Windows, Mac OS, dhe OS/2.

PHP aplikacionet e shkruara në një platformë munden me aspak apo fare pak përshtatje të ekzekutohen në cilëndo pratformë tjetër.

Lehtësia e përdorimit

  • Kjo gjuhë programore është gjuhë tejet e sofistikuar.
  • Ka sintaksë të qartë dhe konzistente.
  • Përmban mbi 5000 funksione.
  • Dokumentacioni është i begatshëm dhe mbulon çdo aspekt të programimit.
  • Është i lehtë për mësim.
  • Mundëson zhvillim rapid të aplikacioneve.

Kodi i hapur

  • PHP ka kod të hapur, që dmth se programuesit mund ta modifikojnë kodin sipas nevojës.
  • Kodi i hapur jep mundësinë që zhvilluesit e aplikacioneve t’u gjejnë gabimet, t’ia raportojnë atë komunitetit, t’i korigjojnë, etj.
  • Mund të merret pa pagesë.
  • Nuk kërkon harduer të shtrenjtë.
  • Mundëson reduktimin e kostos së zhvillimit të aplikacioneve, duke mos e dëmtuar në asnjë aspekt.

Mbështetja e komunitetit

  • PHP është gjuhë programore që gëzon mbështetje të një komuniteti të gjerë të Web zhvilluesve nga e gjithë bota.
  • Si rezultat e punës me mbështjetje nga komuniteti, është:
  • PEAR (PHP Extension and Application Repository, http://pear.php.net/), dhe
  • PECL (the PHP Extension Community Library, http://pecl.php.net/).
  • Këto dy librari përmbajnë ekstensione të shumta që i shtojnë PHP-së funksione të reja, të cilat e lehtësojnë në masë të madhe procesin e programimit.

Ndërlidhja me aplikacionet tjera

PHP mund të lidhet me një numër të konsiderueshëm të sistemeve për menaxhimin e databazave relacionale, siç janë: MySQL, PostgreSQL, Oracle, dhe Microsoft SQL Server.

Versionet më të reja të PHP përkrahin mbi 15 sisteme të ndryshme të databazave, duke poseduar edhe një API të përbashkët për të gjitha.

Përveç databazave, PHP mund të lexojë dhe krijojë XML dokumente, t’i qaset strukturës së XML me XPath, si dhe të kryejë transformimet gjegjëse me XSLT.

Zgjerimi i gjuhës

  • PHP lejon që programuesit të zhvillojnë shtojca të ndryshme për këtë gjuhë, duke e zgjeruar funksionalitetin e tij.
  • Këto shtojca mundësojnë që sot të mund të kryhen:
  • Manipulime me imazhet (GIF, JPEG, and PNG),
  • Zhvillimi i komunikimit me postën elektronike,
  • Të zhvillohen Web servise të bazuara në protokolet REST dhe SOAP, etj.
  • PHP mund t’u qaset bibliotekave në C, klasave të Java dhe COM objekteve.

Funksionimi i Web faqes

Në momentin që një vizitor i sajtit, bën kërkesë për hapjen e një HTML dokumenti, Web serveri ia përcjell HTML dokumentin e ruajtur, paraprakisht duke e interpretuar bllokun e kodit në PHP dhe shndërruar rezultatin në pjesë integrale të faqes.

Mund të thuhet se kodi në PHP i jep gjallëri faqes, duke e ndryshuar përmbajtjen në mënyrë inteligjente duke iu përgjigjur zgjedhjeve që bën vizitori me anë të klikimit apo plotësimit të të dhënave.

Procesi i ekzekutimit

  • Vizitori e hap shfletuesin dhe e shënon adresën (URL) e faqes së dëshiruar.
  • Në bazë të adresës së shënuar, shfletuesi ia dërgon një kërkesë (HTTP request) Web serverit përkatës.
  • Web serveri, me të pranuar të kërkesës, e gjen në server fajllin përkatës në PHP.
  • Përmbajtja e fajllit të zgjedhur përcillet tek PHP interpreteri.
  • PHP intepreteri e analizon fajllin, duke ekzekutuar kodet e shkruara në PHP (kodi ndërmjet ).
  • Brenda këtyre dy etiketave mund të ketë kode:
  • Për të kryer analizën e të dhënave hyrëse,
  • Për komunikim me databazën,
  • Për kryerjen e llogaritjeve të ndryshme, etj.
  • Pasi të jetë kryer ekzekutimi i kodeve të shkruara në PHP, Web serveri e dërgon faqen e gjeneruar drejt shfletuesit të vizitorit, me çka përmbyllet procesi.

Inkorporimi i PHP kodit në HTML

Shembull i inkorporimit të PHP kodit brenda një HTML dokumenti:

1 Leksioni 1
2 
3 
4 Ky tekst është brenda HTML

Rreshtat 1, 2, 3, 4, 5, 6, 10, 11 janë të shkruara në HTML, ndërsa rreshtat 7, 8 dhe 9 janë në PHP. Kodi rezultues në HTML do të duket kështu:

1 Leksioni 1
2 
3 
4 Ky tekst është brenda HTML
5 Ky është dërguar nga PHP

Kod të këtillë do të shohim kur zgjedhim p.sh. opsionin “View Source” në shfletuesin Google Chrome.

Kodi PHP nuk shihet me “View Source”, sepse paraprakisht është transformuar në HTML në server, për t’u dërguar pastaj në shfletues.

Bazuar në shembullin e mësipërm, mund të nxjerren disa përfundime:

  1. HTML dokumentin që brenda vetes përmban PHP kod, duhet ta ruajmë me ekstensionin .php dhe jo .html, siç bëhet rëndomë. Në bazë të këtij ekstensioni, Web serveri vendos nëse ai dokument duhet të dërgohet i pandryshuar drejt shfletuesit (rasti me .html), apo duhet paraprakisht të dërgohet në PHP parser për t’u transformuar në HTML dokument.
  2. Kodi që shkruhet në PHP, fillon me etiketën . Thënë ndryshe, e vazhdon më tutje rrjedhën e atij HTML dokumenti. Si rezultat përfundimtar do ta kemi një HTML dokument, përmbajtja e të cilit është modifikuar në mënyrë dinamike. Mund të shprehemi edhe në mënyrë të thjeshtë: HTML + PHP = HTML.
  3. Konstrukti gjuhësor echo e kryen një detyrë të thjeshtë: gjithë tekstin që është brenda thonjëzave e dërgon në “ekran”, gjegjësisht ia bashkangjet atë HTML dokumentit dhe atë pikërisht në pozitën ku është shënuar PHP kodi. Meqenëse në shembullin tonë, PHP kodi është i shënuar pas dhe përfundon para , edhe HTML kodi rezultues do të vendoset pikërisht brenda kësaj zone:
  4. Çdo rresht programor në PHP duhet të përfundojë me pikëpresje (;). Për dallim nga JavaScript, ku pikëpresja është opsionale, në PHP ajo është e domosdoshme dhe mungesa e pikëpresjes në ndonjë rresht mund të rezultojë në gabime të paparashikueshme gjatë ekzekutimit të skriptës.
  5. Dokumenti që përmban PHP kod nuk ekzekutohet si programet tjera aplikative duke klikuar në ikonën përkatëse në desktop apo Start menu, por ekzekutohet duke e shënuar URL-në e tij në shfletues. Në raste të caktuara, një PHP skriptë mund të startohet edhe nga rreshti komandues.
  6. Në rastin konkret, URL-ja është: http://localhost/leksioni1.php, ku http://localhost/ paraqet adresën e serverit lokal, ndërsa leksioni1.php është vetë emri i skriptës. Në rast se këtë skriptë e kopjojmë në Web server të kompanisë së hostingut, localhost zëvendësohet me domainin përkatës, p.sh.: http://faqjaime.com/leksioni1.php
  7. Rezultat i ekzekutimit të PHP skriptës konkrete do të jetë një HTML dokument, të cilit do t’i shtohen HTML kode shtesë, për ta formuar një tërësi që i dërgohet shfletuesit për ta paraqitur në ekran.

Skripta e mësipërme nuk kryen ndonjë gjë të veçantë, sepse shtimin e paragrafit me një fjali do të mund ta realizonim duke koduar drejtpërsëdrejti në HTML. Fuqia e vërtetë e PHP vie në shprehje atëherë kur nevojitet t’i shtohet faqes ndonjë informatë e karakterit variabil, gjegjësisht dinamik, siç janë p.sh. të dhënat meteorologjike, rezultatet sportive, kurset valutore, postimet nga forumet apo rrjetat sociale, produktet e porositura në një e-dyqan, etj. Në të tilla situata, përmbajtja e faqes nuk paraqitet e njëjtë tek të gjithë vizitorët e sajtit, por do të jetë e ndryshueshme varësisht nga dinamika e ndryshimeve të të dhënave si dhe nga zgjedhjet që i bën vetë vizitori.

Për t’i paraqitur të dhënat e tilla, PHP komunikon me Sistemin për Menaxhimin e Databazave Relacionale (RDBMS), ku më së shpeshti e gjejmë të përdorur sistemin MySQL. Kështu, PHP e siguron logjikën e aplikacionit, ndërsa MySQL i siguron të dhënat të cilat do të përpunohen për t’u paraqitur më pas si faqe HTML në ekranin e vizitorit nëpërmes shfletuesit. PHP interpreteri nuk vepron i pavarur, por mbështetet në serviset që i ofron një Web server. Pra, minimumi i nevojshëm për ta krijuar një ambient për zhvillimin e aplikacioneve dinamike konsiston në instalimin e Web serverit, PHP interpreterit dhe një sistemi për menaxhimin e databazave.

Me një ndryshim fare të vogël në kodin e mësipërm, do të jemi në gjendje të fitojmë një të dhënë dinamike siç është data aktuale, e cila do të ndryshojë vetë me kalimin në datë të re. Përderisa tek HTML faqet statike, për ndryshime të këtilla nevojitet modifikimi i datës brenda HTML dokumentit dhe publikimi i sërishëm i atij dokumenti në Web server, tek përdorimi i PHP kodit kjo detyrë eliminohet sepse sistemi e kryen vetë në mënyrë automatike.

1 Leksioni 1b
2 
3 
4 Ky tekst është brenda HTML

Përgatitja e ambientit të punës

## Instalimi dhe konfigurimi i LAMP/WAMP serverit

Një fillestar ka nevojë të njihet me tërë procesin e instalimit dhe konfigurimit të programeve të nevojshme, për të qenë në gjendje pastaj t’i testojë shembujt e dhënë në manual.

Nëse kodin nga shembulli i parë do të tentonim ta ekzekutonim duke e hapur fajllin leksioni1.php drejtpësëdrejti nga shfletuesi, ai fajll do të na ofrohej për shkarkim dhe nuk do të hapej si Web faqe, siç do të prisnim nga përvojat e mëhershme me HTML dokumente.

Kjo ndodh për shkak se faqja që përmban PHP kod duhet së pari të procesohet në Web server, për t’u transformuar në HTML dokument të cilin pastaj mund ta “kuptojë” një shfletues. Derisa nuk procesohet, është fajll i tipit të panjohur për shfletuesin, prandaj ai ofron vetëm mundësinë e shkarkimit.

Për të qenë në gjendje për të zhvilluar PHP aplikacione në kompjuterin tuaj, nevojitet të instalohen disa aplikacione të cilët veprojnë në koordinim me njëri-tjetrin. Programet e nevojshme janë:

  • Apache Server
  • PHP
  • MySQL

Të gjitha këto aplikacione mund të shkarkohen nga Web faqe përkatës e të tyre dhe të instalohen secili veçmas, mirëpo pastaj nevojitet konfigurimi i tyre për t’i bërë që të funksionojnë si një tërësi. Ky proces di të jetë i ndërlikuar dhe i vështirë për fillestarët, prandaj zgjidhja është në shkarkimin dhe instalimin e të ashtuquajturve Apache–MySQL–PHP packages (AMP), të cilat kanë instalues që i bën këto konfigurime në mënyrë automatike, ku kërkohen intervenime minimale të përdoruesit.

AMP stack

Edhe pse është i mundur instalimi i secilit prej komponentave veç e veç, për shkak të konfigurimit të ndërsjelltë që nuk është i lehtë për fillestarë, mund të zgjedhur të instalojmë një «AMP stack».

AMP është akronim për Apache – MySQL – Perl/PHP/Python. Këto janë pako softuerike që përmbajnë kombinime të ndryshme të programeve.

  • LAMP – për Linux
  • WAMP – për Windows
  • MAMP – për Macintosh

AMP stack: Cross-platform

  • XAMPP
  • AMPPS
  • Zend Server Community Edition
  • Bitnami MAMPStack, LAMPStack, WAMPStack, RubyStack, DjangoStack, JRubyStack, LAPPStack, WAPPStack, MAPPStack, TomcatStack, JBoss, Solr, NodeJS (Open Source License, Apache 2, PHP 5.x,MySQL 5, Python 2.4)

AMP stack: Linux

  • LAMP
  • Bitnami LAMPStack (Open Source License, Apache 2/1.3, PHP 4.x/5.x, MySQL 4.1/5, Python 2.3/2.4)
  • Bitnami LAPPStack (Open Source License, Apache 2, PHP 5.x, PostgreSQL 8.1, Python 2.4)
  • AMPPS
  • XAMPP for Linux

AMP stack: Mac

  • AMPPS
  • MAMP
  • DAMP
  • Bitnami MAMPStack (Open Source License, Apache 2, PHP 5.x, MySQL 5, Python 2.4)
  • XAMPP for OS X

AMP stack: Windows

  • AMPPS
  • EasyPhp
  • Bitnami WAMP Stack
  • WAMPServer
  • XAMPP
  • Uniform Server
  • UwAmp
  • WT-NMP
  • WPN-XM Server Stack

IDE

Komerciale:

  1. JetBrains PhpStorm (http://www.jetbrains.com/phpstorm/)
  2. Zend Studio (http://www.zend.com/products/studio/)
  3. NuSphere PhpED (http://www.nusphere.com/products/phped.htm)
  4. NetBeans IDE (https://netbeans.org/features/php/)
  5. phpDesigner (http://www.mpsoftware.dk/phpdesigner.php)
  6. Embarcadero RadPHP
  7. WaterProof PHPEdit (http://www.waterproof.fr/)
  8. Komodo IDE (http://www.activestate.com/komodo-ide)
  9. Adobe Dreamweaver (http://www.adobe.com/products/dreamweaver.html)

Pa pagesë:

  1. Aptana Studio (http://www.aptana.com/products/studio3)
  2. Eclipse PHP PDT (http://www.eclipse.org/pdt/downloads/)
  3. Komodo Edit (http://www.activestate.com/komodo-edit)

Folderi në Web server

www (c:\wamp\www)

public_html (/home/icksite/public_html)

htdocs (c:Program Files (x86)ZendApache2\htdocs)

URL në shfletues:

http://localhost/

ose

http://127.0.0.1

Nëse zhvillojmë më tepër aplikacione, secili aplikacion duhet të vendoset në folder të veçantë:

c:\wamp\www\cms

Në shfletues, ky folder thirret me:

http://localhost/cms

ose

http://127.0.0.1/cms

Porti standard për protokolin HTTP është 80.

Mirëpo, nëse instalojmë edhe ndonjë Web server në hostin e njëjtë, nevojitet të përdoret një port i ri.

Portet e përdorura më së shpeshti për HTTP:

80, që është vlerë standarde e nënkuptuar (default)

8080, etj.

Për HTTPS: 443

Portet për serviset/protokolet e tjera:

21 – FTP

22 - SSH

25 – SMTP

110 – POP

143 – Incoming IMAP

3306 – MySQL

Folderin e aplikacionit mund ta krijojmë drejtpërsëdrejti brenda folderit në serverin lokal

1 www public_html htdocs

Në këtë rast, për testimin e aplikacionit vetëm duhet të startohet shfletuesi dhe të shënohet URL-ja përkatëse.

Qasja e dytë konsiston në krijimin e një folderi në diskun lokal për aplikacionin dhe një folder tjetër brenda Web serverit për testim.

Para çdo testimi, nevojitet që folderi i aplikacionit të dërgohet (upload) në folderin përkatës në Web server. Në Dreamweaver dhe në disa FTP programe mundësohet sinkronizimi i folderëve.

Sinkronizimi:

Krahasimi i datës/kohës së modifikimit të dokumenteve ndërmjet folderit të aplikacionit dhe folderit përkatës në Web server. Dokumentat që kanë data më të reja barten në folderin e Web serverit.

Ekzekutimi i php skriptave

PHP skriptat mund të ekzekutohen në dy mënyra:

  • Duke e kopjuar fajllin e skriptës në folderin publik të serverit
  • Nëpërmes CLI SAPI (Command Line Interpreter / Server Application Programming Interface)

Metoda e parë është standarde dhe rezultati shfaqet në dritaren e Web shfletuesit.

Me metodën e dytë, skriptat ekzekutohen nga rreshti komandues dhe po aty edhe shfaqen rezultatet.

Shkrimi dhe ekzekutimi i skriptave

Skriptat në PHP janë fajlla tekstualë që përmbajnë kode në PHP, shpesh të kombinuara edhe me kode në HTML, CSS, JavaScript, etj. Me përdorimin e një editori të rëndomtë të tekstit (Notepad), shkruani:

1 <?php
2 echo  ‘Skripta ime e parë!'; 
3 ?>

Ruajeni këtë dokument në folderin përkatës së Web serverit dhe emërojeni skripta1.php

Hapeni shfletuesin dhe shënojeni adresën korresponduese të dokumentit.

Procesi i gjenerimit të faqes rezultuese

Kur kërkojmë ekzekutimin e fajllit skripta1.php:

  • Web serveri e pranon kërkesën
  • Duke qenë se është fajll me ekstension .php, ky fajll i përcillet PHP interpreterit (parserit) për përpunim të mëtejmë
  • PHP parseri i ekzekuton kodet brenda <?php dhe ?>, e merr rezultatin, e bashkon me pjesën HTML të faqes në vendet përkatëse dhe dokumentin final ia kthen Web serverit
  • Web serveri e dërgon faqen e përpunuar në drejtim të shfletuesit.
  • Në rastin konkret, rezultati i kthyer është teksti: ‘Skripta ime e parë!’

Ndërprerja e ekzekutimit të skriptave

Gjatë testimit të një PHP skripte, mund të ndodhë që ajo të mos ekzekutohet, gjegjësisht të mos ekzekutohet deri në fund.

Kjo mund të ndodhë për disa arsye:

  • Gabime në instalimin apo konfigurimin e Web serverit, PHP parserit apo të ndonjë komponente tjetër, kështu që skripta as nuk mund të ekzekutohet.
  • Ka gabime në vetë kodin e PHP skriptës dhe në këtë rast, PHP parseri nuk mund të vazhdojë më tutje me ekzekutim.
  • Ekzekutimi i skriptës ka marrë shumë kohë.
  • Ekzekutimi i skriptës ka konsumuar shumë RAM memorje, më tepër se ç’lejon PHP.

Regullat themelore

Kodi në PHP fillon me <?php ose <?

Mbaron me ?>

Çdo PHP statement duhet të përfundojë me pikëpresje

Rreshtat e zbrazët injorohen

Komentet njërreshtor fillojnë me //

Komentet shumërreshtorë fillojnë me /* ndërsa mbarojnë me */

Komentet injorohen gjatë procesit të gjenerimit të rezultateve, gjegjësisht gjatë interpretimit

Përzierja e PHP me HTML

Kur PHP parseri e lexon skriptën, ai e ekzekuton vetëm pjesën që gjendet brenda PHP etiketave.

Gjithë ç’është jashtë këtyre «zonave», përcillet më tutje pa kurrfarë ndryshimi.

Me këtë mundësohet futja e kodeve PHP përbrenda dokumentit HTML, për të krijuar faqe që përmban të gjitha ato që ofron HTML në aspektin e përmbajtjes dhe strukturës, duke i shtuar edhe kalkulime, veprime me databazën, apo ndonjë gjë tjetër.

Pra, rezultat përfundimtar i këtij miksimi është një dokument krejtësisht në HTML/CSS.

Po të shikohet kodi burimor me «View Source», në asnjë vend nuk do të ketë kode të PHP.

Çdo echo dërgon rezultate në formë tekstuale apo të formatuar në HTML/CSS

Variablat

1 <?php
2 $a = 2;
3 $b = 5;
4 $c = $a * $b;
5 echo "<p>".$a."*".$b."=".$c."</p>";
6 ?>
Tipet e variablave
  • Integer: Numër i plotë. Varësisht nga sistemi operativ, diapazoni i vlerave ndryshon, por rëndom kemi të bëjmë me -2 miliardë deri në +2 miliardë.
  • Floating point number: Numër, rëndomë jo i plotë, që përfshin edhe vendet pas presjes dhjetore, psh 3.14, 4439,2343. Njihet si real number ose float.
  • Character string: Seri e karaktereve. Nuk ka limit sa i përket gjatësisë, gjegjësisht numrit të karaktereve
  • Boolean: Vlera TRUE (Saktë) ose FALSE (pasaktë). Përdoret në analizat logjike.

Matricat

 1 <?php
 2 $ditet = array("E hënë", "E martë", "E mërkurë", "E enjte", "E premte", "E shtunë", \
 3 "E diel");
 4 
 5 echo "<ul>";
 6 foreach($ditet as $dita)
 7 	{
 8 	echo "<li>".$dita."</li>";
 9 	}
10 echo "<ul>";	
11 	
12 
13 ?>

Variablat dhe konstantat

Variabli është një lokacion në memorje, të cilit i jepet një emër simbolik si identifikator, dhe në të vendosen një apo më shumë vlera. Vlerat e vendosura në variabla mund të jenë të tipeve të ndryshme: numra të plotë, numra decimalë, tekste, etj. Emri i variablit shërben si referencë e vlerës së ruajtur në lokacionin e caktuar. Vlera e një variabli mund të ndryshojë gjatë ekzekutimit të programit.

PHP bën pjesë në gjuhët programore të cilat nuk janë strikte sa i përket tipeve të të dhënave. Kjo do të thotë se nuk është i domosdoshëm deklarimi i variablave dhe tipeve të tyre përkatëse. Po ashtu, për dallim prej gjuhëve Java, C, C#, e të tjerë, PHP nuk ka shumë tipe të të dhënave.

Tipet e variablave në PHP:

  • SKALARE
  • Integer.
  • Float/Double.
  • Boolean.
  • String.

JOSKALARE - Array. - Object. - Resource. - NULL.

isset()

### unset() ### var_dump() ### print_r() ### empty() ### gettype() ### settype()

is_scalar()

### is_numeric() ### is_int() ### is_integer() ### is_long() ### is_float() ### is_double() ### is_real() ### is_bool() ### is_string() ### is_array() ### is_object() ### is_iterable() ### is_resource() ### is_null()

intval()

### floatval() ### doubleval() ### strval() ### boolval()

extract()

### compact() ### list()

var_export()

serialize()

Serializimi i një vargu apo objekti, respektivisht ruajtja e vargut apo objektit (më saktë, të vetive të objektit, jo edhe të metodave të tij) në formë të stringut, për të qenë i përshtatshëm për ruajtje në fajll ose databazë.

1 $lista = [ 1=>"E hënë", "E martë", "E mërkurë", "E enjte", "E premte", "E shtunë", "\
2 E diel"];
3 $slista = serialize($lista);
4 file_put_contents( "lista.txt", $slista );

Rezultati: a:7:{i:1;s:8:"E hënë";i:2;s:8:"E martë";i:3;s:11:"E mërkurë";i:4;s:7:"E enjte";i:5;s:8:"E premte";i:6;s:9:"E shtunë";i:7;s:6:"E diel";}

unserialize()

Shndërrimi i të dhënave të serializuara në varg ose objekt.

1 <?php
2 ###### $lista2 = file_get_contents("lista.txt");
3 var_dump(unserialize($lista2));

Rezultati:

1 array (size=7)
2   1 => string 'E hënë' (length=8)
3   2 => string 'E martë' (length=8)
4   3 => string 'E mërkurë' (length=11)
5   4 => string 'E enjte' (length=7)
6   5 => string 'E premte' (length=8)
7   6 => string 'E shtunë' (length=9)
8   7 => string 'E diel' (length=6)

Strukturat

Sikurse gjuhët tjera programore, edhe PHP posedon struktura me anë të të cilave ndërtohet logjika e një aplikacioni. Këtu bëjnë pjesë:

  • Strukturat e degëzimit
  • Struktura e iteracionit

Strukturat e degëzimit

Rrjedha normale e ekzekutimit të rreshtave programor është rrjedhë lineare: pas ekzekutimi të rreshtit të parë, interpreteri kalon në të dytin, në të tretin, e kështu me rradhë deri në fund të programit.

Ekzekutimi sekuencional i rreshtave programorë është i përshtatshëm në rastet kur duhet të ekzekutohet një seri e urdhërave të përsëritshme, psh:

  • Konketohu me server
  • Lexoji të dhënat nga një tabelë
  • Paraqiti të dhënat e lexuara në formë tabelare

Një mënyrë e këtillë e organizimit të kodit presupozon se ekzekutimi nuk do të ndërpritet në asnjërin prej rreshtave, gjë që nuk është konform kushteve reale.

Për shembull, nëse programi nuk konektohet me server, çfarë kuptimi do të kishte leximi i të dhënave nga njët tabelë? Apo, si do të paraqiteshin të dhënat në formë tabelare nëse paraprakisht programi ka dështuar në leximin e të dhënave nga tabela?

Duke u bazuar në këtë shembull të thjeshtë, nxjerrim konkluzionin dhe programi ynë duhet të ketë njëfarë “intelegjence”, e cila do të dijë ç’të bëjë në të dy situatat: edhe kur paraqitet gabimi, edhe kur nuk paraqitet.

Tash, e modifikojmë pak logjikën e algoritmit:

  • Tento të konektohesh në server
  • Nëse dështon, ndërpreje ekzekuzimin, duke raportuar se nuk u realizua koneksioni.
  • Nëse ke sukses, vazhdo më tutje në rreshtin vijues
  • Tento të lexosh të dhënat nga një tabelë
  • Nëse dështon, ndërpreje ekzekutimin, duke raportuar se nuk u lexuan të dhënat nga tabela
  • Nëse ke sukses, kontrollo sa rezultate kanë arritur
  • Nëse rezultate ka 0, pra nuk ka rezultate, ndërpreje ekzekuzimin dhe raporto se tabela nuk ka të dhëna
  • Nëse ka rezultate, atëherë paraqiti në formë tabelare.

Këtu tash shohim se programi e ndërpren ekzekutimin në 3 raste:

  • Dështoi koneksioni në server
  • Dështoi leximi nga tabela
  • Tabela nuk ktheu rezultate

Në shembullin e mësipër, analiza “nëse-kjo-atëherë-ajo” mundëson degëzimin e programit, ku njëra degë e ndal ekzekutimin, ndërsa tjetra e vazhdon.

Degëzimi nuk bëhet vetëm kur duhet të ndalet ekzekutimi i programit, por më shpesh përdoret për degëzimin e logjikës së tij. Një veprim mund të nevojitet nëse është plotësuar kushti, ndërsa veprim tjetër nëse nuk është plotësuar. Pra, me një kusht “nëse” do të kemi dy rrjedha paralele të programit. Se cila rrjedhë do të zgjedhet, varet nga kushti i parashtruar.

if…else

Kushti mund të kthejë vetëm një nga dy përgjigjet e mundshme: TRUE ose FALSE. TRUE nëse kushti është plotësuar, FALSE nëse ai kusht nuk është plotësuar.

 1 <?php
 2 $emri = $_POST['emri'];
 3 
 4 if ($emri == "")
 5 	{
 6 	echo "<p>Nuk e keni shenuar emrin</p>";
 7 	}
 8 
 9 echo "<p>Ju faleminderit!</p>";
10 ?>

Në shembullin e mësipërm, fillimisht lexohet vlera e $_POST['emri'] dhe vendoset në variablin $emri.

Pastaj me if jepet kushti: $emri == "", me çka verifikohet a është ai variabël i zbrazët. Dy thonjëza të njëpasnjëshme simbolizojnë tekstin e zbrazët.

Nëse kushti plotësohet, pra nëse $emri nuk e përmban asnjë shkronjë, atëherë paraqitet raporti: “Nuk e keni shënuar emrin”. Në të kundërtën, pra, nëse kushti nuk është plotësuar, ekzekutimi vazhdon më tej nëpër rreshtat vijues programorë.

Si në rastin kur plotësohet kushti, ashtu edhe atëherë kur nuk plotësohet, programi i mësipërm do ta vazhdojë ekzekutimin te rreshti vijues: echo "<p>Ju faleminderit!</p>".

1 |
2 |
3 |\
4 | |
5 |/
6 |
7 |

Shpesh kemi nevojë që të caktojmë veprime të ndryshme për TRUE dhe FALSE. Për këtë përdorim else:

 1 <?php
 2 $emri = $_POST['emri'];
 3 
 4 if ($emri == "")
 5 	{
 6 	echo "<p>Nuk e keni shenuar emrin</p>";
 7 	}
 8 else
 9 	{
10 	echo "<p>Ju faleminderit!</p>";
11 	}
12 ?>

Këtu, nëse kushti plotësohet, jepet raporti “Nuk e keni plotësuar emrin”, e në të kundërtën - shfaqet teksti “Ju faleminderit!”. Pra, për dallim nga shembulli paraprak, kur në të dy situatat na shfaqej falënderimi, tash na shfaqet vetëm kur e kemi plotësuar emrin.

1    |
2    |
3   /\
4  /  \
5  |   |
6  \   /
7   \/
8    |

Analizat që i bëjmë me if...else, mund të jenë edhe më komplekse se kaq, varësisht prej logjikës që duam të implementojmë në program.

Për shembull, mund të dëshirojmë që t’i analizojmë dy kushte njëkohësisht.

 1 <?php
 2    	$emri = $_POST['emri'];
 3 	$mosha = (int) $_POST['mosha'];
 4 
 5 if ($emri == "" && $mosha == 0)
 6 	{
 7 	echo "<p>Nuk e keni shenuar emrin ose moshën, ose asnjërën!</p>";
 8 	}
 9 else
10 	{
11 	echo "<p>Ju faleminderit!</p>";
12 	}
13 ?>

Këtu analizuam dy kushte përnjëherë, emri dhe moshën. Cilado të jetë e paplotësuar, programi do të raportojë mbi gabimin.

Operatori logjik && që i lidh këto dy kushte, do të japë rezultat TRUE, vetëm nëse janë plotësuar të dy kushtet.

TRUE && TRUE = TRUE TRUE && FALSE = FALSE FALSE && TRUE = FALSE FALSE && FALSE = FALSE

Ndonjëherë mund të duhet që të plotësohet vetëm njëri prej dy kushteve, për të rezultuar krejt shprehja si e saktë. Psh.

1 if ($x == 2 || $y ==1)

Cilado të jetë e saktë, do të bëjë që krejt shprehja të rezultojë e sakte.

TRUE || TRUE = TRUE TRUE || FALSE = TRUE FALSE || TRUE = TRUE FALSE || FALSE = FALSE

Për krahasimin e dy vlerave përdoret barazimi i dyfishtë == dhe jo =. Shenja = është operator i caktimit të vlerës (asignment), si në rastin:

1 $cmimi = 12.50;

ku vlera 12.50 i bartet variablit $cmimi, pra nuk bëhet krahasimi se a është çmimi baras me 12.50. Për krahasime përdoren operatorët vijues:

1 ==
2 ===
3 <
4 <=
5 >=
6 !=
7 !==

Nëse analiza jonë është më komplekse, pra nuk na mjafton vetëm krahasimi se a është një shprehje e saktë apo e pasaktë, mund të konstruktojmë degëzime më të detajuara.

 1 <?php
 2 
 3 if ($x < 2)
 4 	{
 5 	//
 6 	}
 7 elseif ($x > 5 )
 8 	{
 9 	//
10 	}
11 else
12 	{
13 	//
14 	}
15 ?>

Kushti i parë plotësohet nëse $x është më i vogël se 2, ndërsa kushti i dytë - nëse është më i madh se 5. Kushti i tretë plotësohet për të gjitha vlerat e $x prej 2 deri 5. Vlera e $x është vetëm një vlerë, ndërsa dëgëzime janë tre, varësisht prej kushteve të cakura.

Një blloku të if…else mund të fusim if…else tjera, të cilat bëjnë krahasime të tjera, më komplekse.

 1 <?php
 2 
 3 if ($x < 2)
 4 	{
 5 	if ($y > 3)
 6 		{
 7 		if ($z == 5)
 8 			{
 9 			//
10 			}
11 		}
12 	} 
13 else
14 	{
15 	//
16 	}
17 ?>

switch

Struktura switch rëndomë gjen zbatim kur analizojmë vlera nga një listë e paracaktuar e vlerave. Psh, nëse analizojmë numrin e muajit, ai mund të jetë nga 1 deri 12, numri rendor i ditës së javës mund të jetë nga 1 deri 7, etj.

 1 <?php
 2 
 3 $dita = 5;
 4 
 5 switch ($dita)
 6 	case 1:
 7 		echo "E hene ";
 8 		break;
 9 	case 2:
10 		echo "E marte ";
11 		break;
12 	case 3:
13 		echo "E merkure ";
14 		break;
15 	case 4:
16 		echo "E enjte ";
17 		break;
18 	case 5:
19 		echo "E premte ";
20 		break;
21 	default:
22 		echo "Fundjave ";
23 	}
24 ?>

Urdhëri break përdoret për ndarjen më vete të secilit rast (case). Nëse psh. pas case 1 do të largohet break, atëherë kur $dita=1, do të paraqitej raporti “E hene E marte”, pra sikur të ishin plotësuar edhe case 1, edhe case 2.

Sado komplekse të jetë një strukturë e degëzuar, ato gjithmonë shpiejnë teposhtë rrjedhës së ekzekutimit të programit. Pra, pas plotësimit (apo mosplotësimit) të një kushti, rreshtat pasardhës vijnë në radhë për ekzekutim.

Strukturat e iteracionit

Për zgjidhjen e ndonjë problemi, na nevojitet që programi ta përsërisë një bllok të rreshtave programorë. Këtë e quajmë iteracion. Iteracioni pra do të zbatohet sa herë kemi nevojë që një bllok rreshtash të përsëritet disa herë.

for

Struktura ciklike for përdoret kur na nevojitet përsëritja e një blloku për një numër fiks të herave.

Përsëritja e një blloku 10 herë:

1 <?php
2 for ($i=1; $i<=10; $i++)
3 {
4 echo "<p>".$i;
5 }
6 ?>

Ajo çka përsëritet 10 herë është rreshti echo brenda bllokut të shënuar me {}.

  • $i=1 përcakton vlerën fillestare.
  • $i<=10 përcakton deri në cilën vlerë do të shkojë iteratori, në këtë rast deri në 10
  • $i++ e rrit vlerën e $i për 1 në secilin iteracion. Nëse kemi nevojë të përdorim ndonjë vlerë tjetër, psh. që numri të rritet për 3, atëherë shënojmë: $i = $i +3, me ç’rast numërimi do të shkonte: 1, 4, 7, 10, pra do të përsëritej 4 herë, e jo 10.
  • Nëse nisemi nga një vlerë më e madhe dhe duam të “lëshohemi” në një vlerë më të vogël, atëherë përdorim $i–, ose për inkremente të tjera $i = $i - 3

Brenda një blloku mund të vendosim aq rreshta sa ka nevojë. Brenda një blloku mund të vendosim edhe struktura të tjera.

Le ta gjenerojmë tabelën e shumëzimit:

 1 <?php
 2 echo "<table border='1'>";
 3 for ($x=1; $x<=10; $x++)
 4   {
 5   for ($y=1; $y<=10; $y++)
 6     {
 7     $z = $x * $y;
 8     echo "<tr><td>".$x."</td><td>*</td><td>".$y."</td><td>=</td><td>".$z."</td></tr>\
 9 ";
10     }
11   }
12 echo "</table>";
13 ?>

while

Struktura while bën përsëritjen e një blloku derisa vlen një kusht. Në momentin që kushti nuk plotësohet (kthen vlerën FALSE), cikli ndërpritet.

1 <?php
2 $x = 1;
3 while ($x < 10)
4   {
5   echo "<p>".$x;
6   $x++;
7   }
8 ?>

Kushti këtu është që $x të jetë më i vogël se 10. Për aq kohë sa vlen ky kusht, cikli do të përsëritet. Kur $x e arrin vlerën 10, blloku më nuk do të ekzekutohet.

Funksionet

## Built-in functions

Funksionet janë tërësi programore për zgjidhjen e një problemi të caktuar. Mund të përdoren për:

  • ta llogaritur ndonjë vlerë,
  • për ta konstatuar/verifikuar ndonjë gjendje,
  • për ta kryer ndonje veprim, etj.

Ato janë vetë esenca e PHP-së, sikurse që është rasti edhe me gjuhët e tjera programore.

PHP posedon një numër të madh të funksioneve të integruara. Numri i saktë i tyre varet prej PHP ekstensioneve të instaluara; sa më shumë ekstensione - aq më shumë funksioneve i kemi në dispozicion.

Numrin e saktë të funksioneve të një instalimi konkret, mund ta shohim me këtë kod:

1 <?php
2 $f = get_defined_functions();
3 echo count($f['internal']);
4 ?>

Në këtë kompjuter, me WAMP të instaluar dhe vetëm disa ekstensione, numri është 1890. Me shtimin e eksensioneve të tjera, ky numër do të bëhet më i madh.

Edhe pse ky numër i funksioneve është jashtëzakonisht i madh dhe përfshin zgjidhje për situata të ndryshme, megjithatë ka raste kur na nevojitet një funksion i ri që do të kryejë një detyrë shumë specifike. Atëherë paraqitet nevoja për krijimin e një funksioni të ri, të ashtuquajturat “user-defined functions”.

Megjithatë, para se të fillojmë ta ndërtojmë një funksion të ri, është mirë të verifikojmë në PHP manual nëse një funksion që e kryen atë punë tashmë ekziston. Funksionet e integruara, përveç që do të ekzekutohen më shpejt, janë edhe më të sigurtë dhe përmbajnë më pak “bugs”.

Veç kësaj, duke kërkuar nëpër Web, mund të gjejmë funksione të gatshme, apo edhe biblioteka të tëra të funksioneve, me përdorimin e të cilave mund të zgjidhim probleme të ndryshme, pa pasur nevojë ta kuptojmë kodin e tyre, apo të ndërhyjmë.

User-defined functions

Gjatë kërkimit të gabimeve nëpër skriptat tona, shpeshherë e përdorim funksionin var_dump() për të na treguar vlerat e ndonjë variabli, rëndomë të atyre më kompleks siç janë vargjet.

Nëse skripta jonë automatikisht redirektohet në ndonjë faqe tjetër, ne nuk do të jemi në gjendje se çfarë rezultati na jep var_dump(). Për këtë arsye, pas var_dump(), shtojmë edhe die() ose exit. Funksionin die() e shtojmë edhe atëherë kur me qëllim dëshirojmë ta ndalim ekzekutimin e skriptës, në mënyrë që ta izolojmë vendin e gabimit më mirë. Shpesh ndodh që gabimi ka origjinën e ka krejt tjetërkund në krahasim me vendin ku raportohet, prandaj duke e ndalur programin në vende të ndryshme, ne arrijmë ta gjejmë burimin e problemit.

Nëse shpesh na nevojiten këto dy funksionet e cekura (var_dump() dhe die()), do të ishte mjaft e bezdishme që t’i shkruajmë gjithmonë nga fillimi. Për këtë arsye, mund ta krijojmë një funksion të ri për të na e lehtësuar punën.

1 function dd($arg, $message="") {
2 var_dump($arg);
3 die($message);
4 ?>

Këtë funksion e vendosim le të themi në fund të skriptës, edhepse vendndodhja e tij brenda skriptës nuk ka kurrfarë rëndësie, sepse është një tërësi në vete dhe ekzekutohet vetëm kur thirret në mënyrë eksplicite.

Tash, mund t’i analizojmë variablat me funksionin e ri dd(). Nëse për shembull duam ta dimë përmbajtjen e superglobalit $_POST, shkruajmë dd($_POST); ose dd($_POST, "test"); kur duam ta shoqërojmë edhe ndonjë mesazh.

1 <?php
2 dd($_POST);
3 
4 function dd($arg, $message="") {
5 var_dump($arg);
6 die($message);
7 ?>

Tash, pra, ekzekutohen dy funksione njëri pas tjetrit. Së pari var_dump() e tregon përmbajtjen e $_POST, e pastaj die() e ndërpren ekzekutimin e skriptës duke e dhënë mesazhin që e kemi shënuar si parametër të dytë te dd().

Nëse ky funksion na nevojitet edhe në skriptat tjera, mund ta kopjojmë përmbajtjen e tij dhe ta vendosim nëpër skripta. Mirëpo, nëse më vonë dëshirojmë ta bëjmë ndonjë ndryshim në funksion, do të duhet që ta editojmë çdo skriptë ku e kemi vendosur atë funksion.

Për këtë arsye, zgjidhja më praktike është që funksionin ta vendosim në një fajll të veçantë dhe ta bëjmë include në të gjitha skriptat ku na nevojitet.

funksionet.php

1 <?php
2 function dd($arg, $message="") {
3 var_dump($arg);
4 die($message);
5 ?>

skripta.php

1 <?php
2 include("funksionet.php");
3 dd($_POST);
4 ?>

Për aplikacionin tonë mund të nevojitet një numër i funksioneve të ndryshme, kështu që është mirë që ato t’i vendosim në një fajll të përbashkët, jo secilin veç e veç. Kështu do të kemi nevojë vetëm për një include.

Nëse megjithatë numri i funksioneve është i madh, pa nevojë do të inkludohej fajlli i madh, prandaj ne duhet t’i sistemojmë nëpër fajlla duke i grupuar. Bëjmë include vetëm atë fajll që përmban grupin e funksioneve që do t’i përdorim në skriptën tonë. Në këtë mënyrë formojmë “biblioteka” të funksioneve.

Anatomia e funksionit

Emërtimi i funksionit

Funksioni duhet ta ketë një emër që fillon me shkronjë apo në raste të caktuara me _ (underline). Emri duhet të jetë unik, që të mos ketë kolizion me emrat e funksioneve ekzistuese. Emri i funksionit është case-insensitive, që dmth se nëse e emërtojmë funksionin tvsh, mund ta thërrasim edhe me Tvsh ose TVSH.

Pas emrit të funksionit vendosen kllapat (), brenda të cilave do t’i vendosim parametrat, apo thënë ndryshe - vlerat hyrëse të funksionit, nëse funksioni ka nevojë për ta. Pas kllapave vendosen kllapat e mëdha (curly braces), të cilat e definojnë bllokun e kodit të funksionit.

1 function tvsh($input) {
2 . . .
3 }

Tërësitë e funksionit

Funksionet mund t’i zbërthejmë në 3 tërësi:

  • Leximi i vlerave hyrëse
  • Procesimi i vlerave hyrëse dhe krijimi i vlerës dalëse
  • Kthimi i vlerës dalëse

Vlerat hyrëse, apo parametrat e funksionit janë atë vlera që përcillen nga programi/skripta për t’u përpunuar brenda funksionit. Imagjinojeni funksionin si një makinë e cila kërkon lëndë të parë për ta prodhuar një produkt. Lënda e parë e funksioneve janë vlerat numerike, tekstuale, logjike, etj. Ato vlera procesohen brenda funksionit për t’u përfituar rezultati i cili pastaj i kthehet “thirrësit”.

1 <?php
2 echo "Cmimi me TVSH: ".tvsh(100);
3 
4 function tvsh($cm) {
5 $cmt = $cm+($cm*16/100);
6 return $cmt;
7 }
8 ?>

Funksioni tvsh thirret në rreshtin e dytë si tvsh(100), ku vlera 100 është çmimi pa TVSH.

Funksioni e merr atë vlerë nëpërmes parametrit $cm. Parametrat shënohen brenda kllapave pas emërtimit të funksionit.

Rreshti vijues e vendos vlerën e $cm në formulën $cm+($cm*16/100), formulë kjo që vlerën e variablit $cm e rrit për 16%, duke e fituar kështu çmimin me TVSH. Vlerat e re vendoset në variablin $cmt. Në rreshtin tjetër, me urdhërin return e kthejmë vlerën e cmimit me TVSH. Ku apo kujt ia kthejmë. Thirrësit, që në këtë rast është rreshti: echo "Cmimi me TVSH: ".tvsh(100);

Krejt pjesa tvsh(100) zëvendësohet me vlerën e kthyer nga funksioni, pra me numrin 116. Prandaj, echo do ta “printojë” tekstin Cmimi me TVSH: 116.

Funksionet mund të kenë një apo më tepër parametra, por mund edhe të mos e kenë asnjë. Funksionet të cilave nuk kanë nevojë për të dhëna hyrëse, nuk kanë parametra. Për shembull funksionin i PHP time() që tregon UNIX timestamp, nuk ka nevojë për asnjë vlerë hyrëse për të na kthyer si rezultat kohën aktuale.

Brenda funksionet shkruajmë kod në mënyrë standarde, nuk dallon asgjë nga programimi i pjesës tjetër të skriptës.

Kthimi i vlerave

Funksionet kthejnë vlera, por kemi edhe funksione që nuk kanë nevojë të kthejnë vlera fare. Parimisht, nëse funksioni duhet vetëm të kryejë procesim për të ardhur deri te një vlerë, ne zgjedhim që funksioni të kthejë vlerë. Në rastet kur funksioni duhet ta kryejë një veprim, atëherë ai mund të mos kthejë rezultate.

Dallimi ndërmjet argumentit dhe parametrit

Jo rrallë krijohet konfuzion me terminologjinë e përdorur për përshkrimin e funksioneve, ku ngatërrohen kuptimet e fjalës argument me atë parametër.

Kur e thërrasim një funksion, ne atij i përcjellim argumente:

1 $cmimi_tvsh = tvsh(56);

Numri 56 është argument që i jepet funksionit.

Te rreshti ku e definojmë funksionin, i shënojmë parametrat.

1 function tvsh($cm) {
2 
3 }

Variabli $cm këtu është parametër.

Renditja e parametrave duhet të jetë krejtësisht e njëjtë me renditjen e argumenteve, si dhe duhet të jenë të tipit të njëjtë. Pra, argumenti i parë i përgjigjet parametrit të parë dhe të dyja duhet ta kenë tipin e njëjtë (integer, float, array, …). Emrat e parametrave nuk është e thënë të jenë të njëjtë me ato të argumenteve, mund të përdorim çfarëdo emrash, me rëndësi është vetëm renditja e tyre.

1 $cmimi = 100;
2 $tvsh = 16;
3 echo tvsh($cmimi, $tvsh);
1 function tvsh($cm, $shk) {
2 . . .
3 }

Siç shohim parametrit $cm i përgjigjet argumenti $cmimi, ndërsa atij $shk i përgjigjet $tvsh. Emrat janë irelevantë, renditja është ajo që merret parasysh.

Numri i argumenteve nuk është e thënë të jetë i barabartë me numrin e parametrave:

1 echo tvsh(100);
1 function tvsh($cm, $shk=16) {
2 $cmt = $cm+($cm*$shk/100);
3 return $cmt;
4 }

Në këtë rast, funksioni thirret me vetëm një argument, por funksioni i ka dy parametra. Argument i dytë në këtë rast është opcional. Në mungesë të vlerës për parametrin e dytë, vendoset default value, në këtë rast 16. Pra, nëse dëshirojmë të llogarisim me bazën 16% të TVSH, funksionin e thërrasim duke i dhënë vetëm argumentin e parë. Nëse kërkojmë llogaritje me ndonjë bazë tjetër, atëherë e shënojmë edhe argumentin e parë, edhe argumentin e dytë.

Pra, $shk e merr vlerën 16, vetëm atëherë kur mungon argumenti i dytë gjatë thirrjes së funksionit, përndyshe, në prezencë të argumentit të dytë - parametri i dytë e merr vlerën e atij argumenti.

Funksioni kthen vetëm një vlerë: një string, një numër, një boolean,… Nëse dëshirojmë të kthejmë më tepër vlera, e përdorim një varg (array), brenda së cilës i kemi vendosur vlerat e dëshiruara. Në shembullin e mëposhtëm, funksioni do të na kthejë dy vlera: vlerën e TVSH-së dhe çmimin me TVSH.

 1 <?php
 2 $cmimi = tvsh(115, 16);
 3 echo "<p>Vlera e TVSH:".$cmimi['tvsh'];
 4 echo "<p>Cmimi me TVSH:".$cmimi['cmimi_tvsh'];
 5 
 6 function tvsh($cm, $shk=16) {
 7 $tvsh = $cm*$shk/100;
 8 $cmimi_tvsh = $cm+$tvsh;
 9 $rezultati = array('tvsh' => $tvsh, 'cmimi_tvsh' => $cmimi_tvsh);
10 return $rezultati;
11 }

Rezultati:

1 Vlera e TVSH:18.4
2 Cmimi me TVSH:133.4

Jetëgjatësia e variablave brenda funksioneve

Variablat që përdoren brenda funksionit, kanë një dallim me variablat në pjesën tjetër të skriptës: ato “jetojnë” aq sa jeton funksioni, dhe me kryerjen e ekzekutimit të funksionit - ato vlera fshihen. Variablat e tilla quhen variabla lokale. Këto variabla nuk do të bijnë në kolizion me variablat me emërtim të njëjtë që janë jashtë funksionit.

 1 <?php
 2  $cmimi = 100;
 3  echo "<p>Cmimi:".$cmimi;
 4  trego_cmimin();
 5  echo "<p>Cmimi:".$cmimi;
 6  
 7  
 8  function trego_cmimin()
 9  	{
10 	$cmimi = 200;
11 	 echo "<p>Cmimi:".$cmimi;	
12 	}
13 ?>

Rezultati:

1 Cmimi:100
2 Cmimi:200
3 Cmimi:100

Siç shihet nga shembulli, vlera e variablit $cmimi në skriptë është 100, brenda funksionit është 200, dhe kur e “printojmë” prapë - vlera është 100. Pra, ndryshimi i vlerës së variablit me emër të njëjtë brenda funksionit - nuk e ka ndryshuar vlerën e atij variabli jashtë funksionit.

Kjo karakteristikë na mundëson që në funksione të përdorim variabla me emërtime sipas dëshirës, pa pasur frikë se ato do t’i “mbulojnë” vlerat e variablave me emër të njëjtë jashtë skriptës.

Po ashtu, ky “izolim” i variablave brenda funksionit, mundëson që vlera e tyre të mos ndryshohet aksidentalisht, duke e marrë vlerën e ndonjë variabli me emër të njëjtë jashtë funksionit.

Me këtë, funksionet janë si “kuti të zeza”, variablat e të cilave nuk ndikohen nga “ambienti i jashtëm”, duke e bërë kodin e tyre portabil, pra që të mund ta përdorim në cilëndo skriptë që të na nevojitet.

Nëse megjithatë na nevojitet ndonjë “interaksion” i funksionit me “ambientin e jashtëm”, kemi mundësi që variablat e caktuar t’i shpallim si “variabla globalë”, me global.

 1 <?php
 2 $cmimi = 100;
 3 echo "<p>Cmimi pa TVSH:".$cmimi;
 4 echo "<p>Cmimi me TVSH:".trego_cmimin(16);
 5 
 6 function trego_cmimin($tvsh)
 7  	{
 8 	global $cmimi;
 9 	return $cmimi+$cmimi*$tvsh/100;	
10 	}
11 ?>

Këtu, funksionit nuk ia kemi përcjellur fare çmimin me përdorimin e ndonjë argument, e kemi përcjellë vetëm vlerën e TVSH-së (16). Funksioni e merr vlerën e variablit $cmimi nga skripta, sepse është shënuar global $cmimi, pra $cmimi është deklaruar si variabël globale. Pas llogaritjes me formulën $cmimi+$cmimi*$tvsh/100, funksioni me return e kthen atë vlerë, dhe echo-ja e dytë në skriptë e “printon”, duke e dhënë vlerën e re (116).

Nëse vlera e variablit global $cmimi ndryshohet brenda funksionit, edhe skripta do të ketë qasje në atë vlerë të re.

 1 <?php
 2 $cmimi = 100;
 3 echo "<p>[Skripta] Cmimi pa TVSH:".$cmimi;
 4 echo "<p>[Funksioni] Cmimi me TVSH:".trego_cmimin(16);
 5 echo "<p>[Skripta] Cmimi pa TVSH:".$cmimi;
 6  
 7  
 8  function trego_cmimin($tvsh)
 9  	{
10 	global $cmimi;
11 	$cmimi = $cmimi+$cmimi*$tvsh/100;
12 	return $cmimi;	
13 	}
14 ?>

Rezultati:

1 [Skripta] Cmimi pa TVSH:100
2 
3 [Funksioni] Cmimi me TVSH:116
4 
5 [Skripta] Cmimi pa TVSH:116

Në rreshtin e tretë shohim se edhe skripta është duke e parë vlerën e ndryshuar të variablit $cmimi.

Klasat dhe objektet

## 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:

1 <?php
2 class Produkti {
3 
4 }

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:

1 <?php
2 class Produkti {
3 
4 }
5 $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:

1 <?php
2 class Produkti {
3 
4 }
5 $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:

1 <?php
2 class Produkti {
3 	public $cmimi;
4 	public $sasia;
5 }
6 
7 $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:

 1 <?php
 2 class Produkti {
 3 	public $cmimi = 0;
 4 	public $sasia = 1;
 5 }
 6 
 7 $o = new Produkti();
 8 
 9 $o->cmimi = 12;
10 echo "<p>Cmimi: ".$o->cmimi;
11 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ë:

1 <?php
2 $cmimi = 12;
3 $sasia = 1;
4 
5 echo "<p>Cmimi: ".$cmimi;
6 echo "<p>Sasia: ".$sasia;
7 // Rezultati:
8 // Cmimi: 12
9 // Sasia: 1

Metodat

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

 1 <?php
 2 class Produkti {
 3 	public $cmimi = 0;
 4 	public $sasia = 1;
 5 	
 6 	public function vlera($c, $s) {
 7 		$v = $c * $s;
 8 		return $v;
 9 	}
10 }
11 
12 $o = new Produkti();
13 
14 $o->cmimi = 12;
15 $o->sasia = 5;
16 echo "<p>Vlera: ".$o->vlera($o->cmimi, $o->sasia);
17 // Rezultati: 
18 // 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:

 1 <?php
 2 class Produkti {
 3 	public $cmimi = 0;
 4 	public $sasia = 1;
 5 	
 6 	public function vlera() {
 7 		$v = $this->cmimi * $this->sasia;
 8 		return $v;
 9 	}
10 }
11 
12 $o = new Produkti();
13 
14 $o->cmimi = 12;
15 $o->sasia = 5;
16 echo "<p>Vlera: ".$o->vlera();
17 // Rezultati: 
18 // 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.

 1 <?php
 2 class Produkti {
 3 	public $cmimi = 0;
 4 	public $sasia = 1;
 5 	
 6 	public function vlera() {
 7 		$v = $this->cmimi * $this->sasia;
 8 		return $v;
 9 	}
10 }
11 
12 $o = new Produkti();
13 $o->cmimi = 12;
14 $o->sasia = 5;
15 echo "<p>Objekti 1, vlera: ".$o->vlera();
16 // Rezultati: 
17 // Objekti 1, vlera: 60
18 
19 $p = new Produkti();
20 $p->cmimi = 17;
21 $p->sasia = 3;
22 echo "<p>Objekti 2, vlera: ".$p->vlera();
23 // Rezultati: 
24 // 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:

1 <?php
2 $cmimi = 12;
3 $cmimi = 17;
4 echo "<p>Cmimi: ".$cmimi;
5 // Rezultati: 
6 // 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().

 1 <?php
 2 class Produkti {
 3 	public $cmimi;
 4 	public $sasia;
 5 
 6 	public function __construct($c, $s) {
 7 		$this->cmimi = $c;
 8 		$this->sasia = $s;
 9 	}	
10 	public function vlera() {
11 		$v = $this->cmimi * $this->sasia;
12 		return $v;
13 	}
14 }
15 
16 $o = new Produkti(12, 5);
17 echo "<p>Objekti 1, vlera: ".$o->vlera();
18 // Rezultati:
19 // 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:

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

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.

 1 <?php
 2 class Produkti {
 3 	public $cmimi;
 4 	public $sasia;
 5 
 6 	public function __construct($c, $s) {
 7 		$this->cmimi = $c;
 8 		$this->sasia = $s;
 9 	}	
10 
11 	public function __destruct() {
12 		$obj = serialize($this);
13 		file_put_contents( __DIR__."/object.txt", $obj);
14 	}
15 
16 	public function vlera() {
17 		$v = $this->cmimi * $this->sasia;
18 		return $v;
19 	}
20 }
21 
22 
23 $o = new Produkti(12, 5);
24 echo "<p>Objekti 1, vlera: ".$o->vlera();
25 // Rezultati:
26 // 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:

1 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.

1 <?php
2 include('Produkti.php');
3 $us = file_get_contents( __DIR__."/object.txt");
4 $o = unserialize($us);
5 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():

1 $o = new Produkti(12, 5);
2 $p = new Produkti(34, 3);
3 
4 echo "<p>ID e objektit o: ".spl_object_hash($o);
5 // Rezultati i mundshëm: 000000006a72b2a300000000248ccba9
6 echo "<p>ID e objektit o: ".spl_object_hash($p);
7 // 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ë:

1 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:

1 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:

1 Produkti::$shitorja = 3;

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

1 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:

 1 <?php
 2 class Produkti {
 3 	public $cmimi;
 4 	public $sasia;
 5 	public static $shitorja;
 6 
 7 	public function __construct($c, $s) {
 8 		$this->cmimi = $c;
 9 		$this->sasia = $s;
10 	}
11 	public function vlera() {
12 		$v = $this->cmimi * $this->sasia;
13 		return $v;
14 	}
15 }
16 
17 Produkti::$shitorja = 3;
18 
19 $o = new Produkti(12, 5);
20 $p = new Produkti(8, 12);
21 
22 
23 echo "<p>Objekti 1, shitorja: ".$o::$shitorja;
24 echo "<p>Objekti 2, shitorja: ".$p::$shitorja;
25 
26 Produkti::$shitorja = 5;
27 echo "<p>Objekti 1, shitorja: ".$o::$shitorja;
28 echo "<p>Objekti 2, shitorja: ".$p::$shitorja;
29 // Rezultati:
30 // Objekti 1, shitorja: 3
31 // Objekti 2, shitorja: 3
32 // Objekti 1, shitorja: 5
33 // 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:

 1 <?php
 2 class Produkti {
 3 
 4 	public static function vlera($cmimi, $sasia) {
 5 		$v = $cmimi * $sasia;
 6 		return $v;
 7 	}
 8 }
 9 
10 echo Produkti::vlera(10,5);
11 // Rezultati:
12 // 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.

GET

Karakteristikë e metodës GET është se të dhënat që duhet të barten në faqen tjetër, i bashkangjiten URL-së së asaj faqeje tjetër.

Kjo bëhet duke ia shtuar linkut një query string, në të cilin do t'i rendisim variablat dhe vlerat e tyre përkatëse.

Psh. nëse dëshirojmë ta përcjellim vlerën e ID-së së një lajmi të kërkuar në një portal, atëherë query string do ta ketë këtë formë:

?id=5

duke e bërë linkun të duket kështu:

1 http://localhost/lajmet.php?id=5

Nëse dëshirojmë të përcjellim më shumë variabla, ato i bashkangjesim me &:

1 http://localhost/lajmet.php?id=5&category=2

me çka po specifikojmë se e kërkojmë lajmin numër 5 në kategorinë numër 2.

Kufizim i metodës GET është gjatësia e tekstit që mund të bartet, sepse address bar i browserit lejon vendosjen e një numri të kufizuar të karaktereve. Më shumë se 2000 karaktere do të shkaktojnë probleme në shumicën e browserëve.

Problem tjetër me GET është fakti se tekstet që përmbajnë karaktere me kuptim special për browserin mund të shkaktojnë probleme, duke e bërë URL-në e tillë të papërdorshme.

Karakteret e lejuara në URL janë:

1 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~:/?#[]@!$&&#39;()*\
2 +,;=

Të gjitha karakteret tjera duhet paraprakisht të enkodohen me urlencode(), për të mos shkaktuar "keqkuptim" nga ana e browserit.

Pra, në përgjithësi, me metodën GET nuk është i mundur dërgimi në skriptën tjetër i një sasie të madhe të tekstit, plus problemet me karakteret speciale të paenkoduara.

POST

Metoda POST, sikurse edhe metoda GET, përdoret për dërgimin e të dhënave nga një skriptë në tjetrën.

Për dallim nga GET që të dhënat i ekspozon në URL, metoda POST i dërgon të dhënat duke ia bashkangjitur HTTP kërkesës së radhës. Kjo bën që vlerat të mos shfaqen në URL dhe me këtë të mos ruhen në historinë e browser-it.

Pikërisht për shkak se të dhënat nuk i bashkangjiten URL-së, metoda POST është e përshtatshme për dërgimin e të dhënave sensitive, siç është fjalëkalimi. Në mungesë të lidhjes SSL, kjo “mbrojtje” është efikase vetëm nga shikimet e të tjerëve në ekran, apo të atyre që e shfletojnë historinë e browser-it.

Metoda POST ka edhe një avantazh tjetër: lejon dërgimin e një vëllimi më të madh të të dhënave. Se sa do të jetë gjatësia më e madhe e të dhënave, caktohet me direktivën post_max_sizephp.ini:

1 post_max_size = 50M

Këtu caktuam që maksimumi të jetë 50 Mb.

Metoda POST përdoret te formularët:

komentet.html

1 <form method="POST" id="regjistrimi" action="/regjistro.php">
2 <input type="text" name="titulli">
3 <textarea name="mesazhi"></textarea>
4 </form>

Fushat, vlerat e të cilave duhet të dërgohen me POST, duhet ta kenë atributin name. Vlerat e dërguara me POST, lexohen nga superglobali $_POST.

regjistro.php

1 <?php
2 $titulli = $_POST['titulli'];
3 $mesazhi = $_POST['mesazhi'];
4 
5 echo "<h1>".$titulli."</h1>";
6 echo "<blockquote>".$mesazhi."</blockquote>";
7 ?>

Të dhënat e lexuara nga POST duhet të konsiderohen si potencialisht “të ndotura”, prandaj ato në fillim validohen (validate) dhe sanitizohen (sanitize), për t’u përcjellë në procesim të mëtejmë në program.

1 <?php
2 $titulli = filter_var($_POST['titulli'], FILTER_SANITIZE_STRING);
3 $mesazhi = filter_var($_POST['mesazhi'], FILTER_SANITIZE_STRING);
4 
5 echo "<h1>".$titulli."</h1>";
6 echo "<blockquote>".$mesazhi."</blockquote>";
7 ?>

Me këtë do të eliminohet mundësia që bashkë me tekstin benjinj, në skriptën tonë të përcillen edhe kodet malinje.

Të dhënat e dërguara me POST, mund të lexohen edhe nga superglobali $_REQUEST, por kjo nuk është praktikë e mirë për shkaqe sigurie.

Për të dërguar të dhëna me metodën POST pa përdorimin e formularëve, mund të përdoren funksione të PHP, ekstensioni cURL, si dhe funksione të caktuara të JavaScript.

Dërgimi me JavaScript është i lejuar vetëm brenda domainit të njëjtë, kështu që mund të konsiderohet më pak i rrezikshëm se ajo me cURL, me të cilin potencialisht mund të dërgojmë POST kërkesa në cilindo domain.

Data dhe ora

## Funksioni time()

Funksioni time() tregon kohën sipas Unix time, që është numri i sekondave që kanë kaluar nga data 1 Janar 1970 sipas Kohës së Koordinuar Universale (Coordinated Universal Time - UTC). Këtë numër e quajmë timestamp.

Shembull:

1 echo time();

Fitohet numri korrespondues i sekondave, si p.sh. 1499977231.

Funksioni date()

Vlera e timestamp mund të transformohet në datë dhe orë të formatit të dëshiruar me ndihmën e funksionit date(). Ka dy parametra: i pari është për ta shënuar formatin, ndërsa i dyti është vlera e timestamp të dëshiruar; nëse nuk e shënojmë, nënkuptohet vlera e time() që d.m.th. koha aktuale.

1 echo date("d.m.Y H:i:s");
2 echo "<br>";
3 echo date("d.m.Y H:i:s", 1234567890);

Si rezultat fitohen dy rreshta: 13.07.2017 20:40:51 13.02.2009 23:31:30

  • d - Dita e muajit, numër dyshifror, me zero udhëheqëse përpara numrave njëshifrorë (01-31).
  • D - Paraqitja tekstuale e ditës, tri shkronja (Mon - Sun).
  • j - Dita e muajit pa zero udhëheqëse (1-31).
  • l (‘L’ e vogël) - Paraqitja e plotë tekstuale e ditës së javës (Sunday deri Saturday).
  • N - Paraqitja numerike sipas ISO-8601 e ditës së javës, 1 (Për të hënën) deri 7 (Për të dielen).
  • S - Sufiksi rendor i ditës së muajit, 2 karaktere (st, nd, rd ose th).
  • w - Paraqitja numerike e ditës së javës (0 për të dielen deri 6 për të shtunën).
  • z - Dita e vitit, duke filluar nga 0 deri në 365.
  • W - Numri rendor i javës brenda vitit sipas ISO-8601, me javët duke filluar të hënën.
  • F - Paraqitja e plotë tekstuale e muajit, nga January deri December.
  • m - Paraqitja numerike e muajit, me zero udhëheqëse, prej 01 deri 12.

F A full textual representation of a month, such as January or March January through December m Numeric representation of a month, with leading zeros 01 through 12 M A short textual representation of a month, three letters Jan through Dec n Numeric representation of a month, without leading zeros 1 through 12 t Number of days in the given month 28 through 31 Year — — L Whether it’s a leap year 1 if it is a leap year, 0 otherwise. o ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0) Examples: 1999 or 2003 Y A full numeric representation of a year, 4 digits Examples: 1999 or 2003 y A two digit representation of a year Examples: 99 or 03 Time — — a Lowercase Ante meridiem and Post meridiem am or pm A Uppercase Ante meridiem and Post meridiem AM or PM B Swatch Internet time 000 through 999 g 12-hour format of an hour without leading zeros 1 through 12 G 24-hour format of an hour without leading zeros 0 through 23 h 12-hour format of an hour with leading zeros 01 through 12 H 24-hour format of an hour with leading zeros 00 through 23 i Minutes with leading zeros 00 to 59 s Seconds, with leading zeros 00 through 59 u Microseconds (added in PHP 5.2.2). Note that date() will always generate 000000 since it takes an integer parameter, whereas DateTime::format() does support microseconds if DateTime was created with microseconds. Example: 654321 v Milliseconds (added in PHP 7.0.0). Same note applies as for u. Example: 654 Timezone — — e Timezone identifier (added in PHP 5.1.0) Examples: UTC, GMT, Atlantic/Azores I (capital i) Whether or not the date is in daylight saving time 1 if Daylight Saving Time, 0 otherwise. O Difference to Greenwich time (GMT) in hours Example: +0200 P Difference to Greenwich time (GMT) with colon between hours and minutes (added in PHP 5.1.3) Example: +02:00 T Timezone abbreviation Examples: EST, MDT … Z Timezone offset in seconds. The offset for timezones west of UTC is always negative, and for those east of UTC is always positive. -43200 through 50400 Full Date/Time — — c ISO 8601 date (added in PHP 5) 2004-02-12T15:19:21+00:00 r » RFC 2822 formatted date Example: Thu, 21 Dec 2000 16:01:07 +0200 U Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) See also time()

FILES

PHP mundëson ngarkimin (upload) të fajllave nga disku lokal në Web server. Për të dërguar fajlla, e ndërtojmë një formular të këtillë:

1 <form action="dergo.php" method="post" enctype="multipart/form-data">
2     <p>Fajlli: <input type="file" name="fajlli"></p>
3     <input type="submit" value="Dergo">
4 </form>

Këtu duhet të veçojmë dy detaje:

  • Brenda tagut <form> e shtojmë atributin enctype me vlerën multipart/form-data. Kjo i tregon skriptës pranuese se përveç fushave normale të formularit, do të dërgohen edhe të dhëna në formë të fajllave.
  • Tipi i fushës duhet të jetë file, i cili në browser do të shfaqet si buton Browse..., i cili pasi të klikohet, na mundësohet ta zgjedhim fajllin e dëshiruar. Nëse kësaj fushe ia shtojmë atributin multiple, do të mundësohet selektimi i më shumë fajllave me një buton.

Në rastet kur dëshirojmë të përfshijmë disa fajlla me një fushë, emri i fushës te atributi name duhet të shkruhet me [] në fund. Shtohet edhe atributi multiple. Pra:

1 <p>Fajlli: <input type="file" multiple name="fajlli[]"></p>

Kjo do të mundësojë që skripta pranuese ta shohë këtë fushë si një varg (array) dhe jo si variabël të thjeshtë. Secili prej anëtarëve të vargut do të përmbajë informatat për fajllin përkatës. Të dhënat e fajllave lexohen nga superglobali $_FILES.

Ta japim fillimisht shembullin me procesimin e një fajlli:

dergo.php

1 <?php
2 var_dump($_FILES)
3 ?>

Rezultati:

1 array (size=1)
2   'fajlli' => 
3     array (size=5)
4       'name' => string 'd2.jpg' (length=6)
5       'type' => string 'image/jpeg' (length=10)
6       'tmp_name' => string 'C:\wamp\tmp\php266D.tmp' (length=23)
7       'error' => int 0
8       'size' => int 62176

Këtu shohim se superglobali $_FILES ka një anëtar dhe ai quhet fajlli.

Më tej, anëtari fajlli edhe vetë është varg dhe ka 5 anëtarë:

  • name. Emri i fajllit, në këtë rast d2.jpg.
  • type. MIME tipi i fajllit, në këtë rast image/jpeg.
  • tmp_name. Emri i përkohshëm i fajllit në direktoriumin tmp.
  • error. Numri i gabimit. E ka vlerën 0 nëse nuk është paraqitur asnjë gabim.
  • size. Madhësia e fajllit në bajtë.

Çdo fajll që ngarkohet, përkohësisht vendoset në një direktorium të veçantë, në rastin tonë në C:\wamp\tmp\php266D.tmp.

Fajlli vendoset në direktoriumin e përkohshëm sepse, varësisht prej kritereve që i caktojmë në program, mund të ndodh që të mos na nevojitet fare ai fajll.

Vetëm fajllat që i plotësojnë kriteret e caktuara do të vendosen në destinacionin final, gjegjësisht në folderin që e kemi caktuar për vendosjen e fajllave.

Cilat mund të jenë ato kritere?

Mund të jetë tipi i fajllave. Nëse na nevojiten vetëm fajlla që përmbajnë fotografi, atëherë do të kufizohemi në tipet e caktuara, psh: JPG, GIF, PNG.

Kriter tjetër mund të jetë madhësia sepse mund të dëshirojmë që ta caktojmë një madhësi maksimale të fajllit, psh ato deri në 1 Mb.

Gabimet

Për këtë arsye, gjatë procesimit të fajllit të dërguar, ne do t’i cekim kushtet. Fillimisht verifikojmë nëse është shfaqur gabim gjatë ngarkimit.

 1 <?php
 2 
 3 $fajlli = $_FILES['fajlli'];
 4 
 5 if ($fajlli['error'] == 0)
 6 	{
 7 	// Procesojmë më tutje
 8 	}
 9 else
10 	{
11 	echo "Gabim gjatë dërgimit të fajllit!";	
12 	}
13 ?>

Pra, me procesim të fajllit do të shkojmë vetëm nëse kodi i gabimit është 0, pra nëse nuk është shfaqur asnjë gabim.

Gabimet i kanë edhe konstantat korresponduese të tyre, kështu që në vend të 0 mund ta përdorim konstantën UPLOAD_ERR_OK. Lista komplete:

  • UPLOAD_ERR_OK. Vlera 0. Nuk është shfaqur asnjë gabim.
  • UPLOAD_ERR_INI_SIZE. Vlera 1. Madhësia e tejkalon madhësinë maksimale të definuar në direktivën upload_max_filesizephp.ini.
  • UPLOAD_ERR_FORM_SIZE. Vlera 2. Madhësia e fajllit e tejkalon vlerën e caktuar me MAX_FILE_SIZE në formular.
  • UPLOAD_ERR_PARTIAL. Vlera 3. Fajlli është ngarkuar pjesërisht.
  • UPLOAD_ERR_NO_FILE. Vlera 4. Nuk është ngarkuar asnjë fajll, dmth përdoruesi nuk e ka zgjedhur asnjë fajll në formular.
  • UPLOAD_ERR_NO_TMP_DIR. Vlera 6. Mungon folderi i përkohshëm. Kjo caktohet me direktivën upload_tmp_dirphp.ini.
  • UPLOAD_ERR_CANT_WRITE. Vlera 7. Fajlli nuk mund të ruhet në diskun e serverit.
  • UPLOAD_ERR_EXTENSION. Vlera 8. Ndonjë nga ekstensionet e PHP ka shkaktuar ndërprerjen e ngarkimit të fajllit.

Kriteri i dytë mund të jetë madhësia. Nuk do të lejojmë fajllat më të mëdhenj se 1 Mb.

 1 <?php
 2 $fajlli = $_FILES['fajlli'];
 3 if ($fajlli['error'] == 0)
 4 	{
 5 	if ($fajlli['size'] > 1048576)
 6 		{
 7 		// Procedo me tutje	
 8 		}
 9 	else
10 		{
11 		echo "Fajlli teper i madh. Lejohet maks. 1 Mb.";	
12 		}
13 	}
14 else
15 	{
16 	echo "Gabim gjatë dërgimit të fajllit!";	
17 	}
18 ?>	

MAX_FILE_SIZE

Madhësia maksimale e fajllit mund të caktohet edhe në HTML formular, duke e përdorur konstantën MAX_FILE_SIZE, e cila definohet brenda një input fushe, e cila fushë duhet të shënohet para fushës për upload.

1 <form action="dergo.php" method="post" enctype="multipart/form-data">
2     <input type="hidden" name="MAX_FILE_SIZE" value="1048576" />
3     <p>Fajlli: <input type="file" name="fajlli"></p>
4     <input type="submit" value="Dergo">
5 </form>

Vlera e kësaj fushe pastaj lexohet në skriptë dhe bëhet krahasimi i madhësisë së fajllit dhe kësaj vlere. Për shkaqe sigurie, kjo praktikë nuk duhet të konsiderohet e mirë sepse, siç e dijmë, në të gjithë browserët e rinj, vlerat e atributeve të HTML janë të manipulueshme, pra përdoruesi malicioz mund ta ngrisë vetë limitin. Prandaj, gjithmonë vlerëm maksimale e caktojmë me një numër në skriptë.

post_max_size dhe upload_max_filesize

Direktiva upload_max_filesize thamë se cakton madhësinë maksimale të fajllit që ngarkohet. Vlera e kësaj direktive gjithmonë duhet të jetë më e vogël se ajo e direktivës post_max_size. Direktiva post_max_size cakton madhësinë maksimale të të gjitha fushave tekstuale dhe të fajllit që ngarkohet. Pra, nëse për shembull ``post_max_size=10M. ndërsa upload_max_filesize=7M`, atëherë për fushat tekstuale janë në dispozicion 3 Mb.

Kontrollimi i MIME type të fajllit

Bashkë me të dhënat tjera, browseri i dërgon serverit edhe MIME type të fajllit. Disa nga MIME type:

  • application/octet-stream.
  • text/plain. Fajll tekstual.
  • text/html. Fajll që përmban HTML.
  • image/jpeg. Fotografi e tipit JPG/JPEG.
  • image/png. Fotografi e tipit PNG.
  • image/gif. Fotografi e tipit GIF.
  • application/pdf. Dokument i tipit PDF.
  • application/msword. Dokument i Wordit, i tipit .DOC.
  • application/vnd.openxmlformats-officedocument.wordprocessingml.document. Dokument i Wordit, i tipit .DOCX.

Skriptën tonë do ta lejojmë të pranojë vetëm fajlla JPEG dhe PNG:

 1 <?php
 2 $fajlli = $_FILES['fajlli'];
 3 
 4 if ($fajlli['error'] == 0)
 5 	{
 6 	if ($fajlli['size'] <= 1048576)
 7 		{
 8 		if ($fajlli['type'] == "image/jpeg" || $fajlli['type'] == "image/png")
 9 			{
10 			// Procedo me tutje	
11 			}
12                 else
13                        {
14                        echo "Lejohen vetem tipet JPG/JPEG dhe PNG";
15 			}
16 		}
17 	else
18 		{
19 		echo "Fajlli teper i madh. Lejohet maks. 1 Mb.";	
20 		}
21 	}
22 else
23 	{
24 	echo "Gabim gjatë dërgimit të fajllit!";	
25 	}
26 ?>	

Pra, deri tash kemi verifikuar:

  • Nëse është paraqitur ndonjë gabim
  • Nëse madhësia e fajllit është më e vogël ose baras se 1 Mb
  • Nëse tipi i fajllit është JPG/JPEG ose PNG.

Bartja e fajllave në destinacionin final

Tash e përdorim funksionin move_uploaded_file() për ta vendosur fajllin në destinacionin final, psh. në një folder për fotografitë e produkteve, të quajtur products, brenda folderit public.

Fajllat mund të vendosen në cilindo lokacion brenda fajll-sistemit, mirëpo nëse ato duhet të shfaqen në Web faqe, duhet të jenë të vendosura në folderin publik, pra në atë folder ku të gjithë vizitorët kanë qasje.

Për shembull, nëse fotografitë e produkteve i vendosim brenda /home/site/public_html/assets/images/, ndërsa fajlli që do t’i shfaqë gjendet në /home/site/public_html/, ta zëmë i quajtur show.php, atëherë linqet për source brenda show.php do të dukeshin kështu:

1 <img src='/assets/images/foto1.jpg' >
2 <img src='/assets/images/foto2.jpg' >

Pra, për ndërtimin e linqeve nëpër faqe nuk përdoret path absolut /home/site/public_html/assets/images/', por përdoren path që janë relativ ndaj public_html si /assets/images/foto1.jpg`.

Vizitorët e faqes nuk kanë, as nuk guxojnë të kenë qasje në folderët që janë “mbrapa”, “sipër” apo “poshtë” folderit public_html (ose public, www, htdocs,…, varësisht nga serveri).

Folderi ku do të vendosen fotografitë duhet të kenë “write permission”, pra sistemi operativ duhet të lejojë që në atë folder të vendosen fajlla. Kjo në Linux rregullohet me komandën chmod:

1 chmod -Rf 755 ./folderi

Nëse nuk realizohet me 755, provojmë me vlerat 775 dhe 777, si opsion i fundit.

Në Windows, pra aty ku e bëjmë zhvillimin dhe testimin e aplikacionit në mënyrë lokale - nëse kemi bërë login si administator, nuk do të kemi nevojë për të caktuar këto privilegje.

Përndryshe, e ndjekim këtë procedurë:

  • Klikojmë emrin e folderit
  • Klikojmë butonin e djathtë dhe zgjedhim Properties
  • Klikojmë tab-in Security
  • Klikojmë emrin e përdoruesit
  • Klikojmë Edit
  • Te rreshti Write zgjedhim checkbox-in e kolonës Allow

Nëse folderi nuk i ka permissions adekuate, do të shfaqet raporti i gabimit në momentin kur skripta jonë tenton ta vendosë fajllin atje.

 1 <?php
 2 $fajlli = $_FILES['fajlli'];
 3 
 4 if ($fajlli['error'] == 0)
 5 	{
 6 
 7 	if ($fajlli['size'] <= 1048576)
 8 		{
 9 		if ($fajlli['type'] == "image/jpeg" 
10 || $fajlli['type'] == "image/png")
11 			{
12 			$f = move_uploaded_file($fajlli['tmp_name'],
13  "assets/images/".$fajlli['name']);
14 			if ($f)
15 				{
16 				echo "Fajlli u ruajt me sukses";	
17 				}
18 			else
19 				{
20 				echo "Deshtoi ruajtja e fajllit";	
21 				}
22 			}
23 		}
24 	else
25 		{
26 		echo "Fajlli teper i madh. Lejohet maks. 1 Mb.";	
27 		}
28 	}
29 else
30 	{
31 	echo "Gabim gjatë dërgimit të fajllit!";	
32 	}
33 ?>	

Si parametër i parë i funksionit move_uploaded_files() përdoret emri i përkohshëm i fajllit, që është një emër unik i rastësishëm i fajllit në folderin tmp. Përdoret një emër unik në mënyrë që të mos e bëjnë overwrite njëri-tjetrin dy fajlla me emër dhe tip të njëjtë.

Si parametër i dytë përdoret path-i i folderit ku do të ruhet fajlli (path relativ ndaj lokacionit të skriptës), bashkangjitur me emrin e fajllit:

1 "assets/images/".$fajlli['name']

Nëse funksioni move_uploaded_files() e transferon me sukses fajllin, fajlli i përkohshëm fshihet.

Ajo në çka duhet të kemi kujdes në këtë fazë është se nëse fajlli i sapongarkuar ka emër dhe tip të njëjtë me ndonjë fajll që tashmë është në atë folder, ky i dyti do ta bëjë overwrite të parin. Prandaj, për të parandaluar situata të këtilla, shtohet kodi që njëherë kontrollon a ekziston fajll me emër të njëjtë (me funksionin file_exists()), në mënyrë që fajllit t’i ndryshohet emri para se të vendoset në atë folder.

 1 <?php
 2 $fajlli = $_FILES['fajlli'];
 3 
 4 if ($fajlli['error'] == 0)
 5 	{
 6 	if ($fajlli['size'] <= 1048576)
 7 		{
 8 		if ($fajlli['type'] == "image/jpeg" || $fajlli['type'] == "image/png")
 9 			{
10 			$filepath = "assets/images/";
11 			$filename = $fajlli['name'];
12 			
13 			while (file_exists($filepath.$filename))
14 				{
15 				$ext = pathinfo($filename, PATHINFO_EXTENSION);
16 				$basename = pathinfo($filename, PATHINFO_BASENAME);
17 				$name = substr($filename, 0, -strlen($ext)-1);
18 				$filename = $name."_".rand(1, 10000).".".$ext;
19 				}
20 				
21 			if (move_uploaded_file($fajlli['tmp_name'], $filepath.$filename))
22 				{
23 				echo "Fajlli u ruajt me sukses";	
24 				}
25 			else
26 				{
27 				echo "Deshtoi ruajtja e fajllit";	
28 				}
29 			}
30 		}
31 	else
32 		{
33 		echo "Fajlli teper i madh. Lejohet maks. 1 Mb.";	
34 		}
35 	}
36 else
37 	{
38 	echo "Gabim gjatë dërgimit të fajllit!";	
39 	}
40 ?>	

Kjo skriptë do të verifikojë nëse fajlli me emër dhe tip të njëjtë tashmë ekziston në folder. Nëse ekziston, emri të tij i shtohet shenja “_” (underline), dhe më pas një numër i rastësishëm ndërmjet 1 dhe 10000. Kështu, nuk do të ketë gjasa që një fajll ta mbulojë tjetrin.

Natyrisht, këtu mund të përdorim edhe zgjidhje të tjera. Një prej tyre është me përdorimin e ID-së së produktit në emrin e fajllit, duke u siguruar kështu se nuk do të ketë dy fajlla me emër të njëjtë, sepse ID-të janë unike për produktet. Nëse kemi më shumë se një fotografi për produkt, mund të shtojmë numrin rendor 1, 2, 3…

Leximi i ID-së së INSERT-it të fundit në mysqli bëhet me funksionin mysqli_insert_id().

Nëse psh. ID-ja e INSERT-it të fundit është 456, atëherë mund të konstruktojmë emërtime si vijon:

  • 456.jpg
  • product-456.jpg
  • product-456-1.jpg
  • product-456-2.jpg
  • product-456-3.jpg

Me një zgjidhje të këtillë, nuk do të kemi nevojë të verifikojmë nëse fajlli ekziston sepse e dimë se nuk ka dy produkte që kanë ID të njëjtë

Cookie mundëson ruajtjen e të dhënave që duhet të persistojnë ndërmjet faqeve (skriptave) të ndryshme.

Metodat POST dhe GET nuk janë praktike për ruajtjen e të dhënave persistuese, meqë për bartjen e vlerave kërkojnë linqe me query string (në rastin e GET), apo ndërtimin e formularëve (në rastin e POST), gjë që shton shkallën e ndërlikueshmërisë së aplikacionit e me këtë edhe mundësitë për gabime.

Cookie i ruan vlerat që duhet të persistojnë në fajlla specialë që menaxhohen nga browseri.

Sintaksa:

1 bool setcookie ( string $name 
2 [, string $value 
3 [, int $expire = 0 
4 [, string $path 
5 [, string $domain 
6 [, bool $secure = false [, bool $httponly = false ]]]]]] )

Shembull

1 setcookie('company', 'Apple');

Në këtë shembull bazik, ‘company’ është anëtar i vargut $_COOKIE, me vlerën ‘Apple’. Kur kalojmë në një skriptë tjetër të aplikacionit, këtë vlerë do ta shfaqim me:

1 <?php
2 echo $_COOKIE['company'];
3 ?>

Natyrisht, në praktikë, si vlerë do të përdoret një variabël, si psh:

1 setcookie('company', $company_name);

ku vlera e atij variabli është përcaktuar më heret, psh. është lexuar nga formulari, databaza apo burime të tjera.

Si parametër i tretë është jetëgjatësia e cookie, ku si vlerë shënohet UNIX timestamp i kohës kur cookie duhet të skadojë. Kjo më së lehti mund të shënohet si time() + numri i sekondave. Psh. nëse dëshirojmë që cookie të “jetojë” 7 ditë, shënojmë:

1 setcookie('company', 'Apple', time() + 7*24*60*60);

me çka çastit aktual i shtohen 604800 sekonda, pra aq sa ka një javë.

Pra, brenda kësaj periudhe, skripta jonë do të ketë çasje vlerës së indeksit/çelësit ‘company’ dhe ajo do ta japë vlerën ‘Apple’, por me kalimin e këtij afati, cookie do të fshihet dhe me të edhe vlera e ruajtur.

setcookie() kthen vlerën TRUE nëse ka sukses në krijimin e cookie-t, ndërsa FALSE në të kundërtën.

Një cookie do të fshihet nëse si parametër të jetëgjatësisë shënojmë vlerë më të vogël se koha aktuale, psh time()-3600 e bën cookie-n 1 orë të vjetër dhe me këtë shkakton fshirjen e tij.

Nëse jepet vlera 0, cookie do të “jetojë” vetëm deri në fund të sesionit, pra do të fshihet me mbylljen e browserit.

Aspekti i jetëgjatësisë së cookie-t varet edhe nga opsionet e zgjedhura në browser.

Path

Parametri i katërt paraqet shtegun ku cookie do të jetë disponabël. Vlera e nënkuptuar, pra kur nuk shënohet ky parametër fare, është direktoriumi ku gjendet skripta e cila e ka krijuar cookie-n. Në këtë rast, skriptat në direktoriumet tjera nuk do të jenë në gjendje ta lexojnë vlerën e cookie-t.

Nëse si vlerë shënohet /, atëherë vlera e cookie-t mund të lexohet brenda gjithë domainit.

Nëse si vlerë shënohet emri i një direktoriumi, si psh /admin/, vlera e cookie do të jetë në dispozicion të atij direktoriumi dhe të gjitha niveleve të nëndirektoriumeve brenda saj.

Domain

Parametri i pestë është domaini ku vlen ky cookie, me çka atë e “lidhim” për një domain të caktuar.

Secure

Indikon që cookie mund të dërgohet vetëm nëse ndërmjet browserit dhe serverit është vendosur SSL koneksion, pra nëse lidhja është e enkriptuar.

httponly

Kufizon përdorimin e cookie vetëm brenda HTTP protokolit, duke pamundësuar aksesin në të nga JavaScript. Me këtë në masë të konsiderueshme rritet siguria e cookie, meqë kështu mund të parandalohet një numër i XSS sulmeve (cross-site scripting). Për ta aktivizuar këtë mundësi, shënohet vlera TRUE.

Sesionet

E meta kryesore e protokolit HTTP është pamundësia për ta ruajtur gjendjen, gjegjësisht për t’i ruajtur vlerat e variablave gjatë kalimit nga një skriptë në tjetrën. Kjo mangësi deri diku kompensohet si vijon:

  • duke ia bashkangjitur vlerat e dëshiruara URL-së që thirret (GET),
  • duke i dërguar vlerat me anë të një formulari (POST),
  • duke i ruajtur vlerat në një fajll lokal (COOKIE) për t’i bartur pastaj në server

Në të tri rastet flitet për bartje të vlerave, e jo për ruajtjen e tyre. Gjatë bartjes së atyre vlerave, është i mundur edhe ndryshimi i tyre, me apo pa qëllim.

Për aplikacione më komplekse, ku duhet të ruhet gjendja, gjegjësisht zgjedhjet që i ka bërë një vizitor (si në rastin e një e-commerce aplikacioni), asnjëra prej metodave të cekura nuk ofrojnë fleksibilitet të mjaftueshëm.

Metoda GET bie poshtë sepse do të duhej që në çdo URL të shtohet query string me vlerat e variablave, diç që në një moment mund të jetë i pamenaxhueshëm nga ana e skriptës për shkak të kompleksitetit. Veç kësaj, vlerat në URL janë të manipulueshme, gjë që do ta cenonte integritetin e aplikacionit. Imagjinoni nëse në URL vendoset çmimi dhe vizitori e ndryshon atë.

Metoda POST është jopraktike për shkak se në çdo faqe do të duhej të futeshin formularë, të cilat fillimisht i lexojnë vlerat e dëshiruara, për t’ia bartur pastaj skriptës së thirrur.

COOKIE poashtu i ka mangësitë e veta që e bëjnë të papërshtatshëm për përdorim në të gjitha aspektet e një aplikacioni kompleks. Ato mund të jenë të manipulueshme sepse të dhënat ruhen në një fajll lokal, ku sulmuesi mund t’i ndryshojë të dhënat sipas nevojës, për t’iu prezantuar Web serverit si ndonjë vizitor tjetër. Cookiet, nëse nuk janë ndërmarrë masat e duhura, janë të lexueshme nga JavaScript, duke e bërë kështu aplikacionin të ekspozueshëm ndaj XSS.

Zgjidhja më e mirë ofrohet me SESSION, ku mekanizmi për bartjen dhe ruajtjen e vlerave është diç më kompleks, por më i kompletuar dhe më i sigurtë.

Sesioni startohet me funksionin session_start(). Me këtë i udhëzohet serverit që ta krijojë një sesion të ri, apo ta vazhdojë sesionin aktual.

Me krijim të sesionit të ri nënkuptohet krijimi i një fajlli në server, brenda të cilit do të ruhen të gjitha të dhënat e nevojshme. Ai fajll do të largohet automatikisht kur mbaron sesioni.

Pra, pas session_start() krijohet një fajll që i korrespondon atij sesioni. Emërtimi i fajllit është një hash unik, dhe kjo bëhet me qëllim që të parandalohet përdorimi i fajllit të njëjtë për dy sesione. Ai hash paraqet ID-në e sesionit, session_id().

Meqenëse HTTP, gjatë kalimit prej një faqeje në tjetrën nuk e ruan gjendjen, si do të dijë serveri se cili sesion cilit vizitor i përket?

Të dhënat që Web serveri i merr prej browserit janë të pamjaftueshme për ta identifikuar vizitorin në mënyrë unike. Të dhënat “identifikuese” janë:

  • IP adresa
  • Browseri
  • Versioni i browserit
  • Sistemi operativ

Këto të dhëna i dërgohen serveri gjatë çdo HTTP request, psh. kur kërkohet hapja e një faqeje dhe duken kështu:

Mozilla/5.0 (Windows NT 6.3; WOW64; rv:37.0) Gecko/20100101 Firefox/37.0

Këto të dhëna janë të pamjaftueshme për ta identifikuar vizitorin, sepse Web serveri e “sheh” vetëm IP adresën publike, ndërkohë që janë me miliona kompjuterë prapa NAT (Network Address Translation), gjegjësisht prapa routerëve, me IP adresa private. As të dhënat mbi sistemin operativ dhe browserin nuk mund të formojë një identifikator unik sepse shumë përdorues mund ta kenë të njëjtin sistem operativ dhe të njëjtin version të browserit.

Në pamundësi të identifikimit të vizitorit nga të dhënat e dërguara me HTTP, Web serveri shërbehet me një cookie që do t’ia dërgojë browserit, nëse ai cookie tashmë nuk ekziston në atë browser. Cookie do të përmbajë vlerën (hash-in) e session ID. Ky cookie, prej momentit të krijimit e deri në fund të sesionit, do t’i bashkangjitet çdo HTTP requesti duke ia bartur Web serverit ID-në e sesionit, e në bazë të kësaj - Web serveri do të dijë se në cilin fajll t’i regjistrojë vlerat e sesionit.

Pra, për ndërtimin e “urës” ndërmjet browserit dhe Web serverit, na ndihmon prezenca e një cookie që e përmban ID-në e sesionit.

Skenari:

  • Browseri i dërgon Web serverit kërkesë për hapjen e faqes
  • Serveri verifikon HTTP requestin e pranuar dhe kërkon prezencën e cookie me ID-në e sesionit
  • Nëse cookie nuk ekziston, krijohet një sesion i ri (duke krijuar fajll të ri) dhe ID-ja e atij sesioni regjistrohet në një cookie që i dërgohet browserit në çdo HTTP response
  • Nëse cookie ekziston në HTTP request, lexohet vlera e ID-së së sesionit, dhe kërkohet fajlli me atë ID (hash). Nëse fajlli ekziston, procesi vazhdon normalisht. Nëse nuk ekziston, kjo do të thotë se cookie përmban informatë jovalide, kështu që hapet një sesion i ri duke krijuar një fajll të ri në server dhe duke dërguar një cookie të ri në browser.
  • Prej momentit që Web serveri ia dërgon browserit cookien me session ID, browseri do ta kthejë atë cookie në Web server në çdo HTTP request, ndërsa Web serveri do ta kthejë prapa në browser në çdo HTTP response. Me këtë “ping-pong” të cookie ndërmjet browserit dhe serverit ruhet kontinuiteti i “identitetit” të vizitorit përgjatë një sesioni.

Përmbajtja e cookie të sesionit:

PHPSESSID=pd6fqjdm7rc8d7ho5claukmv66

PHPSESSID është emri i cookie, ndërsa pd6fqjdm7rc8d7ho5claukmv66 është ID-ja e sesionit. Në Web server ekziston fajlli me emërtimin sess_pd6fqjdm7rc8d7ho5claukmv66 ku ruhen vlerat e variablave të përdorura gjatë këtij sesioni.

Me echo session_save_path(); mund të verifikojmë se në cilin direktorium ruhen fajllat e sesionit. Në rastin e WAMP, ky lokacion do të jetë c:/wamp/tmp:

  • sess_6nhs0c9j9u7qp3dcrtnh00mm96
  • sess_f5cnai4mdvqkem7v286dr9ar66
  • sess_g76a18t8p7opc328qpj6grd7m23gg10l
  • sess_jkn3s6ac38f029ah9jip2t5v93 …

Nëse e hapim ndonjërin prej fajllave, aty do të gjejmë të dhëna të serializuara të sesionit:

1 shporta|a:2:{i:0;a:3:{s:2:"id";i:1;s:5:"price";d:650.14999999999998;s:3:"qty";d:1;}i\
2 :1;a:3:{s:2:"id";i:2;s:5:"price";d:999.95000000000005;s:3:"qty";d:4;}}crsf_token|s:3\
3 2:"ecdb364e8740ce40879410a7071725e4";

Këto të dhëna janë të ruajtura në plain text dhe mund të dekodohen lehtë si array:

 1 array (size=2)
 2   'shporta' => 
 3     array (size=2)
 4       0 => 
 5         array (size=3)
 6           'id' => int 1
 7           'price' => float 650.15
 8           'qty' => float 1
 9       1 => 
10         array (size=3)
11           'id' => int 2
12           'price' => float 999.95
13           'qty' => float 4
14   'crsf_token' => string 'ecdb364e8740ce40879410a7071725e4' (length=32)

Prandaj, është me rëndësi që askush nga jashtë të mos ketë qasje në këto fajlla.

Jetëgjatësia e sesionit

Sa do të jetojë ky cookie, gjegjësisht sa do të jetë jetëgjatësia e një sesioni? Varet si është konfiguruar PHP serveri. Vlera standarde është 1440 sekonda, gjegjësisht 24 minuta.

php.ini

session.gc_maxlifetime = 1140

Nëse brenda 24 minutave nuk ka aktivitet nga ana e vizitorit, fajlli i sesionit fshihet dhe në mungesë të tij, sesioni do të skadojë.

Natyrisht, ka metoda për tejkalimin e këtij “kufizimi”, me përdorimin e një cookie tjetër që e përmban ID-në e anëtarit (jo të sesionit) dhe që ka jetë më të gjatë (psh 30 ditë). Aplikacioni mund të programohet asisoj që së pari e verifikon ekzistencën e këtij cookie të dytë dhe nëse ekziston, e lexon ID-në e anëtarit nga aty dhe fillon një sesion të ri, duke e deklaruar anëtarin të autentikuar. Në këtë parim funksionon opsioni “Remember me”, që e hasim nëpër Web site të ndryshme.

Fillimi i një sesioni

session_start();

Përfundimi i sesionit

Sesioni mbaron në tri mënyra:

  • Kur vizitori e mbyll browserin
  • Kur vizitori është inaktiv brenda një periudhe kohore
  • Kur vizitori e klikon butonin përkatës për çlajmërim
  • Kur fshihet session cookie nga browseri

Mbyllja e një sesioni:

1 $_SESSION = array(); 
2 session_destroy(); 
3 setcookie('PHPSESSID', ", time()-3600,'/', ", 0, 0);

Session hijacking

//

Menaxhimi i sesioneve nga databaza

Sesionet mund të menaxhohen edhe nga databaza.

Procesimi i formularëve

Formularët janë dritare të aplikacionit tonë ndaj ambientit të jashtëm.

MySQL

MySQL është server për databaza relacionale i cili në radhë të parë përdoret për Web databaza, në të cilat mbështeten aplikacionet dinamike. Përveç versioneve komerciale (MySQL Enterprise Edition, MySQL Cluster), ofrohet edhe versioni pa pagesë MySQL nën emërtimin MySQL Community Server (). MySQL ka përhapje të gjerë dhe është në ofertën standarde të të gjitha kompanive të hostingut. Përdoret nga firma si: Yahoo! Finance, MP3.com, Motorola, NASA, Silicon Graphics, Texas Instruments, etj.

MySQL përdor standarde të hapura siç janë ANSI SQL 99, për të komunikuar me databaza nëpërmes gjuhës SQL (Structured Query Language). Ky standard mundëson selektimin, futjen, përditësimin dhe fshirjen e të dhënave nga databaza.

MySQL përdoret në shumë sisteme operative, siç janë: Linux, Windows, Mac OS X, BSD dhe varianta të ndryshme të UNIX.

Në kuadër të aplikacionit ofrohet edhe klienti për rreshtin komandues interaktiv, i cili mundëson komunikim në me serverin. Përveç kësaj, MySQL ofron edhe metoda të tjera të qasjes në databazë, si p.sh. nëpërmes Web-it (phpMyAdmin), apo programit aplikativ MySQL Workbench (GUI Tool).

MySQL u mundëson gjuhëve të ndryshme programuese t’i qasen databazës, ku mund t’i numërojmë: PHP, Python, Perl, C, C++, C#, Java, etj.

mysqli

Konektimi me databazë

Për konektim me databaza, PHP përdor tri API të ndryshme:

  • PHP’s MySQL Extension
  • PHP’s mysqli Extension
  • PHP Data Objects (PDO)

Ne do ta përdorim ekstensionin mysqli (MySQL Improved), meqenëse ekstensioni MySQL nuk rekomandohet të përdoret për arsye të sigurisë.

Ekstensioni mysqli mundëson përdorimin në stilin procedural dhe stilin OOP (Object Oriented Programming), prej të cilave ne do ta përdorim të parën për spjegim më të thjeshtë.

Veprimi i parë që duhet të bëhet është akti i lidhjes, gjegjësisht konektimit në databazë.

Me këtë rast duhet të ofrohen të dhënat vijuese:

  • Host, pra adresa e kompjuterit ku është i instaluar MySQL serveri
  • Emri i përdoruesit të databazës (user)
  • Fjalëkalimi i databazës (password)
  • Emri i databazës (name)

Ata që përdorin WAMP, nëse nuk e kanë konfiguruar ndryshe, do t’i përdorin këto vlera:

  • Host: 127.0.0.1 ose localhost
  • User: root
  • Password: nuk ka fjalëkalim
  • Name: emri i databazës që dëshirohet të përdoret, në rastin tonë ickshop.

Për konektim përdoret funksioni mysql_connect(), të cilit i jepen 4 vlerat e cekura:

1 $dbcon = mysqli_connect("localhost", "root", "", "ickshop");

Natyrisht, në serverët e produksionit, pra në remote host, në asnjë mënyrë nuk lejohet krijimi i përdoruesit pa fjalëkalim. Fjalëkalimi duhet të jetë i gjatë dhe kompleks, për shkaqe sigurie.

Meqenëse shumë skripta të aplikacionit mund të kenë nevojë për t’u konektuar me databazën, nuk do të ishte praktike që në secilën thirrje të funksionit mysqli_connect() t’i shënojmë direkt të dhënat e kërkuara (host, name, user, password). Kjo për arsye se kur dëshirojmë ta ndryshojmë ndonjërën nga këto vlera, ndryshimet do të duhej t’i bënim në secilën prej skriptave.

Për këtë shkak, rekomandohet që të dhënat për konektim në databazë të vendosen në një fajll të veçantë, i cili fajll do të inkludohet (me require ose include) kudo të jetë nevoja. Nëse duhet të ndryshohen parametrat, ndryshimet bëhen vetëm te ai fajll.

db.php

1 define("DB_HOST", "localhost");
2 define("DB_USER", "root");
3 define("DB_PASS", "");
4 define("DB_NAME", "ickshop");

skriptat:

1 include("db.php");
2 $dbcon = mysqli_connect(DB_HOST, DB_USER, DB_PASS, DB_NAME);
3 .
4 .
5 .

Funksionet kryesore

mysqli_connect()

Hapja e një koneksioni të ri me MySQL serverin. Si vlerë kthyese e jep një objekt i cili e përfaqëson atë koneksion.

Sintaksa:

1 mysqli_connect(host,username,password,dbname,port,socket);

Katër argumentet e para i pamë në shembullin e mëparshëm.

Argumenti “port” cakton TCP portin e MySQL serverit. Nëse nuk shënohet, nënkuptohet vlera 3306.

Argumenti “socket”, që tregon emrin e “MySQL socket file”, një fajll i formatit të veçantë për transmetimin e të dhënave në server dhe nga serveri. Krijohet automatikisht dhe lokacionin përcaktohet në my.ini, me direktivën socket.

Në Windows: C:\wamp\bin\mysql\mysql5.6.17\my.ini

1 socket		= /tmp/mysql.sock

Në rast se konektimi dështon, vlera kthyese e funksionit është FALSE. Kjo vlerë mund të shfrytëzohet për të verifikuar nëse konektimi ka qenë i suksesshëm.

 1 <?php
 2 $dbcon = mysqli_connect("localhost","root","","ickshop");
 3 
 4 if (!$dbcon)
 5 {
 6 echo "Konektimi dështoi!";
 7 die();
 8 }
 9 .
10 .
11 .
12 ?>

mysqli_set_charset()

Caktimi i character set të koneksionit.

Përdoret vlera “utf8” për kompatibilitet me shumicën e alfabeteve botërore.

Gjatë krijimit të databazës, si vlerë për Collation mund të përdoret utf8_unicode_ci.

Në HTML dokumente, meta tagu charset duhet të jetë <meta charset="utf-8">.

Me këto veprime sigurohet që të mos shfaqen karaktere të gabuara nëpër faqet e sajtit, që mund të jenë pasojë e mospërputhjes së character set.

1 mysqli_set_charset($dbcon, 'utf8');

mysqli_connect_errno()

Tregon kodin e gabimit që paraqitet në rastet kur dështon konektimi në server.

  • 2002 - Serveri e refuzon konektimin. Hosti nuk ekziston ose porti i gabuar.
  • 1044 - Përdoruesit të cekur i refuzohet konektimi në server. Emër i gabuar.
  • 1045 - Përdoruesit të cekur i refuzohet konektimi në server. Fjalëkalim i gabuar.
  • 1049 - Databazë e panjohur. Emri i databazës nuk është shkruar mirë.

mysqli_connect_error()

Njëjtë si mysqli_connect_errno(), por raportet janë tekstuale.

  • 2002 - No connection could be made because the target machine actively refused it.
  • 1004 - Access denied for user ‘’@’localhost’ to database ‘ickshop’
  • 1045 - Access denied for user ‘root’@’localhost’ (using password: YES)
  • 1049 - Unknown database ‘ickhshop’

mysqli_real_escape_string()

Sintaksa:

1 mysqli_real_escape_string(connection,escapestring);

Përdorimi:

1 $emri = mysqli_real_escape_string($dbcon, $_POST['emri']);

Çdo e dhënë që duhet të regjistrohet në databazë, me INSERT apo me UPDATE, duhet paraprakisht të pastrohet, për të parandaluar futjen e të dhënave që potencialisht mund të jenë të rrezikshme, psh. ato që mund të shkaktojnë SQL injection.

Funksioni mysqli_real_escape_string() bën konvertimin karaktereve speciale (NUL, \n, \r, \, ‘, “ dhe Control-Z) në escape code, të sigurta për t’u ruajtur në databazë. Merret parasysh charset i databazës, prandaj është më i sigurtë se funksionet korresponduese të PHP, si addslashes. Nuk mund të thirret nëse nuk është vendosur koneksioni me MySQL serverin.

mysqli_query()

Ekzekutimi i SQL kërkesave.

mysqli_fetch_array()

1 $row= mysqli_fetch_array($result, MYSQLI_ASSOC);

mysqli_num_rows()

Përdorimi: mysqli_num_rows($dbcon);

Tregon sa rekorde të tabelës janë lexuar me ekzekutimin e një urdhëri SELECT.

mysqli_affected_rows()

Përdorimi: mysqli_affected_rows($dbcon)

Tregon mbi sa rekorde të tabelës ka pasur efekt ndonjëri prej urdhërave: UPDATE, DELETE apo INSERT. Për shembull, sa rekorde janë fshirë me ekzekutimin e SQL kërkesës së fundit që përmban DELETE.

mysqli_free_result()

Përdorimi:

1 mysqli_free_result($result)

E largon rezultatin e një SQL kërkese të ekzekutuar nga RAM memorja e Web serverit. Shpesh ndodh që SQL kërkesat të lexojnë nga ndonjë tabelë një sasi të madhe të rreshtave, ku të gjitha ato vendosen brenda një variabli, si në shembullin vijues:

1 $q = "SELECT * FROM products";
2 $result = mysqli_query($dbcon, $q);

Nëse SQL kërkesa ka kthyer 1000 rreshta nga tabela products, të gjithë ata rreshta janë të ruajtura brenda variablit $result, efektivisht duke e ngarkuar RAM memorjen. Sapo të procesohen të dhënat e ruajtura në ‘$result’, ai variabël duhet të zbrazet, për ta liruar RAM memorjen. Kjo është me rëndësi sepse në Web server mund të jenë të vendosur shume Web sajte ku secila mund të ketë shumë vizitorë, ndërkohë që RAM memorja është një resurs që shfrytëzohet bashkarisht nga të gjithë, prandaj duhet ta shfrytëzojmë sa më racionalisht.

mysqli_close()

Përdorimi:

1 mysqli_close($dbcon)`

E mbyll koneksionin e zgjedhur. Në fund të procesimit të të dhënave të lexuara nga databaza, rezultatet duhet të fshihen me mysqli_free_result() dhe koneksioni të mbyllet me mysqli_close().

Shembuj me mysqli

Leximi i të dhënave nga një SQL tabelë

Leximi i të dhënave nga tabela products. Janë lexuar fushat Pro_ID dhe Pro_Name.

 1 $q = "SELECT Pro_ID, Pro_Name FROM products";
 2 $result = mysqli_query($dbcon, $q);
 3 
 4 echo "<table>";
 5 while ($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
 6  echo "<tr>
 7  <td>" . $row['Pro_ID'] . "</td>
 8  <td>" . $row['Pro_Name'] . "</td>
 9  </tr>";
10  }
11 echo "</table>";

Prepared statements

 1 <?php
 2 $link = mysqli_connect("localhost", "root", "", "ickshop");
 3 
 4 if (!$link) {
 5 $error = mysqli_connect_error();
 6 $errno = mysqli_connect_errno();
 7 print "$errno: $error\n";
 8 exit();
 9 }
10 
11 $query = "SELECT * FROM products WHERE Pro_Cat_ID=?";
12 
13 $stmt = mysqli_stmt_init($link);
14 if (!mysqli_stmt_prepare($stmt, $query)) {
15 print "Failed to prepare statement\n";
16 } else {
17 $category = "1";
18 mysqli_stmt_bind_param($stmt, "i", $category);
19 // i - integer
20 // d - double
21 // s - string
22 // b - blob
23 
24 
25 mysqli_stmt_execute($stmt);
26 $result = mysqli_stmt_get_result($stmt);
27 
28 echo "<table>";
29 while ($row = mysqli_fetch_assoc($result)) {
30 
31 echo "<tr>
32 <td>" . $row['Pro_ID'] . "</td>
33 <td>" . $row['Pro_Name'] . "</td></tr>";
34 
35 }
36 echo "</table>";
37 }
38 
39 mysqli_stmt_close($stmt);
40 mysqli_close($link);
41 ?>

.

Regular Expressions

Të gjitha Web faqet janë të mbushura me tekst. Edhe vetë HTML dokumentet janë fajlla tekstualë që dallohen nga ato .txt vetëm me prezencën e tag-ave. Çdo input në formularë është tekst dhe ata ruhen në formë teksti në databazë, apo në ndonjë JSON fajll.

Të dhënat që futen në një formular janë të natyrave të ndryshme: emër, email, datë, numër telefoni, numër letërnjoftimi, numër karte krediti, etj. Të gjitha këto kërkojnë formatim të caktuar për të qenë të dhëna korrekte:

  • Emri mund të ketë vetëm shkronja
  • Emaili duhet ta ketë një shenjë @, një tekst para asaj shenje dhe një domain name pas shenjës.
  • Telefoni mund ta ketë formatin +377(44)123-456
  • Data mund ta ketë formatin 28.03.2015, pra dita.muaji.viti, ku dita dhe muaji janë numra dy shifrorë, ndërsa viti është katër shifror.
  • etj.

Aplikacioni ynë nuk duhet të lejojë që të dhënat të ruhen nëse ato nuk janë të formatuara si duhet.

Për t’i parandaluar gabimet e tilla, në ndihmë na vijnë regular expressions, me anë të cilave do të jemi në gjendje ta analizojmë të dhënën dhe të konstatojmë nëse i plotëson kriteret e caktuara. Për shembull, nëse emri përmban vetëm shkronja.

Kriptimi

Të dhënat në Web transferohen në formë të fajllave tekstualë. Edhe nëse është përdorur SSL për ta shifruar komunikimin browser-server, ato të dhëna prapë kthehen në trajtë tekstuale në momentin e arritjes në server.

Pra, të dhënat e pranuara me cilëndo metodë (GET, POST, databazë, JSON, etj.) dhe pa marrë parasysh a është përdorur a jo SSL, në përfundim të procesit të transferimit - ruhen si tekste të rëndomta.

Kështu, ato të dhëna edhe kur ruhen në databazë, do të jenë të ruajtura si tekste. Nëse ndodh që dikush me SQL injection attack arrin t’i qaset të dhënave në server, ai do të jetë në gjendje t’i lexojë të dhënat e ruajtura aty. Kjo në veçanti është e disfavorshme nëse të dhënat janë sensitive, siç janë: fjalëkalimi, numri i kartelës së kreditit, të dhënat financiare, etj.

Për ta parandaluar rrezikun e leximit të të dhënave sensitive nga databaza, duhet të përdoren metoda të shifrimit të të dhënave në server.

Janë dy metoda kryesore për shifrimin e të dhënave:

  • Crypt
  • Hash

Në këtë kapitull do të fokusohemi vetëm në cryptographic hash functions.

Hash

Hash është një metodë tjetër e shifrimit të të dhënave, veçori kryesore e së cilës është se teksti i enkriptuar nuk mund të rikthehet në formën e vet origjinale. Për këtë, ky algoritëm gjen zbatim vetëm në raste të caktuara, dhe kryesisht përdoret për enkriptimin e fjalëkalimeve (password), si dhe për verifikimin e integritetit të fajllave (checksum).

Funksionet në PHP për hash janë:

  • hash()
  • md5()
  • sha1()

Për nevojat e aplikacionit tonë, ne do të përqëndrohemi vetëm në sha1(), por parimet janë të njëjta për secilin prej funksioneve të cekura.

E marrim shembull një fjalëkalim të mundshëm: ick2015.

1 <?php
2 $fjalekalimi = "ick2015";
3 echo $fjalelalimi." ".sha1($fjalekalimi);
4 ?>

Rezultati:

1 ick2015 1488c61e22f54e75d43ac3935fea5e85451eb6be

Pra, nga stringu ick2015 fitohet hash-i:

1488c61e22f54e75d43ac3935fea5e85451eb6be

Gjatësia e stringut rezultues te sha1() është 40 karaktere, pa marrë parasysh sa ka qenë gjatësia e stringut origjinal!

Teorikisht, për çdo tekst të ndryshëm do të gjenerohet një hash tjetër. Pra, nuk duhet që dy tekste të ndryshme ta japin hash-in e njëjtë. Kjo vetëm në teori, gjersa në praktikë kemi raste të evidentuara të kolizioneve (collisions), pra të rasteve kur dy tekste që s’kanë kurrfarë ngjashmërie me njëri-tjetrin - kanë dhënë hash të njëjtë. Më shumë kolizione ka te algoritmet që kanë hash më të shkurtër. Funksioni md5() gjeneron hash 32 karakterësh, gjersa sha1(), siç u përmend më sipër - gjeneron hash 40 karakterësh. Kjo është arsyeja që ndërmjet md5() dhe sha1(), gjithmonë zgjedhet ky i dyti. Rekomandim i ekspertëve të sigurisë si dhe të institucioneve të ndryshme është që md5() të nxirret krejtësisht nga përdorimi.

Për thjeshtim të spjegimit, le ta kuptojmë hash-in si një “nënshkrim” të një teksti. Sikur çdo njeri që ka nënshkrim që dallon prej të tjerëve, ashtu edhe çdo tekst, teorikisht, duhet ta ketë hash-in e vetë unik.

Pyetja që vie vetvetiu është: si e fitoj përmbajtjen origjinale të tekstit duke u bazuar vetëm në 40 karaktere? Si e kthej një tekst me 10000 karaktere nga një hash 40 karakterësh? Përgjigja është: hash as nuk përdoret për kthimin e përmbajtjes origjinale! Pra, përmbajtja e hash-it s’ka kurrfarë lidhje përmbajtësore me tekstin origjinal, është vetëm një “nënshkrim” unik dhe në vete nuk përmban asgjë që mund të asocojë në përmbajtjen e tij.

Thënë ndryshe, prej nënshkrimit nuk mund ta rikonstruktojmë njeriun, por dijmë se nënshkrimi i kujt është kur ai njeri nënshkruhet prapë! Kur ai nënshkruhet prapë, i krahasojmë me nënshkrimet e të gjithë të nënshkruarve, dhe kur ta gjejmë nënshkrimin e njëjtë - konstatojmë se është ai njeri ka qenë këtu më parë.

Përdorimi i hash-it në Web aplikacion

T’i kthehemi Web aplikacionit tonë. Të gjithë anëtarët e regjistruar e Web faqes, i kanë të dhënat të regjistruara në një MySQL tabelë, në rastin tonë të quajtur “users”. Disa nga fushat e tabelës janë:

  • ID-ja unike e anëtarit, që është një numër rendor, i ndryshëm për secilin anëtar
  • Emri dhe mbiemri i anëtarit
  • Email adresa e anëtarit
  • Fjalëkalimin i anëtarit, etj.

Nëse zgjedhim që fjalëkalimin e anëtarit ta ruajmë si plain text, pra tekst të pashifruar, kjo ka ndikim negativ në sigurinë e të dhënave të anëtarëve sepse ato të dhëna sensitive mund të “nxirren” me një SQL injection attack siç u cek në fillim. Prandaj, fjalëkalimet ruhen në formë të hash, sepse sulmuesi që vie në dispozicion të tabelës së anëtarëve, nuk do të jetë në gjendje t’ua dijë fjalëkalimet duke u bazuar në hash. Pa marrë parasysh çfarë fjalëkalimesh kanë pasur dhe çfarë gjatësie kanë pasur fjalëkalimet, në kolonën e fjalëkalimeve, sulmuesi do të shohë vetëm vargje shkronjash e numrash “të rastësishme” me gjatësi prej 40 karakteresh. Duke ditur se nuk mund të ketë funksion që e bën të kundërtën e sha1(), ne mund të pretendojmë se të dhënat janë të palexueshme për sulmuesin.

Rainbow tables

Por, në praktikë, ka dallim fare të vogël në nivelin e sigurisë ndërmjet plain text dhe hash!

Pse?! Sepse tashmë një kohë të gjatë janë duke u formuar databaza tejet të mëdha në të cilat ruhen tekste të ndryshme të shkurtëra bashkë me hash-in korrespondues. Mjafton që të shënohet hash-i, dhe nga databaza do të nxirret teksti origjinal brenda një kohe të shkurtër! Pra, nuk ka nevojë që ndonjë kompjuter të punojë me orë e ditë të tëra për të bërë “crack”, mjafton vetëm një kërkim në databazë! Këto quhen “rainbow tables” dhe tashmë ka në qarkullim databaza të tilla të madhësisë prej disa Tb.

Shembull: https://crackstation.net

Nëse e marrim psh fjalën “tralala”, me sha1() do ta fitojmë këtë hash:

bb31fbff8fac91c17ee6052d98084e6e63184859

Tash, këtë hash e kopjojmë dhe e vendosim në fushën përkatëse të Web sajtit të cekur dhe shtypim butonin “Crack Hashes”. Si rezultat do të shfaqe fjala “tralala”.

Nëpër “rainbow tables”, gjasat më të mëdha për t’u “thyer” i kanë fjalëkalimet vijuese:

  • Fjalëkalimet e shkurtëra: sesame (084a3501edef6845f2f1e4198ec3a2b81cf5c6bc)
  • Fjalëkalimet që përmbajnë vetëm shkronja, qoftë uppercase, qoftë lowercase: prishtinacity (16c952e331eb893de13258bafe24e0d126d3e7c1)
  • Fjalëkalimet që përmbajnë vetëm numra, psh datëlindjet: 27031990 (9aa1c46a255d9b308bda1197a67757a63b05cee8)
  • Fjalëkalimet të çfarëdo gjatësie por që përmbajnë fraza të njohura: hastalavistababy (5541202440b69214af1f24fe6ba385da6709a2bea)

Hash-at e dhënë si shembull mund të testohen në Crack Station, dhe do të shohim se të gjithave u gjindet teksti origjinal!

Meqë në “rainbow tables” tashmë ruhet një numër enorm i hash-ave dhe ky numër është gjithnjë në rritje, ruajtja e fjalëkalimeve në formë hash-i ka fare pak vlerë në aspektin e sigurisë, nëse kemi parasysh fjalëkalimet e shkurtëra dhe ato tipike. Fjalëkalimet e gjata dhe komplekse (që përbëhen nga kombinimi i shkronjave të vogla e të mëdha, numrave dhe shenjave speciale, psh. k!uhFRn9ds54&^!2) nuk janë të rrezikuara nga “thyerja” në “rainbow tables”. Thënë më saktë, ende jo. Me rritjen e fuqisë procesorike të kompjuterëve, në veçanti kur ato lidhen në distributed computing, si dhe të rënies së çmimit të disqeve me kapacitet të madh, nuk është larg dita se këto tabela do të përmbajnë edhe fjalëkalime komplekse, e me këtë t’i diskualifikojnë fjalëkalimet që sot mund të konsiderohen të sigurta.

Fjalëkalimet komplekse, ndonëse më të sigurta, ato janë të vështira për t’u mbajtur në mend nga anëtarët. Për këtë arsye, dikush i ruan fjalëkalimet komplekse psh në email, dhe kështu i hapin dyert atyre që arrijnë t’ua “thejnë” llogarinë e emailit, me ç’rast do të jenë në gjendje t’i marrin këto fjalëkalime! Nëse ruani fjalëkalime të tilla në inbox, atëherë së paku bëjeni fjalëkalimin e emailit sa më kompleks e megjithatë të mbajtshëm në mend. Apo edhe më mirë: fjalëkalimet komplekse i printoni dhe i ruani si hard copy në ndonjë vend të sigurtë.

Ju mund të formoni fjalëkalime komplekse edhe nga tekstet që mbahen në mend lehtë. Psh. e marrim fjalën “Prishina”. Atë mund ta shënojmë si “pR!$hT!nA”, ku njëra shkronjë është e vogël e ajo vijuesja e madhe e kështu me radhë (p-R-h-T-n-A), shkronja “i” është zëvendësuar me “!”, ndërsa shkronja “S” është zëvendësuar me “$”. Ky është fjalëkalim kompleks por që bazohet në një fjalë e njohur.

Salt

Një zgjidhje që mund ta përmirësojë nivelin e sigurisë së fjalëkalimeve është ajo me përdorimin e salt. Çka është salt? Është një numër apo varg shkronjash e numrash, psh: 521457826, a9d5re2.

Salti i bashkangjitet fjalëkalimit në fillim apo në fund, dhe pastaj nxirret hash-i, që në këtë rast do të jetë krejt tjetërfare.

Shkojmë me shembull: Fjala “prishtinacity” e ka si hash stringun “16c952e331eb893de13258bafe24e0d126d3e7c1”.

E marrim një salt, psh. 521457826 dhe ia bashkangjesim në fillim dhe e nxjerrim hashin:

sha1(“521457826prishtinacity”); me ç’rast fitohet hashi i ri: c720ec040d745b38e97df275130c801d7af344aa

Ky tash është hashi i një fjalëkalimi kompleks i përbërë nga 22 karaktere, dhe si i tillë, ka pak gjasa të gjendet në ndonjë “rainbow table”. Edhe po të dekriptohej nga “rainbow tables”, do të rezultonte si “521457826prishtinacity”, ndërkohë që fjalëkalimi real është “prishtinacity”, kështu që prapë do të ishte i papërdorshëm. Natyrisht, me shikim të kujdesshëm, këtë fjalëkalim megjithatë do ta gjenim sepse i paraprin një varg i numrave, të cilat kur i heqim e fitojmë fjalëkalimin “e pastër”. Për këtë, për siguri sa më të lartë, në vend të numrave duhet të përdoren vargje alfanumerike, pra shkronja e numra, asisoj që sulmuesi ta ketë sa më të vështirë të kuptojë se ku fillon e ku mbaron fjalëkalimi, e cila pjesë është salt, pra fragment i shtuar.

Vazhdojmë me hashin që e fituam më sipër. Ai hash, pra c720ec040d745b38e97df275130c801d7af344aa ruhet në tabelën e përdoruesve, në rreshtin e dedikuar për përdoruesin konkret. Duke qenë se është hash i dy teksteve të kombinuara, ne duhet ta ruajmë edhe salt-in në të njëjtin rresht (record), në një fushë të veçantë. Kjo do të na nevojitet gjatë procesit të login, ku fjalëkalimit të shënuar në formularin e login duhet t’ia shtojmë salt-in nga databaza, për t’i bërë hash dhe për ta krahasuar me hash-in e ruajtur në databazë për atë anëtar.

Spjeguar step-by-step:

  • Anëtari regjistrohet, duke i dhënë këto informata: emri dhe mbiemri, email adresa, dhe dy herë fjalëkalimi. Dhënie e fjalëkalimit dy herë s’ka lidhje me hash, por bëhet për t’u siguruar se mos ka gabuar anëtari gjatë shkrimit të fjalëkalimit në formular, ku shkronjat e shkruara nuk shihen. Nëse e shkruan dy herë njëjtë, kjo do të thotë se ai di saktë çka ka shkruar dhe se do ta mbajë në mend. Por, nëse ai e shkruan dy herë fjalëkalimin e njëjtë por ai fjalëkalim nuk është fjalëkalimi që ka menduar se është duke e shkruar, ai nuk do të ketë qasje më vonë me atë fjalëkalim që mendon se e ka shkruar. Kjo ndodh psh kur përdoruesi mendon se është duke e përdorur tastierën amerikane, e në fakt e ka pasur të aktivizuar atë shqipe. Në këtë rast, fjalëkalimi i supozuar “prizreni” do të dërgohet në fakt si “priyreni”, meqë Y dhe Z i kanë vendet e ndërruara në tastierë, kur e përdorim tastierën shqipe! Përdoruesi nuk do ta dijë e ka gabuar, sepse të dy herat e ka shënuar të njëjtë dhe sistemi do të raportojë se gjithçka është në rregull.
  • Lexohen “POST data” nga skripta për regjistrim dhe gjersa të gjitha fushat tjera do të regjistrohen në databazë të pandryshuara, fusha e fjalëkalimit së pari duhet të përgatitet.
  • Me këtë “përgatitje” nënkuptojmë gjenerimin e një numri të madh (apo vargu tekstual) të rastësishëm, psh. një numër ndërmjet 1000000 deri 9999999.
  • Numri i rastësishëm bashkangjitet me fjalëkalimin e dërguar nga formulari i regjistrimit dhe prej kësaj nxirret hash-i. Pra, $pass1 = trim($_POST[‘password1’]);
    1 	$pass1 = trim($_POST['password1']);
    
    $random = rand(1000000, 9999999); $passhash = sha1($random.$pass1);
  • Ky hash tash ruhet në tabelën e anëtarëve, bashkë me të dhënat e tjera.
  • Pra, në databazë nuk u ruajt hash-i origjinal, por hashi i ri i kombinimit salt + fjalëkalimi.

Ky ishte spjegimi i procesit të regjistrimit. Hashi dhe salt-i tash e tutje do të na hyjnë në punë gjatë procesit të identifikimit, gjegjësisht gjatë login. - Anëtari klikon linkun për login, e plotëson fushën për email e pastaj atë për fjalëkalim, pastaj butonin për dërgimin e formularit. - Skripta për verifikim (në rastin tonë: check.php), i lexon të dhënat e dërguara me POST. - Merret email adresa dhe bëhet një SQL query për të verifikuar a ekziston ajo email adresë në tabelën e përdoruesve. - Nëse nuk ekziston jepet raporti se nuk ekziston dhe ofrohet mundësia që vizitori të shkojë prapë te skripta për login. - Nëse ekziston ai email, atëherë lexohen të gjitha të dhënat nga record-i i atij anëtari: ID-ja, emri dhe mbiemri, hashi i ruajtur gjatë regjistrimit, si dhe salt-i. - Merret fjalëkalimi i lexuar nga POST dhe i bashkangjitet salt-it të lexuar nga databaza, dhe në atë formë të bashkuar iu nxirret hash-i. - Krahasohet hashi i përfituar kështu me hashin që më parë u lexua nga databaza. - Nëse hashat nuk janë të njëjtë, dmth fjalëkalimi i shënuar në formular nuk ka qenë i saktë. Raportohet gabimi, jepet opsioni për të bërë login sërish. - Nëse hashat janë të njëjtë, atëherë konkludohet se fjalëkalimi i shënuar në formular është i njëjtë me atë që është ruajtur në databazë. Me këtë rast, në variablin e sesionit $_SESSION[‘auth’] vendoset vlera TRUE. - Gjithkund nëpër skripta ku lejohet aksesi vetëm anëtarëve bëhet verifikimi me:

1 if (isset($_SESSION['auth']) && $_SESSION['auth'])

Nëse plotësohet kushti, anëtari ka akses në atë faqe. Nëse jo, atij i raportohet se nuk ka privilegje për ta vizituar atë faqe, apo redirektohet në “Page Not Found”.

Në rastet kur një anëtar e harron fjalëkalimin e vet, nuk do të jetë e mundur që atij t’i dërgohet fjalëkalimi sepse, siç u cek më sipër, nga hash-i nuk mund të përfitohet teksti origjinal. Në këto situata, duhet të krijohet një fjalëkalim i ri që anëtarit i dërgohet në email në formë jo të kriptuar, ndërsa në databazë regjistrohet e kriptuar.

Dictionary attack

Hashi i kombinuar me salt është tërësisht joefektiv karshi dictionary attack!

Çka është “dictionary attack”?

Siç e pamë, salt i bën komplekse edhe fjalëkalimet e thjeshta, duke ia shtuar një varg numrash përpara apo prapa. Mirëpo, fjalëkalimet e enkriptuara me hash dhe salt janë të efektshme për mbrojtje nga ata që e kanë në dorë tabelën e anëtarëve nga databaza.

Ndërsa, dictionary attack bëhet asisoj që një skriptë e thirr direkt faqen ky lexohen ‘POST data’ (në aplikacionin tonë “check.php”), duke i dërguar fjalëkalime nga një tabelë e parapërgatitur e mijëra fjalëkalimeve të mundshme. Kjo mund të realizohet psh me funksionet e caktuara të PHP apo të ekstensionit cURL.

Për këtë është me rëndësi që të bllokohet qasja në skriptën që i lexon “POST data”, duke verifikuar nëse atë skriptë e ka thirrur ndonjë skriptë tjetër e aplikacionit tonë, apo kërkesa ka ardhur nga jashtë. Për këtë mund të përdoret superglobali $_SERVER[‘PHP_REFERER’]. Por, kjo variabël e lexon vlerën nga “HTTP requests”, e këto edhe mund të falsifikohen nga sulmuesi me eksperiencë.

Konkluzioni: Vetëm fjalëkalimet komplekse ofrojnë një nivel sigurie, por megjithatë - në Web kurrë nuk ka mjaft siguri.

Object Oriented Programming

Në PHP mund të programojmë në dy mënyra:

  • Programim procedural
  • Programim i bazuar në objekte

Shembujt që janë dhënë në libër, të gjitha kanë qenë të bazuara në programim procedural.

Çka nënkuptohet me “programim procedural”?

Ta marrim si shembull një faqe për paraqitjen e produkteve. Për ta shfaqur atë faqe duhen të kryhen disa veprime:

  • Konektimi me MySQL server
  • Dërgimi i SQL kërkesës
  • Ekzekutimi i SQL kërkesës
  • Pranimi i rezultateve
  • Analiza nëse ka apo s’ka rezultate
  • Nëse ka - formohet cikli i cili i lexon një nga një rreshtat me të dhëna të pranuara nga SQL tabela
  • Kryhen llogaritjet e nevojshme
  • Shfaqen rezultatet në shfletues
  • Mbyllet koneksioni me server

Këtu shohim se rreshtat programorë ekzekutohen njëri pas tjetrit në mënyrë sekuencionale, me një renditje të paracaktuar se çka vjen para e çka pas.

Një program i tillë mund të degëzohet dhe mund të ketë iteracione (përsëritje, cikle) të blloqeve të rreshtave programorë (programim procedural i strukturuar_. Sido që të jetë strukturuar kodi, ai gjithmonë e ndjek një sekuencë të paradefinuar të ekzekutimit të rreshtave programorë.

Mund të themi se janë tri konstrukte kryesore të programimit procedural:

  • Sekuenca. Ekzekutimi i urdhërave njëri pas tjetrit.
  • Degëzimi. Ndarja e rrjedhës së ekzekutimit në degë.
  • Iteracioni. Përsëritja e një blloku të rreshtave programorë.

E tërë logjika e programit bazohet në struktura të tilla dhe çfarëdo “shkëputja” nga një strukturë e tillë monolitike diku në ndonjë rresht, si pasojë do të sjellë “rrënimin” e programit - situata kur programi ekzekutohet ndryshe nga ajo që është planifikuar. Në situata të tilla programi ose ndalet sepse ka hasur në gabim, ose bën veprime të gabuara që rezultojnë në rezultate të gabuara në fund.

Gjatë ekzekutimit të një programi të tillë, gjithmonë është i qartë dallimi ndërmjet kodit dhe variablave. Variablat ruajnë vlera, kodi kryen një veprim.

Një stil i këtillë i programimit është i përshtatshëm atëherë kur kemi të bëjmë me ndonjë aplikacion të thjeshtë. Me rritjen e kompleksitetit të aplikacionit, programuesi do ta ketë gjithnjë e më vështirë ta mirëmbajë kodin, për arsye të ndryshme:

  • Shumë kod i pasistemuar në një vend,
  • Përdorimi i numri të madh të variablave, ku shumë prej tyre mund të kenë emra të njëjtë dhe me këtë, në mënyrë të pahetueshme - e ndryshojnë vlerën gjatë ekzekutimit të programit, gjë që e bën shumë të vështirë gjetjen e gabimeve,
  • Përsëritje e blloqeve të njëjta të kodit nëpër skripta të ndryshme
  • Përfshirja e një numri të madh të funksioneve, ku një pjesë e tyre as që përdoren brenda aplikacionit
  • Është vështirë të shtohen gjëra të reja në kod e të mos ndikohet pjesa tjetër e programit. Pra, çdo shtojcë e re mund të sjellë probleme të reja.
  • Është i vështirë ristrukturimi i kodit sepse nxjerrja e një blloku të kodit nga një vend dhe vendosja e tij në një vend tjetër, mund të sjellë probleme me rrjedhën e ekzekutimit të programit.
  • E bën një ndryshim qoftë edhe të vogël diku dhe pasojat mund të shkojnë në formë të efektit të ortekut teposhtë. Si shembull: një ndryshim i emrit apo vlerës së variablit, mund shkaktojë pasoja në rreshtat vijues, sepse i gjithë kodi vepron si një tërësi kompakte.

OOP është një metodologji që mbështetet në marrëdhëniet ndërmjet “objekteve”, duke mundësuar një nivel të abstraksionit gjatë përpilimit të programit. Nëse për shembull, aplikacioni ynë është një e-commerce site, me OOP do të izolohen në tërësi të veçanta objekti i blerësit, objekti i produktit, objekti i shportës, etj.

Brenda objektit të shportës do të ketë: - Veti (properties), dhe - Metoda

Vetia është një emërtim tjetër për variablat siç i kemi mësuar deri tash, ndërsa metoda është një emërtim tjetër për funksion. Të dy këto shprehje përdoren në kontekst të OOP.

Vetitë dhe metodat “koekzistojnë” brenda objektit të njëjtë, për dallim nga programimi procedural, ku variablat dhe funksionet ishin qartësisht të ndara. Në OOP, vlerat e variablave dhe funksionet përkatëse i shohim si një objekt të vetëm.

Objekti i shportës që u morr si shembull, mund të ketë këto veti (properties):

  • ID-në e produktit,
  • Çmimin e produktit,
  • Sasinë e produktit, etj.

Poashtu mund të ketë metoda:

  • Për llogaritjen e vlerës së produktit (sasia x çmimi),
  • Për llogaritjen e TVSH-së,
  • Për llogaritjen e uljes së çmimit për konsumatorë të caktuar, etj.

Krijimi i një klase

1 <?php
2 
3 class Produkti {
4 
5 }

Kjo klasë është plotësisht valide, por nuk kryen kurrfarë pune (për momentin).

Këtë klasë duhet ta ruajmë me emrin produkti.php. Pra, parimi i përgjitshëm është: çdo klasë ruhet si fajll i veçantë dhe emri i fajllit duhet t’i korrespondojë emrit të klasës. Kjo praktikohet për ta mundësuar inkludimin automatik të klasave të nevojshme brenda një skripte. Nëse flasim me terminologjinë e MVC, kjo do të mundësojë leximin automatik të klasave brenda kontrollerëve.

T’i deklarojmë disa variabla.

1 <?php
2 class Produkti {
3     public $sasia;
4     public $cmimi;
5     protected $tvsh = 16;
6     private $vlera = 0;
7 }

Me këtë kemi caktuar vizibilitetin e variablave (respektivisht të vetive. në terminologjinë e OOP).

Vetia publike (public), mund të lexohet dhe ndryshohet nga kontrolleri, pra mund të ndryshohet nga jashtë klasës. Si publike i zgjedhim ato veti të cilat janë vlera me të cilat ne e “furnizojmë” klasën për të kryer llogaritje të mëtejme.

Vetia e mbrojtur (protected) nuk është e dukshme jashtë klasës, pra kontrolleri ynë as nuk mund ta shohë, as ta ndryshojë. Të tilla e deklarojmë variablat të cilat përdoren brenda klasës për llogaritjen e vlerave të ndërmjetme, vlerat e të cilave nuk kanë ndonjë rëndësi për kontrollerin. Me deklarimin e vetisë si të mbrojtur, ne parandalojmë ndryshimin aksidental të atyre vlerave nga ana e kontrollerit. Vetitë e mbrojtura nuk janë plotësisht të izoluara: vlerat e tyre shihen nga nënklasat e një klase, pra nga “fëmijët” e saj.

Vetia private (private) është identike me atë të mbrojtur, me atë dallim që vlerat e vetisë private nuk “shihen” as nga nënklasat e asaj klase.

Nëse dëshirojmë që kontrolleri t’i shohë vlerat e vetive të mbrojtura dhe të atyre private, por pa mundësinë e ndryshimit të tyre, ne mund të ndërtojmë funksione (gjegjësisht metoda, sipas terminologjisë së OOP), detyrë e të cilave do të jetë që ta tregojnë vlerën e një vetie.

 1 <?php
 2 class Produkti {
 3     public $sasia;
 4     public $cmimi;
 5     protected $tvsh = 16;
 6     private $vlera = 0;
 7 
 8    public function tregoTVSH() {
 9    return $this->tvsh;
10   }
11 }

Tash, në kontrollerin tonë mund të shkruajmë kështu:

1 <?php
2 include("produkti.php");
3 
4 $p = new Produkti;
5 $p->sasia = 5;
6 echo $p->tregoTVSH();

Vetia sasia mund të ndryshohet nga kontrolleri, por edhe mund të lexohet. Mirëpo, në rastin e vetisë tvsh, ne nuk mund të shënojmë $p->tvsh=10, sepse tvsh është veti e mbrojtur, dhe si e tillë nuk mund të ndryshohet direkt nga jashtë klasës. Ajo as nuk mund të lexohet direkt me echo $p->tvsh, prapë, për shkak se është veti e mbrojtur.

Megjithatë, kontrolleri ynë ka qasje në vlerën e vetisë tvsh duke e përdorur metodën tregoTVSH():

1 echo $p->tregoTVSH();

që do të shfaqë në browser numrin 16.

Composer

Në kapitullin mbi PHP ekstensionet, pamë se çka janë ato dhe për çka përdoren.

Avantazh i ekstensioneve është shpejtësia e ekzekutimit të tyre, sepse ato janë të shkruara në C. Avantazh tjetër është se brenda skriptave tona nuk duhet të shtojmë asgjë për t’i “informuar” skriptat mbi ekzistencën e një ekstensioni.

Ndonëse ofrojnë shpejtësi, lehtësim të punës si dhe siguri, ekstensionet nuk janë gjithmonë zgjedhje ideale. Kjo për shkak se numri i ekstensioneve, për shkak të natyrës së tyre specifike (duhet të shkruhen në C), është i vogël.

Për fat të mirë, ekziston zgjidhja alternative: bibliotekat e funksioneve të shkruara në PHP. Këto nuk janë të shpejta si ekstensionet për vetë faktin se janë të shkruara në PHP. Pra, sikurse edhe kodi i skriptave tona, ashtu edhe kodi i këtyre bibliotekave me funksione, duhet të kalojnë për procesim në PHP server.

Avantazh i tyre është fleksibiliteti dhe numri i madh i bibliotekave në dispozicion.

  • Janë fleksibile sepse kemi mundësi të ndërhyjmë në kodin e tyre dhe të bëjmë përshtatjet e nevojshme. Te ekstensionet kjo është e pamundur sepse atje ruhen si fajlla binarë.
  • Numri i madh i bibliotekave siguron që do të mund të gjejmë zgjidhje për probleme të natyrave të ndryshme, nga manipulimet me tekste deri te kriptografia e avancuar.

PEAR

Një koleksion tejet i madh i bibliotekave quhet PEAR - PHP Extension and Application Repository (http://pear.php.net/).

E meta e PEAR është se ka shumë biblioteka tashmë të vjetra dhe të pasuportuara nga autorët.

Packagist

Kohëve të fundit, një rritje të madhe në popullaritet shënon Web packagist.org.

Aty mund të gjejmë një numër tejet të madh të pakove të gatshme, mjafton vetëm të bëjmë kërkim me emrin e pakos së dëshiruar.

Mënyra e marrjes së fajllave në packagist.org është specifike, nuk bëhet download direkt nga faqja e ndonjë zip fajlli, as nuk bëhet klonimi si në rastin e Github.

Composer

Për t’i shkarkuar fajllat e një pakoje, nevojitet përdorimi i programit Composer nga http://getcomposer.org .

Windows installer

Nën “Windows Installer” klikojmë te linku Composer-Setup.exe, me të cilin e shkarkojmë fajllin, të cilin fajll më tej e instalojmë.

Gjatë instalimit, do të kërkohet lokacioni ku gjendet fajlli php.exe, që në rastin e WAMP - do të jetë në c:\wamp\bin\php\php5.5.12\php.exe. Të përsërisim: numri i versionit mund të dallojë në kompjuterë të ndryshëm.

Pasi të kryhet instalimi në ktë mënyrë, e startojmë command prompt-in, ku shkruajë: composer. Nëse paraqitet dritarja me opsionet e Composer, instalimi ka qenë i suksesshëm.

Instalimi manual

Instalimi mund të kryhet edhe manualisht, duke e formuar një folder të ri diku në sistem, psh. c:\bin.

Futemi në command prompt, pastaj shënojmë komandën `cd c:\bin’. Këtu e japim komandën e re:

C:\bin>php -r "readfile('https://getcomposer.org/installer');" | php

Në këtë gjendje. ne do të mund ta përdorimin Composer-in vetëm duke e shkruar në command prompt derisa jemi brenda atij folderi, ose duhet ta shënojmë pathin para komandës:

1 cd c:\bin
2 php composer.phar

ose

1 cd c:\users\document\projekti
2 c:\bin\php composer.phar

Pas composer.phar e shënojmë ndonjërën prej komandave të tij, ku neve do të na interesojnë: install dhe update.

Këtu i shohim dy probleme:

  1. composer.phar nuk mund të startohet direkt nga cilido folder.
  2. për ta startuar duhet të shënohet mjaft kod në rreshin komandues

E para: Startojmë Control Panel -> Advanced System Settings -> Advanced -> Environment Variables

Te User variables zgjedhim opsionin PATH, pastaj Edit. Te Variable value, shkojmë në fund të rreshtit dhe shtojmë: ; c:\bin' .E hapim një command prompt të ri dhe testojmë nga cilido folder që nuk është brenda c:\bin, duke shkruar php composer.phar`. Nëse na dalin opcionet e composer, gjithçka është OK dhe kalojmë në hapin e dytë.

E dyta: duke qenë në command prompt, futemi në c:\bin.

1 cd c:\bin

e japim komandën:

1 C:\bin>echo @php "%~dp0composer.phar" %*>composer.bat

Në këtë mënyrë e krijuam një fajll të ri, të quajtur composer.bat të cilin mund ta startojmë thjesht me komandën composer. Pra, jo më php composer.phar, por vetëm composer.

Përdorimi i Composer

Pasi i zgjidhëm dy problemet e sipërcekura, tash kemi mundësi që ta startojmë Composerin nga cilido folder me komandën composer install, nëse dëshirojmë të instalojmë ndonjë pako, apo composer update kur dëshirojmë të bëhet përditësimi i një pakoje.

Të kthehemi te packagist.org. Atje thamë se nuk bëhet shkarkimi direkt i pakove. Në vend të linkut të pakove, atje mund ta marrim një rresht si ky:

1 "os/php-excel": "dev-master"

Ky rresht duhet t’i shtohet fajllit composer.json, i cili fajll duhet të jetë i vendosur brenda folderit ku do ta shkarkojmë pakon/pakot.

Nëse për shembull e kemi caktuar folderin c:\wamp\www\aplikacioni-yne\libraries si vend ku do t’i ruajmë pakot, atëherë composer.json do të vendoset pikërisht aty.

Fajlli duhet ta ketë këtë strukturë:

1 {
2     "require": {
3         "os/php-excel": "dev-master"
4     }
5 }

Nëse duam të përfshijmë më tepër pako, vetëm i shënojmë njëri nën tjetrin, duke i ndarë me presje.

Pas plotësimit të composer.json me të dhënat e marra nga packagist.org, e japim komandën:

1 composer install

Në këtë moment fillon shkarkimi i pakos duke e formuar strukturën përkatëse të fajllave, pra duke i krijuar folderët dhe duke i vendosur fajllat brenda tyre.

Kurdo që të kemi nevojë për t’i përditësuar pakot, e japim komandën:

1 composer update

Inkludimi i pakove në aplikacionin tonë

Pas instalimit të një pakoje, brenda folderit tonë libraries do të jetë formuar folderi vendor, që është folderi brenda të cilit do të vendosen pakot e autorëve të ndryshëm.

Brenda vendor do ta gjejmë folderin që përmban emrin e autorit, të firmës apo projektit që e ka krijuar atë pako.

Brenda folderit me emrin e “vendor”-it, do ta gjejmë folderin me emërtimin e pakos.

Krejt çka duhet të bëjmë në aplikacionin tonë është që ta bëjmë include apo require fajllin autoloade.php nga folderi vendor:

1 <?php
2 require 'vendor/autoload.php';

Në rastin e aplikacionit tonë, do të jetë:

1 <?php
2 require '..libraries/vendor/autoload.php';

Pas kësaj, mund t’i përdorim klasat e pakove të instaluara.

Nëse dëshirojmë t’i përditësojmë të gjitha pakot, hapim command prompt, vendosemi te folderi përkatëse (te ne është libraries), dhe shënojmë:

1 composer update

Nëse dëshirojmë ta përditësojmë vetëm një pako, e cekim emrin e tij:

1 composer update vendor/package

duke i zëvendësuar fjalët vendor dhe package me emërtimet përkatëse të pakos konkrete.

Për ta përditësuar vetë Composer-in, shënojmë:

1 composer self-update

AJAX

Deri para disa vitesh, JavaScript dhe PHP kanë qenë dy botëra plotësisht të ndara; nuk ka qenë i mundur komunikimi i drejtpërdrejtë i një JS skripte me një PHP skriptë.

JavaScript ekzekutohet në browser, ndërkohë që PHP ekzekutohet në Web server. Një lloj "interaksioni" mes tyre mund të sigurohej me anë të gjenerimit dinamik të JS skriptave në PHP. Megjithatë, mungonte mundësia e pranimit të të dhënave nga JavaScript.

Për shkak të këtij kufizimi, ka qenë e pamundur përcjellja e të dhënave dinamike në një Web faqe, në kohë reale. Si zgjidhje gjysmake dhe jo fort praktike ka qenë mundësia e rifreskimit të faqes ("refresh") nga JavaScript, brenda intervaleve të caktuara kohore, për shembull çdo 1 minutë. Kjo metodë pamundësonte çfarëdo interaksioni normal të vizitorit me faqen. meqenëse rifreskimi i faqes e ndërprente çdo veprim që mund të ishte duke bërë vizitori.

XMLHttpRequest

XMLHttpRequest është një JavaScript objekt i cili mundëson leximin e të dhënave nga një URL, pa pasur nevojë për rileximin e Web faqes. Viteve të fundit gjen zbatim në një numër shumë të madh të Web aplikacioneve, duke mundësuar ndërtimin e të ashtuquajturit Web 2.0.

Objekti XMLHttpRequest mundëson komunikimin asinkron ndërmjet browserit dhe serverit, me ç'rast browseri ka mundësi t'i dërgojë kërkesa serverit dhe nga ai të marrë përgjigje në formë të HTML, XML, JSON apo të ndonjë formati tjetër, dhe këto të dhëna t'i vendosë brenda HTML dokumentit, pa pasur nevojë për gjenerimin e faqes me "refresh". Kjo teknologji quhet AJAX.

Në HTML, kur e ndërtojmë një formular, e përdorimin atributin action të elementit form, me të cilin atribut e definojmë URL-në e skriptës e cila do t'i lexojë të dhënat e dërguara. Sa herë që klikohet butoni i tipit "submit", browseri i mbledh të dhënat e formularit dhe e dërgon një HTTP kërkesë serverit për URL-në e definuar në action, me ç'rast pra hapet një faqe tjetër.

SEO friendly URLs

Gjatë zhvillimit të aplikacioneve në PHP, URL-të dijnë të bëhen mjaft komplekse. Shembull:

1 http://localhost/test/index.php?cmd=produkti&id=3

Një URL e tillë do të rezultonte nëse aplikacioni ynë është ruajtur në C:\wamp\www\test.

Me anë të virtual hosts, mund ta eliminojmë fare emrin e folderit test, ashtu që linku i ri do të jetë:

1 http://localhost/index.php?cmd=produkti&id=3

Në vend të localhost mund të jetë ndonjë domain name, nëse aplikacionin e kemi vendosur në remote host, si psh:

1 http://www.onlineshop.com/index.php?cmd=produkti&id=3

Edhe pse u shkurtua pak, URL-ja ende është mjaft komplekse dhe e palexueshme.

Në aspektin e SEO (Search Engine Optimization), URL-të e formuara në mënyrë të këtillë kanë vlerë më të vogël se URL statike, si psh:

1 http://www.onlineshop.com/produkti/3

Për relizim të përkryer të SEO friendly URL, edhe numri në fund duhet të zëvendësohet me slug, pra me fjalë që përmban emërtimin e produktit, në këtë rast. Psh:

1 http://www.onlineshop.com/produkti/cokolade-milka

Por, kjo kërkon një procedurë më të ndërlikuar, që do të shqyrtohet në kapitujt e mëvonshëm.

Pjesa ?cmd=produkti&id=3 quhet query string, dhe më së miri është që në URL-të e aplikacionit tonë të mos figurojnë fare, nëse është e mundur.

mod_rewrite.so

Për të manipuluar me URL-të në mënyrë që atyre t’ua japim dukjen e URL-ve të faqeve statike, duhet të shërbehemi me modulin rewrite_module të Apache.

Këtë e arrijmë në WAMP, duke zgjedhur menunë:

1 Apache - Apache modules - rewrite_module

Kjo realizohet edhe duke e edituar httpd.conf, me heqjen e shenjës # para rreshtit:

1 #LoadModule rewrite_module modules/mod_rewrite.so

Pas heqjes së #, e ristartojmë serverin me Restart All Services në WAMP.

.htaccess

Për momentin, nuk ka ndonjë ndryshim në fuksionimin e aplikacionit tonë. Deri te ndryshimi do të vijmë duke e shtuar një fajll të quajtur .htaccess në folderin kryesor të aplikacionit tonë. Do të shërbehemi me këtë .htaccess, që është “huazuar” nga CodeIgniter:

1 RewriteEngine on
2 RewriteCond $1 !^(index\.php|favicon.ico|assets|robots\.txt)
3 RewriteRule ^(.*)$ /index.php/$1 [L] 

Kodi në rrestin 1 e aktivizon RewriteEngine.

Kodi në rreshtin 2 do të mundësojë që me rastin e thirrjes së cilitdo fajll në aplikacionin tonë, (përveç kur thirren: index.php, favicon.ico, robots.txt dhe folderit assets), Apache do ta thërrasë fajllin index.php.

Pra, nëse shkruajmë psh:

1 http://localhost/kontakti.php
2 
3 http://localhost/login
4 
5 http://localhost/produktet/produkti.php

në të gjitha rastet, Apache do ta servojë fajllin index.php.

Nëse kërkohet ndonjë fajll nga folderi /assets, atëherë do të ofrohet fajlli i kërkuar, e jo index.php. Psh.

1 http://localhost/assets/style.css

Me këtë do të lexohet përmbajtja e style.css drejtëpërsëdrejti.

Kodi në rreshti 3 do të mundësojë që çfarëdo që kemi shkruar në URL pas domainit, t’i bashkangjitet fajllit index.php, duke e ndarë me shenjën /. Pra, nëse kemi shkruar:

1 http://localhost/kontakti

kjo do të përkthehet si:

1 http://localhost/index.php/kontakti

Me këtë ndryshim, ende nuk kemi arritur asgjë, përveç asaj se tash nuk del raporti se faqja nuk është gjetur (Page not found).

Në strukturën e fajllave të aplikacionit tonë, askund nuk gjendet folderi /kontakti, dhe pa Rewrite të aktivizuar, Web serveri do të lajmëronte se ajo faqe/folder nuk ekziston.

Pra, edhepse kemi kërkuar “folderin” kontakti, neve na është ofruar fajlli index.php.

Prej këtu, ne duhet ta vendosim logjikën e analizës së URL-së në index.php në mënyrë që atje të vendoset çka të ndodhë kur vizitori e klikon një link të aplikacionit tonë.

Leximi nga superglobali $_SERVER

Superglobali $_SERVER përmbajnë variabla të ndryshme të cilat tregojnë parametra të ndryshëm dinamikë të serverit. Psh.:

  • Cili URL është kërkuar,
  • Cila skriptë është duke u ekzekutuar,
  • Prej cilit IP po qaset një vizitor, etj.

Të gjitha këto informata mund t’i marrim me funksionin phpinfo(). Neve na intereson vetëm kjo pjesë nga lista e phpinfo():

REQUEST_URI: /kontakti

SCRIPT_NAME: /index.php

PATH_INFO: /kontakti

E shohim se REQUEST_URL dhe PATH_INFO na ka dhënë informatën e njëjtë, por varësisht nga konfigurimi i serverit, mund dhe të mos e japin, gjegjësisht njëra mund të jetë e zbrazët fare. Prandaj, për të vendosur cilën do ta përdorim, së pari duhet të testohet serveri. Ne po e përdorim PATH_INFO. Kjo vlerë në aplikacion lexohet me:

1 $_SERVER['PATH_INFO']

URL-ja mund të ketë më tepër segmente, psh:

  • /produkti/1
  • /produkti/cokolade-milka
  • /produkti/cololade-milka/edit

Për këtë shkak, ne duhet fillimisht ta zbërthejmë URL-në (pjesën pas domainit), në segmente. Kjo arrihet me explode, funksion i cili e ndan një string duke u bazuar në ndonjë karakter të zgjedhur, dhe të gjitha fragmentet e stringut i ruan si elemente të vargut (array).

Në browser psh e shënojmë URL-në: http://kursi.app/produkti/edit/5. Pastaj e analizojmë në skriptë:

1 $url = explode("/", $_SERVER['PATH_INFO']);

Variabli $url është në fakt një varg njëdimensional, i cili ka aq elemente sa segmente ka URL-ja. Në rastin /produkti/cololade-milka/edit, var_dump() do të na i tregojë segmentet si vijon:

1 array (size=4)
2   0 => string '' (length=0)
3   1 => string 'produkti' (length=8)
4   2 => string 'edit' (length=4)
5   3 => string '5' (length=1)

E shohim se segmenti 1 jep fjalën “produkti”, segmenti 2 fjalën “edit” dhe në fund, segmenti 3 e jep numrin 5.

router

Duke u bazuar në vlerat e fituara, pra të segmenteve të URL-së, ne e ndërtojmë router-in e aplikacionit tonë.

Detyra e routerit do të jetë që t’i lexojë segmentet e URL-së, dhe në bazë të atyre të përcaktojë se cilën skriptë do ta ekzekutojë, gjegjësisht ta inkludojë në index.php.

Siç cekëm, $url[1] tregonte segmentin e parë, për neve më kryesorin të URL-së. Duke e shfrytëzuar atë, e ndërtojmë një router rudimentar.

 1 <?php
 2 $url = explode("/", $_SERVER['PATH_INFO']);
 3 if (!empty($url[1]))
 4 	{
 5 	$cmd = filter_var($url[1], FILTER_SANITIZE_STRING);
 6 	
 7 	switch ($cmd)
 8 		{
 9 			case 'login':
10 				require('login.php');
11 				break;
12 			case 'check':
13 				require('check.php');
14 				break;	
15 			case 'produkti':
16 				require('produkti.php');
17 				break;							
18 			case 'kontakti':
19 				require ('kontakti.php');
20 				break;
21 			case 'signup':
22 				require ('regjistrimi.php');
23 				break;							
24 			default:
25 				echo "<p>Nuk ekziston kjo faqe!</p>";
26 		}
27 	}
28 else
29 	{
30 	require("home.php");
31 	}
32 ?>

Nëse tash e shikojmë kronologjikisht si janë ndërtuar URL-të përgjatë kursit, do të shohim se si ato po bëhen gjithnjë e më kompakte dhe më të lexueshme:

1 http://localhost/test/index.php?cmd=produkti&id=3
2 http://localhost/index.php?cmd=produkti&id=3
3 http://kursi.app/index.php?cmd=produkti&id=3
4 http://kursi.app/?cmd=produkti&id=3
5 http://kursi.app/produkti/3

Për leximin e segmentit të dytë do të përdoret $url[2], për të tretin $url[3], e kështu me radhë.

Arkitektura MVC (Model-View-Controller)

Në fillet e veta. PHP nuk ka qenë asgjë më shumë se një templating engine - ka mundësuar futjen e të dhënave dinamike brenda një HTML dokumenti statik. Me të dhëna dinamike nënkuptohet të dhënat që llogariten aty për aty, si data, ora, numëruesi i vizitorëve, ndonjë rezultat i kalkuluar, etj.

PHP mund të intermiksohet me HTML fare lehtë: kudo në HTML dokument ku dëshirojmë të shfaqim të dhëna dinamike, e hapim bllokun e PHP me <?php, dhe kudo tjetër ku dëshirojmë të vazhdojmë me HTML, e mbyllim bllokun e PHP me ?>.

Mund të vendosim sa të duam “shkëputje” të tilla brenda një HTML dokumenti, serveri do të dijë cilën pjesë ta dërgojë ashtu si është, e cilën ta përpunojë para dërgimit, gjithnjë duke u bazuar në tag-at e PHP: <?php dhe ?>.

Nëse PHP na nevojitet brenda një numri të vogël të HTML dokumenteve dhe atë për veprime të thjeshta, kjo qasje do ta kryejë punën e vet pa kurrfarë problemi.

Por, cilido aplikacion më serioz do të përmbajë me dhjetëra apo qindra HTML dokumente, ku PHP nuk do të përdoret vetëm për ta llogaritur dhe shfaqur një të dhënë dinamike. Aplikacionet komplekse kërkojnë kod kompleks, për aspekte të ndryshme të një Web sajti:

  • Sistemin për autentifikim dhe autorizim
  • Komunikimin me sistemin për menaxhimin e databazave relacionale
  • Kryerjen e analizave të ndryshme për ta përcaktuar rrjedhën e ekzekutimit të programit (if, switch…)
  • Përsëritjen e blloqeve të rreshtave programorë (for, foreach, while, do…while)
  • Gjenerimin e fajllave të ndryshëm (tekst, JSON, HTML, XML, etj.)

e shumë e shumë veprime të tjera. Pra, një PHP aplikacion nuk është vetëm një “mjet” për paraqitjen e të dhënave dinamike në një Web faqe, por qenka një tërësi programesh të mirëfillta që zgjidhin probleme nga më të ndryshmet.

Nëse kodi i PHP do të intermiksohej me HTML kodin e faqes, do të ishte tejet e vështirë të mirëmbahej kodi sepse nuk është i lexueshëm, nëse shkruhet copa-copa në vende të ndryshme të HTML dokumentit.

Në rastet kur kemi skripta të tilla të përziera me HTML dhe PHP (e diku edhe me SQL), është e vështirë për të mos thënë e pamundshme puna ekipore e programuesve, dizajnuesve dhe administratorëve të databazave. Kjo sepse në të njëjtin fajll duhet të punojnë së paku 3 persona dhe atë me kusht që secili të jetë në gjendje ta kuptojë kodin e tjetrit. Sepse, nëse Web dizajneri nuk kupton mirë kodin në PHP, programuesi nuk e njeh aq mirë HTML-në, administratori i databazës mund të mos e njohë asnjërën, atëherë lehtë do të vijnë në situata që ta dëmtojnë kodin e njëri-tjetrit. Apo edhe thjeshtë që kodi i njërit mos ta ndjekë logjikën e kodit të tjetrit.

Problemet e cekura kanë sjellë nevojën për ndarjen e “kompetencave” në tërësi të veçanta, ku programuesi nuk ndërhyn në HTML, dizajneri merret vetëm me aspektin e dizajnit, ndërsa administratori merret me krijimin dhe optimizimin e kërkesave që i dërgohen SQL serverit.

Kështu, secili do ta japë maksimumin në fushën e vet, ndërkohë që është i siguruar që nuk do të ketë nevojë të ndërhyjë në kodin e kolegut tjetër.

MVC është paradigmë që implementohet në rastet kur dëshirojmë t’i separojmë 3 tërësitë kryesore të një aplikacioni:

  • Modelet (Models). Administratori i databazës.
  • Pamjet (Views). Web dizajnuesi, front-end developer.
  • Kontrollerët (Controllers). Programuesi, back-end developer.

Modelet

Modelet janë skripta/programe brenda të cilave do të vendoset kodi që merret me leximin dhe shkrimin e të dhënave në sistemin për menaxhimin e databazave. Modelet nuk janë të kufizuara vetëm në dërgimin e SQL query, por te aty mund të bëjmë edhe procesim të të dhënave, që pastaj rezultatet e fituara t’ia dërgojnë kontrollerit që e ka kërkuar atë “shërbim”.

Për shembull, jepet një SQL kërkesë, lexohen të dhënat e fituara, pastaj në bazë të atyre të dhënave ndërtohen SQL kërkesa të tjera për të ardhur deri te një rezultat përfundimtar. Rezultati përfundimtar i kthehet kontrollerit që e ka kërkuar atë.

Rëndomë, rezultati kthyes do të jetë:

  • Një rresht i lexuar nga databaza (varg njëdimensional), psh. të dhënat mbi një anëtar
  • Një tabelë e lexuar nga databaza (varg dydimensional), psh. lista e produkteve
  • Përgjigje në formën TRUE ose FALSE, që indikojnë nëse ekziston apo nuk ekziston ndonjë e dhënë
  • Numër, që tregon për shembull sa rreshta e plotësojnë një kriter

Është mirë që modelet të mos kenë “side-effects”, pra që vlerat e variablave të tyre të mos ndryshojnë vlerat e variablave me emra të njëjtë në pjesët tjera të aplikacionit. Kjo për shkak se flasim për 3 developerë që punojnë njëkohësisht, secili në pjesën e vetë të punës. Nëse ndonjëri do të përdorte variabël me emërtim të njëjtë si tjetri, psh. edhe autori i modelit edhe ai i kontrollerit, secili për nevoja të programit të vet përdor variablin $rezultati, mund të ndodhë që diku në rrjedhën e ekzekutimit të programit, vlera e njërës ta zëvendësojë vlerën e tjetrës dhe me këtë të ndodhin situata ku është vështirë të gjendet ku është gabimi.

Për këtë arsye, modelet dhe kontrollerët organizohen në formë të funksioneve, apo edhe më mirë: në formë të klasave. Me këtë sigurohet që variablat e funksioneve të ndryshme (apo metodat e klasave të ndryshme), të koekzistojnë të pavarura, pa marrë parasysh emrin e njëjtë.

Autori i modelit mund ta ketë përdorur një variabël me emrin $rezultati, por vlera kthyese e funksionit (apo metodës) së tij do të kthejë një rresht, apo një tabelë të lexuar nga tabelat e databazës, kështu që vlera e variablave lokale nuk do t’i ekpozohen kontrollerit fare, dhe në këtë mënyrë nuk do të ketë kolizion ndërmjet tyre, pra edhe në rastet kur në kontroller ekziston një variabël me emër të njëjtë.

Pamjet

Pamjet (views) janë fajlla të shkruara kryesisht në HTML/CSS/JavaScript. Ato nuk përmbajnë ose fare pak përmbajnë PHP kod. PHP do të përdoret nëse nevojitet ndonjë logjikë më komplekse, përtej asaj të vetëm paraqitjes së rezultateve. Kryesisht do të përdoren strukturat për degëzim (if) dhe ato për iteracion (while).

Nëse kemi përdorur ndonjë templating engine si Twig apo Smarty, nuk do të kemi fare nevojë për PHP, sepse ato posedojnë pseudo-tagat e vet për degëzim dhe iteracion.

Kontrollerët

Ndonëse të numëruar në fund, për nga rëndësia janë të parët. E tërë logjika e aplikacionit vendoset brenda kontrollerëve. Këtu bëjnë pjesë veprime të shumta:

  • Leximi dhe analizimi i URL-së (router)
  • Leximi i komponentëve të ndryshëm të aplikacionit, varësisht se çka kërkohet në kontrollerin e caktuar. Njëri kontroller mund të kërkojë dërgimin e emailave, tjetri gjenerimin e JSON fajllave, tjetri leximin e një Excel tabele, etj.
  • Dërgimi i kërkesët modelit për të dhëna nga databaza
  • Analiza e rezultateve të kthyera nga modeli, për procesim të mëtejmë
  • Kryerja e llogaritjeve të ndryshme
  • Dërgimi i të dhënave finale në një template (view), etj.

Pra, mund ta imagjinojmë kontrollerin në pozitë qendrore, prej nga fillon ekzekutimi i aplikacionit tonë. Sa herë do të ketë nevojë për të dhëna nga databaza, kontrolleri do t’i dërgojë kërkesë modelit. Sa herë që kontrolleri do të duhet t’i tregojë rezultatet e një procesi, ato të dhëna do t’ia dërgojë një template-je.

MVC, si plan arkitektural i një aplikacioni, mund të ketë edhe ndërlidhje tjetër ndërmjet komponenteve. Dikush praktikon që relacioni të jetë Controller - Model - View, pra që të dhënat e manipuluar nga kontrolleri të dërgohen në model, e pastaj modeli t’i dërgojë në pamje. Pra, kufiri nuk është tepër strikt dhe mund të aplikohen qasje të ndryshme.

Në PHP framework si CodeIgniter dhe Laravel, praktikohet skema e spjeguar në tekst, pra kontrolleri e përmban logjikën e aplikacionit, modeli të dhënat, ndërsa pamja është hapi i fundit, që bën vetëm paraqitjen e rezultateve në browser, me aspak ose fare pak procesim.

MVC pattern
MVC pattern

Një implementim i thjeshtë i patternit MVC.

Kontrolleri

produkti.php

 1 <?php
 2 include("modelet.php");
 3 include("func.php");
 4 
 5 $id = (isset($_GET['id'])) ? intval($_GET['id']) : 0;
 6 $result = getproduct($id);
 7 $data = array();
 8 if (!empty($result))
 9    {
10    $data['cmimi'] = $result['cmimi'];
11    $data['tvsh'] = $data['cmimi']*16/100;
12    $data['cmimitvsh'] = $data['cmimi']+$data['tvsh'];
13    $data['emri'] = $result['emri'];
14    $data['kodi'] = $result['kodi'];
15    template("produkti", $data); 
16    }
17 else
18    {
19    $data['raporti'] = "Nuk ekziston ky produkt";
20    template("raporti", $data); 
21    }

Modeli

modelet.php

 1 <?php
 2 
 3 $dbcon = @mysqli_connect("localhost", "root", "", "ickshop") OR die("Nuk munda te ko\
 4 nektohem. Gabimi: ".mysqli_connect_error());
 5 mysqli_set_charset($dbcon, 'utf8');
 6 
 7 function getproduct($n) {
 8 	global $dbcon;
 9 	$sql = "SELECT * FROM products WHERE Pro_ID = '$n' LIMIT 1";
10 	$rez = mysqli_query($dbcon, $sql);
11 	if (mysqli_num_rows($rez))
12 		{
13 		while($row = mysqli_fetch_array($rez, MYSQLI_ASSOC))
14 			{
15 			$rreshti = array(
16 			'kodi' => $row['Pro_Code'],
17 			'emri' => $row['Pro_Name'],	
18 			'cmimi' => $row['Pro_Price']);	
19 			}
20 		return $rreshti;
21 		}
22 	else
23 		{
24 		return NULL;	
25 		}
26 }

Funksioni për thirrjen e View

func.php

1 <?php
2 function template($view, $data)
3 	{
4 	extract($data);
5 	include($view."_view.php");		
6 	}

Views

produkti_view.php

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>Untitled Document</title>
 6 </head>
 7 
 8 <body>
 9 <div>
10 <p>Emertimi i produktit: <?=$emri; ?></p>
11 <p>Kodi i produktit: <?=$kodi; ?></p>
12 <p>Cmimi pa TVSH: <?=$cmimi; ?></p>
13 <p>TVSH: <?=$tvsh; ?></p>
14 <p>Cmimi me TVSH: <?=$cmimitvsh; ?></p>
15 </div>
16 </body>
17 </html>

raporti_view.php

 1 <!doctype html>
 2 <html>
 3 <head>
 4 <meta charset="utf-8">
 5 <title>Untitled Document</title>
 6 </head>
 7 
 8 <body>
 9 <div>
10 <?=$raporti; ?>
11 </div>
12 </body>
13 </html>

Kontrollerin e startojmë me: produkti.php?id=2, nëse dëshirojmë t’i shohim detajet e produktit të dytë.

Siç shohim, gjithçka fillon nga kontrolleri, pastaj të dhënat nxirren nga modeli, që pas përpunimit (llogaritjes së TVSH), dërgohen në produkti_view.php kur kemi rezultate, ose në raporti_view.php kur nuk kemi rezultate.

Template Engine - Twig

Në kapitullin për MVC, sqaruam arsyet përse një aplikacion ndahet në tri njësi. Një ndër njësitë ishte View, ku vendoseshin faqet e punuara në HTML/PHP, të cilat janë fokusi i një front-end developeri.

Nëse nuk shërbejemi me ndonjë mjet ndihmës, siç është Twig, faqet që shërbejnë si view, pra shabllonet e faqeve, domosdo do të kenë në vete të ndëthurrur edhe kod në PHP. Kjo e kufizon front-end developerin në shprehjen e kreativitetit të tij, sepse ai përveç HTML/CSS/JavaScript, duhet ta njohë edhe PHP-në.

Veç kësaj, HTML dokumentet e ndërthurrura me PHP janë më të vështira për lexim dhe kryerjen e korigjimeve eventuale.

Template engine janë aplikacione që mund t’i shtohen si komponentë aplikacionit tonë, nëpërmes të cilave do t’i gjenerojmë HTML dokumentet, asisoj që brenda tyre nuk do të ketë asgjë nga PHP. Megjithatë, HTML dokumentet ende do të jenë në gjendje t’i shohin vlerat e variablave të dërguara nga kontrolleri, si dhe të zbatojnë disa nga strukturat, siç janë ato për degëzim dhe iteracion. Kjo bëhet duke e shfrytëzuar sintaksën e template engine, e cila shpesh është më koncize dhe më gjithpërfshirëse se ato të PHP.

Instalimi i Twig

Twig instalohet me Composer, ku në composer.json e shtojmë rreshtin:

1 "twig/twig": "1.18.0"

Fajlli composer.json do të duket kështu:

1 {
2    "require": {
3    "swiftmailer/swiftmailer": "5.3.*@dev",
4    "twig/twig": "1.18.0"
5     } 
6 }

E zgjedhim versionin më të fundit që e kemi në dispozicion.

Pas dhënies së komandës composer update, brenda folderit të zgjedhur do të shkarkohet Twig dhe do të vendoset në folderin vendor/twig/twig.

Composer do ta përditësojë edhe fajllin autoload.php që gjendet brenda folderit vendor. Me këtë mundësohet përfshirja e Twig në mënyrë automatike në aplikacionin tonë, sa herë të paraqitet nevoja për të. Thënë më saktë, do të lexohen në mënyrë automatike klasat e Twig, sa herë që kërkohet krijimi i ndonjë objekti të tij. Mjafton që në index.php ta kemi rreshtin i cili e inkludon fajllin autoload.php:

1 require("../app/libraries/vendor/autoload.php");

Shtegu deri te folderi “vendor” mund të jetë ndryshe, varësisht nga mënyra e organizimit të fajllave nga ana e programuesit.

Inicializimi i Twig

Inicializimi i Twig bëhet me:

1 Twig_Autoloader::register();
2 $loader = new Twig_Loader_Filesystem('../app/views');
3 $twig = new Twig_Environment($loader, array(
4     'cache' => '../app/cache', 
5     'debug' => true
6 ));

Si argument për klasën Twig_Loader_Filesystem jepet lokacioni ku do t’i ruajmë shabllonet e faqeve tona, dhe në shembullin konkret është folderi views, brenda folderit app.

Klasa Twig_Environment mund të ketë disa variabla, e në shembullin tonë i kemi përdorur dy:

  • cache. Këtu shënohet lokacioni i Cache, folder ku do të ruhen kopjet e faqeve të gjeneruara me Twig. Kjo do ta përshpejtojë në masë të madhe përgjigjen e serverit ndaj kërkesave nga browserët, sepse këto janë kopje statike të faqeve dinamike, dhe si të tilla nuk kanë nevojë të gjenerohen pas secilës kërkesë nga browseri. Pra, herën e parë kur thirret një faqe, ajo gjenerohet në mënyrë dinamike, ruhet kopja e saj në cache dhe pastaj i dërgohet browserit. Kur të vijë kërkesa e dytë për të njëjtën faqe, Twig do ta kthejë kopjen e ruajtur. Fajllat në cache ripërtrihen brenda intervaleve të caktuara, por mund edhe të rigjenerohen nëse e fshijmë përmbajtjen e folderit cache. Këtë folder e krijojmë në lokacionin e dëshiruar dhe i japim write permissions.
  • debug. Nëse e vemë vlerën TRUE, Twig do të raportojë mbi gabimet eventuale nëpër faqe. Kjo është e dobishme gjatë fazës së zhvillimit dhe testimit, ndërsa kur sajtin e vendosim në serverin e produksionit, e kthejmë vlerën në FALSE.

Përdorimi i Twig nëpër kontrollerë

Siç kemi cekur edhe herë të tjera, brenda kontrollerëve e kemi të ndaluar të bëjmë echo sepse përmbajtja e tekstit do të shfaqet para se të thirret template. Vetëm imagjinoni kontrollerin si diç që rri sipër, ndërsa view është poshtë. Çfarëdo echo mbi view, do ta paraqesë tekstin para tagut <!DOCTYPE html> të templates! Prandaj, në vend se t’i bëjmë menjëherë echo, ne duhet t’i ruajmë tekstet, vlerat, numrat… në ndonjë variabël, të cilin pastaj do ta “printojë” template. Pra, echo-t do të bëhen ekskluzivisht nga template-t, për ta parandaluar shfaqjen e tekstit para HTML-së.

Nëse kemi më tepër variabla për t’ia dërguar Twig-ut, atëherë e formojmë një varg (array), brenda të cilit do t’i vendosim vlerat që duam t’i çojmë në template.

Për shembull:

1 $data['titulli'] = "Faqja e produkteve";
2 $data['cmimi'] = 125.00;

Arsyeja e dytë për përdorimin e variablave në vend të echo, ka të bëjë me procesin e dërgimit të HTTP headers, si në rastin kur e përdorim funksionin header(). Nëse për shembull, dëshirojmë ta bëjmë një redirektim me header("Location: /kontakti");, ky redirektim do të dështojë nëse funksionit header() i ka paraprirë ndonjë echo. Në këtë rast do të shfaqet raporti: “Warning: Cannot modify header information - headers already sent…” dhe nuk bëhet redirektimi.

Ky problem shfaqet edhe kur i përdorim sesionet në kombinim me header(). Nëse për shembull i kemi dhënë ndonjë vlerë një variabli të sesionit, si:

1 $_SESSION['auth'] = TRUE;

ajo vlerë nuk do të ruhet në fajllin/tabelën e sesioneve, derisa nuk dërgohet një HTTP request i ri. HTTP request dërgojmë kur klikojmë në një link (metoda GET), kur dërgojmë një formular (metoda POST), apo kur e dërgojmë HTTP requestin me header(), si në rastin header("Location: /kontakti");. Nëse skripta jonë i jep vlerë një variablit të sesionit dhe pastaj me header() redirekton në një skriptë tjetër, vlera e variablit të sesionit do të ruhet. Por, nëse para thirrjes së header(), ekzekutohet echo, procesi do të ndalet aty - nuk do të ketë ruajtje të vlerës së sesionit, as nuk do të ketë redirektim. E gjithë çfarë u përmend për sesionet, vlen edhe për cookie-t. Edhe cookie-t e ruajnë vlerën, tek pasi të është dërguar HTTP requesti i radhës, përndryshe vlera e tyre mbetet e pandryshuar.

Thirrja e një template/view

Pasi t’i kemi vendosur vlerat e variablave brenda një vargu, e thërrasim template me:

1 echo $twig->render('raporti.html', $data);

Ku si argument i parë është emri i view, ndërsa i dyti është vargu që i përmban vlerat që duam t’i bartim atje. Në këtë shembull, bëhet thirrja e view të quajtur ‘raporti.html’.

Struktura e një view fajlli

View fajllat, gjegjësisht template-t/shabllonet përdorim një “markup” që është specifik për Twig.

Ato fillojnë me:

1 {% extends "layout.html" %}
2 {% block content %}

ndërsa mbarojnë me:

1 {% endblock %}

Ndërmjet {% block content %} dhe {% endblock %} vendoset i gjithë HTML kodi i faqes. Po çka specifikon rreshti i parë me: {% extends "layout.html" %}?

Me extends tregohet se kjo faqe është “fëmijë” i një faqeje kryesore, të quajtur “layout.html”, apo sido që ta kemi quajtur atë.

Faqja “layout.html” është faqja kryesore, e cila do ta përmbajë seksionet kryesore të faqes:

  • DOCTYPE
  • HEAD
  • BODY

Brenda kësaj faqeje i bëjmë të gjitha linkimet me CSS fajlla, me JavaScript, e vendosim header-in, navigacionin, footer-in, sidebar-in, div-in për përmbajtjen specifike të faqeve, etj.

Te div-i ku duhet të vendoset përmbajtja specifike e faqeve, vendoset kodi:

1 {% block content %}{% endblock %}

Ta rishqyrtojmë ndryshe:

  • E formojmë faqen “layout.html” dhe aty vendosim gjithë HTML/CSS/JavaScript kodin e nevojshëm të faqes. Pra, të gjitha elementet e përbashkëta për të gjitha faqet.
  • Në div-in ku do të futet përmbajtja e nënfaqeve, e vendosim kodin {% block content %}{% endblock %}
  • Çdo nënfaqe do të fillojë me {% extends "layout.html" %}{% block content %}, ndërsa do të mbarojë me {% endblock %}. Ndërmjet tyre futet përmbajtja specifike e asaj faqeje. Për shembull, te faqja e kontakeve do t’i vendosim të dhënat kontaktuese.
  • Nënfaqet duhet ta përmbajnë vetëm HTML kodin që e përshkruan atë nënfaqe, por nuk duhet të përmbajë DOCTYPE, HEAD, BODY… Pra, në nënfaqen për login, ne do ta vendosim vetëm HTML-në për ndërtimin e formularit, por nuk do të kemi nevojë për strukturën komplete të një faqeje.
  • Kur me echo $twig->render('raporti.html', $data) thirret një nënfaqe, Twig do ta bashkangjesë automatikisht atë nënfaqe faqes “layout.html” dhe ashtu të bashkuar do t’ia dërgojë browserit. Pra, vizitori gjithmonë do ta shohë faqen si një tërësi, e jo në dy pjesë, siç i kemi struktuaruar ne gjatë procesit të zhvillimit të aplikacionit.

Variablat në Twig

Për “shtypjen” e vlerës së një variabli, përdoren shenjat {{ var }}. Ky është ekuivalent me <?=$var;?> ose <?php echo $var; ?> në PHP.

Pra, nëse në kontroller e kemi ruajtur vlerën e një variabli si $data[‘tvsh’] = 16, atë do të mund ta “printojmë” me Twig duke shënuar {{ tvsh }}.

Twig, çdo vlere që dërgohet në view i bën escape, që dmth i largon të gjitha HTML tag-at. Kjo bëhet për shkaqe sigurie, për ta eliminuar rrezikun nga XSS (cross-site scriptin). Megjithatë, nëse ndonjë variable nuk dëshirojmë t’ia heqim tag-at, e përdorim opsionin raw.

1 {{ tabela | raw }}

Variabli tabela është dërguar nga kontrolleri si një HTML tabelë që përmban <table>, <tr>, <th>, <td>. Nëse e përdorim në view pa opsionin raw, ajo nuk do të shfaqet si tabelë, por do të jetë tekst i thjeshtë.

Strukturat në Twig

if

1 {% if auth %}
2 <input class="btn btn-success" type="submit" value="Bleje">
3 {% endif %}

if…else

1 {% if auth %}
2 	<li><a href='/shporta'>Shporta</a>		
3 	<li><a href='/profili'>Profili</a>
4 	<li><a href='/logout'>Logout</a>
5 {% else %}
6 	<li><a href='/login'>Login</a></li>
7 	<li><a href='/signup'>Regjistrohu</a></li>
8 {% endif %}

for

1 {% for o in OS %}
2 <option value='{{ o.id }}'>{{ o.emri }}</option>
3 {% endfor %}

set

1 {% set a = 5 %}

Virtual hosts

Ata të cilët e kanë të instaluar WAMP-in, e dijnë se fajllat e një PHP aplikacioni ruhen në C:\wamp\www, të cilave u qasemi nga browseri me URL-në:

1 http://localhost

Nëse kemi më tepër se një projekt, atëherë mund të krijojmë direktoriume brenda atij www, të cilave u qasemi me:

1 http://locahost/projekti1
2 
3 http://locahost/projekti2
4 
5 http://locahost/projekti3

Natyrisht, në vend të “projekti1” mund të shënohet çfarëdo emërtimi i përshtatshëm për atë projekt, psh “onlineshop”.

Gjersa kjo zgjidhje është e lehtë për t’u kuptuar, ajo në vete përmban disa probleme. Problemi kryesor është çështja e linqeve absolute të shënuara brenda HTML/PHP fajllave të aplikacionit, të cilat mund të mos përputhen me strukturën në remote host.

Ta zëmë se faqja gjendet në www/projekti1 dhe duhet të linkohet me një CSS fajll që gjendet në /assets brenda direktoriumit projekti1.

1 <link href="/assets/style.css" rel="stylesheet" type="text/css">

Nëse në një faqe (psh index.php) e linkojmë CSS fajllin në këtë mënyrë dhe e testojmë në browser, do të shohim se faqja nuk do ta gjejë fare CSS fajllin. Kjo ndodh për shkak se shenja / nuk e tregon direktoriumin ku gjendet aplikacioni ynë, por i referohet direktorumit www, pra atje te www kërkohet /assets/style.css, por nuk gjendet atje. Shtegu i saktë është /projekti1/assets/style.css!

1 <link href="/projekti1/assets/style.css" rel="stylesheet" type="text/css">

Zgjidhja duket triviale: shtojmë /projekti1 para çdo linku absolut. Por kjo nuk është zgjidhje e mirë, sepse kur do të dëshirojmë ta sinkronizojmë sajtin tonë me remote host, do të kemi probleme me dead links, sepse atje ne i vendosim në

public_html

për qasje direkte të vizitorëve, pra jo

1 public_html/projekti1

Pra, nëse problemin e zgjidh në local host, më del problem në remote host. Në remote host do të funksiononte linku nëse do të shënohej si

1 /assets/style.css 

ndërsa në atë lokal nëse shënohet si

1 /projekti1/assets/style.css

Na duhet që direktoriumi ku gjendet aplikacioni ynë të trajtohet si sajt në vete, e jo si direktorium i www.

Me këtë do të na lehtësohej puna gjatë zhvillimit dhe testimit të aplikacionit, sepse nuk do të hasnim në probleme siç u cek më sipër. Shumë më mirë është nëse aplikacionin e mbaj në ndonjë folder të veçantë dhe të mos ketë lidhje me www, sepse një fshirje aksidentale e www do t’i fshinte të gjithë folderët e projekteve të tjera që i kemi.

Për ta realizuar këtë “izolim” të folderit si sajt në vete dhe për ta thirrur nga një domain i veçantë fiktiv, duhet t’u kryejmë disa veprime, disa në kuadër të Windows e disa në kuadër të serverit Apache.

Shumë më bukur do të ishte sikur aplikacionin ta startonim me :

1 http://kursi.app

se sa me

1 http://localhost/kursi

Së pari ta caktojmë një “domain” për aplikacionin tonë.

Hapim me një edit teksti fajllin (si Administator):

1 C:\Windows\System32\drivers\etc\hosts

Në fund të fajllit shtojmë:

1 127.0.0.1 kursi.app

E ruajmë fajllin me “Save”. Nëse nuk kreni privilegje të Administratorit, nuk do të jeni në gjendje ta ruani fajllin me ndryshime.

Vërejtje: Duhet të keni parasysh se në vend të .app mund të përdorni çfarëdo “Top Level Domain” tjetër (.dev, .test, etj), por edhe .com, .net, etj. Preferohen këto fiktivet sepse nëse përdorim ndonjë TLD real, do ta bllokojmë qasjen te ai domain nëse vërtet ekziston. Psh. nëse në Web ekziston kursi.com, e ne e kemi zgjedhur pikërisht këtë në hosts, ne efektifisht e kemi bllokuar qasjen në sajtin e vërtetë kursi.com.

Me shtimin e rreshtit në hosts mundësuam që duke shënuar http://kursi.app në browser të hapet http://localhost

Kjo nuk mjafton, sepse neve na duhet përmbajtja e http://localhost/kursi

Tash duhet ta konfigurojmë Web serverin Apache, ku do të krijojmë një host virtual.

Fillimisht i qasemi fajllit httpd.conf në:

1 C:\wamp\bin\apache\apache2.4.9\conf

Në vend të numrit 2.4.9, mund të jetë ndonjë numër tjetër i versionit të Apache, varësisht cilin version e keni instaluar në sistemin tuaj.

Në fajallin httpd.conf e kërkojmë rreshtin me këtë përmbajtje (rreshti 512 në versionin e përdorur në këtë shembull):

1 # Virtual hosts
2 # Include conf/extra/httpd-vhosts.conf

Shenja # është shenjë e komentimit, që do të thotë se nëse një rreshti i paraprin kjo shenjë, ai rresht do të injorohet. Prandaj, ne e fshijmë shenjën #.

1 # Virtual hosts
2 Include conf/extra/httpd-vhosts.conf

Të njëjtin veprim mund ta realizojmë edhe nga WAMP, duke shkuar në menunë Apache - Apache modules dhe duke selektuar opsionin

1 vhost_alias_module

Në vazhdim, shkojmë në:

1 C:\wamp\bin\apache\apache2.4.9\conf\extra

dhe me një editor teksti, e hapim fajllin httpd-vhosts.conf.

Në fund të fajllit e shtojmë këtë kod:

1 <VirtualHost *:80>
2 ServerAdmin emailadresa@juaj.com
3 DocumentRoot "C:\wamp\www\kursi"
4 ServerName kursi.app
5 </VirtualHost>

Nëse e kemi konfiguruar WAMP-in ta përdorë ndonjë port tjetër, psh 8000, atëherë shënojmë:

1 <VirtualHost *:8000>

Te ServerAdmin e shënojmë email adresën e administratorit të serverit, në këtë rast emailin vetanak.

Në DocumentRoot e shënojmë direktoriumin ku e kemi vendosur aplikacionin.

Vërejtje: Nuk është e thënë që projektet tuaja t’i vendosni pikërisht brenda C:\wamp\www. Mund ta përdorni cilindo direktorium në sistem, mjafton ta vendosni shtegun (path) në DocumentRoot.

Në ServerName e shënojmë “domainin” që e kemi zgjedhur në C:WindowsSystem32\drivers\etc\hosts, në rastin konkret kursi.app.

Shkojmë në panelin e WAMP-it dhe zgjedhim “Restart All Services”.

Pas kësaj, kur në browser e hapim adresën *http://kursi.app, do të hapet përmbajtja e aplikacionit që gjendet në C:\wamp\www\kursi.

Për çdo folder/direktorium të ri që dëshirojmë t’i qasemi në këtë mënyrë, duhet ta përsërisim procedurën e njëjtë - duke e shtuar “domainin” në C:WindowsSystem32\drivers\etc\hosts, e pastaj duke e krijuar hostin virtual për atë “domain” në C:\wamp\bin\apache\apache2.4.9\conf\extra\httpd-vhosts.conf.

Aspekte të sigurisë në PHP

Çdo zhvillues i Web aplikacioneve gjithmonë duhet të niset nga fakti se Web-i është ambient i pasigurtë dhe se çdo input nga cilido burim - është “i ndotur”. Nuk ka rëndësi nëse inputi ka ardhur nga HTTP requests (GET, POST, COOKIE…), apo është lexuar nga databaza apo nga ndonjë fajll. Në të gjitha rrethanat, inputi fillimisht duhet të “pastrohet” (sanitize) dhe të validohet (validate), para se të procedohet më tutje me të.

error_reporting

display_errors, log_errors, error_log

$_REQUEST - Rasti 1

Një ndër masat parandaluese më bazike është verifikimi i burimit të inputit. Nuk bën të procedojmë me një input nëse ai vie nga një burim tjetër nga ai që ne presim. E ilustrojmë këtë me rastin e superglobalit $_REQUEST.

Superglobali $_REQUEST është një varg (array) që në vete përmban vlerat e dërguara me $_POST, $_GET dhe $_COOKIE. Nëse e kemi një formular si vijon:

login.php

1 <form action='test.php' method="post">
2   <label for="fjalekalimi">Fjalekalimi:</label>
3   <input type="password" name="fjalekalimi">
4   <input type="submit" value="Submit">
5 </form> 

shohim se metoda e dërgimit është POST dhe se dërgohet fusha e quajtur “fjalekalimi”.

Skripta që e lexon këtë vlerë, mund të duket kështu:

check.php

1 <?php
2 $f = $_POST['fjalekalimi'];
3 ?>

Por, në dukje efekti i njëjtë arrihet edhe me:

1 <?php
2 $f = $_REQUEST['fjalekalimi'];
3 ?>

Ku qëndron dallimi? Dallimi ndërmjet versionit të parë të check.php dhe atij të dytë qëndron në faktin se të dytës mund t’i japim vlerë edhe me GET, gjegjësisht me URL, pa pasur nevojë ta plotësojmë formularin në login.php:

1 http://domaini.com/check.php?fjalekalimi=abc5847

Tash, sulmuesi e ka shumë më lehtë të provojë kombinime të ndryshme të fjalëkalimit, vetëm duke i ndryshuar vlerat në URL. Apo, nëse është në pyetje çmimi i një produkti - të manipulojë me të në URL.

$_REQUEST - Rasti 2

Në të njëjtën mënyrë mund të manipulohet edhe me përmbajtjen e cookie, nëse nuk e kemi konfiguruar PHP-në si duhet. Nëse në php.ini, në direktivën request_order e shënojmë vlerën “CGP” (COOKIE-GET-POST), e jo “GPC” (GET-POST-COOKIE) si është vlera standarde, është i mundur ekzekutimi i skriptave vijuese.

test.php

1 <?php
2 setcookie("anetari", "123456", time() + 3600);
3 header("Location: test2.php");
4 ?>

test2.php

1 <?php
2 $s = $_REQUEST['anetari'];
3 echo $s;
4 ?>

Me një link si ky:

1 http://domaini.com/test.php

që redirekton në test2.php, ku më pas e shtojmë një query string: ?anetari=54847

1 http://domaini.com/test2.php?anetari=54847

do të ndryshohet vlera e asaj që është dashur të lexohet nga cookie me emrin “anetari”, vlerë kjo që më pas do të regjistrohet në databazë, apo do të përdoret për procesim të mëtutjeshëm. Kjo për shkak se i kemi dhënë prioritet metodës GET në krahasim me atë COOKIE për leximin e vlerës së variablit me emër të njëjtë.

Por, është i mundur edhe skenari i dytë, që s’ka lidhje me request_order: që skripta test2.php të thirret direkt, me URL-në e cekur më sipër, por tash në incognito mode apo në browser tjetër, apo duke i fshirë paraprakisht cookies në browserin që jemi duke e përdorur.

Në këtë situatë, skripta “nuk është në dijeni” se vlera e kërkuar duhet të kërkohet në cookie sepse ai cookie as që ekziston, prandaj me $_REQUEST e lexon vlerën e pranuar me GET, pra nga URL-ja. Kështu, me një manipulim të thjeshtë në URL, do të jemi në gjendje ta ndryshojmë vlerën e cookie, me pasoja në rrjedhën e mëtutjeshme të programit, varësisht prej asaj se për çka do të përdoret ajo vlerë.


Vërejtje: Në konfigurimet e reja të PHP, është pamundësuar leximi i cookie nga $_REQUEST.

php.ini:

1 request_order = "GP"

Shohim se në $_REQUEST lejohet vetëm GET (G) DHE POST (P), ndërsa COOKIE (C) mungon.

Request order përcakton renditjen e leximit të vlerave, në rastet kur variabli në GET ka emër të njëjtë me atë në POST.

Pra, nëse në një formular e kemi fushën “emaili”, dhe skriptën që e proceson atë formular e thirrim me një URL që ka në fund një query string si kjo:

1 ?emaili=adresa@domaini.com

atëherë, $_REQUEST në skriptën pranuesi i ka dy vlera të quajtura “emaili”, dhe njëra nga këto duhet të eliminohet.

Me request_order = “GP” i jepet përparësi variablit që ka ardhur me metodën POST, pra ajo i bën overwrite variablit me emrin e njëjtë që ka ardhur me metodën GET. Me këtë mundësohet që vlerat e dërguara me formular, të mos “mbulohen” me vlera të shënuara në URL.

Eliminimi i cookie nga request_order nuk e parandalon skenarin e dytë që u cek më sipër.


Konkluzion: nëse presim që një vlerë do të vie me metodën GET - e lexojmë me $_GET, atë me POST e lexojmë me $_POST, dhe në fund, inputet nga COOKIE do t’i lexojmë vetëm me $_COOKIE. Në asnjë mënyrë nuk duhet të mbështetemi në $_REQUEST për shkak të situatave të përmendura më lartë.

Client-side validation

Shumë Web developerë nuk i japin aq fort rëndësi validimit të të dhënave në PHP, por mjaftohen me masat e ndërmarra në front-end: HTML dhe JavaScript.

Për shembull:

  • Nëse është plotësuar një fushë
  • Nëse emri ka minimalisht x shkronja,
  • Nëse një vlerë numerike është brenda një intervali të paracaktuar, etj.

Në HTML5, te fushat që duhet patjetër të plotësohen shtohet atributi required, me çka do të pamundësohet dërgimi i vlerave të fushave të formularit në server. Por, kjo “mbrojtje” është e dobishme vetëm si përkujtues vizitorëve të faqes se kanë harruar ta plotësojnë ndonjë fushë të caktuar, dhe vlera e asaj fushës duhet të verifikohet edhe njëherë: kur të arrijë në server, gjegjësisht në skriptën që e proceson atë formular.

Kjo për shkak se atributi required lehtë mund të largohet me “Inspect element” në browser.

JavaScript (e me të edhe jQuery) mund të deaktivizohet nga browseri tërësisht, duke pamundësuar çfarëdo client-side validation.

Në këto rrethana, të dhënat që i dërgohen serverit do të jenë tërësisht të pavaliduara dhe skripta jonë nuk guxon t’i marrë ashtu si janë, por duhet së pari t’i validojë dhe pastrojë, para se të procedohet më tutje me to.

Pra, validimi me HTML apo JavaScript duhet të konsiderohet vetëm si një lehtësim për vizitorin për t’i parandaluar gabimet e mundshme gjatë shënimit të të dhënave, por assesi si të dhëna tashmë të verifikuara. Verifikimet e bëra në client-side, duhet të përsëriten edhe në server-side.

Kodi në JavaScript/jQuery:

1 var emri = $('#emri').val()
2 if (emri.length < 3)
3 	{
4 	alert("Minimum 3 shkronja për emrin!")
5 	}

duhet ta ketë ekuivalentin e vet edhe në PHP skriptën tonë:

1 $emri = $_POST['emri'];
2 if (strlen($emri) < 3)
3 	{
4 	echo "Minimum 3 shkronja për emrin!";
5 	}

Një mënyrë tjetër e eliminimit të client-side validation arrihet me ruajtjen e faqes me formular në disk, pra duke zgjedhur “Save As” në browser e duke e ruajtur faqen për shembull në Desktop. Pasi të jetë ruajtur, e hapim faqen e formularit me një tekst editor dhe fshijmë gjithçka që ka të bëjë me validim, si në HTML, ashtu edhe në JavaScript. Për të na funksionuar ky formualr, mjafton që në action ta shënojmë URL-në e plotë, pra në vend të:

1 <form action="test.php" method="POST">

shkruajmë:

1 <form action ="http://domaini.com/test.php" method="POST"> 

apo duke ia shënuar emrin e folderit përpara emrit të skriptës, nëse skripta gjendet brenda ndonjë folderi. Me këtë modifikim, ne do të jemi në gjendje të dërgojmë të dhëna skriptës test.php nga faqja që e kemi ruajtur në kompjuterin tonë!

Anti-CRSF token

Për t’u mbrojtur nga situata të tilla, ne duhet formularëve t’ua shtojmë nga një fushë të tipit hidden, në të cilin do të vendosim një vlerë të rastësishme (anti-CRSF token). Ajo vlerë njëkohësisht ruhet edhe në një variabël sesioni. Skripta që i lexon vlerat nga formulari, duhet ta krahasojë vlerën që vjen nga fusha “e fshehtë” me vlerën e ruajtur në variablin e sesionit. Nëse këto vlera përputhen, kjo do të thotë se formulari është plotësuar nga faqja e pamodifikuar. Në të kundërtën, skripta është furnizuar me të dhëna nga një faqe e modifikuar, nga një skriptë ku manipulimet bëhen me PHP, apo duke u përdorur ndonjë metodë tjetër, si psh me përdorimin e bibliotekës cURL (http://curl.haxx.se/), i cili në mënyrë programore mund të dërgojë POST request.

form.php

 1 <?php
 2 session_start();
 3 $crsftoken =  md5(uniqid(rand(), true));
 4 $_SESSION['token'] = $crsftoken;
 5 ?>
 6 <form action='formcheck.php' method="post">
 7   <label for="emri">Emri:</label>
 8   <input type="text" name="emri">
 9   <input type="hidden" name="token" value="<?=$crsftoken;?>">
10   <input type="submit" value="Submit">
11 </form>

formcheck.php

 1 <?php
 2 session_start();
 3 $token = $_POST['token'];
 4 
 5 if ($_SESSION['token'] != $token)
 6 	{
 7 		die("Input jovalid!");
 8 	}
 9 	
10 $emri = $_POST['emri'];
11 echo $emri;

Nëse formularin do ta kishim të ruajtur lokalisht, ose nëse do të përdornim cURL, vlera e fushës crsftoken do të ishte e zbrazët, kështu që me krahasimin e asaj vlere me vlerën e ruajtur në $_SESSION[‘token’] do të konstatohet se “origjina” e faqes së formularit nuk është serveri ynë.

Edhe sikur t’i jepnim ndonjë vlerë asaj fushe, gjasat janë tejet të vogla, mos të thënë inekzistente, se do ta kishte vlerën e hash-it të një numri të rastësishëm, të cilën e gjeneron serveri, secilën herë me vlerë tjetër, sa herë që hapet faqja e formularit.

Duhet të theksohet se kjo mbrojtje ka kuptim vetëm për formularët që janë brenda faqeve të autentikuara me $_SESSION! Nëse faqet janë publike dhe tokeni i pandryshueshëm, atëherë sulmuesi mund ta gjejë tokenin thjesht duke e lexuar nga “View Page Source”.

Ruajtja e tokenit si variabël sesioni i ka edhe të metat e veta, nëse atë e gjenerojmë sa herë hapet formulari. Kjo vie në shprehje kur e hapim formularin e njëjtë disa herë në browser tabs të ndryshëm. Në këtë situatë, kur hapet tab-i i dytë, vlera e variablit të sesionit ndryshohet, pra zëvendësohet me një vlerë të re. Kur kthehemi te tab-i i parë për ta dërguar formularin, skripta do ta konsiderojë si CRSF sepse tokeni i ruajtur si fushë e fshehtë në formular nuk përputhet me vlerën e tokenit në variablën e sesionit.

Elaborimi i mëtejmë i skenarëve të mundshëm për zgjidhjen e këtyre situatave i tejkalon suazat e këtij manuali.

Dispatch Method

Kjo ka të bëjë me vetë arkitekturën e aplikacionit, ku vetëm një skriptë thirret nga URL-ja, ndërsa, varësisht nga parametrat e dërguar me GET, ajo skriptë cakton cila skriptë do të inkludohet/ekzekutohet.

Shembull i URL-së së një aplikacioni të bazuar në dispatch method:

1 http://domaini.com/index.php?cmd=kontakti

Gjatë gjithë kohës, thirret vetëm dispatch.php, ndërsa brenda saj bëhen thirrjet e skriptave të tjera.

 1 switch ($_GET['cmd'])
 2 		{
 3 			case 'login':
 4 				require 'login.php';
 5 				break;
 6 			case 'check':
 7 				require 'check.php';
 8 				break;	
 9 .
10 .
11 .

Duke i analizuar inputet me switch, ne efektivisht jemi duke i eliminuar inputet që nuk janë të numëruara në case, duke parandaluar kështu inkludimin arbitrar të skriptave, si këtu:

1 requre($_GET['cmd'].".php");

ku do të tentohej inkludimi i cilitdo fajll që ceket në query string të URL-së, e ku mund të ishte ndonjë fajll me të dhëna sensitive, apo edhe një URL i një serveri tjetër (nëse direktiva allow_url_fopenphp.ini është On, e që by default edhe është. Po të mundësohej inkludimi i një skripte nga një URL jashtë domainit tonë, pasojat mund të jenë shkatërrimtare sepse sulmuesi aty mund të fusë kod i cili do të “akomodohej” në serverin tonë, ku do të mund të ndërmerrte çfarëdo veprimi, si psh: nxjerrja e të dhënave, fshirja e databazës, etj.

Kjo qasje, në aspektin e sigurisë e ka një avantazh të madh: të gjitha masat e sigurisë që janë të domosdoshme të ndërmerren për të gjitha skriptat, mund të implementohen brenda këtij fajlli të vetëm. Nëse ato masa do t’i zbatonim veç e veç nëpër të gjitha skriptat, do të na paraqiteshin gabime aty-këtu, sepse diku mund të harrojmë të shtojmë atë që kemi shtuar në skriptat tjera.

Duke e centralizuar logjikën e sigurisë, ne e thjeshtësojmë punën tonë në skriptat tjera, te të cilat do të jetë e nevojshme vetëm të fokusohemi në masat e sigurisë që janë specifike për ato skripta.

I tërë aplikacioni ynë është i bazuar në dispatch method.

Parandalimi i thirrjes direkte të skriptave

Nëse aplikacionin tonë e kemi organizuar sipas “dispatch method”, ne duhet të ndalojmë thirrjen direkte të skriptave, sepse thirrja e tyre duhet të bëhet vetëm në mënyrë të centralizuar nga index.php.

Kjo realizohet në atë mënyrë që në index.php definohet një konstantë, së cilës i jepet një vlerë çfarëdo, psh:

1 define("THIRRJA", 1);

Kemi përdorur konstantë e jo variabël, në mënyrë që vlera e saj mos të ndryshohet aksidentalisht gjatë rrjedhës së ekzekutimit të programit.

Tash, në fillim të secilës skriptë që inkludohet në index.php, duhet të vendoset rreshti i cili verifikon se a ekziston ajo konstantë:

1 <?php if (!defined('THIRRJA')) exit('Nuk keni qasje direkte'); ?>

Pra, nëse kjo skriptë thirret me:

1 http://domaini.com/?cmd=kontakti

atëherë skripta kontakti.php do të inkludohet dhe ekzekutohet, sepse, duke qenë brenda fajllit index.php, ajo do ta “shohë” vlerën e konstantës THIRRJA.

Mirëpo, nëse e njëjta skriptë thirret me:

1 http://domaini.com/kontakti.php

ajo nuk do të ekzekutohet sepse, duke qenë jashtë index.php, ajo nuk është në gjendje ta lexojë konstantën THIRRJA, dhe kështu që plotësohet kushti if (!defined('THIRRJA')), pra që konstanta THIRRJA nuk është e definuar, në mënyrë që më pas të ekzekutohet exit('Nuk keni qasje direkte'), e cila e ndërpren ekzekutimin e skriptës.

Kufizimi i variablave të inputit

Gjatë leximit të vlerave të inputit, ne nuk duhet të lejojmë që çdo vlerë e arritur të përdoret automatikisht nga skripta. Psh. në rastin e $_POST, do t’i lexojmë vetëm vlerat e fushave të pritshme, psh në login na intereson email adresa dhe fjalëkalimi, gjersa fushat tjera eventuale nuk na interesojnë.

1 <?php
2 $emaili = $_POST['emaili'];
3 $fjalekalimi = $_POST['fjalekalimi'];
4 ?>

Qasja e gabuar:

1 <?php
2 foreach ($_POST as $key=>$value)
3 	{
4 	$$key = $value;
5 	}

Me këtë mundësohet që nga çdo anëtar të vargut $_POST të krijohet nga një variabël me emrin e indeksit të vargut e me vlerën e atij anëtari të vargut.

Pra:

  • Për $_POST['emri'], krijohet variabli $emri,
  • Për $_POST['fjalekalimi'], krijohet variabli $fjalekalimi,
  • e kështu me radhë, deri te anëtari i fundit i $_POST

Po prej nga do të vinin anëtarët e tjerë të $_POST, kur në formular i kemi vetëm dy fusha? Do të vinin nga një formular i modifikuar, apo një skriptë e sulmuesit.

Ja një shembull se si mund të ndërrohen vlerat e variablave të skriptës, duke u ndikuar nga vlerat e lexuar nga $_POST, me konverzion automatik si në shembullin e mësipërm:

 1 <?php
 2 $level = $_SESSION['level']; // le të themi është lexuar vlera 1
 3 foreach ($_POST as $key=>$value)
 4 	{
 5 	$$key = $value;
 6 	}
 7 
 8 if ($level == 2)
 9 	{
10 	// Një veprim destruktiv
11 	}

Ku është problemi në këtë rast? Problemi është se sulmuesi mund ta ketë shtuar një fushë me emrin “level” së cilës ia jep vlerën 2, dhe me këtë, $_POST['level'] duke u shndërruar në $level me vlerën 2, do ta mbulojë vlerën paraprake të variablit $level që ka mundur të jetë 1. Kështu, kur analizohet vlera me if ($level == 2), përgjigja do të jetë pozitive, edhe pse në fakt, ai përdorues nuk e ka pasur atë vlerë kur është lexuar vlera nga sesioni. Thënë më qartë, nga një anëtar i thjeshtë u shndërrua në administrator!

Kufizimi i vlerave të inputit

//

Pastrimi i inputit

Çdo input duhet të konsiderohet si potencialisht i rrezikshëm, pa marrë parasysh burimin. Rreziqet që vijnë nga inputi i papastruar ndahen në dy grupe kryesore:

  • XSS (Cross site scripting)
  • SQL injection

Me XSS nënkuptohet futja e kodit potencialisht të rrezikshëm (HTML, JavaScript) brenda tekstit që dërgohet me POST ose GET. Kjo rëndom bëhet kur kemi textarea në të cilat na lejohet të fusim tekst të gjatë.

PHP frameworks

Çdo zhvillues i PHP aplikacioneve, me kalimin e kohës fillon të ndërtojë funksione për t’u përdorur në projekte të ndryshme. Mund të jenë për shembull funksione:

  • për filtrimin e inputit
  • për dërgimin e emailave
  • për analizimin e URL-së, etj.

Këto funksione nuk janë specifike për një projekt, por janë të nevojshme në secilin projekt të ri.

Në vend të ruajtes së skriptave në një folder, fillon ta organizojë në folderë të veçantë, si psh:

  • controllers
  • models
  • views
  • configuration
  • libraries
  • helpers, etj.

Kështu gradualisht e ndërton një strukturë të aplikacionit që do ta përdorë në projektet e veta të reja.

Problemi i vetëm është cilësia e vetë funksioneve; nuk mund të pritet që një autor t’i njohë të gjitha aspektet e problemeve në zhvillimin e Web aplikacioneve dhe i vetëm të ballafaqohet me të gjitha situatat.

Një prej zgjidhjeve të gatshme për shumë aspekte të një aplikacioni është përdorimi i Composer/Packagist, ku mund të gjejmë me mijëra pako që zgjidhin probleme nga më të ndryshmet.

Megjithatë, si bazë për një projekt të ri duhet të përdoret një aplikacion i cili në vete i ka të përfshira të gjitha zgjidhjet për gjërat që nevojiten më së shpeshti. Këtu vijnë në shprehje PHP frameworks.

Pra, PHP framework është një Web aplikacion i gatshëm, i cili:

  • ka karakter të përgjithshëm,
  • ofron zgjidhje për të cilat ka nevojë çdo aplikacion
  • ka një strukturë të caktuar të organizimit të fajllave,
  • mund të zgjerohet me module të reja, sipas nevojës

Lista e PHP frameworks

Ndër PHP frameworks më të përdorur janë:

  • Symfony
  • Zend Framework
  • Laravel
  • CodeIgniter
  • Fuel PHP
  • YII

Micro frameworks:

  • Silex

Framework si PHP ekstension:

  • Phalcon

Cilin ta përdor?

Në përzgjedhjen e framework-ut ndikojnë disa faktorë:

  • Kompleksitet i aplikacionit që do ta zhvillojmë
  • Skalabiliteti i aplikacionit
  • Koha e nevojshme për kompletimin e aplikacionit
  • Përvoja dhe niveli i njohurive e programuesit në aspekte të ndryshme të programimit në PHP. siç janë njohja e koncepteve të OOP, etj.

Për fillestarët, më së shpeshti rekomandohet CodeIgniter, për shkak se:

  • Është platformë e dëshmuar
  • Mësohet relativisht lehtë
  • Ka bazë të gjerë të përdoruesve
  • Dokumentacionin e ka gjithpërfshirës dhe të qartë

Për enterprise-level aplikacione përdoren Symfony dhe Zend Framework. Të dyja këto janë shumë komplekse dhe kërkojnë njohje të shkëlqyeshme të OOP programimit.

Një framework që po bëhet mjaft i popullarizuar është Laravel. Atë mund ta klasifikojmë diku ndërmjet CodeIgniter dhe Symfony, aq më tepër që mjaft komponente huazon nga Symfony.

Laravel karakerizohet me sintaksë elegante dhe komponente të shkëlqyeshme, siç janë:

  • Eloquent ORM, për komunikim me databaza
  • Blade templating engine, për ndërtimin e template-ve
  • Elixir, etj.

Një framework që veçohet nga të tjerët është Phalcon. Ky framework nuk është i shkruar në PHP si këto që u përmendën më sipër, por është i shkruar në C dhe përdoret si PHP ekstension. Kjo mundëson që ai framwork të jetë më i shpejti nga të gjithë sepse kodi i tij nuk interpretohet por është i kompajluar.

PHP extensions

Me rritjen e kërkesave për mundësi të reja në aplikacion, zhvilluesi vazhdimisht do të ketë nevojë të shtojë funksione të reja.

Për shembull, nevojitet një funksion apo një tërësi funksionesh për:

  • krijimin e PDF dokumenteve
  • leximi apo krijimin e Excel tabelave
  • për gjenerimin e XML fajllave
  • për manipulim me fotografitë, etj.

Për të gjitha këto kërkesa nevojitet përfshirja e bibliotekave të veçanta të funksioneve.

Njëra mënyrë është me shtimin e PHP ekstensioneve. Ekstensionet janë programe të shkruara në gjuhën programore C, të cilat bëjnë zgjerimin e funksioneve të PHP. Pra, bazës ekzistuese të funksioneve i shtohet edhe një numër i caktuar i funksioneve të reja.

Nëse jemi në dedicated host, ne vetë caktojmë cilat ekstensione të shtohen apo hiqen. Problem më i madh janë shared host, ku vetëm administratorit i serverit mund të manipulojë me ekstensionet.

Ndër ekstensionet më të rëndësishme të PHP janë: - php_mysql - php_mysqli - php_pdo_mysql - php_gd2 - php_curl, etj

Secili prej këtyre ekstensioneve i shton funksione të reja PHP-së, ndërsa mungesa e tyre mund të shkaktojë mosfunksionimin e ndonjë skripte e cila e përdor ndonjë funksion të një ekstensioni të caktuar.

Me WAMP të instaluar, Ekstensionet i gjejmë në folderin C:\wamp\bin\php\php5.5.12\ext. 5.5.12 është versioni i PHP, mund të jetë ndryshe në kompjuterin tuaj.

GIT

git init

Komanda git init bën krijimin e një Git depoje të re. Me të mund ta shndërrojmë një folder të zbrazët në depo, apo edhe një folder i cili tashmë ka fajlla brenda. Me ekzekutimin e kësaj kmente krijohet një .git nënfolder brenda folderit kryesor të projektit, brenda të cilit do të vendosen të dhëna në lidhje me deponë. Struktura ekzistuese e folderit nuk pëson ndryshim.

Në rreshtin komandues shfaqet raporti:

`Initialized empty Git repository in E:/emri_i_folderit/.git/

Brenda folderit .git do të krijohen këta fajlla/folderë;

.git folder
.git folder

Referenca e funksioneve/urdhërave të përdorura

array()

Krijimi i një vargu, gjegjësisht matrice. Në versionet e reja të PHP mund të shkruhet edhe si [ ].

checkdate()

Verifikon nëse data është valide.

Kthen TRUE nëse është valide, FALSE në të kundërtën.

1 <?php
2 var_dump(checkdate(2, 29, 2001));
3 ?>

date()

Formatimi i datës.

1 <?php
2 echo "<p>Sot eshte data: ".date("d.m.Y")."</p>";
3 ?>

die()

Ndërprerja e ekzekutimit të programit, po apo pa raportim.

Teksti i raportit vendoset si argument i funksionit.

1 die("Nuk keni te drejte ta vizitoni kete faqe!")

echo

E “printon” një tekst, numër, vlerë të variablës.

error_log()

error_reporting()

Nëpërmes këtij funksioni përcaktojmë se cilat kategori të gabimeve të shfaqen në Web faqe.

  • error_reporting(0) - Nuk do të raportohet asnjë gabim.
  • error_reporting(E_ALL) - Do të raportohen të gjitha gabimet.
  • error_reporting(E_ERROR | E_WARNING | E_PARSE) - do të raportohen vetëm kategoritë e cekura.
  • error_reporting(E_ALL & ~E_NOTICE) - do të raportohen të gjitha përveç ato të kategorisë “notice”.

exit()

E ndërpren ekzekutimin e skriptës.

extract()

filter_var()

Bën pastrimit dhe validimin e inputit.

floatval()

for

Formon strukturë ciklike, gjegjësisht iteracione. Kjo mundëson përsëritjen e ekzekutimit të një blloku të kodit.

header()

Dërgimi i një HTTP request.

if...else

Degëzimi i programit, varësisht prej asaj a është plotësuar ndonjë kusht.

ignore_user_abort()

ini_set()

intval()

Konvertimi i një vlere numerike në numër të plotë (integer). Përveç të tjerash, i dobishëm për “pastrimin” e inputit, në rastet kur vlera e pritur është integer.

isset()

Verifikon nëse ekziston një variabël.

mktime()

Si rezultat jep UNIX timestamp, në bazë të parametrave të dhënë.

1 <?php
2 date_default_timezone_set('UTC');
3 echo "<p>".date("d.m.Y h:i:s", mktime(19, 45, 12, 3, 8, 2012, 0))."</p>";
4 echo "<p>".date("d.m.Y h:i:s", mktime(19, 45, 12, 3, 8, 2012, 1))."</p>";
5 ?>

Ora, Minutat, Sekondat, Muaji, Dita, Viti, Koha_verore

Koha verore = 1

nl2br()

Konvertimi i new line ne br, që mundëson paraqitjen në HTML të rreshtave të rinj të textarea.

phpinfo()

Informata gjithpërfshirëse mbi ambientin e serverit.

print_r()

1 <?php
2 $ditet = array("E hënë", "E martë", "E mërkurë", "E enjte", "E premte", "E shtunë", \
3 "E diel");
4 print_r ($ditet);
5 ?>

Prezanton të dhënat mbi një variabël në formë të lexueshme.

session_start()

Fillimi i një sesioni të ri, apo vazhdimi i një sesioni të nisur.

setcookie()

Krijimi i një cookie.

strip_tags()

Eliminon HTML tagat nga inputi, në tërësi apo në mënyrë selektive, varësisht nga opsionet e përdorura.

strlen()

Tregon numrin e shkronjave të një teksti.

1 <?php
2 $emri = "Innovation Centre Kosovo";
3 echo "<p>".$emri." ka ".strlen($emri)."shkronja.</p>";
4 ?>

strval()

substr()

Shkëput një pjesë të tekstit.

time()

E tregon kohën aktuale të matur si numër sekondash nga fillimin i UNIX Epokës (January 1 1970 00:00:00 GMT)

Shembull:

1 <?php
2 $javatjeter = time() + (7 * 24 * 60 * 60);
3 echo "<p>Tash:   ". time()."</p>";
4 echo '<p>Tash:   '. date('d.m.Y')."</p>";
5 echo '<p>Pas 1 jave: '. date('d.m.Y', $javatjeter)."</p>";
6 ?>

Koha aktuale + (7 ditë * 24 orë * 60 minuta * 60 sekonda)

trim()

Eliminon whitespace nga inputi.

urlencode()

E bën tekstin të përshtatshëm për t’u bartur në URL, duke eliminuar problemet me shenjat speciale.

var_dump()

Tregon përmbajtjen e një variabli apo vargu. I dobishëm gjatë debugging.

1 <?php
2 $ditet = array("E hënë", "E martë", "E mërkurë", "E enjte", "E premte", "E shtunë", \
3 "E diel");
4 var_dump ($ditet);
5 ?>

Paraqet informata të strukturuara mbi variablat, duke përfshirë tipin dhe vlerën.

Matricat dhe objektet shqyrtohen në mënyrë rekurzive.