userstreams.phpt 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. --TEST--
  2. User-space streams
  3. --FILE--
  4. <?php
  5. /* This is a fairly aggressive test that looks at
  6. * user streams and also gives the seek/gets/buffer
  7. * layer of streams a thorough testing */
  8. $lyrics = <<<EOD
  9. ...and the road becomes my bride
  10. I have stripped of all but pride
  11. so in her I do confide
  12. and she keeps me satisfied
  13. gives me all I need
  14. ...and with dust in throat I crave
  15. to the game you stay a slave
  16. rover wanderer
  17. nomad vagabond
  18. call me what you will
  19. But Ill take my time anywhere
  20. Free to speak my mind anywhere
  21. and Ill redefine anywhere
  22. Anywhere I roam
  23. Where I lay my head is home
  24. ...and the earth becomes my throne
  25. I adapt to the unknown
  26. under wandering stars Ive grown
  27. by myself but not alone
  28. I ask no one
  29. ...and my ties are severed clean
  30. the less I have the more I gain
  31. off the beaten path I reign
  32. rover wanderer
  33. nomad vagabond
  34. call me what you will
  35. But Ill take my time anywhere
  36. Free to speak my mind anywhere
  37. and Ill never mind anywhere
  38. Anywhere I roam
  39. Where I lay my head is home
  40. But Ill take my time anywhere
  41. Free to speak my mind anywhere
  42. and Ill take my find anywhere
  43. Anywhere I roam
  44. Where I lay my head is home
  45. carved upon my stone
  46. my body lie but still I roam
  47. Wherever I may roam.
  48. Wherever I May Roam
  49. EOD;
  50. /* repeat the data a few times so that it grows larger than
  51. * the default cache chunk size and that we have something
  52. * to seek around... */
  53. $DATA = "";
  54. for ($i = 0; $i < 30; $i++) {
  55. if ($i % 2 == 0)
  56. $DATA .= str_rot13($lyrics);
  57. else
  58. $DATA .= $lyrics;
  59. }
  60. /* store the data in a regular file so that we can compare
  61. * the results */
  62. $tf = tmpfile();
  63. fwrite($tf, $DATA);
  64. $n = ftell($tf);
  65. rewind($tf) or die("failed to rewind tmp file!");
  66. if (ftell($tf) != 0)
  67. die("tmpfile is not at start!");
  68. $DATALEN = strlen($DATA);
  69. if ($n != $DATALEN)
  70. die("tmpfile stored $n bytes; should be $DATALEN!");
  71. class uselessstream
  72. {
  73. }
  74. class mystream
  75. {
  76. public $path;
  77. public $mode;
  78. public $options;
  79. public $position;
  80. public $varname;
  81. function stream_open($path, $mode, $options, &$opened_path)
  82. {
  83. $this->path = $path;
  84. $this->mode = $mode;
  85. $this->options = $options;
  86. $split = parse_url($path);
  87. $this->varname = $split["host"];
  88. if (strchr($mode, 'a'))
  89. $this->position = strlen($GLOBALS[$this->varname]);
  90. else
  91. $this->position = 0;
  92. return true;
  93. }
  94. function stream_read($count)
  95. {
  96. $ret = substr($GLOBALS[$this->varname], $this->position, $count);
  97. $this->position += strlen($ret);
  98. return $ret;
  99. }
  100. function stream_tell()
  101. {
  102. return $this->position;
  103. }
  104. function stream_eof()
  105. {
  106. return $this->position >= strlen($GLOBALS[$this->varname]);
  107. }
  108. function stream_seek($offset, $whence)
  109. {
  110. switch($whence) {
  111. case SEEK_SET:
  112. if ($offset < strlen($GLOBALS[$this->varname]) && $offset >= 0) {
  113. $this->position = $offset;
  114. return true;
  115. } else {
  116. return false;
  117. }
  118. break;
  119. case SEEK_CUR:
  120. if ($offset >= 0) {
  121. $this->position += $offset;
  122. return true;
  123. } else {
  124. return false;
  125. }
  126. break;
  127. case SEEK_END:
  128. if (strlen($GLOBALS[$this->varname]) + $offset >= 0) {
  129. $this->position = strlen($GLOBALS[$this->varname]) + $offset;
  130. return true;
  131. } else {
  132. return false;
  133. }
  134. break;
  135. default:
  136. return false;
  137. }
  138. }
  139. }
  140. try {
  141. stream_wrapper_register("bogus", "class_not_exist");
  142. die("Registered a non-existent class!!!???");
  143. } catch (\TypeError $e) {
  144. echo $e->getMessage() . \PHP_EOL;
  145. }
  146. echo "Not Registered\n";
  147. if (!stream_wrapper_register("test", "mystream")) {
  148. die("test wrapper registration failed");
  149. }
  150. echo "Registered\n";
  151. if (!stream_wrapper_register("bogon", "uselessstream")) {
  152. die("bogon wrapper registration failed");
  153. }
  154. echo "Registered\n";
  155. $b = @fopen("bogon://url", "rb");
  156. if (is_resource($b)) {
  157. die("Opened a bogon??");
  158. }
  159. $fp = fopen("test://DATA", "rb");
  160. if (!$fp || !is_resource($fp)) {
  161. die("Failed to open resource");
  162. }
  163. /* some default seeks that will cause buffer/cache misses */
  164. $seeks = array(
  165. array(SEEK_SET, 0, 0),
  166. array(SEEK_CUR, 8450, 8450),
  167. array(SEEK_CUR, -7904, 546),
  168. array(SEEK_CUR, 12456, 13002),
  169. /* end up at BOF so that randomly generated seek offsets
  170. * below will know where they are supposed to be */
  171. array(SEEK_SET, 0, 0)
  172. );
  173. $whence_map = array(
  174. SEEK_CUR,
  175. SEEK_SET,
  176. SEEK_END
  177. );
  178. $whence_names = array(
  179. SEEK_CUR => "SEEK_CUR",
  180. SEEK_SET => "SEEK_SET",
  181. SEEK_END => "SEEK_END"
  182. );
  183. /* generate some random seek offsets */
  184. $position = 0;
  185. for ($i = 0; $i < 256; $i++) {
  186. $whence = $whence_map[array_rand($whence_map, 1)];
  187. switch($whence) {
  188. case SEEK_SET:
  189. $offset = rand(0, $DATALEN - 1);
  190. $position = $offset;
  191. break;
  192. case SEEK_END:
  193. $offset = -rand(0, $DATALEN - 1);
  194. $position = $DATALEN + $offset;
  195. break;
  196. case SEEK_CUR:
  197. $offset = rand(0, $DATALEN - 1);
  198. $offset -= $position;
  199. $position += $offset;
  200. break;
  201. }
  202. $seeks[] = array($whence, $offset, $position);
  203. }
  204. /* we compare the results of fgets using differing line lengths to
  205. * test the fgets layer also */
  206. $line_lengths = array(1024, 256, 64, 16);
  207. $fail_count = 0;
  208. ob_start();
  209. foreach($line_lengths as $line_length) {
  210. /* now compare the real stream with the user stream */
  211. $j = 0;
  212. rewind($tf);
  213. rewind($fp);
  214. foreach($seeks as $seekdata) {
  215. list($whence, $offset, $position) = $seekdata;
  216. $rpb = ftell($tf);
  217. $rr = (int)fseek($tf, $offset, $whence);
  218. $rpa = ftell($tf);
  219. $rline = fgets($tf, $line_length);
  220. (int)fseek($tf, - strlen($rline), SEEK_CUR);
  221. $upb = ftell($fp);
  222. $ur = (int)fseek($fp, $offset, $whence);
  223. $upa = ftell($fp);
  224. $uline = fgets($fp, $line_length);
  225. (int)fseek($fp, - strlen($uline), SEEK_CUR);
  226. printf("\n--[%d] whence=%s offset=%d line_length=%d position_should_be=%d --\n",
  227. $j, $whence_names[$whence], $offset, $line_length, $position);
  228. printf("REAL: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $rpb, $rpa, ftell($tf), $rr, strlen($rline), $rline);
  229. printf("USER: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $upb, $upa, ftell($fp), $ur, strlen($uline), $uline);
  230. if ($rr != $ur || $rline != $uline || $rpa != $position || $upa != $position) {
  231. $fail_count++;
  232. echo "###################################### FAIL!\n";
  233. $dat = stream_get_meta_data($fp);
  234. var_dump($dat);
  235. break;
  236. }
  237. $j++;
  238. }
  239. if ($fail_count)
  240. break;
  241. }
  242. if ($fail_count == 0) {
  243. ob_end_clean();
  244. echo "SEEK: OK\n";
  245. } else {
  246. echo "SEEK: FAIL\n";
  247. ob_end_flush();
  248. }
  249. $fail_count = 0;
  250. fseek($fp, $DATALEN / 2, SEEK_SET);
  251. fseek($tf, $DATALEN / 2, SEEK_SET);
  252. if (ftell($fp) != ftell($tf)) {
  253. echo "SEEK: positions do not match!\n";
  254. }
  255. $n = 0;
  256. while(!feof($fp)) {
  257. $uline = fgets($fp, 1024);
  258. $rline = fgets($tf, 1024);
  259. if ($uline != $rline) {
  260. echo "FGETS: FAIL\niter=$n user=$uline [pos=" . ftell($fp) . "]\nreal=$rline [pos=" . ftell($tf) . "]\n";
  261. $fail_count++;
  262. break;
  263. }
  264. }
  265. if ($fail_count == 0) {
  266. echo "FGETS: OK\n";
  267. }
  268. /* One final test to see if the position is respected when opened for append */
  269. $fp = fopen("test://lyrics", "a+");
  270. rewind($fp);
  271. var_dump(ftell($fp));
  272. $data = fgets($fp);
  273. fclose($fp);
  274. echo $data . "\n";
  275. ?>
  276. --EXPECT--
  277. stream_wrapper_register(): Argument #2 ($class) must be a valid class name, class_not_exist given
  278. Not Registered
  279. Registered
  280. Registered
  281. SEEK: OK
  282. FGETS: OK
  283. int(0)
  284. ...and the road becomes my bride