Passed
Push — master ( aa10e3...d44ff3 )
by Daimona
02:11
created

CLI::checkNotBothSet()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 3
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 2
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 3
rs 10
1
<?php declare( strict_types=1 );
2
3
namespace BotRiconferme;
4
5
/**
6
 * CLI helper
7
 *
8
 * Example options:
9
 *
10
 * 'username' => 'BotRiconferme'
11
 * 'list-title' => 'Utente:BotRiconferme/List.json',
12
 * 'config-title' => 'Utente:BotRiconferme/Config.json',
13
 * 'msg-title' => 'Utente:BotRiconferme/Messages.json"
14
 *
15
 * --password=(BotPassword)
16
 * OR
17
 * --use-password-file
18
 * which will look for a PASSWORD_FILE file in the current directory containing only the plain password
19
 *
20
 * --private-password=(BotPassword)
21
 * OR
22
 * --use-private-password-file
23
 * which will look for a PRIVATE_PASSWORD_FILE file in the current directory containing only the plain password
24
 *
25
 * --task=update-list
26
 * OR
27
 * --subtask=user-notice
28
 */
29
class CLI {
30
	public const SHORT_OPTS = '';
31
32
	public const LONG_OPTS = [
33
		'username:',
34
		'list-title:',
35
		'config-title:',
36
		'msg-title:',
37
38
		'force-url:',
39
40
		'password:',
41
		'use-password-file',
42
		'private-password:',
43
		'use-private-password-file',
44
45
		'error-title:',
46
47
		'task:',
48
		'subtask:'
49
	];
50
51
	public const REQUIRED_OPTS = [
52
		'username',
53
		'list-title',
54
		'config-title',
55
		'msg-title',
56
	];
57
58
	/** @todo Make it customizable? */
59
	public const PASSWORD_FILE = __DIR__ . '/../password.txt';
60
	public const PRIVATE_PASSWORD_FILE = __DIR__ . '/../private-password.txt';
61
62
	/** @var array */
63
	private $opts;
64
65
	/**
66
	 * @return bool
67
	 */
68
	public static function isCLI() : bool {
69
		return PHP_SAPI === 'cli';
70
	}
71
72
	/**
73
	 * Populate options and check for required ones
74
	 */
75
	public function __construct() {
76
		$opts = getopt( self::SHORT_OPTS, self::LONG_OPTS );
77
		$this->checkRequiredOpts( $opts );
78
		$this->checkConflictingOpts( $opts );
79
		$this->canonicalize( $opts );
80
		$this->opts = $opts;
81
	}
82
83
	/**
84
	 * @param array $opts
85
	 */
86
	private function checkRequiredOpts( array $opts ) : void {
87
		$missingOpts = array_diff( self::REQUIRED_OPTS, array_keys( $opts ) );
88
		if ( $missingOpts ) {
89
			exit( 'Required options missing: ' . implode( ', ', $missingOpts ) );
90
		}
91
92
		$hasPw = array_key_exists( 'password', $opts );
93
		$hasPwFile = array_key_exists( 'use-password-file', $opts );
94
		if ( !$hasPw && !$hasPwFile ) {
95
			exit( 'Please provide a password or use a password file' );
96
		}
97
98
		$hasPrivatePw = array_key_exists( 'private-password', $opts );
99
		$hasPrivatePwFile = array_key_exists( 'use-private-password-file', $opts );
100
		if ( !$hasPrivatePw && !$hasPrivatePwFile ) {
101
			exit( 'Please provide a private password or use a private-password file' );
102
		}
103
	}
104
105
	/**
106
	 * @param array $opts
107
	 */
108
	private function checkConflictingOpts( array $opts ) : void {
109
		$this->checkNotBothSet( $opts,'password', 'use-password-file' );
110
		if ( array_key_exists( 'use-password-file', $opts ) && !file_exists( self::PASSWORD_FILE ) ) {
111
			exit( 'Please create the password file (' . self::PASSWORD_FILE . ')' );
112
		}
113
114
		$this->checkNotBothSet( $opts,'private-password', 'use-private-password-file' );
115
		if ( array_key_exists( 'use-private-password-file', $opts ) && !file_exists( self::PRIVATE_PASSWORD_FILE ) ) {
116
			exit( 'Please create the private-password file (' . self::PRIVATE_PASSWORD_FILE . ')' );
117
		}
118
119
		if ( count( array_intersect_key( $opts, [ 'task' => 1, 'subtask' => 1 ] ) ) === 2 ) {
120
			exit( 'Cannot specify both task and subtask.' );
121
		}
122
	}
123
124
	/**
125
	 * @param array $opts
126
	 * @param string $first
127
	 * @param string $second
128
	 */
129
	private function checkNotBothSet( array $opts, string $first, string $second ) : void {
130
		if ( array_key_exists( $first, $opts ) && array_key_exists( $second, $opts ) ) {
131
			exit( "Can only use one of '$first' and '$second'" );
132
		}
133
	}
134
135
	/**
136
	 * @param array &$opts
137
	 */
138
	private function canonicalize( array &$opts ) : void {
139
		if ( array_key_exists( 'use-password-file', $opts ) ) {
140
			$pw = trim( file_get_contents( self::PASSWORD_FILE ) );
141
			$opts['password'] = $pw;
142
			unset( $opts['use-password-file'] );
143
		}
144
		if ( array_key_exists( 'use-private-password-file', $opts ) ) {
145
			$pw = trim( file_get_contents( self::PRIVATE_PASSWORD_FILE ) );
146
			$opts['private-password'] = $pw;
147
			unset( $opts['use-private-password-file'] );
148
		}
149
	}
150
151
	/**
152
	 * @param string $opt
153
	 * @param mixed|null $default
154
	 * @return mixed
155
	 */
156
	public function getOpt( string $opt, $default = null ) {
157
		return $this->opts[$opt] ?? $default;
158
	}
159
160
	/**
161
	 * @return array Either [ 'task' => taskname ] or [ 'subtask' => subtaskname ]
162
	 */
163
	public function getTaskOpt() : array {
164
		return array_intersect_key(
165
			$this->opts,
166
			[ 'task' => true, 'subtask' => true ]
167
		);
168
	}
169
170
	/**
171
	 * @return string|null
172
	 */
173
	public function getURL() : ?string {
174
		return $this->getOpt( 'force-url' );
175
	}
176
}
177