From c14033bfdddbc161b30c7904db57b91054329a25 Mon Sep 17 00:00:00 2001 From: Marian <42134098+IanDelMar@users.noreply.github.com> Date: Mon, 8 Sep 2025 20:41:45 +0200 Subject: [PATCH 1/5] Add conditional return type for sanitize_term_field --- functionMap.php | 1 + tests/data/return/sanitize-term-field.php | 69 +++++++++++++++++++++++ wordpress-stubs.php | 3 + 3 files changed, 73 insertions(+) create mode 100644 tests/data/return/sanitize-term-field.php diff --git a/functionMap.php b/functionMap.php index 67590fe..6b9cbaf 100644 --- a/functionMap.php +++ b/functionMap.php @@ -136,6 +136,7 @@ 'sanitize_post' => ['(T is \WP_Post ? \WP_Post : (T is object ? object : (T is array ? array : T)))', '@phpstan-template T' => 'of mixed', 'post' => 'T'], 'sanitize_sql_orderby' => ['(T is non-falsy-string ? T|false : false)', '@phpstan-template T' => 'of string', 'orderby' => 'T'], 'sanitize_term' => ['T', '@phpstan-template' => 'T of array|object', 'term' => 'T'], + 'sanitize_term_field' => ["(\$field is 'parent'|'term_id'|'count'|'term_group'|'term_taxonomy_id'|'object_id' ? int<0, max> : (\$context is 'raw' ? T : (\$context is 'attribute'|'edit'|'js' ? string : mixed)))", '@phpstan-template T' => 'of string', 'value' => 'T'], 'sanitize_title_with_dashes' => ['lowercase-string', 'context' => "'display'|'save'"], 'single_cat_title' => ['($display is true ? void : string|void)'], 'single_month_title' => ['($display is true ? false|void : false|string)'], diff --git a/tests/data/return/sanitize-term-field.php b/tests/data/return/sanitize-term-field.php new file mode 100644 index 0000000..9824f76 --- /dev/null +++ b/tests/data/return/sanitize-term-field.php @@ -0,0 +1,69 @@ +', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'raw')); +assertType('int<0, max>', sanitize_term_field('term_id', Faker::string(), $termId, $taxonomy, 'raw')); +assertType('int<0, max>', sanitize_term_field('count', Faker::string(), $termId, $taxonomy, 'raw')); +assertType('int<0, max>', sanitize_term_field('term_group', Faker::string(), $termId, $taxonomy, 'raw')); +assertType('int<0, max>', sanitize_term_field('term_taxonomy_id', Faker::string(), $termId, $taxonomy, 'raw')); +assertType('int<0, max>', sanitize_term_field('object_id', Faker::string(), $termId, $taxonomy, 'raw')); +// Also int range if constant numeric string +assertType('int<0, max>', sanitize_term_field('parent', '123', $termId, $taxonomy, 'raw')); +// Also int range in any other context +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'edit')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'db')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'display')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'rss')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'attribute')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'js')); +assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, Faker::string())); + +// Non int fields in raw context +assertType("'field value'", sanitize_term_field('field', 'field value', $termId, $taxonomy, 'raw')); +assertType('string', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'raw')); + +// Non int field values in edit context may be filtered to mixed, but are escaped using esc_html or esc_attr => string +assertType('string', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'edit')); +assertType('string', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'edit')); + +// Non int field values in attribute/js context are not filtered, but are escaped using esc_attr/esc_js +// => string, but not as given in the argument +assertType('string', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'attribute')); +assertType('string', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'attribute')); +assertType('string', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'js')); +assertType('string', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'js')); + +// Non int fields in any other context may be filtered to mixed => mixed +assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'db')); +assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'display')); +assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'rss')); +assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, Faker::string())); +assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'db')); +assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'display')); +assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'rss')); +assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, Faker::string())); + +// Non constant field in raw context => int<0, max> (from int field) or T (from other field) +assertType("'field value'|int<0, max>", sanitize_term_field(Faker::string(), 'field value', $termId, $taxonomy, 'raw')); +assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'raw')); + +// Non constant field in attribute|edit|js context than raw => int<0, max> (from int field) or string (from other field) +assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'attribute')); +assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'edit')); +assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'js')); + +// Non constant field in any other context than raw => mixed +assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'db')); +assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'display')); +assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'rss')); +assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, Faker::string())); diff --git a/wordpress-stubs.php b/wordpress-stubs.php index 4d26496..6649e0c 100644 --- a/wordpress-stubs.php +++ b/wordpress-stubs.php @@ -139087,6 +139087,9 @@ function sanitize_term($term, $taxonomy, $context = 'display') * 'attribute', or 'js'. Default 'display'. * @return mixed Sanitized field. * @phpstan-param 'raw'|'edit'|'db'|'display'|'rss'|'attribute'|'js' $context + * @phpstan-template T of string + * @phpstan-param T $value + * @phpstan-return ($field is 'parent'|'term_id'|'count'|'term_group'|'term_taxonomy_id'|'object_id' ? int<0, max> : ($context is 'raw' ? T : ($context is 'attribute'|'edit'|'js' ? string : mixed))) */ function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) { From 6b8df3cc85f8efbab0051c77312a9541718c17d2 Mon Sep 17 00:00:00 2001 From: Marian <42134098+IanDelMar@users.noreply.github.com> Date: Mon, 8 Sep 2025 22:14:54 +0200 Subject: [PATCH 2/5] Remove redundant assertions --- tests/data/return/sanitize-term-field.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/data/return/sanitize-term-field.php b/tests/data/return/sanitize-term-field.php index 9824f76..b365e24 100644 --- a/tests/data/return/sanitize-term-field.php +++ b/tests/data/return/sanitize-term-field.php @@ -20,12 +20,6 @@ // Also int range if constant numeric string assertType('int<0, max>', sanitize_term_field('parent', '123', $termId, $taxonomy, 'raw')); // Also int range in any other context -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'edit')); -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'db')); -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'display')); -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'rss')); -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'attribute')); -assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, 'js')); assertType('int<0, max>', sanitize_term_field('parent', Faker::string(), $termId, $taxonomy, Faker::string())); // Non int fields in raw context From a5662a3c035ad63b94dcef173e810bdc31f51a49 Mon Sep 17 00:00:00 2001 From: Marian <42134098+IanDelMar@users.noreply.github.com> Date: Mon, 8 Sep 2025 22:33:28 +0200 Subject: [PATCH 3/5] Remove redundant assertions --- tests/data/return/sanitize-term-field.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/data/return/sanitize-term-field.php b/tests/data/return/sanitize-term-field.php index b365e24..c2eec01 100644 --- a/tests/data/return/sanitize-term-field.php +++ b/tests/data/return/sanitize-term-field.php @@ -41,11 +41,9 @@ assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'db')); assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'display')); assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, 'rss')); -assertType('mixed', sanitize_term_field('field', 'field value', $termId, $taxonomy, Faker::string())); assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'db')); assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'display')); assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, 'rss')); -assertType('mixed', sanitize_term_field('field', Faker::string(), $termId, $taxonomy, Faker::string())); // Non constant field in raw context => int<0, max> (from int field) or T (from other field) assertType("'field value'|int<0, max>", sanitize_term_field(Faker::string(), 'field value', $termId, $taxonomy, 'raw')); From 478d3a51201a0a8d033f25d81d66e6734e071d88 Mon Sep 17 00:00:00 2001 From: Marian <42134098+IanDelMar@users.noreply.github.com> Date: Mon, 8 Sep 2025 22:51:35 +0200 Subject: [PATCH 4/5] Remove redundant assertions --- tests/data/return/sanitize-term-field.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/data/return/sanitize-term-field.php b/tests/data/return/sanitize-term-field.php index c2eec01..d06ab77 100644 --- a/tests/data/return/sanitize-term-field.php +++ b/tests/data/return/sanitize-term-field.php @@ -49,7 +49,7 @@ assertType("'field value'|int<0, max>", sanitize_term_field(Faker::string(), 'field value', $termId, $taxonomy, 'raw')); assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'raw')); -// Non constant field in attribute|edit|js context than raw => int<0, max> (from int field) or string (from other field) +// Non constant field in attribute|edit|js context => int<0, max> (from int field) or string (from other field) assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'attribute')); assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'edit')); assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'js')); @@ -58,4 +58,3 @@ assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'db')); assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'display')); assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'rss')); -assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, Faker::string())); From 82db1166fab2864eb8330318b79362024901ffd8 Mon Sep 17 00:00:00 2001 From: Marian <42134098+IanDelMar@users.noreply.github.com> Date: Mon, 8 Sep 2025 23:02:14 +0200 Subject: [PATCH 5/5] Update sanitize-term-field.php --- tests/data/return/sanitize-term-field.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/data/return/sanitize-term-field.php b/tests/data/return/sanitize-term-field.php index d06ab77..2b88d36 100644 --- a/tests/data/return/sanitize-term-field.php +++ b/tests/data/return/sanitize-term-field.php @@ -54,7 +54,7 @@ assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'edit')); assertType('int<0, max>|string', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'js')); -// Non constant field in any other context than raw => mixed +// Non constant field in any other context than attribute|edit|js|raw => mixed assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'db')); assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'display')); assertType('mixed', sanitize_term_field(Faker::string(), Faker::string(), $termId, $taxonomy, 'rss'));