Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 19 |
|
0.00% |
0 / 5 |
CRAP | |
0.00% |
0 / 1 |
XAnalytics | |
0.00% |
0 / 19 |
|
0.00% |
0 / 5 |
72 | |
0.00% |
0 / 1 |
onBeforePageDisplay | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
generateHeader | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
12 | |||
createHeader | |
0.00% |
0 / 5 |
|
0.00% |
0 / 1 |
2 | |||
onAPIAfterExecute | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
addItem | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | namespace MediaWiki\Extension\XAnalytics; |
4 | |
5 | use MediaWiki\Api\ApiBase; |
6 | use MediaWiki\Api\Hook\APIAfterExecuteHook; |
7 | use MediaWiki\Extension\XAnalytics\Hooks\HookRunner; |
8 | use MediaWiki\MediaWikiServices; |
9 | use MediaWiki\Output\Hook\BeforePageDisplayHook; |
10 | use MediaWiki\Output\OutputPage; |
11 | use MediaWiki\Request\WebResponse; |
12 | use Skin; |
13 | |
14 | class XAnalytics implements |
15 | BeforePageDisplayHook, |
16 | APIAfterExecuteHook |
17 | { |
18 | |
19 | /** |
20 | * Whether the header has already been added |
21 | * |
22 | * @var bool |
23 | */ |
24 | private static $addedHeader = false; |
25 | |
26 | /** |
27 | * Set X-Analytics header before the output buffer is flushed. |
28 | * |
29 | * The PHP output buffer is flushed from multiple places in the MediaWiki |
30 | * codebase (and the codebase of MediaWiki extensions), making it difficult to |
31 | * ensure that the header is reliably injected into every response generated by |
32 | * MediaWiki. This should be fixed. The output buffer of normal page view |
33 | * responses is done in one place, however, so for that use-case, the code is |
34 | * reliable. |
35 | * |
36 | * X-Analytics items can be declared by hooking into 'XAnalyticsSetHeader' or |
37 | * by calling XAnalytics;:addItem(). |
38 | * |
39 | * @see https://wikitech.wikimedia.org/wiki/X-Analytics |
40 | * @param OutputPage $out |
41 | * @param Skin $skin |
42 | */ |
43 | public function onBeforePageDisplay( $out, $skin ): void { |
44 | self::generateHeader( $out ); |
45 | } |
46 | |
47 | /** |
48 | * Runs the XAnalyticsSetHeader hook and adds the header if necessary |
49 | * @param OutputPage $out |
50 | */ |
51 | private static function generateHeader( OutputPage $out ) { |
52 | if ( self::$addedHeader === true ) { |
53 | // Only run once for API requests that use OutputPage |
54 | return; |
55 | } |
56 | self::$addedHeader = true; |
57 | $response = $out->getRequest()->response(); |
58 | $headerItems = []; |
59 | ( new HookRunner( MediaWikiServices::getInstance()->getHookContainer() ) ) |
60 | ->onXAnalyticsSetHeader( $out, $headerItems ); |
61 | if ( count( $headerItems ) ) { |
62 | self::createHeader( $response, $headerItems ); |
63 | } |
64 | } |
65 | |
66 | /** |
67 | * Checks to see if the X-Analytics header is already set, and add |
68 | * the new items to the header and set it |
69 | * |
70 | * @param WebResponse $response |
71 | * @param array $newItems |
72 | */ |
73 | private static function createHeader( WebResponse $response, array $newItems ) { |
74 | $currentHeader = $response->getHeader( 'X-Analytics' ) ?? ''; |
75 | parse_str( preg_replace( '/; */', '&', $currentHeader ), $headerItems ); |
76 | $headerItems = array_merge( $headerItems, $newItems ); |
77 | |
78 | $headerValue = http_build_query( $headerItems, '', ';' ); |
79 | $response->header( 'X-Analytics: ' . $headerValue, true ); |
80 | } |
81 | |
82 | /** |
83 | * @param ApiBase $module |
84 | */ |
85 | public function onAPIAfterExecute( $module ) { |
86 | self::generateHeader( $module->getOutput() ); |
87 | } |
88 | |
89 | /** |
90 | * Add an item to the X-Analytics header that will be output |
91 | * @param string $name |
92 | * @param string $value |
93 | */ |
94 | public static function addItem( $name, $value ) { |
95 | if ( self::$addedHeader ) { |
96 | // If the header is already set, we need to append to it and replace it |
97 | global $wgRequest; |
98 | self::createHeader( $wgRequest->response(), [ $name => $value ] ); |
99 | } |
100 | } |
101 | } |