<?php
namespace App\InsuranceCompany\Common\Subscriber;
use App\Entity\InsuranceCompany;
use App\Repository\InsuranceCompanyRepository;
use App\Service\CommunicationLoggerService;
use App\SoapClient\Event\DecodeEvent;
use App\SoapClient\Event\EncodeEvent;
use App\SoapClient\Event\RequestEvent as CustomRequestEvent;
use App\SoapClient\Event\ResponseEvent as CustomResponseEvent;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Phpro\SoapClient\Event\FaultEvent;
use Phpro\SoapClient\Event\RequestEvent;
use Phpro\SoapClient\Event\ResponseEvent;
use Phpro\SoapClient\Type\RequestInterface;
use Psr\Log\LoggerInterface;
use RuntimeException;
use Soap\Engine\HttpBinding\SoapRequest;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
/**
* Class CommonLogSubscriber
*/
class LogSubscriber implements EventSubscriberInterface
{
/**
* Constructor
*
* @param LoggerInterface $logger
* @param CommunicationLoggerService $communicationLoggerService
* @param InsuranceCompanyRepository $insuranceCompanyRepository
*/
public function __construct(
private readonly LoggerInterface $logger,
private readonly CommunicationLoggerService $communicationLoggerService,
private readonly InsuranceCompanyRepository $insuranceCompanyRepository,
)
{
}
/**
* @param RequestEvent $event
*/
public function onCallerRequest(RequestEvent $event)
{
$this->logger->debug(sprintf(
'SOAP Request: call {method}() with request %s',
print_r($event->getRequest(), true),
), [
'method' => $event->getMethod(),
'request' => $event->getRequest(),
]);
}
/**
* @param EncodeEvent $event
*/
public function onEncode(EncodeEvent $event)
{
$this->logger->debug(sprintf(
'[{id}] SOAP Encode: call {method}() with arguments %s',
print_r($event->getArguments(), true),
), [
'id' => $event->getRequestId(),
'method' => $event->getMethod(),
'arguments' => $event->getArguments(),
]);
}
/**
* @param CustomRequestEvent $event
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function onEngineRequest(CustomRequestEvent $event)
{
$this->addRequestIdToRequestContent($event);
$this->logger->info('[{id}] SOAP Request: call {method}() with arguments of type {argumentTypes}', [
'id' => $event->getRequestId(),
'method' => $event->getMethod(),
'argumentTypes' => implode(', ', array_map(
static fn($arg) => get_debug_type($arg),
$event->getArguments(),
)),
]);
$this->logger->debug(sprintf(
'[{id}] SOAP Encoded: call {method}() with arguments %s and request %s',
print_r($event->getArguments(), true),
print_r($event->getRequest(), true),
), [
'id' => $event->getRequestId(),
'method' => $event->getMethod(),
'arguments' => $event->getArguments(),
'request' => $event->getRequest(),
]);
$this->saveInCommunicationLog($event);
}
/**
* @param CustomResponseEvent $event
*/
public function onEngineResponse(CustomResponseEvent $event)
{
$requestEvent = $event->getRequestEvent();
$this->logger->debug(sprintf(
'[{id}] SOAP Response for call {method}() with response %s',
print_r($event->getResponse(), true),
), [
'id' => $requestEvent->getRequestId(),
'method' => $requestEvent->getMethod(),
'response' => $event->getResponse(),
]);
}
/**
* @param DecodeEvent $event
*/
public function onDecode(DecodeEvent $event)
{
$requestEvent = $event->getRequestEvent();
$this->logger->debug(sprintf(
'[{id}] SOAP decoded for call {method}() with response %s and result %s',
print_r($event->getResponse(), true),
print_r($event->getDecoded(), true),
), [
'id' => $requestEvent->getRequestId(),
'method' => $requestEvent->getMethod(),
'request' => $requestEvent->getRequest(),
'response' => $event->getResponse(),
'decoded' => $event->getDecoded(),
]);
$this->logger->info('[{id}] SOAP Response for call {method}() with result of type {resultType}', [
'id' => $requestEvent->getRequestId(),
'method' => $requestEvent->getMethod(),
'resultType' => get_debug_type($event->getDecoded()),
]);
}
/**
* @param ResponseEvent $event
*/
public function onCallerResponse(ResponseEvent $event)
{
$this->logger->debug(sprintf(
'[phpro/soap-client] response for call {method}(): %s',
print_r($event->getResponse(), true)
), [
'method' => $event->getRequestEvent()->getMethod(),
]);
}
/**
* @param FaultEvent $event
*/
public function onFault(FaultEvent $event)
{
$requestEvent = $event->getRequestEvent();
$exception = $event->getSoapException();
$this->logger->error(sprintf(
'[phpro/soap-client] fault "%s" for request "%s" with params %s',
$exception->getMessage(),
$requestEvent->getMethod(),
print_r($requestEvent->getRequest(), true)
), [
'exception' => $exception,
]);
if (preg_match(
'/System\.InvalidOperationException: There is an error in XML document \((\d+), (\d+)\)\./',
$exception->getMessage(),
$matches
)) {
// TODO mark in log file where the error is
$xmlErrorRow = (int)$matches[1];
$xmlErrorColumn = (int)$matches[2];
$this->logger->error('XML error at row:col ' . json_encode([$xmlErrorRow, $xmlErrorColumn]), [
'exception' => $exception,
'xmlErrorRow' => $xmlErrorRow,
'xmlErrorColumn' => $xmlErrorColumn,
]);
}
}
/**
* {@inheritdoc}
* @noinspection PhpArrayShapeAttributeCanBeAddedInspection
*/
public static function getSubscribedEvents(): array
{
return array(
RequestEvent::class => 'onCallerRequest',
EncodeEvent::class => 'onEncode',
CustomRequestEvent::class => 'onEngineRequest',
CustomResponseEvent::class => 'onEngineResponse',
DecodeEvent::class => 'onDecode',
ResponseEvent::class => 'onCallerResponse',
FaultEvent::class => 'onFault',
);
}
/**
* @param CustomRequestEvent $event
*/
private function addRequestIdToRequestContent(CustomRequestEvent $event)
{
$request = $event->getRequest();
$event->setRequest(new SoapRequest(
request: $request->getRequest() . '<!-- BrokerSoft.RequestId: ' . $event->getRequestId() . ' -->',
location: $request->getLocation(),
action: $request->getAction(),
version: $request->getVersion(),
oneWay: $request->getOneWay(),
));
}
/**
* @param CustomRequestEvent $event
* @throws NoResultException
* @throws NonUniqueResultException
*/
private function saveInCommunicationLog(CustomRequestEvent $event)
{
$arguments = $event->getArguments();
$this->communicationLoggerService->createLog(
$this->detectInsuranceCompany($arguments),
$event->getRequestId(),
'SOAP',
$event->getMethod(),
$arguments,
);
}
/**
* @param array $request
* @return InsuranceCompany
* @throws NonUniqueResultException
* @throws NoResultException
*/
private function detectInsuranceCompany(array $request): InsuranceCompany
{
$requestFirstElement = reset($request);
if ($requestFirstElement instanceof RequestInterface) {
$type = get_class($requestFirstElement);
$typeParts = explode('\\', $type);
if (count($typeParts) < 3 || $typeParts[0] !== 'App' || $typeParts[1] !== 'InsuranceCompany') {
throw new RuntimeException('Unknown request type ' . $type);
}
$shortNameLatin = $typeParts[2];
}
else {
$shortNameLatin = 'DZI';
}
return $this->insuranceCompanyRepository->findActiveByShortNameLatin($shortNameLatin);
}
}