table of contents€¦ · pembaca dapat lebih mudah memahami konsep pemrograman berbasis objek...
TRANSCRIPT
1.1
1.2
1.3
1.4
1.5
1.6
2.1
2.1.1
2.1.2
2.1.3
2.1.4
2.1.5
2.1.6
2.2
2.2.1
2.2.2
TableofContents
PendahuluanJudul
KataPengantar
TentangPenulis
TentangBukuIni
Lisensi
Testimonial
PersiapandanSettingI.PengenalanPHP
SejarahPHP
KelebihanPHP
KekuranganPHP
MarketSharePHP
PHP7-TheNextGeneration
PerbandinganPHP7
II.MinimumRequirementEnvironment
VersiPHPMinimum
MenjalankanPHPMelaluiCommandLine
2
3.1
3.1.1
3.1.2
3.1.3
3.1.4
3.1.5
3.2
3.2.1
3.2.2
3.2.3
3.2.4
3.3
3.3.1
3.3.2
3.3.3
3.4
3.4.1
3.4.2
3.4.3
3.5
3.5.1
3.5.2
3.5.3
Dasar-DasarOOPIII.PemrogramanBerbasisObjek
ApaituPemrogramanBerbasisObjek
KelebihanPemrogramanBerbasisObjek
KekuranganPemrogramanBerbasisObjek
KenapaharusbelajarOOP
FiturDasarYangAdapadaOOP
IV.ClassdanObject
ApaituClass
ContohClasspadaPHP
AnatomiClass
PembuatanObject(Instansiasi)
V.PropertydanMethod
ApaituProperty
ApaituMethod
UrutanParameterpadaMethod
VI.Visibilitas
ApaituVisibilitas
TingkatanVisibilitaspadaPHP
TipsVisibilitas
VII.KonsepStatisdanKonstanta
ApaituKonsepStatis
ContohPenerapanKonsepStatis
VisibilitaspadaKonsepStatis
3
3.5.4
3.5.5
3.5.6
3.6
3.6.1
3.6.2
3.7
3.7.1
3.7.2
3.8
3.8.1
3.8.2
3.8.3
3.8.4
3.8.5
3.9
3.9.1
3.9.2
3.10
3.10.1
3.10.2
4.1
ApaituKonstanta
ContohPenerapanKonsepKonstanta
VisibilitaspadaKonsepKonstanta
VIII.Keyword$thisdanself
Apaitu$this
Apaituself
IX.ReturnValue
ApaituReturnValue
ContohPenggunaanReturnValue
X.ConstructordanDestructor
ApaituConstructor
ContohPenggunaanConstructor
ApaituDesctructor
ContohPenggunaanDesctructor
JanganBilangSiapa-Siapa
XI.Enkapsulasi
ApaituEnkapsulasi
PenerapanEnkapsulasi
XII.Pewarisan
ApaituPewarisan
PenerapanPewarisan
OOPLanjutanXIII.OverloadingdanOverriding
4
4.1.1
4.1.2
4.1.3
4.2
4.2.1
4.2.2
4.2.3
4.2.4
4.3
4.3.1
4.3.2
4.3.3
4.4
4.4.1
4.4.2
4.5
4.5.1
4.5.2
4.5.3
4.5.4
4.6
4.6.1
4.6.2
4.6.3
4.6.4
Keywordparent
ApaituOverloadingdanOverriding
PenerapanOverloadingdanOverriding
XIV.AbstractClassdanAbstractMethod
ApaituAbstractClass
ApaituAbstractMethod
KegunaanAbstractClassdanAbstractMethod
PenerapanAbstractClassdanAbstractMethod
XV.Interface
ApaituInterface
ContohPenggunaanInterface
MembukaWawasan
XVI.MethodChaining
ApaituMethodChaining
CaraPembuatanMethodChaining
XVII.PengelompokanBerkas
PengelompokanBerkaspadaOOPPHP
Keywordnamespacedanuse
ContohPenggunaannamespacedanuse
Memberikanaliasdengankeywordas
XVIII.ParameterCastingdanReturnTypeDeclaration
ApaituParameterCasting
ScalarTypeHinting
ObjectTypeHinting
NullableTypeHinting
5
4.6.5
4.6.6
4.7
4.7.1
4.7.2
5.1
5.1.1
5.1.2
5.2
5.2.1
5.2.2
5.2.3
5.3
5.3.1
5.3.2
5.3.3
5.3.4
5.3.5
5.3.6
5.4
5.4.1
5.4.2
ApaituReturnTypeDeclaration
CaraPenggunaanReturnTypeDeclaration
XIX.RecursiveFunction
ApaituRecursiveFunction
ContohPenggunaanRecursiveFunction
OOPExpertXX.LateStaticBindings
ApaituLateStaticBindings
ContohLateStaticBindings
XXI.Trait
ApaituTrait
CaraPenggunaanTrait
BestPracticepadaTrait
XXII.CodingStandard
ApaituFIGdanPSR
KenapaharusmenerapkanPSR
AturanPenulisanSyntax
ContohPenerapanAturanSyntax
AturanPenulisanClassdanNamespace
ContohPenerapanAturanClassdanNamespace
XXIII.ExceptionHandling
ApaituExceptionHandling
HirarkiErrorpadaPHP
6
5.4.3
5.5
5.5.1
5.5.2
5.5.3
5.5.4
5.6
5.7
5.8
5.8.1
5.8.2
5.8.3
5.8.4
5.8.5
5.8.6
5.8.7
5.9
5.9.1
5.9.2
5.9.3
5.9.4
5.10
5.10.1
5.10.2
ExceptionHandlingpadaPHP
XXIV.AnonymousFunctiondanAnonymousClass
ApaituAnonymousFunction
ContohPenggunaanAnonymousFunction
ApaituAnonymousClass
ContohPenggunaanAnonymousClass
XXV.CaraMembuatVariadicFunction
XXVI.InstansiasipadaKonteksStatis
XXVII.MagicMethodpadaPHP
ApaituMagicMethod
__construct()dan__desctruct()
__set()dan__get()
__isset()dan__unset()
__sleep()dan__wakeup()
__call()dan__callStatic()
__toString()
XXVIII.FinalClassdanFinalMethod
ApaituFinalClass
ContohPenggunaanFinalClass
ApaituFinalMethod
ContohPenggunaanFinalMethod
XXIX.ObjectsebagaiArray
ApaituArrayAccess
ContohPenggunaanArrayAccess
7
6.1
6.1.1
6.1.2
6.1.3
6.1.4
6.2
6.2.1
6.2.2
6.2.3
6.2.4
6.3
6.3.1
6.3.2
6.3.3
6.3.4
6.4
6.4.1
6.4.2
6.4.3
6.5
6.5.1
6.5.2
6.5.3
StudiKasusXXX.PerhitunganPajakPPH21
CaraPerhitunganPajakPPH21
PersiapanProyek
PengelompokanMasalah
PenulisanCode
XXXI.PackageManagementdenganComposer
ApaituComposer
KenapaMenggunakanComposer
InstalasiComposer
Tentangcomposer.json
XXXII.MembuatPackageSendiri
Membuatcomposer.json
AutoloaddenganComposer
PendaftaranPackage
SinkronisasiGithubdanPackagist
XXXIII.DesignPattern
ApaituDesignPattern
ManfaatPenggunaanDesignPattern
Macam-MacamDesignPattern
XXXIV.StudiKasusMembuatFrameworkSederhana
SkopProyek
KonsepFrontController
HTTPRequestdanHTTPResponse
8
6.5.4
6.5.5
6.5.6
6.5.7
6.6.1
6.6.2
6.6.3
6.6.4
6.6.5
6.6.6
6.6.7
6.6.8
6.6.9
6.6
7.1
MengarahkanRequestdenganRouter
MembuatControllerClass
MembuatKernelFramework
Kesimpulan
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
TujuandariProyekTodoList
PembuatanDatabase
KoneksiDatabase
MembuatModelClass
MembuatTodoClass
MembuatControllerClass
MenambahkanTemplateEnginepadaFramework
MembuatTodoControllerClass
Kesimpulan
PenutupPenutup
9
Kode:16C4D834B
PemrogramanBerbasisObjekModern
MenggunakanPHP
MenjadiMasterPemrogramanBerbasisObjek(OOP)denganPHP7-NextGeneration
MuhamadSuryaIksanudin
Judul
10
KataPengantarSegalapujibagiAllahSWT,yangsenantiasamelimpahkanrahmatserta karunia-Nya kepada penulis sehingga dapatmenyelesaikanbuku dengan judul "Pemrograman Berbasis Objek ModernMenggunakan PHP" ini. Shalawat serta salam penulis haturkankepada Rasulullah Sholallahu Alaihi Wassalam yang menjaditeladanterbaikbagiumatmanusia.Rasulyangmembawakitadarijalangelapmenujucahaya.
Terimakasihpenulissampaikankepadaseluruhpihakyang telahmembantu terwujudnya buku ini terutama saudara-saudaraseperjuangan di Komunitas Symfony Framework Indonesia yangtidakpernah lelahmembantupenulisdalampenyusunanbuku ini.Terima kasih juga penulis ucapkan khususnya kepada BapakHendroWicaksono yang telah bersediamengoreksi isi dari bukuini. Terima kasih penulis haturkan kepada Muhibbudin Suretnoyangtelahmembuatkansampuluntukbukuini.
Penulis menyadari dalam penulisan buku ini terdpat banyakkekurangan.Penulismengharapkankritikdansarandaripembaca.Harapan penulismakalah ini bisa bermanfaat bagi pembaca danbagipenelitianberikutnya.
Jakarta,Juli2018
MuhamadSuryaIksanudin
KataPengantar
11
KataPengantar
12
TentangPenulisMuhamadSuryaIksanudin
Penulis lahir di Pemalang, 3 April 1988. Penulistercatat sebagai salah satu member di KomunitasSymfony Framework Indonesia sebuah forumdiskusi berbagai permasalahan yang ada di duniapemrogramankhususnyaPHPdanlebihkhususlagi
tentangFrameworkSymfony.
Penulis juga bekerja sebagai pekerja lepas sebagai SystemAdministrator maupun Web Developer. Disela-sela waktuluangnya,penulis selalumeng-update informasi tentang teknologisecaraumummaupunwebteknologisecarakhusus.
Diluar itu semua, penulis juga hobi ngoding dan telah membuatbeberapaproyekyangdapatdilihatpadahalamanGithubpenulis.
Penulisdapatdihubungimelaluikontakberikut:
Whatsapp:+6287800093915
Email:[email protected]
Facebook:MuhamadSuryaIksanudin
LinkedIn:MuhamadSuryaIksanudin
TentangPenulis
13
TentangBukuIniBukuinisayadedikasikanuntukmenjawabpertanyaan,masukkandankritikyangsaya terimadaribuku "BelajarSantaiOOPPHP".Berbedadenganbuku"BelajarSantaiOOPPHP"yanglebihtothepoint, buku ini mencoba menjelaskan lebih detail setiappembahasanyangada.Selain itu, buku ini jugamembahas lebihbanyakdengancontohyanglebihbervariatifsehinggadiharapkanpembaca dapat lebih mudah memahami konsep PemrogramanBerbasisObjekdaripadabukusebelumnya.
Buku ini menyajikan pembahasan yang lebih mendalam denganjumlah bab yang lebih banyak dan contoh yang lebih mendeketireal case penggunaan Pemrograman Berbasis Objek untukmenyelesaikansebuahmasalahtertentupadapekerjaan.
Buku ini juga mengajarkan best practice serta coding standardyangberlakuglobaldalamkomunitasdanekosistemPHPsepertiPSR (PHP Standard Recomendations) dan Composer sehinggasetelah membaca buku ini, pembaca diharapkan dapatmengimplementasikannyadalampekerjaansecaralangsung.
Semoga buku ini dapatmenjadi salah bacaanwajibprogrammerPHPyanginginmenguasaidanmemahamikonsepPemrogramanBerbasisObjekmenggunakanbahasapemrogramanPHP.
TentangBukuIni
14
LisensiGitbook
Buku ini menggunakan Gitbook sebuah perangkat lunakopensource yang dikhususkan untuk menulis dokumenmenggunakan formatmarkdown.Gitbookmemiliki lisensi ApacheLicense2.0.
VisualStudioCode
Visual Studio Code digunakan sebagai text editor untuk menulisbukuini.Selainitu,VisualStudioCodejugapenulisgunakanuntukmenuliskanbeberapa listingcodeyangadadalambukuini.VisualStudio Code adalah text editor opensource yang menggunakanlisensiMITsehinggaamanuntukdigunakanbaikuntukkeperluancommercialmaupunnoncommercial.
Buku "Pemrograman Berbasis Objek Modern MenggunakanPHP"
Buku ini adalah hasil karya intelektual yang dilindungi olehperundangan yang berlaku di Negara Kesatuan RepublikIndonesia. Meski buku ini didistribusikan dalam format e-booknamunlisensibukuinibersifatprivate.
Andatidakdiperkenankanuntukmeng-copyataumendistribusikanulang buku ini kepada orang lain. Bila Anda melakukan haltersebut, maka Anda berarti telah melakukan pembajakan dan
Lisensi
16
Anda dapat dituntut secara hukum sesuai undang-undang yangberlakudiNegaraKesatuanRepublikIndonesia.
Disetiapbuku ini terdapatkodeunikdisudutkananatashalamanjudul yangmenjadi identitas setiap copy dari buku ini. Kode uniktersebutmenyimpanidentitaspemilikbukuyangsahsehinggabilakodetersebutdigunakanolehorangyangtidakberhakmakakamidapat membatalkan segala benefit yang melekat pada bukutersebutketikapemesanan.
Dengan membeli buku ini, maka Anda berarti telahmengetahuidanmenyetujuiperihallisensiyangadapadabukuini.
Lisensi
17
TestimonialPeter J. Kambey (Head of Executive PHP IndonesiaCommunity)
Sayasangatgembiradengankehadiranbuku ini karenasayamenyadari pengetahuan mengenai OOP (utamanya di PHP)terbuktimenjadihalyangamatpentingdalampengembanganaplikasibisnisyangsayabangun.
UntukmenularkanpengetahuaninimakatopikmengenaiOOPmenjadi salahsatupoint penting yang sering sayabagikandisetiap seminar dan meetup tehnikal dari komunitas PHPIndonesiadenganharapanagarmerekaberusahasemaksimalmungkin untuk belajar dan tumbuh menjadi developer-developerhandaldinegeriini.
AsepBadjaPradipta(CEOTanibox)
Tidak banyak buku pemrograman PHP berbahasa Indonesiayangmemilikibahasansangatmendalam.BukuPemrogramanBerbasisObjekModerenDenganPHPkaryaSuryainiberbeda,cocok dijadikan acuan bagi pemrogram pemula karenamenggunakanbahasayangsederhana,danbisajugamenjadibahanpenyegaranbagiparapemrogramtingkatlanjut.
HendroWicaksono (Founder SLiMS LibraryManagementSystem)
Testimonial
18
Kalaumencari bukumembahas PHP yangmembahasmateridari level pemula sampai mahir, lengkap, serius, tidakkacangan, dan ditulis oleh ahlinya di dunia nyata, makamembeli(danmembaca)bukuiniadalahkeputusanyangtepat.Dengan pengalaman tahunan membangun aplikasi skalaenterprise berbasis OOP, M. Surya Ikhsanudin bisamenjelaskandengansangatdetaildisertaicontohyangmudahdicerna.Veryhighlyrecommendedbook!
Yayan Sopiyan (Senior Developer Zend FrameworkIndonesia)
Buku yang menarik untuk Anda gunakan sebagai panduandalammembangunaplikasiberbasisPHP.MenggunakanteknikPHPmodern,membahas berbagai contoh yang dimaksudkandengan jelas dan terinci, agar diharapkan materi yangdisampaikan dapat dipahami dengan mudah. Sebagaipengguna aktif dan aktivis PHP Zend Framework, sayamerekomendasikanbukukaranganSuryaini.GoodJobBro!
Testimonial
19
I.PengenalanPHPPadabab inikitaakanmembahastentangsejarahPHPdariawalpertama kali dirilis hingga sekarang. Selain itu kita juga akanmembahas tentang kelebihan dan kekurangan PHP sebagaibahasa pemrograman serta market share PHP di dunia webdevelopment.
SejarahBahasaPemrogramanPHPMenurut wikipedia, Pada awalnya PHP merupakan kependekandari Personal Home Page (Situs personal). PHP pertama kalidibuatolehRasmusLerdorfpadatahun1995.PadawaktuituPHPmasih bernama Form Interpreted (FI), yang wujudnya berupasekumpulan skrip yang digunakan untuk mengolah data formulirdariweb.
Selanjutnya Rasmus merilis kode sumber tersebut untuk umumdan menamakannya PHP/FI. Dengan perilisan kode sumber inimenjadi sumber terbuka, maka banyak pemrogram yang tertarikuntukikutmengembangkanPHP.
PadaNovember1997,dirilisPHP/FI2.0.Pada rilis ini, interpreterPHP sudah diimplementasikan dalam program C. Dalam rilis inidisertakan juga modul-modul ekstensi yang meningkatkankemampuanPHP/FIsecarasignifikan.
I.PengenalanPHP
20
Pada tahun 1997, sebuah perusahaan bernama Zend menulisulang interpreter PHPmenjadi lebih bersih, lebih baik, dan lebihcepat. Kemudian pada Juni 1998, perusahaan tersebut merilisinterpreterbaruuntukPHPdanmeresmikanrilis tersebutsebagaiPHP 3.0 dan singkatan PHP diubah menjadi akronim berulangPHP:HypertextPreprocessing.
Padapertengahan tahun1999,Zendmerilis interpreterPHPbarudan rilis tersebut dikenal denganPHP4.0. PHP4.0 adalah versiPHPyangpalingbanyakdipakaipadaawalabadke-21.Versi inibanyak dipakai disebabkan kemampuannya untuk membangunaplikasi web kompleks tetapi tetap memiliki kecepatan danstabilitasyangtinggi.
Pada Juni 2004, Zendmerilis PHP 5.0. Dalam versi ini, inti dariinterpreter PHP mengalami perubahan besar. Versi ini jugamemasukkan model pemrograman berorientasi objek ke dalamPHP untuk menjawab perkembangan bahasa pemrograman kearah paradigma berorientasi objek. Server web bawaanditambahkan pada versi 5.4 untuk mempermudah pengembangmenjalankankodePHPtanpameng-installsoftwareserver.
Pada saat buku ini ditulis, PHP telahmencapai versi 7.2 denganpenambahanekstensi danperbaikanperforma yangmenjanjikan.BerikutadalahinfografistentangsejarahdanperkembanganPHPsertaekosistemnyadariawalhinggatahun2015yaituketikaPHP7atauyang jugadikenaldenganPHPNextGeneration (PHPNG)dirilispublik.
I.PengenalanPHP
21
Sumber:https://blog.fortrabbit.com
I.PengenalanPHP
22
KelebihanPHPSebagai bahasa pemrograman, PHP memiliki banyak kelebihanantaralain:
Komunitasyangbesar
Tidak dapat dipungkiri bahwa komunitas PHP sangat besar dantersebardiseluruhdunia.DiIndonesiasajaadabanyakkomunitasyang berafiliasi dengan PHP baik itu pembahasan PHP secaraumum maupun pembahasan secara khusus misalnya tentangframework. Di facebook ada group PHP Indonesia yangmembahasPHPsecaraumum,danadapulaSymfonyFrameworkIndonesia yang membahas secara khusus tentang frameworkSymfony. Tidak hanya itu, di Telegram, WhatsApp pun banyakbertebarangroupyangmembahastentangPHP.
Resourcesyangmelimpah
Dikarenakankomunitasnyayangbesar,tentusajaakanberdampakpada kemudahan mencari resources yang berhubungan denganPHP baik itu permasalahan yang sering terjadi, library, software,CMShinggaframeworkPHPbanyaksekalibertebarandandenganmudahdapatditemukandengangoogling.
Mudahdipelajari
PHP adalah bahasa pemrograman sejuta umat. Hampir semuaorangyangpernahbergelutdenganduniaWebDevelopmentpastipernah menggunakannya atau setidaknya pernah sekedar
I.PengenalanPHP
23
mencobanya. Tutorial untuk memulai belajar PHP pun denganmudah ditemukan dengan mengetikkan kata kunci pada mesinpencari.
Simpel
PHP itu simpel.Syntax-nya sangat sederhana danmudah sekalidipelajari.Sakingsimpelnya,untukmemulaibelajarPHPkitatidakperlu melakukan setting apapun, cukup install paket softwareseperti XAMPP atau WAMP maka Anda sudah dapat memulaibelajarPHP.
Mudahdanmurahuntukdeployment
Untuk men-deploy program PHP sangatlah mudah, kita cukupmeng-uploadkeserverhostingyanghargajugasangatterjangkaubahkanadayanggratis.
Danmasihbanyaklagikelebihanlainnya.
KekuranganPHPBanyakkorangyangbilangkekuranganutamaPHPadalahbahwaPHP bahasa yang weak type dimana sebuah variable tidakmemiliki tipe data sehingga menyulitkan ketika melakukandebugging.Weaktypeinimenyebabkanterjadinyajugglingdimanasebuahvariableyangtadinyaberisinilai integer misalnyadapatberubahmenjadiberisinilai stringataubahkantipedatalainnya.
I.PengenalanPHP
24
Selain weak type, PHP juga mempunya kekurangan lain yaituinkonsistensiAPI (Application Programming Interface). API disinibukanWebAPIyangmengembalikan jsontapiAPIdisiniadalahfungsi bawaan dari PHP yangmenjadi interface atau tatapmukaantarakitasebagaideveloper danbahasapemrogramanPHP itusendiri.ContohpalingmudahdariketidakkonsistenanPHPadalahdalamhal penamaan fungsimisalnyaantara fungsi substr danstr_replace.
Kenapa substr tidak dinamai str_sub ? Atau str_replacediganti jadi replacestr ? Inilah yang membuat saya sebagaideveloper PHPmalas untuk menghafalkan nama fungsi maupunurutan parameternya. Pada akhirnya saya lebih senang melihatdokumentasi resmi untuk melihat bagaimana sebuah fungsibekerja.
MarketSharePHPBanyak yang menyangsikan PHP akan bertahan dan mampubersaing di era Web 3.0 seperti sekarang ini. Namun komitmenPHPuntuk terusmenghadirkan inovasidanmenjawabkebutuhanuser(Developer)tetapmembawaPHPmenjadipemimpinsebagaibahasayangpalingdigunakanuntukpembuatanwebsitemaupunaplikasi berbasis internet. Tidak tanggung-tanggung, PHPmeninggalkan bahasa pemrograman lain dengan market sharelebihdari80%diikutiolehASP.NetdanJava.
I.PengenalanPHP
25
Pencapaian ini membuktikan bahwa PHP masih tetap diminati,terlebih dengan hadirnya PHP generasi terbaru (PHP 7) yangmenghadirkan banyak fitur baru dari segi kecepatan eksekusi,kestabilan serta keamanan. Berikut adalah info grafis tentangmarketsharePHP.
I.PengenalanPHP
26
Sumber:http://zend.com
I.PengenalanPHP
27
PHP7-TheNextGenerationMelihat kebelakang ketika PHP 5 pertama kali dirilis, versi iniadalah versi PHP pertama yang melahirkan banyak perubahanbesarpadaekosistemPHP.Banyaksekaliframeworkdanmaupuntool pengembangan yang dirilis beberapa bulan setelah versi inidirilis secara publik. Framework seperti Zend, Symfony, Prado,Cake, CodeIgniter, YII dan Laravel adalah sebagian dari banyakframework yang mengiringi perjalanan PHP 5 selama lebih darisatu dekade. Tool dan library lain seperti PHPUnit, PHP CodeSniffer,Doctrine,PropelpunikutmeramaikanekosistemPHP.
Ketika awal kemunculannya, PHP 5 yang mengusung konsepOOP,belummampumenjawabbeberapakebutuhansepertibelumadanya namespace , static type hinting , return type ,closure dan masih banyak lagi. Oleh karena ini, PHP terusberevolusi dan menambahkan fitur-fitur tersebut sedikit demisedikitdisampingjugatetapmemberikanperbaikankeamanandanstabilitas. Hingga PHP 5.6 yaitu versi terakhir dari PHP 5, PHPtelah memiliki beberapa fitur seperti namespace , array type
hintingdan closure.
Namun isu besar muncul pada PHP 5, yaitu masalah padapenggunaanmemoryyangborossertawaktueksekusiyanglama.MasalahtersebutsebenarnyatelahdijawabolehFacebookdenganmembuat engine yang diberinama HHVM (Hip Hop VirtualMachine)yaitumesinyangmampumengubahcodePHPmenjadibytecode seperti pada Java, dan kemudian mengeksekusinya.
I.PengenalanPHP
28
HHVM sendiri selain men-support PHP, juga men-supportHacklang, sebuah bahasa pemrograman yang syntax-nya sangatmiripdenganPHPyangdikembangkanolehFacebook.
Kemunculan HHVM sendiri sebenarnya memberikan keresahanbagiZendselakupengembangPHP.Halinikarena,selainHHVM,Facebook jugamempromosikan bahasa pemrograman baru yaituHacklang yang memang diciptakan untuk "menyaingi" PHP danberjalan dengan optimal pada HHVM. Oleh karena itu, Zendkemudian me-refactor Zend Engine agar bisa lebih baik dariHHVM.
Setelah proses pengembangan yang cukup lama, pada Februari2015PHP 7 lahir sebagaiTheNextGeneration of PHP. Banyaksekali perombakan yang dilakukan oleh Zend terutama padaperforma dan penggunaan memory yang lebih kecil. Selain itu,beberapafiturditambahkanuntukmelengkapi fiturOOPyangadadi PHP seperti return type , static type hinting selain jugaperbaikan beberapa fitur. Dan berikut adalah info grafis tentangfituryangadapadaPHP7.
I.PengenalanPHP
29
Sumber:http://vm5.eu
I.PengenalanPHP
30
PerbandinganPHP7denganHHVMdanPHP5Seperti yang sudah disebutkan diatas, PHP 7 memiliki banyakkeunggulan terutamayangsangatmenonjoladalahperformadanpenggunaan memory yang lebih kecil. Perbaikan performa inimenjadikanPHPkembalimemimpindanmengungguliHHVMdarisisi waktu eksekusi dalam beberapa benchmark. Dan berikutadalah info grafis perbandingan antara PHP 7, HHVM maupunPHP5menggunakanbeberapasoftwarebaikituframework,CMS,maupunPHP7dibandingkandenganbahasapemrogramanlain.
I.PengenalanPHP
31
Sumber:http://www.zend.com
I.PengenalanPHP
32
Dari info grafis diatas, terbukti bahwa PHP 7 mampu menjawabtantangan dan kebutuhan pasar. Dan ini menunjukkan komitmendari Zend selaku pengembang dan perusahaanPHP untuk terusmemberikan yang terbaik untuk pada developer PHP di seluruhdunia.
I.PengenalanPHP
33
II.MinimumRequirementEnvironmentBab ini berguna untuk menyamakan lingkungan pengembangansehinggakedepannyatidakterjadihal-halyangtidakdiharapkan.
VersiPHPMinimumUntuk Versi PHP yang akan kita gunakan sebagai acuan adalahPHP versi 7.2.1 dan karena kebetulan sayamenggunakan Linuxsebagai lingkungan pengembangan, saya tidak menggunakanpaketsoftwaresepertiXAMPP,WAMPatausejenisnya.BilaAndaterbiasa dengan paket software maka saya menyarankan untukmenggunakanXAMPPdenganversiPHPyangsesuai.
Untukmen-downloadXAMPP,Andadapat langsungmengunjungihalaman resminya apache frineds kemudian pilih sistem operasisesuaiyangAndagunakandanpastikanAndamen-downloadversiPHP yang sesuai. Langkah selanjutnya tinggal Anda install padasistemoperasiyangAndagunakan.
II.MinimumRequirementEnvironment
34
MenjalankanPHPMelaluiCommandLineSetelah melakukan instalasi PHP, tahap berikutnya adalah kitadapat menjalankan perintah PHPmelalui terminal/command line.Untuk mengetes apakah kita sudah dapat menjalankan PHPmelaluicommandline,kitadapatmelakukannyadenganmembukaterminal (untuk Linux danMacOS) atau commandprompt (untukWindows)lalumengetikkanperintah php-v.Bilakitatelahdapatmenjalankan perintah PHP maka akan muncul tampilan sebagaiberikut:
Bila belum, maka Anda dapat mendaftarkan PHP kedalamenvironment variable. Untuk Windows dapat mengikuti tutorialpadalinkini.UntukLinuxdanMacOSdapatmengikutitutorialpadalink ini. Anda hanya cukup mengganti lokasi (path) PHP sesuaidengandirektoriataufoldertempatAndameng-installXAMPP.Bilacaratersebuttidakdapatbekerja,Andadapatmencobame-restartPC Anda kemudian coba jalankan PHP melalui command linekembali.
II.MinimumRequirementEnvironment
35
III.PengenalanPemrogramanBerbasisObjekPada bab ini kita akanmembahas tentang apa itu pemrogramanberbasisobjek, kelebihandankekurangannyaserta fiturapasajayang harus ada dalam bahasa pemrograman yang mendukungpemrogramanberbasisobjek.
ApaituPemrogramanBerbasisObjekMenurut wikipedia, Pemrograman berorientasi objek merupakanparadigma pemrograman yang berorientasikan kepada objek.Semua data dan fungsi di dalam paradigma ini dibungkus dalamkelas-kelasatauobjek-objek.
Dalampemrogramanberbasisobjek,kitadituntutuntukmemahamidanmemetakanmasalahkedalamclasssertamemecahmasalahkedalamclass-class yang lebihkecildansimpelagarsolusi yangdibuat lebihspesifik.Selanjutnya,class-class tersebutakansalingberkomunikasi dan berkolaborasi untuk memecahkan masalahyang kompleks. Class-class ini nantinya akan dirubah menjadiobjek-objekpadasaatruntime.
III.PemrogramanBerbasisObjek
36
Setiap class dalam OOP mempunyai method atau fungsi sertapropertyatauatribut.Methoddalamclasssecaramudahdiartikansebagai segala kemampuandariclass atau apa saja yang dapatdilakukan oleh sebuah class. Sedangkan property adalah segalasesuatuyangdimilikiolehclass.DalamOOP,propertydanmethoddalam class saling bekerjasama membangun sebuah solusi darisuatumasalah.
Dalam beberapa referensi,method disebut juga sebagai functionsedangkan property sering disebut juga sebagai attribute.SehinggaAndatidakperlubingungbilanantinyadibukulain,Andamenemui istilah function danattribute sebagai penggantimethoddan property. Yang perlu Anda pahami bahwa function ataumethodadalahfiturataukemampuandarisebuahclasssedangkanproperty atau attribute adalah segala sesuatu yang dimiliki olehsebuahclass.
Sumber:https://github.com
KelebihanPemrogramanBerbasisObjekPemrograman berbasis objek atau biasa disebut OOP, memilikibanyak keunggulan dibandingkan paradigma pemrogramanlainnya. Keunggulan pemrograman berbasis objek antara lain
III.PemrogramanBerbasisObjek
37
sebagaiberikut:
Modularitas: pogram yang dibuat dapat dipecah menjadimodul-modul yang lebih kecil dan nantinya digabungkanmenjadisolusiyangutuh.
Fleksibilitas: karena setiap solusi dibuat dalam bentuk class,ketikaterjadiperubahanmakahanyaclasstersebutsajayangperludirubah.
Ekstensibilitas: penambahan method atau property dapatdilakukandengansangatmudah.
Reuse: class dapat digunakan berkali-kali untuk proyekmaupunmodulyanglain.
Mudah dimaintain: karena setiap class berdiri sendiri, makauntukmemaintainclasstersebutjauhlebihmudah.
Keamanan code: adanya visibilitas memberikan fiturkeamanan dimana developer lain tidak bisa dengan bebasmenggunakanfituryangadapadasebuahobjek.
Waktu development lebih cepat: karena reusable otomatisdapatmempersingkatwaktupengembanganprogram.
DanmasihbanyaklagikeunggulanOOPyanglainnya.
KekuranganPemrogramanBerbasisObjek
III.PemrogramanBerbasisObjek
38
Selain kelebihan, pemrograman berbasis objek juga mempunyaikekuranganantaralainsebagaiberikut:
Learningcurveyanglumayantinggi.
Ukuranprogramjauhlebihbesar.
Pemakaianmemorylebihbesar.
KenapaHarusBelajarOOPSetelahmengetahuikelebihandankekuranganOOP,sekarangkitasampai kepada kesimpulan kenapa kita harus belajar OOP. Tapisebelum kita membahas kenapa kita harus belajar OOP, adabaiknya kita coba melihat realita yang ada. Di ekosistem PHPsekaranginibanyaksekaliframeworkmulaidariyangkecilsepertiSlim, Lumen, dan Zend Expressive hingga framework berskalabesar seperti Zend Framework, dan Symfony. Bila kita cermati,hampir semua framework dibangun dengan menggunakanparadigma OOP sehingga dengan menguasai OOP, kita dapatmemaksimalkanfituryangadadalamframeworktersebut.
Dan berikut adalah beberapa alasan kenapa kita harus belajarOOP:
OOPsangatcocokuntukpembuatanaplikasiberskalabesar.
OOP merupakan gerbang untuk menguasai frameworkdenganmaksimal.
OOPdapatdigunakandiberbagaibahasapemrogramanyangmendukungOOPtidakhanyapadaPHP.
III.PemrogramanBerbasisObjek
39
DenganOOP kita dapatmenerapkan designpattern denganlebihmudah.
DanmasihbanyaklagialasanuntukbelajarOOP.
FiturDasarYangAdapadaOOPBerikutadalahbeberapafiturdasaryangharusadadalambahasapemrogramanyangmendukungkonsepOOP.
Enkapsulasi
Enkapsulasi adalah sebuah fitur yang menggabungkan antarafungsionalitas dan data untuk menyembunyikan informasi.Enkapsulasimemungkinkankitamenggunakanfungsidarisebuahobjek tanpa perlu mengetahui detail dari apa yang terjadi dalamfungsi tersebut. Ekapsulasi mengatur bagaimana kitamenggunakan objek, fungsi atau atribut apa saja yang dapatdigunakandanyangtidakdapatdigunakan.
Pewarisan
Pewarisan adalah sebuah fitur yang memungkinkan kitamenggunakan fitur dari suatu class tanpa perlu mendefinisikanulangsemuamethoddanproperty yangadapadaclass tersebut.
III.PemrogramanBerbasisObjek
40
Pewarisaninisangatbermanfaatjikakitainginmempunyaisebuahclassyangsecarafiturmiripdenganclasslainnamunadasebuahspesifikasi khusus yang spesifik dari class tersebut. DalamOOP,classyangmewariskansifatataufiturdisebutsebagaiparentclasssedangkanclass yang diwarisi sifat atau fitur disebut child class.Child class memiliki semua fitur yang ada pada parent classditambahdenganfiturspesifikmiliknyasendiri.
Sumber:http://www.adityawiraha.com
Polimorfisme
Polimorfismeadalahsebuahfituryangeratkaitannyadenganfitursebelumnya yaitu pewarisan. Fitur ini secara mudah adalahsebuah kemampuan dari objek untuk merespon atau mengolahsecaraberbedaterhadapinputyangsama.
Sayaagak kesulitan dalammenjelaskanbagaimanapolimorfismebekerja, namun nanti kita akan memahami hal tersebut ketikamembahastentangpewarisan,abstractclass,abtractmethoddaninterface. Untuk sekarang, Anda cukup pahami bahwa
III.PemrogramanBerbasisObjek
41
polimorfisme adalah salah satu fitur yang harus ada dalamOOPdimanasebuahmethodyangsamapadaclassyangberbedaakanmemberikanoutputyangberbedapulajikadigunakan.
Itulah3konsepdasaryangharusadapadabahasapemrogramanyang mendukung konsep OOP. Meski secara konsep sama,biasanya setiap bahasa pemrograman mempunyai caraimplementasiyangberbedadarifitur-fiturdiatas.
III.PemrogramanBerbasisObjek
42
IV.ClassdanObjectBab iniakanmenjabarkan tentangapa ituclass,bagaimanacaramembuat class, apa itu object, proses pembuatan object sertamemahamiperbedaanantaraclassdan_object.
ApaituClassSecara mudah, class adalah cetakan atau blueprint dari objek.Didalam class terdapat property danmethod. Dalam OOP, classmerupakan kerangka dasar yang harus dibuat sebelum kitamembuatrealobject.Untuklebihjelas,perhatikangambarberikut:
Sumber:cloudfront.net
Biladiperhatikandarigambardiatas,dapatkitasimpulkanbahwasebuah class dapat memiliki banyak object. Dalam prakteknya,sebuah class memang dapat memiliki banyak object tergantung
IV.ClassdanObject
43
dari berapa kali kita melakukan instansiasi. Instansiasi adalahprosesataumekanismepembuatanobjectdalamOOP.PadaOOP,instansiasi ditandai dengan penggunaan keyword new diikutinamadariclassyangakankitainstansiasi.
ContohClasspadaPHPUntuk membuat sebuah class pada PHP kita menggunakankeyword class diikutinamadariclass tersebut.Sebagai contohkita akan membuat sebuah class Mobil , maka kita dapatmembuatnyasebagaiberikut:
<?php
classMobil
{
}
Kita juga dapat menambahkanmethod atau function pada classsehinggaclass Mobilakanmenjadisepertiberikut:
<?php
classMobil
{
publicfunctionjalan()
{
echo'Mobilberjalan';
}
}
IV.ClassdanObject
44
Seperti yang sudah disebutkan pada pembahasan sebelumnya,bahwaselainmemilikimethod,class jugadapatmemilikiproperty.Untukmembuatpropertypadaclasskitadapatmenambah
Mudahsekalikan?kanlangsungsepertiberikut:
<?php
classMobil
{
public$roda;
publicfunctionjalan()
{
echo'Mobilberjalan';
}
}
AnatomiClassUntuk lebihmemahami tentangstrukturdariclass,mari kitacobamembedahkembaliclass Mobildiatas.
classMobil
{
}
Keyword class adalah sebuah keyword yang digunakan PHPuntuk menandai bahwa sebuah class didefinisikan. Keywordclass diikuti olehnamaclassdalamhal iniadalah Mobil dan
IV.ClassdanObject
45
diikuti oleh kurung kurawal {} . Didalam kurung kurawal itulahsegalamethodmaupunpropertydarisebuahclassdidefinisikan.
public$roda;
Keyword public menandaibahwasebuahpropertyataumethodmemilikivisibilitaspublic.DiPHPterdapat4visibilitasyaituprivate,protected,publicdandefault(tidakdidefinisikan).Tentangvisibilitasini akan dibahas secara tersendiri pada pembahasan berikutnya.Setelahkeyword publicdiikutiolehvariable $roda yangdalamkonsepOOPdisebutsebagaipropertyyaitusebuahvariableyangdapatdigunakandalamlingkupobject.
publicfunctionjalan()
{
echo'Mobilberjalan';
}
Pada contoh diatas, terdapat keyword public yang fungsinyatelah dijelaskan sebelumnya. Setelah keyword public diikutikeyword function yang digunakan untuk menandai bahwasebuah fungsi atau method didefinisikan. Keyword function
diikuti oleh namamethod yaitu jalan dan ikuti dengan tandakurung ()dilanjutkandengankurungkurawal {}.
Diantara kurung kurawal {} terdapat baris programyaitu echo'oleh _object_ Mobil berjalan'; , itulah yang disebut sebagaibadanmethod.Didalambadanmethod kita dapatmendefinisikanvariable,memanggilmethodlainataubahwamemanggilclasslain.Mudahnya, didalam method kita dapat mendefinisikan apa saja
IV.ClassdanObject
46
yang kita dapat definisikan diluar method. Namun yang perludiingat adalah bahwa segala variable yang didefinisikan dalammethod bersifat local sehingga hanya dapat digunakan padalingkupmethod tersebut. JikaAnda inginmembuatvariable yangdapatdiaksesdalamlingkupobjectmakagunakanlahproperty.
Diantaratandakurung () sebenarnyakitadapatmendefinisikanparameter yaitu variable yang akan dikirimkan ketika methodtersebutdipanggil.Untukcontohpenggunaanparameterakanlebihjelas lagipadapembahasan-pembahasanselanjutnya.Untuksaatini kita cukup tahu dulu bahwa sebuah method dapat memilikiparameter.
PembuatanObject(Instansiasi)Seperti yang sudah dijelaskan sebelumnya bahwa sebuah classhanyalah blueprint, maka untuk membuatnya menjadi real kitaperlu melakukan instansiasi. Proses instansiasi atau pembuatanobject pada PHP ditandai dengan keyword new diikuti dengannamaclass.Perhatikancontohberikut:
<?php
classMobil
{
public$roda;
publicfunctionjalan()
{
echo'Mobilberjalan';
}
IV.ClassdanObject
47
}
$avanza=newMobil();
Pada contoh diatas, kita menginstansiasi class Mobil danmemasukkannya kedalamvariable $avanza . Selain cara diatas,kita juga dapatmelakukan instansiasi tanpamenggunakan tandakurung ()setelahnamaclass.
<?php
classMobil
{
public$roda;
publicfunctionjalan()
{
echo'Mobilberjalan';
}
}
$avanza=newMobil;
Untuk memanggilmethod maupun property yang bersifat publicdapatdilakukansebagaiberikut:
<?php
classMobil
{
public$roda;
publicfunctionjalan()
{
IV.ClassdanObject
48
echo'Mobilberjalan';
}
}
$avanza=newMobil();
$avanza->roda=4;
echo$avanza->jalan();
echoPHP_EOL;
echo$avanza->roda;
echoPHP_EOL;
Sehinggabilaprogramdiatasdijalankanmakaakanmenghasilkanoutputsebagaiberikut:
phpMobil.php
Output:
Mobilberjalan
4
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/RR865.
IV.ClassdanObject
49
V.PropertydanMethodPembahasan yang akan dibahas pada bab ini adalah mengenaiapa itu property, apa itu method serta memahami apa ituparameterdanbagaimanacaramenyusunparameteryangbaik.
ApaituPropertySeperti yang sudah disinggung pada pembahasan sebelumnya,property adalah sebuah variable dapat digunakan dalam lingkupclass atau object. Property sering disebut juga sebagai segalasesuatuyangdimilikiolehclass.Propertymemiliki visibilitassertadapat memiliki nilai default. Perhatikan kembali contoh classMobilberikut:
<?php
classMobil
{
public$roda;
}
Padacontohdiatas,property $rodamemilikivisibilitaspublicdantanpa nilai default atau nilai awal. Untuk memberikan nilai awalpadaproperty $roda,kitadapatmelakukannyasebagaiberikut:
<?php
V.PropertydanMethod
50
classMobil
{
public$roda=4;
}
Pada contoh diatas, kita memberikan nilai awal pada property$roda dengannilai 4 .Makaketikakitamelakukan instansiasi,secara otomasiproperty $roda akan langsungmempunyai nilai4sebagaimanayangtelahkitadefinisikan.
<?php
classMobil
{
public$roda=4;
}
$avanza=newMobil();
echo$avanza->roda;
echoPHP_EOL;
Maka ketika program dieksekusi akan menghasilkan outputsebagaiberikut:
phpMobil.php
Output:
4
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/mYNf4.
V.PropertydanMethod
51
ApaituMethodJikaproperty adalah segala sesuatu yangdimiliki olehclass atauobject,makamethodadalahsegalasesuatuyangdapatdilakukanoleh class atau object. Sama seperti property, method jugamemilikivisibilitassertadapatmemilikiparameter.Parameterdapatmemilikinilaiawalataudefaultvalue.Bilaparametertidakmemilikidefault value maka parameter tersebut dianggap sebagaimandatoryparameter.Perhatikankembaliclass Mobilberikut:
<?php
classMobil
{
publicfunctionjalan()
{
echo'Mobilberjalan';
}
}
Padacontohdiatas,method jalan()memilikivisibilitaspublicdantidak memiliki parameter. Untuk menambahkan parameter, kitadapatmendefinisikannyasebagaiberikut:
<?php
classMobil
{
publicfunctionjalan($arah='depan')
{
echo'Mobilberjalankearah'.$arah;
}
}
V.PropertydanMethod
52
Pada contoh diatas, kitamenambahkan parameter $arah padamethod jalan()sertamenambahkandefaultvalue depan padaparametertersebut.Kitadapatmengetescodediatasdengancarasebagaiberikut:
<?php
classMobil
{
publicfunctionjalan($arah='depan')
{
echo'Mobilberjalankearah'.$arah;
}
}
$avanza=newMobil();
echo$avanza->jalan();
echoPHP_EOL;
echo$avanza->jalan('belakang');
echoPHP_EOL;
Ketikaprogramdiatasdijalankan,makaoutput-nyaadalahsebagaiberikut:
phpMobil.php
Output:
Mobilberjalankearahdepan
Mobilberjalankearahbelakang
V.PropertydanMethod
53
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/Klcha.
Bilakitaperhatikan,defaultvalueakanditimpaolehnilaiyangkitamasukkan.Dalamcontohdiatas,kitamemasukkannilai belakangsehinggaparameter $arah yang tadinyaberisi depan bergantimenjadi belakang . Dalam contoh real, default value dapatdigunakanuntukmengesetnilaiawal $port MySQLdarikoneksidatabase yang secaradefault adalah 3306 serta $host yangbiasanyamemilikinilaiawal localhost.Perhatikancontohberikut:
<?php
classKoneksi
{
publicfunctionconnect($username,$password,$host='l
ocalhost',$port=3306)
{
//Logickoneksi
}
}
Padacontohdiatas,parameter $usernamedan $passwordbersifatmandotory sehingga bila kita tidak memasukkan nilai tersebutketika memanggil fungsi connect() maka akan terjadi error.Perhatikancodedibawahini:
<?php
classKoneksi
{
publicfunctionconnect($username,$password,$host='l
ocalhost',$port=3306)
V.PropertydanMethod
54
{
//Logickoneksi
}
}
$koneksi=newKoneksi();
$koneksi->connect();
Bilakitamenjalankanprogramdiatasmakaakanterjadierror PHPFatal error: Uncaught ArgumentCountError: Too few arguments to
functionKoneksi::connect()sepertigambardibawahini:
Ini terjadi karena kita tidak memasukkan nilai $username dan$password sedangkan parameter tersebut bersifat mandatory.Untuk memperbaikinya, kita cukup memasukkan nilai sebagaiberikut:
<?php
classKoneksi
{
publicfunctionconnect($username,$password,$host='l
ocalhost',$port=3306)
{
//Logickoneksi
}
}
$koneksi=newKoneksi();
$koneksi->connect('root','');
V.PropertydanMethod
55
Bila kita mengeksekusi program diatas maka tidak akan terjadierrordantidakmengeluarkanoutputapapunkarenamemangkitahanyamembuatsebuahmethodkosong.
UrutanParameterpadaMethodSebenarnya tidak ada aturan yang baku yang mengatur tentangurutan parameter pada sebuah method. Kita bisa sajamendefinisikan sesuka kita parameter-parameter dari sebuahmethod, namun tips berikut dapat dipertimbangkan untukmenghindarierror danmemudahkan kita pada saat pemanggilanmethod.
Parameter yang mandatory diletakkan didepan parameteryangmemilikidefaultvalue.
Penamaan parameter harus spesifik dan memiliki arti yangjelassertamenunjukkankegunaandariparametertersebut.
Gunakantypehinting (akandibahaspadababterpisah)padaparameter untuk menghindari kesalahan ketika memberikannilaipadaparameter.
Bila Anda perhatikan, apakah class Koneksi telah menerapkantips diatas? Jawabannya adalah iya. Hanya saja, kita belummenerapkantypehintingkarenamemangbelumdibahas.
V.PropertydanMethod
56
VI.VisibilitasBab ini akan membahas tentang apa itu visibilitas dalam OOP,tingkatan visibilitas serta bagaimana mendefinisikan visibilitasdalambahasapemrogramanPHP.
ApaituVisibilitasVisibilitasadalahsalahsatufiturpentingyangadapadaOOP.Fiturini mengatur hak akses terhadap property maupunmethod darisebuahclass. Hak akses disini berbeda dengan hak akses padaaplikasi, hak akses disini adalah hak akses yang ada pada levelbahasapemrograman.
Visibilitas dalam OOP berperan penting dalam menjaminkeamananinformasiyangterdapatpadapropertymaupunmethod.Dengan fitur ini, programmer dapat membatasi dan mengaturprogrammer lainnya tentang bagaimana mengakses sebuahpropertyataumethoddarisebuahclassataufituryangdibuatnya.
TingkatanVisibilitaspadaPHPDalambahasaPHP,Visibilitasdibedakanmenjadi4 yaituprivate,protected, public dan default atau tidak didefinisikan. Berikutadalahpenjelasanmasing-masingvisibilitas.
VI.Visibilitas
57
Private
Visibilitas private adalah visibilitas paling rendah pada OOP.Sebuah property atau method yang diberikan visibilitas privatemaka property atau method tersebut hanya dapat diakses darilingkupclassdimanapropertyataumethod tersebut didefinisikan.Untuk memberikan visibilitas private pada property ataumethodkitadapatmenggunakankeyword privatedidepanpropertyataumethod.Perhatikancontohberikut:
<?php
classMobil
{
private$roda=4;
privatefunctionjalan()
{
echo'Mobilberjalan';
}
}
$avanza=newMobil();
echo$avanza->jalan();
echoPHP_EOL;
echo$avanza->roda;
echoPHP_EOL;
Bilaprogramdiatasdijalankan,makaakanmunculerror PHPFatalerror:UncaughtError:CalltoprivatemethodMobil::jalan()from
context . Hal ini terjadi karena kita mencoba mengakses fungsi
VI.Visibilitas
58
jalan()yangmemilikivisibilitasprivatediluarlingkupclassyaitudipanggildarilingkupobject.Halyangsamajugaakanterjadipadaproperty $roda dimanaproperty tersebut jugamemilikivisibilitasprivate.
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/VQ18h.
Untuk mengakses property atau method dalam lingkup classdigunakan keyword $this yang akan dibahas secara spesifikpadababterpisah.Perhatikancontohberikut:
<?php
classMobil
{
private$roda=4;
privatefunctionjalan()
{
echo'Mobilberjalan';
}
publicfunctionjumlahRoda()
{
echo$this->roda;
}
}
$avanza=newMobil();
echo$avanza->jumlahRoda();
echoPHP_EOL;
VI.Visibilitas
59
Anda juga dapat menjalankan program diatas secara onlinedenganmembuka linkberikuthttps://3v4l.org/u9Z94.Bilaprogramdiatasdieksekusi,makaakanmunculoutput 4sebagaimananilaidariproperty $roda.
Protected
Sebuahproperty ataumethod yang diberikan visibilitasprotectedmaka property ataumethod tersebut dapat diakses dari lingkupclass dimana property atau method tersebut didefinisikan sertaturunandariclasstersebut.Untukmemberikanvisibilitasprotectedpada property atau method kita dapat menggunakan keywordprotected didepan property atau method. Perhatikan contohberikut:
<?php
classMobil
{
private$roda=4;
protectedfunctionjalan()
{
echo'Mobilberjalan';
}
publicfunctionjumlahRoda()
{
echo$this->roda;
}
}
$avanza=newMobil();
VI.Visibilitas
60
echo$avanza->jalan();
echoPHP_EOL;
Bilaprogramdiatasdijalankan,makaakanmunculerror PHPFatalerror: Uncaught Error: Call to protected method Mobil::jalan()
from context . Hal ini terjadi karena kita mencoba mengaksesfungsi jalan() yang memiliki visibilitas protected diluar lingkupclassyaitudipanggildarilingkupobject.
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/qPsFG.
Public
Visibilitas public adalah visibilitas tertinggi pada OOP. Sebuahproperty atau method yang diberikan visibilitas public makaproperty atau method tersebut dapat diakses baik dari lingkupclass maupun object. Untuk memberikan visibilitas public padaproperty ataumethod kita dapatmenggunakan keyword publicdidepan property ataumethod. Mari kita melihat kembali contohberikut:
<?php
classMobil
{
private$roda=4;
publicfunctionjumlahRoda()
{
echo$this->roda;
}
}
VI.Visibilitas
61
$avanza=newMobil();
echo$avanza->jumlahRoda();
echoPHP_EOL;
Method jumlahRoda()diberikanvisibilitaspublicsehinggamethodtersebut dapat diaksesdari luarclass yaitu dalam lingkupobject.Sehingga bila program diatas dieksekusi, maka akan munculoutput 4 sebagaimana nilai dari property $roda . Anda jugadapat menjalankan program diatas secara online denganmembukalinkberikuthttps://3v4l.org/umsp4.
Defaultatautidakdidefinisikan
Bilakitatidakmendefinisikanvisibilitaspadapropertyataumethodsecara eksplisit, maka PHP secara default akan memberikanvisibilitas pada property atau method tersebut dengan visibilitaspublic. Sehingga public function jumlahRoda() sama denganfunctionjumlahRoda()yaitusama-samamemilikivisibilitaspublic.
TipsVisibilitasUntuk menghindari kebingungan dan menjaga konsistensi codemakaadabaiknyaAndamenerapkantipsberikut:
Definisikanvisibilitassecaraeksplisit.
Gunakanvisibilitasprivateatauprotectedpadaproperty.
Hindari penggunaan visibilitaspublic padaproperty, gunakanhanyajikamemangbenar-benardiperlukan.
VI.Visibilitas
62
Batasivisibilitaspadamethodjikamemungkinkan.
VI.Visibilitas
63
VII.KonsepStatisdanKonstantaPada bab ini kita akanmembahasmengenai konsep static padaOOP,carapenerapannyahinggatingkatanvisibilitasnya.Selainitujuga kita akan membahas tentang konstanta dan bagaimanamendefinisikan konstanta menggunakan bahasa pemrogramanPHP.
ApaituKonsepStatisDalam pemrograman berbasis objek segala sesuatu diibaratkanatau dimodelkan dalam class dan untuk merealisasikannya kitaharus melakukan instansiasi. Sebagai contoh ketika kita inginmengetahui jumlahrodapadaclass Mobil kitaharusmelakukaninstansiasi class Mobil dan memasukkannya kedalam variablebaru kemudian kita memanggilnya method jumlahRoda() . Ituadalah aturan dasar dari konsep OOP dimana segala sesuatudimodelkankedalamclassatauobject.
Konsep statis ataustatic adalah sebuah konsep yang keluar dariaturan dasar dari konsepOOP tersebut. Pada konsep static kitatidakperlumelakukan instansiasi untukdapatmemanggil sebuahproperty ataumethod. Karena tidak perlu melakukan instansiasi,maka kita dapat langsung memanggil property atau method
VII.KonsepStatisdanKonstanta
64
dengan menggunakan nama class diikuti :: (scope resolutionoperation) diikuti property atau method( NamaClass::$propertyStatic).
ContohPenerapanKonsepStatisUntukmendefinisikansebuahpropertyataumethodmenjadistatis,kitamenggunakankeyword staticsepertipadacontohberikut:
<?php
classSinga
{
publicstatic$KAKI=4;
publicstaticfunctionlari()
{
echo'Singaberlari';
}
}
echoSinga::$KAKI;
echoPHP_EOL;
echoSinga::lari();
echoPHP_EOL;
Bila program diatas dijalankan maka akan menghasilkan outputsebagaiberikut:
phpSinga.php
Output:
4
VII.KonsepStatisdanKonstanta
65
Singaberlari
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/NQ0Fv.
Untukmengaksespropertyataumethodstatic pada lingkupclasskitadapatmenggunakan3carayaitudenganmenggunakannamaclassseperticaradiatas,menggunakankeyword self ataubisajugamenggunakankeyword static.Perhatikancontohberikut:
<?php
classSinga
{
publicstatic$KAKI=4;
publicfunctionkaki1()
{
echoSinga::$KAKI;
}
publicfunctionkaki2()
{
echoself::$KAKI;
}
publicfunctionkaki3()
{
echostatic::$KAKI;
}
}
$singa=newSinga();
echo$singa->kaki1();
echoPHP_EOL;
VII.KonsepStatisdanKonstanta
66
echo$singa->kaki2();
echoPHP_EOL;
echo$singa->kaki3();
echoPHP_EOL;
Bilaprogramdiatasdijalankanmakaoutputdarimethod kaki1(),kaki2 maupun kaki3 akan sama yaitu 4 . Anda dapatmenjalankanprogramdiatassecaraonline denganmembuka linkberikuthttps://3v4l.org/dABh0.
VisibilitaspadaKonsepStatisSamasepertipropertyataumethodpadaumumnyayangmemilikivisibilitas, property atau method yang statis pun tetap memilikivisibilitas.Tingkatanvisibilitasnyapunsamasepertipadapropertyatau method biasa yaitu private, protected, public dan default.Tidak ada perbedaan antara statis maupun non statis padapropertydanmethodselainpadacaramengaksesnyasaja.
ApaituKonstantaBila kita kembali ke pelajaran jaman SMA, konstanta adalahsebuahnilaiyangtidakberubahdantelahditetapkannilainyadarisejakawal.Contohkonstantaadalah PIyangnilainya 3.14adajugagayagravitasiyangnilainya 9.8.Tidakjauhberbedadengankonsepdiatas,padaOOPkonstantaadalahsebuahnilaiyangtidakdapatdirubahselamaprosesruntime.
VII.KonsepStatisdanKonstanta
67
ContohPenerapanKonsepKonstantaUntuk mendefinisikan sebuah konstanta kita menggunakankeyword const dan untuk mengakses konstanta didalam classkita menggunakan keyword self dan diluar class kitamenggunakannamaclass.Perhatikancontohberikut:
<?php
classLingkaran
{
publicconstPI=3.14;
publicfunctionluas($jari)
{
echoself::PI*$jari*$jari;
}
}
$lingkaran=newLingkaran();
echoLingkaran::PI;
echoPHP_EOL;
$lingkaran->luas(7);
echoPHP_EOL;
VII.KonsepStatisdanKonstanta
68
Bila program diatas dijalankan maka output-nya adalah sebagaiberikut:
Output:
3.14
153.86
Anda dapat menjalankan program diatas secara online denganmembukalinkberikuthttps://3v4l.org/IW70B.
VisibilitaspadaKonsepKonstantaPadaPHP7.1,PHPmenambahkanfiturbarupadakonstantayaituvisibilitas sehingga kita dapat memberikan visibilitas padakonstanta seperti halnya property dan method. Tingkatanvisibilitasnya pun sama seperti pada property ataumethod yaituprivate,protected,publicdandefault.
VII.KonsepStatisdanKonstanta
69
VIII.Keyword$thisdanself
Bab ini akan membahas salah satu fitur yang wajib ada dalambahasapemrogramanyangmendukungkonsepOOPyaitu $thisdan self serta perbedaan keduanya dalam bahasapemrogramanPHP.
Apaitu$this
Pada pembahasan sebelumnya kita sudah cukup banyakmenggunakankeyword $this ketikamemanggilproperty dalamlingkup class seperti pembahasan tentang visibilitas. Perhatikankembalicontohdibawahini:
<?php
classMobil
{
private$roda=4;
publicfunctionjumlahRoda()
{
echo$this->roda;
}
}
$avanza=newMobil();
VIII.Keyword$thisdanself
70
echo$avanza->jumlahRoda();
echoPHP_EOL;
Padapemrogramanberbasisobjek,konsepkeyword $this pastiada walaupun cara penulisan dan atau syntax-nya mungkinberbeda.PadaOOP,keyword $thisadalahsebuahvariableyangmerujukpadaobjectyangdiinstansiasi.Variable $this nantinyaakandigantidenganvariablehasilinstansiasiketikasebuahobjectterbentuk.Padacontohdiatas,variable $this akandiganti olehvariable $avanza . Untuk lebih memahaminya, mari kita lihatcontohdibawahini:
<?php
classPrinter
{
private$content;
publicfunctionsetContent($content)
{
$this->content=$content;
}
publicfunctioncetak()
{
echo$this->content;
}
}
$printer1=newPrinter();
$printer1->setContent('Akuprintersatu');
$printer1->cetak();
echoPHP_EOL;
$printer2=newPrinter();
VIII.Keyword$thisdanself
71
$printer2->setContent('Akuprinterdua');
echo$printer2->cetak();
echoPHP_EOL;
$printer1->cetak();
echoPHP_EOL;
Bila program diatas dieksekusi, maka akan menghasilkan outputsebagaiberikut:
phpPrinter.php
Akuprintersatu
Akuprinterdua
Akuprintersatu
Anda juga dapat menjalankan program diatas secara onlinedenganmembukalinkberikuthttps://3v4l.org/vKJct.
Dari contohdiatas dapat kita pahami bahwakeyword $this itumerujuk pada spesifik object dan tidak bercampur antara satuobject dengan object lainnya. Ini dibuktikan dengan hasil outputantara $printer1 dan $printer2 yang berbeda walaupunkeduanya sama-sama menginisiasi class Printer . Nilai $content pada $printer1 tidak akan tertimpa oleh nilai$content pada $printer2 karena keduanyamerupakanobjectyangberbeda.
Secara mudah, keyword $this adalah sebuah keyword yangdigunakan untuk merujuk pada obejct yang belum diketahui dandigunakan untuk mempermudah kita dalam menuliskan code.Keyword $thishanyadapatdiaksesdariinternalclassdantidak
VIII.Keyword$thisdanself
72
dapatdiaksesdari luarclassataupada lingkupobject.Selain itu,keyword $this bersifat read only seperti halnya konstantasehinggakitatidakdapatmengubahnilainya.
Apaituself
Sama seperti keyword $this , keyword self juga memilikipengertiandanfungsiyangsama.Yangmembedakannyadengankeyword $this adalahbahwakeyword self hanyadigunakanuntuk memanggil property ataumethod yang bersifat statis danjuga untuk memanggil konstanta. Perhatikan kembali contohberikut:
<?php
classLingkaran
{
constPI=3.14;
publicfunctionluas($jari)
{
echoself::PI*$jari*$jari;
}
}
$lingkaran=newLingkaran();
echoLingkaran::PI;
echoPHP_EOL;
VIII.Keyword$thisdanself
73
$lingkaran->luas(7);
echoPHP_EOL;
Samasepertikeyword $this ,keyword self jugahanyadapatdigunakan pada lingkup class dan tidak dapat digunakan padalingkupobject.
VIII.Keyword$thisdanself
74
IX.ReturnValueSetelahmembacabab ini diharapkanAndaakanmemahamiapaitu return value dan bagaimana caramendefinisikan return valuemenggunakanbahasapemrogramanPHP.
ApaituReturnValueSebelum kita mengenal apa itu return value, ada baiknya sayasedikitberceritasebagaipenggandaiandarireturnvalue.
Muhammadadalahseorangpetanikedelai.Iamenanambenihkedelai kemudian merawatnya hingga benih itu tumbuh.Setelahtumbuh,iamemupuknyahinggapohonkedelaiitusiapuntukdipanen.
Muhammad kemudian memanen kedelai tersebut setelahdirasakedelaitersebutsiapuntukdipanen.
Daripengandaiandiatas,kitadapatmengibaratkanbahwaprosesmenanam hingga panen adalah sebuah proses yang terjadididalammethod atau fungsi. Sedangkan kedelai yang dihasilkanadalahreturnvaluedariprosestersebut.
DalamOOP, return value adalah sebuah nilai yang dikembalikanoleh sebuahmethod ketikamethod tersebut dipanggil. Tipe datadari return value tidak harus sama dengan tipe data yang
IX.ReturnValue
75
dimasukkan melalui parameter. Bahkan ketika sebuah methodtidak memiliki parameter sekalipun,method tersebut tetap dapatmengembalikanreturnvalue.
ContohPenggunaanReturnValueUntuk membuat sebuah method dapat mengembalikan sebuahnilai kita menggunakan keyword return . Perhatikan kembalicontohclass Lingkaranberikut:
<?php
classLingkaran
{
constPI=3.14;
publicfunctionluas($jari)
{
echoself::PI*$jari*$jari;
}
}
$lingkaran=newLingkaran();
$lingkaran->luas(7);
echoPHP_EOL;
Pada class Lingkaran diatas, method luas() langsungmengeluarkan output berubah echo hasil dari perkalian rumusluas lingkaran.Bila kitahendakmengubahagarmethod luas()tersebutmengembalikan return valuemaka kita harusmenggantiechodengankeyword returnseperticontohdibawahini:
IX.ReturnValue
76
<?php
classLingkaran
{
constPI=3.14;
publicfunctionluas($jari)
{
returnself::PI*$jari*$jari;
}
}
$lingkaran=newLingkaran();
echo$lingkaran->luas(7);
echoPHP_EOL;
Kitajugadapatmemasukkanreturnvaluekedalamsebuahvariablesebagaiberikut:
<?php
classLingkarancara-penggunaan
{
constPI=3.14;
publicfunctionluas($jari)
{
returnself::PI*$jari*$jari;
}
}
$lingkaran=newLingkaran();
$luas=$lingkaran->luas(7);
echo$luas;
IX.ReturnValue
77
echoPHP_EOL;
Bagaimanadapatdipahamikan?Mudahbukan?MarikitamasukiduniaOOPPHPlebihdalamlagi.
IX.ReturnValue
78
X.ConstructordanDestructorPadabab ini akandibahasapa ituconstructor, apa itudestructordanbagaimanacarapenggunaannyapadabahasapemrogramanPHP.
ApaituConstructorConstructoradalahsebuahmethodkhususyangdieksekusiketikasebuah class diinstansiasi. Constructor digunakan untukmempersiapkan object ketika keyword new dipanggil. Dalamconstructorkitadapatmelakukanapapunyangkitadapat lakukanpadamethodbiasanamuntidakbisamengembalikanreturnvalue.
Muncul pertanyaan, kenapa constructor tidak dapatmengembalikan return value? Ya jelas lah tidak bisamengembalikan return value, kan keyword new itu sudahmengembalikanberupaobjectdariclassyangdiinstansiasi.Masakemudianconstructormengembalikanlaginilaiyangsesuai?
Misalnya,kitapunyaclass A makaketikamenginisiasiclass Atersebutdengankeyword new kedalamvariable $a maka saatitu sebenarnya telah mengembalikan nilai berupa object A kedalamvariable $atersebut.
Bagaimana jadinya jika didalam constructor kita dapatmengembalikan nilai dan kemudianmembuat constructor denganmengembalikannilaiinteger 1misalnya.Makayangterjadiketika
X.ConstructordanDestructor
79
kita melakukan instansiasi class A dan fungsi constructordipanggil,alih-alihkitamendapatkanobject Ayangadakitajustrumendapatkaninteger 1.
Bagaimanasudahjelaskankenapacontructor hanyadapatberisilogictapitidakbisamengembalikanreturnvalue?
ContohPenggunaanConstructorUntukmembuat constructor kita harus membuatmethod dengannama __construct().Perhatikancontohberikut:
<?php
classConnection
{
private$host;
private$port;
private$username;
private$password;
private$database;
publicfunction__construct($username,$password,$datab
ase,$host='localhost',$port=3306)
{
$this->host=$host;
$this->port=$port;
$this->username=$username;
$this->password=$password;
$this->database=$database;
X.ConstructordanDestructor
80
}
publicfunctionconnect()
{
returnnewPDO(sprintf('mysql:host=%s;port=%d;dbname
=%s',$this->host,$this->port,$this->database),$this->use
rname,$this->password);
}
}
$connection=newConnection('root','aden','quiz');
$pdo=$connection->connect();
Pada contoh diatas, kita membuat class Connection yangberfungsi untuk melakukan koneksi ke RDBMS MySQL denganmenggunakandriver PDO(PHPDataObjects).Class Connectionmempunyai constructor yang memiliki 5 parameter dimana 3diantaranyabersifatmandatory.Ketikakitamelakukaninstansiasi,makaperlumemasukkanparameterkeclass Connection sepertipadacontohdiatas.
Muncul sebuah pertanyaan, bagaimana bila kita tidakmendefinisikan constructor seperti pada contoh-contohsebelumnya? Sebenarnya, secara default PHP akan membuat"virtual"constructortanpaparameterdantanpalogicjikakitatidakmendefinisikan constructor secara eksplisit. Perhatikan contohdibawahini:
<?php
classConnection
{
publicfunction__construct()
X.ConstructordanDestructor
81
{
}
}
Kira-kira seperti itulah yang terjadi jika kita tidak mendefinisikanconstructor secara eksplisit pada class yang kita buat. Namunseperti yang saya jelaskan diatas, constructor tersebut bersifatvirtualsehinggatidakbenar-benarada.
ApaituDestructorDestructor adalah sebuahmethod khusus yang dieksekusi ketikasebuah object dihapus dari memory. Secara mudah, destructoradalahkebalikandariconstructor.Samasepertipadaconstructor,PHP juga akan membuat destructor tanpa parameter dan tanpalogic jika kita tidak mendefinisikan destructor secara eksplisit.Berbeda dengan constructor yang dapat memiliki parameter,destructor tidakdapatmemiliki parameter danhanyadapat berisilogic.
Kenapa desctrutor tidak dapat memiliki parameter? Ya karenabagaimanacaranyakitamemasukkansebuahnilaisesaatsebelumobjectdihancurkan?
ContohPenggunaanDesctructorUntuk membuat desctrutor kita harus membuat method dengannama __destruct().Perhatikancontohberikut:
X.ConstructordanDestructor
82
<?php
classConnection
{
publicfunction__destruct()
{
echo'Objectdihapusdarimemory';
}
}
$connection=newConnection();
unset($connection);
Padacontohdiatas, ketika fungsi unset() dijalankanolehPHP,maka akan mengeluarkan output Object dihapus dari memory
sebagaimanayangdidefinisikanpadamethod __destruct().
Bila tidak ada sesuatu yang khusus yang harus dilakukan ketikaobject dihapus darimemory, sebaiknya kita tidak perlumembuatsebuahdestructorsecaraeksplisit.
JanganBilangSiapa-SiapaSaya tidak tahu apakah ini sebuah fitur ataukah sebuah bug,namun pada PHP kita dapat melakukan pemanggilan fungsi __construct() maupun __destruct() dari sebuah object.Perhatikancontohberikut:
<?php
classConnection
{
X.ConstructordanDestructor
83
publicfunction__destruct()
{
echo'Objectdihapusdarimemory';
}
}
$connection=newConnection();
$connection->__destruct();
unset($connection);
ApakahAndaberfikirketikakitamemanggilfungsi __desctruct()secara langsungmaka object akan dihapus darimemory? Kalaujawaban Anda adalah iya, maka Anda salah besar! Buktinyavariable $connection tetap dapat di- unset() dan bukanundefinedvariable.
Pada PHP, kita dapat memanggil fungsi __destruct() maupun__construct()secaralangsungsebagaimanafungsibiasa.Syaratagar fungsi tersebut dapat dipanggil adalah Anda harusmendefinisikannya secara eksplisit. Bagi saya ini sangatmenyebalkan,kenapa?Perhatikancontohberikut:
<?php
$a=new\DateTimeImmutable();
$b=$a->add(new\DateInterval('P1D'));
echo$b->format('Y-m-d');
echoPHP_EOL;
echo$a->format('Y-m-d');
echoPHP_EOL;
X.ConstructordanDestructor
84
Menuruthalamandokumentasiresminya,class DateTimeImmutableadalah sebuah class yang seperti class DateTime namun nilaiyang ada didalamnya tidak dapat berubah sesaat setelah kitamelakukan instansiasi. Alih-alih mengubah nilai, DateTimeImmutable justru mengembalikan objectDateTimeImmutablebaruketikakitamencobauntukmengubahnilaiyangadadidalamnya.
Pada contoh diatas misalnya, variable $a adalah object dariDateTimeImmutable lalu kemudian kita mencoba mengubahnyadengan menambahkan 1 hari dari tanggal sekarang denganmemanggilmethod add().Sesuaidengandokumentasiresminya,makaketikakitamemanggilmethod add()yangterjadibukannyanilaiobject $ayangberubah,tapikitajustrumendapatkanobjectbaruyaitu $bdengannilaibaru.
phptest_dateimmutable.php
Output:
2018-01-20
2018-01-19
Hal diatas adalah behavior yang benar dari DateTimeImmutable ,namun karena kita tahu bahwa constructor dapat kita panggilwalaupun object telah terbentuk, mari kita coba rubah nilai dariobject $ayangseharusnyatidakdapatberubahitu.
<?php
$a=new\DateTimeImmutable();
X.ConstructordanDestructor
85
$b=$a->add(new\DateInterval('P1D'));
echo$b->format('Y-m-d');
echoPHP_EOL;
echo$a->format('Y-m-d');
echoPHP_EOL;
$a->__construct($b->format('Y-m-d'));
echo$a->format('Y-m-d');
echoPHP_EOL;
Bila kita jalankan program diatas,maka hasilnya adalah sebagaiberikut:
phptest_dateimmutable.php
Output:
2018-01-20
2018-01-19
2018-01-20
Dan akhirnya, DateTimeImmutable yang seharusnya tidak dapatberubahnilainyapundapatkitarubahnilainya.
Meski hal tersebut (memanggil fungsi __construct maupun__destruct secara langsung) tidak wajar dilakukan, namun haltersebut dapat dilakukan dan justru dengan itulah kita dapatmelakukansesuatuyangseharusnyatidakdapatdilakukan.
X.ConstructordanDestructor
86
XI.EnkapsulasiEnkapsulasi adalah salah satu fitur yang ada pada bahasapemrograman yang mendukung OOP yang memungkinkan kitamengaturbagaimanasebuahobjectdipresentasikan.Babiniakanmembahas tentang enkapsulasi secara lebih dalam dan mudahdipahami.
ApaituEnkapsulasiEnkapsulasiadalahsebuahmekanismepenyembunyian informasipada property atau method dalam class. Enkapsulasi dalamkonsep OOP diwujudkan dalam visibilitas yang telah kita bahassebelumnya.Dengan enkapsulasi, kita bisamengatur bagaimanasebuahpropertydisetataudiaksesnilainya.Bahkankitajugabisamengaturbagaimanasebuahmethoddiaksesataudipanggil.
PenerapanEnkapsulasiEnkapsulasi memungkinkan kita mengatur bagaimana programatau object lain berinteraksi dengan object yang kita buat.Sehingga dengan enkapsulasi kita bisa membuat pembatasanakses sekaligus mengatur cara mengakses property maupunmethod.Perhatikankembalicontohberikut:
<?php
XI.Enkapsulasi
87
classConnection
{
private$host;
private$port;
private$username;
private$password;
private$database;
publicfunction__construct($username,$password,$datab
ase,$host='localhost',$port=3306)
{
$this->host=$host;
$this->port=$port;
$this->username=$username;
$this->password=$password;
$this->database=$database;
}
publicfunctiongetConnection()
{
returnnewPDO(sprintf('mysql:host=%s;port=%d;dbname
=%s',$this->host,$this->port,$this->database),$this->use
rname,$this->password);
}
}
Pada class Connection kita mengatur bahwa ketika object Connection dibuat, kita harus memasukkan parameter$username , $password dan seterusnya. Setelah itu, kita hanya
XI.Enkapsulasi
88
bisa mengakses method getConnection() sementara semuapropertyyangada tidakdapatkitaaksessecara langsungkarenabersifatprivate.
<?php
classMobil
{
private$roda=4;
protectedfunctionroda()
{
return$this->roda+2;
}
publicfunctiongetRoda()
{
return$this->roda();
}
}
Pada class Mobil diatas, kita memperbolehkan object untukmengaksesmethod getRoda() dimanamethod tersebut ternyatamemanggilmethod roda()danmengembalikannilaidarimethodtersebut. Object Mobil tidak diberikan cara untuk bisamemanipulasi nilai darivariable $roda sehinggaobject tersebutcuma bisa menerima hasil pemrosesan darimethod getRoda()tanpabisaberbuatapa-apa.
Kesimpulan yang bisa kita peroleh adalah bahwa enkapsulasimemberikan kebebasan kepada kita untuk mengatur perilakuobjectdariclassyangkitabuat.
XI.Enkapsulasi
89
XI.Enkapsulasi
90
XII.PewarisanPada bab ini akan dibahas apa itu pewarisan dan bagaimanamenerapkanpewarisanpadabahasapemrogramanPHP.
ApaituPewarisanPewarisanadalahmekanismepemberiansifatmaupuncirikhususdari induk (parent) kepada keturunannya (child). Bila kitahubungkan dengan konsep pada OOP maka pewarisan adalahmekanismepemberikanpropertymaupunmethoddariparentclasskepadachildclass.
Propertydanmethodyangdiwariskanadalahproperty,methoddankonstanta yang mempunyai visibilitas protected dan publicsebagaimanayangtelahdibawahpadababsebelumnya.
PenerapanPewarisanUntukmembuatsebuahclassmenjadi turunandariclass lainnya,kitamenggunakankeyword extendssebagaiberikut:
<?php
classHewan
{
private$jenis;
XII.Pewarisan
91
publicfunctionsetJenis($jenis)
{
$this->jenis=$jenis;
}
publicfunctiongetJenis()
{
return$this->jenis;
}
}
classKambingextendsHewan
{
}
classHarimauextendsHewan
{
}
classSingaextendsHewan
{
}
$kambing=newKambing();
$kambing->setJenis('Herbivora');
$harimau=newHarimau();
$harimau->setJenis('Karnivora');
$singa=newsinga();
$singa->setJenis('Karnivora');
echo$kambing->getJenis();
echoPHP_EOL;
echo$harimau->getJenis();
echoPHP_EOL;
echo$singa->getJenis();
XII.Pewarisan
92
echoPHP_EOL;
Padacontohdiatas,secaraotomatisclass Kambing, HarimaudanSinga memiliki semua method yang ada pada class Hewansehingga bila program diatas dijalankanmaka output-nya adalahsebagaiberikut:
phpHewan.php
Output:
Herbivora
Karnivora
Karnivora
PadabahasapemrogramanPHPtidakdikenalmultiple inheritancesehingga kita setiap child class hanya boleh memiliki 1 parentclass.Perhatikancontohberikut:
<?php
classHewan
{
private$jenis;
publicfunctionsetJenis($jenis)
{
$this->jenis=$jenis;
}
publicfunctiongetJenis()
{
return$this->jenis;
}
}
XII.Pewarisan
93
classMamalia
{
publicfunctionmenyusui()
{
echo'miksusuaaahhh';
}
}
classKambingextendsHewan,Mamalia
{
}
$kambing=newKambing();
Bila program diatas dijalankan maka yang terjadi adalah errorsyntaxkarenasetelahpadabaris ,Mamalia seharusnyaadalahtandakurungkurawanbuka {sepertigambarberikut:
Namun kita dapat menggunakan nested inheritance untukmengakalihaltersebutsepertitampakpadacontohdibawahini:
<?php
classHewan
{
private$jenis;
publicfunctionsetJenis($jenis)
{
$this->jenis=$jenis;
}
XII.Pewarisan
94
publicfunctiongetJenis()
{
return$this->jenis;
}
}
classMamaliaextendsHewan
{
publicfunctionmenyusui()
{
echo'miksusuaaahhh';
}
}
classKambingextendsMamalia
{
}
$kambing=newKambing();
$kambing->menyusui();
echoPHP_EOL;
XII.Pewarisan
95
XIII.OverloadingdanOverridingBab ini akanmenjabarkan apa ituoverloading, apa ituoverridingserta bagaimana menerapkannya pada bahasa pemrogramanPHP. Selain itu, pada bab ini juga akan dibahas tentang apa itukeyword parentdanbagaimanacarapenggunaannya.
Keywordparent
Pada pembahasan sebelumnya kita telah memahami tentang 2keywordyangmerujukpadaobjectyangdiinstansiasiyaitu $thisdan self .Padapembahasankali ini, sayaakanmenambahkansatulagikeywordyangmerujukpadaobjectyaitu parent.
Samaseperti $this dan self yangakanbersifat dinamisdanakan digantikan oleh real object ketika diinstansiasi, keywordparentpundemikian.Hanyasajabilakeyword $thisdan selfitu merujuk pada class dimana ia didefinisikan, maka keywordparentitumerujukkepadaparentclass.
Keyword parent hanya dapat digunakan pada child class danhanyadigunakanuntukmemanggilmethodyangadapadaparentclass.Keyword inieratkaitannyadenganpembahasanberikutnyayaituoverloadingdanoverriding.
XIII.OverloadingdanOverriding
96
ApaituOverloadingdanOverridingBerbedadengankonsepOOPpadaJavayangmemungkinkankitamendefinisikan ulang sebuahmethod dengan nama yang samaasalkan parameternya berbeda. Di PHP kita tidak diperbolehkanuntuk melakukan hal tersebut sehingga konsep overloading danoverridingdalamOOPPHPtidakdapatdipisahkan.
Selain itu karena kita tidak dapat mendefinisikan ulang sebuahmethod dengannamayangsama,makakonsepoverloading danoverriding dalam OOP PHP menjadi bergantung dengan konseppewarisanyangtelahkitabahassebelumnya.
Overriding secara mudah dipahami sebagai pendefinisian ulangsebuah method milik parent class oleh child class. Biasanyapendefinisian ulang tersebut diperlukan jika kita inginmenambahkan logic atau memodifikasi logic yang ada padamethod tersebut.Penambahanataumodifikasi logicpadamethoddichildclasstersebutitulahyangdisebutoverloading.
PenerapanOverloadingdanOverriding
XIII.OverloadingdanOverriding
97
Untuk lebih memahami konsep overloading dan overriding padaPHP,perhatikancontohberikut:
<?php
classPostRepository
{
publicfunctiongetLatestPost()
{
$posts=[
[
'id'=>1,
'title'=>'JudulPertama',
'content'=>'ContohContentPertama',
],
[
'id'=>2,
'title'=>'JudulKedua',
'content'=>'ContohContentKedua',
],
[
'id'=>3,
'title'=>'JudulKetiga',
'content'=>'ContohContentKetiga',
],
];
return$posts;
}
}
classSuffledPostRepositoryextendsPostRepository
{
publicfunctiongetLatestPost()
{
$posts=parent::getLatestPost();
XIII.OverloadingdanOverriding
98
shuffle($posts);
return$posts;
}
}
$postRepository=newPostRepository();
print_r($postRepository->getLatestPost());
$suffledPostRepository=newSuffledPostRepository();
print_r($suffledPostRepository->getLatestPost());
Pada contoh diatas kita mempunyai dua class yaitu PostRepository dan SuffledPostRepository , dimana classSuffledPostRepositoryadalahturunandariclass PostRepository.Pada class SuffledPostRepository kita meng-override methodgetLatestPost() danmenambahkan logic yaitumengacak indexdari $posts . Penambahan logic inilah yang disebut denganoverloading.
$posts=parent::getLatestPost();
Padabariscodediatas,kitamemanggilmethod getLatestPost()milikclass PostRepositorysebagaimanatelahsayajelaskanpadapembahasanpenggunaankeyword parent.Keyword parentkitagunakankarenapahamdan tahudenganpastibahwa logicpadaparentclasssudahsesuaidenganapayangkitainginkansehinggakita tidak perlu menulis ulang semua code tersebut. Kita hanyaperlu sedikit perubahan untuk membuatnya sesuai dengankebutuhan.
XIII.OverloadingdanOverriding
99
Untukmelihatoutput dari program diatas, Anda dapat membukalinkberikuthttps://3v4l.org/R1UsN.
Agar semkain memahami tentang override dan overload,perhatikancontohdibawahini:
<?php
classConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
thrownewRuntimeException('Andaharusmengimplement
asikanmethodconnect()sesuaidengandatabasedriveryangA
ndagunakan.');
}
}
classMySQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
/**
*CeritanyainilogickoneksikedatabaseMySQL
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
classPostgreSQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
XIII.OverloadingdanOverriding
100
$host='localhost')
{
/**
*CeritanyainilogickoneksikedatabasePostgreSQ
L
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
Berbeda dengan contoh sebelumnya dimana kita dapatmenggunakan keyword parent untuk mengambil logic padaparentclass,padacontohdiatas,parentclassmemaksakitauntukmembuat logic sendiri sesuai dengan database driver yang kitagunakan. Ini terjadi karena saya men-trigger exceptionmenggunakan keyword throw (pembahasan tentang exceptiondankeyword throw akan dibahas pada bab terpisah) sehinggaakan memicu error ketika method connect() pada classConnectiondipanggil.
Bagaimana sudah dapat dipahami bagaimana override danoverload bekerja? Untuk memaksa child classmengimplementasikan logic-nya sendiri seperti cara diatassebenarnya tidak benar karena padaOOPada namanya konsepabstract yang akan dibahas setelah pembahasan override danoverloadini.
XIII.OverloadingdanOverriding
101
XIV.AbstractClassdanAbstractMethodPadababiniakandibahassecaralebihmendalamtentangapaituabstract class, apa itu abstract method dan bagaimana carapenerapannyamenggunakanbahasapemrogramanPHP.
ApaituAbstractClassAbstractclass adalahsebuahclass dalamOOPyang tidakdapatdiinstansiasiataudibuatobject-nya.Abstractclassbiasanyaberisifitur-fitur dari sebuah class yang belum implementasikan. Sepertipadapembahasansebelumnyatentangclass Connectiondimanakita harus membuat implementasi dari class tersebut denganmeng-extends-nya menjadi MySQLConnection dan PostgreSQLConnection . Karena abstract class harusdiimplementasikanmelaluiprosespewarisan,makadalamabstractclassberlakuaturan-aturanyangadapadakonseppewarisanyangtelahkitabahassebelumnya.
Didalamsebuahabstract class kita dapatmembuatproperty danmethodyangnantinyadapatdigunakanolehchildclass.Tentusajapropertydanmethodyangdapatdigunakanolehchildclassadalahpropertydanmethodyangmemilikivisibilitasprotecteddanpublic.
XIV.AbstractClassdanAbstractMethod
102
Apakah kita dapat mendefinisikan property dan method denganvisibilitasprivate padaabstract class? Tentu saja bisa, dan tidakada yang melarangnya. Biasanya property atau method padaabstract class dibuat private karena kita tidak ingin child classdapat mengakses property atau method tersebut atau kitamembuatcaralainuntukmengaksesprivateproperty.
ApaituAbstractMethodSama seperti abstract class, abstract method adalah sebuahmethod yang harus diimplementasikan oleh child class.Abstractmethodhanyaadapadaabstractclassdaninterface(akandibahassecaraterpisah).
Bila biasanya setiap method yang kita buat pasti mempunyaikurangkurawal {},padaabstractmethodhaltersebuttidakdapatditemuikarenaabtractmethod adalah sebuahmethod yang tidakmemilikibodyataubadanmethod.
Pada child class, abstract method harus didefinisikan ulang dankitatidakdapatmenggunakankeyword parent untukmemanggilabstract method pada parent class. Bila kita melakukan haltersebutmakaakanterjadierror.
KegunaanAbstractClassdanAbstractMethod
XIV.AbstractClassdanAbstractMethod
103
Saya banyak mendapat pertanyaan baik melalui WhatsAppmaupun pesan di Facebook tentang apa sih kegunaan abstractclass dan abstract method? Ada juga yang bertanya tentangkenapasebuahclassataumethodharusdibuatmenjadiabstract?Pembahasan kali ini saya dedikasikan untuk menjawabpertanyaan-pertanyaantersebut,walaupunpastinyatidakmungkindapatmemuaskansemuaorangyangbutuhpenjelasantentangitusemua.
Secaramudahabstractclassdanabstractmethod bergunauntukmemastikan child class memiliki fitur-fitur yang telah ditentukansebelumnya.Abstract class akan sangat berguna pada saat kitamembahas tentang type hinting atau parameter hinting. Denganabstract class dan abstract method kita bisa lebih percaya diriketikamemanggilsebuahmethodkarenadapatdipastikanmethodtersebutdimilikichildclass.
ContohPenggunaanAbstractClassdanAbstractMethodUntukmendefinisikansebuahclassataumethodmenjadiabstractkitamenggunakankeyword abstractdidepanclassataumethodtersebut. Untuk lebih jelasnya,mari kita ubah class Connectionyang sebelumnya telah kita buat dengan cara yang tidak benar.Perhatikancontohberikut:
<?php
abstractclassConnection
XIV.AbstractClassdanAbstractMethod
104
{
abstractpublicfunctionconnect($database,$username,$
password,$host='localhost');
}
classMySQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
/**
*CeritanyainilogickoneksikedatabaseMySQL
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
classPostgreSQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
/**
*CeritanyainilogickoneksikedatabasePostgreSQ
L
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
XIV.AbstractClassdanAbstractMethod
105
Dengan cara demikian, maka class MySQLConnection danPostgreSQLConnectiondipaksauntukmendefinisikanulangmethod connect() sama seperti tujuan utama kita membuat class Connection pada bab sebelumnya. Bila kita tidakmengimplementasikanmethod connect()makaakanterjadierror.Perhatikancontohberikut:
<?php
abstractclassConnection
{
abstractpublicfunctionconnect($database,$username,$
password,$host='localhost');
}
classMySQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
/**
*CeritanyainilogickoneksikedatabaseMySQL
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
classPostgreSQLConnectionextendsConnection
{
publicfunctionconnect($database,$username,$password,
$host='localhost')
{
/**
XIV.AbstractClassdanAbstractMethod
106
*CeritanyainilogickoneksikedatabasePostgreSQ
L
*
*Andatidakbisamenggunakan_keyword_`parent`un
tukmemanggil_method_`connect()`milik_parentclass_
*karenaakanmengakibatkanerror.
**/
}
}
classOracleConnectionextendsConnection
{
}
Pada contoh diatas, kita membuat sebuah child class Oracle
Connection yang extends dari class Connection tanpamengimplementasikanmethod connect() . Bila program diatasdijalankan maka akan terjadi error PHP Fatal error: Class
OracleConnectioncontains1abstractmethodandmustthereforebe
declared abstract or implement the remaining methods
(Connection::connect)sepertigambardibawahini.
PerluAndaketahui bahwadidalamabstract class kita jugadapatmembuatnonabstractmethodsebagaiberikut:
<?php
abstractclassAbstractTaxCalculator
{
private$previous;
abstractpublicfunctionmaxPtkp();
XIV.AbstractClassdanAbstractMethod
107
abstractpublicfunctionminPtkp();
abstractpublicfunctiontaxPercentage();
publicfunctiongetPrevious()
{
return$this->previous;
}
publicfunctionsetPrevious($taxCalculator)
{
$this->previous=$taxCalculator;
}
publicfunctioncalculate($ptkp)
{
$previousValue=0;
if($previous=$this->getPrevious()){
$previousValue=$this->getPrevious()->calculate
($previous->maxPtkp());
$ptkp-=$previous->maxPtkp();
}
return($this->taxPercentage()*$ptkp)+$previousV
alue;
}
publicfunctionisSupportPtkp($ptkp)
{
if($ptkp<$this->maxPtkp()&&$ptkp>=$this->minP
tkp()){
returntrue;
}
returnfalse;
}
XIV.AbstractClassdanAbstractMethod
108
}
Bagaimana apakah sudah lebih bisamemahami apa itu abstractclassdanabtractmethod?Contohdiatashanyalahsebuahilustrasidarisebuahabstractclassyangmemilikiabstractmethoddannonabstractmethod.
Pada pembahasan-pembahasan selanjutnya, kita akan semakinmemahami kegunaan abstract class dan abtract method secaralebih mendalam dengan melihat langsung penggunaan abstractclassdanabstractmethoddalamrealproject.
XIV.AbstractClassdanAbstractMethod
109
XV.InterfaceBabiniakanmenerangkantentangapaituinterfacedalamkonsepOOPsertabagaimanacarapenggunaannya.Selain itu jugapadabab ini akan membahas tentang trait, sebuah fitur yang dapatmembuatcodeyangkitabuatsemakinreusable,sertabagaimanabestpracticedalampenggunaaninterfacedantrait.
ApaituInterfaceApa yang Anda bayangkan jika mendengar kata interface?Mungkin yang terbayang adalah sebuah halaman web yanglengkap dengan biasa Anda sebut dengan istilah interface atauuser interface. Tapi dalam pemrograman berbasis objek, konsepinterface yang dimaksud sangat berbeda jauh dengan interfaceyangAndabayangkantersebut.
Dalam pemrograman berbasis objek, interface adalah sebuahclass yang semua method-nya adalah abstract method. Karenasemuamethod-nya adalah abstract method maka interface punharus diimplementasikan oleh child class seperti halnya padaabstract class. Hanya saja bila kita sebelumnya menggunakankeyword extends untuk mengimplementasikan sebuah abstractclass, maka pada interface kita menggunakan keywordimplementsuntukmengimplementasikansebuahinterface.
XV.Interface
110
ContohPenggunaanInterfaceUntuklebihmemahamibagaimanasebuahinterfacebekerja,cobaperhatikancontohberikut:
<?php
interfaceHewanInterface
{
publicfunctiongetJenis();
}
classKambingimplementsHewanInterface
{
publicfunctiongetJenis()
{
return'Herbivora';
}
}
classHarimauimplementsHewanInterface
{
publicfunctiongetJenis()
{
return'Karnivora';
}
}
classSingaimplementsHewanInterface
{
publicfunctiongetJenis()
{
return'Karnivora';
}
}
XV.Interface
111
Pada contoh diatas, kita memiliki interface HewanInterface danmempunyai1abstractmethodyaitu getJenis() sertamemiliki3implementasiyaitu Kambing , Harimau dan Singa .Setiapclassyang mengimplementasikan interface HewanInterface harusmembuat implementasi darimethod getJenis() seperti tampakpadacontohdiatas.
Selain itu juga kita dapat menggabungkan antara interface danabstractclasssebagaimanatampakpadacontohdibawahini:
<?php
interfaceHewanInterface
{
publicfunctiongetJenis();
}
abstractclassHewan
{
private$jenis;
publicfunctionsetJenis($jenis)
{
$this->jenis=$jenis;
}
publicfunctiongetJenis()
{
return$this->jenis;
}
}
classKambingextendsHewanimplementsHewanInterface
{
}
XV.Interface
112
classHarimauextendsHewanimplementsHewanInterface
{
}
classSingaextendsHewanimplementsHewanInterface
{
}
$kambing=newKambing();
$kambing->setJenis('Herbivora');
$harimau=newHarimau();
$harimau->setJenis('Karnivora');
$singa=newsinga();
$singa->setJenis('Karnivora');
echo$kambing->getJenis();
echoPHP_EOL;
echo$harimau->getJenis();
echoPHP_EOL;
echo$singa->getJenis();
echoPHP_EOL;
Denganmembuatcode seperti diatas,child class kita tidak perlumengimplementasikan abstract method milik interfaceHewanInterface karena sudah diimplementasikan oleh abstractclass Hewan.Dengancaratersebut,makacodekitasemakinrapi,mudah dibaca, dan reusable. Bila code diatas dijalankan, makaoutput-nyaadalahsebagaiberikut:
phpHewan.php
Output:
Herbivora
XV.Interface
113
Karnivora
Karnivora
Mungkin sampai disini Anda belum terlalu paham kenapa kitaharus membuat interface sementara kita bisa menggunakanabstractclasskarenakeduanyaterlihatsamasaja.Bahkandisatusisi,abstract class jauh lebihmenjanjikan karena kitamasih bisamembuat logic, sementara pada interface karena semuanyaadalahabstractmethod secara otomatis kita tidak diperkenankanmembuatlogicpadainterface.
Padaprateknya, interfacedanabstractclassmemiliki fungsi yangberbeda.Interfaceberfungsiuntukmemastikanchildclassmemilikifitur-fituryangtelahditetapkandalaminterface,sementaraabstractclassbergunauntukmemberikan fitur-fiturdasarpadachild classdimana fitur-fitur tersebut dapat digunakan secara bersama-samaolehchildclass.
Seperti contoh diatas, dimana inteface HewanInterface memilikifitur getJenis() , sementaraabstract class Hewan memberikanfitur setJenis() dan getJenis() untuk dapat dipakai secarabersama-samaolehchildclass Singa, Harimau,dan Kambing.
Tidak seperti pada pewarisan, pada interface kita dapatmenggunakan multiple interface sekaligus seperti pada contohberikut:
<?php
interfaceHewanInterface
{
publicfunctiongetJenis();
XV.Interface
114
}
interfaceMamaliaInterface
{
publicfunctionmenyusui();
}
abstractclassHewan
{
private$jenis;
publicfunctionsetJenis($jenis)
{
$this->jenis=$jenis;
}
publicfunctiongetJenis()
{
return$this->jenis;
}
}
classKambingextendsHewanimplementsHewanInterface,Mamal
iaInterface
{
publicfunctionmenyusui()
{
echo'nyusukambing';
}
}
classHarimauextendsHewanimplementsHewanInterface,Mamal
iaInterface
{
publicfunctionmenyusui()
{
echo'nyusumaung';
}
XV.Interface
115
}
classSingaextendsHewanimplementsHewanInterface,Mamalia
Interface
{
publicfunctionmenyusui()
{
echo'nyususinga';
}
}
MembukaWawasanDi eramilenial seperti sekarang ini penggunaan interface sangatmasif. Banyak framework dan library yang kalau kita maumembaca source code-nya maka akan mudah sekali bagi kitauntukmenemukaninterface.
Penggunaaninterface tidak lainkarenafituryangdimiliki interfaceitu sendiri yaitu sebagai hirarki tertinggi pada parameter casting(akan dibahas pada bab tersendiri) dimana setiap object yangmengimplementasikansebuahinterfaceakanvalidjikadimasukkankedalammethod yang menggunakan interface tersebut sebagaitypehintingatauparametercasting.
Seperti pada framework Laravel, dimana interface akan sangatmudah ditemukan pada folder Contracts seperti nampak padaGithubrepositoryLaravelberikut.
Pada paradigma pemrograman modern, ada istilah "interface ascontract" yang maksudnya adalah interface digunakan padaparameter casting sebagai pengikat bahwa object yang akan
XV.Interface
116
dimasukkankedalammethodpastimemilikifitur-fiturataumethod-methodyangdidefinisikanpadainterfacetersebut.
Sehingga dengan menggunakan interface tersebut sebagaiparamtercastingpadamethodmakadidalammethodtersebutkitabisa dengan percaya diri untuk menggunakan method-methodyang ada pada interface tanpa takut terjadi error undefined
method.
XV.Interface
117
XVI.MethodChainingTak terasa sudah 15 bab kita bahas dari awal buku ini hinggasekarang. Pada bab kali ini kita akan sedikit refreshing danmembahas sebuah konsep ringan dari OOP yang banyakditerapkanpadaframeworkyaitumethodchaining.
ApaituMethodChainingSeperti yang sudah dijelaskan diatas, Method Chaining adalahsalah satu konsep yang ada pada pemrograman berbasis objek.Method Chaining memungkinkan kita memanggilmethod secaraberantai dalam satu baris sekaligus. Fungsi method chainingadalah mempersingkat pemanggilan method yang tadinya kitaperlu memanggilmethod dalam beberapa baris, denganmethodchainingkitadapatmelakukannyadenganhanyasatubariscode.Perhatikanilustrasiberikut:
$mutator=newStringMutator('MuhamadSuryaIksanudin');
$mutator->bold();
$mutator->underline();
$mutator->italic();
$mutator->strike();
Denganmethod chaining kita dapat membuatnya menjadi lebihsingkatsepertipadailustrasiberikut:
XVI.MethodChaining
118
$mutator=newStringMutator('MuhamadSuryaIksanudin');
$mutator->bold()->underline()->italic()->strike();
Bagaimana terlihat lebih simpel bukan?Method chaining ini jugaumum digunakan pada framework salah satunya adalahCodeIgniter yang menggunakan metode method chaining padalibraryquerybuildermiliknya.
$this->db->select('*')->from('user')->where('id=1')->get()
;
Familiar kan dengan syntax diatas? Ingin tau bagaimana caramembuatnya?Pembahasanselanjutnyaakanmenjelaskansecaramen-detail bagaimana cara membuat method chaining danbagaimanasebuahmethoddapatdirangkaisepertiitu.
CaraPembuatanMethodChainingUntuk membuat sebuahmethod dapat dipanggil secara berantaiseperti padacontohdiatas, kitaperlumemahamikembali konsepreturn value dan penggunaan dari keyword $this . Keduanyadiperlukan karena untuk membuat method chaining kita perlumengembalikan object itu sendiri kepada pemanggil. Perhatikancontohberikut:
<?php
classStringMutator
{
XVI.MethodChaining
119
private$word;
publicfunction__construct($word)
{
$this->word=$word;
}
publicfunctionbold()
{
$this->word=sprintf('<b>%s</b>',$this->word);
return$this;
}
publicfunctionunderline()
{
$this->word=sprintf('<u>%s</u>',$this->word);
return$this;
}
publicfunctionitalic()
{
$this->word=sprintf('<i>%s</i>',$this->word);
return$this;
}
publicfunctionstrike()
{
$this->word=sprintf('<strike>%s</strike>',$this->
word);
return$this;
}
publicfunction__toString()
{
XVI.MethodChaining
120
return$this->word;
}
}
Pada contoh diatas, setiapmethod dalam class mengembalikanobject itu sendiri yang ditandai dengan return $this . Denganmengembalikan object itu sendiri maka kita bisa langsungmemanggilmethodyangadapadaobjecttersebutsecaralangsungdarimethod yang kita panggil sebelumnya. Kita bisa memanggilmethod bold() setelah memanggilmethod italic() secaraberurutansepertiberikut:
$mutator->italic()->bold();
Pada class StringMutator diatas saya menggunakan magicmethod __toString()yaitusebuahmethodyangakandieksekusisecara otomatis ketika kita mencoba mencetak object dengankeyword echo misalnya sehingga code kita menjadi semakinsingkatlagisepertipadacontohdibawahini:
echo(newStringMutator('Tebal'))->bold();
echo'<br/>';
echo(newStringMutator('TebaldanMiring'))->bold()->italic
();
echo'<br/>';
echo(newStringMutator('Tebal,Miring,danGarisBawah'))->
bold()->italic()->underline();
echo'<br/>';
echo(newStringMutator('Dicoret'))->strike();
echo'<br/>';
XVI.MethodChaining
121
Bila program diatas dijalankan dan dibukamelaluibrowsermakaoutput-nyaakannampaksebagaiberikut:
XVI.MethodChaining
122
XVII.PengelompokanBerkasBab ini akan membahas tentang bagaimana caramengelompokkan berkas padaOOPPHPagar aplikasi yang kitabangunlebihterorganisirdanlebihmudahdimaintain.
PengelompokanBerkaspadaOOPPHPKetika membangun sebuah aplikasi apalagi aplikasi tersebuttergolong besar, mengelompokkan aplikasi berdasarkan moduladalahsalahsatucaramengelompokkanberkas.Pengelompokkantersebut biasanya berdasarkan fungsionalitas dari sebuah moduldalamsebuahaplikasi.
Dalamparadigmapemrogramanberbasisobjek,pengelompokkanberkas berdasarkan fungsionalitasnya dimanakanpackaging ataupemaketan. Packinging biasanya dilakukan denganmengelompokkanbeberapaclass yangmasih satu fungsionalitasmenjadisatufolder.
Namun akan menjadi masalah jika ternyata didalam folder yangberlainanterdapatclassyangmemilikinamayangsama.Misalnyakitapunyaduafolderyaitu Httpdan Apidimanapadamasing-masing folder tersebut terdapat class Request yangmengimplementasikaninterface RequestInterface yangberfungsiuntukmenanganirequestpadamasing-masingfungsionalitas.
XVII.PengelompokanBerkas
123
Susunanfoldertersebutnampaksebagaiberikut:
Danberikutadalahisidarimasing-masingfile:
<?php
//filename:RequestInterface.php
interfaceRequestInterface
{
publicfunctionhandle();
}
<?php
//filename:Http/Request.php
classRequestimplementsRequestInterface
{
publicfunctionhandle()
{
echo'HandleHttpRequest';
}
}
<?php
//filename:Api/Request.php
classRequestimplementsRequestInterface
XVII.PengelompokanBerkas
124
{
publicfunctionhandle()
{
echo'HandleApiRequest';
}
}
Bila kitamembuat file pemanggil semisal index.php dengan isisebagaiberikut:
<?php
require__DIR__.'/RequestInterface.php';
require__DIR__.'/Http/Request.php';
require__DIR__.'/Api/Request.php';
$request=newRequest();
$request->handle();
echoPHP_EOL;
Makaketikakitamenjalankanfile index.phptersebutakanterjadierror karena PHP menganggap bahwa class Request telahdidefinisikansebelumnya.Pesanerror tersebutadalah PHP Fatalerror:CannotdeclareclassRequest,becausethenameisalready
inusesepertipadagambarberikut:
Untukmengatasihal tersebut,PHPmempunyaisebuah fituryangdisebut namespacing. Fitur ini akan dibahas lebih lanjut padabahasanberikutnya.
XVII.PengelompokanBerkas
125
Keywordnamespacedanuse
Konsep namespacing sebenarnya sangat erat kaitannya dengankehidupan kita. Konsep ini biasa dipakai oleh pengembangperumahanuntukmengelompokkanrumahberdasarkanlokasinya.Pengelompokan tersebut biasanya didasarkan pada lorong ataujalanyangadadiperumahantersebutyaitukemudiandisebutblok.
Blokinilahyangdisebutnamespacingdimanasetiapblokmemilikibeberapa rumah yang letaknya sama yaitu masih dalam satulorong. Rumah-rumah itu kemudian diberikan nomer yang samadengannomerrumahyangadapadabloklainsemisalpadablokAterdapat nomer rumah1, kemudianpadablokB jugaadanomerrumah 1. Walaupun memiliki nomer rumah yang sama, namuntidakakanterjadisalahalamat,karenawalaupunnomerrumahnyasamatapilokasibloknyaberbeda.
Penggelompokkan tersebut diimplementasikan oleh PHP dalambentuknamespace dimana dalamnamespace yang berbeda kitadapat mendefinisikan nama class yang sama. Untukmendefinisikan suatu namespace kita menggunakan keyword namespace dan untuk menggunakan suatu namespace kitamenggunakankeyword use.
ContohPenggunaanKeywordnamespacedanuse
XVII.PengelompokanBerkas
126
Seperti yang sudah dijelaskan diatas, keyword namespace danusedigunakanuntukmengemlompokkanclass.Untukmemahamibagaimana penggunaan keyword tersebut, mari kita kembali kecontoh sebelumnya dan memberikan namespace pada classRequestsebagaiberikut:
<?php
//filename:Http/Request.php
namespaceHttp;
useRequestInterface;
classRequestimplementsRequestInterface
{
publicfunctionhandle()
{
echo'HandleHttpRequest';
}
}
<?php
//filename:Api/Request.php
namespaceApi;
useRequestInterface;
classRequestimplementsRequestInterface
{
publicfunctionhandle()
{
echo'HandleApiRequest';
}
}
XVII.PengelompokanBerkas
127
Kita perlu menggunakan use ketika memanggil interface RequestInterface karena jika tidak maka interface RequestInterface akan dianggap bagian dari namespacetersebut. Hal ini akan mengakibatkan error karena interface RequestInterface tidak memiliki atau bukan bagian darinamespacetersebut.
Kemudian kita juga harus mengubah file index.php untukmenggunakan class Request sesuai dengan namespace yangtelahkitabuat.
<?php
require__DIR__.'/RequestInterface.php';
require__DIR__.'/Http/Request.php';
require__DIR__.'/Api/Request.php';
useHttp\Request;
$request=newRequest();
$request->handle();
echoPHP_EOL;
Jika program diatas dieksekusi maka output-nya adalah HandleHttpRequestsepertipadagambarberikut:
Selanjutnya, mari kita mencoba memanggil juga classApi\Request (penyebutannamaclass bersamaandengannamanamespace-nya disebut juga dengan FQCN atau Fully Qualified
XVII.PengelompokanBerkas
128
Class Name) pada index.php sehingga program kita akanberubahmenjadisebagaiberikut:
<?php
require__DIR__.'/RequestInterface.php';
require__DIR__.'/Http/Request.php';
require__DIR__.'/Api/Request.php';
useHttp\Request;
useApi\Request;
$request=newRequest();
$request->handle();
echoPHP_EOL;
$apiRequest=newRequest();
$apiRequest->handle();
echoPHP_EOL;
Bila kita menjalakan program diatas ternyata justru terjadi errorPHPFatalerror:CannotuseApi\RequestasRequestbecausethe
nameisalreadyinusesepertigambardibawah:
Untuk mengatasi error tersebut, kita dapat memanggil classmenggunakanFQCNtanpamenggunakankeyword usesehinggafile index.phpakanjadisepertiberikut:
<?php
require__DIR__.'/RequestInterface.php';
require__DIR__.'/Http/Request.php';
require__DIR__.'/Api/Request.php';
XVII.PengelompokanBerkas
129
$request=newHttp\Request();
$request->handle();
echoPHP_EOL;
$apiRequest=newApi\Request();
$apiRequest->handle();
echoPHP_EOL;
Dan bila kita jalankan kembali file index.php maka output-nyaadalahsebagaiberikut:
phpindex.php
Output:
HandleHttpRequest
HandleApiRequest
Padabanyakkasuskitaperlumenggunakanclasslainyangmasihsatunamespace,untukmenggunakanclass lain yangmasihsatunamespacekitatidakperlumenggunakankeyword usedandapatlangsungmenggunakannyasepertipadacontohberikut:
<?php
//filename:Http/OtherClass.php
namespaceHttp;
classOtherClass
{
private$request;
publicfunction__construct()
{
XVII.PengelompokanBerkas
130
$this->request=newRequest();
}
}
Padacontohdiatas,class Request otomatis akanmerujuk padaclass Http\Request sebab class Http\Request memilikinamespaceyangsamadenganclass OtherCassyaitu Http.
Memberikanaliasdengankeywordas
Meski kita dapat memanggil class menggunakan FQCN untukmengatasi error ketika menggunakan use dengan nama akhirclassyangsamasepertipadacontohdiatas.Namuncaratersebutkurangeleganbahkanakanmembuatcodeyangkitabuattampakjelek bila ternyata class yang akan kita gunakan memilikinamespaceyangpanjang.
Cara yang terbaik untuk mengatasi error tersebut diatas adalahdengan memberikan alias pada class yang kita gunakanmenggunakankeyword as .Sehinggacodepada file index.phpakanmenjadisepertiberikut:
<?php
require__DIR__.'/RequestInterface.php';
require__DIR__.'/Http/Request.php';
require__DIR__.'/Api/Request.php';
useHttp\RequestasHttpRequest;
useApi\RequestasApiRequest;
XVII.PengelompokanBerkas
131
$request=newHttpRequest();
$request->handle();
echoPHP_EOL;
$apiRequest=newApiRequest();
$apiRequest->handle();
echoPHP_EOL;
Bagaimanatampaklebihsimpeldanlebihnyamandibacabukan?
XVII.PengelompokanBerkas
132
XVIII.ParameterCastingdanReturnTypeDeclarationSetelahmembacabab inidiharapkanAndadapatmemahamiapaitu parameter casting dan apa itu return type declaration sertabagaimanacaramenggunakannyapadakasusnyata.
ApaituParameterCastingParameter casting atau yang biasa disebut juga dengan typehinting adalah sebuah cara untuk memastikan bahwa parameteryang dimasukkan kedalam sebuah method sesuai dengan tipedatayangtelahkitatentukan.Dengankatalain,typehintingadalahcara kita memaksa pemanggil method untuk memasukkanparameteryangtepatdansesuai.
Dari sisi developer, type hinting memberikan kepercayaan dirikarenakitadapatmemastikandanmenentukansecaraspesifiktipedatayangbisamasukkedalammethod.Hal iniakanmengurangikemungkinan error atau bug yang terjadi yang disebabkan olehkesalahan tipe data baik yang terjadi karena kesalahanpengolahan maupun kesalahan karena data yang salah yangberasaldaridatabase.
HinggaPHPversi5.6,PHPhanyamengenaltypehintinguntuktipedata array , callable , dan object (termasuk didalamnyainterface ). Baru pada PHP versi 7, PHPmenambahkan lebih
XVIII.ParameterCastingdanReturnTypeDeclaration
133
banyak typehinting,yaitudenganmenambahkandukunganuntuktipedata string, int, floatdan bool.Penambahantersebutsemakin menegaskan komitmen PHP untuk menjadi bahasapemrogramanyangtypesafety.
ScalarTypeHintingScalar type atau yang jugadisebut sebagaiprimitive type adalahtipedatayanghanyamemuatsatudatadalamdirinya.Contohdaritipedatascalaradalah string, int, float,dan bool.Namundalamcontohnanti saya jugaakanmemasukkan array karenamenurutsaya array dantipedatascalarpada typehinting tidakjauh berbeda sehingga akan lebih mudah untuk membahasnyasekaligus.
Untukmendefinisikan typecastingatau typehinting pada sebuahmethod kita menambahkan tipe data atau cast type sebelumpendefinisianvariableparameter.Perhatikancontohberikut:
<?php
classParameterCasting
{
publicfunction__construct(array$arrayType)
{
}
publicfunctionstringCast(string$stringType)
{
}
publicfunctionintCast(int$intType)
XVIII.ParameterCastingdanReturnTypeDeclaration
134
{
}
publicfunctionfloatCast(float$floatType)
{
}
publicfunctionbooleanCast(bool$booleanType)
{
}
}
Pada contoh diatas, ketika kita membuat object dari classParameterCastingmakakitawajibmemasukkanparameterberupaarray bila kita tidakmemasukkan parameter pasti akan terjadierrorsepertiyangsudahkitasama-samapahami.Namunjikakitamemasukkanparameterselaintipedata array,karenakitasudahmelakukancastingbahwayangbolehdimasukkanhanyatipedataarray ,maka jugaakanmengakibatkanerror.Perhatikancontohdibawahini:
$object=newParameterCasting('initidakbolehmasuk');
Bilaprogramdiatasdijalankan,makaakanterjadierror PHPFatalerror: Uncaught TypeError: Argument 1 passed to
ParameterCasting::__construct()mustbeofthetypearray,string
givensepertipadagambardibawah:
XVIII.ParameterCastingdanReturnTypeDeclaration
135
Namun bila kita menggantinya dengan tipe data array sepertipadacontohdibawahini:
$object=newParameterCasting(array());
Maka tidakakan terjadierror karenakitamemasukkanparametersesuai dengan spesifikasi yang sudah kita buat sebelumnya.Begitujugadenganmethodyanglain,jikakitasalahmemasukkanparametermakaakanterjadierrorsepertisebelumnya.
Khusus untuk tipe data float secara default kita bisamemasukkan tipe data int ataupun float (numeric) dan iniberlaku juga sebaliknya untuk int karena PHP menganggapkeduanyaadalah sama-samanumeric. Kita dapatmembuat PHPmembedakantipedata float dan int secaraspesifikdenganmenggunakanstrictmodeyaitudenganmenambahkanbariscode declare(strict_types=1) diatas sebuah file yang inginkan.Perhatikancontohberikut:
<?php
classParameterCasting
{
publicfunction__construct(array$arrayType)
{
}
publicfunctionstringCast(string$stringType)
{
}
publicfunctionintCast(int$intType)
{
XVIII.ParameterCastingdanReturnTypeDeclaration
136
}
publicfunctionfloatCast(float$floatType)
{
}
publicfunctionbooleanCast(bool$booleanType)
{
}
}
$object=newParameterCasting(array());
$object->floatCast(1);
$object->intCast(1.0);
Pada contoh diatas tidak akan terjadi error seperti yang sudahsaya jelaskan sebelumnya walaupun kita memasukkan int
kedalammethod floatCast() yang di-casting hanya menerimafloat , begitu juga padamethod intCast() yang di-castinghanyamenerima inttetapikitatetapbisamemasukkantipedatafloatkedalamnya.
Untuk membuat keduanya secara spesifik hanya menerima tipedata sesuai dengan casting-nya, maka kita tambahkandeclare(strict_types=1)dibagianpalingatassebagaiberikut:
<?php
declare(strict_types=1);
classParameterCasting
{
publicfunction__construct(array$arrayType)
{
}
XVIII.ParameterCastingdanReturnTypeDeclaration
137
publicfunctionstringCast(string$stringType)
{
}
publicfunctionintCast(int$intType)
{
}
publicfunctionfloatCast(float$floatType)
{
var_dump($floatType);
}
publicfunctionbooleanCast(bool$booleanType)
{
}
}
$object=newParameterCasting(array());
$object->floatCast(1);
$object->intCast(1.0);
Dan bila program diatas dijalankan kembali, maka akan terjadierror PHPFatalerror:UncaughtTypeError:Argument1passedtoParameterCasting::intCast() must be of the type integer, float
given karena kita mencoba memasukkan tipe data float
kedalam method intCast() , sedangkan int 1 yang kitamasukkankedalammethod floatCast() secaraotomatisdiubaholehPHPmenjaditipefloatingpointsepertipadagambardibawah:
XVIII.ParameterCastingdanReturnTypeDeclaration
138
ObjectTypeHintingSama seperti pada scalar type hinting, object type hintingmempunyai fungsi untuk membatasi nilai parameter yangdimasukkan kedalam sebuah method. Bedanya adalah yangsekarang kita casting parameter menggunakan nama class atauinterface.
Padaobjecttypehintingsayatidakakanmenunjukkanbagaimanajika yang di-passing kedalam method tipe datanya tidak sesuaikarena sudah dijelaskan pada pembahasan sebelumnya. Padapembahasan kali ini kita akan fokus kepada hirarki pada objecttypehinting.Perhatikancontohberikut:
<?php
classPost
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle()
{
return$this->title;
}
XVIII.ParameterCastingdanReturnTypeDeclaration
139
publicfunctiongetContent()
{
return$this->content;
}
}
<?php
namespaceMutator;
classStringMutator
{
publicstaticfunctionbold(string$word)
{
returnsprintf('<b>%s</b>',$word);
}
publicstaticfunctionunderline(string$word)
{
returnsprintf('<u>%s</u>',$word);
}
publicstaticfunctionitalic(string$word)
{
returnsprintf('<i>%s</i>',$word);
}
publicstaticfunctionstrike(string$word)
{
returnsprintf('<strike>%s</strike>',$word);
}
}
<?php
XVIII.ParameterCastingdanReturnTypeDeclaration
140
namespaceMutator;
usePost;
classPostMutator
{
private$post;
publicfunction__construct(Post$post)
{
$this->post=$post;
}
publicfunctiongetBoldTitle()
{
returnStringMutator::bold($this->post->getTitle());
}
publicfunctiongetItalicContent()
{
returnStringMutator::italic($this->post->getContent
());
}
}
Pada contoh diatas terdapat 3 class yaitu Post ,Mutator\PostMutator ,dan Mutator\StringMutator .Dimanaclass Post menjadi type hinting pada constructor classMutator\PostMutator sedangkan class Mutator\StringMutatoradalah class utilitas yang dibutuhkan oleh classMutator\PostMutator untuk memanipulasi isi dari class Post .Untuk lebih jelas bagaimana cara menggunakannya, perhatikancontohfilepemanggildibawahini:
<?php
XVIII.ParameterCastingdanReturnTypeDeclaration
141
require__DIR__.'/Post.php';
require__DIR__.'/Mutator/PostMutator.php';
require__DIR__.'/Mutator/StringMutator.php';
useMutator\PostMutator;
$post=newPost('BelajarOOP','SayasedangbelajarOOPPHP'
);
$mutator=newPostMutator($post);
echo$mutator->getBoldTitle();
Bilaprogramdiatasdijalankandibrowsermakaoutput-nyatampaksebagaiberikut:
PerluAndapahamibahwapadaobjecttypehintingberlakuhirarkidimanasemakintinggimakasuatuhirarkimakaobjectyangdapatdimasukkan kedalam sebuahmethod akan semakin banyak danbegitu sebaliknya, jika semakin kebawah maka semakin spesifikjugaobjectyangdapatdimasukkankedalamsebuahmethod.
Susunan hirarki tersebut dari atas kebawah adalah interface,abstractclass,parentclass,danchildclasssesuaidenganhirarkipada pewarisan. Sehingga bila kita menggunakan interfacesebagaitypehintingpadasebuahmethodmakasemuaclassyangmengimplementasikan interface tersebut akan valid jika
XVIII.ParameterCastingdanReturnTypeDeclaration
142
dimasukkan atau di-passing didalam method tersebut. Hal iniberlaku juga pada abstract class dan seterusnya. Perhatikancontohberikut:
<?php
interfacePostInterface
{
publicfunctiongetTitle();
publicfunctiongetContent();
}
<?php
classPostimplementsPostInterface
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle()
{
return$this->title;
}
publicfunctiongetContent()
{
return$this->content;
XVIII.ParameterCastingdanReturnTypeDeclaration
143
}
}
<?php
namespaceMutator;
usePostInterface;
classPostMutator
{
private$post;
publicfunction__construct(PostInterface$post)
{
$this->post=$post;
}
publicfunctiongetBoldTitle()
{
returnStringMutator::bold($this->post->getTitle());
}
publicfunctiongetItalicContent()
{
returnStringMutator::italic($this->post->getContent
());
}
}
Pada contoh diatas kita menambahkan interface PostInterfacedan membuat class Post mengimplementasikan interfacetersebut. Kemudian kita juga mengubah type hinting pada
XVIII.ParameterCastingdanReturnTypeDeclaration
144
constructor class Mutator\PostMutator menjadi menggunakaninterface PostInterface . Setelah itu kita ubah file pemanggilmenjadisebagaiberikut:
<?php
require__DIR__.'/PostInterface.php';
require__DIR__.'/Post.php';
require__DIR__.'/Mutator/PostMutator.php';
require__DIR__.'/Mutator/StringMutator.php';
useMutator\PostMutator;
$post=newPost('BelajarOOP','SayasedangbelajarOOPPHP'
);
$mutator=newPostMutator($post);
echo$mutator->getBoldTitle();
Bila kita buka program diatas menggunakan browser makahasilnya akan sama dengan program sebelumnya, bukan? Inimenunjukkanbahwadenganmenggunakaninterfacesebagaitypehintingmakaclassyangmengimplementasikan interfacedianggapsebagaiparameteryangvalid.
Marikitacobalagidenganmembuatclass PostItalicTitle yaitusebuahclassyangmengimplementasikaninterface PostInterfacejugauntukmembuktikanbahwahirarkiparameteryangtelahsayajelaskandiatasbenar-benarbekerjadenganbaik.
<?php
classPostItalicTitleimplementsPostInterface
XVIII.ParameterCastingdanReturnTypeDeclaration
145
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle()
{
returnsprintf('<i>%s</i>',$this->title);
}
publicfunctiongetContent()
{
return$this->content;
}
}
Kemudiankitaubahfilepemanggilmenjadisepertiberikut:
<?php
require__DIR__.'/PostInterface.php';
require__DIR__.'/Post.php';
require__DIR__.'/PostItalicTitle.php';
require__DIR__.'/Mutator/PostMutator.php';
require__DIR__.'/Mutator/StringMutator.php';
useMutator\PostMutator;
$post=newPostItalicTitle('BelajarOOP','Sayasedangbela
jarOOPPHP');
XVIII.ParameterCastingdanReturnTypeDeclaration
146
$mutator=newPostMutator($post);
echo$mutator->getBoldTitle();
Bilakitabukaprogramdiatasmenggunakanbrowsermakaoutput-nyaakantampaksepertiberikut:
Kita juga bisa mengubah class PostItalicTitle menjadi childclassdariclass Postsepertiberikut:
<?php
classPostItalicTitleextendsPost
{
publicfunctiongetTitle()
{
returnsprintf('<i>%s</i>',parent::getTItle());
}
}
Kemudiankitaubahfilepemanggilmenjadisepertiberikut:
<?php
require__DIR__.'/PostInterface.php';
require__DIR__.'/Post.php';
require__DIR__.'/PostItalicTitle.php';
require__DIR__.'/Mutator/PostMutator.php';
require__DIR__.'/Mutator/StringMutator.php';
XVIII.ParameterCastingdanReturnTypeDeclaration
147
useMutator\PostMutator;
$post=newPostItalicTitle('BelajarOOP','Sayasedangbela
jarOOPPHP');
$mutator=newPostMutator($post);
echo$mutator->getBoldTitle();
Bila kita buka program diatas menggunakan browser makahasilnyaakansamadenganprogramsebelumnya.
NullableTypeHintingNullabletypehintingadalahfiturbaruyangditambahkanpadaPHPversi 7.1.Nullable type hinting memungkinkan kita memasukkanparameter null pada parameter casting. Untuk menggunakanfitur inikitahanyacukupmenambahkan tanda tanya ? didepantypehinting.Perhatikancontohberikut:
<?php
namespaceMutator;
classStringMutator
{
publicstaticfunctionbold(?string$word)
{
returnsprintf('<b>%s</b>',$word);
}
}
XVIII.ParameterCastingdanReturnTypeDeclaration
148
Pada contoh ditas, kita dapat memasukkan nilai string ataunullkedalammethod bold().Halinijugaberlakupadasemuatipedatatermasuktipedataobject(interface,abstractclass,parentclass, dan child class). Dengan nullable hinting maka kita dapatmemanggilmethod bold() dengan StringMutator::bold(null)tanpatakutterjadierror.
Namun bila kita memanggilmethod tersebut tanpa memasukkanparameter StringMutator::bold() tetapakan terjadierror karenamethod bold()memerlukanparameterwalaupunitu null.
ApaituReturnTypeDeclarationSamasepertiparametercasting,returntypedeclarationataubiasadisebut juga returnhinting berfungsi untukmemastikannilai yangdikembalikan oleh sebuahmethod telah sesuai dengan tipe datayang diinginkan. Pada return hinting berlaku semua aturan yangadapadatypehintingsehinggasayatidakperlumenjelaskanulangpermasalahan tersebut. Pada return hinting juga berlaku nullablehintingsepertipadatypehinting.
CaraPenggunaanReturnTypeDeclarationUntuk mendeklarasikan return type kita menggunakan tanda titikdua : setelah tanda kurung () dan sebelum tanda kurungkurawal {}.Untuklebihjelasnya,marikitamodifikasicontohyangsudahadadiatassepertidibawahini:
XVIII.ParameterCastingdanReturnTypeDeclaration
149
<?php
interfacePostInterface
{
publicfunctiongetTitle():string;
publicfunctiongetContent():string;
}
<?php
classPostimplementsPostInterface
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle():string
{
return$this->title;
}
publicfunctiongetContent():string
{
return$this->content;
}
}
XVIII.ParameterCastingdanReturnTypeDeclaration
150
<?php
namespaceMutator;
classStringMutator
{
publicstaticfunctionbold(string$word):string
{
returnsprintf('<b>%s</b>',$word);
}
publicstaticfunctionunderline(string$word):string
{
returnsprintf('<u>%s</u>',$word);
}
publicstaticfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
publicstaticfunctionstrike(string$word):string
{
returnsprintf('<strike>%s</strike>',$word);
}
}
<?php
namespaceMutator;
usePostInterface;
classPostMutator
{
private$post;
XVIII.ParameterCastingdanReturnTypeDeclaration
151
publicfunction__construct(PostInterface$post)
{
$this->post=$post;
}
publicfunctiongetBoldTitle():string
{
returnStringMutator::bold($this->post->getTitle());
}
publicfunctiongetItalicContent():string
{
returnStringMutator::italic($this->post->getContent
());
}
publicfunctiongetOriginalPost():PostInterface
{
return$this->post;
}
}
Pada contoh diatas kita memberikan return hinting pada semuaclass yang sebelumnya kita gunakan. Kemudian sayamenambahkanmethodbarupadaclass PostMutatoryaitumethod getOriginalPost() yang mengembalikan object dari interface PostInterface untuk memberikan contoh bagaimanamenggunakanobjectsebagaireturnhinting.
Untuk mendefinisikan nullable return hinting kita menggunakantanda tanya ? dibelakang tanda titik dua : dan sebelum tipedata.Perhatikancontohberikut:
<?php
XVIII.ParameterCastingdanReturnTypeDeclaration
152
namespaceMutator;
usePostInterface;
classPostMutator
{
private$post;
publicfunction__construct(?PostInterface$post)
{
$this->post=$post;
}
publicfunctiongetOriginalPost():?PostInterface
{
return$this->post;
}
}
Selain itu, kita juga dapat memastikan sebuah method tidakmengembalikan apapun atau biasa disebut void denganmenggunakan keyword void pada return type hinting sepertipadacontohdibawahini:
<?php
namespaceMutator;
usePostInterface;
classPostMutator
{
private$post;
publicfunction__construct(?PostInterface$post)
XVIII.ParameterCastingdanReturnTypeDeclaration
153
{
$this->post=$post;
}
publicfunctiongetOriginalPost():?PostInterface
{
return$this->post;
}
publicfunctionnoReturnValue():void
{
}
}
Bagaimana apakah penjelasan diatas dapat dipahami? Kalaubelum silahkan langsung dicoba agar lebih paham bagaimanasemuaitubekerja.
XVIII.ParameterCastingdanReturnTypeDeclaration
154
XIX.RecursiveFunctionBagaimana apakah kepala terasa hampir mendidih setelahmembaca penjelasan tentang parameter casting dan return typehinting pada bab sebelumnya? Kalau iya, mari kita refreshingsejenak dengan membahas sesuatu yang ringan yaitu tentangrecursivefunction.
ApaituRecursiveFunctionRecursivefunctionadalahsebuahfunctionyangmemanggildirinyasendiri dalam badan function-nya. Recursive function biasanyadipakaiuntukmenyelesaikanpermasalahanyangmempunyaipoladasaryangberulangsepertiperhitunganfaktorial.
Keuntungan menggunakan recursive function adalahmempersingkat code yang kita tulis. Namun yang perludiperhatikan adalah bahwa kita harus benar-benar pahambagaimana function tersebut bekerja. Jika kita tidak pahambagaimananestedcallyangterjadididalamrecursivefunctionbisasajabukansolusisingkatyangdidapattapijustrupermasalahyangjustru kita sama sekali tidak mengetahui bagaimana caramengatasinya.
Recursive function sangat perlu dipelajari dan dipahami olehprogrammerkarenadalambanyakkasusrecursivefunctionterbuktimampumenyelesaikanpermasalahanyangkompleksdandinamis
XIX.RecursiveFunction
155
denganlebihmudah.
ContohPenggunaanRecursiveFunctionSebelum membuat recursive function mari kita terlebih dahulumemahami tentang faktorial bilangan. Faktorial secara mudahdipahamisebagaiperkalianberuntundari n nilaihingga n=1dimana n adalah bilangan positif. Secaramatematika, faktorialdilambangkandengan n!.
Danberikutadalahcontohfaktorialbilangandari1hingga7:
1!=1
2!=2X1
3!=3X2X1
4!=4X3X2X1
5!=5X4X3X2X1
6!=6X5X4X3X2X1
7!=7X6X5X4X3X2X1
Bagaimanamembuatprogramuntukmenghitungfaktorialtersebutdengandantanparecursivefunctionmarikitacobabuat.Pertamakalikitabuatprogrammenghitungfaktorialtanparecursivefunctionterlebihdahulu.Perhatikancontohberikut:
<?php
classFaktorial
{
publicstaticfunctionnonRecursive(int$number)
XIX.RecursiveFunction
156
{
$result=1;
for($i=1;$i<=$number;$i++){
$result*=$i;
}
return$result;
}
}
echoFaktorial::nonRecursive(4);
echoPHP_EOL;
Bila kita jalankan program diatas, maka hasilnya adalah 24
karena 4X3X2X1=24sepertiberikut:
phpFaktorial.php
Output:
24
Caradiatasadalahcaramenghintungfaktorialtanpamenggunakanrecursive function, lalu bagaimana cara menghitung faktorialbilangan menggunakan recursive function? Perhatikan contohdibawahini:
<?php
classFaktorial
{
publicstaticfunctionnonRecursive(int$number)
{
$result=1;
for($i=1;$i<=$number;$i++){
XIX.RecursiveFunction
157
$result*=$i;
}
return$result;
}
publicstaticfunctionrecursive(int$number)
{
if(2>$number){
return$number;
}
return$number*self::recursive($number-1);
}
}
echoFaktorial::nonRecursive(5);
echoPHP_EOL;
echoFaktorial::recursive(5);
echoPHP_EOL;
Padabaris $number * self::recursive($number - 1) inilah yangdisebut nested call pada recursive function. Jika program diatasdijalankanmakaoutput-nyaadalahsebagaiberikut:
phpFaktorial.php
Output:
120
120
Selain untuk faktorial, kita juga dapat menggunakan recursivefunction untuk menyelesaikan deret fibonacci yang urutannyaadalahsebagaiberikut:
XIX.RecursiveFunction
158
0,1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,
987,...
Bila inginmengetahui bagaimana rumusbilangan fibonaccimakasilahkankliklangsunghalamanwikipediaini.
Untukmengetahuinilai deret fibonacci pada n indexmaka kitadapatmembuatnyaprogramsebagaiberikut:
<?php
classFibonacci
{
publicstaticfunctioncalculate(int$limit)
{
if(0===$limit||1===$limit){
return$limit;
}
if(2===$limit){
return1;
}
returnself::calculate($limit-1)+self::calculate
($limit-2);
}
}
//indexdimulaidari0
//0,1,1,2,3,5,8
echoFibonacci::calculate(6);
echoPHP_EOL;
Jika program diatas dijalankan maka output-nya adalah sebagaiberikut:
XIX.RecursiveFunction
159
phpFibonacci.php
Output:
8
Sepanjang pengalaman saya melamar pekerjaan, pengetahuantentang recursive function sering kali ditanyakan dan dijadikanbahan test. Semoga dengan adanya bab ini dapat menjadikanAndamampumemahami danmengaplikasikan recursive functiondalamkasusnyata.
Yang terpenting ketika membuat recursive function adalah Andaharus memastikan bahwa function tersebut akan berhenti. JikaAnda tidak dapat memastikan hal tersebut, maka yang terjadiprogramAndaakanterusberputarsampaimemoryhabis.
XIX.RecursiveFunction
160
XX.LateStaticBindingsPada bab ini kita akan membahas tentang apa itu late staticbinding dan cara penggunaannya pada bahasa pemrogramanPHP.
ApaituLateStaticBindingsLate static bindings adalah salah satu fitur PHP yang bekerjahanya pada static inheritance dimana child class meng-overridestatic functionmilikparent class. Secara bahasa yang sederhanalate static bindings adalah memasukkanmethod static ke dalammethodstatic yang lainnyadimanasalahsatudarimethodstatic-nyadi-overridemelaluikonseppewarisan.
Latestaticbindingsbekerjadenganmenyimpannamaclass padaakhir "non-forwading call". Hal ini terjadi karena penggunaankeyword selfhanyamereferensiterhadapclassdimanakeywordtersebut dideklarasikan.Artinya jika kitamemanggilmethod padaparentclassmelaluichildclassmakakeyword selftidakbekerjasebagaimanayangkitaharapkan.
ContohLateStaticBindingsUntuk lebih memahami bagaimana late static bindings bekerja,perhatikancontohdibawahini:
XX.LateStaticBindings
161
<?php
classStringMutator
{
publicstaticfunctionbold(string$word):string
{
returnsprintf('<b>%s</b>',$word);
}
publicstaticfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
publicstaticfunctionboldItalic(string$word):string
{
returnself::italic(self::bold($word));
}
}
classChildStringMutatorextendsStringMutator
{
publicstaticfunctionbold(string$word):string
{
return'<b>STATICLATEBINDINGS</b>';
}
}
echoChildStringMutator::boldItalic('MuhamadSuryaIksanudin'
);
Bila melihat contoh diatas, seharusnya ketika kita memanggilChildStringMutator::boldItalic('MuhamadSuryaIksanudin')makaoutput-nya adalah <i><b>STATIC LATE BINDINGS</b></i> karena
XX.LateStaticBindings
162
kita telah meng-override method bold() pada classChildStringMutator.Namunbilakitamenjalankanprogramdiatasdanmembukabrowsermakayangmunculadalahsebagaiberikut:
Atau yang kalau kita tulis dalamplain text adalah <i><b>MuhamadSurya Iksanudin</b></i> tidak sesuai dengan harapan kita. Initerjadikarenamethod italicdan bold()ketikadipanggilpadamethod boldItalic() menggunakan referensi self sehinggamethod italic()danterutama bold() karenamethod tersebutyangkitaoverride,hanyamerujukpadaclass StringMutator.
Untukmengatasihaltersebut,PHPmenyediakankeyword staticyangmemiliki fitur latestaticbindings sebagaimana yang sedangkitabahassekarang.Perhatikancontohberikut:
<?php
classStringMutator
{
publicstaticfunctionbold(string$word):string
{
returnsprintf('<b>%s</b>',$word);
}
publicstaticfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
XX.LateStaticBindings
163
publicstaticfunctionboldItalic(string$word):string
{
returnstatic::italic(static::bold($word));
}
}
classChildStringMutatorextendsStringMutator
{
publicstaticfunctionbold(string$word):string
{
return'<b>STATICLATEBINDINGS</b>';
}
}
echoChildStringMutator::boldItalic('MuhamadSuryaIksanudin'
);
Dan bila kita buka program diatas menggunakan browser makahasilnyaadalahsebagaiberikut:
Bagaimana sudah sesuai dengan espektasi kita kan? Setelahmengetahuibagaimana latestaticbindings bekerja, apakahAndaakan tetapmenggunakankeyword self untukmemanggilstaticmethod ataukah akan beralih menggunakan keyword static ?PilihanadaditanganAnda.
XX.LateStaticBindings
164
XXI.TraitPadapembahasankaliinikitaakanmempelajaritentangfituryangadapadaOOPPHPyaitutrait.Selainmempelajariapaitutraitdancara penggunaannya, dalam bab ini juga akan dibahas tentangbestpracticeketikamenggunakanfiturtraittersebut.
ApaituTraitSepertiyangsudahdibahassebelumnyabahwadalamOOPPHPtidakmengenalmultipleinheritancesehinggasebuahclassdipaksahanya boleh 1 parent class. Meski kita dapat menggunakan fiturnested inheritance, namun dalam beberapa kasus fitur tersebutbelumdapatmengakomodirkebutuhankita.
Untuk mengatasi hal tersebut, PHP memperkenalkan fitur baruyang disebut trait. Trait adalah sebuah mekanisme dalam PHPyangmemungkinkankitamenggunakan(reuse)codepadasebuahtrait seperti halnya pada pewarisan. Namun tidak seperti padapewarisanyanghanyamewariskanprotecteddanpublicsaja,padatraitsemuacodedapatdiwariskan.
Dalambahasayangsangatgampang,traitadalahcopypastecodeyang dilalukan melalui bahasa pemrograman. Sehingga bilasebuahclassmenggunakantraitmakainterpreterPHPakanmem-paste-kan code pada trait tersebut ke class yangmenggunakannya.
XXI.Trait
165
Trait juga dapat diibaratkan seperti potongan puzzle yang dapatdipasangkankeclassapapun.Andadapatmenggunakannyapadaabstractclassmaupunclassbiasa(bukanabstractclass).
Traitadalahkebalikandari interface dimanasetiapmethoddalamsebuahtraitharusberupanonabstractmethod.
PenggunaanTraitUntukmembuat sebuah trait kitamenggunakan keyword traitdiikutinamatraittersebut.Perhatikancontohberikut:
<?php
traitStringMutator
{
publicfunctionbold(string$word):string
{
returnsprintf('<b>%s</b>',$word);
}
publicfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
publicfunctionboldItalic(string$word):string
{
return$this->italic($this->bold($word));
}
}
XXI.Trait
166
Danuntukmenggunakantraitkitamemakaikeyword use sepertihalnyapadapemanggilanclasspadanamespace.Bedanya,kalaupemanggilanclasspadanamespaceposisikeyword use beradadiluardariblockclass,makapadatraitposisikeyword useberadadidalamblockclass.Perhatikancontohberikut:
<?php
classPost
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle():string
{
return$this->title;
}
publicfunctiongetContent():string
{
return$this->content;
}
}
traitStringMutator
{
publicfunctionbold(string$word):string
{
XXI.Trait
167
returnsprintf('<b>%s</b>',$word);
}
publicfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
publicfunctionboldItalic(string$word):string
{
return$this->italic($this->bold($word));
}
}
classPostMutator
{
useStringMutator;//Pemanggilan_trait_
private$post;
publicfunction__construct(Post$post)
{
$this->post=$post;
}
publicfunctionboldTitle():string
{
return$this->bold($this->post->getTitle());
}
}
$postMutator=newPostMutator(newPost('Judul','Inicontoh
contentdariberita.'));
echo$postMutator->boldTitle();
XXI.Trait
168
Pada contoh diatas, class PostMutator secara otomatis akanmewarisi semua fitur atau method yang ada pada traitStringMutator sehingga pada class PostMutator kita dapatmemanggilmethod bold() milik StringMutator .Untukmelihathasilnya, silahkan Anda buka sendiri program diatas melaluibrowser karena saya sedang malas mendemokan buat Anda,hehehe.
Dalam satu class kita dapat menggunakan lebih dari satu traitsepertipadacontohberikut:
<?php
classPost
{
private$title;
private$content;
publicfunction__construct(string$title,string$conte
nt)
{
$this->title=$title;
$this->content=$content;
}
publicfunctiongetTitle():string
{
return$this->title;
}
publicfunctiongetContent():string
{
return$this->content;
}
XXI.Trait
169
}
traitBoldText
{
publicfunctionbold(string$word):string
{
returnsprintf('<b>%s</b>',$word);
}
}
traitItalicText
{
publicfunctionitalic(string$word):string
{
returnsprintf('<i>%s</i>',$word);
}
}
classPostMutator
{
useBoldText;
useItalicText;
private$post;
publicfunction__construct(Post$post)
{
$this->post=$post;
}
publicfunctionboldItalicTitle():string
{
return$this->italic($this->bold($this->post->getTit
le()));
}
}
$postMutator=newPostMutator(newPost('Judul','Inicontoh
XXI.Trait
170
contentdariberita.'));
echo$postMutator->boldItalicTitle();
Meskidalamhalamandokumentasiresminya,traitmemilikibanyaksekalifitur,namunpadaprakteknyajarangsekaliprogrammeryangmenggunakan fitur-fitur tersebut selain daripada fitur-fitur yangumum.Hal ini untukmengurangi kompleksitas programdan agarprogramtersebutlebihmudahdibacadandi-trace.
BestPracticepadaTraitSepertiyangsudahdijelaskandiatas,traitadalahsebuahfituryangsangat powerful dalam konsep OOP PHP. Saking powerful-nya,fitur ini dapat membuat Anda dalam masalah besar (kesulitanmelakukan tracing code) jika Anda tidak bijak dalammenggunakannya. Penggunaan trait secara berlebihan akanmengakibatkan kompleksitas program menjadi meningkat danmembuatkitasemakinsulitdalammelakukantracing.
Untukmenghindarihaltersebut,adabaiknyaAndamemperhatikanhal-haldibawahini:
Janganmembuatnestedtrait.
Pembuatan nested trait, dimana sebuah trait menggunakan traitlainnya, hanya akan membuat Anda semakin sulit dalammelakukantracingcode.
Gunakantraitseminimalmungkin.
XXI.Trait
171
Meski sangat powerful tapi trait itu seperti micin. Kalau terlalubanyakakanbikinbegokalaukataanakmilenial.
Gunakantraitsebagaiaktifatorsebuahinterface.
Biladiperlukan,buatlah trait yangberisi semuamethod yangadapada interface khususnya pada interface yang sering digunakan.Untuklebihjelasperhatikancontohrealberikut:
<?php
/*
*ThisfileispartoftheSymfonypackage.
*
*(c)FabienPotencier<[email protected]>
*
*Forthefullcopyrightandlicenseinformation,pleasevi
ewtheLICENSE
*filethatwasdistributedwiththissourcecode.
*/
namespaceSymfony\Component\DependencyInjection;
/**
*ContainerAwareInterfaceshouldbeimplementedbyclasses
thatdependsonaContainer.
*
*@authorFabienPotencier<[email protected]>
*/
interfaceContainerAwareInterface
{
publicfunctionsetContainer(ContainerInterface$contain
er=null);
}
<?php
/*
*ThisfileispartoftheSymfonypackage.
XXI.Trait
172
*
*(c)FabienPotencier<[email protected]>
*
*Forthefullcopyrightandlicenseinformation,pleasevi
ewtheLICENSE
*filethatwasdistributedwiththissourcecode.
*/
namespaceSymfony\Component\DependencyInjection;
/**
*ContainerAwaretrait.
*
*@authorFabienPotencier<[email protected]>
*/
traitContainerAwareTrait
{
/**
*@varContainerInterface
*/
protected$container;
publicfunctionsetContainer(ContainerInterface$contain
er=null)
{
$this->container=$container;
}
}
Sehinggabilakitamembuatclass Ayangmengimplementasikaninterface Symfony\Component\DependencyInjection\ContainerAwareInterface
maka class A tersebut cukup menggunakan traitSymfony\Component\DependencyInjection\ContainerAwareTraittanpaperlumenulisulangmethod setContainer()padaclass Asepertiberikut:
<?php
XXI.Trait
173
useSymfony\Component\DependencyInjection\ContainerAwareInte
rface;
useSymfony\Component\DependencyInjection\ContainerAwareTrait
;
classAimplementsContainerAwareInterface
{
useContainerAwareTrait;
}
Setidaknya 3 poin simpel diatas cukup untuk membuat programAndasedikitberkurangkompleksitasnyaketikamenggunakantrait.
XXI.Trait
174
XXII.CodingStandardPada pembahasan kali ini kita akan mempelajari tentang codingstandard yang berlaku pada ekosistem PHP, apa itu PSR sertakenapakitaharusmenuliscodesesuaidenganPSR.
ApaituFIGdanPSRPHPStandard Recomendations atau yang lebih dikenal denganPSR adalah kumpulan aturan atau standard yang diakui danberlaku pada ekosistem PHP. Penetapan standarisasi tersebutbertujuan untuk memudahkan kolaborasi antar developer PHPdiseluruhdunia.
PSR merupakan bentuk kontribusi nyata komunitas PHP untukkemajuan PHP itu sendiri. Dengan adanya PSR, developer PHPdiseluruh dunia bisamempunyai standard dan coding style yangseragam dan ketika mereka membuat project open source,developer PHP lainnya dapat dengan mudah untuk ikutberkontribusi.
Salah satu manfaat dari PSR adalah lahirnya composer sebuahdependencymanager yang dibuat untukmemudahkan developerPHPuntukmelakukaninstalasi,meng-updatemaupunmenghapuspackage atau library yang dibutuhkan dalam sebuah proyekdengan mudah. Pembahasan tentang composer akan dibahassecaralebihspesifikpadababtersendiri.
XXII.CodingStandard
175
Penerapan PSR saat ini sudah sangat lazim dilakukan olehdeveloper terutama yang menggunakan framework sepertiSymfony, Laravel, Slim, dan Zend. Sehingga bisa dikatakansebagaiseorangdeveloperkitawajibmengetahuidanmenerapkanPSR.
KenapaharusMenerapkanPSRDengan mengikuti sebuah standard tertentu secara langsungmaupun tidak langsung akan memberikan manfaat kepada kita,begitu pula ketika kita mengikuti standard yang ada padaekosistem PHP yaitu PSR. Dengan mengikuti PSR kita akanmendapatkanmanfaatantaralain:
Codeprogrammenjadilebihrapidanterstruktur
Code yang kita buat menjadi semakin reusable karenadapat digunakan oleh developer lain yang menerapkanPSRdenganmudah.
Mudah mengganti suatu fungsional atau library denganfungsionalataulibrarylainnyakarenamempunyaistandardyangsama.
Memudahkankitaberkolaborasidengandeveloperlainnya.
AturanPenulisanSyntax
XXII.CodingStandard
176
Aturan penulisan syntax pada PSR diatur dalam PSR-1 dankemudian disempurnakan dengan PSR-2. Dalam rekomendasiPSR,aturanmenulissyntaxadalahsebagaiberikut:
Hanyabolehmenggunakantag <?php>dan <?=>dantidakbolehmenggunakanvariasitagyanglain.
Penulisan namespace, dan class harus mengikuti aturanautoloadpadaPSR-0danPSR-4.
Penulisan nama class, interface dan trait harusmenggunakan StudlyCaps yaitumiripseperti camelCasehanyasajahurufawalharusbesar.
Constanta harus ditulis dalam huruf kapital danmenggunakanunderscore( _)sebagaipemisahkata.
Penulisannamamethodharusmenggunakan camelCase.
Penulisan nama property tidak atur secara ketat, hanyasajaharuskonsisten.
Indentpadaharusmenggunakan4spasidanbukantab.
Danmasihbanyaklagi,silahkanbacasendiriataugunakanGoogleTranslatebilaAndakesulitanmenerjemahkannya.
ContohPenerapanAturanSyntaxSebenarnya dari awal bila Anda memperhatikan, contoh-contohyang kita tulis sebenarnya telahmenerapkan aturan-aturan yagnada pada PSR-1 maupun PSR-2 diatas seperti pada contoh
XXII.CodingStandard
177
berikut:
<?php
//filename:Api/Request.php
namespaceApi;
useRequestInterface;
classRequestimplementsRequestInterface
{
publicfunctionhandle()
{
echo'HandleApiRequest';
}
}
MenggunakanPHPCSFixerBanyaknyaaturandalamPSR-1danPSR-2membuatkitakadangtanpa sadarmelanggar atau tidakmenerapkannya. Dan bila kitaseorangprogrammeryangketatdalammenerapkanaturan,halitutentuperluditangani.Namununtukmengetahuiapakahcodeyangkitatulistelahsesuaidenganstandardataubelumadalahsesuatuyangsulitjikaproyekyangkitabanguncukupbesardancodeyangtelahkitatulissudahcukupbanyak.
BeruntunglahdikomunitasPHPadaorangyangawaredenganhaltersebutdanmaumembuatkantooluntukmengatasihal tersebut.AdalahFabienPotencierdanDariuszRuminski,duadeveloper inimengembangkansebuahtoolyangdiberinamaPHPCSFixeratau
XXII.CodingStandard
178
PHPCodingStandardFixersebuahyangdapatmembetulkancodeyang Anda tulis sesuai dengan aturan coding standard yangberlaku.
Untukmenggunakan tool tersebut,Andadapatmen-download filephar-nyamelaluiwebsiteSensiolabdanmenyimpannyapadarootproyekyangAndakerjakan.
Untuk menggunakannya, Anda cukup mengetikan dari terminalataucommandpromptperintahberikut:
phpphp-cs-fixer.pharfix<folder>--rules=@PSR2
Denganmenjalankanperintahsingkatdiatas,makaPHPCSFixerakanmembetulkan syntax code Anda secara otomatis mengikutiaturanPSR-2yangotomatisjugamengikutaturanPSR-1.
Untuk lebih detail tentang PHPCS Fixer serta opsi perintahnya,Andadapatmembacanya langsungpadawebsiteSensiolabyanglink-nyatelahditautkandiatas.
AturanPenulisanClassdanNamespacePadaPSR,aturanpenamaanclassdannamespacediaturmelaluiPSR-0 yang kemudian disempurnakan melalui PSR-4. Dalamrekomendasi PSR, aturan penulisan nama class dannamespaceadalahsebagaiberikut:
Sebuah class secara FQCN harus ditulis dengan
XXII.CodingStandard
179
<VendorName>\(<Namespace>\)*<ClassName>
<VendorName>dapatberisinamapackageataulibraryataunamaperusahaanatauorganisasipemiliklibrarytersebut.
Diperbolehkan pada <Namespace> menggunakan nestednamespace seperti <Namspace1>\<Namspace2> danseterusnya.
Setiapnamespaceharusdikonversikedalamfolderkerja.
Tandaunderscore ( _)pada <Namespace> tidakmemilikiartiapapun.
Bila pada <ClassName> terdapatunderscore ( _ ) makaharusdikonversikedalamfolder.
<ClassName>harusdiawalidenganhurufbesar.
Aturaniniberlakujugauntukinterfacedantrait.
Satufilehanyabolehberisisatuclasssaja.
ContohPenerapanAturanClassdanNamespaceContoh penerapan PSR-0 dan PSR-4 dapat dilihat pada classPostMutator pada bab sebelumnya. Isi dari class PostMutatoradalahsebagaiberikut:
<?php
XXII.CodingStandard
180
namespaceMutator;
usePostInterface;
classPostMutator
{
private$post;
publicfunction__construct(PostInterface$post)
{
$this->post=$post;
}
publicfunctiongetBoldTitle():string
{
returnStringMutator::bold($this->post->getTitle());
}
publicfunctiongetItalicContent():string
{
returnStringMutator::italic($this->post->getContent
());
}
publicfunctiongetOriginalPost():PostInterface
{
return$this->post;
}
}
Denganmenerapkan aturan-aturan diatas,maka secara otomatiscodeyangkitatulisakanmenjadi lebihmudahdibacadanmudahdimaintain.
XXII.CodingStandard
181
XXII.CodingStandard
182
XXIII.ExceptionHandlingExceptionhandlingadalahsalahsatufiturpentingyangharusadapada setiap program yang kita buat agar program yang dibuatmenjadi lebih aman. Pada bab ini akan dibawah tentang apa ituexception handling dan penerapannya dalam bahasapemrogramanPHP.
ApaituExceptionHandlingException adalah sebuah kondisi dimana program berhenti dantidak bisamelanjutkan eksekusi code atau biasa yang kita sebutdenganerror. Kecuali karena kesalahan syntax, pada PHP errortersebut dapat kita tangkap dan kita tangani agar program yangseharusnya berhenti dapat tetap melanjutkan eksekusi ataumengirimkan pesan error yang human readable atau dapatdipahamiolehpenggunaawam.
Pada PHP, ketika program terjadi error baik karena kesalahanmemasukkan parameter, kesalahan aritmatika seperti pembagiandengannol ( 0 ) atau yang lainnya,PHPakanmenangkaperrortersebut dan menganggapnya sebagai object. Karena dianggapsebagaiobjectmaka ketikaerror tersebut akan ditampilkan padalayar, maka secara otomatis akan memanggil method__toString().
XXIII.ExceptionHandling
183
Kita dapat menangkap object error tersebut dan kemudianmenanganinya sesuai dengan keinginan kita atau biasa disebutexception handling. Jadi exception handling adalah mekanismeuntukmenangkaperror yang terjadipadaprogramdankemudianmenanganinya.
HirarkiErrorpadaPHPPHPmerombaksecarabesar-besaranmekanismeerror reportingpada PHP 7 dan menambahkan banyak class baru untukmenanganierrorsesuaidengankonteksnya.BilapadaPHP5kitahanya mengenal class Exception dan turunannya untukmenangani error pada program, maka pada PHP 7 kita harusmembedakanantaraexceptiondanerrorkarenapadaPHP7,PHPmemperkenalkanclassbaruyaituclass Error.
Secara umum, kesalahan yang dilakukan programmer sepertisalah memasukkan parameter, pembagian dengan nol ( 0 ),kurang memasukkan parameter dan kesalahan-kesalahan lainyang dilakukan secara tidak sengaja oleh programmer masukdalamkategorierror sehinggaakandianggapsebagaiobject dariclass Error.
Sedangkankesalahanprogramyangterjadikarenadisengajaolehsang programmer biasanya masuk dalam kategori exceptionmeskipun Anda dapat membuatnya masuk kategori error namunsangat tidakdianjurkan.Contohkesalahanprogramyangsengajaadalahsebagaiberikut:
XXIII.ExceptionHandling
184
<?php
classConnection
{
publicfunctionconnect()
{
thrownewRuntimeException('Andaharusmengimplement
asikanmethodconnect()sesuaidengandatabasedriveryangA
ndagunakan.');
}
}
Ketika kitamembuatobject Connection danmemanggilmethod connect() maka kita akan memicu exception yaituRuntimeException.
Class Error dan Exception adalah class yangmengimplementasikaninterface Throwabledanmemilikibeberapachild class. Adapunhirarkiclass Error dan Exception adalahsebagaiberikut:
Throwable
Error
ArithmeticError
DivisionByZeroErrorAssertionError
ParseError
TypeError
ArgumentCountError
XXIII.ExceptionHandling
185
Exception
RuntimeException
InvalidArgumentException
...
Karena baik berupa class maka kita pun dapat membuat childclass dari class Error maupun Exception sesuai dengankebutuhankita.
ExceptionHandlingpadaPHPSebelum membahas tentang exception handling pada PHP, adabaiknya kita pahami dulu alur penanganan error yang dilakukanolehPHP.Perhatikancontohsederhanaberikut:
<?php
classErrorable
{
publicstaticfunctioncalculate(int$number)
{
return($number%0);//Bilamenggunakan$number/0
makahanyaakanmemunculkan_warning_bukan_error_
}
}
Errorable::calculate(7);
echo'Initidakdieksekusi';
echoPHP_EOL;
XXIII.ExceptionHandling
186
Pada contoh diatas ketika kita memanggilmethod calculate()akan terjadierror disebabkan oleh pembagian dengan nol ( 0 ).PHPsecaraotomatisakanmembuatobject DivisionByZeroErrordankemudianmencariblock catchuntukkemudianmemberikanerror tersebut agar dapat ditangani. Bila block catch tidakditemukan, maka PHP akan mencoba memanggil functiondidefinisikan pada fungsi set_exception_handler() . Bila fungsi set_exception_handler() tidak didefinisikan, maka PHP akanmengubahnya menjadi menjadi fatal error, yaitu mencetak errorpadalayar.
Mari kita fokus pada kalimat, kemudianmencari block catchuntuk kemudian memberikan error tersebut agar dapatditangani. Inti dari kalimat tersebutlah yang akan coba kitalakukan,yaitumenanganierroryangterjadi.
Untuk menangani error tersebut, kita harus membuat barisprogramyangmungkindapatmemicuerrorberadadidalamblocktry dan kemudian bila terjadi error kita akan menangkapnyamenggunakanblock catch.Perhatikancontohdiatas:
<?php
classErrorable
{
publicstaticfunctioncalculate(int$number)
{
return($number%0);
}
XXIII.ExceptionHandling
187
}
try{
Errorable::calculate(7);
}catch(DivisionByZeroError$e){
echo$e->getMessage();
echoPHP_EOL;
}
echo'Initetapdieksekusi';
echoPHP_EOL;
Ketika programdiatas dijalakanmaka akanmengeluarkanoutputsebagaiberikut:
Dengan menggunakan block try {} catch () {} kita dapatmembuatprogramkitatetapberjalanwalaupundalamkondisierroratau menampilkan pesan yang lebih manusiawi agar padadipahamipenggunayangawam.Kitajugadapatmenangkaperroryang kita sengaja buat sama seperti yang kita lakukan diatas.Perhatikancontohberikut:
<?php
classConnection
{
publicfunctionconnect()
{
thrownewRuntimeException('Andaharusmengimplement
asikanmethodconnect()sesuaidengandatabasedriveryangA
ndagunakan.');
}
XXIII.ExceptionHandling
188
}
try{
$connection=newConnection();
$connection->connect();
}catch(RuntimeException$e){
echo$e->getMessage();
echoPHP_EOL;
}
echo'Initetapdieksekusi';
echoPHP_EOL;
Bila dijalakan program diatas akanmengeluarkanoutput sebagaiberikut:
Selainitukitajugadapatmenggunakaninterface Throwableuntukmenangani Error dan Exception maupun child class darikeduanyasekaliguskarena Throwableadalahinterfacedarikeduaclasstersebut.
Kita juga dapatmenggunakanmultiple catch untuk menanganisebuaherrorsepertipadacontohberikut:
<?php
classErrorable
{
publicstaticfunctioncalculate(int$number)
{
return($number%0);
}
XXIII.ExceptionHandling
189
}
try{
Errorable::calculate(7);
}catch(Exception$e){
echo'Tidakmasukkesini';
echoPHP_EOL;
}catch(DivisionByZeroError$e){
echo$e->getMessage();
echoPHP_EOL;
}
Ketika programdiatas dijalakanmaka akanmengeluarkanoutputsebagaiberikut:
Block catch yangdiatas tidak dieksekusi karenablock tersebuthanya menangani object Exception sedangkan error yangdihasilkan oleh program adalah object DivisionByZeroError
sehinggaakanditangkapolehblock catchberikutnya.
Kita juga dapat membuat sebuah program dieksekusi walaupunterjadi error dan tak dapat ditangani oleh block catch
menggunakanblock finallyseperticontohdibawahini:
<?php
classErrorable
{
publicstaticfunctioncalculate(int$number)
{
return($number%0);
}
XXIII.ExceptionHandling
190
}
try{
Errorable::calculate(7);
}catch(Exception$e){
echo'Tidakmasukkesini';
echoPHP_EOL;
}finally{
echo'Apapunyangterjadi,iniharustetapdieksekusi';
echoPHP_EOL;
}
Bila dijalakan program diatas akanmengeluarkanoutput sebagaiberikut:
Bila melihat output diatas, terlihat block finally lebih dahuludieksekusi daripada error yang muncul. Seperti yang telahdijelaskan diatas, hal ini terjadi karena ketika error terjadi, PHPakanmencariblock catchyangdapatmenanganierror tersebut.Bila itu tidak ditemukanmaka PHP akanmengecek apakah adaerror handler yang di-set pada fungsi set_exception_handler() .Bila tidak ada, maka PHP akan mengubahnya menjadi menjadifatalerror,yaitumencetakerrorpadalayar.
Karena ketika mencari block catch untuk menangani errortersebut tidak ada sedangkanblock finally masih dalam satublock dengan block catch , maka PHP mengeksekusi blockfinallyterlebihdahulusebelummencarierrorhandler.
XXIII.ExceptionHandling
191
XXIII.ExceptionHandling
192
XXIV.AnonymousFunctiondanAnonymousClassPada pembahasan kali ini kita akan memahami tentang apa ituanonymous class, anonymous function dan cara penggunaaankeduanya.
ApaituAnonymousFunctionPada pemrogramanmodern, anonymous function sangat umumdigunakan terutama pada bahasa pemrograman Javascript yangmenurutsayapalingbanyak.Anonymousfunctionadalah functiontanpanamadanhanyamemilikibodyfunctionsajaatauyanglazimjuga disebut lambda function atau closure function. Perbedaanantaralambdadanclosureadalahbahwadalamclosurekitadapatmenggunakanvariabledariluarkonteksfunctiontersebut.
Anonymousfunctionbergunaketikakitainginmembuatsolusiyangspesifik tanpa dan hanya digunakan sekali saja sehingga akanlebihefektifkitamembuatanonymousfunctiondaripadamembuatsebuahclasssecarautuhataumembuatfunctionsecaraterpisah.
XXIV.AnonymousFunctiondanAnonymousClass
193
ContohPenggunaanAnonymousFunctionUntukmembuatanonymousfunctionpadaPHPsamasepertipadabahasa pemrograman lainnya. Kita dapat membuat anonymousfunctionsebagaicallbackdarisuatufunctionsepertiyanglazimkitagunakanpadaJavascript.Perhatikancontohberikut:
<?php
echopreg_replace_callback('/[a-z]+/i',function(array$mat
ch){
return'PHP'===$match[0]?'OOP':$match[0];
},'SayaBelajarPHP');
echoPHP_EOL;
Bila program diatas dijalankan maka output-nya adalah Saya
BelajarOOPsepertitampakpadagambardibawahini:
Kita jugabisamemasukkananonymousfunction kedalamsebuahvariablesepertipadacontohberikut:
<?php
$print=function($word){
echo$word;
};
$print('Iniadalah_lambdafunction_');
XXIV.AnonymousFunctiondanAnonymousClass
194
echoPHP_EOL;
Bila program diatas dieksekusi maka output-nya adalah sebagaiberikut:
Selain kedua cara diatas, kita juga dapat menggunakan variabledari luar untuk dimasukkan kedalam anonymous function atauyangbiasadisebutclosuresepertipadacontohdibawahini:
<?php
$word='Iniadalah_closure_';
$print=function()use($word){
echo$word;
};
$print();
echoPHP_EOL;
Dan bila program diatas dijalankan maka output-nya adalahsebagaiberikut:
Demikianlah beberapa contoh penggunaan anonymous functionpada PHP. Untuk contoh penggunaan yang lebih banyak, Andadapatmembukadokumentasiresminyapadahalamanini.
XXIV.AnonymousFunctiondanAnonymousClass
195
ApaituAnonymousClassSama seperti konsep anonymous function, anonymous classadalah sebuah class tanpa nama yang dibuat sesaat sebelumproses instansiasi atau setelah keyword new dideklarasikan.Anonymous class sangat membantu jika kita memerlukan objectyangsimpeldanhanyasekalidigunakan.Namunpadaprakteknya,anonymousclassjarangdigunakan.
ContohPenggunaanAnonymousClassUntuk membuat anonymous class kita cukup mendeklarasikankeyword newdiikutikeyword classsepertipadacontohdibawahini:
<?php
$foo=newclass{
publicfunctionfoo()
{
echo'foo';
}
};
$foo->foo();
XXIV.AnonymousFunctiondanAnonymousClass
196
echoPHP_EOL;
Anonymous class kita juga meng-extends class lainnya,mengimplementasikan interface serta dapat menggunakan trait.Perhatikancontohberikut:
<?php
classSebuahClass{}
interfaceSebuahInterface{}
traitSebuahTrait{}
var_dump(newclass(10)extendsSebuahClassimplementsSebuah
Interface{
private$num;
publicfunction__construct($num)
{
$this->num=$num;
}
useSebuahTrait;
});
Hanya sebatas itu saja yang dapat saya jelaskan tentanganonymous class karena saya sendiri seingat saya tidak pernahmenggunakannya.
XXIV.AnonymousFunctiondanAnonymousClass
197
XXV.CaraMembuatVariadicFunctionVariadic function adalah sebuah fungsi yang dapat menerimaparameter secara dinamis. Secara default sebenarnya semuafunction dalam PHP adalah variadic function karena dapatmenerima parameter berapapun yang dimasukkan kedalamnya.Perhatikancontohberikut:
<?php
classVariadic
{
publicstaticfunctionfoo(int$number)
{
var_dump($number);
}
}
Variadic::foo(7,'ini','tetap','masuk');
Jika program diatas dijalankanmaka tidak akan terjadierror danmengeluarkanouputsebagaiberikut:
Kitadapatmenangkapparameter-parameterdarivariadicfunctiondengan menggunakan func_get_args() seperti pada contohberikut:
XXV.CaraMembuatVariadicFunction
198
<?php
classVariadic
{
publicstaticfunctionfoo(int$number)
{
var_dump(func_get_args());
}
}
Variadic::foo(7,'ini','tetap','masuk');
Jikaprogramdiatasdieksekusimakaouput-nyasebagaiberikut:
Kitajugadapatmenggunakantitiktiga( ...)untukmendefiniskanvariadicfunctiondancarainiadalahcarayangdirekomendasikan.Perhatikancontohberikut:
<?php
classVariadic
{
publicstaticfunctionadd(int...$numbers)
{
returnarray_sum($numbers);
}
}
echoVariadic::add(7,9,5,6);
echoPHP_EOL;
XXV.CaraMembuatVariadicFunction
199
Bila program diatas dijalankan maka ouput-nya adalah sebagaiberikut:
Pada contoh diatas terlihat bahwa variable $numbers yangmerupakan variadic paramter ditangkap sebagai array integer.Dengansyntaxdiataskitadapatmenambahkan typehintingpadavariadic parameter sehingga dapat menjamin parameter yangmasuksesuaidenganspesifikasi yang telahkita tentukan.Selainkita juga memasukkan parameter secara variadic, perhatikancontohberikut:
<?php
classVariadic
{
publicstaticfunctionadd(int...$numbers)
{
returnarray_sum($numbers);
}
}
$numbers=[7,9,5,6];
echoVariadic::add(...$numbers);
echoPHP_EOL;
Bagaimana apakah penjelasan diatas sudah cukup membukapemahaman tentang variadic function? Anda dapatmengembangkansyntaxdiatassesuaidengankebutuhanAnda.
XXV.CaraMembuatVariadicFunction
200
XXV.CaraMembuatVariadicFunction
201
XXIX.InstansiasipadaKonteksStatisMarikitarefreshingsejenakagarapayangtelahkitapelajaridapatlebihmengenadantersimpandalamhatikita.Pembahasankaliiniakanringansajayaitutentangcarainstansiasiobjectpadakonteksstatic.
Seperti yang sudah kita pahami bahwa konsep static adalahsebuahkonsepyangkeluardariaturandasarpadapemrogramanberbasis objek. Dalam konteks static kita tidak perlu membuatobjectterlebihdahuluuntukmenggunakannyasepertipadacontohberikut:
<?php
classA
{
publicstaticfunctionfoo()
{
echo'foo';
}
}
A::foo();
Lalubagaimanajikakitainginmenginstansiasiclasspadakonteksstatic.Perhatikancontohberikut:
XXVI.InstansiasipadaKonteksStatis
202
<?php
classA
{
publicfunctionbar()
{
echo'bar';
}
publicstaticfunctionfoo()
{
echo'foo';
//sayainginmemanggil_method_`bar()`disini
}
}
A::foo();
Bagaimanaagarsayadapatmelakukanhalsepertiyangsayatulispadakomentarprogramdiatas?
Untukdapatmemanggilmethod bar()daristaticmethod foo()kita dapat melakukannya menggunakan cara instansiasi sepertibiasanyayaitumenggunakankeyword new diikuti dengannamaclasstersebut.Perhatikancontohberikut:
<?php
classA
{
publicfunctionbar()
{
echo'bar';
}
XXVI.InstansiasipadaKonteksStatis
203
publicstaticfunctionfoo()
{
echo'foo';
(newA())->bar();
}
}
A::foo();
Selain itu kita juga dapat menggunakan keyword self untukmenginstansiasi class dimana keyword tersebut dideklarasikan.Dalamhal ini,kitadapatmenginstansiasiclass A menggunakankeyword selfsepertipadacontohdibawahini:
<?php
classA
{
publicfunctionbar()
{
echo'bar';
}
publicstaticfunctionfoo()
{
echo'foo';
(newself())->bar();
}
}
A::foo();
XXVI.InstansiasipadaKonteksStatis
204
Kita juga dapat menggunakan keyword static untukmenginstansiasiclasspadastaticmethodyangmerujukpadaclassdimanakeyword tersebutdideklarasikan.Selain itu juga,keyword static memiliki fitur late static bindings sehingga lebihdirekomendasikan.Perhatikancontohberikut:
<?php
classA
{
publicfunctionbar()
{
echo'bar';
}
publicstaticfunctionfoo()
{
echo'foo';
(newstatic())->bar();
}
}
A::foo();
Bila dijalankan, ketiga program diatas akanmenghasilkan outputyangsamayaitukata foobar.
Selaincaradiatas,kitajugadapatmenggunakanscoperesolutionoperator ( :: ) untuk memanggil non static method dari staticmethoddalamlingkupclass.Perhatikancontohberikut:
<?php
classA
XXVI.InstansiasipadaKonteksStatis
205
{
publicfunctionbar()
{
echo'bar';
}
publicstaticfunctionfoo()
{
echo'foo';
static::bar();
}
}
A::foo();
Caradiatasjugaberlakupadakeyword selfsehinggakitadapatmenggantikeyword staticdengankeyword self.
XXVI.InstansiasipadaKonteksStatis
206
MagicMethodpadaPHPPada bab ini kita akan membahas tentang magic method, apafungsinyadanadaberapamagicmethoddalamPHP.
ApaituMagicMethodMagic method adalah sekumpulan method yang secara defaultdidaftarkan oleh PHP pada object ketika object tersebut dibuat.Magic method akan dipanggil secara otomatis pada kondisitertentusesuaidenganskopdarimagicmethodtersebut.
MagicmethodpadaPHPditandaidengandoubleunderscore( __)sebagai awalan dari namamethod. Contohmagic method yangsudah kita bahas maupun sudah kita gunakan adalah method__construct(), __destruct(),dan __toString().
Karenamagic method menggunakan awalan double underscore( __),makaadabaiknyakitatidakmemberinamamethoddenganmemakaidoubleunderscoresebagaiawalan.Hal inidimaksudkanagarkitaterhindardarikebingungan.
__construct()dan__desctruct()
XXVII.MagicMethodpadaPHP
207
Pada pembahasan sebelumnya kita telah membahas secaramendalam tentang magic method __construct() dan__destruct() sehingga kali ini saya hanya cukup mengulangbahwamagicmethod __construct() dan __destruct() adalahmagicmethodyangberhubunganlangsungdenganobjectcreationdimana magic method __construct() dipanggil pada saatpembuatan object atau instansiasi, sementara magic method__destruct()dipanggilketikaobjectdihapusdarimemory.
__set()dan__get()
Magicmethod __set()dan __get()adalahmagicmethodyangdipanggilpadaprosespemberiannilaidanpengaksesannilaipadasuatuproperty.Magicmethod inibanyakdigunakanpadamodernframework terutama framework yang berhubungan dengandatabaseatauyangbiasadikenaldengansebutanORM.
Magicmethod __set()akandipanggilsecaraotomatisketikakitahendak memberikan nilai pada suatu property yang tidak dapatdiakses.Misalnyakitaakanmemasukkannilaipadapropertyyangmemiliki visibilitas private ataupun protected ataupun ketika kitaingin memasukkan nilai ke property yang belum didefinisikan.Perhatikancontohberikut:
<?php
classMagicMethod
{
private$name;
}
XXVII.MagicMethodpadaPHP
208
$magic=newMagicMethod();
$magic->name='MuhamadSuryaIksanudin';
Bila kita jalankan program diatas tentu akan terjadi error karenakitamencobamengaksesdanmemberinilaiproperty $nameyangmemilikivisibilitasprivate.Untukmengatasimasalahtersebut,kitadapatmenggunakanmagicmethod __set()sebagaiberikut:
<?php
classMagicMethod
{
private$name;
publicfunction__set($property,$value)
{
if('name'===$property){
$this->name=$value;
}else{
thrownewParseError(sprintf('Undefinedproperty
%sinclass%s',$property,__CLASS__));
}
}
}
$magic=newMagicMethod();
$magic->name='MuhamadSuryaIksanudin';
Bilakitamenjalankanprogramdiatasmakatidakakanterjadierrordan bila var_dump() variable $magic maka hasilnya tampaksepertigambarberikut:
XXVII.MagicMethodpadaPHP
209
Terlihat bahwaproperty $name telahmemiliki nilai sebagaimanayang kita set diatas. Perlu diketahui bahwa magic method__set()diPHPsecaradefaultmemilikilogicsebagaiberikut:
<?php
classMagicMethod
{
publicfunction__set($property,$value)
{
$this->{$property}=$value;
}
}
Sehingga kita dapat memasukkan property apapun kedalamsebuahclasstanpatakuterrorsepertiberikut:
<?php
classMagicMethod
{
publicfunction__set($property,$value)
{
$this->{$property}=$value;
}
}
$magic=newMagicMethod();
$magic->name='MuhamadSuryaIksanudin';
var_dump($magic);
XXVII.MagicMethodpadaPHP
210
Sehingga kita dapat menuliskan code diatas secara singkatsebagaiberikut:
<?php
classMagicMethod
{
}
$magic=newMagicMethod();
$magic->name='MuhamadSuryaIksanudin';
var_dump($magic);
Property yang dibuat secara on fly tersebut otomatis memilikivisibilitas public karena seperti yang telah dibahas padapembahasanvisibilitasbahwavisibilitasdefault padaPHPadalahpublic.
Kebalikan dari magic method __set() adalah magic method__get() .Magic method ini akan dipanggil ketika kita mencobamengaksesproperty yang tidakdapatdiakses.Perhatikan contohberikut:
<?php
classMagicMethod
{
private$name;
publicfunction__construct($name)
{
$this->name=$name;
}
XXVII.MagicMethodpadaPHP
211
}
$magic=newMagicMethod('MuhamadSuryaIksanudin');
echo$magic->name;
echoPHP_EOL;
Bila kita jalankan program diatas tentu akan terjadi error karenakitamencobamengaksesproperty $nameyangmemilikivisibilitasprivate. Untuk mengatasi masalah tersebut, kita dapatmenggunakanmagicmethod __get()sebagaiberikut:
<?php
classMagicMethod
{
private$name;
publicfunction__construct($name)
{
$this->name=$name;
}
publicfunction__get($property)
{
if('name'===$property){
return$this->name;
}else{
thrownewParseError(sprintf('Undefinedproperty
%sinclass%s',$property,__CLASS__));
}
}
}
$magic=newMagicMethod('MuhamadSuryaIksanudin');
echo$magic->name;
echoPHP_EOL;
XXVII.MagicMethodpadaPHP
212
Untukmelihatbagaimanamagicmethod __set() dan __get()diterapkan pada kasus nyata, Anda dapat melihat code dariEloquentyaituORMmilikframeworkLaravelberikut:
//source:https://github.com/illuminate/database/blob/maste
r/Eloquent/Model.php#L1404
publicfunction__get($key)
{
return$this->getAttribute($key);
}
/**
*Dynamicallysetattributesonthemodel.
*
*@paramstring$key
*@parammixed$value
*@returnvoid
*/
publicfunction__set($key,$value)
{
$this->setAttribute($key,$value);
}
__isset()dan__unset()
Magicmethodberikutnyaadalah __isset()dan __unset(),yaitumagicmethod yang akan dipanggil otomatis ketika kitamencobamengecek eksistensi atau ada tidaknya suatu property padasebuahclass.
XXVII.MagicMethodpadaPHP
213
Magicmethod __isset() akan dipanggil ketika kita memanggilfungsi isset() atau empty() untuk mengecek ada tidaknyaproperty yang tidak dapat diakses dari luar. Perhatikan contohberikut:
<?php
classMagicMethod
{
private$name;
}
$magic=newMagicMethod();
var_dump(isset($magic->name));
Tanpa kita menjalankan program diatas, kita dapat tahu bahwaoutputprogramdiatasadalah falsekarenaproperty $nametidakdapat diakses sehingga dianggap tidak ada. Denganmenggunakanmagicmethod __isset() kitadapatmemanipulasioutput dari pengecekan isset() diatas. Perhatikan contohberikut:
<?php
classMagicMethod
{
private$name;
publicfunction__isset($property)
{
if('name'===$property){
returntrue;
}
XXVII.MagicMethodpadaPHP
214
}
}
$magic=newMagicMethod();
var_dump(isset($magic->name));
Sekarang bila kita jalankan kembali program diatasmaka ouput-nya adalah true . Untuk mengecek eksistensi sebuah propertysecara presisi, saya sarankan Anda menggunakan fungsi property_exists() karena fungsi tersebut jauh lebih presisidaripadafungsi isset().
Kebalikan darimagic method __isset() adalahmagic method__unset() .Magicmethod iniakandipanggilketikakitamencobameng-unset sebuah property yang tidak dapat diakses. Magicmethod __unset()biasanyadigunakanuntukmemanipulasiarraykey pada sebuah class dimana array key tersebut dianggapsebagai property jika diakses dari luar atau biasa disebut virtualproperty.Perhatikancontohberikut:
<?php
classMagicMethod
{
private$data=[
'name'=>'MuhamadSuryaIksanudin',
'address'=>'Inyourhearth',
];
publicfunction__unset($property)
{
if(isset($this->data[$property])){
unset($this->data[$property]);
XXVII.MagicMethodpadaPHP
215
}
}
}
$magic=newMagicMethod();
var_dump($magic);
unset($magic->address);
var_dump($magic);
Bila program diatas dijalankan maka output-nya adalah sebagaiberikut:
Pada gambar diatas terlihat bahwa sebelum fungsi unset()
dipanggil,property $data memiliki dua indeks yaitu name danaddress , namun setelah fungsi unset() dipanggil, property$datahanyamemilikisatuindeksyaitu namesaja.
Untuk melihat bagaimana magic method __isset() dan__unset()diterapkanpadakasusnyata,AndadapatmelihatcodedariEloquentyaituORMmilikframeworkLaravelberikut:
//source:https://github.com/illuminate/database/blob/maste
r/Eloquent/Model.php#L1472
publicfunction__isset($key)
{
XXVII.MagicMethodpadaPHP
216
return$this->offsetExists($key);
}
/**
*Unsetanattributeonthemodel.
*
*@paramstring$key
*@returnvoid
*/
publicfunction__unset($key)
{
$this->offsetUnset($key);
}
__sleep()dan__wakeup()
Magic method selanjutnya yang akan kita bahas adalah magicmethod __sleep() dan __wakeup() . Magic method ini akandipanggil secara otomatis pada proses serialization danunserialization.Serializationyaitusebuahprosesuntukmengubahobject menjadi data yang dapat disimpan baik dalam persistentstorage seperti RDBMS atau ditransmisikan lagi ke program lainmelaluinetwork, sedangkanunserialization adalah kebalikan dariserialization.
Magicmethod __sleep() akan dipanggil ketika kita memanggilfungsi serialize()untukmelakukanserialisasipadaobject.Padamagicmethod __sleep() kitahanyaperlumengembalikanarrayberupanamapropertyyangakankitaserialize.Perhatikancontohberikut:
XXVII.MagicMethodpadaPHP
217
<?php
classMagicMethod
{
private$data=[
'name'=>'MuhamadSuryaIksanudin',
'address'=>'Inyourhearth',
];
publicfunction__sleep()
{
return['data'];
}
}
$magic=newMagicMethod();
var_dump(serialize($magic));
Bila program diatas dijalankan maka output-nya adalah sebagaiberikut:
Perlu diketahui bahwa magic method __sleep() hanya akanmemproses property yang secara visibilitas masuk dalamjangkauan class dimana magic method __sleep() tersebutdipanggil. Misalkan pada kasus pewarisan, magic method __sleep() didefinisikan pada parent class dimana kitamengembalikan private property yang ada pada parent class,kemudian kitamelakukan serialization pada child class sehinggasecara otomatis magic method __sleep() pada parent class
XXVII.MagicMethodpadaPHP
218
dipanggil,makaPHPtidakakanmampumemprosesprivateclasstersebut dan akan mengeluarkan notice atau peringatan.Perhatikancontohberikut:
<?php
classParentClass
{
private$myOwnProperty;
publicfunction__sleep()
{
return['myOwnProperty'];
}
}
classMagicMethodextendsParentClass
{
private$data=[
'name'=>'MuhamadSuryaIksanudin',
'address'=>'Inyourhearth',
];
}
$magic=newMagicMethod();
var_dump(serialize($magic));
Bila program diatas dieksekusi maka output-nya adalah sebagaiberikut:
XXVII.MagicMethodpadaPHP
219
Untuk melihat bagaimanamagic method __sleep() diterapkanpada kasus nyata, Anda dapat melihat code dari Doctrine yaituORMberikut:
//source:https://github.com/doctrine/doctrine2/blob/master
/lib/Doctrine/ORM/Mapping/ClassMetadata.php#L305
publicfunction__sleep()
{
$serialized=[];
//Thismetadataisalwaysserialized/cached.
$serialized=array_merge($serialized,[
'declaredProperties',
'fieldNames',
//'embeddedClasses',
'identifier',
'className',
'parent',
'table',
'valueGenerationPlan',
]);
//Therestofthemetadataisonlyserializedifnecess
ary.
if($this->changeTrackingPolicy!==ChangeTrackingPolicy
::DEFERRED_IMPLICIT){
$serialized[]='changeTrackingPolicy';
}
if($this->customRepositoryClassName){
$serialized[]='customRepositoryClassName';
}
if($this->inheritanceType!==InheritanceType::NONE){
$serialized[]='inheritanceType';
$serialized[]='discriminatorColumn';
XXVII.MagicMethodpadaPHP
220
$serialized[]='discriminatorValue';
$serialized[]='discriminatorMap';
$serialized[]='subClasses';
}
if($this->isMappedSuperclass){
$serialized[]='isMappedSuperclass';
}
if($this->isEmbeddedClass){
$serialized[]='isEmbeddedClass';
}
if($this->isVersioned()){
$serialized[]='versionProperty';
}
if($this->lifecycleCallbacks){
$serialized[]='lifecycleCallbacks';
}
if($this->entityListeners){
$serialized[]='entityListeners';
}
if($this->namedQueries){
$serialized[]='namedQueries';
}
if($this->namedNativeQueries){
$serialized[]='namedNativeQueries';
}
if($this->sqlResultSetMappings){
$serialized[]='sqlResultSetMappings';
}
if($this->cache){
XXVII.MagicMethodpadaPHP
221
$serialized[]='cache';
}
if($this->readOnly){
$serialized[]='readOnly';
}
return$serialized;
}
Lawan dari magic method __slepp() adalah magic method__wakeup() , yaitumagicmethod yangakandipanggil ketika kitamemanggilfungsi unserialize().Perhatikancontohberikut:
<?php
classConnection
{
protected$link;
private$database;
private$username;
private$password;
private$host;
private$port;
publicfunction__construct($database,$username,$passw
ord,$host='localhost',$port=3306)
{
$this->host=$host;
$this->port=$port;
$this->username=$username;
XXVII.MagicMethodpadaPHP
222
$this->password=$password;
$this->database=$database;
$this->connect();
}
publicfunction__sleep()
{
returnarray('host','port','username','password',
'database');
}
publicfunction__wakeup()
{
$this->connect();
}
privatefunctionconnect()
{
$this->link=newPDO(sprintf('mysql:host=%s;port=%d
;dbname=%s',$this->host,$this->port,$this->database),$th
is->username,$this->password);
}
}
Padacontohdiatas, ketikakitamemanggil fungsi unserialize()maka secara otomatis koneksi akan di-reset yaitu denganmemanggilmethod connect().
Untukcontohnyatapenggunaanmethod __wakeup(),AndadapatmelihatnyapadapotongancodedariframeworkLaravelberikut:
//source:https://github.com/laravel/framework/blob/5.5/src
/Illuminate/Database/Eloquent/Model.php#L1500
XXVII.MagicMethodpadaPHP
223
/**
*Whenamodelisbeingunserialized,checkifitneedsto
bebooted.
*
*@returnvoid
*/
publicfunction__wakeup()
{
$this->bootIfNotBooted();
}
__call()dan__callStatic()
Magic method __call() dan __callStatic() adalah magicmethodyangakandipanggilsecaraotomatisketikakitamemanggilmethodmaupunstaticmethodyangtidakdapatdiakses.
Magicmethod call() akandipanggilsecaraotomatisketikakitamemanggilsebuahmethodyangtidakdapatdiaksespadakonteksobject.Perhatikancontohberikut:
<?php
classMagicMethod
{
privatefunctionfoo(string$name)
{
echo$name;
}
XXVII.MagicMethodpadaPHP
224
publicfunction__call($name,$arguments)
{
if('foo'===$name){
$this->foo($arguments[0]);
}else{
thrownewError(sprintf('Undefinedmethod%son
class%s',$name,__CLASS__));
}
}
}
$magic=newMagicMethod();
$magic->foo('Surya');
echoPHP_EOL;
Padacontohdiataskitamemanggilmethod foo() yangmemilikivisibilitasprivateyangseharusnyatidakdapatdiakses,tapidenganmenggunakan magic method __call() , kita tetap dapatmemanggilnya dan tidak terjadi error. Bila program diatasdijalankanmakahasilnyaadalahsebagaiberikut:
Perludiingatbahwaparameteryangdimasukkankedalammethod,misal pada contohdiatas adalahstring Surya , ketikamasuk kemagicmethod __call()akandikonversimenjadiarray.
Magicmethodselanjutnyaadalahmagicmethod __callStatic() ,yaitumagic method yang akan dipanggil ketika kita memanggilmethod yang tidakdapatdiaksespadakonteksstatic. Perhatikancontohberikut:
XXVII.MagicMethodpadaPHP
225
<?php
classMagicMethod
{
privatefunctionfoo(string$name)
{
echo$name;
}
publicstaticfunction__callStatic($name,$arguments)
{
if('foo'===$name){
self::foo($arguments[0]);
}else{
thrownewError(sprintf('Undefinedmethod%son
class%s',$name,__CLASS__));
}
}
}
MagicMethod::foo('Surya');
echoPHP_EOL;
Bila contoh program diatas dieksekusi maka akan menghasilkanoutputsebagaiberikut:
Magic method __call() dan __callStatic() cukup seringdigunakan pada framework Laravel seperti pada potongan codeberikut:
//source:https://github.com/laravel/framework/blob/5.5/src
XXVII.MagicMethodpadaPHP
226
/Illuminate/Database/Eloquent/Model.php#L1464
publicfunction__call($method,$parameters)
{
if(in_array($method,['increment','decrement'])){
return$this->$method(...$parameters);
}
return$this->newQuery()->$method(...$parameters);
}
/**
*Handledynamicstaticmethodcallsintothemethod.
*
*@paramstring$method
*@paramarray$parameters
*@returnmixed
*/
publicstaticfunction__callStatic($method,$parameters)
{
return(newstatic)->$method(...$parameters);
}
__toString()
Magicmethodterakhiryangakankitabahasadalah __toString().Meski masih banyak magic method dalam PHP, namun tidakmungkinkitamembahasnyasatupersatusehinggasayaputuskanuntuk membahasnya hingga sampai pada magic method__toString()saja.
Magic method __toString() akan dipanggil secara otomatisketika kita berusaha mencetak sebuah object seperti ketika kitamenggunakankeyword echo.Perhatikancontohberikut:
XXVII.MagicMethodpadaPHP
227
<?php
classMagicMethod
{
}
echonewMagicMethod();
echoPHP_EOL;
Pada contoh diatas, bila kita jalankan maka akan terjadi errorsepertigambardibawahini:
Hal ini terjadi karena kita berusaha mencetak sebuah object kelayar menggunakan keyword echo dan hal tersebut tidakdiperbolehkan. Untuk mengatasi hal tersebut, kita harusmengkonversi object kedalam string yaitu dengan menggunakanmagicmethod __toString()sepertipadacontohberikut:
<?php
classMagicMethod
{
publicfunction__toString()
{
return'Sayadicetaknih';
}
}
echonewMagicMethod();
echoPHP_EOL;
XXVII.MagicMethodpadaPHP
228
Sehinggabiladijalankanmakaouput-nyaadalahsebagaiberikut:
Magic method __toString() sangat mudah ditemukan padaframework dan cukup sering digunakan pada proyek. Padaframework,penggunaanmagicmethod __toString()dapatdilihatpadapotongancodedariframeworkSymfonyberikut:
//source:https://github.com/symfony/symfony/blob/master/sr
c/Symfony/Component/Security/Csrf/CsrfToken.php#L55
/**
*ReturnsthevalueoftheCSRFtoken.
*
*@returnstringThetokenvalue
*/
publicfunction__toString()
{
return$this->value;
}
XXVII.MagicMethodpadaPHP
229
FinalClassdanFinalMethodPadababinikitaakanmembahastentangapaitufinalclass,apaitu final method, kegunaan keduanya serta bagaimanamengaplikasikannyadalamcodeyangkitabuat.
ApaituFinalClassPadabeberapakasusdiduniapemrogramanberbasisobjek, kitakadangperlumemastikanbahwasebuahclassyangkitabuattidakdapat diturunkanmenggunakankeyword extends . Ketika kasustersebutterjadi,kitadapatmenggunakankeyword finalsebelumkeyword class untuk memastikan bahwa class tersebut tidakdapatditurunkan.
ContohPenggunaanFinalClassUntuk lebih jelasnya tentang bagaimana final class bekerja,perhatikancontohberikut:
<?php
classFinalClass
{
}
classChildClassextendsFinalClass
XXVIII.FinalClassdanFinalMethod
230
{
}
Pada contoh diatas, kita ingin memastikan bahwa class FinalClass tidak dapat diwariskan, maka kita perlumenambahkankeyword final padaclass FinalClass tersebutmenjadisepertiberikut:
<?php
finalclassFinalClass
{
}
classChildClassextendsFinalClass
{
}
Sehingga bila code diatas dijalankan akan menghasilkan errorsebagaiberikut:
Pada kasus nyata, final class digunakan pada framework ApiPlatform, sebuah framework yang khusus dibuat untukmembangun aplikasi RESTful web API, seperti pada potongancodeberikut:
//source:https://github.com/api-platform/core/blob/master/s
rc/DataProvider/ChainCollectionDataProvider.php
<?php
XXVIII.FinalClassdanFinalMethod
231
/*
*ThisfileispartoftheAPIPlatformproject.
*
*(c)KévinDunglas<[email protected]>
*
*Forthefullcopyrightandlicenseinformation,pleasevi
ewtheLICENSE
*filethatwasdistributedwiththissourcecode.
*/
declare(strict_types=1);
namespaceApiPlatform\Core\DataProvider;
useApiPlatform\Core\Exception\ResourceClassNotSupportedExce
ption;
/**
*Trieseachconfigureddataproviderandreturnstheresul
tofthefirstabletohandletheresourceclass.
*
*@authorKévinDunglas<[email protected]>
*/
finalclassChainCollectionDataProviderimplementsContextAw
areCollectionDataProviderInterface
{
private$dataProviders;
/**
*@paramContextAwareCollectionDataProvclassiderInterfa
ce[]|CollectionDataProviderInterface[]$dataProviders
*/
publicfunction__construct(array$dataProviders)
{
$this->dataProviders=$dataProviders;
}
XXVIII.FinalClassdanFinalMethod
232
/**
*{@inheritdoc}
*/
publicfunctiongetCollection(string$resourceClass,str
ing$operationName=null,array$context=[])
{
foreach($this->dataProvidersas$dataProvider){
try{
if($dataProviderinstanceofRestrictedDataP
roviderInterface
&&!$dataProvider->supports($resourceCla
ss,$operationName,$context)){
continue;
}
return$dataProvider->getCollection($resourc
eClass,$operationName,$context);
}catch(ResourceClassNotSupportedException$e)
{
@trigger_error(sprintf('Throwinga"%s"ina
dataproviderisdeprecatedinfavorofimplementing"%s"',
ResourceClassNotSupportedException::class,RestrictedDataPr
oviderInterface::class),E_USER_DEPRECATED);
continue;
}
}
return[];
}
}
ApaituFinalMethod
XXVIII.FinalClassdanFinalMethod
233
Sama seperti pada pada final class, finalmethod adalah sebuahmethod yang tidak dapat di-override ketika sebuah classditurunkan. Bila kita menggunakan final class maka secaraotomatis semua method yang ada akan menjadi final method,makadengan finalmethod kita dapatmemilihmethodmana sajayang ingin kita buat menjadi final dan tidak dapat di-overridesementaraclass-nyatetapdapatditurunkan.
Meski saya jarang menemukan kasus nyata penggunaan finalmethod namun konsep finalmethod ini cukup penting dipahamikarenamerupakansalahsatu fitur yangadapadaOOP terutamaPHP.
ContohPenggunaanFinalMethodSama seperti pada final class, untuk membuat sebuah methodmenjadi final, kita dapat mengunakan keyword final sebelumpendefinisianvisibilitaspadamethod.Perhatikancontohberikut:
<?php
classFinalClass
{
finalpublicfunctionfinalMethod()
{
}
}
Sehingga bila kita mencoba untuk meng-override methodfinalMethod()sebagaimanacontohdibawahini:
XXVIII.FinalClassdanFinalMethod
234
<?php
classFinalClass
{
finalpublicfunctionfinalMethod()
{
}
}
classChildClassextendsFinalClass
{
publicfunctionfinalMethod()
{
}
}
Maka akan terjadi error sebagaimana tampak pada gambardibawahini:
Penggunaan final method sangat jarang karena biasanyaprogrammer langsungmenggunakan finalclass untukmembatasipenurunan. Hal ini karena final class akan membuat semuamethodmenjadifinalmethodsecaraotomatis.
XXVIII.FinalClassdanFinalMethod
235
XXIX.ObjectsebagaiArrayAndapenggunaframeworkLaravel?ApakahAndafamiliardengansyntax $app['events'] atau sejenisnya?Padapembahasan kaliinikitaakanmembahascaramembuatsyntaxdemikian.
ApaituArrayAccessPadabeberapaframeworkmodernsepertipadaLaraveldanSilexpenggunaan syntax $app['events'] adalah hal yang biasadilakukan.Padahalkalaukitatelusuribahwavariable $appadalahsebuahobjectdanbukanarray.
Arrayaccessadalahpersonifikasisebuahobjectyangseakan-akandia adalaharray. Karenaobject tersebut berperan sebagai arraymaka kita dapat menggunakan syntax array seperti$object['sesuatu']ataupun $object['sesuatu']='nilai'.
ContohPenggunaanArrayAccessPadaPHP,untukmembuatsebuahobjectdapatberperansebagaiarraymakaclassdariobjecttersebutharusmengimplementasikaninterface ArrayAccess. Interface ArrayAccess memiliki4methodyaitu offsetSet() , offsetExists() , offsetUnset() , danoffsetGet().Untuklebihjelasnya,perhatikancontohdibawahini:
XXIX.ObjectsebagaiArray
236
<?php
classArrayAccessClassimplementsArrayAccess
{
private$container;
publicfunctionoffsetSet($offset,$value)
{
if(is_null($offset)){
$this->container[]=$value;
}else{
$this->container[$offset]=$value;
}
}
publicfunctionoffsetExists($offset)
{
returnisset($this->container[$offset]);
}
publicfunctionoffsetUnset($offset)
{
unset($this->container[$offset]);
}
publicfunctionoffsetGet($offset)
{
returnisset($this->container[$offset])?$this->con
tainer[$offset]:null;
}
}
$object=newArrayAccessClass();
$object['name']='MuhamadSuryaIksanudin';
$object['address']='Inyourmemory';
var_dump($object);
XXIX.ObjectsebagaiArray
237
Jika program diatas dieksekusi maka akan menghasilkan outputsebagaiberikut:
Dari contoh diatas terlihat semua key yang kita definisikan akandikonversijadikeypadaproperty $container sebagaimanayangkitadefinisikanpadamethod offsetSet().
PerludiketahuibahwaobjectyangmengimplementasikaninterfaceArrayAccess hanya dapat digunakan untuk akses array sepertidiatasnamuntidakdapatdigunakankedalaminteratorseperti for(){}atau foreach().
XXIX.ObjectsebagaiArray
238
XXX.PerhitunganPajakPPH21Pajak Penghasilan Pasal 21 atau biasa disingkat PPH21 adalahsalah satu komponen wajib pada perhitungan penggajian suatuperusahaan. Pada bab ini kita akan mencoba membuat sebuahmodul untukmenghitung besaran PPH21 yang harus dibayarkanberdasarkanPenghasilanKenaPajakataudisingkatPKP.TentangapaituPPH21dapatAndabacapadahalamanini.
CaraPerhitunganPPH21Dalam perhitungan PPH21, sebenarnya terdapat banyakparameter yang harus dimasukkan, baik sebagai penambahmaupun pengurangnya. Namun pada kesempatan kali ini, kitaakan fokus pada hasil akhir parameter-parameter tersebut yaituPKP(PenghasilanKenaPajak).KarenamenggunakanPKP,makakitacukupmenghitungbesaranpajakberdasarkangolonganPKP-nyasaja.
Dalam kasus ini, kita menganggap bahwa semua yang dihitungpajaknya melalui modul ini adalah orang yang memiliki NPWPsehinggaaturanperhitunganpajaknyaadalahsebagaiberikut:
WajibPajakdenganpenghasilan tahunansampaidenganRp50.000.000,-adalah5%
XXX.PerhitunganPajakPPH21
239
Wajib Pajak dengan penghasilan tahunan di atasRp50.000.000,- sampai dengan Rp250.000.000,- adalah15%
Wajib Pajak dengan penghasilan tahunan di atasRp250.000.000,- sampai denganRp500.000.000,- adalah25%
Wajib Pajak dengan penghasilan tahunan di atasRp500.000.000,-adalah30%
Berdasarkan ketentuan diatas, misalkan Surya mempunyai PKPsebesar Rp60.000.000,- setahun maka perhitungannya adalahsebagaiberikut:
PKP=60.000.000
5%X50.000.000=2.500.000
15%X10.000.000=1.500.000
PPH21=2.500.000+1.500.000=4.000.000
Bagaimana sudahmulaimengerti caramenghitungnya? Jadi dariPKP tidak langsung dikalikan dengan persentase tapi digunakanpersentase berjenjang. Untuk lebih memahami, perhatikan lagicontohberikut:
PKP=300.000.000
5%X50.000.000=2.500.000
15%X200.000.000=30.000.000
25%X50.000.000=12.500.000
XXX.PerhitunganPajakPPH21
240
PPH21=2.500.000+30.000.000+12.500.000=45.000.000
Dari kedua contoh diatas, kita dapat memahami bahwaperhitungan PPH21 itu menggunakan perhitungan berjenjangdimana persentase yang lebih kecil akan tetap dihitung dan jadipengurangpersentaseselanjutnya.
PersiapanProyekPada tahap ini saya ingin kitamenyamakan persepsi bahwaapayangkitabangunadalahsebuahmodulperhitunganPPH21yangmenerapkansemuapembahasanyangtelahkitapelajardariawalhingga akhir. Adapun permasalah yang timbul seperti code yangtidakoptimaldanlainsebagainyaadalahsebuahpembahasanlainyangtidakakankitapermasalahkandisini.
Untuk menyamakan persepsi maka saya akan membuat folderkerja PPH21 yang didalamnya terdapat folder src dan fileindex.php . Folder src adalah tempat kita menyusun solusidalam bentuk code sementara file index.php adalah file yangakankitagunakanuntukmengetessolusiyangkitabuat.Sehinggasusunanfolderkitaakantampaksepertiberikut:
XXX.PerhitunganPajakPPH21
241
PengelompokanMasalahdanSolusiBila kita mengacu pada aturan PPH21 diatas, maka kita dapatmengelompokkan permasalahan berdasarkan aturan tersebutyaitu:
WajibPajakdenganpenghasilan tahunansampaidenganRp50.000.000,-adalah5%
Wajib Pajak dengan penghasilan tahunan di atasRp50.000.000,- sampai dengan Rp250.000.000,- adalah15%
Wajib Pajak dengan penghasilan tahunan di atasRp250.000.000,- sampai denganRp500.000.000,- adalah25%
Wajib Pajak dengan penghasilan tahunan di atasRp500.000.000,-adalah30%
Setiap dari aturan diatas adalah sebuahmasalah tersendiri yangdapatkitarepresentasikansebagaiclass.Sehinggakitasedikitnyaakan membuat 5 class untuk menyelesaikan masalah PPH21tersebut dimana4class adalah sebagaiclass solusi dan 1 classsebagaiclasswrapper atau penggabung yang akan menyatukansemua solusi tersebut sehingga lebih mudah untuk digunakan.Class wrapper inilah yang nantinya akan dipanggil dari fileindex.php.
XXX.PerhitunganPajakPPH21
242
Untuk menyamakan solusi, maka kita perlu membuat interfaceuntuk masing-masing class solusi agar ketika kita panggil padaclass wrapper, kita dapat memastikan bahwa setiap class solusimemilikimethodataufituryangsama.
Dan kita akan menambahkan abstract class untuk digunakanbersama-sama antar class solusi agar tidak tidak mengulang-ngulang code yang dapat digunakan bersama-sama. Sehinggasekarangsusunanfolderkitaakannampaksepertiberikut:
MungkinsampaidisiniAndabingungkenapaharusbanyakfiledanclassyangterlibat,padahalpermasalahnyacukupsimpel.Initidaklain karena kita akanmenerapkan semua yang telah kita pelajarisehinggaAndaakanlebihpahambagaimanaOOPmenyelesaikanmasalah dan bagaimana satu class dengan class lainnya salingberinteraksimembangunsolusi.
PenulisanCodeCodepertamayangkitatulisadalahfile CalculatorInterface.phpyangberisiinterfacedarisemuakalkulatorkita.Danberikutadalahcode-nya:
<?php
XXX.PerhitunganPajakPPH21
243
//file:CalculatorInterface.php
namespaceModernOOP\StudiKasus\PPH21;
interfaceCalculatorInterface
{
publicfunctioncalculate(float$pkp):float;
publicfunctionmaxPkp():float;
publicfunctionminPkp():float;
publicfunctiontaxPercentage():float;
}
Pada code diatas terlihat bahwa interface CalculatorInterfacememiliki4methodsyangharusdiimplementasikanolehclassyangmengimplementasikan interface tersebut. Dengan interface kitadapatmemastikan bahwa seluruh classmemiliki fitur yang samayangtelahdidefinisikanpadainterfacetersebut.
Dari interface diatas, kita dapat membuat abstract class karenapada perhitungan PPH21 kita dapat menggunakan recursivefunction. Berikut adalah code untuk abstract classAbstractCalculatorpadafile AbstractCalculator.php.
<?php
//file:AbstractCalculator.php
namespaceModernOOP\StudiKasus\PPH21;
abstractclassAbstractCalculatorimplementsCalculatorInter
face
{
private$chain;
XXX.PerhitunganPajakPPH21
244
publicfunction__construct(?CalculatorInterface$chain
=null)
{
$this->chain=$chain;
}
publicfunctioncalculate(float$pkp):float
{
$previousValue=0;
if($previous=$this->chain){
$previousValue=$this->chain->calculate($previo
us->maxPkp());
$pkp-=$previous->maxPkp();
}
return($this->taxPercentage()*$pkp)+$previousVa
lue;
}
}
Sedikitsayajelaskantentangcodediatas.Padabarisberikut:
private$chain;
publicfunction__construct(?CalculatorInterface$chain=nu
ll)
{
$this->chain=$chain;
}
Kita mengaitkan antara rule yang satu dengan rule sebelumnyasebagaimana perhitungan PPH21 yang telah kita simulasikandiatas. Misalnya rule kedua ( 15% ) terkait dengan rule pertama
XXX.PerhitunganPajakPPH21
245
( 5% ) dan seterusnya sehingga kita memerlukan baris codetersebut.
$previousValue=0;
if($previous=$this->chain){
$previousValue=$this->chain->calculate($previous->maxP
kp());
$pkp-=$previous->maxPkp();
}
Pada baris diatas, kita mengecek apakah dalam rule tersebutterkaitdenganrulesebelumnyadankalauadakaitandenganrulesebelumnya maka akan dikalkulasi terlebih dahulu. Hal tersebutdilakukan secara recursive dengan memanggil methodcalculate() hingga tidak ada rule yang dikaitkan dengan ruletersebutdankemudianhasilperhitungannyadimasukkankedalamvariable $previousValue.
return($this->taxPercentage()*$pkp)+$previousValue;
Nilai dari variable $previousValue kemudian dijadikan faktorpenambah pada perhitungan akhir dari PPH21 tersebut. Codepadamethod calculate()dariabstractclass AbstractCalculatordiatas adalah inti dari modul perhitungan PPH21 yang kita buatkarena nantinya semua child class akan menggunakan methodtersebut.
Code selanjutnya adalah code dari file FirstRuleCalculator.phpdanberikutadalahisinya:
<?php
XXX.PerhitunganPajakPPH21
246
//file:FirstRuleCalculator.php
namespaceModernOOP\StudiKasus\PPH21;
classFirstRuleCalculatorextendsAbstractCalculator
{
publicfunctionmaxPkp():float
{
return50000000;
}
publicfunctionminPkp():float
{
return0;
}
publicfunctiontaxPercentage():float
{
return0.05;
}
}
Terlihat simpel sekali bukan? Itu karena kita sudah membuatabstractclass AbstractCalculatoryangberisilogicintidarimodulkita sehingga child class hanya mengimplementasikan method-methodyangbelumdiimplementasikansaja.
Untuk class-class selanjutnya pun code-nya akan mirip-miripdenganclass FirstRuleCalculatorkarenamemanghanyatinggalmengubah isi dari method minPkp() , maxPkp() dantaxPercentage()saja.Danberikutadalahcode-nya.
<?php
//file:SecondRuleCalculator.php
XXX.PerhitunganPajakPPH21
247
namespaceModernOOP\StudiKasus\PPH21;
classSecondRuleCalculatorextendsAbstractCalculator
{
publicfunctionmaxPkp():float
{
return250000000;
}
publicfunctionminPkp():float
{
return50000000;
}
publicfunctiontaxPercentage():float
{
return0.15;
}
}
<?php
//file:ThirdRuleCalculator.php
namespaceModernOOP\StudiKasus\PPH21;
classThirdRuleCalculatorextendsAbstractCalculator
{
publicfunctionmaxPkp():float
{
return500000000;
}
publicfunctionminPkp():float
{
return250000000;
}
XXX.PerhitunganPajakPPH21
248
publicfunctiontaxPercentage():float
{
return0.25;
}
}
<?php
//file:FourthRuleCalculator.php
namespaceModernOOP\StudiKasus\PPH21;
classFourthRuleCalculatorextendsAbstractCalculator
{
publicfunctionmaxPkp():float
{
return10000000000000000;//bikinsebesarmungkinsam
paiimpossibleadaorangsetahundapatsegitu
}
publicfunctionminPkp():float
{
return500000000;
}
publicfunctiontaxPercentage():float
{
return0.3;
}
}
Bagaimana, jadi terlihat lebihsimpelkan denganadanyaabstractclass AbstractCalculator?Selanjutnyakitaakanmembuatclasswrapper untuk memudahkan dalam pemanggilan pada file index.php . Class wrapper PPH21Calculator tersebut berisisebagaiberikut:
XXX.PerhitunganPajakPPH21
249
<?php
//file:PPH21Calculator.php
namespaceModernOOP\StudiKasus\PPH21;
finalclassPPH21Calculator
{
private$calculators;
publicfunction__construct(CalculatorInterface...$calc
ulators)
{
$this->calculators=$calculators;
}
publicfunctioncalculate(float$pkp):float
{
foreach($this->calculatorsas$calculator){
if($pkp<$calculator->maxPkp()&&$pkp>=$cal
culator->minPkp()){
return$calculator->calculate($pkp);
}
}
}
}
Setelah semua class telah dibuat, maka selanjutnya kita tinggalbuat file pemanggilnya yaitu index.php . File tersebut berisisebagaiberikut:
<?php
require__DIR__.'/src/CalculatorInterface.php';
require__DIR__.'/src/AbstractCalculator.php';
require__DIR__.'/src/FirstRuleCalculator.php';
require__DIR__.'/src/SecondRuleCalculator.php';
XXX.PerhitunganPajakPPH21
250
require__DIR__.'/src/ThirdRuleCalculator.php';
require__DIR__.'/src/FourthRuleCalculator.php';
require__DIR__.'/src/PPH21Calculator.php';
useModernOOP\StudiKasus\PPH21\FirstRuleCalculator;
useModernOOP\StudiKasus\PPH21\SecondRuleCalculator;
useModernOOP\StudiKasus\PPH21\ThirdRuleCalculator;
useModernOOP\StudiKasus\PPH21\FourthRuleCalculator;
useModernOOP\StudiKasus\PPH21\PPH21Calculator;
$first=newFirstRuleCalculator();
$second=newSecondRuleCalculator($first);
$third=newThirdRuleCalculator($second);
$fourth=newFourthRuleCalculator($third);
$calculator=newPPH21Calculator($first,$second,$third,$
fourth);
//1.250.000
echo$calculator->calculate(25000000);
echoPHP_EOL;
//1.500.000
echo$calculator->calculate(30000000);
echoPHP_EOL;
//2.250.000
echo$calculator->calculate(45000000);
echoPHP_EOL;
//2.500.000
echo$calculator->calculate(50000000);
echoPHP_EOL;
//4.000.000
echo$calculator->calculate(60000000);
echoPHP_EOL;
//6.250.000
echo$calculator->calculate(75000000);
echoPHP_EOL;
//32.500.000
XXX.PerhitunganPajakPPH21
251
echo$calculator->calculate(250000000);
echoPHP_EOL;
//45.000.000
echo$calculator->calculate(300000000);
echoPHP_EOL;
//82.500.000
echo$calculator->calculate(450000000);
echoPHP_EOL;
//170.000.000
echo$calculator->calculate(750000000);
echoPHP_EOL;
Komentar pada file index.php diatas adalah espektasi yangseharusnya dari perhitungan PPH21 yang benar. Jika fileindex.phptersebutdieksekusimakaoutput-nyasebagaiberikut:
Dari gambar diatas, terlihat antara espektasi danoutput programdihasilkan telahsama.Sampaidisini,berartiprogramperhitunganPPH21 yang kita buat telah selesai dan hasilnya sesuai denganaturanyangberlaku.
Denganstudikasusdiatas,kitatelahmenerapkansemuayangkitapelajaritentangOOPmulaidariclasshinggafinalclass.Selain itukita juga dapat mengambil kesimpulan bahwa dengan OOP kitadapat menyelesaikan permasalahan dengan lebih mudah karenakita dapat memecah masalah menjadi bagian yang lebih kecilseperti pada class-class FirstRuleCalculator ,
XXX.PerhitunganPajakPPH21
252
SecondRuleCalculator , dan seterusnya sehingga solusi yangdibuatpunmenjadilebihsimpeldanfokuspadasatumasalahkecilsaja.
Sebenarnya untuk menyelesaikan masalah PPH21 tersebut, kitadapat membuatnya menjadi lebih simpel hanya satu class sajasebagaiberikut:
<?php
classPPH21Calculator
{
privatefunctionfirstRule(float$pkp):float
{
if(0<$pkp&&50000000>=$pkp){//0-50jt
return$pkp*0.05;
}
return0;
}
privatefunctionsecondRule(float$pkp):float
{
if(50000000<$pkp&&250000000>=$pkp){//50jt-
250jt
$pkp-=50000000;
$prev=$this->firstRule(50000000);
return($pkp*0.15)+$prev;
}
return0;
}
XXX.PerhitunganPajakPPH21
253
privatefunctionthirdRule(float$pkp):float
{
if(250000000<$pkp&&500000000>=$pkp){//250jt
-500jt
$pkp-=250000000;
$prev=$this->secondRule(250000000);
return($pkp*0.25)+$prev;
}
return0;
}
privatefunctionfourthRule(float$pkp):float
{
if(500000000<$pkp&&10000000000000000>=$pkp){
//>500jt
$pkp-=500000000;
$prev=$this->thirdRule(500000000);
return($pkp*0.3)+$prev;
}
return0;
}
publicfunctioncalculate(float$pkp):float
{
return$this->firstRule($pkp)?:$this->secondRule($
pkp)?:$this->thirdRule($pkp)?:$this->fourthRule($pkp);
}
}
$pph21=newPPH21Calculator();
XXX.PerhitunganPajakPPH21
254
//1250000
echo$pph21->calculate(25000000);
echoPHP_EOL;
//1500000
echo$pph21->calculate(30000000);
echoPHP_EOL;
//2250000
echo$pph21->calculate(45000000);
echoPHP_EOL;
//2500000
echo$pph21->calculate(50000000);
echoPHP_EOL;
//4000000
echo$pph21->calculate(60000000);
echoPHP_EOL;
//6250000
echo$pph21->calculate(75000000);
echoPHP_EOL;
//32500000
echo$pph21->calculate(250000000);
echoPHP_EOL;
//45000000
echo$pph21->calculate(300000000);
echoPHP_EOL;
//82500000
echo$pph21->calculate(450000000);
echoPHP_EOL;
//170000000
echo$pph21->calculate(750000000);
echoPHP_EOL;
Bagaimana, apakah Anda merasa zonk? Tidak perlu merasademikian karena contohdiatas hanya sebagai perbandingan sajadan apa yang telah Anda buat jauh lebih mudah dimaintainseandainya terdapat perubahan peraturan maupun penambahanrulebarudikemudianhari.
XXX.PerhitunganPajakPPH21
255
XXX.PerhitunganPajakPPH21
256
XXXI.PackageManagementdenganComposerSalahsatuhaltersulitdalammengerjakansebuahproyeksoftwaredevelopment adalah me-manage dependency library yangdibutuhkan dalam proyek tersebut. Hal ini akan dipermudahdengan sebuah tool yaitu composer yang akan kita bahas padababini.
ApaituComposerComposeradalahsebuahtoolyangbertujuanuntukmemudahkandeveloper dalam me-manage dependency pada proyek berbasisPHP. Composer seperti npm pada NodeJs, yum pada Redhat,bundlerpadaRuby,atauaptpadaUbuntu.
Composer dapatdigunakanuntukmeng-install,meng-update danmenghapus library yang kita gunakan dalam proyek kita.ComposerpertamakalidiperkenalkanolehJordiBoggianodanNilsAdermann tidak lama setelah PSR-0 disetujui sebagai standardpadaekosistemPHP.
Composer pertama kali digunakan sebagai tool instalasi utamaoleh framework Symfony karena Jordi adalah salah satu coredeveloper framework tersebut. Sehingga penamaan folder yangdigunakan oleh composer pun menggunakan konsensus yangberlakupadaframeworkSymfonyyaitufolder vendor.
XXXI.PackageManagementdenganComposer
257
KenapaMenggunakanComposerSelainsebagaidependencymanager,composerjugamemilikifiturutamayaituautoloaddimanakitatidakperlulagimeng-includefilePHP satu per satu seperti yang terjadi pada pembahasansebelumnya. Pada pembahasan sebelumnya, ketika kita hendakmenggunakan file atau class kita harus meng-include sebagaiberikut:
require__DIR__.'/src/CalculatorInterface.php';
require__DIR__.'/src/AbstractCalculator.php';
require__DIR__.'/src/FirstRuleCalculator.php';
require__DIR__.'/src/SecondRuleCalculator.php';
require__DIR__.'/src/ThirdRuleCalculator.php';
require__DIR__.'/src/FourthRuleCalculator.php';
require__DIR__.'/src/PPH21Calculator.php';
Bila kebutuhan file atau class kita sedikit, mungkin hal tersebuttidak terlalumasalah, namun jika kebutuhan file atau class yangkitaperlukanbanyak,makahal tersebutakansangatmenyulitkandan cukup memakan banyak line code. Dengan composer haltersebut tak perlu terjadi karena composer dapat digunakansebagai autoloader yaitu dengan memanggil file autoload.phpyangadadifolder vendorsebagaiberikut:
require__DIR__.'/vendor/autoload.php';
Dengansatubarisdiatasmakakita tidakperlu lagimenggunakanblock requirelagisehinggakitabisamenghapusblock requireyangpanjangdiatas sehingga index.php akanmenjadi sebagai
XXXI.PackageManagementdenganComposer
258
berikut:
<?php
require__DIR__.'/vendor/autoload.php';
useModernOOP\StudiKasus\PPH21\FirstRuleCalculator;
useModernOOP\StudiKasus\PPH21\SecondRuleCalculator;
useModernOOP\StudiKasus\PPH21\ThirdRuleCalculator;
useModernOOP\StudiKasus\PPH21\FourthRuleCalculator;
useModernOOP\StudiKasus\PPH21\PPH21Calculator;
$first=newFirstRuleCalculator();
$second=newSecondRuleCalculator($first);
$third=newThirdRuleCalculator($second);
$fourth=newFourthRuleCalculator($third);
$calculator=newPPH21Calculator($first,$second,$third,$
fourth);
//1250000
echo$calculator->calculate(25000000);
echoPHP_EOL;
//1500000
echo$calculator->calculate(30000000);
echoPHP_EOL;
//2250000
echo$calculator->calculate(45000000);
echoPHP_EOL;
//2500000
echo$calculator->calculate(50000000);
echoPHP_EOL;
//4000000
echo$calculator->calculate(60000000);
echoPHP_EOL;
//6250000
echo$calculator->calculate(75000000);
XXXI.PackageManagementdenganComposer
259
echoPHP_EOL;
//32500000
echo$calculator->calculate(250000000);
echoPHP_EOL;
//45000000
echo$calculator->calculate(300000000);
echoPHP_EOL;
//82500000
echo$calculator->calculate(450000000);
echoPHP_EOL;
//170000000
echo$calculator->calculate(750000000);
echoPHP_EOL;
Sehinggadapatdisimpulkan,selainsebagaidependencymanager,composerjugadigunakansebagaiautoloaderuntukmeminimalkanpenggunaan requirepadacodekita.
InstalasiComposerUntukmenginstallcomposersangatlahmudah,Andacukupmeng-copyscriptberikut:
php-r"copy('https://getcomposer.org/installer','composer-
setup.php');"
php-r"if(hash_file('SHA384','composer-setup.php')==='5
44e09ee996cdf60ece3804abc52599c22b1f40f4323403c44d44fdfdd586
475ca9813a858088ffbc1f233e9b180f061'){echo'Installerveri
fied';}else{echo'Installercorrupt';unlink('composer-s
etup.php');}echoPHP_EOL;"
phpcomposer-setup.php
php-r"unlink('composer-setup.php');"
XXXI.PackageManagementdenganComposer
260
Padascript diatas, kitamelakukan4 hal yaitudownload installer,verifikasi SHA384 ,menjalankansetup dan kemudianmenghapusfile installer. Bila semua tahap diatas berhasil, maka pada foldertempat kita menjalankan script diatas akan terdapat filecomposer.pharsepertipadagambarberikut:
Bila Anda menggunakan sistem operasi Windows, Anda dapatmenggunakan installer yang lebih mudah melalui link berikutsehingga nantinya Anda dapat membuka command prompt danmenjalankanperintah composer-V.
Sementara untuk pengguna sistem operasi Linux atau Unix-Likekitadapatmemindahkanfile composer.phardanmenggantinamamenjadi /usr/local/bin/composer kemudianmenjalakanperintahsudo chmod a+x /usr/local/bin/composer sehingga akan tampaksebagaiberikut:
Baik menggunakan Windows, Linux, maupun Unix-Like, padaakhirnyaAnda dapatmenjalankan perintah composer -V sepertipadagambarberikut:
XXXI.PackageManagementdenganComposer
261
Tentangcomposer.json
Untuk menggunakan composer, Anda harus membuat filecomposer.json pada rootproject dankemudianmendeskripsikankebutuhankitapadablock requiredifile composer.jsonsebagaiberikut:
{
"require":{
"monolog/monolog":"1.23.*"
}
}
Padacode diatas, kita mendeskripsikan bahwa proyek yang kitabangun membutuhkan package monolog/monolog dengan versi1.23.*yangartinyaadalahversi 1.23.ndimana nadalahversiterakhirdariversidasar 1.23 . Penulisan versi sendirimengikutistandardSemanticVersioningdanmerujukpada tagdi repositoryGithub.
Pada contoh diatas, ketika kita menjalankan composer update
maka secara otomatis composer akan men-download package monolog/monolog dari Packagist dan menyimpannya di foldervendorsejajardenganfile composer.json.
XXXI.PackageManagementdenganComposer
262
Bagian penting lainnya dari composer.json adalah blockautoload dimana kita mendeskripsikan namespace dari proyekkitasertarootsource-nya.Perhatikancontohberikut:
"autoload":{
"psr-4":{
"ModernOOP\\StudiKasus\\PPH21\\":"src/"
}
}
Padacontohdiatas,kitamemberitahucomposerbahwaproyekkitamemiliki namespace ModernOOP\StudiKasus\PPH21 dan merujukpadafolder src.Iniberartibahwasemuaclassyangadadifolder src harus dimulai dengan namespaceModernOOP\StudiKasus\PPH21sebagaimanaaturanpadaPSR-4.
Untukketeranganlebihlengkaptentangfile composer.json ,Andadapatmembacanyapadahalamanini.
XXXI.PackageManagementdenganComposer
263
XXXII.MembuatPackageSendiriPadabab ini kitaakanmembahasbagaimanamembuatpackagecomposer sendiri dan kemudian mendaftarkannya ke Packagist.PackageyangakankitadaftarkanadalahmodulkalkulatorPPH21yangtelahkitabuatsebelumnya.
Membuatcomposer.json
Seperti yang sudah dibahas pada bab sebelumnya tentangcomposer, kali ini kita akan membuat file composer.json untukmodulkalkulatorPPH21kita. Isidari file composer.json tersebutadalahsebagaiberikut:
{
"type":"library",
"license":"proprietary",
"name":"modernoop/pph21",
"authors":[
{
"name":"MuhamadSuryaIksanudin",
"email":"[email protected]",
"homepage":"https://github.com/ad3n"
}
],
"require":{
"php":"^7.2"
},
XXXII.MembuatPackageSendiri
264
"config":{
"preferred-install":{
"*":"dist"
},
"sort-packages":true,
"classmap-authoritative":true,
"optimize-autoloader":true
},
"autoload":{
"psr-4":{
"ModernOOP\\StudiKasus\\PPH21\\":"src/"
}
}
}
File tersebut diletakkan sejajar folder PPH21 atau pada rootproject. Pada file composer.json diatas, autoload PSR-4 kitaarahkankefolder srckarenasemuaclassyangkitabuatberadadisana. Kemudian jalankan perintah composer update hinggaselesai maka pada folder PPH21 akan muncul folder baru yaitufolder vendoryangberisifolder composerdanfile autoload.php.
TidakadafolderlainkarenakitatidakmenggunakanlibraryapapunsebagaidependencydarimodulkalkulatorPPH21tersebut.
AutoloaddenganComposerSebelummendaftarkanpackagekitakePackagist,terlebihdahulukitateshasilinstalasikitadenganmengubahfile index.php yangberisibanyak requiresepertidibawahini:
require__DIR__.'/src/CalculatorInterface.php';
XXXII.MembuatPackageSendiri
265
require__DIR__.'/src/AbstractCalculator.php';
require__DIR__.'/src/FirstRuleCalculator.php';
require__DIR__.'/src/SecondRuleCalculator.php';
require__DIR__.'/src/ThirdRuleCalculator.php';
require__DIR__.'/src/FourthRuleCalculator.php';
require__DIR__.'/src/PPH21Calculator.php';
Menjadiautoload dengan hanya sebuah baris require sebagaiberikut:
require__DIR__.'/vendor/autoload.php';
Sehinggaisidarifile index.phpberubahmenjadisebagaiberikut:
<?php
require__DIR__.'/vendor/autoload.php';
useModernOOP\StudiKasus\PPH21\FirstRuleCalculator;
useModernOOP\StudiKasus\PPH21\SecondRuleCalculator;
useModernOOP\StudiKasus\PPH21\ThirdRuleCalculator;
useModernOOP\StudiKasus\PPH21\FourthRuleCalculator;
useModernOOP\StudiKasus\PPH21\PPH21Calculator;
$first=newFirstRuleCalculator();
$second=newSecondRuleCalculator($first);
$third=newThirdRuleCalculator($second);
$fourth=newFourthRuleCalculator($third);
$calculator=newPPH21Calculator($first,$second,$third,$
fourth);
//1250000
echo$calculator->calculate(25000000);
XXXII.MembuatPackageSendiri
266
echoPHP_EOL;
//1500000
echo$calculator->calculate(30000000);
echoPHP_EOL;
//2250000
echo$calculator->calculate(45000000);
echoPHP_EOL;
//2500000
echo$calculator->calculate(50000000);
echoPHP_EOL;
//4000000
echo$calculator->calculate(60000000);
echoPHP_EOL;
//6250000
echo$calculator->calculate(75000000);
echoPHP_EOL;
//32500000
echo$calculator->calculate(250000000);
echoPHP_EOL;
//45000000
echo$calculator->calculate(300000000);
echoPHP_EOL;
//82500000
echo$calculator->calculate(450000000);
echoPHP_EOL;
//170000000
echo$calculator->calculate(750000000);
echoPHP_EOL;
Kita kemudian dapat mengetesnya dengan memanggil fileindex.php menggunakan perintah php index.php dari rootproject. Bila tidak ada error maka berarti instalasi composer kitatelah berhasil dan kita sudah menggunakan fitur autoload daricomposer.
XXXII.MembuatPackageSendiri
267
PendaftaranPackageSebelummendaftarkanpackagekePackagist,terlebihdahulukitaharus meng-upload-nya ke Github. Untuk tutorial bagaimanameng-upload proyek atau package kita ke Github, Anda dapatmembacatutoriallengkapnyapadalinkini.
Anggap saja Anda sudah meng-upload proyek kita ke Githubdengan URL https://github.com/ad3n/PPH21 , maka selanjutnyakita buka website Packagist yaitu https://packagist.org
kemudian kita pilih menu sign in dan pilih Use Github. Ikutiprosesnyahinggakemudiankitaotomatisakanlogindengantampilhalamansebagaiberikut:
Kemudian kita pilih menu submit dan kemudian masukkan URLrepositorykitayaitu https://github.com/ad3n/PPH21dankemudianpilihtombolcheck.Bilanamayangkitadaftarkantelahada,makaAndadapatmengganti baris "name": "modernoop/pph21" dengannamayangsesuaidengankeinginanAnda.Setelah itu kemudiankliktombolSubmit.
Bila kita berhasilmendaftarkanpackage kita,maka akanmuncultampilansebagaiberikut:
XXXII.MembuatPackageSendiri
268
Bila sudah maka kita dapat meng-install package kitamenggunakancomposer denganmenjalankan perintah composerrequiremodernoop/pph21.Secaraotomatisdidalamfolder vendorakanmunculfolder modernoop/pph21 yangberisimodulkalkulatorPPH21.
SinkronisasiGithubdanPackagistSecara default, Packagist tidak melakukan sinkronisasi antararepositorykitayangdiGithubdenganpackageyangkitadaftarkanpada Packagist. Untuk itu kita perlu menggunakan GithubHookServicesuntukmelakukanhaltersebut.
Untukmelakukanhal tersebut,pertamakalikitaharusmeng-copyAPI Token yang ada pada halaman profile Packagist terlebihdahulu.HalamanAPITokentampaksepertigambarberikut:
XXXII.MembuatPackageSendiri
269
Kemudian kita kembali ke halaman repository kita di Github danklikmenusettingdanpilih integrationsandserviceskemudiankliktombol add service lalu ketikan Packagist seperti tampak padagambarberikut:
Lalu isikanusername denganusername Github, kemudian tokendengan API Token dari Packagist. Pada isian URL, Anda dapatmengosonginya. Kemudian klik tombolAdd Service seperti padagambarberikut:
XXXII.MembuatPackageSendiri
270
Dengancara seperti itu,maka jika kitameng-updatecode kita diGithubrepositorymakasecaraotomatispadaPackagistjugaakanter-update.
XXXII.MembuatPackageSendiri
271
XXXIII.DesignPatternPerkembanganteknologidanmetodelogipengembangansoftwarememunculkan tool-tool yang dapat digunakan untuk membantumenyelesaikan masalah dan mempercepat pengambangansoftwareitusendiri.Salahsatutooltersebutadalahdesignpatternyangakankitabahaspadababini.
ApaituDesignPatternDesign pattern adalah solusi umum yang berupa best practiceyang digunakan untuk menyelesaikan permasalah-permasalahanyang muncul pada pengembangan software. Design patternsemakinberkembangdenganadanyakonsepOOPkarenahampirsemua design pattern diimplementasikan menggunakan konseptersebut.
Design pattern banyak diimplementasikan dalam pembuatansebuah framework. Dalam sebuah framework terdapat banyakdesignpatternyangdigunakandansalingmendukungsatudenganlainnya untuk menyelesaikan permasalahan yang ada padaframeworktersebut.
ManfaatPenggunaanDesignPattern
XXXIII.DesignPattern
272
Pemahaman tentang design pattern sangat bermanfaat dalammempercepat perancangan solusi serta memberikan kemudahanbagideveloper lain untukmemahami code yang kita tulis. Selainitu, code yang ditulis menggunakan design pattern akan lebihmudahuntukdi-refactoringdikemudianhari.
Manfaat lain penggunaan design pattern adalah efisiensi code,dimanacodeyangkitatulismenjadilebihsingkatdanlebihmudahfokusdalammenyelesaikanpermasalahantertentusehinggaketikamasalah tersebutberkembang,makakitadapatmengembangkansolusiyangbarudenganlebihmudah.
Macam-MacamDesignPatternSecaragarisbesar,designpatterndibagimenjadi3yaitu:
Kreasional(Creational)
Pattern ini fokus pada bagaimana sebuah object diinstansiasi.Contohpattern iniadalahabstract factory,builder,pool,singleton,danlainsebagainya.
Struktural
Patterninifokuspadabagaimanamempermudahclassatauobjectyangmemilikifungsionalitasbaruberkolaborasi.Contohpatterniniadalahdecorator,adapter,composite,danlainsebagainya.
Behavioral
XXXIII.DesignPattern
273
Pattern ini mengatur bagaimana komunikasi antar class dalammenyelesaikan sebuah masalah. Contoh pattern ini antara lainchainofresponsibilities,strategy,observerdanmasihbanyaklagi.
Pada modul PPH21 yang telah kita buat sebelumnya, kitamenggunakanchainofresponsibilitiespatternuntukdimanasetiapkalkulator dikaitkan dengan kalkulator lainnya. Pembahasantentang design pattern akan dibuat pada buku yang terpisah,tungguselalubuku-bukudarisayaya(promositidakterselubung).
XXXIII.DesignPattern
274
XXXIV.StudiKasusMembuatFrameworkSederhanaPadapembahasankaliinikitaakanmencobamembuatframeworksendirimenggunakanpackageyangtelahadadiPackagist.
SkopProyekAgar pembahasan pada bab ini tidak melebar jauh, maka sayaakan membatasi pembahasan kali ini yaitu tentang bagaimanamenampilkan pesan Hello World dan pesan Selamat Datang,
{nama} menggunakan mekanisme framework. Framework yangkita bangun pada bab ini adalah sebuah framework sederhanayanghanyadapatmenangani request yangsederhana tanpaadainteraksidengandatabasedanform.
KonsepFrontControllerSebagai awal pembuatan framework kita saya telah membuatfolder ModernFramework sebagai root project kita danberisi 2 fileyaitu hello.phpdan greeting.phpdimanaisidarimasing-masingfileadalahsebagaiberikut:
<?php
//filename:hello.php
XXXIV.StudiKasusMembuatFrameworkSederhana
275
echo'HelloWorld';
<?php
//filename:greeting.php
echosprintf('SelamatDatang,%s',$_GET['nama']);
Secara normal, untuk memanggil file tersebut maka kita harusmembuat browser dan mengetikkan http://localhost:8000/hello.php untuk file hello.php dan http://localhost:8000/greeting.php?nama=Surya untuk filegreeting.php.
Anda tidak perlu bingung kenapa ada angka 8000 setelahlocalhost , itu hanyalahnomerport yangdihasilkan ketika sayamenggunakanbuilt-inwebserverdiPHPmenggunakanperintah:
php-Slocalhost:8000
Karenapadaframeworkbiasanyamenggunakan1file index.phpsebagaifrontcontrollermakasayamembuatfile index.phpsejajardengan file hello.php dan greeting.php dengan isi sebagaiberikut:
<?php
//filename:index.php
if('/hello'===$_SERVER['REQUEST_URI']){
require'hello.php';
}
XXXIV.StudiKasusMembuatFrameworkSederhana
276
if(false!==strpos($_SERVER['REQUEST_URI'],'/greeting'))
{
require'greeting.php';
}
Ketikakitamenjalankanbuilt-inwebservermenggunakanperintahdibawahini:
Kemudian membuat browser maka hasilnya adalah sebagaiberikut:
Sampai tahap ini kita telah mengimplementasikan konsep frontcontroller yaitu hanyamenggunakan 1 file saja sebagai pengaturrequestyangmasukyaitufile index.php.
HTTPRequestdanHTTPResponse
XXXIV.StudiKasusMembuatFrameworkSederhana
277
Padadasarnyaketikaklienmembuatsebuahhalamanwebmakasebenarnya dia telah melakukan request ke server yang disebutsebagai HTTPRequest dan kemudian ketika server memberikanresponse,setelahmemprosesrequesttersebutsebelumnya,itulahyangdisebutsebagaiHTTPResponse.
Pada pembahasan kali ini kita akan menggunakan OOP untukmemprosesrequestdanresponsepadaframeworkyangkitabuat.Untukitukitaperlumeng-installpackagemenggunakancomposer.Package yang akan kita gunakan adalah symfony/http-
foundation , package yang sama yang digunakan juga olehframeworkLaraveluntukmenanganirequestdanresponse.
Untukmeng-installkitacukupmenjalankanperintah composerreqsymfony/http-foundation dari root project dan secara otomatiscomposerakanmembuatfile composer.jsondanmeng-install-kanpackagetersebutuntukkitasebagaimanagambarberikut:
Setelahberhasilmeng-install,selanjutnyakitaperlumengubahfile index.php dan mendaftarkan composer autoloader sebagaiberikut:
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
XXXIV.StudiKasusMembuatFrameworkSederhana
278
$request=Request::createFromGlobals();
$path=$request->getPathInfo();
$route=['/hello'=>'hello.php','/greeting'=>'greeting.
php'];
if(isset($route[$path])){
include$route[$path];
}
Dengan code diatas, kita telah mengimplementasikan Symfonyrequest ( Symfony\Component\HttpFoundation\Request ) untukmengarahkan permintaan klien. Sedikit penjelasan tentang codediatas, baris code $request = Request::createFromGlobals()
adalahprosesinstansiasiobject RequestberdasarkanPHPsuperglobalvariable( $_GET, $_POST,danseterusnya).
Kitaperlumengubahjugafile hello.phpdan greeting.phpuntukmengimplementasikan Symfony response agar framework yangkitabuatlebihpowerful.
<?php
//filename:hello.php
useSymfony\Component\HttpFoundation\Response;
$response=newResponse();
$response->setContent('HelloWorld');
$response->send();
<?php
XXXIV.StudiKasusMembuatFrameworkSederhana
279
//filename:greeting.php
useSymfony\Component\HttpFoundation\Response;
$response=newResponse();
$response->setContent(sprintf('SelamatDatang,%s',$request
->get('nama')));
$response->send();
Padabaris $request->get('nama') inimengambil parameter darirequest yaitu nama . Sehingga bila kita membuka halamanwebmisal http://localhost:8000/greeting?nama=Surya , maka$request->get('nama')akanmengembalikannilai Surya.
Karena pada file hello.php dan greeting.php sama-samamenggunakan variable $response , maka kita dapatmemindahkannya ke index.php agar lebih simpel dan mudahdibaca.Sehinggafile-filetersebutakanmenjadisebagaiberikut:
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
$request=Request::createFromGlobals();
$path=$request->getPathInfo();
$response=newResponse();
$route=['/hello'=>'hello.php','/greeting'=>'greeting.
php'];
XXXIV.StudiKasusMembuatFrameworkSederhana
280
if(isset($route[$path])){
include$route[$path];
}
$response->send();
<?php
//filename:hello.php
$response->setContent('HelloWorld');
<?php
//filename:greeting.php
$response->setContent(sprintf('SelamatDatang,%s',$request
->get('nama')));
Bagaimana menjadi lebih simpel bukan? Kita juga dapatmenambahkanresponse jika 404 jikaternyatahalamanyangdi-requesttidakditemukan.
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
$request=Request::createFromGlobals();
$path=$request->getPathInfo();
XXXIV.StudiKasusMembuatFrameworkSederhana
281
$response=newResponse();
$route=['/hello'=>'hello.php','/greeting'=>'greeting.
php'];
if(isset($route[$path])){
include$route[$path];
}else{
$response->setContent('Halamantidakditemukan');
$response->setStatusCode(Response::HTTP_NOT_FOUND);
}
$response->send();
Sampai disini pembahasan kita tentang request dan responsepadaframeworkkitasebagaiimplementasidariHTTPRequestdanHTTP Response. Untuk pemahaman lebih mendalam tentangkomponenSymfonyHTTPFoundation, Anda dapatmembacanyamelaluilinkini.
MengarahkanRequestdenganRouterPada framework modern seperti Symfony, Zend, dan Laravel,routingsystemadalahsalahsatukomponenpenting.Samahalnyadengan framework-framework tersebut, framework yang kita
XXXIV.StudiKasusMembuatFrameworkSederhana
282
bangunpunakanmenerapkanroutingsystemuntukmengarahkanrequestmenujuhalamanyangakanmemproses request tersebuthinggamenghasilkanresponse.
Untukmenerapkanroutingsystemkitaperlumeng-installpackagesymfony/routing dengan menjalankan perintah composer req
symfony/routingdarirootprojectsebagaimanagambarberikut:
Sebelumkitamenggunakan routingsystempada frameworkyangkita buat, ada baiknya kita pahami terlebih dahulu bagaimanaSymfonyroutingbekerja.Perhatikancontohberikut:
<?php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\Routing\Matcher\UrlMatcher;
$request=Request::createFromGlobals();
$routes=newRouteCollection();
$routes->add('hello',newRoute('/hello'));
$routes->add('greeting',newRoute('/greeting/{nama}',['nam
a'=>'Surya']));
XXXIV.StudiKasusMembuatFrameworkSederhana
283
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
print_r($matcher->match('/hello'));
/**
*Array
*(
*[_route]=>hello
*)
*/
print_r($matcher->match('/greeting'));
/**
*Array
*(
*[nama]=>Surya
*[_route]=>greeting
*)
*/
print_r($matcher->match('/greeting/Ihsan'));
/**
*Array
*(
*[nama]=>Ihsan
*[_route]=>greeting
*)
*/
Dari contohdiatas terlihat bahwaketika kitamelakukanmatchingterhadappathrequestmakaSymfonyroutingakanmengembalikansebuaharraydenganindeks _routeuntuknamadarirouteyangsesuaidanrouteparamjikaroutetersebutmemilikiparameter.
XXXIV.StudiKasusMembuatFrameworkSederhana
284
Setelah kita memahami bagaimana Symfony routing bekerja,selanjutnyakitaubahfile index.php untukmengimplementasikanroutingsystemsebagaiberikut:
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\Routing\Matcher\UrlMatcher;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
$request=Request::createFromGlobals();
$routes=newRouteCollection();
$routes->add('hello',newRoute('/hello'));
$routes->add('greeting',newRoute('/greeting/{nama}',['nam
a'=>'Surya']));
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
try{
$response=newResponse();
extract($matcher->match($request->getPathInfo()));
includesprintf('%s.php',$_route);
}catch(ResourceNotFoundException$e){
XXXIV.StudiKasusMembuatFrameworkSederhana
285
$response=newResponse('Halamantidakditemukan',Resp
onse::HTTP_NOT_FOUND);
}
$response->send();
Kita juga perlu mengubah file greeting.php menjadi sepertiberikut:
<?php
//filename:greeting.php
$response->setContent(sprintf('SelamatDatang,%s',$nama));
Dengan code diatas, ketika kita membuka URLhttp://localhost:8000/greetingmaka $namaakanberisi Suryasebagai default route param. Namun jika kita membuka URLhttp://localhost:8000/greeting/Ihsan maka $nama akan berisiIhsansepertiterlihatpadagambarberikut:
Padacodediatas,jikaroutesemakinbanyakmakafile index.phpakan semakin panjang sehingga kita perlu memindahkan routingdan membuat file sendiri untuk mendefinisikan routing tersebut.
XXXIV.StudiKasusMembuatFrameworkSederhana
286
Saya membuat file config/routes.php sejajar index.php danmemindahkanroutingkedalamfiletersebut.
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('hello',newRoute('/hello'));
$routes->add('greeting',newRoute('/greeting/{nama}',['nam
a'=>'Surya']));
Untuk memanggil file tersebut, saya mengubah file index.phpsebagaiberikut:
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\Routing\Matcher\UrlMatcher;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
$request=Request::createFromGlobals();
include__DIR__.'/config/routes.php';
XXXIV.StudiKasusMembuatFrameworkSederhana
287
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
try{
$response=newResponse();
extract($matcher->match($request->getPathInfo()));
includesprintf('%s.php',$_route);
}catch(ResourceNotFoundException$e){
$response=newResponse('Halamantidakditemukan',Resp
onse::HTTP_NOT_FOUND);
}
$response->send();
Sehinggasusunanfolderkitasekarangmenjadisepertiberikut:
Untuk mengetahui secara lebih mendalam tentang Symfonyrouting,Andadapatmembacadokumentasiresminyapadalinkini.
MembuatKernelFramework
XXXIV.StudiKasusMembuatFrameworkSederhana
288
Sejatinya file index.php hanyadijadikan sebagai front controllertanpa ada logic disana. Untuk itu kita perlu memindahkan logicyangadapada file index.php kedalam file tersendiri.File inilahyang nantinya akan mengarahkan request, mendelegasikanrequest ke controller hingga menerima dan mengembalikanresponsekeklien.
Oleh karena itu saya membuat folder src dan membuatnamespace ModernFramework sebagai root namespace sertamendaftarkannamespace tersebutke composer.json agar dapatdiregistrasikecomposerautoloader.
{
"require":{
"symfony/http-foundation":"^4.0",
"symfony/routing":"^4.0"
},
"autoload":{
"psr-4":{
"ModernFramework\\":"src/"
}
}
}
Untukmeng-update composer autoloader kita perlu menjalankanperintah composerdump-autoloadterlebihdahulu.
Untukmembuatkernelkitamembutuhkanpackage symfony/http-kernel dan untuk meng-install-nya kita cukup menjalankanperintah composer req symfony/http-kernel dari root projectsepertigambardibawahini:
XXXIV.StudiKasusMembuatFrameworkSederhana
289
Setelah itu kita membuat file Application.php di dalam foldersrcdenganisisebagaiberikut:
<?php
//filename:src/Application.php
namespaceModernFramework;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\HttpKernel\HttpKernelInterface;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
useSymfony\Component\Routing\Matcher\UrlMatcher;
classApplicationimplementsHttpKernelInterface
{
publicfunctionhandle(Request$request,$type=self::M
ASTER_REQUEST,$catch=true)
{
include__DIR__.'/../config/routes.php';
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
try{
XXXIV.StudiKasusMembuatFrameworkSederhana
290
$response=newResponse();
extract($matcher->match($request->getPathInfo())
);
includesprintf('%s/../%s.php',__DIR__,$_route
);
return$response;
}catch(ResourceNotFoundException$e){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
}
}
Dan kemudian kita perlu mengubah file index.php untukmenyesuaikandenganperubahantersebut.
<?php
//filename:index.php
require__DIR__.'/vendor/autoload.php';
useSymfony\Component\HttpFoundation\Request;
useModernFramework\Application;
$request=Request::createFromGlobals();
$kernel=newApplication();
$response=$kernel->handle($request);
$response->send();
XXXIV.StudiKasusMembuatFrameworkSederhana
291
Bagaimanajaditampaklebihsimpelkanfile index.php yangkitamiliki? Selanjutnya kita akan membuat controller untukmenggantikanfile greeting.phpdan hello.php.
MembuatControllerClassSejauh ini framework yang kita buat telah menerapkan kernelsehinggafile index.phpkitamenjadi lebihsimpel.Namunsemuaitu masih sangat sederhana. Kita perlu memisahkan logic danmembuat controller untuk memproses request dan memberikanresponse.
Didalamfolder src kitamembuat folderbaruyaitu Controllerdanmembuatfilecontroller HelloController.phpsebagaiberikut:
<?php
//filename:src/Controller/HelloController.php
namespaceModernFramework\Controller;
useSymfony\Component\HttpFoundation\Response;
classHelloController
{
publicfunctionhello()
{
returnnewResponse('HelloWorld');
}
publicfunctiongreet($nama)
{
returnnewResponse(sprintf('SelamatDatang,%s',$n
ama));
XXXIV.StudiKasusMembuatFrameworkSederhana
292
}
}
Sampai disini controller kita belum dapat digunakan, namun kitasudahbisamenghapus file hello.php dan greeting.php yangberada pada root project sehingga sususan folder kita menjadisebagaiberikut:
Setiapaction ataumethod pada controller harus mengembalikanobject Response bila tidakmakaakanterjadierror.Danagarkitadapat mengarahkan request ke controller maka kita perlumengubah routing yang telah kita buat dengan menambahkanspesialindeks _controllersebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('hello',newRoute('/hello',[
'_controller'=>'ModernFramework\Controller\HelloContro
ller::hello',
XXXIV.StudiKasusMembuatFrameworkSederhana
293
]));
$routes->add('greeting',newRoute('/greeting/{nama}',[
'nama'=>'Surya',
'_controller'=>'ModernFramework\Controller\HelloContro
ller::greet',
]));
Denganpenambahancodediatas,makakitadapatmenggunakancontroller resolver dan argument resolver untuk mendapatkanobject controller serta parameter dari controller tersebut. Kitamenambahkancontrollerresolverdanargumentresolverpada fileApplication.phpsebagaiberikut:
<?php
//filename:src/Application.php
namespaceModernFramework;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\HttpKernel\HttpKernelInterface;
useSymfony\Component\HttpKernel\Controller\ControllerResolv
er;
useSymfony\Component\HttpKernel\Controller\ArgumentResolver
;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
useSymfony\Component\Routing\Matcher\UrlMatcher;
classApplicationimplementsHttpKernelInterface
{
publicfunctionhandle(Request$request,$type=self::M
ASTER_REQUEST,$catch=true)
{
include__DIR__.'/../config/routes.php';
XXXIV.StudiKasusMembuatFrameworkSederhana
294
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
$controllerResolver=ControllerResolver();
$argumentResolver=ArgumentResolver();
try{
$request->attributes->add($matcher->match($reque
st->getPathInfo()));
$controller=$controllerResolver->getController
($request);
$arguments=$argumentResolver->getArguments($re
quest,$controller);
returncall_user_func_array($controller,$argume
nts);
}catch(ResourceNotFoundException$e){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
}
}
Sedikit penjelasan dari perubahan code diatas, pada baris code $request->attributes->add($matcher->match($request-
>getPathInfo())) kita memasukkan hasil dari $matcher-
>match($request->getPathInfo()) menjadi attribute dari requestagar dapat dipanggil menggunakan $request->get() maupun$request->attributes->get() .Hal tersebutdiperlukankarenakita
XXXIV.StudiKasusMembuatFrameworkSederhana
295
menggunakan controller resolver dan argument resolver untukmendapatkan controller dan parameter dari method atau actionpadacontroller.
Dengan perubahan file Application.php seperti diatas, makasetiap request akan diarahkan ke controller sehingga ketika kitamembukabrowser,makahasilnyaadalahsebagaiberikut:
KesimpulanSampai disini berarti pembahasan kita tentang cara membuatframework sederhana telah usai. Untuk membuat sebuahframework yang sederhana ternyata tidak terlalu sulit, kita hanyamemerlukan3packagedarikomponenSymfony,dengansusunanfolderterakhiradalahsebagaiberikut:
XXXIV.StudiKasusMembuatFrameworkSederhana
296
Meski sangat sederhana, namun kita dapat belajar tentangbagaimana sebuah requestmasuk, diarahkan ke controller untukkemudian diproses oleh controller hingga mengembalikanresponse. Pada pembahasan selanjutnya, kita akanmengembangkan framework yang telah kita buat untuk dapatberinteraksidengandatabase.
XXXIV.StudiKasusMembuatFrameworkSederhana
297
XXXV.StudiKasusTodoListMenggunakanOOPdanMVCPembahasan kali ini adalah pengembangan dari pembahasansebelumnya. Pada pembahasan kali ini, kita akanmengembangkan framework yang telah kita buat padapembahasan sebelumnya dengan menambahkan fitur koneksidatabasedantemplateengine.
Sayameng-copyseluruhcodepadapembahasansebelumnyadanmengganti nama folder-nya menjadi Todo agar membedakanantaracodesebelumdansetelahpenambahanfiturdatabasedantemplateengine.
TujuandariProyekTodoListTujuan utama dari pembuatan aplikasi todo list ini adalahmemahamikonsepMVC(Model-View-Controller).Selaintujuanutama tersebut, pada pembahasan kali ini kita juga akan belajarbagaimana menambahkan fitur koneksi database serta templateenginepadaframeworkyangkitabuat.
PembuatanDatabase
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
298
Pada aplikasi todo list ini kita hanya akanmenggunakan 1 tabeldatabase yaitu todo dengan kolom id , activity , danis_done.KitaakanmenggunakandatabaseengineSQLitenamunAnda dapat menggunakan MySQL atau MariaDB atau RDBMSapapunyangdidukungolehPDO(PHPDataObjects).
SayamenggunakanSQLitehanyakarenabagisayaSQLitesangatringandanportablesehinggacocokuntukprosespengembanganaplikasi. Sementara untuk kebutuhan produksi, penggunaanSQLitesangattidakdianjurkan.
UntukSQLdaritabel todoadalahsebagaiberikut:
CREATETABLEtodo
(
idINTEGERPRIMARYKEYAUTOINCREMENT,
activityVARCHAR(255)NOTNULL,
is_doneINTEGERDEFAULT0
);
UntukmembuatdatabasedenganSQLite,kitacukupmembuatfiletodo.db pada root project dan kemudian menjalankan perintahmelaluicommandlineataucommandpromptsebagaiberikut:
sqlite3todo.db
MakaakanmunculshellSQLitesebagaiberikut:
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
299
Kita kemudian mengetikkan perintah SQL diatas dan kemudiantekanenteruntukmenjalankansepertipadagambarberikut:
KoneksiDatabaseSetelah membuat database, tahap selanjutnya adalah membuatkoneksi antara database dan aplikasi. Untuk membuat koneksisayamenggunakanpackage pixie dan untuk meng-install-nyakitacukupmengetikkan composerrequsmanhalalit/pixiedarirootprojectsepertigambarberikut:
Dan kemudian kita membuat file Database.php didalam foldersrc/Utildenganisisebagaiberikut:
<?php
//filename:src/Util/Database.php
namespaceModernFramework\Util;
usePixie\Connection;
usePixie\QueryBuilder\QueryBuilderHandler;
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
300
classDatabase
{
privatestatic$connection;
privatestatic$pdo;
privatefunction__construct($host,$database,$driver=
'mysql',$username='root',$password=null,$port=3306,
$charset='utf8')
{
$config=[
'driver'=>$driver,
'host'=>$host,
'database'=>$database,
'username'=>$username,
'password'=>$password,
'charset'=>$charset,
'port'=>$port,
];
static::$connection=newConnection($driver,$confi
g);
static::$pdo=static::$connection->getPdoInstance()
;
}
publicstaticfunctionconnect($host,$database,$driver
='mysql',$username='root',$password=null,$port=33
06,$charset='utf8')
{
returnnewstatic($host,$database,$driver,$userna
me,$password,$port,$charset);
}
publicfunctionexecute(string$query,array$parameters)
{
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
301
$statement=static::$pdo->prepare($query);
foreach($parametersas$parameter=>$value){
$statement->bindValue(sprintf(':%s',$parameter)
,$value);
}
$statement->execute();
}
publicfunctioncreateQueryBuilder()
{
returnnewQueryBuilderHandler(static::$connection);
}
}
Setelah itu kita harus membuat konfigurasi database pada fileconfig/database.phpdenganisisebagaiberikut:
<?php
//filename:config/database.php
useModernFramework\Util\Database;
$database=Database::connect('localhost','todo.db','sqlit
e');
Tahap terakhir kita ubah Application.php agar menge-load fileconfig/database.phpsebagaiberikut:
<?php
//filename:src/Application.php
namespaceModernFramework;
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
302
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\HttpKernel\HttpKernelInterface;
useSymfony\Component\HttpKernel\Controller\ControllerResolv
er;
useSymfony\Component\HttpKernel\Controller\ArgumentResolver
;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
useSymfony\Component\Routing\Matcher\UrlMatcher;
classApplicationimplementsHttpKernelInterface
{
publicfunctionhandle(Request$request,$type=self::M
ASTER_REQUEST,$catch=true)
{
include__DIR__.'/../config/routes.php';
include__DIR__.'/../config/database.php';
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
$controllerResolver=newControllerResolver();
$argumentResolver=newArgumentResolver();
try{
$request->attributes->add($matcher->match($reque
st->getPathInfo()));
$controller=$controllerResolver->getController
($request);
$arguments=$argumentResolver->getArguments($re
quest,$controller);
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
303
returncall_user_func_array($controller,$argume
nts);
}catch(ResourceNotFoundException$e){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
}
}
Sampaidisinikitatelahselesaimembuatkoneksiantaradatabasedanaplikasi.Tahapselanjutnyaadalahmembuatmodelagardapatberinteraksidengandatabase.
MembuatModelClassSetelah selesai membuat koneksi database, maka tahapselanjutnya adalah membuat class Model sebagai dasar darisemuamodelyangakankitabuat.Class Modeliniadalahsebuahabstractclasssehinggawajibdi-extendsolehchildclass.
Pada class Model kita akan menerapkan active record patternseperti yang digunakan juga oleh framework Laravel. Bila Andapengguna framework Laravel, mungkin Anda tidak asing dengansyntaxberikut:
$object->save();
Padamodel,kitaakanjugaakanmenerapkansyntaxtersebut.Halini bertujuan agar Anda juga memahami bagaimana frameworkseperti Laravel dan lainnya bekerja untuk menangani operasi
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
304
database.Namun tentu sajaapa yang kita buat inimasih sangatsederhanadanperlupengembanganlebihlanjut.
Untuk membuat class Model kita perlu menambahkan terlebihdahulu package symfony/property-access sebagai packageutilitas. Untuk meng-install package tersebut, kita cukupmengetikkan perintah composer req symfony/property-access
sepertigambarberikut:
Setelah instalasiberhasil,selanjutnyakitamembuatclass Modelsebagaiberikut:
<?php
//filename:src/Model/Todo.php
namespaceModernFramework\Model;
useModernFramework\Util\Database;
useSymfony\Component\PropertyAccess\PropertyAccess;
abstractclassModel
{
private$connection;
abstractpublicfunctiongetId():?int;
publicfunction__construct(Database$connection)
{
$this->connection=$connection;
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
305
}
publicfunctionsave():void
{
$class=new\ReflectionClass(get_class($this));
$table=strtolower($class->getShortName());
$properties=array_filter($class->getProperties(\Re
flectionProperty::IS_PRIVATE|\ReflectionProperty::IS_PROTECT
ED),function($property){
return'id'===$property?false:true;
});
$columns=array_map(function($property){
return$this->toUnderScore($property->getName());
},$properties);
$accessor=PropertyAccess::createPropertyAccessor()
;
$parameters=[];
foreach($columnsas$key=>$property){
$parameters[$property]=$accessor->getValue($th
is,$property);
}
if($this->getId()){
$this->update($table,$columns,$parameters);
}else{
$this->insert($table,$columns,$parameters);
}
}
publicfunctiondelete()
{
if($id=$this->getId()){
$class=new\ReflectionClass(get_class($this));
$table=strtolower($class->getShortName());
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
306
$this->connection->execute(sprintf('DELETEFROM
%sWHEREid=:id',$table),['id'=>$id]);
}
}
publicfunctionfind(int$id):?self
{
$class=new\ReflectionClass(get_class($this));
$table=strtolower($class->getShortName());
$result=$this->connection->createQueryBuilder()->f
rom($table)->find($id);
if(!$result){
return$result;
}
return$this->normalize($result);
}
publicfunctionfindAll():array
{
$class=new\ReflectionClass(get_class($this));
$table=strtolower($class->getShortName());
$results=$this->connection->createQueryBuilder()->
from($table)->get();
foreach($resultsas$key=>$result){
$results[$key]=$this->normalize($result);
}
return$results;
}
privatefunctioninsert(string$table,array$columns,a
rray$values):void
{
$parameters=array_map(function($column){
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
307
returnsprintf(':%s',$column);
},$columns);
$this->connection->execute(sprintf('INSERTINTO%s(%
s)VALUES(%s)',$table,implode(',',$columns),implode(','
,$parameters)),$values);
}
privatefunctionupdate(string$table,array$columns,a
rray$values):void
{
$parameters=array_map(function($column){
returnsprintf('%s=:%s',$column,$column);
},$columns);
$this->connection->execute(sprintf('UPDATE%sSET%s
WHEREid=:id',$table,implode(',',$parameters)),$value
s);
}
privatefunctiontoUnderScore(string$column):string
{
returnstrtolower(preg_replace('/([a-z])([A-Z])/','
$1_$2',str_replace('','_',$column)));
}
privatefunctionnormalize(\stdClass$data):Model
{
$clone=clone$this;
$accessor=PropertyAccess::createPropertyAccessor()
;
foreach(json_decode(json_encode($data),true)as$p
roperty=>$value){
$accessor->setValue($clone,$property,$value);
}
return$clone;
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
308
}
Denganmeng-extendsclass Modeldiatas,makachildclassakandapatmenggunakansyntaxberikut:
//Untukoperasiinsertdanupdate
$object->save();
//Untukoperasidelete
$object->delete();
//Untukmendapatkandataberdasarkanid
$object->find($id);
//Untukmendapatkansemuadata
$object->findAll();
MembuatTodoClassSetelahmembuat class Model , selanjutnya kita perlu membuatclass Todo yangmeng-extendsclass Model .Class inilah yangnantinyaakanberinteraksidengandatabasesecaralangsung.
Class Todomerepresentasikantabel tododidatabasesehingganamaproperty-nyaadalahnamakolompadatabeldatabase.Danberikutadalahclass Todotersebut:
<?php
//filename:src/Model/Todo.php
namespaceModernFramework\Model;
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
309
useModernFramework\Util\Database;
classTodoextendsModel
{
constDONE=1;
constTODO=0;
private$id;
private$activity;
private$isDone;
publicfunction__construct(Database$connection)
{
parent::__construct($connection);
$this->isDone=self::TODO;
}
publicfunctiongetId():?int
{
return$this->id;
}
publicfunctionsetId(string$id):void
{
$this->id=(int)$id;
}
publicfunctiongetActivity():string
{
return$this->activity;
}
publicfunctionsetActivity(string$activity):void
{
$this->activity=$activity;
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
310
}
publicfunctionisDone():bool
{
return(bool)$this->isDone;
}
publicfunctiondone():void
{
$this->isDone=self::DONE;
}
publicfunctionsetIsDone(string$done):void
{
$this->isDone=(int)$done===self::DONE?self::D
ONE:self::TODO;
}
}
Karena ketika diambil dari database semua tipe data menjadistring maka baik $id maupun $done harus kita konversidahulumenjadi integeragarsesuaidengankebutuhankita.
Sampaidisinikita telahselesaimembuatclassmodeluntuk tabel todo . Tahap selanjutnya adalah pembuatan controller dandilanjutkandenganpembautantemplate.
MembuatControllerClassBerbeda dengan controller pada pembahasan sebelumnya, padapembahasankali ini,controller yangakankitabuatakanmemilikiparent class seperti halnya pada model. Hal ini dilakukan agar
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
311
kompleksitas dapat dipecah dan parent class dapat digunakankembalipadacontroller-controlleryanglainnya.
Classcontrolleryangakankitabuatbersifatabstractsamasepertihalnyaclass Model dannantinyapadaclasscontroller inilahkitamemasukkan model agar dapat digunakan oleh controller. Danberikutadalahclasscontrollertersebut:
<?php
//filename:src/Controller/Controller.php
namespaceModernFramework\Controller;
useModernFramework\Model\Model;
useModernFramework\Util\Database;
useSymfony\Component\HttpFoundation\Response;
abstractclassController
{
private$connection;
publicfunctionsetConnection(Database$connection):void
{
$this->connection=$connection;
}
protectedfunctiongetConnection():Database
{
return$this->connection;
}
protectedfunctiongetModel(string$model):Model
{
returnnew$model($this->connection);
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
312
protectedfunctionrender(string$view):Response
{
returnnewResponse($view);
}
}
Untuk dapat menyesuaikan dengan class controller diatas, kitaperlumengubahfile Application.phpdenganmenambahkancode $controller->setConnection($database) sebelum pemanggilancontrolleruntukmemasukkankoneksikecontroller.Sehinggacodepadafile Application.phpmenjadisepertidibawahini:
<?php
//filename:src/Application.php
namespaceModernFramework;
useModernFramework\Controller\Controller;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\Routing\RequestContext;
useSymfony\Component\HttpKernel\HttpKernelInterface;
useSymfony\Component\HttpKernel\Controller\ControllerResolv
er;
useSymfony\Component\HttpKernel\Controller\ArgumentResolver
;
useSymfony\Component\Routing\Exception\ResourceNotFoundExce
ption;
useSymfony\Component\Routing\Matcher\UrlMatcher;
classApplicationimplementsHttpKernelInterface
{
constBASE_PATH=__DIR__.'/../';
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
313
publicfunctionhandle(Request$request,$type=self::M
ASTER_REQUEST,$catch=true)
{
includeself::BASE_PATH.'config/routes.php';
includeself::BASE_PATH.'config/database.php';
$context=newRequestContext();
$context->fromRequest($request);
$matcher=newUrlMatcher($routes,$context);
$controllerResolver=newControllerResolver();
$argumentResolver=newArgumentResolver();
try{
$request->attributes->add($matcher->match($reque
st->getPathInfo()));
$controller=$controllerResolver->getController
($request);
$arguments=$argumentResolver->getArguments($re
quest,$controller);
if($controllerinstanceofController){
$controller->setConnection($database);
}
returncall_user_func_array($controller,$argume
nts);
}catch(ResourceNotFoundException$e){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
}
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
314
Dengancaradiatas,makasetiapcontroller yangkitabuatsecaraotomatismemilikikoneksidatabase.Haliniakanmemudahkankitaketikaberinteraksidenganmodel.
MenambahkanTemplateEnginepadaFrameworkAgar code yang kita tulis menjadi lebih bersih dan lebih mudahdibaca serta tidak bercampur antara file controller dan modeldengan file view maka kita perlumenambahkan template enginepada framework kita. Selain berfungsi untuk memisahkan antarapresentasi dan logic, template engine juga berguna untukmemudahkankolaborasidenganfrontenddeveloper.
Pada framework yang kita buat, untuk template engine kita akanmenggunakan Twig. Saya memilih Twig karena Twig di-compilemenjadi plain PHP code sehingga lebih cepat. Selain itu Twigaman karena menggunakan auto escaping serta mudahdigunakan.
Untukmeng-installTwigkitacukupmengetikkanperintah composerreqtwig/twigsepertigambarberikut:
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
315
Setelah selesai melakukan instalasi, selanjutnya kita buat classViewyangakanmenggunakanTwigsebagaitemplateengine. Isidariclass Viewtersebutadalahsebagaiberikut:
<?php
//filename:src/View/View.php
namespaceModernFramework\View;
useSymfony\Component\HttpFoundation\Response;
classView
{
private$twig;
publicfunction__construct(string$templatePath,string
$cachePath=null)
{
$this->twig=new\Twig_Environment(
new\Twig_Loader_Filesystem($templatePath),
['cache'=>null!==$cachePath?$cachePath:f
alse]
);
}
publicfunctionrender(string$template,array$variable
s=[])
{
returnnewResponse($this->twig->render($template,$
variables));
}
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
316
Kita membutuhkan template path sebagai base directory daritemplate. Selain itu kita juga membutuhkan cache path sebagaitempat menyimpan file hasil compile dari Twig. Fitur cache inisangatdisarankanpadaenvironmentproduction,namunbilaAndatidakinginmenggunakannya,Andadapatmengabaikannya.
Penggunaanclass View ininantinyasamaseperti $this->load->view() padaCodeIgniter atau $this->render() pada Symfonyketika dipanggil dari controller. Untuk itu kita perlu mengubahabstractclass Controllerkitasebagaiberikut:
<?php
//filename:src/Controller/Controller.php
namespaceModernFramework\Controller;
useModernFramework\Application;
useModernFramework\Model\Model;
useModernFramework\View\View;
useModernFramework\Util\Database;
useSymfony\Component\HttpFoundation\Response;
abstractclassController
{
private$connection;
private$template;
publicfunction__construct()
{
$this->template=newView(Application::BASE_PATH.'t
emplate',Application::BASE_PATH.'cache');
}
publicfunctionsetConnection(Database$connection):void
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
317
{
$this->connection=$connection;
}
protectedfunctiongetConnection():Database
{
return$this->connection;
}
protectedfunctiongetModel(string$model):Model
{
returnnew$model($this->connection);
}
protectedfunctionrender(string$template,array$varia
bles=[]):Response
{
return$this->template->render($template,$variables
);
}
}
Pada root project kita buat 2 folder yaitu folder template untuktempat menyimpan template kita, serta cache sebagai tempatmenyimpanfilehasilcompiledariTwig.
Ketikakitamengubahtemplatemakakitaharusmenghapussemuaisi pada folder cache agar kita dapat melihat perubahan yangtelahkitabuat.
Sampai tahap ini kita telah mengintegrasikan antara controllerdengan model serta controller dengan view. Tahap selanjutnyaadalah todo controller dan membuat operasi CRUD untuk tabel
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
318
todo.Bagaimanasemakinmenarikbukan?
MembuatTodoControllerClassTahapterakhirdaripembahasankitaadalahpembuatancontroller TodoController yang akan menangani request danmengembalikan response kepada klien. Pada classTodoControllernantinyamodeldanviewakandigunakan.
Pada tahap awal, kita akanmenampilkan data seluruh todo daridatabasesebagaiberikut:
<?php
//filename:src/Controller/TodoController.php
namespaceModernFramework\Controller;
useSymfony\Component\HttpFoundation\Response;
classTodoControllerextendsController
{
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
[]]);
}
}
Action method index() diatas belum dapat menampilkan datayang ada di database, namun nantinya kit akan menampilkansemuadatadaritabel todopadaactionmethod index().
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
319
Setelah membuat action method, kita harus membuat template index.html.twig pada folder template dengan isi sebagaiberikut:
<!--filename:template/index.html.twig-->
<table>
<thead>
<tr>
<td>No</td>
<td>Tugas</td>
<td>Selesai?</td>
<td>Pilihan</td>
<tr>
</thead>
<tbody>
{%fori,todointodos%}
<tr>
<td>{{(i+1)}}</td>
<td>{{todo.activity}}</td>
<td>{{todo.isDone?'Selesai':'Belum'}}</td>
<td>{%iffalse==todo.isDone%}<ahref="/todo/
'~todo.id~'/done">Selesai</a>|{%endif%}<ahref="/tod
o/{{todo.id}}/edit">Edit</a>|<ahref="/todo/{{todo.id}
}/delete">Hapus</a></td>
</tr>
{%endfor%}
</tbody>
</table>
<p><ahref="/todo/new">Tambah</a></p>
Setelah itu kita harusmendaftarkan action method index() keroute agar dapat kita panggil menggunakan browser. Untukmendaftarkanroute,kitacukupmenambahkanbariscodeberikut:
$routes->add('todo_index',newRoute('/todo',[
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
320
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
Secaralengkap,file routes.phpkitaadalahsebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('todo_index',newRoute('/todo',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
Selanjutnya kita tinggal menjalankan built-in web server denganmengetikkan perintah php -S localhost:8000 -t . ./index.php
darirootprojectsepertipadagambarberikut:
Dan kemudian kita dapat membuka browser dan mengetikkanalamat http://localhost:8000/todo ,makaakanmunculhalamansebagaiberikut:
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
321
Dari gambar terlihat tidak ada data yang ditampilkan, yang adahanya link Tambah yangaktif. Kita dapatmembuka link tersebutdengan mengekliknya maka akan muncul pesan Halaman tidak
ditemukansepertiberikut:
Hal tersebut wajar karena kita belum membuat action methoduntukroute /todo/new.Agarhalamantersebuttidakmunculpesantersebut, kita harus membuat action method new() sebagaiberikut:
<?php
//filename:src/Controller/TodoController.php
namespaceModernFramework\Controller;
useModernFramework\Model\Todo;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
classTodoControllerextendsController
{
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
322
[]]);
}
publicfunctionnew(Request$request)
{
$activity=$request->get('activity');
if($activity){
$todo=$this->getModel(Todo::class);
$todo->setActivity($activity);
$todo->save();
}
return$this->render('new.html.twig');
}
}
Kemudian kita membuat file new.html.twig dengan isi sebagaiberikut:
<!--filename:template/new.html.twig-->
<formaction="/todo/new"method="POST">
<inputtype="text"name="activity"/>
<buttontype="submit">Simpan</button>
</form>
Dan selanjutnya kita perlu mendaftarkan action method new()kedalamroutesebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
323
$routes->add('todo_index',newRoute('/todo',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
$routes->add('todo_new',newRoute('/todo/new',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::new',
]));
Kita dapat me-refresh kembali browser kita maka akan munculhalamansebagaiberikut:
Dan ketika kita mengisi form tersebut dan mengeklik tombolSimpan maka kan kembali ke halaman list seperti padagambarberikut:
Tidakperlukhawatirkarenamemangpadaactionmethod index()kita belum memanggil data dari database. Agar data yang telahkitamasukkanmuncul,makakitaperlumengubahactionmethodindex()menjadisebagaiberikut:
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
324
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
$this->getModel(Todo::class)->findAll()]);
}
Setelahkitarefreshkembalihalaman /todomakadatayangkitamasukkanakanmunculsepertigambarberikut:
Terlihat list todo kita sudah muncul, selanjutnya kita klik linkSelesai makaakanmuncul Halamantidakditemukan sehinggakita perlu membuat action method-nya terlebih dahulu. Actionmethod untuk menangani link Selesai tersebut adalah actionmethod done()denganisisebagaiberikut:
publicfunctiondone($id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
$todo->done();
returnnewRedirectResponse('/todo');
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
325
}
Secara lengkap, class TodoController akan menjadi sebagaiberikut:
<?php
//filename:src/Controller/TodoController.php
namespaceModernFramework\Controller;
useModernFramework\Model\Todo;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\HttpFoundation\RedirectResponse;
classTodoControllerextendsController
{
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
$this->getModel(Todo::class)->findAll()]);
}
publicfunctionnew(Request$request)
{
$activity=$request->get('activity');
if($activity){
$todo=$this->getModel(Todo::class);
$todo->setActivity($activity);
$todo->save();
returnnewRedirectResponse('/todo');
}
return$this->render('new.html.twig');
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
326
publicfunctiondone($id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
$todo->done();
$todo->save();
returnnewRedirectResponse('/todo');
}
}
Dankemudiankitadaftarkanactionmethodtersebutkedalamroutesebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('todo_index',newRoute('/todo',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
$routes->add('todo_new',newRoute('/todo/new',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::new',
]));
$routes->add('todo_done',newRoute('/todo/{id}/done',[
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
327
'_controller'=>'ModernFramework\Controller\TodoControl
ler::done',
]));
Setelahkitarefreshkembalihalaman /todomakadatayangkitamasukkanakanmunculsepertigambarberikut:
Sampai tahap ini kita sudahdapatmenampilkan list, tambahdanmeng-update todo menjadi Selesai . Tahap selanjutnya kitaakanmembuathalamaneditsebagaiberikut:
<!--filename:template/edit.html.twig-->
<formaction="/todo/{{todo.id}}/edit"method="POST">
<inputtype="text"name="activity"value="{{todo.activi
ty}}"/>
<buttontype="submit">Simpan</button>
</form>
Kemudian kita membuat action method edit() dengan isisebagaiberikut:
publicfunctionedit(Request$request,$id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
328
esponse::HTTP_NOT_FOUND);
}
if(Request::METHOD_POST===$request->getMethod())
{
$todo->setActivity($request->get('activity'));
$todo->save();
returnnewRedirectResponse('/todo');
}
return$this->render('edit.html.twig',['todo'=>$t
odo]);
}
Secaralengkapclass TodoControllermenjadisebagaiberikut:
<?php
//filename:src/Controller/TodoController.php
namespaceModernFramework\Controller;
useModernFramework\Model\Todo;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\HttpFoundation\RedirectResponse;
classTodoControllerextendsController
{
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
$this->getModel(Todo::class)->findAll()]);
}
publicfunctionnew(Request$request)
{
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
329
$activity=$request->get('activity');
if($activity){
$todo=$this->getModel(Todo::class);
$todo->setActivity($activity);
$todo->save();
returnnewRedirectResponse('/todo');
}
return$this->render('new.html.twig');
}
publicfunctiondone($id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
$todo->done();
$todo->save();
returnnewRedirectResponse('/todo');
}
publicfunctionedit(Request$request,$id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
if(Request::METHOD_POST===$request->getMethod())
{
$todo->setActivity($request->get('activity'));
$todo->save();
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
330
returnnewRedirectResponse('/todo');
}
return$this->render('edit.html.twig',['todo'=>$t
odo]);
}
}
Dan kemudian kita daftarkan action method tersebut ke dalamroutekitasebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('todo_index',newRoute('/todo',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
$routes->add('todo_new',newRoute('/todo/new',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::new',
]));
$routes->add('todo_done',newRoute('/todo/{id}/done',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::done',
]));
$routes->add('todo_edit',newRoute('/todo/{id}/edit',[
'_controller'=>'ModernFramework\Controller\TodoControl
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
331
ler::edit',
]));
Setelah itu kita klik link edit akanmuncul halamanedit sepertigambarberikut:
Dan kemudian kita coba edit isian kemudian klik tombol simpanmaka akan kembali ke halaman list namun dengan data yangsudahdiperbaruisebagaiberikut:
Action terakhir yang harus kita buat adalah action methoddelete()denganisisebagaiberikut:
publicfunctiondelete($id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
332
$todo->delete();
returnnewRedirectResponse('/todo');
}
Sehinggaclass TodoControllerakanmenjadisebagaiberikut:
<?php
//filename:src/Controller/TodoController.php
namespaceModernFramework\Controller;
useModernFramework\Model\Todo;
useSymfony\Component\HttpFoundation\Request;
useSymfony\Component\HttpFoundation\Response;
useSymfony\Component\HttpFoundation\RedirectResponse;
classTodoControllerextendsController
{
publicfunctionindex()
{
return$this->render('index.html.twig',['todos'=>
$this->getModel(Todo::class)->findAll()]);
}
publicfunctionnew(Request$request)
{
$activity=$request->get('activity');
if($activity){
$todo=$this->getModel(Todo::class);
$todo->setActivity($activity);
$todo->save();
returnnewRedirectResponse('/todo');
}
return$this->render('new.html.twig');
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
333
}
publicfunctiondone($id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
$todo->done();
$todo->save();
returnnewRedirectResponse('/todo');
}
publicfunctionedit(Request$request,$id)
{
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
if(Request::METHOD_POST===$request->getMethod())
{
$todo->setActivity($request->get('activity'));
$todo->save();
returnnewRedirectResponse('/todo');
}
return$this->render('edit.html.twig',['todo'=>$t
odo]);
}
publicfunctiondelete($id)
{
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
334
$todo=$this->getModel(Todo::class)->find($id);
if(!$todo){
returnnewResponse('Halamantidakditemukan',R
esponse::HTTP_NOT_FOUND);
}
$todo->delete();
returnnewRedirectResponse('/todo');
}
}
Dan kemudian kita daftarkan action method delete() tersebutkedalamroutesebagaiberikut:
<?php
//filename:config/routes.php
useSymfony\Component\Routing\RouteCollection;
useSymfony\Component\Routing\Route;
$routes=newRouteCollection();
$routes->add('todo_index',newRoute('/todo',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::index',
]));
$routes->add('todo_new',newRoute('/todo/new',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::new',
]));
$routes->add('todo_done',newRoute('/todo/{id}/done',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::done',
]));
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
335
$routes->add('todo_edit',newRoute('/todo/{id}/edit',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::edit',
]));
$routes->add('todo_delete',newRoute('/todo/{id}/delete',[
'_controller'=>'ModernFramework\Controller\TodoControl
ler::delete',
]));
Setelahkitaklik link Hapus makaakankembalihalaman /tododandatatelahdihapussepertipadagambarberikut:
KesimpulanSampaidisiniberartikitatelahmembuatoperasiCRUDuntuktabeltodo .Dengan selesainyaoperasiCRUD tersebutmaka selesaijuga pembahasan kita tentang todo list menggunakan MVC.Kesimpulan yang dapat kita ambil adalah bahwa membuatframeworkMVC tidaklah sulit asalkankitamemahamibagaimanaalurrequestdanresponsedenganbaik.
Memang framework yang kita buat belummemiliki fitur _validasi,keamanan, form dan lain sebagainya, namun sudah dapatdigunakanuntukoperasiCRUDsederhana.
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
336
XXXV.StudiKasusTodoListMenggunakanOOPdanMVC
337
PenutupTerima kasih saya sampaikan kepada Anda karena Anda telahmeluangkanwaktuuntukmembaca tulisandarisaya ini.Tak lupasaya memohon maaf atas segala kesalahan penulisan, codemaupungambaryangsayasajikandalamtulisanini.
SayaberharapAndatidakmembagikanbukuinikepadasiapapunseraya mengajak mereka yang menginginkan buku ini untukmembeli langsung buku agar saya semakin termotivasi untukmenulis.
Jangan lupa untuk menantikan seri buku dari saya lainnya danterusupgradeskillAndadanjanganpernahpuasdenganapayangAndaraihsaatini.Akhirkatadarisaya"RaihlahkemuliaandiDuniadengan ilmu, gapailah kemuliaan di Akhirat dengan ilmu, dancapailahkemuliaankeduanyadenganilmu".
Wassalamu'alaikumWarohmatullahWabarokatuhu.
Penutup
338