1<?php
2namespace Aws;
3
4use Aws\Exception\AwsException;
5use GuzzleHttp\Promise;
6use GuzzleHttp\Promise\RejectedPromise;
7use Psr\Http\Message\RequestInterface;
8use Exception;
9
10/**
11 * Returns promises that are rejected or fulfilled using a queue of
12 * Aws\ResultInterface and Aws\Exception\AwsException objects.
13 */
14class MockHandler implements \Countable
15{
16    private $queue;
17    private $lastCommand;
18    private $lastRequest;
19    private $onFulfilled;
20    private $onRejected;
21
22    /**
23     * The passed in value must be an array of {@see Aws\ResultInterface} or
24     * {@see AwsException} objects that acts as a queue of results or
25     * exceptions to return each time the handler is invoked.
26     *
27     * @param array    $resultOrQueue
28     * @param callable $onFulfilled Callback to invoke when the return value is fulfilled.
29     * @param callable $onRejected  Callback to invoke when the return value is rejected.
30     */
31    public function __construct(
32        array $resultOrQueue = [],
33        callable $onFulfilled = null,
34        callable $onRejected = null
35    ) {
36        $this->onFulfilled = $onFulfilled;
37        $this->onRejected = $onRejected;
38
39        if ($resultOrQueue) {
40            call_user_func_array([$this, 'append'], $resultOrQueue);
41        }
42    }
43
44    /**
45     * Adds one or more variadic ResultInterface or AwsException objects to the
46     * queue.
47     */
48    public function append()
49    {
50        foreach (func_get_args() as $value) {
51            if ($value instanceof ResultInterface
52                || $value instanceof Exception
53                || is_callable($value)
54            ) {
55                $this->queue[] = $value;
56            } else {
57                throw new \InvalidArgumentException('Expected an Aws\ResultInterface or Exception.');
58            }
59        }
60    }
61
62    /**
63     * Adds one or more \Exception or \Throwable to the queue
64     */
65    public function appendException()
66    {
67        foreach (func_get_args() as $value) {
68            if ($value instanceof \Exception || $value instanceof \Throwable) {
69                $this->queue[] = $value;
70            } else {
71                throw new \InvalidArgumentException('Expected an \Exception or \Throwable.');
72            }
73        }
74    }
75
76    public function __invoke(
77        CommandInterface $command,
78        RequestInterface $request
79    ) {
80        if (!$this->queue) {
81            $last = $this->lastCommand
82                ? ' The last command sent was ' . $this->lastCommand->getName() . '.'
83                : '';
84            throw new \RuntimeException('Mock queue is empty. Trying to send a '
85                . $command->getName() . ' command failed.' . $last);
86        }
87
88        $this->lastCommand = $command;
89        $this->lastRequest = $request;
90
91        $result = array_shift($this->queue);
92
93        if (is_callable($result)) {
94            $result = $result($command, $request);
95        }
96
97        if ($result instanceof \Exception) {
98            $result = new RejectedPromise($result);
99        } else {
100            // Add an effective URI and statusCode if not present.
101            $meta = $result['@metadata'];
102            if (!isset($meta['effectiveUri'])) {
103                $meta['effectiveUri'] = (string) $request->getUri();
104            }
105            if (!isset($meta['statusCode'])) {
106                $meta['statusCode'] = 200;
107            }
108            $result['@metadata'] = $meta;
109            $result = Promise\promise_for($result);
110        }
111
112        $result->then($this->onFulfilled, $this->onRejected);
113
114        return $result;
115    }
116
117    /**
118     * Get the last received request.
119     *
120     * @return RequestInterface
121     */
122    public function getLastRequest()
123    {
124        return $this->lastRequest;
125    }
126
127    /**
128     * Get the last received command.
129     *
130     * @return CommandInterface
131     */
132    public function getLastCommand()
133    {
134        return $this->lastCommand;
135    }
136
137    /**
138     * Returns the number of remaining items in the queue.
139     *
140     * @return int
141     */
142    public function count()
143    {
144        return count($this->queue);
145    }
146}
147