Skip to content

User Defined Operator Overloads #7388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
58a1456
Initial commit of user defined operator overloads
JordanRL Aug 11, 2021
c46492d
Added InvalidOperator exception and fixed error handling of operator …
JordanRL Aug 11, 2021
6811080
Started on ZEND_IS_LARGER work
JordanRL Aug 13, 2021
4a84a91
Further implementation of op overloads
JordanRL Aug 18, 2021
6be3d1a
Finished implementation of compare and updated several tests; added s…
JordanRL Aug 19, 2021
5fe2ad1
Fixing various comparison issues and making comparison optional
JordanRL Aug 20, 2021
ff79f94
Added bitwise operators and updated several tests
JordanRL Aug 22, 2021
88bd550
Interim commit
JordanRL Nov 26, 2021
0a0d9ec
Resolving merge conflicts
JordanRL Nov 26, 2021
2fa6d98
Improvements to operator overloads and IS_LARGER opcode
JordanRL Nov 27, 2021
a2c3f15
Fixing some bugs
JordanRL Nov 29, 2021
80497fc
Updating extensions for changes to objects and ZEND_UNCOMPARABLE
JordanRL Nov 29, 2021
3953f37
Resolving failing tests for operator overloads
JordanRL Nov 29, 2021
6fe6fe2
Fixing extension handling for object compare handler
JordanRL Nov 29, 2021
ae584fb
Updating pow test
JordanRL Nov 29, 2021
0ca2ad0
Fixing fallback from __equals to __compareto, and fixing named_params…
JordanRL Nov 30, 2021
ce042d1
Resolving some build errors in CI
JordanRL Nov 30, 2021
0139183
Resolving a missed build error in CI
JordanRL Nov 30, 2021
1e3038f
Resolving SIGSEGV
JordanRL Nov 30, 2021
f3a44b8
removing ZEND_USER_FUNCTION set
JordanRL Nov 30, 2021
96d72ce
Removing test code
JordanRL Nov 30, 2021
cfda296
Removing old comment
JordanRL Nov 30, 2021
bbcaf38
Merge branch 'master' into operator-overloads
JordanRL Dec 1, 2021
b23f4e7
Trying JIT handling
JordanRL Dec 2, 2021
5ebaed4
Fixing typo in case
JordanRL Dec 2, 2021
b042c7d
Another case typo
JordanRL Dec 2, 2021
29ddc0d
Use operator keyword instead of magic methods
bwoebi Dec 3, 2021
faab4f2
Merge pull request #3 from bwoebi/operator-overloads
JordanRL Dec 3, 2021
3697f39
Use operator keyword instead of magic methods
bwoebi Dec 3, 2021
52f3f09
Merge pull request #4 from bwoebi/operator-overloads
JordanRL Dec 3, 2021
fc3a89c
Updating InvalidOperatorError name
JordanRL Dec 4, 2021
9afc789
Adding more tests for op overloads
JordanRL Dec 4, 2021
e044f53
Resolving more memory leaks and segfaults
JordanRL Dec 5, 2021
3bae673
Switched to OperandPosition enum
JordanRL Dec 9, 2021
280ad16
Updates
JordanRL Dec 9, 2021
7e1601b
Fix enum case usage in zend_std_call_op_override
bwoebi Dec 10, 2021
5f500bc
Merge pull request #5 from bwoebi/operator-overloads
JordanRL Dec 10, 2021
b0492c4
Adding helper functions for extensions
JordanRL Dec 10, 2021
3d33ddd
Disallow direct method calls to operators
JordanRL Dec 11, 2021
6395654
Reflection support for operator overloads
JordanRL Dec 11, 2021
814d9a2
Merge branch 'master' into operator-overloads
JordanRL Dec 11, 2021
8d1994f
Allowing direct calls via closures
JordanRL Dec 11, 2021
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
Prev Previous commit
Next Next commit
Further implementation of op overloads
  • Loading branch information
JordanRL committed Aug 18, 2021
commit 4a84a91008b7648fc28b9866661e1c3511de402c
31 changes: 31 additions & 0 deletions Zend/tests/operator_overloads/add_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
operator overload: add operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __add(int $other, bool $left): A
{
$return = new A();
$return->value = $this->value + $other;

return $return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indent

}
}

$obj = new A();
$obj->value = 3;

$num1 = 2 + $obj;

