<?php
namespace Payum\Core;
use Payum\Core\Action\ActionInterface;
use Payum\Core\Exception\LogicException;
use Payum\Core\Exception\RequestNotSupportedException;
use Payum\Core\Exception\UnsupportedApiException;
use Payum\Core\Extension\Context;
use Payum\Core\Extension\ExtensionCollection;
use Payum\Core\Extension\ExtensionInterface;
use Payum\Core\Reply\ReplyInterface;
class Gateway implements GatewayInterface
{
/**
* @var Action\ActionInterface[]
*/
protected $actions;
/**
* @var mixed[]
*/
protected $apis;
/**
* @var \Payum\Core\Extension\ExtensionCollection
*/
protected $extensions;
/**
* @var Context[]
*/
protected $stack;
/**
*/
public function __construct()
{
$this->stack = [];
$this->actions = [];
$this->apis = [];
$this->extensions = new ExtensionCollection();
}
/**
* @param mixed $api
* @param bool $forcePrepend
*
* @return void
*/
public function addApi($api, $forcePrepend = false)
{
$forcePrepend ?
array_unshift($this->apis, $api) :
array_push($this->apis, $api)
;
}
/**
* @param Action\ActionInterface $action
* @param bool $forcePrepend
*
* @return void
*/
public function addAction(ActionInterface $action, $forcePrepend = false)
{
$forcePrepend ?
array_unshift($this->actions, $action) :
array_push($this->actions, $action)
;
}
/**
* @param \Payum\Core\Extension\ExtensionInterface $extension
* @param bool $forcePrepend
*
* @return void
*/
public function addExtension(ExtensionInterface $extension, $forcePrepend = false)
{
$this->extensions->addExtension($extension, $forcePrepend);
}
/**
* {@inheritDoc}
*/
public function execute($request, $catchReply = false)
{
$context = new Context($this, $request, $this->stack);
array_push($this->stack, $context);
try {
$this->extensions->onPreExecute($context);
if (false == $context->getAction()) {
if (false == $action = $this->findActionSupported($context->getRequest())) {
throw RequestNotSupportedException::create($context->getRequest());
}
$context->setAction($action);
}
$this->extensions->onExecute($context);
$context->getAction()->execute($request);
$this->extensions->onPostExecute($context);
array_pop($this->stack);
} catch (ReplyInterface $reply) {
$context->setReply($reply);
$this->extensions->onPostExecute($context);
array_pop($this->stack);
if ($catchReply && $context->getReply()) {
return $context->getReply();
}
if ($context->getReply()) {
throw $context->getReply();
}
} catch (\Exception $e) {
$context->setException($e);
$this->onPostExecuteWithException($context);
}
return;
}
protected function onPostExecuteWithException(Context $context)
{
array_pop($this->stack);
$exception = $context->getException();
try {
$this->extensions->onPostExecute($context);
} catch (\Exception $e) {
// logic is similar to one in Symfony's ExceptionListener::onKernelException
$wrapper = $e;
while ($prev = $wrapper->getPrevious()) {
if ($exception === $wrapper = $prev) {
throw $e;
}
}
$prev = new \ReflectionProperty('Exception', 'previous');
$prev->setAccessible(true);
$prev->setValue($wrapper, $exception);
throw $e;
}
if ($context->getException()) {
throw $context->getException();
}
}
/**
* @param mixed $request
*
* @return ActionInterface|false
*/
protected function findActionSupported($request)
{
foreach ($this->actions as $action) {
if ($action instanceof GatewayAwareInterface) {
$action->setGateway($this);
}
if ($action instanceof ApiAwareInterface) {
$apiSet = false;
$unsupportedException = null;
foreach ($this->apis as $api) {
try {
$action->setApi($api);
$apiSet = true;
break;
} catch (UnsupportedApiException $e) {
$unsupportedException = $e;
}
}
if (false == $apiSet) {
throw new LogicException(sprintf('Cannot find right api for the action %s', get_class($action)), 0, $unsupportedException);
}
}
if ($action->supports($request)) {
return $action;
}
}
return false;
}
}