Completed
Push — master ( 503b9d...92f54d )
by Frederik
03:38
created

AbstractConnection::fireEvent()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 5
nc 3
nop 1
1
<?php
2
declare(strict_types=1);
3
4
namespace Genkgo\Mail\Protocol;
5
6
/**
7
 * Class AbstractConnection
8
 * @package Genkgo\Mail\Protocol
9
 * @codeCoverageIgnore
10
 */
11
abstract class AbstractConnection implements ConnectionInterface
12
{
13
    /**
14
     *
15
     */
16
    private const RECEIVE_BYTES = 1024;
17
    /**
18
     * @var resource
19
     */
20
    protected $resource;
21
    /**
22
     * @var \Closure[]
23
     */
24
    private $listeners = [
25
        'connect' => []
26
    ];
27
28
    /**
29
     *
30
     */
31
    final public function __destruct()
32
    {
33
        $this->disconnect();
34
    }
35
36
    /**
37
     * @param string $name
38
     * @param \Closure $callback
39
     */
40
    final public function addListener(string $name, \Closure $callback): void
41
    {
42
        $this->listeners[$name][] = $callback;
43
    }
44
45
    /**
46
     * @param $name
47
     */
48
    final protected function fireEvent($name): void
49
    {
50
        if (!isset($this->listeners[$name])) {
51
            return;
52
        }
53
54
        foreach ($this->listeners[$name] as $listener) {
0 ignored issues
show
Bug introduced by
The expression $this->listeners[$name] of type object<Closure> is not traversable.
Loading history...
55
            $listener();
56
        }
57
    }
58
59
    /**
60
     *
61
     */
62
    abstract public function connect(): void;
63
64
    /**
65
     *
66
     */
67
    final public function disconnect(): void
68
    {
69
        if ($this->resource !== null) {
70
            fclose($this->resource);
71
            $this->resource = null;
72
        }
73
    }
74
75
    /**
76
     * @param float $timeout
77
     */
78
    final public function timeout(float $timeout): void
79
    {
80
        stream_set_timeout($this->resource, $timeout);
81
    }
82
83
    /**
84
     * @param string $request
85
     * @return int
86
     */
87
    final public function send(string $request): int
88
    {
89
        $this->verifyConnection();
90
91
        $bytesWritten = fwrite($this->resource, $request);
92
93
        if ($bytesWritten === false) {
94
            throw new \RuntimeException(sprintf('Could not send command:'));
95
        }
96
97
        $this->verifyAlive();
98
99
        return $bytesWritten;
100
    }
101
102
    /**
103
     * @return string
104
     */
105
    final public function receive(): string
106
    {
107
        $this->verifyConnection();
108
109
        $response = fgets($this->resource, self::RECEIVE_BYTES);
110
111
        $this->verifyAlive();
112
113
        return $response;
114
    }
115
116
    /**
117
     * @param array $keys
118
     * @return array
119
     */
120
    final public function getMetaData(array $keys = []): array
121
    {
122
        $this->verifyConnection();
123
        $this->verifyAlive();
124
125
        $metaData = stream_get_meta_data($this->resource);
126
        if (!$metaData) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $metaData of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
127
            return [];
128
        }
129
130
        $keys = array_map('strtolower', $keys);
131
132
        return array_filter(
133
            $metaData,
134
            function ($key) use ($keys) {
135
                return in_array(strtolower($key), $keys);
136
            },
137
            ARRAY_FILTER_USE_KEY
138
        );
139
    }
140
141
    /**
142
     *
143
     */
144
    private function verifyConnection()
145
    {
146
        if ($this->resource === null) {
147
            $this->connect();
148
        }
149
    }
150
151
    /**
152
     *
153
     */
154
    private function verifyAlive()
155
    {
156
        $info = stream_get_meta_data($this->resource);
157
        if ($info['timed_out']) {
158
            throw new \RuntimeException('Connection has timed out');
159
        }
160
    }
161
}