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!
Very good information. Lucky me I found your site by chance (stumbleupon).
I’ve bookmarked it for later!
강남도깨비
18 Oct 25 at 12:54 pm
https://t.me/Official_1xbet_1xbet/1791
Josephadvem
18 Oct 25 at 12:54 pm
نکته مهم خطرناک به وبسایتهای قمار.
اینجانب به عنوان کسی که اتفاق مستقیم
کردهام، اعلام میکنم که آنها سایتها وسیله برای دزدی
دارایی مردم هستند. شرط شروع موفق
است، اما سپس برنامهها طوری طراحی میگردند که بازیکن پایانه ناکام
باشید. تهدید وابستگی جدیتر از هر عامل دیگری است و تواند سلامتتان را ویران کند.
اجتناب کنید!
از دست دادن پول کازینو
18 Oct 25 at 12:55 pm
Game slot sudah jadi salah satu bentuk hiburan paling dikenal di industri dunia judi, serta Slot MPO paling baru menyediakan kemahiran yang makin memikat kepada para fans.
Seiring dengan perkembangan industri, slot online gacor waktu ini tampil dengan bermacam fitur inovatif yang tidak cuma
meningkatkan keseruan, tapi juga memberikan probabilitas menang yang lebih
besar. Dalam artikel ini, kita bakal menjelajahi apakah yang menciptakan Slot mpo slot
terbaru maniak memukau dan mengapa Kamu wajib mencobanya.
mpo slot
18 Oct 25 at 12:55 pm
https://telegra.ph/Kupit-usilitel-svyazi-kroks-10-13-2
JesseHow
18 Oct 25 at 12:57 pm
Выведение из запоя в стационаре Воронежа — помощь при острых состояниях и хронической зависимости. Наши опытные наркологи используют современные методы лечения для быстрого и безопасного вывода из запоя.
Ознакомиться с деталями – [url=https://vyvod-iz-zapoya-v-stacionare-voronezh24.ru/]наркология вывод из запоя в стационаре[/url]
Stuartbooms
18 Oct 25 at 12:59 pm
сколько стоит оформить перепланировку [url=zakazat-proekt-pereplanirovki-kvartiry11.ru]zakazat-proekt-pereplanirovki-kvartiry11.ru[/url] .
zakazat proekt pereplanirovki kvartiri_tket
18 Oct 25 at 12:59 pm
Своевременный вызов нарколога позволяет снизить риски осложнений, таких как судорожный синдром, панкреатит или сердечная недостаточность, часто сопровождающие тяжелое алкогольное или наркотическое отравление. По данным исследований, раннее вмешательство сокращает сроки госпитализации и улучшает прогнозы выздоровления.
Подробнее тут – [url=https://narkolog-na-dom-chelyabinsk13.ru/]вызвать нарколога на дом срочно челябинск[/url]
Charlesnak
18 Oct 25 at 1:00 pm
https://t.me/Official_1xbet_1xbet/1781
Josephadvem
18 Oct 25 at 1:00 pm
mostbet skachat apk [url=http://mostbet4185.ru]http://mostbet4185.ru[/url]
mostbet_uz_uwer
18 Oct 25 at 1:01 pm
букмекерская контора мелбет официальный сайт [url=melbetbonusy.ru]букмекерская контора мелбет официальный сайт[/url] .
melbet_gxOi
18 Oct 25 at 1:03 pm
сделать проект перепланировки квартиры [url=https://proekt-pereplanirovki-kvartiry16.ru]https://proekt-pereplanirovki-kvartiry16.ru[/url] .
proekt pereplanirovki kvartiri_wsMl
18 Oct 25 at 1:03 pm
Вывод из запоя в стационаре — это шанс начать путь к трезвой жизни, и в Самаре этот шанс предоставляет «Частный Медик 24».
Ознакомиться с деталями – [url=https://vyvod-iz-zapoya-v-stacionare-samara25.ru/]быстрый вывод из запоя в стационаре[/url]
GilbertCoeby
18 Oct 25 at 1:06 pm
проект перепланировки недорого [url=https://www.proekt-pereplanirovki-kvartiry16.ru]https://www.proekt-pereplanirovki-kvartiry16.ru[/url] .
proekt pereplanirovki kvartiri_toMl
18 Oct 25 at 1:06 pm
можно ли купить диплом медсестры [url=https://frei-diplom14.ru]можно ли купить диплом медсестры[/url] .
Diplomi_mtoi
18 Oct 25 at 1:08 pm
сколько стоит перепланировка квартиры [url=http://zakazat-proekt-pereplanirovki-kvartiry11.ru/]http://zakazat-proekt-pereplanirovki-kvartiry11.ru/[/url] .
zakazat proekt pereplanirovki kvartiri_cnet
18 Oct 25 at 1:11 pm
где согласовать перепланировку квартиры [url=http://soglasovanie-pereplanirovki-kvartiry11.ru]http://soglasovanie-pereplanirovki-kvartiry11.ru[/url] .
soglasovanie pereplanirovki kvartiri _cpMi
18 Oct 25 at 1:12 pm
Tenho um entusiasmo vibrante por PlayPIX Casino, e uma plataforma que transborda vitalidade. A selecao de jogos e fenomenal, incluindo apostas esportivas que aceleram o coracao. Com uma oferta inicial para impulsionar. O suporte ao cliente e excepcional, acessivel a qualquer momento. As transacoes sao confiaveis, no entanto ofertas mais generosas seriam bem-vindas. No geral, PlayPIX Casino vale uma visita epica para amantes de emocoes fortes ! Tambem o design e moderno e vibrante, instiga a prolongar a experiencia. Um diferencial significativo as opcoes variadas de apostas esportivas, oferece recompensas continuas.
Clicar para ver|
JungleVibeK8zef
18 Oct 25 at 1:12 pm
Estou completamente encantado com BETesporte Casino, e uma plataforma que vibra como um estadio em dia de final. Ha uma explosao de jogos emocionantes, suportando jogos compativeis com criptomoedas. 100% ate R$600 + apostas gratis. O acompanhamento e impecavel, sempre pronto para entrar em campo. Os pagamentos sao seguros e fluidos, de vez em quando bonus mais variados seriam um golaco. Para finalizar, BETesporte Casino oferece uma experiencia inesquecivel para jogadores em busca de emocao ! Acrescentando que a plataforma e visualmente impactante, adiciona um toque de estrategia. Um diferencial importante as opcoes variadas de apostas esportivas, que impulsiona o engajamento.
Obter informaГ§Гµes|
VortexGoalW2zef
18 Oct 25 at 1:13 pm
Have you ever considered about adding a little bit more than just your articles?
I mean, what you say is fundamental and everything. But imagine if
you added some great visuals or videos to give your posts more,
“pop”! Your content is excellent but with pics and videos, this website could undeniably be one
of the greatest in its niche. Good blog!
Discover More Here
18 Oct 25 at 1:14 pm
Ищете, где в Саратове действительно помогают справиться с алкоголизмом? Статья о клинике Торпедомед на LadyCaramelka охватывает всё: диагностика, методы, цены и отзывы. Подробнее тут – http://essentiallife.ru/flatboard/view.php/topic/2025-09-020516038cd36/p/1
Crystaldum
18 Oct 25 at 1:16 pm
Just extended my $MTAUR vesting for that 10% bonus—smart play. The audited contracts and cliff mechanisms build trust. Can’t wait to battle crypto monsters in full release. minotaurus token
WilliamPargy
18 Oct 25 at 1:17 pm
cjukfcjdfybt [url=https://soglasovanie-pereplanirovki-kvartiry11.ru/]https://soglasovanie-pereplanirovki-kvartiry11.ru/[/url] .
soglasovanie pereplanirovki kvartiri _sfMi
18 Oct 25 at 1:18 pm
cjukfcjdfybt [url=www.soglasovanie-pereplanirovki-kvartiry14.ru/]www.soglasovanie-pereplanirovki-kvartiry14.ru/[/url] .
soglasovanie pereplanirovki kvartiri _iyEl
18 Oct 25 at 1:19 pm
mostbet bonus ro‘yxatdan so‘ng [url=www.mostbet4182.ru]www.mostbet4182.ru[/url]
mostbet_uz_aukt
18 Oct 25 at 1:19 pm
Клиника «Похмельная служба» в Нижнем Новгороде предлагает капельницу от запоя с выездом на дом. Наши специалисты обеспечат вам комфортное и безопасное лечение в привычной обстановке.
Ознакомиться с деталями – [url=https://vyvod-iz-zapoya-nizhnij-novgorod12.ru/]вывод из запоя дешево[/url]
Miltondiolo
18 Oct 25 at 1:19 pm
mistbet [url=https://www.mostbet4182.ru]mistbet[/url]
mostbet_uz_tfkt
18 Oct 25 at 1:21 pm
melbet официальный сайт зеркало [url=www.melbetbonusy.ru]melbet официальный сайт зеркало[/url] .
melbet_puOi
18 Oct 25 at 1:22 pm
https://t.me/Official_1xbet_1xbet/1659
Josephadvem
18 Oct 25 at 1:23 pm
план перепланировки [url=http://www.proekt-pereplanirovki-kvartiry16.ru]http://www.proekt-pereplanirovki-kvartiry16.ru[/url] .
proekt pereplanirovki kvartiri_qgMl
18 Oct 25 at 1:24 pm
диплом медсестры с аккредитацией купить [url=https://www.frei-diplom14.ru]диплом медсестры с аккредитацией купить[/url] .
Diplomi_nzoi
18 Oct 25 at 1:24 pm
cjukfcjdfybt [url=http://soglasovanie-pereplanirovki-kvartiry14.ru]http://soglasovanie-pereplanirovki-kvartiry14.ru[/url] .
soglasovanie pereplanirovki kvartiri _lsEl
18 Oct 25 at 1:25 pm
tadalafilo [url=https://tadalafiloexpress.com/#]cialis generico[/url] tadalafilo
GeorgeHot
18 Oct 25 at 1:25 pm
услуга перепланировки квартиры стоимость [url=http://stoimost-soglasovaniya-pereplanirovki-kvartiry.ru]http://stoimost-soglasovaniya-pereplanirovki-kvartiry.ru[/url] .
stoimost soglasovaniya pereplanirovki kvartiri_cqPt
18 Oct 25 at 1:26 pm
I want to to thank you for this very good read!!
I definitely loved every little bit of it.
I have got you bookmarked to look at new stuff you post…
https://github.com/sgcaptainworks-arch/Chat-GPT-5
18 Oct 25 at 1:27 pm
https://t.me/s/Official_1xbet_1xbet/1648
Josephadvem
18 Oct 25 at 1:29 pm
https://t.me/s/Official_1xbet_1xbet/1787
Josephadvem
18 Oct 25 at 1:30 pm
оформить перепланировку цена [url=http://zakazat-proekt-pereplanirovki-kvartiry11.ru/]http://zakazat-proekt-pereplanirovki-kvartiry11.ru/[/url] .
zakazat proekt pereplanirovki kvartiri_txet
18 Oct 25 at 1:31 pm
Minotaurus presale docs detail fair distribution. $MTAUR’s play-to-earn model sustainable. Endless mazes promise hours of play.
mtaur token
WilliamPargy
18 Oct 25 at 1:31 pm
стоимость согласования перепланировки в бти [url=http://www.stoimost-soglasovaniya-pereplanirovki-kvartiry.ru]http://www.stoimost-soglasovaniya-pereplanirovki-kvartiry.ru[/url] .
stoimost soglasovaniya pereplanirovki kvartiri_spPt
18 Oct 25 at 1:32 pm
My relatives always say that I am wasting my time here at net,
but I know I am getting know-how all the time by reading
such pleasant articles.
crazy games
18 Oct 25 at 1:32 pm
Hey hey, Singapore folks, maths іs probably the highly crucial
primary subject, promoting creativity іn issue-resolving
for groundbreaking careers.
Anglo-Chinese Junior College stands аs a beacon of ѡell balanced
education, mixing extensive academics ԝith a nurturing Christian values tһat inspires moral stability ɑnd
individual growth. Тhe college’s state-of-the-art facilities аnd knowledgeable faculty support outstanding efficiency іn Ƅoth arts and sciences, ᴡith trainees frequently accomplishing leading accolades.
Τhrough іts emphasis on sports and carrying
оut arts, trainees establish discipline, camaraderie,
аnd a passion for quality beyօnd the classroom.
International partnerships ɑnd exchange chances improve tһe finding out experience, promoting international awareness аnd cultural gratitude.
Alumni thrive іn diverse fields, testimony tⲟ tһe
college’s function іn forming principled leaders prepared to contribute favorably tо
society.
Millennia Institute stands out witһ іts distinctive thгee-yeаr pre-university
path гesulting in tһe GCE А-Level evaluations, offering
flexible аnd in-depth study choices іn commerce, arts, аnd sciences customized tо accommodate a varied variety of learners and theіr unique aspirations.
Αs a centralized institute, іt provides tailored guidance аnd support systems,
consisting оf dedicated scholastic advisors ɑnd therapy services,
t᧐ mɑke sսrе every student’ѕ holistic advancement ɑnd academic success іn a motivating environment.
Ƭhe institute’s ѕtate-of-the-art facilities, ѕuch as digital learning
hubs, multimedia resource centers, ɑnd collective workspaces,
produce аn appealing platform fⲟr ingenious mentor
methods аnd hands-on tasks tһat bridge theory ᴡith
uѕeful application. Ꭲhrough strong market collaborations, students
access real-ѡorld experiences ⅼike internships, workshops ᴡith experts, and scholarship
chances thаt improve their employability аnd profession preparedness.
Alumni from Millennia Institute consistently attain success іn higher education and
professional arenas, ѕhowing the institution’s unwavering
dedication tο promoting lifelong knowing, versatility, аnd individual empowerment.
Listen սp, Singapore moms ɑnd dads, maths remaіns perһaps the highly іmportant primary topic, encouraging imagination for pгoblem-solving fߋr
creative professions.
Don’t mess аround lah, link a excellent Junior College ⲣlus maths superiority in ordеr to ensure elevated A Levels гesults ɑnd seamless shifts.
Folks, worry ɑbout the gap hor, maths groundwork proves vital ԁuring
Junior College іn grasping figures, essential iin t᧐ɗay’s online
economy.
Oi oi, Singapore folks, maths іs ⅼikely the extremely crucial primary topic, promoting imagination tһrough problem-solving fߋr groundbreaking careers.
Ɗo not play play lah, link a excellent Junior College ᴡith maths excellence tߋ assure hiցh A Levels scores ɑnd
effortless shifts.
Hіgh A-level GPAs lead to leadership roles іn uni societies and bеyond.
Avoid taкe lightly lah, combine а g᧐od Junior College ⲣlus maths proficiency fօr ensure һigh А
Levels гesults pⅼuѕ seamless shifts.
Parents, dread tһe difference hor, maths groundwork proves vital іn Junior College fօr
understanding figures, crucial іn modern tech-driven market.
Ηere is my site; jc 2 math tuition
jc 2 math tuition
18 Oct 25 at 1:32 pm
Hi to every body, it’s my first go to see of this web site;
this web site carries amazing and truly fine material in support of readers.
http://www.wowanka.com
18 Oct 25 at 1:34 pm
зеркала melbet [url=http://melbetbonusy.ru/]зеркала melbet[/url] .
melbet_cgOi
18 Oct 25 at 1:35 pm
купить медицинский диплом медсестры [url=https://www.frei-diplom14.ru]купить медицинский диплом медсестры[/url] .
Diplomi_yjoi
18 Oct 25 at 1:35 pm
That is really attention-grabbing, You’re an overly professional blogger.
I have joined your feed and look forward to seeking extra of
your fantastic post. Additionally, I have shared your web site in my social networks
m98 bet
18 Oct 25 at 1:39 pm
Minotaurus coin’s utility in boosts and customizations is practical. ICO’s community building events foster loyalty. Early stage feels opportunistic.
minotaurus coin
WilliamPargy
18 Oct 25 at 1:42 pm
согласовать перепланировку квартиры [url=https://www.soglasovanie-pereplanirovki-kvartiry11.ru]https://www.soglasovanie-pereplanirovki-kvartiry11.ru[/url] .
soglasovanie pereplanirovki kvartiri _usMi
18 Oct 25 at 1:43 pm
Подарок для конкурента https://xrumer.xyz/
В работе несколько програм.
Есть оптовые тарифы
[url=https://xrumer.xyz/]Подарок для конкурента[/url]
Lorenzomag
18 Oct 25 at 1:43 pm
перепланировка квартиры цена под ключ [url=www.zakazat-proekt-pereplanirovki-kvartiry11.ru]www.zakazat-proekt-pereplanirovki-kvartiry11.ru[/url] .
zakazat proekt pereplanirovki kvartiri_rvet
18 Oct 25 at 1:45 pm