Completed
Push — master ( 89ec26...6a878a )
by adam
04:53 queued 02:13
created

MiddlewareFactory::newRetryDecider()   C

Complexity

Conditions 12
Paths 1

Size

Total Lines 72
Code Lines 39

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 44
CRAP Score 12.0015

Importance

Changes 2
Bugs 0 Features 0
Metric Value
c 2
b 0
f 0
dl 0
loc 72
ccs 44
cts 45
cp 0.9778
rs 5.519
cc 12
eloc 39
nc 1
nop 0
crap 12.0015

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace Mediawiki\Api\Guzzle;
4
5
use GuzzleHttp\Exception\ConnectException;
6
use GuzzleHttp\Exception\RequestException;
7
use GuzzleHttp\Middleware;
8
use GuzzleHttp\Psr7\Request;
9
use GuzzleHttp\Psr7\Response;
10
use Psr\Log\LoggerAwareInterface;
11
use Psr\Log\LoggerInterface;
12
use Psr\Log\NullLogger;
13
14
/**
15
 * @access private
16
 *
17
 * @author Addshore
18
 */
19
class MiddlewareFactory implements LoggerAwareInterface {
20
21
	/**
22
	 * @var LoggerInterface
23
	 */
24
	private $logger;
25
26 6
	public function __construct() {
27 6
		$this->logger = new NullLogger();
28 6
	}
29
30
	public function setLogger( LoggerInterface $logger ) {
31
		$this->logger = $logger;
32
	}
33
34
	/**
35
	 * @access private
36
	 *
37
	 * @param bool $delay default to true, can be false to speed up tests
38
	 *
39
	 * @return callable
40
	 */
41 6
	public function retry( $delay = true ) {
42 6
		if( $delay ) {
43 1
			return Middleware::retry( $this->newRetryDecider(), $this->getRetryDelay() );
44
		} else {
45 5
			return Middleware::retry( $this->newRetryDecider() );
46
		}
47
	}
48
49
	/**
50
	 * Returns a method that takes the number of retries and returns the number of miliseconds
51
	 * to wait
52
	 *
53
	 * @return callable
54
	 */
55 1
	private function getRetryDelay() {
56
		return function( $numberOfRetries ) {
57 1
			return 1000 * $numberOfRetries;
58 1
		};
59
	}
60
61
	/**
62
	 * @return callable
63
	 */
64
	private function newRetryDecider() {
65 6
		return function (
66
			$retries,
67
			Request $request,
68
			Response $response = null,
69
			RequestException $exception = null
70
		) {
71
			// Don't retry if we have run out of retries
72 6
			if ( $retries >= 5 ) {
73 1
				return false;
74
			}
75
76 6
			$shouldRetry = false;
77
78
			// Retry connection exceptions
79 6
			if( $exception instanceof ConnectException ) {
80 3
				$shouldRetry = true;
81 3
			}
82
83 6
			if( $response ) {
84 5
				$headers = $response->getHeaders();
85 5
				$data = json_decode( $response->getBody(), true );
86
87
				// Retry on server errors
88 5
				if( $response->getStatusCode() >= 500 ) {
89 1
					$shouldRetry = true;
90 1
				}
91
92 5
				if ( array_key_exists( 'mediawiki-api-error', $headers ) ) {
93 2
					foreach( $headers['mediawiki-api-error'] as $mediawikiApiErrorHeader ) {
94
						if (
95
							// Retry if we have a response with an API error worth retrying
96 2
							in_array(
97 2
								$mediawikiApiErrorHeader,
98
								array(
99 2
									'ratelimited',
100 2
									'readonly',
101 2
									'internal_api_error_DBQueryError',
102
								)
103 2
							)
104
							||
105
							// Or if we have been stopped from saving as an 'anti-abuse measure'
106
							// Note: this tries to match "actionthrottledtext" i18n messagae for mediawiki
107
							(
108 2
								$mediawikiApiErrorHeader == 'failed-save' &&
109 1
								strstr( $data['error']['info'], 'anti-abuse measure' )
110 1
							)
111 2
						) {
112 2
							$shouldRetry = true;
113 2
						}
114
115 2
					}
116 2
				}
117 5
			}
118
119
			// Log if we are retrying
120 6
			if( $shouldRetry ) {
121 6
				$this->logger->warning(
122 6
					sprintf(
123 6
						'Retrying %s %s %s/5, %s',
124 6
						$request->getMethod(),
125 6
						$request->getUri(),
126 6
						$retries + 1,
127 6
						$response ? 'status code: ' . $response->getStatusCode() :
128 3
							$exception->getMessage()
0 ignored issues
show
Bug introduced by
It seems like $exception is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
129 6
					)
130 6
				);
131 6
			}
132
133 6
			return $shouldRetry;
134 6
		};
135
	}
136
137
}
138