63 throw new \InvalidArgumentException(sprintf\
64 ('%s is not callable.', $controller));
65 }
66 $this->router->add($path, new Route(
67 $path,
68 array('_controller' => $controller)
69 ));
70 }
71 }
Sampai disini, sebenarnyaframework kita sudah jadi. Serius,core frameworksebenarnya memang sesimpel itu. Yang membuatframe- workterkesan kompleks adalah karena adanya proses validasi dan lain-lain padacoreataukerneldari framework tersebut.
Untukframeworkyang kita buat, proses-proses tersebut ditiadakan agar Anda dapat fokus memahami bahwa sebenarnya membuat frameworkitu sangatlah mudah.
MembuatFrameworkSederhana 181
Front Controller
Dan berikut adalahfileindex.phpataufront controllerdariframe- workyang kita buat:
1 <?php
2 $loader = require __DIR__.'/../vendor/autoload.php';
3
4 use Symfony\Component\HttpFoundation\Request;
5 use Symfony\Component\HttpFoundation\Response;
6 use BelajarOOP\Framework\Application;
7
8 //Grabing request
9 $request = Request::createFromGlobals();
10
11 //Bootstraping
12 $app = new Application(array('database' => array('host'\
13 => 'localhost')));//Contoh config saja tapi tidak dipa\
14 kai 15
16 //Route register
17 $app->route('/hello/{name}', function ($name) { 18 return new Response(sprintf('Hello %s', $name));
19 });
20
21 //Request handling
22 $response = $app->handle($request);
23
24 //Send response 25 $response->send();
Seperti yang saya sudah saya jelaskan diatas, bahwaframeworkkita sudah jadi. Untuk memastikan kebenaran tersebut, mari kita coba
jalankanframeworktersebut.
Untuk mempermudah dan agar tidak perlu settingvhost dan web server, saya menggunakanbuilt-in web server untuk menjalankan framework kita. Cukup mengetikanphp -S localhost:8000 dari folder webmaka aplikasi kita sudah berjalan.
Berikut adalah tampilancommanduntuk menjalankanweb server:
Built In Web Server
Dan tampilan dariframework kita, ketika ketika mengetikkanlo-
calhost:8000/hello/<Nama>adalah sebagai berikut:
Framework
Bagaimana? Mudah sekali bukan sebenarnya. Untuk langkah se- lanjutnya, kita akan menerapkanobserver patternpadaframework kita agar,frameworkyang kita buat makin fleksibel.
VII. Event Dispatcher
Tahap terakhir dari framework kita adalah menambahkan hook agar framework yang kita buat lebih mudah di-extend. Kita akan
MembuatFrameworkSederhana 183 menggunakanEvent Dispatcher Component yang menerapkanob- server patternuntuk membuathooktersebut.
Sebelum membuathook, mari kita buat terlebih dahuluevent class- nya sebagai berikut:
1 <?php 2
3 namespace BelajarOOP\Framework\Event;
4
5 use Symfony\Component\EventDispatcher\Event;
6 use Symfony\Component\HttpFoundation\Response;
7
8 class FilterResponseEvent extends Event 9 {
10 private $response;
11
12 public function setResponse(Response $response)
13 {
14 $this->response = $response;
15 }
16
17 public function getResponse()
18 {
19 return $this->response;
20 }
21 }
1 <?php 2
3 namespace BelajarOOP\Framework\Event;
4
5 use Symfony\Component\EventDispatcher\Event;
6 use Symfony\Component\HttpFoundation\Request;
7 use Symfony\Component\HttpFoundation\Response;
8
9 class FilterRequestEvent extends Event 10 {
11 private $request;
12
13 private $response;
14
15 public function setRequest(Request $request)
16 {
17 $this->request = $request;
18 }
19
20 public function getRequest()
21 {
22 return $this->request;
23 }
24
25 public function setResponse(Response $response)
26 {
27 $this->response = $response;
28 }
29
30 public function getResponse()
31 {
32 return $this->response;
33 }
34 }
MembuatFrameworkSederhana 185 Selanjutnya adalah kita membuat namaeventpadaclassApplica-
tionsebagai berikut:
1 <?php 2
3 namespace BelajarOOP\Framework;
4
5 use BelajarOOP\Framework\Http\Kernel;
6
7 class Application extends Kernel 8 {
9 const PRE_REQUEST_EVENT = 'kernel.request';
10 const PRE_RESPONSE_EVENT = 'kernel.response';
11
12 protected $configs;
13
14 public function __construct(array $configs = array(\
15 ))
16 {
17 parent::__construct();
18 $this->configs = $configs;
19 }
20
21 public function setConfig($key, $value)
22 {
23 $this->configs[$key] = $value;
24 }
25
26 public function getConfig($key)
27 {
28 if (array_key_exists($key, $this->configs)) { 29 return $this->configs[$key];
30 }
31
32 return null;
33 } 34 }
Tahap terakhir yaitu kita membuat hook untuk framework kita.
Kita perlu membuatnya padaclassKernelpadamethodhandle(). Selain itu, saya juga menambahkanmethodon()padaclassKernel agar bisa dipanggil padaindex.php.
Berikut adalahcode dari classKernelsetelah kita menambahkan event dispatcher:
1 <?php 2
3 namespace BelajarOOP\Framework\Http;
4
5 use BelajarOOP\Framework\Application;
6 use BelajarOOP\Framework\Event\FilterRequestEvent;
7 use BelajarOOP\Framework\Event\FilterResponseEvent;
8 use InvalidArgumentException;
9 use Symfony\Component\EventDispatcher\EventDispatcher;
10 use Symfony\Component\HttpFoundation\Request;
11 use Symfony\Component\HttpFoundation\Response;
12 use Symfony\Component\HttpKernel\HttpKernelInterface;
13 use Symfony\Component\Routing\Exception\ResourceNotFoun\
14 dException;
15 use Symfony\Component\Routing\Matcher\UrlMatcher;
16 use Symfony\Component\Routing\RequestContext;
17 use Symfony\Component\Routing\Route;
18 use Symfony\Component\Routing\RouteCollection;
19
20 class Kernel implements HttpKernelInterface 21 {
22 private $router;
23
24 private $eventDispatcher;
MembuatFrameworkSederhana 187
25
26 public function __construct()
27 {
28 //Symfony Route Collection
29 $this->router = new RouteCollection();
30
31 //Event Dispatcher
32 $this->eventDispatcher = new EventDispatcher();
33 }
34
35 public function handle(Request $request, $type = se\
36 lf::MASTER_REQUEST, $catch = true)
37 {
38 $filterRequest = new FilterRequestEvent();
39 $filterRequest->setRequest($request);
40
41 //Add Filter Query Event
42 $this->eventDispatcher->dispatch(Application::P\
43 RE_REQUEST_EVENT, $filterRequest);
44
45 if ($response = $filterRequest->getResponse()) {
46 return $response;
47 }
48
49 //Request Context
50 $context = new RequestContext();
51 $context->fromRequest($request);
52
53 //Url Matcher
54 $matcher = new UrlMatcher($this->router, $conte\
55 xt);
56
57 try {
58 //Matching Url and Getting Route
59 $attributes = $matcher->match($request->get\
60 PathInfo());
61
62 //Get Controller from Route attribute @see \ 63 `route()` method
64 $controller = $attributes['_controller'];
65
66 unset($attributes['_controller']);
67
68 //Make it simple, use call_user_func_array \ 69 to calling controller and passing attributes as paramet\
70 er
71 $response = call_user_func_array($controlle\
72 r, $attributes);
73
74 $filterResponse = new FilterResponseEvent();
75 $filterResponse->setResponse($response);
76
77 //Add Filter Response Event
78 $this->eventDispatcher->dispatch(Applicatio\
79 n::PRE_RESPONSE_EVENT, $filterResponse);
80
81 $response = $filterResponse->getResponse();
82 } catch (ResourceNotFoundException $e) {
83 $response = new Response('Not found!', Resp\
84 onse::HTTP_NOT_FOUND);
85 }
86
87 return $response;
88 }
89
90 public function route($path, $controller)
91 {
92 if (!is_callable($controller)) {
93 throw new \InvalidArgumentException(sprintf\
94 ('%s is not callable.', $controller));
MembuatFrameworkSederhana 189
95 }
96 $this->router->add($path, new Route(
97 $path,
98 array('_controller' => $controller)
99 ));
100 }
101
102 public function on($event, $callback)
103 {
104 if (! is_callable($callback)) {
105 throw new InvalidArgumentException(sprintf(\
106 '%s is not callable.', $callback));
107 }
108
109 $this->eventDispatcher->addListener($event, $ca\
110 llback);
111 }
112 }
Langkah terakhir adalah, mencoba melakukanhooking pada in-
dex.php untuk menguji bahwa code kita berjalan dengan benar.
Dan berikut adalah file index.php yang sudah kita tambahkan hook:
1 <?php
2 $loader = require __DIR__.'/../vendor/autoload.php';
3
4 use Symfony\Component\HttpFoundation\Request;
5 use Symfony\Component\HttpFoundation\Response;
6 use BelajarOOP\Framework\Application;
7 use BelajarOOP\Framework\Event\FilterRequestEvent;
8
9 //Grabing request
10 $request = Request::createFromGlobals();
11
12 //Bootstraping
13 $app = new Application(array('database' => array('host'\
14 => 'localhost')));//Contoh config saja tapi tidak dipa\
15 kai 16
17 //Route register
18 $app->route('/hello/{name}', function ($name) { 19 return new Response(sprintf('Hello %s', $name));
20 });
21
22 $app->on(Application::PRE_REQUEST_EVENT, function (Filt\
23 erRequestEvent $event) {
24 if ('/admin' === $event->getRequest()->getPathInfo(\
25 )) {
26 $event->setResponse(new Response('Anda harus lo\
27 gin sebagai admin untuk mengakses halaman ini.'));
28 }
29 });
30
31 //Request handling
32 $response = $app->handle($request);
33
34 //Send response 35 $response->send();
Jadi ceritanya, kita akan mencoba membatas akses padaurl/admin. Ketika kita mengakses halaman tersebut, maka akan muncul pesan
Anda harus login sebagai admin untuk mengakses halaman ini.. Untuk memastikan bahwahookkita bekerja sebagaimana mestinya, mari kita buka kembali browser dan mengakses alamat local-
host:8000/admin. Jika benar, maka akan muncul pesan seperti diatas sebagai berikut:
MembuatFrameworkSederhana 191
Hooking Framework
Bagaimana? Mudah sekali bukan? Ternyata membuat framework tidak sesulit yang kita bayangkan. Anda dapat mencoba den- gan menggunakan PRE_RESPONSE_EVENTuntuk lebih meyakinkan bahwaframeworktelah bekerja sesuai dengan yang kita inginkan.
VIII. Kesimpulan
Dengan menguasai OOP, membuatframeworksederhana tidaklah sulit. Bahkanframeworkyang kita buatpun tidak perlu banyakclass untuk membuatnya.
Saya tidak menyarankan Anda menggunakanframeworkyang kita buat untukproject realAnda tanpa ada perbaikan dan penambahan validasi padaframework.
Framework masih sangat premature sehingga perlu banyak per- baikan dan penambahan disana-sini agar semakinpowerful.