Swift_Transport_StreamBuffer::terminate()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 19
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 14
CRAP Score 4.1755

Importance

Changes 0
Metric Value
cc 4
eloc 15
nc 4
nop 0
dl 0
loc 19
rs 9.2
c 0
b 0
f 0
ccs 14
cts 18
cp 0.7778
crap 4.1755
1
<?php
2
3
/*
4
 * This file is part of SwiftMailer.
5
 * (c) 2004-2009 Chris Corbyn
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
/**
12
 * A generic IoBuffer implementation supporting remote sockets and local processes.
13
 *
14
 * @author Chris Corbyn
15
 */
16
class Swift_Transport_StreamBuffer extends Swift_ByteStream_AbstractFilterableInputStream implements Swift_Transport_IoBuffer
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
    /**
19
     * A primary socket
20
     *
21
     * @var resource
22
     */
23
    private $_stream;
24
25
    /**
26
     * The input stream
27
     */
28
    private $_in;
29
30
    /**
31
     * The output stream
32
     */
33
    private $_out;
34
35
    /**
36
     * Buffer initialization parameters
37
     */
38
    private $_params = array();
39
40
    /**
41
     * The ReplacementFilterFactory
42
     *
43
     * @var Swift_ReplacementFilterFactory
44
     */
45
    private $_replacementFactory;
46
47
    /**
48
     * Translations performed on data being streamed into the buffer
49
     *
50
     * @var array
51
     */
52
    private $_translations = array();
53
54
    /**
55
     * Create a new StreamBuffer using $replacementFactory for transformations.
56
     *
57
     * @param Swift_ReplacementFilterFactory $replacementFactory
58
     */
59 8
    public function __construct(Swift_ReplacementFilterFactory $replacementFactory)
60
    {
61 8
        $this->_replacementFactory = $replacementFactory;
62 8
    }
63
64
    /**
65
     * Perform any initialization needed, using the given $params.
66
     *
67
     * Parameters will vary depending upon the type of IoBuffer used.
68
     *
69
     * @param array $params
70
     */
71 5
    public function initialize(array $params)
72
    {
73 5
        $this->_params = $params;
74 5
        switch ($params['type']) {
75 5
            case self::TYPE_PROCESS:
76
                $this->_establishProcessConnection();
77
                break;
78 5
            case self::TYPE_SOCKET:
79 5
            default:
80 5
                $this->_establishSocketConnection();
81 5
                break;
82 5
        }
83 5
    }
84
85
    /**
86
     * Set an individual param on the buffer (e.g. switching to SSL).
87
     *
88
     * @param string $param
89
     * @param integer  $value
90
     */
91
    public function setParam($param, $value)
92
    {
93
        if (isset($this->_stream)) {
94
            switch ($param) {
95
                case 'timeout':
96
                    if ($this->_stream) {
97
                        stream_set_timeout($this->_stream, $value);
98
                    }
99
                    break;
100
101
                case 'blocking':
102
                    if ($this->_stream) {
103
                        stream_set_blocking($this->_stream, 1);
104
                    }
105
106
            }
107
        }
108
        $this->_params[$param] = $value;
109
    }
110
111
    /**
112
     * @return mixed <p>Return <strong>true</strong> on success, <strong>false</strong> if negotiation has failed or <strong>0</strong> if there isn't enough data and you should try again.</p>
113
     */
114
    public function startTLS()
115
    {
116
        return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
117
    }
118
119
    /**
120
     * Perform any shutdown logic needed.
121
     */
122 4
    public function terminate()
123
    {
124 4
        if (isset($this->_stream)) {
125 4
            switch ($this->_params['type']) {
126 4
                case self::TYPE_PROCESS:
127
                    fclose($this->_in);
128
                    fclose($this->_out);
129
                    proc_close($this->_stream);
130
                    break;
131 4
                case self::TYPE_SOCKET:
132 4
                default:
133 4
                    fclose($this->_stream);
134 4
                    break;
135 4
            }
136 4
        }
137 4
        $this->_stream = null;
138 4
        $this->_out = null;
139 4
        $this->_in = null;
140 4
    }
