WebRequestTest.php 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. <?php
  2. /**
  3. * @group WebRequest
  4. */
  5. class WebRequestTest extends MediaWikiTestCase {
  6. protected $oldServer;
  7. protected function setUp() {
  8. parent::setUp();
  9. $this->oldServer = $_SERVER;
  10. }
  11. protected function tearDown() {
  12. $_SERVER = $this->oldServer;
  13. parent::tearDown();
  14. }
  15. /**
  16. * @dataProvider provideDetectServer
  17. * @covers WebRequest::detectServer
  18. * @covers WebRequest::detectProtocol
  19. */
  20. public function testDetectServer( $expected, $input, $description ) {
  21. $this->setMwGlobals( 'wgAssumeProxiesUseDefaultProtocolPorts', true );
  22. $this->setServerVars( $input );
  23. $result = WebRequest::detectServer();
  24. $this->assertEquals( $expected, $result, $description );
  25. }
  26. public static function provideDetectServer() {
  27. return [
  28. [
  29. 'http://x',
  30. [
  31. 'HTTP_HOST' => 'x'
  32. ],
  33. 'Host header'
  34. ],
  35. [
  36. 'https://x',
  37. [
  38. 'HTTP_HOST' => 'x',
  39. 'HTTPS' => 'on',
  40. ],
  41. 'Host header with secure'
  42. ],
  43. [
  44. 'http://x',
  45. [
  46. 'HTTP_HOST' => 'x',
  47. 'SERVER_PORT' => 80,
  48. ],
  49. 'Default SERVER_PORT',
  50. ],
  51. [
  52. 'http://x',
  53. [
  54. 'HTTP_HOST' => 'x',
  55. 'HTTPS' => 'off',
  56. ],
  57. 'Secure off'
  58. ],
  59. [
  60. 'https://x',
  61. [
  62. 'HTTP_HOST' => 'x',
  63. 'HTTP_X_FORWARDED_PROTO' => 'https',
  64. ],
  65. 'Forwarded HTTPS'
  66. ],
  67. [
  68. 'https://x',
  69. [
  70. 'HTTP_HOST' => 'x',
  71. 'HTTPS' => 'off',
  72. 'SERVER_PORT' => '81',
  73. 'HTTP_X_FORWARDED_PROTO' => 'https',
  74. ],
  75. 'Forwarded HTTPS'
  76. ],
  77. [
  78. 'http://y',
  79. [
  80. 'SERVER_NAME' => 'y',
  81. ],
  82. 'Server name'
  83. ],
  84. [
  85. 'http://x',
  86. [
  87. 'HTTP_HOST' => 'x',
  88. 'SERVER_NAME' => 'y',
  89. ],
  90. 'Host server name precedence'
  91. ],
  92. [
  93. 'http://[::1]:81',
  94. [
  95. 'HTTP_HOST' => '[::1]',
  96. 'SERVER_NAME' => '::1',
  97. 'SERVER_PORT' => '81',
  98. ],
  99. 'Apache bug 26005'
  100. ],
  101. [
  102. 'http://localhost',
  103. [
  104. 'SERVER_NAME' => '[2001'
  105. ],
  106. 'Kind of like lighttpd per commit message in MW r83847',
  107. ],
  108. [
  109. 'http://[2a01:e35:2eb4:1::2]:777',
  110. [
  111. 'SERVER_NAME' => '[2a01:e35:2eb4:1::2]:777'
  112. ],
  113. 'Possible lighttpd environment per bug 14977 comment 13',
  114. ],
  115. ];
  116. }
  117. /**
  118. * @param array $data Request data
  119. * @param array $config
  120. * - float 'requestTime': Mock value for `$_SERVER['REQUEST_TIME_FLOAT']`.
  121. * @return WebRequest
  122. */
  123. protected function mockWebRequest( array $data = [], array $config = [] ) {
  124. // Cannot use PHPUnit getMockBuilder() as it does not support
  125. // overriding protected properties afterwards
  126. $reflection = new ReflectionClass( WebRequest::class );
  127. $req = $reflection->newInstanceWithoutConstructor();
  128. $prop = $reflection->getProperty( 'data' );
  129. $prop->setAccessible( true );
  130. $prop->setValue( $req, $data );
  131. if ( isset( $config['requestTime'] ) ) {
  132. $prop = $reflection->getProperty( 'requestTime' );
  133. $prop->setAccessible( true );
  134. $prop->setValue( $req, $config['requestTime'] );
  135. }
  136. return $req;
  137. }
  138. /**
  139. * @covers WebRequest::getElapsedTime
  140. */
  141. public function testGetElapsedTime() {
  142. $now = microtime( true ) - 10.0;
  143. $req = $this->mockWebRequest( [], [ 'requestTime' => $now ] );
  144. $this->assertGreaterThanOrEqual( 10.0, $req->getElapsedTime() );
  145. // Catch common errors, but don't fail on slow hardware or VMs (T199764).
  146. $this->assertEquals( 10.0, $req->getElapsedTime(), '', 60.0 );
  147. }
  148. /**
  149. * @covers WebRequest::getVal
  150. * @covers WebRequest::getGPCVal
  151. * @covers WebRequest::normalizeUnicode
  152. */
  153. public function testGetValNormal() {
  154. // Assert that WebRequest normalises GPC data using UtfNormal\Validator
  155. $input = "a \x00 null";
  156. $normal = "a \xef\xbf\xbd null";
  157. $req = $this->mockWebRequest( [ 'x' => $input, 'y' => [ $input, $input ] ] );
  158. $this->assertSame( $normal, $req->getVal( 'x' ) );
  159. $this->assertNotSame( $input, $req->getVal( 'x' ) );
  160. $this->assertSame( [ $normal, $normal ], $req->getArray( 'y' ) );
  161. }
  162. /**
  163. * @covers WebRequest::getVal
  164. * @covers WebRequest::getGPCVal
  165. */
  166. public function testGetVal() {
  167. $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a' ], 'crlf' => "A\r\nb" ] );
  168. $this->assertSame( 'Value', $req->getVal( 'x' ), 'Simple value' );
  169. $this->assertSame( null, $req->getVal( 'z' ), 'Not found' );
  170. $this->assertSame( null, $req->getVal( 'y' ), 'Array is ignored' );
  171. $this->assertSame( "A\r\nb", $req->getVal( 'crlf' ), 'CRLF' );
  172. }
  173. /**
  174. * @covers WebRequest::getRawVal
  175. */
  176. public function testGetRawVal() {
  177. $req = $this->mockWebRequest( [
  178. 'x' => 'Value',
  179. 'y' => [ 'a' ],
  180. 'crlf' => "A\r\nb"
  181. ] );
  182. $this->assertSame( 'Value', $req->getRawVal( 'x' ) );
  183. $this->assertSame( null, $req->getRawVal( 'z' ), 'Not found' );
  184. $this->assertSame( null, $req->getRawVal( 'y' ), 'Array is ignored' );
  185. $this->assertSame( "A\r\nb", $req->getRawVal( 'crlf' ), 'CRLF' );
  186. }
  187. /**
  188. * @covers WebRequest::getArray
  189. */
  190. public function testGetArray() {
  191. $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => [ 'a', 'b' ] ] );
  192. $this->assertSame( [ 'Value' ], $req->getArray( 'x' ), 'Value becomes array' );
  193. $this->assertSame( null, $req->getArray( 'z' ), 'Not found' );
  194. $this->assertSame( [ 'a', 'b' ], $req->getArray( 'y' ) );
  195. }
  196. /**
  197. * @covers WebRequest::getIntArray
  198. */
  199. public function testGetIntArray() {
  200. $req = $this->mockWebRequest( [ 'x' => [ 'Value' ], 'y' => [ '0', '4.2', '-2' ] ] );
  201. $this->assertSame( [ 0 ], $req->getIntArray( 'x' ), 'Text becomes 0' );
  202. $this->assertSame( null, $req->getIntArray( 'z' ), 'Not found' );
  203. $this->assertSame( [ 0, 4, -2 ], $req->getIntArray( 'y' ) );
  204. }
  205. /**
  206. * @covers WebRequest::getInt
  207. */
  208. public function testGetInt() {
  209. $req = $this->mockWebRequest( [
  210. 'x' => 'Value',
  211. 'y' => [ 'a' ],
  212. 'zero' => '0',
  213. 'answer' => '4.2',
  214. 'neg' => '-2',
  215. ] );
  216. $this->assertSame( 0, $req->getInt( 'x' ), 'Text' );
  217. $this->assertSame( 0, $req->getInt( 'y' ), 'Array' );
  218. $this->assertSame( 0, $req->getInt( 'z' ), 'Not found' );
  219. $this->assertSame( 0, $req->getInt( 'zero' ) );
  220. $this->assertSame( 4, $req->getInt( 'answer' ) );
  221. $this->assertSame( -2, $req->getInt( 'neg' ) );
  222. }
  223. /**
  224. * @covers WebRequest::getIntOrNull
  225. */
  226. public function testGetIntOrNull() {
  227. $req = $this->mockWebRequest( [
  228. 'x' => 'Value',
  229. 'y' => [ 'a' ],
  230. 'zero' => '0',
  231. 'answer' => '4.2',
  232. 'neg' => '-2',
  233. ] );
  234. $this->assertSame( null, $req->getIntOrNull( 'x' ), 'Text' );
  235. $this->assertSame( null, $req->getIntOrNull( 'y' ), 'Array' );
  236. $this->assertSame( null, $req->getIntOrNull( 'z' ), 'Not found' );
  237. $this->assertSame( 0, $req->getIntOrNull( 'zero' ) );
  238. $this->assertSame( 4, $req->getIntOrNull( 'answer' ) );
  239. $this->assertSame( -2, $req->getIntOrNull( 'neg' ) );
  240. }
  241. /**
  242. * @covers WebRequest::getFloat
  243. */
  244. public function testGetFloat() {
  245. $req = $this->mockWebRequest( [
  246. 'x' => 'Value',
  247. 'y' => [ 'a' ],
  248. 'zero' => '0',
  249. 'answer' => '4.2',
  250. 'neg' => '-2',
  251. ] );
  252. $this->assertSame( 0.0, $req->getFloat( 'x' ), 'Text' );
  253. $this->assertSame( 0.0, $req->getFloat( 'y' ), 'Array' );
  254. $this->assertSame( 0.0, $req->getFloat( 'z' ), 'Not found' );
  255. $this->assertSame( 0.0, $req->getFloat( 'zero' ) );
  256. $this->assertSame( 4.2, $req->getFloat( 'answer' ) );
  257. $this->assertSame( -2.0, $req->getFloat( 'neg' ) );
  258. }
  259. /**
  260. * @covers WebRequest::getBool
  261. */
  262. public function testGetBool() {
  263. $req = $this->mockWebRequest( [
  264. 'x' => 'Value',
  265. 'y' => [ 'a' ],
  266. 'zero' => '0',
  267. 'f' => 'false',
  268. 't' => 'true',
  269. ] );
  270. $this->assertSame( true, $req->getBool( 'x' ), 'Text' );
  271. $this->assertSame( false, $req->getBool( 'y' ), 'Array' );
  272. $this->assertSame( false, $req->getBool( 'z' ), 'Not found' );
  273. $this->assertSame( false, $req->getBool( 'zero' ) );
  274. $this->assertSame( true, $req->getBool( 'f' ) );
  275. $this->assertSame( true, $req->getBool( 't' ) );
  276. }
  277. public static function provideFuzzyBool() {
  278. return [
  279. [ 'Text', true ],
  280. [ '', false, '(empty string)' ],
  281. [ '0', false ],
  282. [ '1', true ],
  283. [ 'false', false ],
  284. [ 'true', true ],
  285. [ 'False', false ],
  286. [ 'True', true ],
  287. [ 'FALSE', false ],
  288. [ 'TRUE', true ],
  289. ];
  290. }
  291. /**
  292. * @dataProvider provideFuzzyBool
  293. * @covers WebRequest::getFuzzyBool
  294. */
  295. public function testGetFuzzyBool( $value, $expected, $message = null ) {
  296. $req = $this->mockWebRequest( [ 'x' => $value ] );
  297. $this->assertSame( $expected, $req->getFuzzyBool( 'x' ), $message ?: "Value: '$value'" );
  298. }
  299. /**
  300. * @covers WebRequest::getFuzzyBool
  301. */
  302. public function testGetFuzzyBoolDefault() {
  303. $req = $this->mockWebRequest();
  304. $this->assertSame( false, $req->getFuzzyBool( 'z' ), 'Not found' );
  305. }
  306. /**
  307. * @covers WebRequest::getCheck
  308. */
  309. public function testGetCheck() {
  310. $req = $this->mockWebRequest( [ 'x' => 'Value', 'zero' => '0' ] );
  311. $this->assertSame( false, $req->getCheck( 'z' ), 'Not found' );
  312. $this->assertSame( true, $req->getCheck( 'x' ), 'Text' );
  313. $this->assertSame( true, $req->getCheck( 'zero' ) );
  314. }
  315. /**
  316. * @covers WebRequest::getText
  317. */
  318. public function testGetText() {
  319. // Avoid FauxRequest (overrides getText)
  320. $req = $this->mockWebRequest( [ 'crlf' => "Va\r\nlue" ] );
  321. $this->assertSame( "Va\nlue", $req->getText( 'crlf' ), 'CR stripped' );
  322. }
  323. /**
  324. * @covers WebRequest::getValues
  325. */
  326. public function testGetValues() {
  327. $values = [ 'x' => 'Value', 'y' => '' ];
  328. // Avoid FauxRequest (overrides getValues)
  329. $req = $this->mockWebRequest( $values );
  330. $this->assertSame( $values, $req->getValues() );
  331. $this->assertSame( [ 'x' => 'Value' ], $req->getValues( 'x' ), 'Specific keys' );
  332. }
  333. /**
  334. * @covers WebRequest::getValueNames
  335. */
  336. public function testGetValueNames() {
  337. $req = $this->mockWebRequest( [ 'x' => 'Value', 'y' => '' ] );
  338. $this->assertSame( [ 'x', 'y' ], $req->getValueNames() );
  339. $this->assertSame( [ 'x' ], $req->getValueNames( [ 'y' ] ), 'Exclude keys' );
  340. }
  341. /**
  342. * @dataProvider provideGetIP
  343. * @covers WebRequest::getIP
  344. */
  345. public function testGetIP( $expected, $input, $squid, $xffList, $private, $description ) {
  346. $this->setServerVars( $input );
  347. $this->setMwGlobals( [
  348. 'wgUsePrivateIPs' => $private,
  349. 'wgHooks' => [
  350. 'IsTrustedProxy' => [
  351. function ( &$ip, &$trusted ) use ( $xffList ) {
  352. $trusted = $trusted || in_array( $ip, $xffList );
  353. return true;
  354. }
  355. ]
  356. ]
  357. ] );
  358. $this->setService( 'ProxyLookup', new ProxyLookup( [], $squid ) );
  359. $request = new WebRequest();
  360. $result = $request->getIP();
  361. $this->assertEquals( $expected, $result, $description );
  362. }
  363. public static function provideGetIP() {
  364. return [
  365. [
  366. '127.0.0.1',
  367. [
  368. 'REMOTE_ADDR' => '127.0.0.1'
  369. ],
  370. [],
  371. [],
  372. false,
  373. 'Simple IPv4'
  374. ],
  375. [
  376. '::1',
  377. [
  378. 'REMOTE_ADDR' => '::1'
  379. ],
  380. [],
  381. [],
  382. false,
  383. 'Simple IPv6'
  384. ],
  385. [
  386. '12.0.0.1',
  387. [
  388. 'REMOTE_ADDR' => 'abcd:0001:002:03:4:555:6666:7777',
  389. 'HTTP_X_FORWARDED_FOR' => '12.0.0.1, abcd:0001:002:03:4:555:6666:7777',
  390. ],
  391. [ 'ABCD:1:2:3:4:555:6666:7777' ],
  392. [],
  393. false,
  394. 'IPv6 normalisation'
  395. ],
  396. [
  397. '12.0.0.3',
  398. [
  399. 'REMOTE_ADDR' => '12.0.0.1',
  400. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  401. ],
  402. [ '12.0.0.1', '12.0.0.2' ],
  403. [],
  404. false,
  405. 'With X-Forwaded-For'
  406. ],
  407. [
  408. '12.0.0.1',
  409. [
  410. 'REMOTE_ADDR' => '12.0.0.1',
  411. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  412. ],
  413. [],
  414. [],
  415. false,
  416. 'With X-Forwaded-For and disallowed server'
  417. ],
  418. [
  419. '12.0.0.2',
  420. [
  421. 'REMOTE_ADDR' => '12.0.0.1',
  422. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  423. ],
  424. [ '12.0.0.1' ],
  425. [],
  426. false,
  427. 'With multiple X-Forwaded-For and only one allowed server'
  428. ],
  429. [
  430. '10.0.0.3',
  431. [
  432. 'REMOTE_ADDR' => '12.0.0.2',
  433. 'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
  434. ],
  435. [ '12.0.0.1', '12.0.0.2' ],
  436. [],
  437. false,
  438. 'With X-Forwaded-For and private IP (from cache proxy)'
  439. ],
  440. [
  441. '10.0.0.4',
  442. [
  443. 'REMOTE_ADDR' => '12.0.0.2',
  444. 'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
  445. ],
  446. [ '12.0.0.1', '12.0.0.2', '10.0.0.3' ],
  447. [],
  448. true,
  449. 'With X-Forwaded-For and private IP (allowed)'
  450. ],
  451. [
  452. '10.0.0.4',
  453. [
  454. 'REMOTE_ADDR' => '12.0.0.2',
  455. 'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
  456. ],
  457. [ '12.0.0.1', '12.0.0.2' ],
  458. [ '10.0.0.3' ],
  459. true,
  460. 'With X-Forwaded-For and private IP (allowed)'
  461. ],
  462. [
  463. '10.0.0.3',
  464. [
  465. 'REMOTE_ADDR' => '12.0.0.2',
  466. 'HTTP_X_FORWARDED_FOR' => '10.0.0.4, 10.0.0.3, 12.0.0.2'
  467. ],
  468. [ '12.0.0.1', '12.0.0.2' ],
  469. [ '10.0.0.3' ],
  470. false,
  471. 'With X-Forwaded-For and private IP (disallowed)'
  472. ],
  473. [
  474. '12.0.0.3',
  475. [
  476. 'REMOTE_ADDR' => '12.0.0.1',
  477. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  478. ],
  479. [],
  480. [ '12.0.0.1', '12.0.0.2' ],
  481. false,
  482. 'With X-Forwaded-For'
  483. ],
  484. [
  485. '12.0.0.2',
  486. [
  487. 'REMOTE_ADDR' => '12.0.0.1',
  488. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  489. ],
  490. [],
  491. [ '12.0.0.1' ],
  492. false,
  493. 'With multiple X-Forwaded-For and only one allowed server'
  494. ],
  495. [
  496. '12.0.0.2',
  497. [
  498. 'REMOTE_ADDR' => '12.0.0.2',
  499. 'HTTP_X_FORWARDED_FOR' => '10.0.0.3, 12.0.0.2'
  500. ],
  501. [],
  502. [ '12.0.0.2' ],
  503. false,
  504. 'With X-Forwaded-For and private IP and hook (disallowed)'
  505. ],
  506. [
  507. '12.0.0.1',
  508. [
  509. 'REMOTE_ADDR' => 'abcd:0001:002:03:4:555:6666:7777',
  510. 'HTTP_X_FORWARDED_FOR' => '12.0.0.1, abcd:0001:002:03:4:555:6666:7777',
  511. ],
  512. [ 'ABCD:1:2:3::/64' ],
  513. [],
  514. false,
  515. 'IPv6 CIDR'
  516. ],
  517. [
  518. '12.0.0.3',
  519. [
  520. 'REMOTE_ADDR' => '12.0.0.1',
  521. 'HTTP_X_FORWARDED_FOR' => '12.0.0.3, 12.0.0.2'
  522. ],
  523. [ '12.0.0.0/24' ],
  524. [],
  525. false,
  526. 'IPv4 CIDR'
  527. ],
  528. ];
  529. }
  530. /**
  531. * @expectedException MWException
  532. * @covers WebRequest::getIP
  533. */
  534. public function testGetIpLackOfRemoteAddrThrowAnException() {
  535. // ensure that local install state doesn't interfere with test
  536. $this->setMwGlobals( [
  537. 'wgSquidServersNoPurge' => [],
  538. 'wgSquidServers' => [],
  539. 'wgUsePrivateIPs' => false,
  540. 'wgHooks' => [],
  541. ] );
  542. $this->setService( 'ProxyLookup', new ProxyLookup( [], [] ) );
  543. $request = new WebRequest();
  544. # Next call throw an exception about lacking an IP
  545. $request->getIP();
  546. }
  547. public static function provideLanguageData() {
  548. return [
  549. [ '', [], 'Empty Accept-Language header' ],
  550. [ 'en', [ 'en' => 1 ], 'One language' ],
  551. [ 'en, ar', [ 'en' => 1, 'ar' => 1 ], 'Two languages listed in appearance order.' ],
  552. [
  553. 'zh-cn,zh-tw',
  554. [ 'zh-cn' => 1, 'zh-tw' => 1 ],
  555. 'Two equally prefered languages, listed in appearance order per rfc3282. Checks c9119'
  556. ],
  557. [
  558. 'es, en; q=0.5',
  559. [ 'es' => 1, 'en' => '0.5' ],
  560. 'Spanish as first language and English and second'
  561. ],
  562. [ 'en; q=0.5, es', [ 'es' => 1, 'en' => '0.5' ], 'Less prefered language first' ],
  563. [ 'fr, en; q=0.5, es', [ 'fr' => 1, 'es' => 1, 'en' => '0.5' ], 'Three languages' ],
  564. [ 'en; q=0.5, es', [ 'es' => 1, 'en' => '0.5' ], 'Two languages' ],
  565. [ 'en, zh;q=0', [ 'en' => 1 ], "It's Chinese to me" ],
  566. [
  567. 'es; q=1, pt;q=0.7, it; q=0.6, de; q=0.1, ru;q=0',
  568. [ 'es' => '1', 'pt' => '0.7', 'it' => '0.6', 'de' => '0.1' ],
  569. 'Preference for Romance languages'
  570. ],
  571. [
  572. 'en-gb, en-us; q=1',
  573. [ 'en-gb' => 1, 'en-us' => '1' ],
  574. 'Two equally prefered English variants'
  575. ],
  576. [ '_', [], 'Invalid input' ],
  577. ];
  578. }
  579. /**
  580. * @dataProvider provideLanguageData
  581. * @covers WebRequest::getAcceptLang
  582. */
  583. public function testAcceptLang( $acceptLanguageHeader, $expectedLanguages, $description ) {
  584. $this->setServerVars( [ 'HTTP_ACCEPT_LANGUAGE' => $acceptLanguageHeader ] );
  585. $request = new WebRequest();
  586. $this->assertSame( $request->getAcceptLang(), $expectedLanguages, $description );
  587. }
  588. protected function setServerVars( $vars ) {
  589. // Don't remove vars which should be available in all SAPI.
  590. if ( !isset( $vars['REQUEST_TIME_FLOAT'] ) ) {
  591. $vars['REQUEST_TIME_FLOAT'] = $_SERVER['REQUEST_TIME_FLOAT'];
  592. }
  593. if ( !isset( $vars['REQUEST_TIME'] ) ) {
  594. $vars['REQUEST_TIME'] = $_SERVER['REQUEST_TIME'];
  595. }
  596. $_SERVER = $vars;
  597. }
  598. }