Completed
Push — 5.x ( 2cf8fe...f77ff9 )
by Lars
04:26
created

Swift_Transport_StreamBuffer::_commit()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42
Metric Value
dl 0
loc 22
ccs 0
cts 13
cp 0
rs 8.6737
cc 6
eloc 12
nc 6
nop 1
crap 42
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
            default:
80 5
                $this->_establishSocketConnection();
81 5
                break;
82
        }
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
    public function startTLS()
112
    {
113
        return stream_socket_enable_crypto($this->_stream, true, STREAM_CRYPTO_METHOD_TLS_CLIENT);
114
    }
115
116
    /**
117
     * Perform any shutdown logic needed.
118
     */
119
    public function terminate()
120
    {
121
        if (isset($this->_stream)) {
122
            switch ($this->_params['type']) {
123
                case self::TYPE_PROCESS:
124
                    fclose($this->_in);
125
                    fclose($this->_out);
126
                    proc_close($this->_stream);
127
                    break;
128
                case self::TYPE_SOCKET:
129
                default:
130
                    fclose($this->_stream);
131
                    break;
132
            }
133
        }
134
        $this->_stream = null;
135
        $this->_out = null;
136
        $this->_in = null;
137
    }
138
139
    /**
140
     * Set an array of string replacements which should be made on data written
141
     * to the buffer.
142
     *
143
     * This could replace LF with CRLF for example.
144
     *
145
     * @param string[] $replacements
146
     */
147 2
    public function setWriteTranslations(array $replacements)
148
    {
149 2
        foreach ($this->_translations as $search => $replace) {
150 1
            if (!isset($replacements[$search])) {
151
                $this->removeFilter($search);
152 1
                unset($this->_translations[$search]);
153
            }
154
        }
155
156 2
        foreach ($replacements as $search => $replace) {
157 2
            if (!isset($this->_translations[$search])) {
158 2
                $this->addFilter(
159 2
                    $this->_replacementFactory->createFilter($search, $replace),
160
                    $search
161
                );
162 2
                $this->_translations[$search] = true;
163
            }
164
        }
165 2
    }
166
167
    /**
168
     * Get a line of output (including any CRLF).
169
     *
170
     * The $sequence number comes from any writes and may or may not be used
171
     * depending upon the implementation.
172
     *
173
     * @param int $sequence of last write to scan from
174
     *
175
     * @throws Swift_IoException
176
     *
177
     * @return string
178
     */
179 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...
180
    {
181 5
        if (isset($this->_out) && !feof($this->_out)) {
182 5
            $line = fgets($this->_out);
183 5
            if (!$line) {
184 1
                $metas = stream_get_meta_data($this->_out);
185 1
                if ($metas['timed_out']) {
186 1
                    throw new Swift_IoException('Connection to ' . $this->_getReadConnectionDescription() . ' Timed Out');
187
                }
188
            }
189
190 4
            return $line;
191
        }
192
    }
193
194
    /**
195
     * Reads $length bytes from the stream into a string and moves the pointer
196
     * through the stream by $length.
197
     *
198
     * If less bytes exist than are requested the remaining bytes are given instead.
199
     * If no bytes are remaining at all, boolean false is returned.
200
     *
201
     * @param int $length
202
     *
203
     * @throws Swift_IoException
204
     *
205
     * @return string|null
206
     */
207 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...
208
    {
209
        if (isset($this->_out) && !feof($this->_out)) {
210
            $ret = fread($this->_out, $length);
211
            if (!$ret) {
212
                $metas = stream_get_meta_data($this->_out);
213
                if ($metas['timed_out']) {
214
                    throw new Swift_IoException(
215
                        'Connection to ' . $this->_getReadConnectionDescription() . ' Timed Out'
216
                    );
217
                }
218
            }
219
220
            return $ret;
221
        }
222
    }
223
224
    /** Not implemented */
225
    public function setReadPointer($byteOffset)
226
    {
227
    }
228
229
    /** Flush the stream contents */
230
    protected function _flush()