141
142
    /**
143
     * Set an array of string replacements which should be made on data written
144
     * to the buffer.
145
     *
146
     * This could replace LF with CRLF for example.
147
     *
148
     * @param string[] $replacements
149
     */
150 6
    public function setWriteTranslations(array $replacements)
151
    {
152 6
        foreach ($this->_translations as $search => $replace) {
153 5
            if (!isset($replacements[$search])) {
154 4
                $this->removeFilter($search);
155 4
                unset($this->_translations[$search]);
156 4
            }
157 6
        }
158
159 6
        foreach ($replacements as $search => $replace) {
160 6
            if (!isset($this->_translations[$search])) {
161 6
                $this->addFilter(
162 6
                    $this->_replacementFactory->createFilter($search, $replace),
163
                    $search
164 6
                );
165 6
                $this->_translations[$search] = true;
166 6
            }
167 6
        }
168 6
    }
169
170
    /**
171
     * Get a line of output (including any CRLF).
172
     *
173
     * The $sequence number comes from any writes and may or may not be used
174
     * depending upon the implementation.
175
     *
176
     * @param int $sequence of last write to scan from
177
     *
178
     * @throws Swift_IoException
179
     *
180
     * @return string
181
     */
182 5 View Code Duplication
    public function readLine($sequence)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
    {
184
        if (
185 5
            !isset($this->_out)
186 5
            ||
187 5
            feof($this->_out)
188 5
        ) {
189
          // TODO?
190
          return;
191
        }
192
193 5
        $line = fgets($this->_out);
194 5
        if (!$line) {
195 1
            $metas = stream_get_meta_data($this->_out);
196 1
            if ($metas['timed_out']) {
197 1
                throw new Swift_IoException('Connection to ' . $this->_getReadConnectionDescription() . ' Timed Out');
198
            }
199
        }
200
201 4
        return $line;
202
    }
203
204
    /**
205
     * Reads $length bytes from the stream into a string and moves the pointer
206
     * through the stream by $length.
207
     *
208
     * If less bytes exist than are requested the remaining bytes are given instead.
209
     * If no bytes are remaining at all, boolean false is returned.
210
     *
211
     * @param int $length
212
     *
213
     * @throws Swift_IoException
214
     *
215
     * @return string|null
216
     */
217 View Code Duplication
    public function read($length)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
218
    {
219
        if (
220
            !isset($this->_out)
221
            ||
222
            feof($this->_out)
223
        ) {
224
            // TODO?
225
            return;
226
        }
227
228
        $ret = fread($this->_out, $length);
229
        if (!$ret) {
230
            $metas = stream_get_meta_data($this->_out);
231
            if ($metas['timed_out']) {
232
                throw new Swift_IoException(
233
                    'Connection to ' . $this->_getReadConnectionDescription() . ' Timed Out'
234
                );
235
            }
236
        }
237
238
        return $ret;
239
    }
240
241
    /**
242
     * WARNING: Not implemented
243
     *
244
     * @param int $byteOffset
245
     */
246
    public function setReadPointer($byteOffset)
247
    {
248
    }
249
250
    /** Flush the stream contents */
251 4
    protected function _flush()
252
    {
253 4
        if (isset($this->_in)) {
254 4
            fflush($this->_in);
255 4
        }
256 4
    }
257
258
    /**
259
     * Write this bytes to the stream
260
     *
261
     * @param string $bytes
262
     *
263
     * @return int
264
     *
265
     * @throws Swift_IoException
266
     */
267 4
    protected function _commit($bytes)
268
    {
269 4
        if (!isset($this->_in)) {
270
            // TODO?
271
            return;
272
        }
273
274 4
        $bytesToWrite = strlen($bytes);
275 4
        $totalBytesWritten = 0;
276
277 4
        while ($totalBytesWritten < $bytesToWrite) {
278 4
            $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten));
