Skip to content

Commit d68c9a6

Browse files
alexisbouchezclaude
andcommitted
feat: implement Batch 13 — INI, streams, sessions, output buffering (14 items, 30 new tests)
- ini_set() permission validation, ini_get_all() with real directive data - session_cache_limiter/expire, session_create_id with prefix, cookie params - stream_context_create/get_options/get_params/set_option with real storage - php://filter stream wrapper (base64, rot13, toupper, tolower, strip_tags, qp) - data:// stream wrapper (plain text and base64) - stream_get_meta_data with real eof/mode, stream_copy_to_stream - ob_implicit_flush, ob_gzhandler with Content-Encoding headers - Expanded MIME types (55+), directory index candidates (index.htm) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 728cae8 commit d68c9a6

File tree

7 files changed

+1178
-83
lines changed

7 files changed

+1178
-83
lines changed

TODO.txt

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ These are organized by the categories with the most missing functions.
394394
[ ] 7C.01 fgetss — fgets + strip tags (deprecated PHP 7.3, removed 8.0)
395395
[x] 7C.02 stream_get_contents — read remaining stream to string
396396
- Already implemented in builtins/file.rs; verified with test
397-
[ ] 7C.03 stream_context_create / stream_context_set_option
397+
[x] 7C.03 stream_context_create / stream_context_set_option
398398
[ ] 7C.04 stream_filter_append / stream_filter_prepend
399399
[ ] 7C.05 stream_socket_client / stream_socket_server
400400
[ ] 7C.06 stream_select (multiplex I/O)
@@ -700,34 +700,34 @@ PHASE 9: RUNTIME FEATURES
700700
[ ] 9B.02 ob_get_level() — return current nesting depth
701701
[ ] 9B.03 ob_get_flush() — flush and return contents
702702
[ ] 9B.04 ob_list_handlers() — list active handlers
703-
[ ] 9B.05 ob_implicit_flush() — auto-flush after output
704-
[ ] 9B.06 Output handler for gzip compression (ob_gzhandler)
703+
[x] 9B.05 ob_implicit_flush() — auto-flush after output
704+
[x] 9B.06 Output handler for gzip compression (ob_gzhandler)
705705

706706
--- 9C: INI System ---
707707
[ ] 9C.01 php.ini file parsing (not just -d CLI flags)
708708
[ ] 9C.02 .user.ini / .htaccess per-directory overrides
709-
[ ] 9C.03 ini_set() permission level validation (PHP_INI_USER vs PHP_INI_SYSTEM)
709+
[x] 9C.03 ini_set() permission level validation (PHP_INI_USER vs PHP_INI_SYSTEM)
710710
[ ] 9C.04 Register remaining ~180 standard directives
711-
[ ] 9C.05 ini_get_all() — return all directives with access levels
711+
[x] 9C.05 ini_get_all() — return all directives with access levels
712712

713713
--- 9D: Sessions ---
714714
[ ] 9D.01 session_regenerate_id() — prevent session fixation
715715
[ ] 9D.02 session_set_save_handler() — custom save handlers
716716
[ ] 9D.03 SessionHandlerInterface implementation
717-
[ ] 9D.04 session_cache_limiter() / session_cache_expire()
718-
[ ] 9D.05 session_create_id() — custom ID generation
719-
[ ] 9D.06 Session cookie parameters (SameSite, HttpOnly, Secure)
717+
[x] 9D.04 session_cache_limiter() / session_cache_expire()
718+
[x] 9D.05 session_create_id() — custom ID generation
719+
[x] 9D.06 Session cookie parameters (SameSite, HttpOnly, Secure)
720720

721721
--- 9E: Streams ---
722722
[ ] 9E.01 Stream context creation and option management
723723
[ ] 9E.02 Stream filters (convert.*, string.*, zlib.*, etc.)
724724
[ ] 9E.03 Custom stream wrappers via stream_wrapper_register
725-
[ ] 9E.04 php://filter stream wrapper
726-
[ ] 9E.05 data:// stream wrapper
725+
[x] 9E.04 php://filter stream wrapper
726+
[x] 9E.05 data:// stream wrapper
727727
[ ] 9E.06 glob:// stream wrapper
728728
[ ] 9E.07 Stream notification callbacks
729-
[ ] 9E.08 stream_get_meta_data — metadata access
730-
[ ] 9E.09 stream_copy_to_stream — efficient copy
729+
[x] 9E.08 stream_get_meta_data — metadata access
730+
[x] 9E.09 stream_copy_to_stream — efficient copy
731731

732732

733733
================================================================================
@@ -755,10 +755,10 @@ PHASE 10: SAPI COMPLETENESS
755755

