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