279 4
            if (false === $bytesWritten || 0 === $bytesWritten) {
280
                throw new Swift_IoException(
281
                    'Connection to ' . $this->_getReadConnectionDescription() . ' has gone away'
282
                );
283
            }
284
285 4
            $totalBytesWritten += $bytesWritten;
286 4
        }
287
288 4
        if ($totalBytesWritten > 0) {
289 4
            return ++$this->_sequence;
290
        }
291
292
        // TODO?
293
        return;
294
    }
295
296
    /**
297
     * Establishes a connection to a remote server.
298
     *
299
     * @throws Swift_TransportException
300
     */
301 5
    private function _establishSocketConnection()
302
    {
303
        // set default "php"-options for "stream_context_create"
304
        // and overwrite it with the "user"-options
305 5
        $options = stream_context_get_options(stream_context_get_default());
306
307 5
        $host = $this->_params['host'];
308 5 View Code Duplication
        if (!empty($this->_params['protocol'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
309 1
            $host = $this->_params['protocol'] . '://' . $host;
310 1
        }
311
312 5
        $timeout = 15;
313 5
        if (!empty($this->_params['timeout'])) {
314 5
            $timeout = $this->_params['timeout'];
315 5
        }
316
317 5
        if (!empty($this->_params['sourceIp'])) {
318
            $options['socket']['bindto'] = $this->_params['sourceIp'] . ':0';
319
        }
320
321 5 View Code Duplication
        if (!empty($this->_params['verifySsl'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
322
            $options = array_replace_recursive($options, $this->_params['verifySsl']);
323
        }
324
325 5 View Code Duplication
        if (!empty($this->_params['stream_context_options'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
            $options = array_replace_recursive($options, $this->_params['stream_context_options']);
327
        }
328
329 5
        $streamContext = stream_context_create($options);
330
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
331 5
        $this->_stream = @stream_socket_client($host . ':' . $this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext);
332
333 5
        if (false === $this->_stream) {
334
            throw new Swift_TransportException('Connection could not be established with host ' . $this->_params['host'] . ' [' . $errstr . ' #' . $errno . ']');
335
        }
336
337 5
        if (!empty($this->_params['blocking'])) {
338 5
            stream_set_blocking($this->_stream, 1);
339 5
        } else {
340
            stream_set_blocking($this->_stream, 0);
341
        }
342
343 5
        stream_set_timeout($this->_stream, $timeout);
344 5
        $this->_in = &$this->_stream;
345 5
        $this->_out = &$this->_stream;
346 5
    }
347
348
    /**
349
     * Opens a process for input/output.
350
     */
351
    private function _establishProcessConnection()
352
    {
353
        $command = $this->_params['command'];
354
        $descriptorSpec = array(
355
            0 => array('pipe', 'r'),
356
            1 => array('pipe', 'w'),
357
            2 => array('pipe', 'w'),
358
        );
359
360
        $pipes = array();
361
        $this->_stream = proc_open($command, $descriptorSpec, $pipes);
362
        stream_set_blocking($pipes[2], 0);
363
364
        $err = stream_get_contents($pipes[2]);
365
        if ($err) {
366
            throw new Swift_TransportException('Process could not be started [' . $err . ']');
367
        }
368
369
        $this->_in = &$pipes[0];
370
        $this->_out = &$pipes[1];
371
    }
372
373
    /**
374
     * @return string
375
     */
376 1
    private function _getReadConnectionDescription()
377
    {
378 1
        switch ($this->_params['type']) {
379 1
            case self::TYPE_PROCESS:
380
                return 'Process ' . $this->_params['command'];
381
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
382
383 1
            case self::TYPE_SOCKET:
384 1
            default:
385 1
                $host = $this->_params['host'];
386 1 View Code Duplication
                if (!empty($this->_params['protocol'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
387 1
                    $host = $this->_params['protocol'] . '://' . $host;
388 1
                }
389 1
                $host .= ':' . $this->_params['port'];
390
391 1
                return $host;
392
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
393
        }
394
    }
395
}
396