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