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 159try { 160 stream_wrapper_register("bogus", "class_not_exist"); 161 die("Registered a non-existent class!!!???"); 162} catch (\TypeError $e) { 163 echo $e->getMessage() . \PHP_EOL; 164} 165echo "Not Registered\n"; 166 167if (!stream_wrapper_register("test", "mystream")) { 168 die("test wrapper registration failed"); 169} 170echo "Registered\n"; 171 172if (!stream_wrapper_register("bogon", "uselessstream")) { 173 die("bogon wrapper registration failed"); 174} 175echo "Registered\n"; 176 177$b = @fopen("bogon://url", "rb"); 178if (is_resource($b)) { 179 die("Opened a bogon??"); 180} 181 182$fp = fopen("test://DATA", "rb"); 183if (!$fp || !is_resource($fp)) { 184 die("Failed to open resource"); 185} 186 187/* some default seeks that will cause buffer/cache misses */ 188$seeks = array( 189 array(SEEK_SET, 0, 0), 190 array(SEEK_CUR, 8450, 8450), 191 array(SEEK_CUR, -7904, 546), 192 array(SEEK_CUR, 12456, 13002), 193 194 /* end up at BOF so that randomly generated seek offsets 195 * below will know where they are supposed to be */ 196 array(SEEK_SET, 0, 0) 197); 198 199$whence_map = array( 200 SEEK_CUR, 201 SEEK_SET, 202 SEEK_END 203); 204$whence_names = array( 205 SEEK_CUR => "SEEK_CUR", 206 SEEK_SET => "SEEK_SET", 207 SEEK_END => "SEEK_END" 208 ); 209 210/* generate some random seek offsets */ 211$position = 0; 212for ($i = 0; $i < 256; $i++) { 213 $whence = $whence_map[array_rand($whence_map, 1)]; 214 switch($whence) { 215 case SEEK_SET: 216 $offset = rand(0, $DATALEN - 1); 217 $position = $offset; 218 break; 219 case SEEK_END: 220 $offset = -rand(0, $DATALEN - 1); 221 $position = $DATALEN + $offset; 222 break; 223 case SEEK_CUR: 224 $offset = rand(0, $DATALEN - 1); 225 $offset -= $position; 226 $position += $offset; 227 break; 228 } 229 230 $seeks[] = array($whence, $offset, $position); 231} 232 233/* we compare the results of fgets using differing line lengths to 234 * test the fgets layer also */ 235$line_lengths = array(1024, 256, 64, 16); 236$fail_count = 0; 237 238ob_start(); 239foreach($line_lengths as $line_length) { 240 /* now compare the real stream with the user stream */ 241 $j = 0; 242 rewind($tf); 243 rewind($fp); 244 foreach($seeks as $seekdata) { 245 list($whence, $offset, $position) = $seekdata; 246 247 $rpb = ftell($tf); 248 $rr = (int)fseek($tf, $offset, $whence); 249 $rpa = ftell($tf); 250 $rline = fgets($tf, $line_length); 251 (int)fseek($tf, - strlen($rline), SEEK_CUR); 252 253 $upb = ftell($fp); 254 $ur = (int)fseek($fp, $offset, $whence); 255 $upa = ftell($fp); 256 $uline = fgets($fp, $line_length); 257 (int)fseek($fp, - strlen($uline), SEEK_CUR); 258 259 printf("\n--[%d] whence=%s offset=%d line_length=%d position_should_be=%d --\n", 260 $j, $whence_names[$whence], $offset, $line_length, $position); 261 printf("REAL: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $rpb, $rpa, ftell($tf), $rr, strlen($rline), $rline); 262 printf("USER: pos=(%d,%d,%d) ret=%d line[%d]=`%s'\n", $upb, $upa, ftell($fp), $ur, strlen($uline), $uline); 263 264 if ($rr != $ur || $rline != $uline || $rpa != $position || $upa != $position) { 265 $fail_count++; 266 echo "###################################### FAIL!\n"; 267 $dat = stream_get_meta_data($fp); 268 var_dump($dat); 269 break; 270 } 271 272 $j++; 273 } 274 if ($fail_count) 275 break; 276} 277 278if ($fail_count == 0) { 279 ob_end_clean(); 280 echo "SEEK: OK\n"; 281} else { 282 echo "SEEK: FAIL\n"; 283 ob_end_flush(); 284} 285 286$fail_count = 0; 287 288fseek($fp, $DATALEN / 2, SEEK_SET); 289fseek($tf, $DATALEN / 2, SEEK_SET); 290 291if (ftell($fp) != ftell($tf)) { 292 echo "SEEK: positions do not match!\n"; 293} 294 295$n = 0; 296while(!feof($fp)) { 297 $uline = fgets($fp, 1024); 298 $rline = fgets($tf, 1024); 299 300 if ($uline != $rline) { 301 echo "FGETS: FAIL\niter=$n user=$uline [pos=" . ftell($fp) . "]\nreal=$rline [pos=" . ftell($tf) . "]\n"; 302 $fail_count++; 303 break; 304 } 305} 306 307if ($fail_count == 0) { 308 echo "FGETS: OK\n"; 309} 310 311/* One final test to see if the position is respected when opened for append */ 312$fp = fopen("test://lyrics", "a+"); 313rewind($fp); 314var_dump(ftell($fp)); 315$data = fgets($fp); 316fclose($fp); 317echo $data . "\n"; 318 319?> 320--EXPECT-- 321stream_wrapper_register(): Argument #2 ($class) must be a valid class name, class_not_exist given 322Not Registered 323Registered 324Registered 325SEEK: OK 326FGETS: OK 327int(0) 328...and the road becomes my bride 329