var_dump($num1->value);

$num2 = $obj + 3;

var_dump($num2->value);
?>
--EXPECT--
int(5)
int(6)
36 changes: 36 additions & 0 deletions Zend/tests/operator_overloads/div_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
operator overload: div operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __div(int $other, bool $left): A
{
$return = new A();

if ($left) {
$return->value = $this->value / $other;
} else {
$return->value = $other / $this->value;
}

return $return;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: indent

}
}

$obj = new A();
$obj->value = 6;

$num1 = 12 / $obj;

var_dump($num1->value);

$num2 = $obj / 2;

var_dump($num2->value);
?>
--EXPECT--
int(2)
int(3)
43 changes: 43 additions & 0 deletions Zend/tests/operator_overloads/equals_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
--TEST--
operator overload: div operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __equals(mixed $other): bool
{
return ($this->value == $other);
}
}

$obj = new A();
$obj->value = 6;

$bool1 = 12 == $obj;

var_dump($bool1);

$bool2 = $obj == 2;

var_dump($bool2);

$bool3 = $obj == 6;

var_dump($bool3);

$bool4 = $obj == 6.0;

var_dump($bool4);

$bool5 = $obj != 6.0;

var_dump($bool5);
?>
--EXPECT--
bool(false)
bool(false)
bool(true)
bool(true)
bool(false)
36 changes: 36 additions & 0 deletions Zend/tests/operator_overloads/mod_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
operator overload: mod operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __mod(int $other, bool $left): A
{
$return = new A();

if ($left) {
$return->value = $this->value % $other;
} else {
$return->value = $other % $this->value;
}

return $return;
}
}

$obj = new A();
$obj->value = 2;

$num1 = 3 % $obj;

var_dump($num1->value);

$num2 = $obj % 4;

var_dump($num2->value);
?>
--EXPECT--
int(1)
int(2)
31 changes: 31 additions & 0 deletions Zend/tests/operator_overloads/mul_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
operator overload: mul operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __mul(int $other, bool $left): A
{
$return = new A();
$return->value = $this->value * $other;

return $return;
}
}

$obj = new A();
$obj->value = 3;

$num1 = 2 * $obj;

var_dump($num1->value);

$num2 = $obj * 3;

var_dump($num2->value);
?>
--EXPECT--
int(6)
int(9)
19 changes: 19 additions & 0 deletions Zend/tests/operator_overloads/operator_omitted_type.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
operator overload: no explicit type
--FILE--
<?php

class A {
public int $value;

public function __add($other, bool $left): self
{
$return = new A();
$return->value = $this->value + $other;

return $return;
}
}
?>
--EXPECTF--
Fatal error: A::__add(): Parameter #1 ($other) must explicitly define a type in %s on line %d
56 changes: 56 additions & 0 deletions Zend/tests/operator_overloads/operator_unimplemented.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
--TEST--
operator overload: unimplemented
--FILE--
<?php

class A {
public int $value;
}

class B {
public int $value;

public function __add(int|A $other, bool $left): B
{
$return = new B();
if (is_int($other)) {
$return->value = $this->value + $other;
} else {
$return->value = $this->value + $other->value;
}

return $return;
}
}

$obj1 = new A();
$obj1->value = 3;
$obj2 = new B();
$obj2->value = 2;

try {
$num1 = $obj1 + 2;
} catch (InvalidOperator) {
echo "OK!".PHP_EOL;
}

try {
$num2 = 2 + $obj1;
} catch (InvalidOperator) {
echo "STILL OK!".PHP_EOL;
}

$num3 = $obj1 + $obj2;

var_dump($num3->value);

$num4 = $obj2 + $obj1;

var_dump($num4->value);

?>
--EXPECT--
OK!
STILL OK!
int(5)
int(5)
36 changes: 36 additions & 0 deletions Zend/tests/operator_overloads/pow_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
operator overload: pow operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __pow(int $other, bool $left): A
{
$return = new A();

if ($left) {
$return->value = $this->value ** $other;
} else {
$return->value = $other ** $this->value;
}

return $return;
}
}

$obj = new A();
$obj->value = 2;

$num1 = 5 ** $obj;

var_dump($num1->value);

$num2 = $obj ** 3;

var_dump($num2->value);
?>
--EXPECT--
int(25)
int(8)
36 changes: 36 additions & 0 deletions Zend/tests/operator_overloads/sub_operator.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
operator overload: sub operator with scalars
--FILE--
<?php

