From f05e1a55cf0d44e9611e06a3d7b4fccdcb90ce7b Mon Sep 17 00:00:00 2001 From: nsfisis Date: Sun, 21 Jun 2026 17:42:32 +0900 Subject: refactor(php-shim): split filter_var into per-filter functions Replace the dispatch-on-constant filter_var() and filter_var_with_options() with dedicated filter_var_boolean/url/email/ip and filter_var_int_with_range, dropping the FILTER_VALIDATE_* constants and updating all call sites. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../symfony/console/command/complete_command.rs | 3 +- crates/shirabe-php-shim/src/filter.rs | 56 ++++++++++------------ crates/shirabe/src/autoload/class_loader.rs | 8 ++-- crates/shirabe/src/command/diagnose_command.rs | 40 +++++----------- crates/shirabe/src/command/home_command.rs | 4 +- crates/shirabe/src/command/init_command.rs | 17 +++---- crates/shirabe/src/config.rs | 10 ++-- .../src/package/loader/validating_array_loader.rs | 12 ++--- crates/shirabe/src/util/error_handler.rs | 8 ++-- crates/shirabe/src/util/no_proxy_pattern.rs | 23 ++------- crates/shirabe/src/util/remote_filesystem.rs | 15 +++--- 11 files changed, 76 insertions(+), 120 deletions(-) (limited to 'crates') diff --git a/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs b/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs index 80186a3..c8e6b6e 100644 --- a/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs +++ b/crates/shirabe-external-packages/src/symfony/console/command/complete_command.rs @@ -208,9 +208,8 @@ impl Command for CompleteCommand { output: Rc>, ) -> anyhow::Result<()> { let _ = (input, output); - self.is_debug = shirabe_php_shim::filter_var( + self.is_debug = shirabe_php_shim::filter_var_boolean( &shirabe_php_shim::getenv("SYMFONY_COMPLETION_DEBUG").unwrap_or_default(), - shirabe_php_shim::FILTER_VALIDATE_BOOLEAN, ); Ok(()) diff --git a/crates/shirabe-php-shim/src/filter.rs b/crates/shirabe-php-shim/src/filter.rs index a23752b..bd1a6fa 100644 --- a/crates/shirabe-php-shim/src/filter.rs +++ b/crates/shirabe-php-shim/src/filter.rs @@ -1,39 +1,31 @@ -use crate::PhpMixed; -use indexmap::IndexMap; +// Without FILTER_NULL_ON_FAILURE, php_filter_boolean trims surrounding +// whitespace, lowercases, and yields true only for "1"/"true"/"on"/"yes"; +// every other input (including the "0"/"false"/"off"/"no"/"" set) yields +// false. +pub fn filter_var_boolean(value: &str) -> bool { + let trimmed = value.trim_matches([' ', '\t', '\n', '\r', '\0', '\x0B']); + matches!( + trimmed.to_ascii_lowercase().as_str(), + "1" | "true" | "on" | "yes" + ) +} -pub const FILTER_VALIDATE_EMAIL: i64 = 274; +// TODO(phase-c): PHP's FILTER_VALIDATE_URL parses with php_url_parse_ex and +// additionally validates the host as a domain/IPv6 literal. reqwest::Url +// (WHATWG/RFC 3986) is stricter on some inputs and more lenient on others, +// so this is not a byte-for-byte compatible validator. +pub fn filter_var_url(value: &str) -> bool { + reqwest::Url::parse(value).is_ok() +} -pub const FILTER_VALIDATE_BOOLEAN: i64 = 258; -pub const FILTER_VALIDATE_URL: i64 = 273; -pub const FILTER_VALIDATE_IP: i64 = 275; -pub const FILTER_VALIDATE_INT: i64 = 257; +pub fn filter_var_email(_value: &str) -> bool { + todo!() +} -pub fn filter_var(value: &str, filter: i64) -> bool { - match filter { - // Without FILTER_NULL_ON_FAILURE, php_filter_boolean trims surrounding - // whitespace, lowercases, and yields true only for "1"/"true"/"on"/"yes"; - // every other input (including the "0"/"false"/"off"/"no"/"" set) yields - // false. - FILTER_VALIDATE_BOOLEAN => { - let trimmed = value.trim_matches([' ', '\t', '\n', '\r', '\0', '\x0B']); - matches!( - trimmed.to_ascii_lowercase().as_str(), - "1" | "true" | "on" | "yes" - ) - } - // TODO(phase-c): PHP's FILTER_VALIDATE_URL parses with php_url_parse_ex and - // additionally validates the host as a domain/IPv6 literal. reqwest::Url - // (WHATWG/RFC 3986) is stricter on some inputs and more lenient on others, - // so this is not a byte-for-byte compatible validator. - FILTER_VALIDATE_URL => reqwest::Url::parse(value).is_ok(), - _ => todo!(), - } +pub fn filter_var_ip(_value: &str) -> bool { + todo!() } -pub fn filter_var_with_options( - _value: &str, - _filter: i64, - _options: &IndexMap, -) -> PhpMixed { +pub fn filter_var_int_with_range(_value: &str, _min: i64, _max: i64) -> bool { todo!() } diff --git a/crates/shirabe/src/autoload/class_loader.rs b/crates/shirabe/src/autoload/class_loader.rs index 73b5c06..daa5d97 100644 --- a/crates/shirabe/src/autoload/class_loader.rs +++ b/crates/shirabe/src/autoload/class_loader.rs @@ -4,10 +4,10 @@ use indexmap::IndexMap; use std::sync::{LazyLock, Mutex}; use shirabe_php_shim::{ - DIRECTORY_SEPARATOR, FILTER_VALIDATE_BOOLEAN, InvalidArgumentException, PhpMixed, array_merge, - array_values, call_user_func_array, defined, file_exists, filter_var, function_exists, - include_file, ini_get, spl_autoload_register, spl_autoload_unregister, - stream_resolve_include_path, strlen, strpos, strrpos, strtr, substr, + DIRECTORY_SEPARATOR, InvalidArgumentException, PhpMixed, array_merge, array_values, + call_user_func_array, defined, file_exists, function_exists, include_file, ini_get, + spl_autoload_register, spl_autoload_unregister, stream_resolve_include_path, strlen, strpos, + strrpos, strtr, substr, }; /// @var array diff --git a/crates/shirabe/src/command/diagnose_command.rs b/crates/shirabe/src/command/diagnose_command.rs index 43e878d..4b1c241 100644 --- a/crates/shirabe/src/command/diagnose_command.rs +++ b/crates/shirabe/src/command/diagnose_command.rs @@ -10,14 +10,14 @@ use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; use shirabe_external_packages::symfony::process::ExecutableFinder; use shirabe_php_shim::{ - CURL_HTTP_VERSION_2_0, CURL_VERSION_HTTP2, CURL_VERSION_HTTP3, CURL_VERSION_ZSTD, - FILTER_VALIDATE_BOOLEAN, INFO_GENERAL, InvalidArgumentException, OPENSSL_VERSION_NUMBER, - OPENSSL_VERSION_TEXT, PHP_BINARY, PHP_EOL, PHP_VERSION, PHP_VERSION_ID, - PHP_WINDOWS_VERSION_BUILD, PhpMixed, RuntimeException, count, curl_version, defined, - disk_free_space, extension_loaded, file_exists, filter_var, function_exists, get_class, - get_class_err, hash, implode, ini_get, ioncube_loader_iversion, ioncube_loader_version, - is_array, is_string, key, ob_get_clean, ob_start, phpinfo, reset, rtrim, sprintf, str_contains, - str_replace, str_starts_with, strpos, strstr, strtolower, trim, version_compare, + CURL_HTTP_VERSION_2_0, CURL_VERSION_HTTP2, CURL_VERSION_HTTP3, CURL_VERSION_ZSTD, INFO_GENERAL, + InvalidArgumentException, OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_TEXT, PHP_BINARY, PHP_EOL, + PHP_VERSION, PHP_VERSION_ID, PHP_WINDOWS_VERSION_BUILD, PhpMixed, RuntimeException, count, + curl_version, defined, disk_free_space, extension_loaded, file_exists, filter_var_boolean, + function_exists, get_class, get_class_err, hash, implode, ini_get, ioncube_loader_iversion, + ioncube_loader_version, is_array, is_string, key, ob_get_clean, ob_start, phpinfo, reset, + rtrim, sprintf, str_contains, str_replace, str_starts_with, strpos, strstr, strtolower, trim, + version_compare, }; use std::cell::RefCell; use std::rc::Rc; @@ -1177,10 +1177,7 @@ impl DiagnoseCommand { errors.insert("iconv_mbstring".to_string(), PhpMixed::Bool(true)); } - if !filter_var( - ini_get("allow_url_fopen").as_deref().unwrap_or(""), - FILTER_VALIDATE_BOOLEAN, - ) { + if !filter_var_boolean(ini_get("allow_url_fopen").as_deref().unwrap_or("")) { errors.insert("allow_url_fopen".to_string(), PhpMixed::Bool(true)); } @@ -1205,10 +1202,7 @@ impl DiagnoseCommand { if !defined("HHVM_VERSION") && !extension_loaded("apcu") - && filter_var( - ini_get("apc.enable_cli").as_deref().unwrap_or(""), - FILTER_VALIDATE_BOOLEAN, - ) + && filter_var_boolean(ini_get("apc.enable_cli").as_deref().unwrap_or("")) { warnings.insert("apc_cli".to_string(), PhpMixed::Bool(true)); } @@ -1243,10 +1237,7 @@ impl DiagnoseCommand { } } - if filter_var( - ini_get("xdebug.profiler_enabled").as_deref().unwrap_or(""), - FILTER_VALIDATE_BOOLEAN, - ) { + if filter_var_boolean(ini_get("xdebug.profiler_enabled").as_deref().unwrap_or("")) { warnings.insert("xdebug_profile".to_string(), PhpMixed::Bool(true)); } else if XdebugHandler::is_xdebug_active() { warnings.insert("xdebug_loaded".to_string(), PhpMixed::Bool(true)); @@ -1265,13 +1256,8 @@ impl DiagnoseCommand { } if extension_loaded("uopz") - && !(filter_var( - ini_get("uopz.disable").as_deref().unwrap_or(""), - FILTER_VALIDATE_BOOLEAN, - ) || filter_var( - ini_get("uopz.exit").as_deref().unwrap_or(""), - FILTER_VALIDATE_BOOLEAN, - )) + && !(filter_var_boolean(ini_get("uopz.disable").as_deref().unwrap_or("")) + || filter_var_boolean(ini_get("uopz.exit").as_deref().unwrap_or(""))) { warnings.insert("uopz".to_string(), PhpMixed::Bool(true)); } diff --git a/crates/shirabe/src/command/home_command.rs b/crates/shirabe/src/command/home_command.rs index 1620205..5b05815 100644 --- a/crates/shirabe/src/command/home_command.rs +++ b/crates/shirabe/src/command/home_command.rs @@ -6,7 +6,7 @@ use shirabe_external_packages::symfony::console::command::command::Command; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; use shirabe_php_shim::PhpMixed; -use shirabe_php_shim::{FILTER_VALIDATE_URL, filter_var}; +use shirabe_php_shim::filter_var_url; use std::cell::RefCell; use std::rc::Rc; @@ -73,7 +73,7 @@ impl HomeCommand { Some(u) => u, }; - if !filter_var(&url, FILTER_VALIDATE_URL) { + if !filter_var_url(&url) { return false; } diff --git a/crates/shirabe/src/command/init_command.rs b/crates/shirabe/src/command/init_command.rs index f58952e..22cd75b 100644 --- a/crates/shirabe/src/command/init_command.rs +++ b/crates/shirabe/src/command/init_command.rs @@ -12,11 +12,11 @@ use shirabe_external_packages::symfony::console::input::ArrayInput; use shirabe_external_packages::symfony::console::input::InputInterface; use shirabe_external_packages::symfony::console::output::OutputInterface; use shirabe_php_shim::{ - FILE_IGNORE_NEW_LINES, FILTER_VALIDATE_EMAIL, InvalidArgumentException, PHP_EOL, PhpMixed, - array_filter, array_flip, array_flip_strings, array_intersect_key, array_keys, array_map, - basename, empty, explode, file, file_exists, file_get_contents, file_put_contents, - function_exists, get_current_user, implode, is_dir, is_string, preg_quote, realpath, - server_get, sprintf, str_replace, strpos, strtolower, trim, ucwords, + FILE_IGNORE_NEW_LINES, InvalidArgumentException, PHP_EOL, PhpMixed, array_filter, array_flip, + array_flip_strings, array_intersect_key, array_keys, array_map, basename, empty, explode, file, + file_exists, file_get_contents, file_put_contents, function_exists, get_current_user, implode, + is_dir, is_string, preg_quote, realpath, server_get, sprintf, str_replace, strpos, strtolower, + trim, ucwords, }; use std::cell::RefCell; use std::rc::Rc; @@ -1076,12 +1076,7 @@ impl InitCommand { } pub(crate) fn is_valid_email(&self, email: &str) -> bool { - // assume it's valid if we can't validate it - if !function_exists("filter_var") { - return true; - } - - shirabe_php_shim::filter_var(email, FILTER_VALIDATE_EMAIL) + shirabe_php_shim::filter_var_email(email) } fn update_dependencies(&self, output: std::rc::Rc>) { diff --git a/crates/shirabe/src/config.rs b/crates/shirabe/src/config.rs index b65961e..bb46483 100644 --- a/crates/shirabe/src/config.rs +++ b/crates/shirabe/src/config.rs @@ -11,10 +11,10 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - E_USER_DEPRECATED, FILTER_VALIDATE_URL, PHP_URL_HOST, PHP_URL_SCHEME, PhpMixed, - RuntimeException, array_key_exists, array_merge, array_reverse, array_search_mixed, - array_unique, current, empty, filter_var, implode, in_array, is_array, is_int, is_string, key, - parse_url, php_to_string, reset, rtrim, strtolower, strtoupper, strtr, substr, trigger_error, + E_USER_DEPRECATED, PHP_URL_HOST, PHP_URL_SCHEME, PhpMixed, RuntimeException, array_key_exists, + array_merge, array_reverse, array_search_mixed, array_unique, current, empty, filter_var_url, + implode, in_array, is_array, is_int, is_string, key, parse_url, php_to_string, reset, rtrim, + strtolower, strtoupper, strtr, substr, trigger_error, }; use std::cell::RefCell; @@ -1103,7 +1103,7 @@ impl Config { repo_options: &IndexMap, ) -> Result<()> { // Return right away if the URL is malformed or custom (see issue #5173), but only for non-HTTP(S) URLs - if !filter_var(url, FILTER_VALIDATE_URL) && !Preg::is_match(r"{^https?://}", url) { + if !filter_var_url(url) && !Preg::is_match(r"{^https?://}", url) { return Ok(()); } diff --git a/crates/shirabe/src/package/loader/validating_array_loader.rs b/crates/shirabe/src/package/loader/validating_array_loader.rs index cb7b7ae..1ea51c1 100644 --- a/crates/shirabe/src/package/loader/validating_array_loader.rs +++ b/crates/shirabe/src/package/loader/validating_array_loader.rs @@ -6,10 +6,10 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_external_packages::composer::spdx_licenses::SpdxLicenses; use shirabe_php_shim::{ - E_USER_DEPRECATED, FILTER_VALIDATE_EMAIL, PHP_EOL, PhpMixed, array_intersect_key, array_values, - filter_var, get_debug_type, is_array, is_bool, is_int, is_numeric, is_scalar, is_string, - json_encode, parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, strtolower, - strtotime, substr, trigger_error, trim, var_export, + E_USER_DEPRECATED, PHP_EOL, PhpMixed, array_intersect_key, array_values, filter_var_email, + get_debug_type, is_array, is_bool, is_int, is_numeric, is_scalar, is_string, json_encode, + parse_url_all, php_to_string, sprintf, str_replace, strcasecmp, strtolower, strtotime, substr, + trigger_error, trim, var_export, }; use shirabe_semver::constraint::AnyConstraint; use shirabe_semver::constraint::MatchNoneConstraint; @@ -314,7 +314,7 @@ impl ValidatingArrayLoader { .and_then(|v| v.as_string()) .map(|s| s.to_string()); if let Some(email_str) = email - && !filter_var(&email_str, FILTER_VALIDATE_EMAIL) + && !filter_var_email(&email_str) { self.warnings.push(format!( "authors.{}.email : invalid value ({}), must be a valid email address", @@ -384,7 +384,7 @@ impl ValidatingArrayLoader { .and_then(|v| v.as_string()) .map(|s| s.to_string()); if let Some(email_str) = support_email - && !filter_var(&email_str, FILTER_VALIDATE_EMAIL) + && !filter_var_email(&email_str) { self.warnings.push(format!( "support.email : invalid value ({}), must be a valid email address", diff --git a/crates/shirabe/src/util/error_handler.rs b/crates/shirabe/src/util/error_handler.rs index e0590d9..b9210fc 100644 --- a/crates/shirabe/src/util/error_handler.rs +++ b/crates/shirabe/src/util/error_handler.rs @@ -3,9 +3,9 @@ use crate::io::IOInterface; use crate::io::IOInterfaceImmutable; use shirabe_php_shim::{ - E_ALL, E_DEPRECATED, E_USER_DEPRECATED, E_USER_WARNING, E_WARNING, ErrorException, - FILTER_VALIDATE_BOOLEAN, PHP_EOL, PhpMixed, STDERR, debug_backtrace, error_reporting, - filter_var, ini_get, is_resource, set_error_handler, + E_ALL, E_DEPRECATED, E_USER_DEPRECATED, E_USER_WARNING, E_WARNING, ErrorException, PHP_EOL, + PhpMixed, STDERR, debug_backtrace, error_reporting, filter_var_boolean, ini_get, is_resource, + set_error_handler, }; use std::cell::{Cell, RefCell}; use std::rc::Rc; @@ -37,7 +37,7 @@ impl ErrorHandler { let mut message = message; let xdebug_scream = ini_get("xdebug.scream").unwrap_or_default(); - if filter_var(&xdebug_scream, FILTER_VALIDATE_BOOLEAN) { + if filter_var_boolean(&xdebug_scream) { message += "\n\nWarning: You have xdebug.scream enabled, the warning above may be\na legitimately suppressed error that you were not supposed to see."; } diff --git a/crates/shirabe/src/util/no_proxy_pattern.rs b/crates/shirabe/src/util/no_proxy_pattern.rs index 08cd92f..3715ea9 100644 --- a/crates/shirabe/src/util/no_proxy_pattern.rs +++ b/crates/shirabe/src/util/no_proxy_pattern.rs @@ -4,10 +4,9 @@ use anyhow::Result; use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::Preg; use shirabe_php_shim::{ - FILTER_VALIDATE_INT, FILTER_VALIDATE_IP, PHP_URL_HOST, PHP_URL_PORT, PHP_URL_SCHEME, PhpMixed, - RuntimeException, array_key_exists, chr, empty, explode, filter_var, filter_var_with_options, - inet_pton, ltrim, parse_url, str_pad, str_repeat, stripos, strlen, strpbrk, strpos, substr, - substr_count, unpack, + PHP_URL_HOST, PHP_URL_PORT, PHP_URL_SCHEME, PhpMixed, RuntimeException, array_key_exists, chr, + empty, explode, filter_var_int_with_range, filter_var_ip, inet_pton, ltrim, parse_url, str_pad, + str_repeat, stripos, strlen, strpbrk, strpos, substr, substr_count, unpack, }; /// Tests URLs against NO_PROXY patterns @@ -271,7 +270,7 @@ impl NoProxyPattern { } // See if this is an ip address - if !filter_var(&host, FILTER_VALIDATE_IP) { + if !filter_var_ip(&host) { return Ok(!modified); } @@ -483,18 +482,6 @@ impl NoProxyPattern { /// Wrapper around filter_var FILTER_VALIDATE_INT fn validate_int(&self, int: &str, min: i64, max: i64) -> bool { - let mut options: IndexMap = IndexMap::new(); - let mut inner: IndexMap = IndexMap::new(); - inner.insert("min_range".to_string(), PhpMixed::Int(min)); - inner.insert("max_range".to_string(), PhpMixed::Int(max)); - options.insert( - "options".to_string(), - PhpMixed::Array(inner.into_iter().collect()), - ); - - !matches!( - filter_var_with_options(int, FILTER_VALIDATE_INT, &options), - PhpMixed::Bool(false) - ) + filter_var_int_with_range(int, min, max) } } diff --git a/crates/shirabe/src/util/remote_filesystem.rs b/crates/shirabe/src/util/remote_filesystem.rs index bada2af..e36a174 100644 --- a/crates/shirabe/src/util/remote_filesystem.rs +++ b/crates/shirabe/src/util/remote_filesystem.rs @@ -4,12 +4,12 @@ use indexmap::IndexMap; use shirabe_external_packages::composer::pcre::{CaptureKey, Preg}; use shirabe_php_shim::{ - FILTER_VALIDATE_BOOLEAN, PHP_URL_HOST, PHP_URL_PATH, PHP_URL_SCHEME, PHP_VERSION_ID, PhpMixed, - RuntimeException, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_PROGRESS, + PHP_URL_HOST, PHP_URL_PATH, PHP_URL_SCHEME, PHP_VERSION_ID, PhpMixed, RuntimeException, + STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_FILE_SIZE_IS, STREAM_NOTIFY_PROGRESS, array_replace_recursive, base64_encode, explode, extension_loaded, file_put_contents, - filter_var, gethostbyname, http_clear_last_response_headers, http_get_last_response_headers, - ini_get, json_decode, parse_url, preg_quote, sprintf, strpos, strtolower, strtr, substr, trim, - zlib_decode, + filter_var_boolean, gethostbyname, http_clear_last_response_headers, + http_get_last_response_headers, ini_get, json_decode, parse_url, preg_quote, sprintf, strpos, + strtolower, strtr, substr, trim, zlib_decode, }; use crate::config::Config; @@ -410,10 +410,7 @@ impl RemoteFilesystem { result = None; } if !error_message.is_empty() - && !filter_var( - &ini_get("allow_url_fopen").unwrap_or_default(), - FILTER_VALIDATE_BOOLEAN, - ) + && !filter_var_boolean(&ini_get("allow_url_fopen").unwrap_or_default()) { error_message = format!( "allow_url_fopen must be enabled in php.ini ({})", -- cgit v1.3.1