Completed
Push — master ( a1dfae...853db6 )
by Roberto
06:19 queued 03:11
created

PhpSerial::write()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 9
ccs 0
cts 7
cp 0
rs 9.6666
cc 2
eloc 6
nc 2
nop 1
crap 6
1
<?php
2
3
namespace Posprint\Extras;
4
5
/**
6
 * Serial port control class
7
 *
8
 * Refactoring from original, https://github.com/Xowap/PHP-Serial, to meet PSR standards
9
 * and propose improvements and fixes to the fact that the original is not actively
10
 * maintained for many years.
11
 * by Roberto L. Machado <linux dot rlm at gmail dot com>
12
 * 
13
 * IMPORTANT: check and adjust permissions for serial port access by server user like www-data
14
 * 
15
 * @author Rémy Sanchez <[email protected]>
16
 * @author Rizwan Kassim <[email protected]>
17
 * @thanks Aurélien Derouineau for finding how to open serial ports with windows
18
 * @thanks Alec Avedisyan for help and testing with reading
19
 * @thanks Jim Wright for OSX cleanup/fixes.
20
 * @copyright under GPL 2 licence
21
 */
22
23
use RuntimeException;
24
use InvalidArgumentException;
25
26
class PhpSerial
27
{
28
    const OS_UNKNOWN = 0;
29
    const OS_WIN = 1; //WINS32 WINNT Windows
30
    const OS_LINUX = 2;
31
    const OS_CYGWIN = 3; //Cygwin Windows Linux like commands
32
    const OS_UNIX = 4;
33
    const OS_BSD = 5; //FreeBSD or NetBSD or OpenBSD /dev/ttyu1
34
    const OS_OSX = 6; //Darwin MacOS
35
    const OS_HPUX = 7; //tty1p0
36
    const SERIAL_DEVICE_NOTSET = 0;
37
    const SERIAL_DEVICE_SET = 1;
38
    const SERIAL_DEVICE_OPENED = 2;
39
    const PARITY_NONE = 0;
40
    const PARITY_ODD = 1;
41
    const PARITY_EVEN = 2;
42
    const FLOW_NONE = 0; //no flow control
43
    const FLOW_RTSCTS = 1; // use RTS/CTS handshaking
44
    const FLOW_XONXOFF = 2; //use XON/XOFF protocol
45
    
46
    /**
47
     * Pointer for device
48
     * 
49
     * @var resource
50
     */
51
    protected $handle = null;
52
    /**
53
     * Data buffer
54
     * 
55
     * @var string
56
     */
57
    protected $buffer = "";
58
    /**
59
     * This var says if buffer should be flushed by write (true) or
60
     * manually (false)
61
     *
62
     * @var bool
63
     */
64
    protected $autoflush = false;
65
    /**
66
     * Wait time after send data to serial
67
     * 
68
     * @var float
69
     */
70
    protected $waittime = 0.1;
71
    /**
72
     * OS type where php is running
73
     * linux is default
74
     * 
75
     * @var int
76
     */
77
    protected $ostype = 2; 
78
    /**
79
     * Mode command to set up serial port
80
     * formated device mode for especific OS use
81
     * 
82
     * @var string
83
     */
84
    protected $mode = '';
85
    /**
86
     * Status of port
87
     * NoSet, Set or Open
88
     * 
89
     * @var int
90
     */
91
    protected $state = self::SERIAL_DEVICE_NOTSET;
92
    /**
93
     * Port name
94
     * 
95
     * @var string
96
     */
97
    protected $port = '/dev/ttyS0';
98
    /**
99
     * Data bits
100
     * 
101
     * @var int
102
     */
103
    protected $databits = 8;
104
    /**
105
     * Baud Rate
106
     * 
107
     * @var int 
108
     */
109
    protected $baudrate = 9600;
110
    /**
111
     * Parity
112
     * 
113
     * @var int
114
     */
115
    protected $parity = self::PARITY_NONE;
116
    /**
117
     * Stop Bits
118
     * 
119
     * @var float
120
     */
121
    protected $stopbits = 1;
122
    /**
123
     * Flow Control
124
     * 
125
     * @var int
126
     */
127
    protected $flowcontrol = self::FLOW_NONE;
128
    /**
129
     * Formated device name command
130
     * 
131
     * @var string
132
     */
133
    protected $device = '/dev/ttyS0';
134
    /**
135
     * Formated Data Bits command
136
     * 
137
     * @var string
138
     */
139
    protected $formatedDataBits = 'cs8';
140
    /**
141
     * Formated Baud Rate command
142
     * 
143
     * @var string
144
     */
145
    protected $formatedBaudRate = '9600';
146
    /**
147
     * Formated parity command
148
     * 
149
     * @var string
150
     */
151
    protected $formatedParity = '-parenb';
152
    /**
153
     * Formated stop bits command
154
     * 
155
     * @var string
156
     */
157
    protected $formatedStopBits = '-cstopb';
158
    /**
159
     * Formated flow control command
160
     * 
161
     * @var string
162
     */
163
    protected $formatedFlowControl = 'clocal -crtscts -ixon -ixoff';
164
    
165
    /**
166
     * Parity data
167
     * 
168
     * @var array
169
     */
170
    private $parityargs = [
171
        "none" => [0, "-parenb"],
172
        "odd"  => [1, "parenb parodd"],
173
        "even" => [2, "parenb -parodd"]
174
    ];
175
    
176
    /**
177
     * Basud Rate data
178
     * 
179
     * @var array
180
     */
181
    private $baudsargs = array (
182
        110    => 11,
183
        150    => 15,
184
        300    => 30,
185
        600    => 60,
186
        1200   => 12,
187
        2400   => 24,
188
        4800   => 48,
189
        9600   => 96,
190
        19200  => 19,
191
        38400  => 38400,
192
        57600  => 57600,
193
        115200 => 115200
194
    );
195
196
    /**
197
     * Constructor
198
     * Set ostype parameter
199
     * 
200
     * @param int $forceOS
201
     */
202 14
    public function __construct($forceOS = null)
203
    {
204 14
        if (! is_null($forceOS)) {
205
            if ($this->ostype !== $forceOS && ($forceOS > 0 && $forceOS < 8)) {
206
                $this->ostype = $forceOS;
207
                //clear params
208
                $this->clearParams();
209
            }
210
        } else {
211 14
            $this->ostype = $this->getOs();
212
        }
213 14
    }
214
    
215
    /**
216
     * Clear class params
217
     * Used for testing proporses
218
     */
219
    protected function clearParams()
220
    {
221
        $this->mode = null;
222
        $this->state = null;
223
        $this->port = null;
224
        $this->databits = null;
225
        $this->baudrate = null;
226
        $this->parity = null;
227
        $this->stopbits = null;
228
        $this->flowcontrol = null;
229
        $this->device = null;
230
        $this->formatedDataBits = null;
231
        $this->formatedBaudRate = null;
232
        $this->formatedParity = null;
233
        $this->formatedStopBits = null;
234
        $this->formatedFlowControl = null;
235
    }
236
237
    /**
238
     * Close port
239
     */
240 14
    public function __destruct()
241
    {
242 14
        $this->close();
243 14
    }
244
    
245
    /**
246
     * Open set port
247
     * 
248
     * @return boolean
249
     */
250 2
    public function open()
251
    {
252 2
        if ($this->state === self::SERIAL_DEVICE_OPENED && is_resource($this->handle)) {
253
            return true;
254
        }
255
        //before the serial port is opened it must be configured,
256
        //and in windows environment, all sets at a single time
257 2
        $this->setUp();
0 ignored issues
show
Unused Code introduced by
The call to the method Posprint\Extras\PhpSerial::setUp() seems un-needed as the method has no side-effects.

PHP Analyzer performs a side-effects analysis of your code. A side-effect is basically anything that might be visible after the scope of the method is left.

Let’s take a look at an example:

class User
{
    private $email;

    public function getEmail()
    {
        return $this->email;
    }

    public function setEmail($email)
    {
        $this->email = $email;
    }
}

If we look at the getEmail() method, we can see that it has no side-effect. Whether you call this method or not, no future calls to other methods are affected by this. As such code as the following is useless:

$user = new User();
$user->getEmail(); // This line could safely be removed as it has no effect.

On the hand, if we look at the setEmail(), this method _has_ side-effects. In the following case, we could not remove the method call:

$user = new User();
$user->setEmail('email@domain'); // This line has a side-effect (it changes an
                                 // instance variable).
Loading history...
258 2
        $this->handle = @fopen($this->device, 'r+b');
259 2
        if ($this->handle === false) {
260 1
            $this->handle = null;
261 1
            $this->state = self::SERIAL_DEVICE_NOTSET;
262 1
            throw new RuntimeException('Fail to open device. Check permissions.');
263
        }
264 1
        stream_set_blocking($this->handle, false);
265 1
        $this->state = self::SERIAL_DEVICE_OPENED;
266 1
        return true;
267
    }
268
    
269
    /**
270
     * Close serial port
271
     * 
272
     * @return boolean
273
     */
274 14
    public function close()
275
    {
276 14
        if ($this->state !== self::SERIAL_DEVICE_OPENED || ! is_resource($this->handle)) {
277 13
            return true;
278
        }
279 1
        if (fclose($this->handle)) {
280 1
            $this->handle = null;
281 1
            $this->state = self::SERIAL_DEVICE_SET;
282 1
            return true;
283
        }
284
        return false;
285
    }
286
    
287
    /**
288
     * Use class parameters to configure the serial port
289
     */
290 2
    public function setUp()
291
    {
292 2
        if ($this->state === self::SERIAL_DEVICE_SET) {
293
            return true;
294
        }
295 2
        if ($this->ostype == 0) {
296
            return false;
297
        }
298
        $modesos = [
299 2
            1 => 'MODE', //windows mode com4: BAUD=9600 PARITY=n DATA=8 STOP=1 to=off dtr=off rts=off
300 2
            2 => "stty -F", //linux
301 2
            3 => "stty -F", //cygwin
302 2
            4 => 'stty -F', //unix
303 2
            5 => 'stty -F', //BSD
304 2
            6 => "stty -f", //MacOS
305
            7 => 'stty -F' //HPUX
306 2
        ];
307
308 2
        $mode = $modesos[$this->ostype]
309
                . " "
310 2
                . "$this->device "
311 2
                . "$this->formatedBaudRate "
312 2
                . "$this->formatedParity "
313 2
                . "$this->formatedDataBits "
314 2
                . "$this->formatedStopBits "
315 2
                . "$this->formatedFlowControl";
316
        
317 2
        return $mode;
318
    }
319
    
320
    /**
321
     * Set automatic send massage to serial
322
     * 
323
     * @param bool $auto
324
     * @param float $waittime
325
     */
326
    public function setAuto($auto, $waittime)
327
    {
328
        if (! is_bool($auto)) {
329
            $data = false;
0 ignored issues
show
Unused Code introduced by
$data is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
330
        }
331
        if (! is_float($waittime)) {
332
            $waittime = 0.1;
333
        }
334
        $this->waittime = $waittime;
335
        $this->autoflush = $auto;
336
    }
337
    
338
    /**
339
     * Returns automatic mode
340
     * 
341
     * @return bool
342
     */
343
    public function getAuto()
344
    {
345
        return $this->autoflush;
346
    }
347
348
    /**
349
     * Read serial port
350
     * 
351
     * @return string
352
     */
353
    public function read()
354
    {
355
        return '';
356
    }
357
    
358
    /**
359
     * Write data to buffer or serial port
360
     * depends of getAuto()
361
     * if  getAuto() == true this command writes directly to port
362
     * if  getAuto() == false this command writes to buffer (default)
363
     * 
364
     * @param string $data
365
     * @return boolean
366
     */
367
    public function write($data)
368
    {
369
        $this->buffer .= $data;
370
        if ($this->autoflush === true) {
371
            $this->flush();
372
            usleep((int) ($this->waittime * 1000000));
373
        }
374
        return true;
375
    }
376
    
377
    /**
378
     * Flushs imediatly data to serial port
379
     * 
380
     * @return boolean
381
     */
382
    public function flush()
383
    {
384
        if (fwrite($this->handle, $this->buffer) !== false) {
385
            $this->buffer = "";
386
            return true;
387
        }
388
        return false;
389
    }
390
391
    /**
392
     * Set port name
393
     * 
394
     * @param string $port
395
     */
396 1
    public function setPort($port)
397
    {
398
        //identify input if $port like COM?? even in others OS
399 1
        $flagWinMode = preg_match("@^COM(\d+):?$@i", $port, $matches);
400
        //select port from OS type
401 1
        switch ($this->ostype) {
402 1
            case self::OS_WIN:
403
                $this->device = ($flagWinMode) ? "COM$matches[1]:" : $port;
404
                break;
405 1
            case self::OS_LINUX:
406 1
            case self::OS_CYGWIN:
407 1
                $this->device = ($flagWinMode) ? "/dev/ttyS".($matches[1]-1) : $port;
408 1
                break;
409
            case self::OS_UNIX:
410
            case self::OS_BSD:
411
            case self::OS_OSX:
412
            case self::OS_HPUX:
413
            default:
414
                $this->device = $port;
415 1
        }
416 1
        $this->port = $port;
417 1
    }
418
    
419
    /**
420
     * Returns port name
421
     *  
422
     * @return string
423
     */
424 1
    public function getPort()
425
    {
426 1
        return $this->port;
427
    }
428
    
429
    /**
430
     * Returns device formated name
431
     * 
432
     * @return string
433
     */
434 1
    public function getDevice()
435
    {
436 1
        return $this->device;
437
    }
438
    
439
    /**
440
     * Sets the length of a character.
441
     * length of a character (5 <= length <= 8)
442
     * 
443
     * @param int $length
444
     * @return boolean
445
     */
446 1
    public function setDataBits($length)
447
    {
448 1
        if ($length < 5 || $length > 8) {
449
            $length = 8;
450
        }
451 1
        $this->databits = $length;
452 1
        $this->formatedDataBits = $this->zDataBits($length);
453 1
        return true;
454
    }
455
    
456
    /**
457
     * Returns char length
458
     * 
459
     * @return int
460
     */
461 2
    public function getDataBits()
462
    {
463 2
        return $this->databits;
464
    }
465
    
466
    /**
467
     * Format data bits commands
468
     * 
469
     * @param int $length
470
     * @return string
471
     */
472 1
    protected function zDataBits($length)
473
    {
474 1
        $fdatabits = "cs$length";
475 1
        if ($this->ostype == self::OS_WIN) {
476
            //windows
477
            $fdatabits = "DATA=$length";
478
        }
479 1
        return $fdatabits;
480
    }
481
482
    /**
483
     * Set serial baud rate
484
     * 
485
     * @param int $rate
486
     * @return boolean
487
     */
488 1
    public function setBaudRate($rate)
489
    {
490 1
        if (! isset($this->baudsargs[$rate])) {
491
            $rate = 9600;
492
        }
493 1
        $this->baudrate = $rate;
494 1
        $this->formatedBaudRate = $this->zBaudRate($rate);
495 1
        return true;
496
    }
497
    
498
    /**
499
     * Return baud rate
500
     * 
501
     * @return int
502
     */
503 2
    public function getBaudRate()
504
    {
505 2
        return $this->baudrate;
506
    }
507
    
508
    /**
509
     * Format baud rate command
510
     * 
511
     * @param int $rate
512
     * @return string
513
     */
514 1
    protected function zBaudRate($rate)
515
    {
516 1
        $baud = "$rate";
517 1
        if ($this->ostype == self::OS_WIN) {
518
            //windows
519
            $baud = "BAUD=".$this->baudsargs[$rate];
520
        }
521 1
        return $baud;
522
    }
523
524
525
    /**
526
     * Sets parity mode
527
     * 
528
     * @param string $parity odd, even, none
529
     * @return boolean
530
     */
531 1
    public function setParity($parity)
532
    {
533 1
        if (! isset($this->parityargs[$parity])) {
534
            $parity = 'none';
535
        }
536 1
        $this->parity = $this->parityargs[$parity][0];
537 1
        $this->formatedParity = $this->zParity($parity);
538 1
        return true;
539
    }
540
    
541
    /**
542
     * Get parity mode set
543
     * 
544
     * @return string
545
     */
546 2
    public function getParity()
547
    {
548 2
        switch ($this->parity) {
549 2
            case 0:
550 1
                return 'none';
551 1
            case 1:
552 1
                return 'odd';
553
            case 2:
554
                return 'even';
555
        }
556
    }
557
    
558
    /**
559
     * Format parity command
560
     * 
561
     * @param string $parity
562
     * @return string
563
     */
564 1
    protected function zParity($parity)
565
    {
566 1
        $fparity = $this->parityargs[$parity][1];
567 1
        if ($this->ostype == self::OS_WIN) {
568
            //windows
569
            $fparity = "PARITY=" .  strtoupper(substr($parity, 0, 1));
570
        }
571 1
        return $fparity;
572
    }
573
574
575
    /**
576
     * Set length of stop bits
577
     * the length of a stop bit.
578
     * It must be either 1, 1.5 or 2.
579
     * 1.5 is not supported under linux and on some computers.
580
     * 
581
     * @param float $length
582
     * @return boolean
583
     */
584 1
    public function setStopBits($length)
585
    {
586 1
        if ($length !== 1 && $length !== 1.5 && $length !== 2) {
587
            $length = 1;
588
        }
589 1
        $this->stopbits = $length;
0 ignored issues
show
Documentation Bug introduced by
It seems like $length can also be of type integer. However, the property $stopbits is declared as type double. Maybe add an additional type check?

Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.

For example, imagine you have a variable $accountId that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to the id property of an instance of the Account class. This class holds a proper account, so the id value must no longer be false.

Either this assignment is in error or a type check should be added for that assignment.

class Id
{
    public $id;

    public function __construct($id)
    {
        $this->id = $id;
    }

}

class Account
{
    /** @var  Id $id */
    public $id;
}

$account_id = false;

if (starsAreRight()) {
    $account_id = new Id(42);
}

$account = new Account();
if ($account instanceof Id)
{
    $account->id = $account_id;
}
Loading history...
590 1
        $this->formatedStopBits = $this->zStopBits($length);
591 1
        return true;
592
    }
593
    
594
    /**
595
     * Return stop bits set
596
     * 
597
     * @return float
598
     */
599 2
    public function getStopBits()
600
    {
601 2
        return $this->stopbits;
602
    }
603
    
604
    /**
605
     * Format stop bit command
606
     * 
607
     * @param float $length
608
     * @return string
609
     */
610 1
    public function zStopBits($length)
611
    {
612 1
        $stopb = (($length == 1) ? "-" : "") . "cstopb";
613 1
        if ($this->ostype === self::OS_WIN) {
614
            $stopb = "STOP=" . $length;
615
        }
616 1
        return $stopb;
617
    }
618
    
619
    /**
620
     * Set the flow control mode.
621
     * Availible modes :
622
     *   "none" : no flow control
623
     *   "rts/cts" : use RTS/CTS handshaking
624
     *   "xon/xoff" : use XON/XOFF protocol
625
     * 
626
     * @param string $flow
627
     * @return boolean
628
     */
629 1
    public function setFlowControl($flow)
630
    {
631
        switch ($flow) {
632 1
            case 'rts/cts':
633
                $this->flowcontrol = self::FLOW_RTSCTS;
634
                break;
635 1
            case 'xon/xoff':
636 1
                $this->flowcontrol = self::FLOW_XONXOFF;
637 1
                break;
638
            default:
639
                $this->flowcontrol = self::FLOW_NONE;
640
        }
641 1
        $this->formatedFlowControl = $this->zFlowControl($this->flowcontrol);
642 1
        return true;
643
    }
644
    
645
    /**
646
     * Returns flow control set
647
     * 
648
     * @return string
649
     */
650 2
    public function getFlowControl()
651
    {
652 2
        switch ($this->flowcontrol) {
653 2
            case 0:
654 1
                return 'none';
655 1
            case 1:
656
                return 'rts/cts';
657 1
            case 2:
658 1
                return 'xon/xoff';
659
        }
660
    }
661
    
662
    /**
663
     * Return flow control command formated for OP type
664
     * 
665
     * @param int $flow
666
     * @return string
667
     */
668 1
    protected function zFlowControl($flow)
669
    {
670
        $modeos = [
671
            //windows
672 1
            self::OS_WIN => ["xon=off octs=off rts=on","xon=off octs=on rts=hs","xon=on octs=off rts=on"],
673
            //linux
674 1
            self::OS_LINUX => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"],
675
            //cygwin
676 1
            self::OS_CYGWIN => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"],
677
            //unix
678 1
            self::OS_UNIX => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"],
679
            //bsd
680 1
            self::OS_BSD => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"],
681
            //macos
682 1
            self::OS_OSX => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"],
683
            //hpux
684 1
            self::OS_HPUX => ["clocal -crtscts -ixon -ixoff","-clocal crtscts -ixon -ixoff","-clocal -crtscts ixon ixoff"]
685 1
        ];
686 1
        return (string) $modeos[$this->ostype][$flow];
687
    }
688
    
689
    /**
690
     * Find OS type
691
     * 
692
     * @return int
693
     */
694 14
    protected function getOs()
695
    {
696 14
        $oss = strtoupper(substr(PHP_OS, 0, 3));
697
        switch ($oss) {
698 14
            case 'DAR':
699
                return self::OS_OSX;
700 14
            case 'WIN':
701
                return self::OS_WIN;
702 14
            case 'LIN':
703 14
                return self::OS_LINUX;
704
            case 'CYG':
705
                return self::OS_CYGWIN;
706
            case 'HPU':
707
                return self::OS_HPUX;
708
            case 'BSD':
709
                return self::OS_BSD; //este esta incorreto
710
            case 'UNI':
711
                return self::OS_UNIX;
712
            default:
713
                return self::OS_UNKNOWN;
714
        }
715
    }
716
    
717
    /**
718
     * Exec command line in OS console
719
     * 
720
     * @param type $cmd
721
     * @param type $out
722
     * @return type
723
     */
724
    public function execCommand($cmd, &$out = null)
725
    {
726
        $desc = array(
727
            1 => array("pipe", "w"),
728
            2 => array("pipe", "w")
729
        );
730
        $proc = proc_open($cmd, $desc, $pipes);
731
        $ret = stream_get_contents($pipes[1]);
732
        $err = stream_get_contents($pipes[2]);
733
        fclose($pipes[1]);
734
        fclose($pipes[2]);
735
        $retVal = proc_close($proc);
736
        if (func_num_args() == 2) {
737
            $out = array($ret, $err);
738
        }
739
        return $retVal;
740
    }
741
}
742