ShellResource::getResource()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
ccs 0
cts 2
cp 0
crap 2
1
<?php
2
3
namespace Dazzle\SSH\Driver\Shell;
4
5
use Dazzle\Event\BaseEventEmitterTrait;
6
use Dazzle\Loop\LoopInterface;
7
use Dazzle\SSH\SSH2DriverInterface;
8
use Dazzle\SSH\SSH2ResourceInterface;
9
use Dazzle\Throwable\Exception\Runtime\ReadException;
10
use Dazzle\Throwable\Exception\Runtime\WriteException;
11
use Error;
12
use Exception;
13
14
class ShellResource implements SSH2ResourceInterface
15
{
16
    use BaseEventEmitterTrait;
17
18
    /**
19
     * @var SSH2DriverInterface
20
     */
21
    protected $driver;
22
23
    /**
24
     * @var resource
25
     */
26
    protected $resource;
27
28
    /**
29
     * @var bool
30
     */
31
    private $paused;
32
33
    /**
34
     * @var bool
35
     */
36
    private $closing;
37
38
    /**
39
     * @var bool
40
     */
41
    private $readable;
42
43
    /**
44
     * @var bool
45
     */
46
    private $writable;
47
48
    /**
49
     * @var int
50
     */
51
    private $bufferSize;
52
53
    /**
54
     * @var string
55
     */
56
    private $prefix;
57
58
    /**
59
     * @var string
60
     */
61
    private $successSuffix;
62
63
    /**
64
     * @var string
65
     */
66
    private $failureSuffix;
67
68
    /**
69
     * @param SSH2DriverInterface $driver
70
     * @param resource $resource
71
     */
72 2
    public function __construct(SSH2DriverInterface $driver, $resource)
73
    {
74 2
        $this->driver = $driver;
75 2
        $this->resource = $resource;
76
77 2
        $this->paused = true;
78 2
        $this->closing = false;
79 2
        $this->readable = true;
80 2
        $this->writable = true;
81
82 2
        $this->bufferSize = 4096;
83
84 2
        $this->prefix        = md5(microtime());
85 2
        $this->successSuffix = md5(microtime());
86 2
        $this->failureSuffix = md5(microtime());
87 2
    }
88
89
    /**
90
     * @return string
91
     */
92 2
    public function getId()
93
    {
94 2
        return $this->prefix;
95
    }
96
97
    /**
98
     * @return string
99
     */
100
    public function getPrefix()
101
    {
102
        return $this->prefix;
103
    }
104
105
    /**
106
     * @return string
107
     */
108 2
    public function getSuccessSuffix()
109
    {
110 2
        return $this->successSuffix;
111
    }
112
113
    /**
114
     * @return string
115
     */
116 2
    public function getFailureSuffix()
117
    {
118 2
        return $this->failureSuffix;
119
    }
120
121
    /**
122
     * @override
123
     * @inheritDoc
124
     */
125
    public function setLoop(LoopInterface $loop = null)
126
    {
127
        $this->driver->setLoop($loop);
128
    }
129
130
    /**
131
     * @override
132
     * @inheritDoc
133
     */
134
    public function getLoop()
135
    {
136
        return $this->driver->getLoop();
137
    }
138
139
    /**
140
     * @override
141
     * @inheritDoc
142
     */
143 2
    public function pause()
144
    {
145 2
        if (!$this->paused)
146
        {
147
            $this->paused = true;
148
        }
149 2
    }
150
151
    /**
152
     * @override
153
     * @inheritDoc
154
     */
155
    public function resume()
156
    {
157
        if ($this->paused)
158
        {
159
            $this->paused = false;
160
        }
161
    }
162
163
    /**
164
     * @override
165
     * @inheritDoc
166
     */
167
    public function isPaused()
168
    {
169
        return $this->paused;
170
    }
171
172
    /**
173
     * @override
174
     * @inheritDoc
175
     */
176
    public function getResource()
177
    {
178
        return $this->resource;
179
    }
180
181
    /**
182
     * @override
183
     * @inheritDoc
184
     */
185
    public function getResourceId()
186
    {
187
        return (int) $this->resource;
188
    }
189
190
    /**
191
     * @override
192
     * @inheritDoc
193
     */
194
    public function getMetadata()
195
    {
196
        return stream_get_meta_data($this->resource);
197
    }
198
199
    /**
200
     * @override
201
     * @inheritDoc
202
     */
203
    public function getStreamType()
204
    {
205
        return $this->getMetadata()['stream_type'];
206
    }
207
208
    /**
209
     * @override
210
     * @inheritDoc
211
     */
212
    public function getWrapperType()
213
    {
214
        return $this->getMetadata()['wrapper_type'];
215
    }
216
217
    /**
218
     * @override
219
     * @inheritDoc
220
     */
221
    public function isOpen()
222
    {
223
        return !$this->closing;
224
    }
225
226
    /**
227
     * @override
228
     * @inheritDoc
229
     */
230
    public function isSeekable()
231
    {
232
        return $this->getMetadata()['seekable'];
233
    }
234
235
    /**
236
     * @override
237
     * @inheritDoc
238
     */
239
    public function tell()
240
    {
241
        throw new ReadException('Cannot tell offset of this kind of stream.');
242
    }
243
244
    /**
245
     * @override
246
     * @inheritDoc
247
     */
248
    public function seek($offset, $whence = SEEK_SET)
249
    {
250
        throw new WriteException('Cannot seek on this kind of stream.');
251
    }
252
253
    /**
254
     * @override
255
     * @inheritDoc
256
     */
257
    public function rewind()
258
    {
259
        throw new WriteException('Cannot rewind this kind of stream.');
260
    }
261
262
    /**
263
     * @override
264
     * @inheritDoc
265
     */
266 2
    public function close()
267
    {
268 2
        if ($this->closing)
269
        {
270
            return;
271
        }
272
273 2
        $this->closing = true;
274 2
        $this->readable = false;
275 2
        $this->writable = false;
276
277 2
        $this->emit('close', [ $this ]);
278 2
        $this->pause();
279 2
        $this->emit('done', [ $this ]);
280 2
    }
281
282
    /**
283
     * @override
284
     * @inheritDoc
285
     */
286
    public function isReadable()
287
    {
288
        return $this->readable;
289
    }
290
291
    /**
292
     * @override
293
     * @inheritDoc
294
     */
295
    public function setBufferSize($bufferSize)
296
    {
297
        $this->bufferSize = $bufferSize;
298
    }
299
300
    /**
301
     * @override
302
     * @inheritDoc
303
     */
304 2
    public function getBufferSize()
305
    {
306 2
        return $this->bufferSize;
307
    }
308
309
    /**
310
     * @override
311
     * @inheritDoc
312
     */
313
    public function read($length = null)
314
    {
315
        if (!$this->readable)
316
        {
317
            return $this->throwAndEmitException(
318
                new ReadException('Stream is no longer readable.')
319
            );
320
        }
321
322
        if ($length === null)
323
        {
324
            $length = $this->bufferSize;
325
        }
326
327
        $ret = fread($this->resource, $length);
328
329
        if ($ret === false)
330
        {
331
            return $this->throwAndEmitException(
332
                new ReadException('Cannot read stream.')
333
            );
334
        }
335 View Code Duplication
        else if ($ret !== '')
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...
336
        {
337
            $this->emit('data', [ $this, $ret ]);
338
339
            if (strlen($ret) < $length)
340
            {
341
                $this->emit('end', [ $this ]);
342
            }
343
        }
344
345
        return $ret;
346
    }
347
348
    /**
349
     * @override
350
     * @inheritDoc
351
     */
352
    public function isWritable()
353
    {
354
        return $this->writable;
355
    }
356
357
    /**
358
     * @override
359
     * @inheritDoc
360
     */
361 2
    public function write($text = '')
362
    {
363 2
        if (!$this->writable)
364
        {
365
            return $this->throwAndEmitException(
366
                new WriteException('Stream is no longer writable.')
367
            );
368
        }
369
370 2
        $command = sprintf(
371 2
            "echo %s && %s && echo %s || echo %s\n",
372 2
            $this->prefix,
373 2
            $text,
374 2
            $this->successSuffix . ':$?',
375 2
            $this->failureSuffix . ':$?'
376
        );
377
378 2
        $sent = fwrite($this->resource, $command);
379
380 2
        if ($sent === false)
381
        {
382
            return $this->throwAndEmitException(
383
                new WriteException('Error occurred while writing to the stream resource.')
384
            );
385
        }
386
387 2
        $this->writable = false; // this is single-use stream only!
388 2
        $this->emit('drain', [ $this ]);
389 2
        $this->emit('finish', [ $this ]);
390
391 2
        return true;
392
    }
393
394
    /**
395
     * Emit error event and the throws it too.
396
     *
397
     * @param Error|Exception $ex
398
     * @return null
399
     * @throws Error|Exception
400
     */
401
    protected function throwAndEmitException($ex)
402
    {
403
        $this->emit('error', [ $this, $ex ]);
404
        throw $ex;
405
    }
406
}
407