231
    {
232
        if (isset($this->_in)) {
233
            fflush($this->_in);
234
        }
235
    }
236
237
    /**
238
     * Write this bytes to the stream
239
     *
240
     * @param string $bytes
241
     *
242
     * @return int
243
     *
244
     * @throws Swift_IoException
245
     */
246
    protected function _commit($bytes)
247
    {
248
        if (isset($this->_in)) {
249
            $bytesToWrite = strlen($bytes);
250
            $totalBytesWritten = 0;
251
252
            while ($totalBytesWritten < $bytesToWrite) {
253
                $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten));
254
                if (false === $bytesWritten || 0 === $bytesWritten) {
255
                    throw new Swift_IoException(
256
                        'Connection to ' . $this->_getReadConnectionDescription() . ' has gone away'
257
                    );
258
                }
259
260
                $totalBytesWritten += $bytesWritten;
261
            }
262
263
            if ($totalBytesWritten > 0) {
264
                return ++$this->_sequence;
265
            }
266
        }
267
    }
268
269
    /**
270
     * Establishes a connection to a remote server.
271
     */
272 5
    private function _establishSocketConnection()
273
    {
274
        // set default "php"-options for "stream_context_create"
275
        // and overwrite it with the "user"-options
276 5
        $options = stream_context_get_options(stream_context_get_default());
277
278 5
        $host = $this->_params['host'];
279 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...
280 1
            $host = $this->_params['protocol'] . '://' . $host;
281
        }
282
283 5
        $timeout = 15;
284 5
        if (!empty($this->_params['timeout'])) {
285 5
            $timeout = $this->_params['timeout'];
286
        }
287
288 5
        if (!empty($this->_params['sourceIp'])) {
289
            $options['socket']['bindto'] = $this->_params['sourceIp'] . ':0';
290
        }
291
292 5
        if (isset($this->_params['stream_context_options'])) {
293 4
            $options = array_merge($options, $this->_params['stream_context_options']);
294
        }
295
296 5
        $streamContext = stream_context_create($options);
297 5
        $this->_stream = @stream_socket_client($host . ':' . $this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext);
298
299 5
        if (false === $this->_stream) {
300
            throw new Swift_TransportException('Connection could not be established with host ' . $this->_params['host'] . ' [' . $errstr . ' #' . $errno . ']');
301
        }
302
303 5
        if (!empty($this->_params['blocking'])) {
304 5
            stream_set_blocking($this->_stream, 1);
305
        } else {
306
            stream_set_blocking($this->_stream, 0);
307
        }
308
309 5
        stream_set_timeout($this->_stream, $timeout);
310 5
        $this->_in = &$this->_stream;
311 5
        $this->_out = &$this->_stream;
312 5
    }
313
314
    /**
315
     * Opens a process for input/output.
316
     */
317
    private function _establishProcessConnection()
318
    {
319
        $command = $this->_params['command'];
320
        $descriptorSpec = array(
321
            0 => array('pipe', 'r'),
322
            1 => array('pipe', 'w'),
323
            2 => array('pipe', 'w'),
324
        );
325
326
        $this->_stream = proc_open($command, $descriptorSpec, $pipes);
327
        stream_set_blocking($pipes[2], 0);
328
329
        $err = stream_get_contents($pipes[2]);
330
        if ($err) {
331
            throw new Swift_TransportException('Process could not be started [' . $err . ']');
332
        }
333
334
        $this->_in = &$pipes[0];
335
        $this->_out = &$pipes[1];
336
    }
337
338 1
    private function _getReadConnectionDescription()
339
    {
340 1
        switch ($this->_params['type']) {
341 1
            case self::TYPE_PROCESS:
342
                return 'Process ' . $this->_params['command'];
343
                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...
344
345 1
            case self::TYPE_SOCKET:
346
            default:
347 1
                $host = $this->_params['host'];
348 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...
349 1
                    $host = $this->_params['protocol'] . '://' . $host;
350
                }
351 1
                $host .= ':' . $this->_params['port'];
352
353 1
                return $host;
354
                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...
355
        }
356
    }
357
}
358