TimeoutStage   A
last analyzed

Complexity

Total Complexity 17

Size/Duplication

Total Lines 103
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 17
lcom 1
cbo 3
dl 0
loc 103
ccs 52
cts 52
cp 1
rs 10
c 0
b 0
f 0

4 Methods

Rating   Name   Duplication   Size   Complexity  
A processStage() 0 15 4
B handleTimeoutOnDescriptor() 0 26 5
A updateMetadataForAttempt() 0 18 3
B isSingleSocketTimeout() 0 14 5
1
<?php
2
/**
3
 * Async sockets
4
 *
5
 * @copyright Copyright (c) 2015-2017, Efimov Evgenij <[email protected]>
6
 *
7
 * This source file is subject to the MIT license that is bundled
8
 * with this source code in the file LICENSE.
9
 */
10
namespace AsyncSockets\RequestExecutor\Pipeline;
11
12
use AsyncSockets\Event\TimeoutEvent;
13
use AsyncSockets\Exception\SocketException;
14
use AsyncSockets\RequestExecutor\Metadata\RequestDescriptor;
15
use AsyncSockets\RequestExecutor\RequestExecutorInterface;
16
17
/**
18
 * Class TimeoutStage
19
 */
20
class TimeoutStage extends AbstractTimeAwareStage
21
{
22
    /** {@inheritdoc} */
23 34
    public function processStage(array $requestDescriptors)
24
    {
25
        /** @var RequestDescriptor[] $requestDescriptors */
26 34
        $result    = [ ];
27 34
        $microTime = microtime(true);
28 34
        foreach ($requestDescriptors as $key => $descriptor) {
29 15
            $isTimeout = $this->isSingleSocketTimeout($descriptor, $microTime) &&
30 15
                         !$this->handleTimeoutOnDescriptor($descriptor);
31 13
            if ($isTimeout) {
32 10
                $result[$key] = $descriptor;
33 10
            }
34 32
        }
35
36 32
        return $result;
37
    }
38
39
    /**
40
     * Fire timeout event and processes user response
41
     *
42
     * @param RequestDescriptor $descriptor
43
     *
44
     * @return bool True if we may do one more attempt, false otherwise
45
     */
46 22
    public function handleTimeoutOnDescriptor(RequestDescriptor $descriptor)
47
    {
48 22
        $meta  = $descriptor->getMetadata();
49 22
        $event = new TimeoutEvent(
50 22
            $this->executor,
51 22
            $descriptor->getSocket(),
52 22
            $meta[RequestExecutorInterface::META_USER_CONTEXT],
53 22
            $meta[RequestExecutorInterface::META_CONNECTION_FINISH_TIME] !== null &&
54 6
            !$descriptor->getSocket()->isServer() ?
55 22
                TimeoutEvent::DURING_IO :
56
                TimeoutEvent::DURING_CONNECTION
57 22
        );
58
        try {
59 22
            $this->callSocketSubscribers($descriptor, $event);
60 13
            $result = $event->isNextAttemptEnabled();
61 22
        } catch (SocketException $e) {
62 5
            $this->callExceptionSubscribers($descriptor, $e);
63 5
            $result = false;
64
        }
65
66 18
        if ($result) {
67 2
            $this->updateMetadataForAttempt($descriptor, $event->when());
68 2
        }
69
70 18
        return $result;
71
    }
72
73
    /**
74
     * Update data inside descriptor to make one more attempt
75
     *
76
     * @param RequestDescriptor $descriptor Operation descriptor
77
     * @param string            $when When Timeout occurerd, one of TimeoutEvent::DURING_* consts
78
     *
79
     * @return void
80
     */
81 2
    private function updateMetadataForAttempt(RequestDescriptor $descriptor, $when)
82
    {
83
        switch ($when) {
84 2
            case TimeoutEvent::DURING_IO:
85 1
                $descriptor->setMetadata(RequestExecutorInterface::META_LAST_IO_START_TIME, null);
86 1
                break;
87 1
            case TimeoutEvent::DURING_CONNECTION:
88 1
                $descriptor->setRunning(false);
89 1
                $descriptor->setMetadata(
90
                    [
91 1
                        RequestExecutorInterface::META_LAST_IO_START_TIME     => null,
92 1
                        RequestExecutorInterface::META_CONNECTION_START_TIME  => null,
93 1
                        RequestExecutorInterface::META_CONNECTION_FINISH_TIME => null,
94
                    ]
95 1
                );
96 1
                break;
97
        }
98 2
    }
99
100
    /**
101
     * Checks whether given params lead to timeout
102
     *
103
     * @param RequestDescriptor $descriptor Descriptor object
104
     * @param double            $microTime Current time with microseconds
105
     *
106
     * @return bool True, if socket with this params in timeout, false otherwise
107
     */
108 15
    private function isSingleSocketTimeout(RequestDescriptor $descriptor, $microTime)
109
    {
110 15
        $desiredTimeout    = $this->timeoutSetting($descriptor);
111 15
        $lastOperationTime = $this->timeSinceLastIo($descriptor);
112 15
        $hasConnected      = $this->hasConnected($descriptor);
113
114 15
        return ($desiredTimeout !== RequestExecutorInterface::WAIT_FOREVER) &&
115
               (
116 15
                   ($hasConnected && $lastOperationTime !== null) ||
117
                   !$hasConnected
118 15
               ) &&
119 14
               ($microTime - $lastOperationTime >= $desiredTimeout)
120 15
               ;
121
    }
122
}
123