756756
--- 10C: Built-in Web Server (-S) ---
757757
[ ] 10C.01 Router script support (-S with .php router file)
758-
[ ] 10C.02 Static file serving (MIME type detection)
758+
[x] 10C.02 Static file serving (MIME type detection)
759759
[ ] 10C.03 Concurrent request handling
760760
[ ] 10C.04 Access logging
761-
[ ] 10C.05 Directory index (index.php, index.html)
761+
[x] 10C.05 Directory index (index.php, index.html)
762762

763763

764764
================================================================================

crates/php-rs-sapi-cli/src/server.rs

Lines changed: 56 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,19 +36,59 @@ fn mime_type(path: &str) -> &'static str {
3636
match ext {
3737
"html" | "htm" => "text/html; charset=UTF-8",
3838
"css" => "text/css",
39-
"js" => "application/javascript",
39+
"js" | "mjs" => "application/javascript",
4040
"json" => "application/json",
4141
"xml" => "application/xml",
42+
"xhtml" => "application/xhtml+xml",
43+
"rss" => "application/rss+xml",
44+
"atom" => "application/atom+xml",
4245
"png" => "image/png",
4346
"jpg" | "jpeg" => "image/jpeg",
4447
"gif" => "image/gif",
45-
"svg" => "image/svg+xml",
48+
"webp" => "image/webp",
49+
"avif" => "image/avif",
50+
"bmp" => "image/bmp",
51+
"tiff" | "tif" => "image/tiff",
52+
"svg" | "svgz" => "image/svg+xml",
4653
"ico" => "image/x-icon",
47-
"woff" | "woff2" => "font/woff2",
54+
"woff" => "font/woff",
55+
"woff2" => "font/woff2",
4856
"ttf" => "font/ttf",
57+
"otf" => "font/otf",
58+
"eot" => "application/vnd.ms-fontobject",
4959
"pdf" => "application/pdf",
50-
"txt" => "text/plain; charset=UTF-8",
60+
"txt" | "text" | "log" => "text/plain; charset=UTF-8",
61+
"csv" => "text/csv; charset=UTF-8",
62+
"tsv" => "text/tab-separated-values; charset=UTF-8",
63+
"md" | "markdown" => "text/markdown; charset=UTF-8",
64+
"yaml" | "yml" => "text/yaml; charset=UTF-8",
5165
"php" => "text/html; charset=UTF-8",
66+
"zip" => "application/zip",
67+
"gz" | "gzip" => "application/gzip",
68+
"tar" => "application/x-tar",
69+
"bz2" => "application/x-bzip2",
70+
"7z" => "application/x-7z-compressed",
71+
"rar" => "application/vnd.rar",
72+
"mp3" => "audio/mpeg",
73+
"mp4" => "video/mp4",
74+
"webm" => "video/webm",
75+
"ogg" => "audio/ogg",
76+
"ogv" => "video/ogg",
77+
"wav" => "audio/wav",
78+
"flac" => "audio/flac",
79+
"avi" => "video/x-msvideo",
80+
"mov" => "video/quicktime",
81+
"mkv" => "video/x-matroska",
82+
"wasm" => "application/wasm",
83+
"map" => "application/json",
84+
"swf" => "application/x-shockwave-flash",
85+
"doc" => "application/msword",
86+
"docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
87+
"xls" => "application/vnd.ms-excel",
88+
"xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
89+
"ppt" => "application/vnd.ms-powerpoint",
90+
"pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
91+
"rtf" => "application/rtf",
5292
_ => "application/octet-stream",
5393
}
5494
}
@@ -464,15 +504,19 @@ fn handle_connection(stream: &mut std::net::TcpStream, config: &ServerConfig) {
464504
// Resolve the file path
465505
let mut file_path = config.docroot.join(req.path.trim_start_matches('/'));
466506

467-
// If path is a directory, look for index.php or index.html
507+
// If path is a directory, look for index files (PHP priority, then HTML)
468508
if file_path.is_dir() {
469-
let index_php = file_path.join("index.php");
470-
let index_html = file_path.join("index.html");
471-
if index_php.exists() {
472-
file_path = index_php;
473-
} else if index_html.exists() {
474-
file_path = index_html;
475-
} else {
509+
let candidates = ["index.php", "index.html", "index.htm"];
510+
let mut found = false;
511+
for name in &candidates {
512+
let candidate = file_path.join(name);
513+
if candidate.exists() {
514+
file_path = candidate;
515+
found = true;
516+
break;
517+
}
518+
}
519+
if !found {
476520
send_404(stream, &req.path);
477521
return;
478522
}

0 commit comments

Comments
 (0)