Completed
Push — 5.x ( c9dc5a...95e895 )
by Lars
17:11 queued 04:33
created

Swift_Transport_StreamBuffer::read()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 16
Code Lines 9

Duplication

Lines 16
Ratio 100 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 9
c 0
b 0
f 0
nc 4
nop 1
dl 16
loc 16
ccs 0
cts 12
cp 0
crap 30
rs 8.8571
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
    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 4
    public function terminate()
120
    {
121 4
        if (isset($this->_stream)) {
122 4
            switch ($this->_params['type']) {
123 4
                case self::TYPE_PROCESS:
124
                    fclose($this->_in);
125
                    fclose($this->_out);
126
                    proc_close($this->_stream);
127
                    break;
128 4
                case self::TYPE_SOCKET:
129 4
                default:
130 4
                    fclose($this->_stream);
131 4
                    break;
132 4
            }
133 4
        }
134 4
        $this->_stream = null;
135 4
        $this->_out = null;
136 4
        $this->_in = null;
137 4
    }
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 6
    public function setWriteTranslations(array $replacements)
148
    {
149 6
        foreach ($this->_translations as $search => $replace) {
150 5
            if (!isset($replacements[$search])) {
151 4
                $this->removeFilter($search);
152 4
                unset($this->_translations[$search]);
153 4
            }
154 6
        }
155
156 6
        foreach ($replacements as $search => $replace) {
157 6
            if (!isset($this->_translations[$search])) {
158 6
                $this->addFilter(
159 6
                    $this->_replacementFactory->createFilter($search, $replace),
160
                    $search
161 6
                );
162 6
                $this->_translations[$search] = true;
163 6
            }
164 6
        }
165 6
    }
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 4
    protected function _flush()
231
    {
232 4
        if (isset($this->_in)) {
233 4
            fflush($this->_in);
234 4
        }
235 4
    }
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 4
    protected function _commit($bytes)
247
    {
248 4
        if (isset($this->_in)) {
249 4
            $bytesToWrite = strlen($bytes);
250 4
            $totalBytesWritten = 0;
251
252 4
            while ($totalBytesWritten < $bytesToWrite) {
253 4
                $bytesWritten = fwrite($this->_in, substr($bytes, $totalBytesWritten));
254 4
                if (false === $bytesWritten || 0 === $bytesWritten) {
255
                    throw new Swift_IoException(
256
                        'Connection to ' . $this->_getReadConnectionDescription() . ' has gone away'
257
                    );
258
                }
259
260 4
                $totalBytesWritten += $bytesWritten;
261 4
            }
262
263 4
            if ($totalBytesWritten > 0) {
264 4
                return ++$this->_sequence;
265
            }
266
        }
267
    }
268
269
    /**
270
     * Establishes a connection to a remote server.
271
     *
272
     * @throws Swift_TransportException
273
     */
274 5
    private function _establishSocketConnection()
275
    {
276
        // set default "php"-options for "stream_context_create"
277
        // and overwrite it with the "user"-options
278 5
        $options = stream_context_get_options(stream_context_get_default());
279
280 5
        $host = $this->_params['host'];
281 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...
282 1
            $host = $this->_params['protocol'] . '://' . $host;
283 1
        }
284
285 5
        $timeout = 15;
286 5
        if (!empty($this->_params['timeout'])) {
287 5
            $timeout = $this->_params['timeout'];
288 5
        }
289
290 5
        if (!empty($this->_params['sourceIp'])) {
291
            $options['socket']['bindto'] = $this->_params['sourceIp'] . ':0';
292
        }
293
294 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...
295
            $options = array_replace_recursive($options, $this->_params['verifySsl']);
296
        }
297
298 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...
299
            $options = array_replace_recursive($options, $this->_params['stream_context_options']);
300
        }
301
302 5
        $streamContext = stream_context_create($options);
303
        /** @noinspection PhpUsageOfSilenceOperatorInspection */
304 5
        $this->_stream = @stream_socket_client($host . ':' . $this->_params['port'], $errno, $errstr, $timeout, STREAM_CLIENT_CONNECT, $streamContext);
305
306 5
        if (false === $this->_stream) {
307
            throw new Swift_TransportException('Connection could not be established with host ' . $this->_params['host'] . ' [' . $errstr . ' #' . $errno . ']');
308
        }
309
310 5
        if (!empty($this->_params['blocking'])) {
311 5
            stream_set_blocking($this->_stream, 1);
312 5
        } else {
313
            stream_set_blocking($this->_stream, 0);
314
        }
315
316 5
        stream_set_timeout($this->_stream, $timeout);
317 5
        $this->_in = &$this->_stream;
318 5
        $this->_out = &$this->_stream;
319 5
    }
320
321
    /**
322
     * Opens a process for input/output.
323
     */
324
    private function _establishProcessConnection()
325
    {
326
        $command = $this->_params['command'];
327
        $descriptorSpec = array(
328
            0 => array('pipe', 'r'),
329
            1 => array('pipe', 'w'),
330
            2 => array('pipe', 'w'),
331
        );
332
333
        $pipes = array();
334
        $this->_stream = proc_open($command, $descriptorSpec, $pipes);
335
        stream_set_blocking($pipes[2], 0);
336
337
        $err = stream_get_contents($pipes[2]);
338
        if ($err) {
339
            throw new Swift_TransportException('Process could not be started [' . $err . ']');
340
        }
341
342
        $this->_in = &$pipes[0];
343
        $this->_out = &$pipes[1];
344
    }
345
346 1
    private function _getReadConnectionDescription()
347
    {
348 1
        switch ($this->_params['type']) {
349 1
            case self::TYPE_PROCESS:
350
                return 'Process ' . $this->_params['command'];
351
                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...
352
353 1
            case self::TYPE_SOCKET:
354 1
            default:
355 1
                $host = $this->_params['host'];
356 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...
357 1
                    $host = $this->_params['protocol'] . '://' . $host;
358 1
                }
359 1
                $host .= ':' . $this->_params['port'];
360
361 1
                return $host;
362
                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...
363
        }
364
    }
365
}
366