<?php
namespace App\WsProxy\Subscriber;
use App\Ws\SoapClient\Transport;
use App\WsProxy\Subscriber\Type\SoapCall;
use Phpro\SoapClient\Event\RequestEvent;
use Phpro\SoapClient\Event\ResponseEvent;
use Soap\ExtSoapEngine\Transport\TraceableTransport;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Stopwatch\Stopwatch;
use Symfony\Bundle\FrameworkBundle\DataCollector\AbstractDataCollector;
use Throwable;
use function count;
use function strlen;
/**
* Class SoapCallCollector
* @package Phpro\SoapClient\BridgeBundle\SoapCallCollector
*/
class SoapCallCollectorSubscriber extends AbstractDataCollector implements EventSubscriberInterface
{
/**
* Used for timing the requests
* @var Stopwatch
*/
protected $stopwatch;
/**
* Collection of requests
* @var array
*/
protected $data = [
'calls' => []
];
/**
* @var Transport
*/
protected Transport $transport;
public function __construct(
?Stopwatch $stopwatch
)
{
$this->setStopwatch($stopwatch);
$this->transport = new Transport();
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents(): array
{
return [
ResponseEvent::class => 'onClientResponse',
RequestEvent::class => 'onClientRequest'
];
}
/**
* Process client request event
* @param RequestEvent $e
*/
public function onClientRequest(RequestEvent $e): void
{
$call = new SoapCall($this->stopwatch);
$call->setRequest($e);
$this->data['calls'][] = $call;
}
/**
* Process client response event
* @param ResponseEvent $e
*/
public function onClientResponse(ResponseEvent $e): void
{
$responseClassname = get_class($e->getResponse());
$transport = $this->transport->getTransport($this->transport->getClient($responseClassname));
/** @var SoapCall $call */
$call = array_pop($this->data['calls']);
$call->setResponse($e, $transport, $this->transport->getClientClassName($responseClassname));
$this->data['calls'][] = $call;
}
/**
* Collects data for the given Request and Response.
*
* @param Request $request A Request instance
* @param Response $response A Response instance
* @param Throwable|null $exception An Exception instance
*
* @api
*/
public function collect(Request $request, Response $response, Throwable $exception = null): void
{
// Nothing to do here, we implemented our own methods by hooking into the events
}
/**
* Returns the name of the collector.
* @return string The collector name
* @api
*/
public function getName(): string
{
return 'phpro.soap_client';
}
/**
* Get all soap calls
* @return SoapCall[]
*/
public function getCalls(): array
{
return $this->data['calls'];
}
/**
* Setter method for Stopwatch
* @param Stopwatch|null $stopwatch
* @return $this
*/
public function setStopwatch(?Stopwatch $stopwatch = null): self
{
$this->stopwatch = $stopwatch;
return $this;
}
/**
* Get the total timing of all soap calls
* @return int
*/
public function getTotalTiming(): int
{
$timing = 0;
/** @var SoapCall $call */
foreach ($this->data['calls'] as $call) {
$timing += $call->getTiming();
}
return $timing;
}
/**
* Gets the average timing of all soap calls
* @return float
*/
public function getAverageTiming(): float
{
$totalTiming = $this->getTotalTiming();
$totalCalls = (float) count($this->data['calls']);
return $totalTiming / ($totalCalls <= 0 ? 1 : $totalCalls);
}
public function getLowestTiming(): int
{
$lowestTiming = null;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
if ($lowestTiming === null){
$lowestTiming = $call->getTiming();
}
elseif ($call->getTiming() < $lowestTiming){
$lowestTiming = $call->getTiming();
}
}
return $lowestTiming;
}
public function getHighestTiming(): int
{
$highestTiming = null;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
if ($highestTiming === null){
$highestTiming = $call->getTiming();
}
elseif ($call->getTiming() > $highestTiming){
$highestTiming = $call->getTiming();
}
}
return $highestTiming;
}
public function getTotalResponseBodySize(): float
{
$size = 0.0;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
$size += (float) strlen($call->getResponseBody());
}
return $size / 1024;
}
public function getTotalReceivedSize(): float
{
$size = 0.0;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
$size += (float) strlen($call->getResponseBody());
$size += (float) strlen($call->getResponseHeaders());
}
return $size / 1024;
}
public function getTotalRequestBodySize(): float
{
$size = 0.0;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
$size += (float) strlen($call->getRequestBody());
}
return $size / 1024;
}
public function getTotalSentSize(): float
{
$size = 0.0;
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
$size += (float) strlen($call->getRequestBody());
$size += (float) strlen($call->getRequestHeaders());
}
return $size / 1024;
}
public function getDataExchangeInfoPerCall(): array
{
$data = [];
/**
* @var SoapCall $call
*/
foreach($this->data['calls'] as $call){
$name = $call->getClientName();
$requestBodySize = (float) strlen($call->getRequestBody());
$totalRequestSize = $requestBodySize + (float) strlen($call->getRequestHeaders());
$responseBodySize = (float) strlen($call->getResponseBody());
$totalResponseSize = $responseBodySize + (float) strlen($call->getResponseHeaders());
$data[] = [
'name' => $name,
'method' => $call->getMethod(),
'timing' => $call->getTiming(),
'requestBodySize' => $requestBodySize / 1024,
'totalRequestSize' => $totalRequestSize / 1024,
'responseBodySize' => $responseBodySize / 1024,
'totalResponseSize' => $totalResponseSize / 1024,
];
}
return $data;
}
public function reset(): void
{
// TODO: Implement reset() method.
}
/**
* @param string $factoryClass
* @param TraceableTransport $transport
* @return $this
*/
public function setTransport(string $factoryClass, TraceableTransport $transport): SoapCallCollectorSubscriber
{
$this->transport->setTransport($this->transport->getClient($factoryClass), $transport);
return $this;
}
}