Completed
Pull Request — master (#39)
by Sam
02:37
created

MediawikiSession::reallyGetToken()   B

Complexity

Conditions 4
Paths 3

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 4.026

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 25
ccs 15
cts 17
cp 0.8824
rs 8.5806
cc 4
eloc 18
nc 3
nop 1
crap 4.026
1
<?php
2
3
namespace Mediawiki\Api;
4
5
use Exception;
6
use Psr\Log\LoggerAwareInterface;
7
use Psr\Log\LoggerInterface;
8
use Psr\Log\LogLevel;
9
use Psr\Log\NullLogger;
10
11
/**
12
 * @since 0.1
13
 *
14
 * @author Addshore
15
 */
16
class MediawikiSession implements LoggerAwareInterface {
17
18
	/**
19
	 * @var array
20
	 */
21
	private $tokens = [];
22
23
	/**
24
	 * @var MediawikiApi
25
	 */
26
	private $api;
27
28
	/**
29
	 * @var bool if this session is running against mediawiki version pre 1.25
30
	 */
31
	private $usePre125TokensModule = false;
32
33
	/**
34
	 * @var LoggerInterface
35
	 */
36
	private $logger;
37
38
	/**
39
	 * @param MediawikiApi $api The API object to use for this session.
40
	 */
41 6
	public function __construct( MediawikiApi $api ) {
42 6
		$this->api = $api;
43 6
		$this->logger = new NullLogger();
44 6
	}
45
46
	/**
47
	 * Sets a logger instance on the object
48
	 *
49
	 * @since 1.1
50
	 *
51
	 * @param LoggerInterface $logger The new Logger object.
52
	 *
53
	 * @return null
54
	 */
55
	public function setLogger( LoggerInterface $logger ) {
56
		$this->logger = $logger;
57
	}
58
59
	/**
60
	 * Tries to get the specified token from the API
61
	 *
62
	 * @since 0.1
63
	 *
64
	 * @param string $type The type of token to get.
65
	 *
66
	 * @return string
67
	 */
68 7
	public function getToken( $type = 'csrf' ) {
69
		// If we don't already have the token that we want
70 7
		if ( !array_key_exists( $type, $this->tokens ) ) {
71 7
			$this->logger->log( LogLevel::DEBUG, 'Getting fresh token', [ 'type' => $type ] );
72
73
			// If we know that we don't have the new module mw<1.25
74 7
			if ( $this->usePre125TokensModule ) {
75
				return $this->reallyGetPre125Token( $type );
76
			} else {
77 7
				return $this->reallyGetToken( $type );
78
			}
79
80
		}
81
82 4
		return $this->tokens[$type];
83
	}
84
85 2
	private function reallyGetPre125Token( $type ) {
86
		// Suppress deprecation warning
87 2
		$result = @$this->api->postRequest( // @codingStandardsIgnoreLine
88 2
			new SimpleRequest( 'tokens', [ 'type' => $this->getOldTokenType( $type ) ] )
89
		);
90 2
		$this->tokens[$type] = array_pop( $result['tokens'] );
91
92 2
		return $this->tokens[$type];
93
	}
94
95 5
	private function reallyGetToken( $type ) {
96
		// We suppress errors on this call so the user doesn't get get a warning that isn't their fault.
97 5
		$result = @$this->api->postRequest( // @codingStandardsIgnoreLine
98 5
			new SimpleRequest( 'query', [
99 5
				'meta' => 'tokens',
100 5
				'type' => $this->getNewTokenType( $type ),
101 5
				'continue' => '',
102
			] )
103
		);
104
		// If mw<1.25 (no new module)
105 5
		$metaWarning = "Unrecognized value for parameter 'meta': tokens";
106 5
		if ( isset( $result['warnings']['query']['*'] )
107 5
			&& false !== strpos( $result['warnings']['query']['*'], $metaWarning ) ) {
108 2
			$this->usePre125TokensModule = true;
109 2
			$this->logger->log( LogLevel::DEBUG, 'Falling back to pre 1.25 token system' );
110 2
			$this->tokens[$type] = $this->reallyGetPre125Token( $type );
111 3
		} elseif ( !isset( $result['query']['tokens'] ) ) {
112
			$err = "Unable to retrieve token " . print_r( $result, true );
113
			throw new Exception( $err );
114
		} else {
115 3
			$this->tokens[$type] = array_pop( $result['query']['tokens'] );
116
		}
117
118 5
		return $this->tokens[$type];
119
	}
120
121
	/**
122
	 * Tries to guess a new token type from an old token type
123
	 *
124
	 * @param string $type
125
	 *
126
	 * @return string
127
	 */
128 5
	private function getNewTokenType( $type ) {
129
		switch ( $type ) {
130 5
			case 'edit':
131 5
			case 'delete':
132 5
			case 'protect':
133 5
			case 'move':
134 5
			case 'block':
135 5
			case 'unblock':
136 5
			case 'email':
137 5
			case 'import':
138 5
			case 'options':
139
				return 'csrf';
140
		}
141
		// Return the same type, don't know what to do with this..
142 5
		return $type;
143
	}
144
145
	/**
146
	 * Tries to guess an old token type from a new token type
147
	 *
148
	 * @param $type
149
	 *
150
	 * @return string
151
	 */
152 2
	private function getOldTokenType( $type ) {
153
		switch ( $type ) {
154
			// Guess that we want an edit token, this may not always work as we might be trying to
155
			// use it for something else...
156 2
			case 'csrf':
157 2
				return 'edit';
158
		}
159
		return $type;
160
	}
161
162
	/**
163
	 * Clears all tokens stored by the api
164
	 *
165
	 * @since 0.2
166
	 */
167 2
	public function clearTokens() {
168 2
		$this->logger->log( LogLevel::DEBUG, 'Clearing session tokens', [ 'tokens' => $this->tokens ] );
169 2
		$this->tokens = [];
170 2
	}
171
172
}
173