class A {
public int $value;

public function __sub(int $other, bool $left): A
{
$return = new A();

if ($left) {
$return->value = $this->value - $other;
} else {
$return->value = $other - $this->value;
}

return $return;
}
}

$obj = new A();
$obj->value = 3;

$num1 = 2 - $obj;

var_dump($num1->value);

$num2 = $obj - 3;

var_dump($num2->value);
?>
--EXPECT--
int(-1)
int(0)
14 changes: 12 additions & 2 deletions Zend/zend_API.c
Original file line number Diff line number Diff line change
Expand Up @@ -2426,6 +2426,15 @@ static void zend_check_magic_method_arg_type(uint32_t arg_num, const zend_class_
}
}

static void zend_check_magic_method_explicit_type(uint32_t arg_num, const zend_class_entry *ce, const zend_function *fptr, int error_type)
{
if (!ZEND_TYPE_IS_SET(fptr->common.arg_info[arg_num].type)) {
zend_error(error_type, "%s::%s(): Parameter #%d ($%s) must explicitly define a type",
ZSTR_VAL(ce->name), ZSTR_VAL(fptr->common.function_name),
arg_num + 1, ZSTR_VAL(fptr->common.arg_info[arg_num].name));
}
}

static void zend_check_magic_method_return_type(const zend_class_entry *ce, const zend_function *fptr, int error_type, int return_type)
{
if (!(fptr->common.fn_flags & ZEND_ACC_HAS_RETURN_TYPE)) {
Expand Down Expand Up @@ -2490,10 +2499,9 @@ static void zend_check_magic_method_binary_operator_overload(
zend_check_magic_method_args(2, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr, error_type);
zend_check_magic_method_explicit_type(0, ce, fptr, error_type);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ANY);
zend_check_magic_method_arg_type(1, ce, fptr, error_type, MAY_BE_BOOL);
zend_check_magic_method_return_type(ce, fptr, error_type,
(MAY_BE_BOOL|MAY_BE_STRING|MAY_BE_LONG|MAY_BE_DOUBLE|MAY_BE_ARRAY|MAY_BE_OBJECT));
}

static void zend_check_magic_method_comparison_operator_overload(
Expand All @@ -2502,6 +2510,7 @@ static void zend_check_magic_method_comparison_operator_overload(
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr, error_type);
zend_check_magic_method_explicit_type(0, ce, fptr, error_type);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ANY);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_BOOL);
}
Expand Down Expand Up @@ -2626,6 +2635,7 @@ ZEND_API void zend_check_magic_method_implementation(const zend_class_entry *ce,
zend_check_magic_method_args(1, ce, fptr, error_type);
zend_check_magic_method_non_static(ce, fptr, error_type);
zend_check_magic_method_public(ce, fptr, error_type);
zend_check_magic_method_explicit_type(0, ce, fptr, error_type);
zend_check_magic_method_arg_type(0, ce, fptr, error_type, MAY_BE_ANY);
zend_check_magic_method_return_type(ce, fptr, error_type, MAY_BE_LONG);
}
Expand Down
2 changes: 2 additions & 0 deletions Zend/zend_compile.c
Original file line number Diff line number Diff line change
Expand Up @@ -2161,6 +2161,8 @@ ZEND_API bool zend_is_smart_branch(const zend_op *opline) /* {{{ */
case ZEND_IS_NOT_EQUAL:
case ZEND_IS_SMALLER:
case ZEND_IS_SMALLER_OR_EQUAL:
case ZEND_IS_LARGER:
case ZEND_IS_LARGER_OR_EQUAL:
case ZEND_CASE:
case ZEND_CASE_STRICT:
case ZEND_ISSET_ISEMPTY_CV:
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_exceptions.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ ZEND_API zend_class_entry *zend_ce_value_error;
ZEND_API zend_class_entry *zend_ce_arithmetic_error;
ZEND_API zend_class_entry *zend_ce_division_by_zero_error;
ZEND_API zend_class_entry *zend_ce_unhandled_match_error;
ZEND_API zend_class_entry *zend_ce_operator_error;

/* Internal pseudo-exception that is not exposed to userland. Throwing this exception *does not* execute finally blocks. */
static zend_class_entry zend_ce_unwind_exit;
Expand Down
Loading