CommonHttpTests.php 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Http
  17. * @subpackage UnitTests
  18. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id$
  21. */
  22. // Read local configuration
  23. if (! defined('TESTS_ZEND_HTTP_CLIENT_BASEURI') &&
  24. is_readable('TestConfiguration.php')) {
  25. require_once 'TestConfiguration.php';
  26. }
  27. require_once 'Zend/Http/Client.php';
  28. require_once 'Zend/Uri/Http.php';
  29. /**
  30. * This Testsuite includes all Zend_Http_Client that require a working web
  31. * server to perform. It was designed to be extendable, so that several
  32. * test suites could be run against several servers, with different client
  33. * adapters and configurations.
  34. *
  35. * Note that $this->baseuri must point to a directory on a web server
  36. * containing all the files under the _files directory. You should symlink
  37. * or copy these files and set 'baseuri' properly.
  38. *
  39. * You can also set the proper constant in your test configuration file to
  40. * point to the right place.
  41. *
  42. * @category Zend
  43. * @package Zend_Http_Client
  44. * @subpackage UnitTests
  45. * @copyright Copyright (c) 2005-2014 Zend Technologies USA Inc. (http://www.zend.com)
  46. * @license http://framework.zend.com/license/new-bsd New BSD License
  47. * @group Zend_Http
  48. * @group Zend_Http_Client
  49. */
  50. abstract class Zend_Http_Client_CommonHttpTests extends PHPUnit_Framework_TestCase
  51. {
  52. /**
  53. * The bast URI for this test, containing all files in the _files directory
  54. * Should be set in TestConfiguration.php or TestConfiguration.php.dist
  55. *
  56. * @var string
  57. */
  58. protected $baseuri;
  59. /**
  60. * Common HTTP client
  61. *
  62. * @var Zend_Http_Client
  63. */
  64. protected $client = null;
  65. /**
  66. * Common HTTP client adapter
  67. *
  68. * @var Zend_Http_Client_Adapter_Interface
  69. */
  70. protected $_adapter = null;
  71. /**
  72. * Configuration array
  73. *
  74. * @var array
  75. */
  76. protected $config = array(
  77. 'adapter' => 'Zend_Http_Client_Adapter_Socket'
  78. );
  79. /**
  80. * Set up the test case
  81. *
  82. */
  83. protected function setUp()
  84. {
  85. if (defined('TESTS_ZEND_HTTP_CLIENT_BASEURI') &&
  86. Zend_Uri_Http::check(TESTS_ZEND_HTTP_CLIENT_BASEURI)) {
  87. $this->baseuri = TESTS_ZEND_HTTP_CLIENT_BASEURI;
  88. if (substr($this->baseuri, -1) != '/') $this->baseuri .= '/';
  89. $name = $this->getName();
  90. if (($pos = strpos($name, ' ')) !== false) {
  91. $name = substr($name, 0, $pos);
  92. }
  93. $uri = $this->baseuri . $name . '.php';
  94. $this->_adapter = new $this->config['adapter'];
  95. $this->client = new Zend_Http_Client($uri, $this->config);
  96. $this->client->setAdapter($this->_adapter);
  97. } else {
  98. // Skip tests
  99. $this->markTestSkipped("Zend_Http_Client dynamic tests are not enabled in TestConfiguration.php");
  100. }
  101. }
  102. /**
  103. * Clean up the test environment
  104. *
  105. */
  106. protected function tearDown()
  107. {
  108. $this->client = null;
  109. $this->_adapter = null;
  110. }
  111. /**
  112. * Simple request tests
  113. */
  114. /**
  115. * Test simple requests
  116. *
  117. */
  118. public function testSimpleRequests()
  119. {
  120. $methods = array('GET', 'POST', 'OPTIONS', 'PUT', 'PATCH', 'DELETE');
  121. foreach ($methods as $method) {
  122. $res = $this->client->request($method);
  123. $this->assertEquals('Success', $res->getBody(), "HTTP {$method} request failed.");
  124. }
  125. }
  126. /**
  127. * Test we can get the last request as string
  128. *
  129. */
  130. public function testGetLastRequest()
  131. {
  132. $this->client->setUri($this->baseuri . 'testHeaders.php');
  133. $this->client->setParameterGet('someinput', 'somevalue');
  134. $this->client->setHeaders(array(
  135. 'X-Powered-By' => 'My Glorious Golden Ass',
  136. ));
  137. $res = $this->client->request(Zend_Http_Client::TRACE);
  138. if ($res->getStatus() == 405 || $res->getStatus() == 501) {
  139. $this->markTestSkipped("Server does not allow the TRACE method");
  140. }
  141. $this->assertEquals($this->client->getLastRequest(), $res->getBody(), 'Response body should be exactly like the last request');
  142. }
  143. /**
  144. * GET and POST parameters tests
  145. */
  146. /**
  147. * Test we can properly send GET parameters
  148. *
  149. * @dataProvider parameterArrayProvider
  150. */
  151. public function testGetData($params)
  152. {
  153. $this->client->setUri($this->client->getUri(true) . '?name=Arthur');
  154. $this->client->setParameterGet($params);
  155. $res = $this->client->request('GET');
  156. $this->assertEquals(serialize(array_merge(array('name' => 'Arthur'), $params)), $res->getBody());
  157. }
  158. /**
  159. * Test we can properly send POST parameters with
  160. * application/x-www-form-urlencoded content type
  161. *
  162. * @dataProvider parameterArrayProvider
  163. */
  164. public function testPostDataUrlEncoded($params)
  165. {
  166. $this->client->setUri($this->baseuri . 'testPostData.php');
  167. $this->client->setEncType(Zend_Http_Client::ENC_URLENCODED);
  168. $this->client->setParameterPost($params);
  169. $res = $this->client->request('POST');
  170. $this->assertEquals(serialize($params), $res->getBody(), "POST data integrity test failed");
  171. }
  172. /**
  173. * Test we can properly send PUT parameters with
  174. * application/x-www-form-urlencoded content type
  175. *
  176. * @dataProvider parameterArrayProvider
  177. */
  178. public function testPutDataUrlEncoded($params)
  179. {
  180. $this->client->setUri($this->baseuri . 'testPutData.php');
  181. $this->client->setEncType(Zend_Http_Client::ENC_URLENCODED);
  182. $this->client->setParameterPost($params);
  183. $res = $this->client->request('PUT');
  184. $this->assertEquals(serialize($params), $res->getBody(), "PUT data integrity test failed");
  185. }
  186. /**
  187. * Test we can properly send PUT parameters without
  188. * content type, relying on default content type (urlencoded)
  189. *
  190. * @dataProvider parameterArrayProvider
  191. */
  192. public function testPutDataDefault($params)
  193. {
  194. $this->client->setUri($this->baseuri . 'testPutData.php');
  195. // note that no content type is set
  196. $this->client->setParameterPost($params);
  197. $res = $this->client->request('PUT');
  198. $this->assertEquals(serialize($params), $res->getBody(), "PUT data integrity test failed for default content-type");
  199. }
  200. /**
  201. * Test we can properly send PATCH parameters with
  202. * application/x-www-form-urlencoded content type
  203. *
  204. * @dataProvider parameterArrayProvider
  205. */
  206. public function testPatchDataUrlEncoded($params)
  207. {
  208. $this->client->setUri($this->baseuri . 'testPatchData.php');
  209. $this->client->setEncType(Zend_Http_Client::ENC_URLENCODED);
  210. $this->client->setParameterPost($params);
  211. $res = $this->client->request('PATCH');
  212. $this->assertEquals(serialize($params), $res->getBody(), "PATCH data integrity test failed");
  213. }
  214. /**
  215. * Test we can properly send PATCH parameters without
  216. * content type, relying on default content type (urlencoded)
  217. *
  218. * @dataProvider parameterArrayProvider
  219. */
  220. public function testPatchDataDefault($params)
  221. {
  222. $this->client->setUri($this->baseuri . 'testPatchData.php');
  223. // note that no content type is set
  224. $this->client->setParameterPost($params);
  225. $res = $this->client->request('PATCH');
  226. $this->assertEquals(serialize($params), $res->getBody(), "PATCH data integrity test failed for default content-type");
  227. }
  228. /**
  229. * Test we can properly send parameters with
  230. * application/x-www-form-urlencoded content type
  231. *
  232. * @dataProvider parameterArrayProvider
  233. */
  234. public function testDeleteDataUrlEncoded($params)
  235. {
  236. $this->client->setUri($this->baseuri . 'testDeleteData.php');
  237. $this->client->setEncType(Zend_Http_Client::ENC_URLENCODED);
  238. $this->client->setParameterPost($params);
  239. $res = $this->client->request('DELETE');
  240. $this->assertEquals(serialize($params), $res->getBody(), "DELETE data integrity test failed");
  241. }
  242. /**
  243. * Test we can properly send POST parameters with
  244. * multipart/form-data content type
  245. *
  246. * @dataProvider parameterArrayProvider
  247. */
  248. public function testPostDataMultipart($params)
  249. {
  250. $this->client->setUri($this->baseuri . 'testPostData.php');
  251. $this->client->setEncType(Zend_Http_Client::ENC_FORMDATA);
  252. $this->client->setParameterPost($params);
  253. $res = $this->client->request('POST');
  254. $this->assertEquals(serialize($params), $res->getBody(), "POST data integrity test failed");
  255. }
  256. /**
  257. * Test we can properly send PUT parameters with
  258. * multipart/form-data content type
  259. *
  260. * @dataProvider parameterArrayProvider
  261. */
  262. public function testPutDataMultipart($params)
  263. {
  264. $this->client->setUri($this->baseuri . 'testRawPutData.php');
  265. $this->client->setParameterPost($params);
  266. $this->client->setMethod(Zend_Http_Client::PUT);
  267. $this->client->setEncType(Zend_Http_Client::ENC_FORMDATA);
  268. $response = $this->client->request();
  269. $responseText = $response->getBody();
  270. $this->_checkPresence($responseText, $params);
  271. }
  272. /**
  273. * Test we can properly send PATCH parameters with
  274. * multipart/form-data content type
  275. *
  276. * @dataProvider parameterArrayProvider
  277. */
  278. public function testPatchDataMultipart($params)
  279. {
  280. $this->client->setUri($this->baseuri . 'testRawPatchData.php');
  281. $this->client->setParameterPost($params);
  282. $this->client->setMethod(Zend_Http_Client::PATCH);
  283. $this->client->setEncType(Zend_Http_Client::ENC_FORMDATA);
  284. $response = $this->client->request();
  285. $responseText = $response->getBody();
  286. $this->_checkPresence($responseText, $params);
  287. }
  288. /**
  289. * Checks the presence of keys (non-numeric only) and values
  290. * in the raw form data reponse for a PUT or DELETE request recursively
  291. *
  292. * An example response (I do not know of a better way to check it):
  293. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  294. * Content-Disposition: form-data; name="quest"
  295. *
  296. * To seek the holy grail
  297. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  298. * Content-Disposition: form-data; name="YourMother"
  299. *
  300. * Was a hamster
  301. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  302. * Content-Disposition: form-data; name="specialChars"
  303. *
  304. * <>$+ &?=[]^%
  305. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  306. * Content-Disposition: form-data; name="array[]"
  307. *
  308. * firstItem
  309. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  310. * Content-Disposition: form-data; name="array[]"
  311. *
  312. * secondItem
  313. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828
  314. * Content-Disposition: form-data; name="array[]"
  315. *
  316. * 3rdItem
  317. * -----ZENDHTTPCLIENT-c63973519a6bb3ec45495876f5e15828--
  318. * @param string $responseText
  319. * @param string|integer|array $needle
  320. */
  321. protected function _checkPresence($responseText, $needle)
  322. {
  323. if (is_array($needle)) {
  324. foreach ($needle as $key => $value) {
  325. // treat array values recursively, let them re-enter this function
  326. if (is_array($value)) {
  327. $this->_checkPresence($responseText, $value);
  328. continue;
  329. }
  330. // continue with non array keys and values
  331. // numeric keys will not be present in the response
  332. if (!is_int($key)) {
  333. $this->assertGreaterThan(
  334. 0,
  335. strpos($responseText, $key),
  336. "key '$key' is missing from the reponse for raw multipart PUT or DELETE request"
  337. );
  338. }
  339. $this->assertGreaterThan(
  340. 0,
  341. strpos($responseText, $value),
  342. "value '$value' is missing from the reponse for raw multipart PUT or DELETE request"
  343. );
  344. }
  345. }
  346. }
  347. /**
  348. * Test we can properly send DELETE parameters with
  349. * multipart/form-data content type
  350. *
  351. * @dataProvider parameterArrayProvider
  352. */
  353. public function testDeleteDataMultipart($params)
  354. {
  355. $this->client->setUri($this->baseuri . 'testRawDeleteData.php');
  356. $this->client->setParameterPost($params);
  357. $this->client->setMethod(Zend_Http_Client::DELETE);
  358. $this->client->setEncType(Zend_Http_Client::ENC_FORMDATA);
  359. $response = $this->client->request();
  360. $responseText = $response->getBody();
  361. $this->_checkPresence($responseText, $params);
  362. }
  363. /**
  364. * Test using raw HTTP POST data
  365. *
  366. */
  367. public function testRawPostData()
  368. {
  369. $data = "Chuck Norris never wet his bed as a child. The bed wet itself out of fear.";
  370. $res = $this->client->setRawData($data, 'text/html')->request('POST');
  371. $this->assertEquals($data, $res->getBody(), 'Response body does not contain the expected data');
  372. }
  373. /**
  374. * Test using raw HTTP PUT data
  375. *
  376. * @group ZF-11030
  377. *
  378. */
  379. public function testRawPutData()
  380. {
  381. $data = "Chuck Norris never wet his bed as a child. The bed wet itself out of fear.";
  382. $this->client->setUri($this->baseuri . 'testRawPutData.php');
  383. $this->client->setRawData($data, 'text/plain');
  384. $this->client->setMethod(Zend_Http_Client::PUT);
  385. $response = $this->client->request();
  386. $this->assertEquals($data, $response->getBody(), 'Response body does not contain the expected data');
  387. }
  388. /**
  389. * Test using raw HTTP DELETE data
  390. *
  391. * @group ZF-11030
  392. *
  393. */
  394. public function testRawDeleteData()
  395. {
  396. $data = "Chuck Norris never wet his bed as a child. The bed wet itself out of fear.";
  397. $this->client->setUri($this->baseuri . 'testRawDeleteData.php');
  398. $this->client->setRawData($data, 'text/plain');
  399. $this->client->setMethod(Zend_Http_Client::DELETE);
  400. $response = $this->client->request();
  401. $this->assertEquals($data, $response->getBody(), 'Response body does not contain the expected data');
  402. }
  403. /**
  404. * Make sure we can reset the parameters between consecutive requests
  405. *
  406. */
  407. public function testResetParameters()
  408. {
  409. $params = array(
  410. 'quest' => 'To seek the holy grail',
  411. 'YourMother' => 'Was a hamster',
  412. 'specialChars' => '<>$+ &?=[]^%',
  413. 'array' => array('firstItem', 'secondItem', '3rdItem')
  414. );
  415. $headers = array("X-Foo" => "bar");
  416. $this->client->setParameterPost($params);
  417. $this->client->setParameterGet($params);
  418. $this->client->setHeaders($headers);
  419. $res = $this->client->request('POST');
  420. $this->assertContains(serialize($params) . "\n" . serialize($params),
  421. $res->getBody(), "returned body does not contain all GET and POST parameters (it should!)");
  422. $this->client->resetParameters();
  423. $res = $this->client->request('POST');
  424. $this->assertNotContains(serialize($params), $res->getBody(),
  425. "returned body contains GET or POST parameters (it shouldn't!)");
  426. $this->assertContains($headers["X-Foo"], $this->client->getHeader("X-Foo"), "Header not preserved by reset");
  427. $this->client->resetParameters(true);
  428. $this->assertNull($this->client->getHeader("X-Foo"), "Header preserved by reset(true)");
  429. }
  430. /**
  431. * Test parameters get reset when we unset them
  432. *
  433. */
  434. public function testParameterUnset()
  435. {
  436. $this->client->setUri($this->baseuri . 'testResetParameters.php');
  437. $gparams = array (
  438. 'cheese' => 'camambert',
  439. 'beer' => 'jever pilnsen',
  440. );
  441. $pparams = array (
  442. 'from' => 'bob',
  443. 'to' => 'alice'
  444. );
  445. $this->client->setParameterGet($gparams)->setParameterPost($pparams);
  446. // Remove some parameters
  447. $this->client->setParameterGet('cheese', null)->setParameterPost('to', null);
  448. $res = $this->client->request('POST');
  449. $this->assertNotContains('cheese', $res->getBody(), 'The "cheese" GET parameter was expected to be unset');
  450. $this->assertNotContains('alice', $res->getBody(), 'The "to" POST parameter was expected to be unset');
  451. }
  452. /**
  453. * Header Tests
  454. */
  455. /**
  456. * Make sure we can set a single header
  457. *
  458. */
  459. public function testHeadersSingle()
  460. {
  461. $this->client->setUri($this->baseuri . 'testHeaders.php');
  462. $headers = array(
  463. 'Accept-encoding' => 'gzip,deflate',
  464. 'X-baz' => 'Foo',
  465. 'X-powered-by' => 'A large wooden badger'
  466. );
  467. foreach ($headers as $key => $val) {
  468. $this->client->setHeaders($key, $val);
  469. }
  470. $acceptHeader = "Accept: text/xml,text/html,*/*";
  471. $this->client->setHeaders($acceptHeader);
  472. $res = $this->client->request('TRACE');
  473. if ($res->getStatus() == 405 || $res->getStatus() == 501) {
  474. $this->markTestSkipped("Server does not allow the TRACE method");
  475. }
  476. $body = strtolower($res->getBody());
  477. foreach ($headers as $key => $val)
  478. $this->assertContains(strtolower("$key: $val"), $body);
  479. $this->assertContains(strtolower($acceptHeader), $body);
  480. }
  481. /**
  482. * Test we can set an array of headers
  483. *
  484. */
  485. public function testHeadersArray()
  486. {
  487. $this->client->setUri($this->baseuri . 'testHeaders.php');
  488. $headers = array(
  489. 'Accept-encoding' => 'gzip,deflate',
  490. 'X-baz' => 'Foo',
  491. 'X-powered-by' => 'A large wooden badger',
  492. 'Accept: text/xml,text/html,*/*'
  493. );
  494. $this->client->setHeaders($headers);
  495. $res = $this->client->request('TRACE');
  496. if ($res->getStatus() == 405 || $res->getStatus() == 501) {
  497. $this->markTestSkipped("Server does not allow the TRACE method");
  498. }
  499. $body = strtolower($res->getBody());
  500. foreach ($headers as $key => $val) {
  501. if (is_string($key)) {
  502. $this->assertContains(strtolower("$key: $val"), $body);
  503. } else {
  504. $this->assertContains(strtolower($val), $body);
  505. }
  506. }
  507. }
  508. /**
  509. * Test we can set a set of values for one header
  510. *
  511. */
  512. public function testMultipleHeader()
  513. {
  514. $this->client->setUri($this->baseuri . 'testHeaders.php');
  515. $headers = array(
  516. 'Accept-encoding' => 'gzip,deflate',
  517. 'X-baz' => 'Foo',
  518. 'X-powered-by' => array(
  519. 'A large wooden badger',
  520. 'My Shiny Metal Ass',
  521. 'Dark Matter'
  522. ),
  523. 'Cookie' => array(
  524. 'foo=bar',
  525. 'baz=waka'
  526. )
  527. );
  528. $this->client->setHeaders($headers);
  529. $res = $this->client->request('TRACE');
  530. if ($res->getStatus() == 405 || $res->getStatus() == 501) {
  531. $this->markTestSkipped("Server does not allow the TRACE method");
  532. }
  533. $body = strtolower($res->getBody());
  534. foreach ($headers as $key => $val) {
  535. if (is_array($val))
  536. $val = implode(', ', $val);
  537. $this->assertContains(strtolower("$key: $val"), $body);
  538. }
  539. }
  540. /**
  541. * Redirection tests
  542. */
  543. /**
  544. * Test the client properly redirects in default mode
  545. *
  546. */
  547. public function testRedirectDefault()
  548. {
  549. $this->client->setUri($this->baseuri . 'testRedirections.php');
  550. // Set some parameters
  551. $this->client->setParameterGet('swallow', 'african');
  552. $this->client->setParameterPost('Camelot', 'A silly place');
  553. // Request
  554. $res = $this->client->request('POST');
  555. $this->assertEquals(3, $this->client->getRedirectionsCount(), 'Redirection counter is not as expected');
  556. // Make sure the body does *not* contain the set parameters
  557. $this->assertNotContains('swallow', $res->getBody());
  558. $this->assertNotContains('Camelot', $res->getBody());
  559. }
  560. /**
  561. * Make sure the client properly redirects in strict mode
  562. *
  563. */
  564. public function testRedirectStrict()
  565. {
  566. $this->client->setUri($this->baseuri . 'testRedirections.php');
  567. // Set some parameters
  568. $this->client->setParameterGet('swallow', 'african');
  569. $this->client->setParameterPost('Camelot', 'A silly place');
  570. // Set strict redirections
  571. $this->client->setConfig(array('strictredirects' => true));
  572. // Request
  573. $res = $this->client->request('POST');
  574. $this->assertEquals(3, $this->client->getRedirectionsCount(), 'Redirection counter is not as expected');
  575. // Make sure the body *does* contain the set parameters
  576. $this->assertContains('swallow', $res->getBody());
  577. $this->assertContains('Camelot', $res->getBody());
  578. }
  579. /**
  580. * Make sure redirections stop when limit is exceeded
  581. *
  582. */
  583. public function testMaxRedirectsExceeded()
  584. {
  585. $this->client->setUri($this->baseuri . 'testRedirections.php');
  586. // Set some parameters
  587. $this->client->setParameterGet('swallow', 'african');
  588. $this->client->setParameterPost('Camelot', 'A silly place');
  589. // Set lower max redirections
  590. // Try with strict redirections first
  591. $this->client->setConfig(array('strictredirects' => true, 'maxredirects' => 2));
  592. $res = $this->client->request('POST');
  593. $this->assertTrue($res->isRedirect(),
  594. "Last response was not a redirection as expected. Response code: {$res->getStatus()}. Redirections counter: {$this->client->getRedirectionsCount()} (when strict redirects are on)");
  595. // Then try with normal redirections
  596. $this->client->setParameterGet('redirection', '0');
  597. $this->client->setConfig(array('strictredirects' => false));
  598. $res = $this->client->request('POST');
  599. $this->assertTrue($res->isRedirect(),
  600. "Last response was not a redirection as expected. Response code: {$res->getStatus()}. Redirections counter: {$this->client->getRedirectionsCount()} (when strict redirects are off)");
  601. }
  602. /**
  603. * Test we can properly redirect to an absolute path (not full URI)
  604. *
  605. */
  606. public function testAbsolutePathRedirect()
  607. {
  608. $this->client->setUri($this->baseuri . 'testRelativeRedirections.php');
  609. $this->client->setParameterGet('redirect', 'abpath');
  610. $this->client->setConfig(array('maxredirects' => 1));
  611. // Get the host and port part of our baseuri
  612. $uri = $this->client->getUri()->getScheme() . '://' . $this->client->getUri()->getHost() . ':' .
  613. $this->client->getUri()->getPort();
  614. $res = $this->client->request('GET');
  615. $this->assertEquals("{$uri}/path/to/fake/file.ext?redirect=abpath", $this->client->getUri(true),
  616. "The new location is not as expected: {$this->client->getUri(true)}");
  617. }
  618. /**
  619. * Test we can properly redirect to a relative path
  620. *
  621. */
  622. public function testRelativePathRedirect()
  623. {
  624. $this->client->setUri($this->baseuri . 'testRelativeRedirections.php');
  625. $this->client->setParameterGet('redirect', 'relpath');
  626. $this->client->setConfig(array('maxredirects' => 1));
  627. // Set the new expected URI
  628. $uri = clone $this->client->getUri();
  629. $uri->setPath(rtrim(dirname($uri->getPath()), '/') . '/path/to/fake/file.ext');
  630. $uri = $uri->__toString();
  631. $res = $this->client->request('GET');
  632. $this->assertEquals("{$uri}?redirect=relpath", $this->client->getUri(true),
  633. "The new location is not as expected: {$this->client->getUri(true)}");
  634. }
  635. /**
  636. * HTTP Authentication Tests
  637. *
  638. */
  639. /**
  640. * Test we can properly use Basic HTTP authentication
  641. *
  642. */
  643. public function testHttpAuthBasic()
  644. {
  645. $this->client->setUri($this->baseuri. 'testHttpAuth.php');
  646. $this->client->setParameterGet(array(
  647. 'user' => 'alice',
  648. 'pass' => 'secret',
  649. 'method' => 'Basic'
  650. ));
  651. // First - fail password
  652. $this->client->setAuth('alice', 'wrong');
  653. $res = $this->client->request();
  654. $this->assertEquals(401, $res->getStatus(), 'Expected HTTP 401 response was not recieved');
  655. // Now use good password
  656. $this->client->setAuth('alice', 'secret');
  657. $res = $this->client->request();
  658. $this->assertEquals(200, $res->getStatus(), 'Expected HTTP 200 response was not recieved');
  659. }
  660. /**
  661. * Test that we can properly use Basic HTTP authentication by specifying username and password
  662. * in the URI
  663. *
  664. */
  665. public function testHttpAuthBasicWithCredentialsInUri()
  666. {
  667. $uri = str_replace('http://', 'http://%s:%s@', $this->baseuri) . 'testHttpAuth.php';
  668. $this->client->setParameterGet(array(
  669. 'user' => 'alice',
  670. 'pass' => 'secret',
  671. 'method' => 'Basic'
  672. ));
  673. // First - fail password
  674. $this->client->setUri(sprintf($uri, 'alice', 'wrong'));
  675. $res = $this->client->request();
  676. $this->assertEquals(401, $res->getStatus(), 'Expected HTTP 401 response was not recieved');
  677. // Now use good password
  678. $this->client->setUri(sprintf($uri, 'alice', 'secret'));
  679. $res = $this->client->request();
  680. $this->assertEquals(200, $res->getStatus(), 'Expected HTTP 200 response was not recieved');
  681. }
  682. /**
  683. * Test we can unset HTTP authentication
  684. *
  685. */
  686. public function testCancelAuth()
  687. {
  688. $this->client->setUri($this->baseuri. 'testHttpAuth.php');
  689. // Set auth and cancel it
  690. $this->client->setAuth('alice', 'secret');
  691. $this->client->setAuth(false);
  692. $res = $this->client->request();
  693. $this->assertEquals(401, $res->getStatus(), 'Expected HTTP 401 response was not recieved');
  694. $this->assertNotContains('alice', $res->getBody(), "Body contains the user name, but it shouldn't");
  695. $this->assertNotContains('secret', $res->getBody(), "Body contains the password, but it shouldn't");
  696. }
  697. /**
  698. * Test that we can unset HTTP authentication when credentials is specified in the URI
  699. *
  700. */
  701. public function testCancelAuthWithCredentialsInUri()
  702. {
  703. $uri = str_replace('http://', 'http://%s:%s@', $this->baseuri) . 'testHttpAuth.php';
  704. // Set auth and cancel it
  705. $this->client->setUri(sprintf($uri, 'alice', 'secret'));
  706. $this->client->setAuth(false);
  707. $res = $this->client->request();
  708. $this->assertEquals(401, $res->getStatus(), 'Expected HTTP 401 response was not recieved');
  709. $this->assertNotContains('alice', $res->getBody(), "Body contains the user name, but it shouldn't");
  710. $this->assertNotContains('secret', $res->getBody(), "Body contains the password, but it shouldn't");
  711. }
  712. /**
  713. * Cookie and CookieJar Tests
  714. *
  715. */
  716. /**
  717. * Test we can set string cookies with no jar
  718. *
  719. */
  720. public function testCookiesStringNoJar()
  721. {
  722. $this->client->setUri($this->baseuri. 'testCookies.php');
  723. $cookies = array(
  724. 'name' => 'value',
  725. 'cookie' => 'crumble'
  726. );
  727. foreach ($cookies as $k => $v) {
  728. $this->client->setCookie($k, $v);
  729. }
  730. $res = $this->client->request();
  731. $this->assertEquals($res->getBody(), serialize($cookies), 'Response body does not contain the expected cookies');
  732. }
  733. /**
  734. * Make sure we can set object cookies with no jar
  735. *
  736. */
  737. public function testSetCookieObjectNoJar()
  738. {
  739. $this->client->setUri($this->baseuri. 'testCookies.php');
  740. $refuri = $this->client->getUri();
  741. $cookies = array(
  742. Zend_Http_Cookie::fromString('chocolate=chips', $refuri),
  743. Zend_Http_Cookie::fromString('crumble=apple', $refuri)
  744. );
  745. $strcookies = array();
  746. foreach ($cookies as $c) {
  747. $this->client->setCookie($c);
  748. $strcookies[$c->getName()] = $c->getValue();
  749. }
  750. $res = $this->client->request();
  751. $this->assertEquals($res->getBody(), serialize($strcookies), 'Response body does not contain the expected cookies');
  752. }
  753. /**
  754. * Make sure we can set an array of object cookies
  755. *
  756. */
  757. public function testSetCookieObjectArray()
  758. {
  759. $this->client->setUri($this->baseuri. 'testCookies.php');
  760. $refuri = $this->client->getUri();
  761. $cookies = array(
  762. Zend_Http_Cookie::fromString('chocolate=chips', $refuri),
  763. Zend_Http_Cookie::fromString('crumble=apple', $refuri),
  764. Zend_Http_Cookie::fromString('another=cookie', $refuri)
  765. );
  766. $this->client->setCookie($cookies);
  767. $strcookies = array();
  768. foreach ($cookies as $c) {
  769. $strcookies[$c->getName()] = $c->getValue();
  770. }
  771. $res = $this->client->request();
  772. $this->assertEquals($res->getBody(), serialize($strcookies), 'Response body does not contain the expected cookies');
  773. }
  774. /**
  775. * Make sure we can set an array of string cookies
  776. *
  777. */
  778. public function testSetCookieStringArray()
  779. {
  780. $this->client->setUri($this->baseuri. 'testCookies.php');
  781. $cookies = array(
  782. 'chocolate' => 'chips',
  783. 'crumble' => 'apple',
  784. 'another' => 'cookie'
  785. );
  786. $this->client->setCookie($cookies);
  787. $res = $this->client->request();
  788. $this->assertEquals($res->getBody(), serialize($cookies), 'Response body does not contain the expected cookies');
  789. }
  790. /**
  791. * Make sure we can set cookie objects with a jar
  792. *
  793. */
  794. public function testSetCookieObjectJar()
  795. {
  796. $this->client->setUri($this->baseuri. 'testCookies.php');
  797. $this->client->setCookieJar();
  798. $refuri = $this->client->getUri();
  799. $cookies = array(
  800. Zend_Http_Cookie::fromString('chocolate=chips', $refuri),
  801. Zend_Http_Cookie::fromString('crumble=apple', $refuri)
  802. );
  803. $strcookies = array();
  804. foreach ($cookies as $c) {
  805. $this->client->setCookie($c);
  806. $strcookies[$c->getName()] = $c->getValue();
  807. }
  808. $res = $this->client->request();
  809. $this->assertEquals($res->getBody(), serialize($strcookies), 'Response body does not contain the expected cookies');
  810. }
  811. /**
  812. * File Upload Tests
  813. *
  814. */
  815. /**
  816. * Test we can upload raw data as a file
  817. *
  818. */
  819. public function testUploadRawData()
  820. {
  821. if (!ini_get('file_uploads')) {
  822. $this->markTestSkipped('File uploads disabled.');
  823. }
  824. $this->client->setUri($this->baseuri. 'testUploads.php');
  825. $rawdata = file_get_contents(__FILE__);
  826. $this->client->setFileUpload('myfile.txt', 'uploadfile', $rawdata, 'text/plain');
  827. $res = $this->client->request('POST');
  828. $body = 'uploadfile myfile.txt text/plain ' . strlen($rawdata) . "\n";
  829. $this->assertEquals($body, $res->getBody(), 'Response body does not include expected upload parameters');
  830. }
  831. /**
  832. * Test we can upload an existing file
  833. *
  834. */
  835. public function testUploadLocalFile()
  836. {
  837. if (!ini_get('file_uploads')) {
  838. $this->markTestSkipped('File uploads disabled.');
  839. }
  840. $this->client->setUri($this->baseuri. 'testUploads.php');
  841. $this->client->setFileUpload(__FILE__, 'uploadfile', null, 'text/x-foo-bar');
  842. $res = $this->client->request('POST');
  843. $size = filesize(__FILE__);
  844. $body = "uploadfile " . basename(__FILE__) . " text/x-foo-bar $size\n";
  845. $this->assertEquals($body, $res->getBody(), 'Response body does not include expected upload parameters');
  846. }
  847. public function testUploadLocalDetectMime()
  848. {
  849. if (!ini_get('file_uploads')) {
  850. $this->markTestSkipped('File uploads disabled.');
  851. }
  852. $detect = null;
  853. if (function_exists('finfo_file')) {
  854. $f = @finfo_open(FILEINFO_MIME);
  855. if ($f) $detect = 'finfo';
  856. } elseif (function_exists('mime_content_type')) {
  857. if (mime_content_type(__FILE__)) {
  858. $detect = 'mime_magic';
  859. }
  860. }
  861. if (! $detect) {
  862. $this->markTestSkipped('No MIME type detection capability (fileinfo or mime_magic extensions) is available');
  863. }
  864. $file = dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'staticFile.jpg';
  865. $this->client->setUri($this->baseuri. 'testUploads.php');
  866. $this->client->setFileUpload($file, 'uploadfile');
  867. $res = $this->client->request('POST');
  868. $size = filesize($file);
  869. $body = "uploadfile " . basename($file) . " image/jpeg $size\n";
  870. $this->assertEquals($body, $res->getBody(), 'Response body does not include expected upload parameters (detect: ' . $detect . ')');
  871. }
  872. public function testUploadNameWithSpecialChars()
  873. {
  874. if (!ini_get('file_uploads')) {
  875. $this->markTestSkipped('File uploads disabled.');
  876. }
  877. $this->client->setUri($this->baseuri. 'testUploads.php');
  878. $rawdata = file_get_contents(__FILE__);
  879. $this->client->setFileUpload('/some strage/path%/with[!@#$&]/myfile.txt', 'uploadfile', $rawdata, 'text/plain');
  880. $res = $this->client->request('POST');
  881. $body = 'uploadfile myfile.txt text/plain ' . strlen($rawdata) . "\n";
  882. $this->assertEquals($body, $res->getBody(), 'Response body does not include expected upload parameters');
  883. }
  884. public function testStaticLargeFileDownload()
  885. {
  886. $this->client->setUri($this->baseuri . 'staticFile.jpg');
  887. $got = $this->client->request()->getBody();
  888. $expected = $this->_getTestFileContents('staticFile.jpg');
  889. $this->assertEquals($expected, $got, 'Downloaded file does not seem to match!');
  890. }
  891. /**
  892. * Test that one can upload multiple files with the same form name, as an
  893. * array
  894. *
  895. * @link http://framework.zend.com/issues/browse/ZF-5744
  896. */
  897. public function testMutipleFilesWithSameFormNameZF5744()
  898. {
  899. if (!ini_get('file_uploads')) {
  900. $this->markTestSkipped('File uploads disabled.');
  901. }
  902. $rawData = 'Some test raw data here...';
  903. $this->client->setUri($this->baseuri . 'testUploads.php');
  904. $files = array('file1.txt', 'file2.txt', 'someotherfile.foo');
  905. $expectedBody = '';
  906. foreach($files as $filename) {
  907. $this->client->setFileUpload($filename, 'uploadfile[]', $rawData, 'text/plain');
  908. $expectedBody .= "uploadfile $filename text/plain " . strlen($rawData) . "\n";
  909. }
  910. $res = $this->client->request('POST');
  911. $this->assertEquals($expectedBody, $res->getBody(), 'Response body does not include expected upload parameters');
  912. }
  913. /**
  914. * Test that lines that might be evaluated as boolean false do not break
  915. * the reading prematurely.
  916. *
  917. * @link http://framework.zend.com/issues/browse/ZF-4238
  918. */
  919. public function testZF4238FalseLinesInResponse()
  920. {
  921. $this->client->setUri($this->baseuri . 'ZF4238-zerolineresponse.txt');
  922. $got = $this->client->request()->getBody();
  923. $expected = $this->_getTestFileContents('ZF4238-zerolineresponse.txt');
  924. $this->assertEquals($expected, $got);
  925. }
  926. public function testStreamResponse()
  927. {
  928. if(!($this->client->getAdapter() instanceof Zend_Http_Client_Adapter_Stream)) {
  929. $this->markTestSkipped('Current adapter does not support streaming');
  930. return;
  931. }
  932. $this->client->setUri($this->baseuri . 'staticFile.jpg');
  933. $this->client->setStream();
  934. $response = $this->client->request();
  935. $this->assertTrue($response instanceof Zend_Http_Response_Stream, 'Request did not return stream response!');
  936. $this->assertTrue(is_resource($response->getStream()), 'Request does not contain stream!');
  937. $stream_name = $response->getStreamName();
  938. $stream_read = stream_get_contents($response->getStream());
  939. $file_read = file_get_contents($stream_name);
  940. $expected = $this->_getTestFileContents('staticFile.jpg');
  941. $this->assertEquals($expected, $stream_read, 'Downloaded stream does not seem to match!');
  942. $this->assertEquals($expected, $file_read, 'Downloaded file does not seem to match!');
  943. }
  944. public function testStreamResponseBody()
  945. {
  946. if(!($this->client->getAdapter() instanceof Zend_Http_Client_Adapter_Stream)) {
  947. $this->markTestSkipped('Current adapter does not support streaming');
  948. return;
  949. }
  950. $this->client->setUri($this->baseuri . 'staticFile.jpg');
  951. $this->client->setStream();
  952. $response = $this->client->request();
  953. $this->assertTrue($response instanceof Zend_Http_Response_Stream, 'Request did not return stream response!');
  954. $this->assertTrue(is_resource($response->getStream()), 'Request does not contain stream!');
  955. $body = $response->getBody();
  956. $expected = $this->_getTestFileContents('staticFile.jpg');
  957. $this->assertEquals($expected, $body, 'Downloaded stream does not seem to match!');
  958. }
  959. public function testStreamResponseNamed()
  960. {
  961. if(!($this->client->getAdapter() instanceof Zend_Http_Client_Adapter_Stream)) {
  962. $this->markTestSkipped('Current adapter does not support streaming');
  963. return;
  964. }
  965. $this->client->setUri($this->baseuri . 'staticFile.jpg');
  966. $outfile = tempnam(sys_get_temp_dir(), "outstream");
  967. $this->client->setStream($outfile);
  968. $response = $this->client->request();
  969. $this->assertTrue($response instanceof Zend_Http_Response_Stream, 'Request did not return stream response!');
  970. $this->assertTrue(is_resource($response->getStream()), 'Request does not contain stream!');
  971. $this->assertEquals($outfile, $response->getStreamName());
  972. $stream_read = stream_get_contents($response->getStream());
  973. $file_read = file_get_contents($outfile);
  974. $expected = $this->_getTestFileContents('staticFile.jpg');
  975. $this->assertEquals($expected, $stream_read, 'Downloaded stream does not seem to match!');
  976. $this->assertEquals($expected, $file_read, 'Downloaded file does not seem to match!');
  977. }
  978. public function testStreamRequest()
  979. {
  980. if(!($this->client->getAdapter() instanceof Zend_Http_Client_Adapter_Stream)) {
  981. $this->markTestSkipped('Current adapter does not support streaming');
  982. return;
  983. }
  984. $data = fopen(dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'staticFile.jpg', "r");
  985. $res = $this->client->setRawData($data, 'image/jpeg')->request('PUT');
  986. $expected = $this->_getTestFileContents('staticFile.jpg');
  987. $this->assertEquals($expected, $res->getBody(), 'Response body does not contain the expected data');
  988. }
  989. /**
  990. * Test that we can deal with double Content-Length headers
  991. *
  992. * @link http://framework.zend.com/issues/browse/ZF-9404
  993. */
  994. public function testZF9404DoubleContentLengthHeader()
  995. {
  996. $this->client->setUri($this->baseuri . 'ZF9404-doubleContentLength.php');
  997. $expect = filesize(dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'ZF9404-doubleContentLength.php');
  998. $response = $this->client->request();
  999. if (! $response->isSuccessful()) {
  1000. throw new ErrorException("Error requesting test URL");
  1001. }
  1002. $clen = $response->getHeader('content-length');
  1003. if (! (is_array($clen))) {
  1004. $this->markTestSkipped("Didn't get multiple Content-length headers");
  1005. }
  1006. $this->assertEquals($expect, strlen($response->getBody()));
  1007. }
  1008. /**
  1009. * @group ZF-10645
  1010. */
  1011. public function testPutRequestsHonorSpecifiedContentType()
  1012. {
  1013. $this->client->setUri($this->baseuri . 'ZF10645-PutContentType.php');
  1014. $this->client->setMethod(Zend_Http_Client::PUT);
  1015. $data = array('foo' => 'bar');
  1016. $this->client->setRawData(http_build_query($data), 'text/html; charset=ISO-8859-1');
  1017. $response = $this->client->request();
  1018. $request = $this->client->getLastRequest();
  1019. $this->assertContains('text/html; charset=ISO-8859-1', $request, $request);
  1020. $this->assertContains('REQUEST_METHOD: PUT', $response->getBody(), $response->getBody());
  1021. }
  1022. /**
  1023. * @group ZF-11418
  1024. */
  1025. public function testMultiplePuts()
  1026. {
  1027. $this->client->setUri($this->baseuri . 'ZF10645-PutContentType.php');
  1028. $data= 'test';
  1029. $this->client->setRawData($data, 'text/plain');
  1030. $this->client->setMethod(Zend_Http_Client::PUT);
  1031. $response = $this->client->request();
  1032. $this->assertContains('REQUEST_METHOD: PUT', $response->getBody(), $response->getBody());
  1033. $this->client->resetParameters(true);
  1034. $this->client->setUri($this->baseuri . 'ZF10645-PutContentType.php');
  1035. $this->client->setMethod(Zend_Http_Client::PUT);
  1036. $response = $this->client->request();
  1037. $request= $this->client->getLastRequest();
  1038. $this->assertNotContains('Content-Type: text/plain', $request);
  1039. }
  1040. /**
  1041. * @group ZF-11598
  1042. */
  1043. public function testGetAdapterWithoutSet()
  1044. {
  1045. $client = new Zend_Http_Client(null, $this->config);
  1046. $adapter = $client->getAdapter();
  1047. $this->assertTrue(!empty($adapter));
  1048. }
  1049. /**
  1050. * Internal helpder function to get the contents of test files
  1051. *
  1052. * @param string $file
  1053. * @return string
  1054. */
  1055. protected function _getTestFileContents($file)
  1056. {
  1057. return file_get_contents(dirname(realpath(__FILE__)) . DIRECTORY_SEPARATOR .
  1058. '_files' . DIRECTORY_SEPARATOR . $file);
  1059. }
  1060. /**
  1061. * Data provider for complex, nesting parameter arrays
  1062. *
  1063. * @return array
  1064. */
  1065. static public function parameterArrayProvider()
  1066. {
  1067. return array(
  1068. array(
  1069. array(
  1070. 'quest' => 'To seek the holy grail',
  1071. 'YourMother' => 'Was a hamster',
  1072. 'specialChars' => '<>$+ &?=[]^%',
  1073. 'array' => array('firstItem', 'secondItem', '3rdItem')
  1074. )
  1075. ),
  1076. array(
  1077. array(
  1078. 'someData' => array(
  1079. "1",
  1080. "2",
  1081. 'key' => 'value',
  1082. 'nesting' => array(
  1083. 'a' => 'AAA',
  1084. 'b' => 'BBB'
  1085. )
  1086. ),
  1087. 'someOtherData' => array('foo', 'bar')
  1088. )
  1089. ),
  1090. array(
  1091. array(
  1092. 'foo1' => 'bar',
  1093. 'foo2' => array('baz', 'w00t')
  1094. )
  1095. )
  1096. );
  1097. }
  1098. /**
  1099. * Data provider for invalid configuration containers
  1100. *
  1101. * @return array
  1102. */
  1103. static public function invalidConfigProvider()
  1104. {
  1105. return array(
  1106. array(false),
  1107. array('foo => bar'),
  1108. array(null),
  1109. array(new stdClass),
  1110. array(55)
  1111. );
  1112. }
  1113. }