PHP hook, building hooks in your application
Introduction
One of the real challenges in building any type of framework, core or application is making it possible for the developers to hook into the business logic at specific points. Since PHP is not event based, nor it works with interrupts you have to come up an alternative.
The test case
Lets assume we are the main developers of a webshop framework. Programmers can use our framework to build complete webshops. Programmers can manage the orders that are placed on the webshop with the order class. The order class is part of our framework and we don’t want it to be extended by any programmer. However we don’t want to limit to programmers in their possibilities to hook into the orders process.
For example programmers should be able to send an email to the webshopowner if an order changes from one specific delivery status to another. This functionality is not part of the default behavior in our framework and is custom for the progammers webshop implementation.
Like said before, PHP doesn’t provide interrupts or real events so we need to come up with another way to implement hooks into our application. Lets take a look at the observer pattern.
Implementing the Observer pattern
The observer pattern is a design-pattern that describes a way for objects to be notified to specific state-changes in objects of the application.
For the first implementation we can use SPL. The SPL provides in two simple objects:
SPLSubject
- attach (new observer to attach)
- detach (existing observer to detach)
- notify (notify all observers)
SPLObserver
- update (Called from the subject (i.e. when it’s value has changed).
iOrderRef = $iOrderRef;
// Get order information from the database or an other resources
$this->iStatus = Order::STATUS_SHIPPED;
}
/**
* Attach an observer
*
* @param SplObserver $oObserver
* @return void
*/
public function attach(SplObserver $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (isset($this->aObservers[$sHash])) {
throw new Exception('Observer is already attached');
}
$this->aObservers[$sHash] = $oObserver;
}
/**
* Detach observer
*
* @param SplObserver $oObserver
* @return void
*/
public function detach(SplObserver $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (!isset($this->aObservers[$sHash])) {
throw new Exception('Observer not attached');
}
unset($this->aObservers[$sHash]);
}
/**
* Notify the attached observers
*
* @param string $sEvent, name of the event
* @param mixed $mData, optional data that is not directly available for the observers
* @return void
*/
public function notify()
{
foreach ($this->aObservers as $oObserver) {
try {
$oObserver->update($this);
} catch(Exception $e) {
}
}
}
/**
* Add an order
*
* @param array $aOrder
* @return void
*/
public function delete()
{
$this->notify();
}
/**
* Return the order reference number
*
* @return int
*/
public function getRef()
{
return $this->iOrderRef;
}
/**
* Return the current order status
*
* @return int
*/
public function getStatus()
{
return $this->iStatus;
}
/**
* Update the order status
*/
public function updateStatus($iStatus)
{
$this->notify();
// ...
$this->iStatus = $iStatus;
// ...
$this->notify();
}
}
/**
* Order status handler, observer that sends an email to secretary
* if the status of an order changes from shipped to delivered, so the
* secratary can make a phone call to our customer to ask for his opinion about the service
*
* @package Shop
*/
class OrderStatusHandler implements SplObserver
{
/**
* Previous orderstatus
* @var int
*/
protected $iPreviousOrderStatus;
/**
* Current orderstatus
* @var int
*/
protected $iCurrentOrderStatus;
/**
* Update, called by the observable object order
*
* @param Observable_Interface $oSubject
* @param string $sEvent
* @param mixed $mData
* @return void
*/
public function update(SplSubject $oSubject)
{
if(!$oSubject instanceof Order) {
return;
}
if(is_null($this->iPreviousOrderStatus)) {
$this->iPreviousOrderStatus = $oSubject->getStatus();
} else {
$this->iCurrentOrderStatus = $oSubject->getStatus();
if($this->iPreviousOrderStatus === Order::STATUS_SHIPPED && $this->iCurrentOrderStatus === Order::STATUS_DELIVERED) {
$sSubject = sprintf('Order number %d is shipped', $oSubject->getRef());
//mail('secratary@example.com', 'Order number %d is shipped', 'Text');
echo 'Mail sended to the secratary to help her remember to call our customer for a survey.';
}
}
}
}
$oOrder = new Order(26012011);
$oOrder->attach(new OrderStatusHandler());
$oOrder->updateStatus(Order::STATUS_DELIVERED);
$oOrder->delete();
?>
There are several problems with the implementation above. To most important disadvantage is that we have only one update method in our observer. In this update method we don’t know when and why we are getting notified, just that something happened. We should keep track of everything that happens in the subject. (Or use debug_backtrace… just joking, don’t even think about using it that way ever!).
Taking it a step further, events
Lets take a look at the next example, we will extend the Observer implementation with some an additional parameter for the eventname that occured.
Finishing up, optional data
iOrderRef = $iOrderRef;
// Get order information from the database or something else...
$this->iStatus = Order::STATUS_SHIPPED;
}
/**
* Attach an observer
*
* @param Observer_Interface $oObserver
* @return void
*/
public function attachObserver(Observer_Interface $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (isset($this->aObservers[$sHash])) {
throw new Exception('Observer is already attached');
}
$this->aObservers[$sHash] = $oObserver;
}
/**
* Detach observer
*
* @param Observer_Interface $oObserver
* @return void
*/
public function detachObserver(Observer_Interface $oObserver)
{
$sHash = spl_object_hash($oObserver);
if (!isset($this->aObservers[$sHash])) {
throw new Exception('Observer not attached');
}
unset($this->aObservers[$sHash]);
}
/**
* Notify the attached observers
*
* @param string $sEvent, name of the event
* @param mixed $mData, optional data that is not directly available for the observers
* @return void
*/
public function notifyObserver($sEvent, $mData=null)
{
foreach ($this->aObservers as $oObserver) {
try {
$oObserver->update($this, $sEvent, $mData);
} catch(Exception $e) {
}
}
}
/**
* Add an order
*
* @param array $aOrder
* @return void
*/
public function add($aOrder = array())
{
$this->notifyObserver('onAdd');
}
/**
* Return the order reference number
*
* @return int
*/
public function getRef()
{
return $this->iOrderRef;
}
/**
* Return the current order status
*
* @return int
*/
public function getStatus()
{
return $this->iStatus;
}
/**
* Update the order status
*/
public function updateStatus($iStatus)
{
$this->notifyObserver('onBeforeUpdateStatus');
// ...
$this->iStatus = $iStatus;
// ...
$this->notifyObserver('onAfterUpdateStatus');
}
}
/**
* Order status handler, observer that sends an email to secretary
* if the status of an order changes from shipped to delivered, so the
* secratary can make a phone call to our customer to ask for his opinion about the service
*
* @package Shop
*/
class OrderStatusHandler implements Observer_Interface
{
protected $iPreviousOrderStatus;
protected $iCurrentOrderStatus;
/**
* Update, called by the observable object order
*
* @param Observable_Interface $oObservable
* @param string $sEvent
* @param mixed $mData
* @return void
*/
public function update(Observable_Interface $oObservable, $sEvent, $mData=null)
{
if(!$oObservable instanceof Order) {
return;
}
switch($sEvent) {
case 'onBeforeUpdateStatus':
$this->iPreviousOrderStatus = $oObservable->getStatus();
return;
case 'onAfterUpdateStatus':
$this->iCurrentOrderStatus = $oObservable->getStatus();
if($this->iPreviousOrderStatus === Order::STATUS_SHIPPED && $this->iCurrentOrderStatus === Order::STATUS_DELIVERED) {
$sSubject = sprintf('Order number %d is shipped', $oObservable->getRef());
//mail('secratary@example.com', 'Order number %d is shipped', 'Text');
echo 'Mail sended to the secratary to help her remember to call our customer for a survey.';
}
}
}
}
$oOrder = new Order(26012011);
$oOrder->attachObserver(new OrderStatusHandler());
$oOrder->updateStatus(Order::STATUS_DELIVERED);
$oOrder->add();
?>
Now we are able to take action on different events that occur.
Disadvantages
Although this implementation works quite well there are some drawbacks. One of those drawbacks is that we need to dispatch an event in our framework, if we don’t programmers can’t hook into our application. Triggering events everywhere give us a small performance penalty however I do think this way of working gives the programmers a nice way to hook into your application on those spots that you want them to hook in.
Just for the record
Notice that this code is just an example and can still use some improvements, for example: each observer is initialized even it will maybe never be notified, therefore I suggest to make use of lazy in some cases for loading the objects. There are other systems to hook into an application, more to follow!
купить диплом в челябинске [url=www.rudik-diplom12.ru/]купить диплом в челябинске[/url] .
Diplomi_npPi
27 Oct 25 at 7:11 pm
виртуальная сим карта
MiguelActic
27 Oct 25 at 7:11 pm
купить диплом в усолье-сибирском [url=www.rudik-diplom1.ru/]купить диплом в усолье-сибирском[/url] .
Diplomi_wyer
27 Oct 25 at 7:12 pm
kraken
kraken darknet market
Henryamerb
27 Oct 25 at 7:14 pm
ibdesignstreet – Found exactly what I needed, instructions were clear and concise.
Eddie Borski
27 Oct 25 at 7:16 pm
расташоп
расташоп
27 Oct 25 at 7:16 pm
wettstrategie sportwetten
Here is my site: die Besten wetten
die Besten wetten
27 Oct 25 at 7:17 pm
купить диплом в заречном [url=rudik-diplom12.ru]купить диплом в заречном[/url] .
Diplomi_ewPi
27 Oct 25 at 7:18 pm
Автоматические выключатели
JulioGer
27 Oct 25 at 7:19 pm
кракен даркнет маркет
кракен Россия
Henryamerb
27 Oct 25 at 7:21 pm
Автоматические выключатели
JulioGer
27 Oct 25 at 7:24 pm
Hi there! I just wanted to ask if you ever have any problems with hackers?
My last blog (wordpress) was hacked and I ended up losing
many months of hard work due to no data backup. Do you have any solutions to stop hackers?
product recall news
27 Oct 25 at 7:25 pm
If you are going for best contents like me, just pay a visit this web site everyday as it gives feature contents, thanks
비아그라 판매하는곳
27 Oct 25 at 7:25 pm
pgmbconsultancy – Found exactly what I needed, instructions were clear and concise.
Edgar Gallaspy
27 Oct 25 at 7:28 pm
kraken РФ
kraken market
Henryamerb
27 Oct 25 at 7:29 pm
kraken ссылка
кракен Россия
Henryamerb
27 Oct 25 at 7:30 pm
WOW just what I was looking for. Came here by searching for hosting
تاثیر هاست بر سئو سایت
27 Oct 25 at 7:32 pm
гидроизоляция подвала москва [url=www.gidroizolyaciya-podvala-cena.ru]гидроизоляция подвала москва[/url] .
gidroizolyaciya podvala cena_erKt
27 Oct 25 at 7:32 pm
К сожалению, то же самое касается многих
веб-сайтов на базе Tor.
http://www.baharabi.tr.gg/FORUM/topic-2-1-game.htm
27 Oct 25 at 7:32 pm
sportwetten online erfahrungen (Shelia) paysafecard ohne oasis
Shelia
27 Oct 25 at 7:33 pm
вирт номер аренда
MiguelActic
27 Oct 25 at 7:34 pm
abbysauce – Found exactly what I needed, instructions were clear and concise.
Son Dulek
27 Oct 25 at 7:34 pm
kraken vk2
кракен даркнет маркет
Henryamerb
27 Oct 25 at 7:35 pm
Главная цель — безопасно стабилизировать состояние, вернуть контроль над сном, гидратацией и базовой активностью в первые сутки и удержать результат. Для этого мы используем модульный подход: один этап — одна клиническая задача — один измеримый маркер. Такой принцип исключает «гонку доз», снижает риск перегрузки объёмом и делает прогноз понятным для пациента и близких. Мы сознательно разделяем «короткие эффекты» (уменьшение тошноты, сглаживание тахикардии) и «эффекты консолидации» (устойчивый сон, ясность утром), чтобы каждая корректировка была точной и доказуемой.
Выяснить больше – [url=https://narkologicheskaya-klinika-v-nizhnem-novgorode16.ru/]наркологические клиники алкоголизм нижний новгород[/url]
Prestonned
27 Oct 25 at 7:37 pm
Wow, incredible blog layout! How long have you been blogging for?
you made blogging look easy. The overall look of your website is
fantastic, as well as the content!
quiet cat fountain review
27 Oct 25 at 7:37 pm
купить приложение к диплому техникума [url=www.frei-diplom10.ru/]купить приложение к диплому техникума[/url] .
Diplomi_qzEa
27 Oct 25 at 7:38 pm
лечение зависимостей [url=www.narkologicheskaya-klinika-27.ru]лечение зависимостей[/url] .
narkologicheskaya klinika_xupl
27 Oct 25 at 7:41 pm
подвал дома ремонт [url=https://gidroizolyaciya-cena-7.ru/]https://gidroizolyaciya-cena-7.ru/[/url] .
gidroizolyaciya cena_cfSi
27 Oct 25 at 7:41 pm
kraken вход
kraken 2025
Henryamerb
27 Oct 25 at 7:42 pm
This post is worth everyone’s attention. How can I find out more?
toto
27 Oct 25 at 7:42 pm
Dⲟn’t tаke lightly lah, pair а reputable Junior College ԝith
maths superiority fⲟr ensure superior А Levels marks аnd effortless shifts.
Mums ɑnd Dads, fear the disparity hor, math foundation proves critical іn Junior College іn understanding infoгmation,
crucial іn current online market.
Anderson Serangoon Junior College іs a lively institution born from the merger ߋf two
prestigious colleges, promoting аn encouraging environment thɑt emphasizes
holistic advancement ɑnd academic excellence.
Ꭲhe college boasts modern centers, consisting օf advanced laboratories ɑnd
collective spaces, enabling students tⲟ engage
deeply in STEM ɑnd innovation-driven tasks.
Ԝith a strong concentrate on management ɑnd character building, trainees benefit fгom diverse ϲo-curricular
activities tһɑt cultivate durability аnd team effort.
Itѕ dedication tо worldwide point of views throuɡh exchange programs
widens horizons ɑnd prepares students for an interconnected ԝorld.
Graduates ᧐ften protected pⅼaces in top universities, reflecting tһe
college’s devotion tо supporting confident, ᴡell-rounded individuals.
Tampines Meridian Junior College, born fгom the dynamic merger ߋf Tampines Junior College аnd Meridian Junior College, delivers ɑn innovative ɑnd culturally rich education highlighted Ƅy specialized
electives іn drama and Malay language, supporting
expressive ɑnd multilingual talents in a forward-thinking community.
Тһe college’ѕ innovative centers, encompassing theater
areas, commerce simulation labs, ɑnd science innvation hubs,
support varied academic streams tһat motivate interdisciplinary exploration ɑnd practical skill-building аcross arts, sciences,
and company. Talent advancement programs, paired ᴡith abroad
immersion journeys ɑnd cultural festivals, foster strong leadership qualities, cultural awareness, ɑnd flexibility to international characteristics.
Ꮃithin a caring and understanding school culture, trainees tɑke part in health
initiatives,peer support ѕystem, and ϲo-curricular clubs
that promote resilience, psychological intelligence, аnd
collaborative spirit. Аs a outcome, Tampines Meridian Junior College’ѕ trainees achieve holistic growth ɑnd arе ѡell-prepared tօ tackle global difficulties, emerging ɑs confident, flexible people prepared fߋr university success and Ьeyond.
Օh man, regardless whether school іs atas, maths
acts lіke the make-oг-break topic to developing confidence rеgarding figures.
Alas, primary maths instructs practical implementations ѕuch as budgeting, so ensure your youngster masters thɑt correctly fгom early.
Оh, maths acts ⅼike the groundwork pillar оf primary schooling, assisting kids іn spatial analysis tο buildijng careers.
Oh, mathematics іѕ tһе foundation stone οf primary schooling,
assisting kids with geometric reasoning tο architecture paths.
Ꮤithout solid Math scores in A-levels, options
fߋr science streams dwindle fast in uni admissions.
Ꭺvoid tаke lightly lah, combine а reputable Junior College alongside math proficiency fⲟr ensure
һigh A Levels marks ɑnd seamless transitions.
Feel free tߋ vissit my web-site … Compassvale Secondary School
Compassvale Secondary School
27 Oct 25 at 7:44 pm
I’m not sure why but this website is loading
extremely slow for me. Is anyone else having this problem or is it a issue on my end?
I’ll check back later on and see if the problem
still exists.
cat treadmill alternative
27 Oct 25 at 7:44 pm
Fantastic post but I was wanting to know if you could write a litte more on this topic?
I’d be very thankful if you could elaborate a little bit more.
Cheers!
cat reaction Uahpet fountain
27 Oct 25 at 7:47 pm
I don’t even understand how I finished up here, but I thought this put up
was great. I do not understand who you might be but definitely
you are going to a well-known blogger for those who are not already.
Cheers!
trygge norske casino
27 Oct 25 at 7:47 pm
kraken vpn
кракен тор
Henryamerb
27 Oct 25 at 7:50 pm
ЧЕРЕЗ АСЮ купить скорость, кокаин, мефедрон, гашиш На удивление был поражен скоростью работы магазина. Все четко как в аптеке))) Благодарствую
AntonioSog
27 Oct 25 at 7:50 pm
кракен android
kraken 2025
Henryamerb
27 Oct 25 at 7:51 pm
Hey hey, composed pom рi pi, math proves one of tһe t᧐p topics іn Junior College, establishing base in A-Level
calculus.
Αpart from institution amenities, focus ᴡith mathematics іn oгdеr to prevent frequent pitfalls
likе careless mistakes іn tests.
Folks, fearful of losing style օn lah, robust primary mathematics
гesults tߋ improved science comprehension ɑnd tech aspirations.
Nanyang Junior College champs bilingual quality, blending cultural
heritage ԝith modern-day education tߋ support confident global residents.
Advanced centers support strong programs іn STEM, arts, and liberal arts,
promoting development ɑnd imagination. Students grow in a vibrant neighborhood ѡith opportunities ffor
management аnd international exchanges. Τhe college’ѕ focus ᧐n values
and urability builds character alongside scholastic prowess.
Graduates stand оut in leading institutions, carrying
forward ɑ legacy of achievement аnd cultural appreciation.
Anglo-Chinese School (Independent) Junior College delivers аn enriching education deeply
rooted іn faith, ᴡhere intellectual exploration іs harmoniously
balanced with core ethical concepts, directing students t᧐wards becoming empathetic aand accountable global
people geared ᥙp to deal witһ intricate societal obstacles.
Ƭhe school’s prestigious International Baccalaureate
Diploma Programme promotes advanced іmportant thinking, гesearch study skills, ɑnd interdisciplinary knowing,
strengthened ƅy remarkable resources ⅼike devoted development centers and
professional professors ԝho mentor students
іn accomplishing scholastic difference. А broad spectrum ߋf cο-curricular
offerings, from advanced robotics ⅽlubs tһat encourage technological imagination tо symphony orchestras tһаt develop
musical talents, ɑllows students tо discover ɑnd fine-tune their distinct abilities іn a supportive and stimulating environment.
Βy integrating service learning efforts, ѕuch as neighborhood outreach tasks аnd volunteer
programs both locally аnd globally, the college cultivates а strong sense оf social duty,
empathy, and active citizenship ɑmongst itѕ trainee body.
Graduates ߋf Anglo-Chinese School (Independent) Junior College аre exceptionally
well-prepared for entry into elite universities worldwide, carrying ԝith them a distinguished tradition of academic excellence,
personal stability, ɑnd a commitment to lifelong knowing ɑnd contribution.
Οһ man, no matter if school remɑins atas, mathematics serves as the
makе-or-break discipline іn developing confidence regarɗing numbers.
Alas, primary mathematics teaches real-ᴡorld implementations like budgeting, thսs ensure your
kid grasps this right from young.
Ɗon’t play play lah, link a reputable Junior College ᴡith
mathematics superiority fⲟr assure elevated А Levels resultѕ
аnd effortless transitions.
Mums аnd Dads, fear the disparity hor, mathematics groundwork іs vital ɑt Junior
College tօ understanding figures, essential fоr modern online market.
Parents, kiasu approach activated lah, robust primary math guides tߋ superior
science grasp plᥙѕ construction goals.
Ⲟh, math serves as the foundation block іn primary education, aiding kids with geometric analysis fⲟr design careers.
Ꭰon’t be kiasu f᧐r notһing; ace yߋur A-levels to snag thօse scholarships and ɑvoid thе competition ⅼater.
Eh eh, composed pom ⲣi pi, maths remains ɑmong іn the leading topics dᥙring Junior College,
building groundwork fօr A-Level advanced math.
Ᏼesides to institution resources, emphasize սpon maths іn order tο ѕtоp typical mistakes lіke
careless blunders іn assessments.
Parents, kiasu approach activated lah, strong primary mathematics leads fоr superior scientific
understanding pⅼus tech dreams.
Аlso visit mу webpage; YCK secondary school
YCK secondary school
27 Oct 25 at 7:53 pm
Thanks for sharing this article! Electric cargo bikes are growing worldwide as
a green alternative. Green Speedy combines modular design,
flexibility, and sustainability to meet the needs of families and businesses.
Learn more: https://www.green-speedy.com/blog
cargo bike disabled transport
27 Oct 25 at 7:54 pm
Wow that was strange. I just wrote an incredibly long comment but after I clicked submit my comment didn’t appear.
Grrrr… well I’m not writing all that over again. Anyways,
just wanted to say superb blog!
gama казино фриспины
27 Oct 25 at 7:54 pm
кракен онион
kraken qr code
Henryamerb
27 Oct 25 at 7:56 pm
диплом колледжа купить в уфе [url=https://www.frei-diplom12.ru]диплом колледжа купить в уфе[/url] .
Diplomi_muPt
27 Oct 25 at 7:56 pm
Currently it looks like BlogEngine is the preferred blogging platform out there right now.
(from what I’ve read) Is that what you are using on your blog?
pajak emas
27 Oct 25 at 7:56 pm
клиника вывод из запоя москва [url=www.narkologicheskaya-klinika-27.ru/]клиника вывод из запоя москва[/url] .
narkologicheskaya klinika_jhpl
27 Oct 25 at 7:58 pm
תודה על המידע. אני מסכים לגמרי שהעתיד
נמצא באופני מטען.
אנחנו בגרין ספידי מציעים אופני מטען חדשניים שמתאימים גם למשפחות וגם
לעסקים.
פרטים נוספים כאן: https://www.green-speedy.com/blog
מטען מוביליות
27 Oct 25 at 7:59 pm
By linking math tⲟ innovative projects, OMT stirs ᥙp an intеrest іn students, motivating
tһem tο accept the subject and pursue exam proficiency.
Experience versatile learning anytime,аnywhere through OMT’s thorouցh online e-learning platform,
featuring unlimited access tо video lessons ɑnd interactive quizzes.
Singapore’s emphasis on vital thinking tһrough mathematics highlights tһe imⲣortance of
math tuition, wһicһ helps students develop tһe
analytical skills demanded ƅy the nation’s forward-thinking syllabus.
Ꮃith PSLE mathematics questions typically including real-ᴡorld applications, tuition ρrovides targeted practice to establish critical believing abilities іmportant for
high ratings.
Comprehensive coverage оf the entiгe Ο Level syllabus in tuition makes ϲertain no
topics, frⲟm collections to vectors, ɑre neglected in а pupil’s alteration.
Junior college tuition supplies access tо supplemental sources ⅼike worksheets ɑnd
video clip explanations, reinforcing А Level syllabus coverage.
Ꮤhat differentiates OMT іs its personalized curriculum tһat straightens
with MOE while concentrating on metacognitive skills,
ѕhowing trainees just hoѡ to learn math successfully.
Bite-sized lessons maкe it νery easy tⲟ suit leh, bring aƄout constant technique ɑnd much bettеr ցeneral qualities.
In ɑ hectic Singapore class, math tuition ρrovides tһe
slower, thorough explanations needed tto construct ѕelf-confidence f᧐r examinations.
Feel free to surf tо mү pɑge … 1 to 1 maths tuition
1 to 1 maths tuition
27 Oct 25 at 8:00 pm
Why people still make use of to read news papers when in this technological world everything is
available on web?
dewabet link
27 Oct 25 at 8:00 pm
Hey there would you mind stating which blog platform you’re
using? I’m going to start my own blog soon but I’m having a tough time choosing between BlogEngine/Wordpress/B2evolution and Drupal.
The reason I ask is because your design and style seems different then most blogs and I’m looking for something unique.
P.S Sorry for getting off-topic but I had to ask!
ankara kürtaj
27 Oct 25 at 8:01 pm
kraken vk3
кракен 2025
Henryamerb
27 Oct 25 at 8:02 pm
Hello, i feel that i saw you visited my blog thus i got here
to go back the want?.I am attempting to to find things to improve my website!I assume its ok to make use of a few of your ideas!!
레비트라 시알리스 비교
27 Oct 25 at 8:03 pm