Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ext/intl/spoofchecker/spoofchecker.stub.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class Spoofchecker
public const int SIMPLE_CASE_INSENSITIVE = UNKNOWN;
#endif

#if U_ICU_VERSION_MAJOR_NUM >= 74
/** @cvalue UBIDI_LTR */
public const int LTR = UNKNOWN;
/** @cvalue UBIDI_RTL */
public const int RTL = UNKNOWN;
#endif

public function __construct() {}

/**
Expand All @@ -72,4 +79,11 @@ public function setChecks(int $checks): void {}
/** @tentative-return-type */
public function setRestrictionLevel(int $level): void {}
public function setAllowedChars(string $pattern, int $patternOptions = 0): void {}

#if U_ICU_VERSION_MAJOR_NUM >= 74
/**
* @param int $errorCode
*/
public function areBidiConfusable(int $direction, string $string1, string $string2, &$errorCode = null): bool {}
#endif
}
31 changes: 30 additions & 1 deletion ext/intl/spoofchecker/spoofchecker_arginfo.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

43 changes: 43 additions & 0 deletions ext/intl/spoofchecker/spoofchecker_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,3 +223,46 @@ U_CFUNC PHP_METHOD(Spoofchecker, setAllowedChars)
php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
}
}

#if U_ICU_VERSION_MAJOR_NUM >= 74
/* {{{ Checks if a given text contains any confusable characters, for a given text direction */
U_CFUNC PHP_METHOD(Spoofchecker, areBidiConfusable)
{
uint32_t ret = 0;
zend_long direction;
zend_string *s1, *s2;
zval *error_code = NULL;
SPOOFCHECKER_METHOD_INIT_VARS;

ZEND_PARSE_PARAMETERS_START(3, 4)
Z_PARAM_LONG(direction)
Z_PARAM_STR(s1)
Z_PARAM_STR(s2)
Z_PARAM_OPTIONAL
Z_PARAM_ZVAL(error_code)
ZEND_PARSE_PARAMETERS_END();

SPOOFCHECKER_METHOD_FETCH_OBJECT;

if (direction != UBIDI_LTR && direction != UBIDI_RTL) {
zend_argument_value_error(1, "must be either Spoofchecker::LTR or Spoofchecker::RTL");
RETURN_THROWS();
}

if (UNEXPECTED(ZSTR_LEN(s1) > INT32_MAX || ZSTR_LEN(s2) > INT32_MAX)) {
SPOOFCHECKER_ERROR_CODE(co) = U_BUFFER_OVERFLOW_ERROR;
} else {
ret = uspoof_areBidiConfusableUTF8(co->uspoof, (UBiDiDirection)direction, ZSTR_VAL(s1), (int32_t)ZSTR_LEN(s1), ZSTR_VAL(s2), (int32_t)ZSTR_LEN(s2), SPOOFCHECKER_ERROR_CODE_P(co));
}
if (U_FAILURE(SPOOFCHECKER_ERROR_CODE(co))) {
php_error_docref(NULL, E_WARNING, "(%d) %s", SPOOFCHECKER_ERROR_CODE(co), u_errorName(SPOOFCHECKER_ERROR_CODE(co)));
RETURN_TRUE;
}

if (error_code) {
ZEND_TRY_ASSIGN_REF_LONG(error_code, ret);
}
RETVAL_BOOL(ret != 0);
}
/* }}} */
#endif
5 changes: 5 additions & 0 deletions ext/intl/tests/spoofchecker_self_references.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ $checker->isSuspicious("", $checker);
$checker = new Spoofchecker();
$checker->areConfusable("", "", $checker);

if (version_compare(INTL_ICU_VERSION, '74.0') >= 0) {
$checker = new Spoofchecker();
$checker->areBidiConfusable(Spoofchecker::LTR, "", "", $checker);
}

echo "Done\n";

?>
Expand Down
14 changes: 14 additions & 0 deletions ext/intl/tests/spoofchecker_typed_references.phpt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,20 @@ $checker = new Spoofchecker();
$checker->areConfusable("", "", $test->x);
var_dump($test);

if (version_compare(INTL_ICU_VERSION, '74.0') >= 0) {
$test = new Test;
$test->x = "";

$checker = new Spoofchecker();
$checker->areBidiConfusable(Spoofchecker::LTR, "", "", $test->x);
/* Asserted quietly rather than dumped, so that the expected output stays
the same on ICU < 74, where the method does not exist. */
if ($test->x !== "1") {
echo "unexpected value: ";
var_dump($test->x);
}
}

?>
--EXPECT--
object(Test)#1 (1) {
Expand Down
54 changes: 54 additions & 0 deletions ext/intl/tests/spoofchecker_ubidi.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
--TEST--
Spoofchecker::areBidiConfusable() checks if strings are confusable in a given direction.
--EXTENSIONS--
intl
--SKIPIF--
<?php if (version_compare(INTL_ICU_VERSION, '74.0') < 0) die('skip for ICU >= 74.0'); ?>
--FILE--
<?php
$s = new Spoofchecker();

try {
$s->areBidiConfusable(Spoofchecker::RTL + 1, "a", "a");
} catch (ValueError $e) {
echo $e->getMessage() . PHP_EOL;
}

/* "A1<aleph>" and "A<aleph>1" both display as "A1<aleph>" in a left to right
* context, but differ in a right to left one. */
var_dump($s->areBidiConfusable(Spoofchecker::LTR, "A1\u{05D0}", "A\u{05D0}1"));
var_dump($s->areBidiConfusable(Spoofchecker::RTL, "A1\u{05D0}", "A\u{05D0}1"));

/* Mirror case: confusable in a right to left context only. */
var_dump($s->areBidiConfusable(Spoofchecker::LTR, "\u{05D0}A_1", "\u{05D0}1_A"));
var_dump($s->areBidiConfusable(Spoofchecker::RTL, "\u{05D0}A_1", "\u{05D0}1_A"));

/* Neither direction reorders these into each other. */
var_dump($s->areBidiConfusable(Spoofchecker::LTR, "Mark_", "_Mark"));
var_dump($s->areBidiConfusable(Spoofchecker::RTL, "Mark_", "_Mark"));

/* areConfusable() ignores the text direction and misses both cases above. */
var_dump($s->areConfusable("A1\u{05D0}", "A\u{05D0}1"));
var_dump($s->areConfusable("\u{05D0}A_1", "\u{05D0}1_A"));

$errorCode = null;
var_dump($s->areBidiConfusable(Spoofchecker::LTR, "A1\u{05D0}", "A\u{05D0}1", $errorCode));
var_dump($errorCode === Spoofchecker::MIXED_SCRIPT_CONFUSABLE);

var_dump($s->areBidiConfusable(Spoofchecker::LTR, "Mark_", "_Mark", $errorCode));
var_dump($errorCode);
?>
--EXPECT--
Spoofchecker::areBidiConfusable(): Argument #1 ($direction) must be either Spoofchecker::LTR or Spoofchecker::RTL
bool(true)
bool(false)
bool(false)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(true)
bool(false)
int(0)
Loading