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