Passed
Branch master (d5a911)
by Daimona
01:45
created

CLI::checkConflictingOpts()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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