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!
Mums and Dads, kiasu approach activated lah, solid primary math гesults for
improved STEM understanding рlus tech dreams.
Oһ, mathematics serves ɑs tһe base stone іn primary learning, helping
kids wіth spatial analysis tߋ building careers.
Yishun Innova Junior College combines strengths fօr digital literacy and leadership excellence.
Updated facilities promote development ɑnd long-lasting knowing.
Diverse programs іn media and languages foster creativity ɑnd citizenship.
Neighborhood engagements construct compassion ɑnd skills. Trainees emerge ɑs confident, tech-savvy leaders prepared fοr the digital age.
Catholic Junior College օffers a transformative educational experience fixated ageless worths οf compassion, stability,
and pursuit оf fɑct, cultivating a close-knit community
ᴡherе trainees feel supported ɑnd motivated to grow ƅoth
intellectually аnd spiritually in a peaceful аnd inclusive
setting. Ꭲhe college offers extensive scholastic programs іn tһе liberal arts, sciences, and social sciences,
ρrovided by passionate аnd skilled mentors ᴡho
utilize innovative teaching techniques t᧐ trigger intеrest and motivate deep, meaningful knowing tһɑt extends faar Ьeyond assessments.
An lively selection of ⅽo-curricular activities, including competitive
sports ցroups that promote physical health ɑnd friendship, ɑѕ well ɑѕ
creative societies tһɑt nurture innovative eexpression tһrough drama ɑnd
visual arts, makes іt pοssible fߋr trainees to explore tһeir
inteгests and develop ѡell-rounded personalities.
Opportunities fߋr meaningful neighborhood service, ѕuch as partnerships ᴡith regional charities аnd worldwide humanitarian journeys, assist build compassion,
management skills, ɑnd a authentic commitment tߋ making ɑ difference
in the lives ߋf others. Alumni from Catholic Junior
College frequently Ƅecome thoughtful аnd ethical leaders in different professional fields, geared ᥙp ᴡith the understanding,
strength, and moral compass to contribute favorably ɑnd sustainably
tօ society.
In addition beyond establishment amenities, emphasize with math to аvoid frequent
errors ѕuch ɑs sloppy mistakes dᥙring exams.
Mums ɑnd Dads, kiasu mode activated lah, robust primary
math leads t᧐ superior science grasp рlus engineering dreams.
Mums аnd Dads, competitive style activated lah, strong primary mathematics
guides tο better science comprehension ɑs weell as tech goals.
Οh, mathematics acts lіke thee groundwork pillar ⲟf primary schooling, helping children ԝith geometric thinking tο building careers.
Mums ɑnd Dads, fear tһе difference hor, maths foundation proves essential ɑt
Junior College foг grasping informɑtion, crucial іn modern tech-driven economy.
Wah lao, гegardless іf school іs fancy, math іѕ the decisive subject іn cultivates poise гegarding figures.
Oh no, primary maths teaches everyday սѕes including financial planning, thus mɑke sᥙre your child gеtѕ thiѕ
correctly bеginning earⅼy.
Eh eh, calm pom pі pі, math proves рart in tһе top subjects at Junior College, building groundwork t᧐ A-Level advanced
math.
Math builds quantitative literacy, essential fоr informed citizenship.
Folks, dread tһe disparity hor, mathematics base proves essential ɗuring Junior College t᧐
comprehending data, vital wіthin current tech-driven ѕystem.
Wah lao, regardlеss іf establishment proves atas, math іѕ tһe critical topic for
developing poise іn calculations.
Ꮮook аt my web site – Zhenghua Secondary School Singapore
Zhenghua Secondary School Singapore
24 Oct 25 at 4:38 pm
купить диплом с занесением в реестр казань [url=www.frei-diplom1.ru]www.frei-diplom1.ru[/url] .
Diplomi_nsOi
24 Oct 25 at 4:39 pm
купить проведенный диплом отзывы [url=www.frei-diplom5.ru]www.frei-diplom5.ru[/url] .
Diplomi_gjPa
24 Oct 25 at 4:39 pm
как купить диплом техникума торговли [url=http://www.frei-diplom12.ru]как купить диплом техникума торговли[/url] .
Diplomi_mzPt
24 Oct 25 at 4:39 pm
1x lite [url=https://www.1xbet-7.com]https://www.1xbet-7.com[/url] .
1xbet_tlol
24 Oct 25 at 4:41 pm
купить диплом с занесением в реестр в архангельске [url=www.frei-diplom6.ru/]купить диплом с занесением в реестр в архангельске[/url] .
Diplomi_ntOl
24 Oct 25 at 4:41 pm
Hi, its pleasant paragraph on the topic of media print,
we all be familiar with media is a wonderful source of data.
Numerous
24 Oct 25 at 4:41 pm
купить диплом в элисте [url=www.rudik-diplom3.ru/]купить диплом в элисте[/url] .
Diplomi_erei
24 Oct 25 at 4:41 pm
1 x bet giri? [url=https://1xbet-giris-7.com]https://1xbet-giris-7.com[/url] .
1xbet giris_gzKn
24 Oct 25 at 4:41 pm
I have read so many content concerning the
blogger lovers however this post is really a nice paragraph,
keep it up.
บาคาร่า168
24 Oct 25 at 4:42 pm
Арбитражные споры — это конфликты, которые возникают в ходе деятельности физических
и юридических лиц и касаются
правовых вопросов.
В соответствии с российским законодательством,
арбитражный суд является специализированным органом,
разрешающим такие споры.
Категории арбитражных споров
Конфликты, связанные с договорными обязательствами.
Споры о праве собственности.
Конфликты, касающиеся долговых обязательств.
Различные споры, возникающие во время
коммерческой практики.
Процесс арбитражного разбирательства
Процесс ведения арбитражных дел начинается с подачи искового заявления
в арбитражный суд.
В соответствии со статьей 125 Арбитражного процессуального кодекса, иск требует
наличия следующих элементов:
Название суда.
Сведения о сторонах.
Детали обстоятельств дела.
Претензии истца.
Документы, подтверждающие требования.
Процесс подготовки документов
Для успешного разрешения спора необходимо подготовить и предоставить суду всю релевантную информацию.
Это могут быть:
Договоры.
Письменные доказательства.
Показания свидетелей.
Иные документы, подтверждающие позиции
сторон.
Решения арбитражного суда
Решение суда принимается на основании представленных данных и аргументов сторон.
Суд учитывает все аспекты дела и выносит решение, которое может быть обжаловано в вышестоящих инстанциях.
Крайне важно учитывать соблюдение сроков
для подачи апелляционных жалоб.
Итоги и советы
Для более эффективного
ведения арбитражных дел рекомендуется заранее проконсультироваться с юристом, имеющим опыт в данной сфере.
Такой подход позволит избежать
распространенных ошибок и увеличить шансы на удачный результат.
В итоге, арбитражные споры — это непростая процедура, требующая внимательной подготовки,
знаний законодательства и учета всех особенностей работы арбитражного суда. услуги арбитражного адвоката Итоги
Обзор арбитражных споров демонстрирует их важность в правосудии, обеспечивая защиту прав граждан
и компаний. Арбитражные суда рассматривают споры, вытекающие из гражданских и коммерческих отношений,
что позволяет эффективно решать конфликты, связанные с
обязательствами, договорами и
иными правовыми аспектами.
Законодательство Российской Федерации предоставляет четкие и упорядоченные правила о порядке рассмотрения таких дел.
Это позволяет сторонам быть уверенными в прозрачности и справедливости
судебного процесса. Все стадии арбитражного разбирательства,
начиная от подачи иска и заканчивая вынесением решения,
регулируются Арбитражным процессуальным кодексом.
Важно учитывать следующие аспекты:
Наличие корректных документов, которые подтверждают требования сторон.
Важно соблюдать сроки, которые определены законом
для подачи исковых заявлений.
Понимание прав и обязанностей сторон в процессе.
Точное оформление исков и сопутствующих
судебных бумаг.
Право на подачу апелляции, если решение суда не устраивает.
Уникальность каждого арбитражного спора означает, что его исход зависит от многих факторов, включая доказательства, правовую позицию сторон и профессионализм судей.
При этом важно помнить, что арбитражные суды действуют в соответствии с законодательством, что обеспечивает законность всех
решений.
В связи с вышеизложенным, можно заключить, что арбитражные споры являются неотъемлемой частью правовой системы, способствующей разрешению конфликтов и защите законных интересов граждан и организаций.
Ознакомление с практическими
аспектами и особенностями данного процесса позволит лучше подготовиться к участию в подобных
делах, минимизируя риски и достигая необходимых результатов.
Таким образом, арбитражные споры требуют внимательного подхода
и осведомленности о законодательстве.
Информация о практике арбитражных судов, а также квалифицированное юридическое сопровождение могут значительно повысить шансы на успешный исход дела.
Мы надеемся, что этот материал поможет читателям глубже разобраться в основных принципах
и особенностях арбитражных споров.
https://enciclopedi.net/ChristianaqLewersno
24 Oct 25 at 4:42 pm
1xbet yeni giri? [url=https://www.1xbet-giris-3.com]1xbet yeni giri?[/url] .
1xbet giris_jcMi
24 Oct 25 at 4:42 pm
где купить диплом техникума денег [url=http://frei-diplom8.ru/]где купить диплом техникума денег[/url] .
Diplomi_cpsr
24 Oct 25 at 4:43 pm
как купить диплом занесенный в реестр [url=https://frei-diplom1.ru]как купить диплом занесенный в реестр[/url] .
Diplomi_ngOi
24 Oct 25 at 4:43 pm
discovervalue.bond – It’s great to find a site that focuses on personal growth and learning.
Dannie Banfill
24 Oct 25 at 4:44 pm
xbet giri? [url=http://1xbet-giris-10.com/]xbet giri?[/url] .
1xbet giris_kmka
24 Oct 25 at 4:44 pm
купить диплом моряка [url=https://rudik-diplom8.ru]купить диплом моряка[/url] .
Diplomi_srMt
24 Oct 25 at 4:44 pm
worldvehicleexpo.com – Content reads clearly, helpful examples made concepts easy to grasp.
Ronni Lecates
24 Oct 25 at 4:45 pm
купить диплом инженера строителя [url=www.rudik-diplom10.ru]купить диплом инженера строителя[/url] .
Diplomi_reSa
24 Oct 25 at 4:45 pm
https://t.me/bs_1xbet/39
https://t.me/bs_1xbet/16
24 Oct 25 at 4:46 pm
купить диплом о высшем образовании [url=www.rudik-diplom11.ru]купить диплом о высшем образовании[/url] .
Diplomi_kwMi
24 Oct 25 at 4:46 pm
купить диплом медсестры [url=www.frei-diplom13.ru]купить диплом медсестры[/url] .
Diplomi_bekt
24 Oct 25 at 4:47 pm
mjiuzixun.com – Mobile version looks perfect; no glitches, fast scrolling, crisp text.
Sid Askia
24 Oct 25 at 4:47 pm
диплом с занесением в реестр купить [url=http://frei-diplom6.ru]диплом с занесением в реестр купить[/url] .
Diplomi_imOl
24 Oct 25 at 4:47 pm
https://t.me/s/bs_1xbet/15
https://t.me/s/bs_1xbet/35
24 Oct 25 at 4:47 pm
купить диплом инженера электрика [url=www.rudik-diplom4.ru/]купить диплом инженера электрика[/url] .
Diplomi_usOr
24 Oct 25 at 4:47 pm
teamfuture.bond – Content is clear and concise, very informative and helpful.
Modesto Logosso
24 Oct 25 at 4:47 pm
J’aime l’ambiance futuriste de Monte Cryptos Casino, ca offre une sensation numerique unique. Les options sont vastes comme un reseau, avec des slots aux designs modernes. Le bonus d’entree est eclatant. Le support client est irreprochable, garantissant un service premium. Les retraits sont rapides comme une transaction, de temps a autre plus de promos regulieres dynamiseraient l’experience. Dans l’ensemble, Monte Cryptos Casino vaut une exploration virtuelle pour les adeptes de jeux modernes ! A noter l’interface est fluide comme un flux de donnees, donne envie de prolonger l’aventure. Un avantage notable les evenements communautaires decentralises, garantit des transactions fiables.
Aller plus loin|
CodeVortexP6zef
24 Oct 25 at 4:48 pm
1xbet giri? yapam?yorum [url=https://1xbet-giris-10.com/]1xbet giri? yapam?yorum[/url] .
1xbet giris_voka
24 Oct 25 at 4:48 pm
J’ai une affection pour Impressario Casino, c’est une plateforme qui evoque le raffinement. Il y a une abondance de jeux captivants, comprenant des jeux compatibles avec les cryptos. Le bonus de bienvenue est delicieux. Les agents repondent avec courtoisie, joignable a toute heure. Les transactions sont fiables, cependant des recompenses additionnelles seraient royales. En resume, Impressario Casino vaut une visite sophistiquee pour les amateurs de sensations elegantes ! En bonus la navigation est simple et gracieuse, amplifie le plaisir de jouer. Egalement appreciable les paiements securises en crypto, propose des avantages personnalises.
Voir le site|
MarseilleMystiqueO4zef
24 Oct 25 at 4:48 pm
купить диплом в шадринске [url=www.rudik-diplom6.ru]купить диплом в шадринске[/url] .
Diplomi_geKr
24 Oct 25 at 4:49 pm
В клинике применяются комплексные программы, охватывающие все стадии терапии. Такой подход позволяет обеспечить пациенту полный спектр помощи — от первой консультации до социальной адаптации.
Узнать больше – [url=https://narcologicheskaya-klinika-omsk0.ru/]вывод наркологическая клиника омск[/url]
Michaelmoire
24 Oct 25 at 4:50 pm
https://centenarysdachurch.org/we-deal-with-best-business-managment/
Earle Bah
24 Oct 25 at 4:50 pm
как купить диплом проведенный [url=www.frei-diplom4.ru/]как купить диплом проведенный[/url] .
Diplomi_mbOl
24 Oct 25 at 4:51 pm
купить диплом о высшем образовании [url=http://www.rudik-diplom8.ru]купить диплом о высшем образовании[/url] .
Diplomi_qtMt
24 Oct 25 at 4:51 pm
купить диплом с занесением в реестр цена [url=https://frei-diplom6.ru]купить диплом с занесением в реестр цена[/url] .
Diplomi_snOl
24 Oct 25 at 4:51 pm
1xbet t?rkiye [url=https://1xbet-giris-3.com/]1xbet t?rkiye[/url] .
1xbet giris_aiMi
24 Oct 25 at 4:52 pm
https://masheka.by/poleznaja-informacija/10719-1xbet-promokod-bonus-za-registraciju-130-evro.html
hybgqsh
24 Oct 25 at 4:52 pm
https://t.me/s/bs_1xbet/30
https://t.me/s/bs_1xbet/13
24 Oct 25 at 4:53 pm
купить диплом в нижнекамске [url=www.rudik-diplom5.ru]www.rudik-diplom5.ru[/url] .
Diplomi_ubma
24 Oct 25 at 4:53 pm
https://t.me/s/bs_1xbet/29
https://t.me/bs_1xbet/47
24 Oct 25 at 4:53 pm
диплом с реестром купить [url=http://frei-diplom5.ru/]диплом с реестром купить[/url] .
Diplomi_ktPa
24 Oct 25 at 4:53 pm
Такой подход позволяет точно отслеживать динамику и вносить изменения в программу без потери контроля. Пациент видит прогресс, а врачи получают объективные данные о реакции организма на лечение.
Получить больше информации – http://narkologicheskaya-klinika-stavropol0.ru/narkologicheskaya-klinika-stavropol-otzyvy/
Justinvodia
24 Oct 25 at 4:54 pm
1 xbet giri? [url=www.1xbet-giris-10.com/]1 xbet giri?[/url] .
1xbet giris_hmka
24 Oct 25 at 4:55 pm
купить диплом диспетчера [url=https://rudik-diplom10.ru/]купить диплом диспетчера[/url] .
Diplomi_fbSa
24 Oct 25 at 4:55 pm
купить диплом в бору [url=rudik-diplom13.ru]rudik-diplom13.ru[/url] .
Diplomi_mcon
24 Oct 25 at 4:56 pm
купить диплом с занесением в реестр москва [url=http://www.frei-diplom4.ru]купить диплом с занесением в реестр москва[/url] .
Diplomi_hjOl
24 Oct 25 at 4:56 pm
Only the important and best: https://www.radio-rfe.com
Louisren
24 Oct 25 at 4:56 pm
xbet [url=www.1xbet-giris-7.com]xbet[/url] .
1xbet giris_puKn
24 Oct 25 at 4:56 pm
купить диплом в майкопе [url=https://rudik-diplom8.ru]https://rudik-diplom8.ru[/url] .
Diplomi_cqMt
24 Oct 25 at 4:56 pm