NotifyHandler::listen()   A
last analyzed

Complexity

Conditions 4
Paths 2

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 14
ccs 0
cts 9
cp 0
rs 9.7998
c 0
b 0
f 0
cc 4
nc 2
nop 1
crap 20
1
<?php
2
/**
3
 * @copyright Copyright (c) 2016 Robin Appelman <[email protected]>
4
 * This file is licensed under the Licensed under the MIT license:
5
 * http://opensource.org/licenses/MIT
6
 *
7
 */
8
9
namespace Icewind\SMB\Wrapped;
10
11
use Icewind\SMB\Change;
12
use Icewind\SMB\Exception\Exception;
13
use Icewind\SMB\Exception\RevisionMismatchException;
14
use Icewind\SMB\INotifyHandler;
15
16
class NotifyHandler implements INotifyHandler {
17
	/** @var Connection */
18
	private $connection;
19
20
	/** @var string */
21
	private $path;
22
23
	/** @var bool */
24
	private $listening = true;
25
26
	// see error.h
27
	const EXCEPTION_MAP = [
28
		ErrorCodes::RevisionMismatch => RevisionMismatchException::class,
29
	];
30
31
	/**
32
	 * @param Connection $connection
33
	 * @param string $path
34
	 */
35
	public function __construct(Connection $connection, string $path) {
36
		$this->connection = $connection;
37
		$this->path = $path;
38 2
	}
39 2
40 2
	/**
41 2
	 * Get all changes detected since the start of the notify process or the last call to getChanges
42
	 *
43
	 * @return Change[]
44
	 */
45
	public function getChanges(): array {
46
		if (!$this->listening) {
47
			return [];
48 2
		}
49 2
		stream_set_blocking($this->connection->getOutputStream(), false);
50 2
		$lines = [];
51
		while (($line = $this->connection->readLine())) {
52
			$this->checkForError($line);
53
			$lines[] = $line;
54
		}
55
		stream_set_blocking($this->connection->getOutputStream(), true);
56
		return array_values(array_filter(array_map([$this, 'parseChangeLine'], $lines)));
57
	}
58
59
	/**
60
	 * Listen actively to all incoming changes
61
	 *
62
	 * Note that this is a blocking process and will cause the process to block forever if not explicitly terminated
63
	 *
64
	 * @param callable(Change):?bool $callback
0 ignored issues
show
Documentation introduced by
The doc-type callable(Change):?bool could not be parsed: Expected "|" or "end of type", but got "(" at position 8. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
65
	 */
66
	public function listen(callable $callback): void {
67
		if ($this->listening) {
68
			$this->connection->read(function (string $line) use ($callback): bool {
69
				$this->checkForError($line);
70
				$change = $this->parseChangeLine($line);
71
				if ($change) {
72
					$result = $callback($change);
73
					return $result === false ? false : true;
74
				} else {
75
					return true;
76
				}
77
			});
78
		}
79
	}
80
81
	private function parseChangeLine(string $line): ?Change {
82
		$code = (int)substr($line, 0, 4);
83
		if ($code === 0) {
84
			return null;
85
		}
86
		$subPath = str_replace('\\', '/', substr($line, 5));
87
		if ($this->path === '') {
88
			return new Change($code, $subPath);
89
		} else {
90
			return new Change($code, $this->path . '/' . $subPath);
91
		}
92
	}
93
94
	private function checkForError(string $line): void {
95
		if (substr($line, 0, 16) === 'notify returned ') {
96
			$error = substr($line, 16);
97
			throw Exception::fromMap(array_merge(self::EXCEPTION_MAP, Parser::EXCEPTION_MAP), $error, 'Notify is not supported with the used smb version');
98
		}
99
	}
100
101 2
	public function stop(): void {
102 2
		$this->listening = false;
103 2
		$this->connection->close();
104 2
	}
105
106 2
	public function __destruct() {
107 2
		$this->stop();
108 2
	}
109
}
110