Skip to content

[RFC] Switch expression #5308

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 5 commits into from
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
36 changes: 36 additions & 0 deletions Zend/tests/switch/001.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
--TEST--
Basic switch expression functionality test
--FILE--
<?php

function wordify($x) {
return switch ($x) {
0 => 'Zero',
1 => 'One',
2 => 'Two',
3 => 'Three',
4 => 'Four',
5 => 'Five',
6 => 'Six',
7 => 'Seven',
8 => 'Eight',
9 => 'Nine',
};
}

for ($i = 0; $i <= 9; $i++) {
print wordify($i) . "\n";
}

?>
--EXPECT--
Zero
One
Two
Three
Four
Five
Six
Seven
Eight
Nine
19 changes: 19 additions & 0 deletions Zend/tests/switch/002.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
--TEST--
Switch expression omit trailing comma
--FILE--
<?php

function print_bool($bool) {
echo switch ($bool) {
true => "true\n",
false => "false\n"
};
}

print_bool(true);
print_bool(false);

?>
--EXPECT--
true
false
24 changes: 24 additions & 0 deletions Zend/tests/switch/003.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
--TEST--
Switch expression default case
--FILE--
<?php

function get_value($i) {
return switch ($i) {
1 => 1,
2 => 2,
default => 'default',
};
}

echo get_value(0) . "\n";
echo get_value(1) . "\n";
echo get_value(2) . "\n";
echo get_value(3) . "\n";

?>
--EXPECT--
default
1
2
default
31 changes: 31 additions & 0 deletions Zend/tests/switch/004.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Switch expression with true as expression
--FILE--
<?php

function get_range($i) {
return switch (true) {
$i >= 50 => '50+',
$i >= 40 => '40-50',
$i >= 30 => '30-40',
$i >= 20 => '20-30',
$i >= 10 => '10-20',
default => '0-10',
};
}

echo get_range(22) . "\n";
echo get_range(0) . "\n";
echo get_range(59) . "\n";
echo get_range(13) . "\n";
echo get_range(39) . "\n";
echo get_range(40) . "\n";

?>
--EXPECT--
20-30
0-10
50+
10-20
30-40
40-50
12 changes: 12 additions & 0 deletions Zend/tests/switch/005.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
--TEST--
Switch expression discarding result
--FILE--
<?php

switch (1) {
1 => print "Executed\n",
};

?>
--EXPECT--
Executed
10 changes: 10 additions & 0 deletions Zend/tests/switch/006.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--TEST--
Switch expression with no cases
--FILE--
<?php

$x = switch (true) {};

?>
--EXPECTF--
Parse error: syntax error, unexpected '}' in %s on line %d
26 changes: 26 additions & 0 deletions Zend/tests/switch/007.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
--TEST--
Switch expression exception on unhandled case
--FILE--
<?php

function get_value($i) {
return switch ($i) {
1 => 1,
2 => 2,
};
}

echo get_value(1) . "\n";
echo get_value(2) . "\n";
echo get_value(3) . "\n";

?>
--EXPECTF--
1
2

Fatal error: Uncaught UnhandledSwitchCaseError in %s
Stack trace:
#0 %s: get_value(3)
#1 {main}
thrown in %s on line %d
23 changes: 23 additions & 0 deletions Zend/tests/switch/008.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
--TEST--
Copy link
Member Author

Choose a reason for hiding this comment

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

This test is useless now.

Switch expression precedence
--FILE--
<?php

print switch (!true) {
false => "! has higher precedence\n"
};

$throwableInterface = Throwable::class;
print switch (new RuntimeException() instanceof $throwableInterface) {
true => "instanceof has higher precedence\n"
};

print switch (10 ** 2) {
100 => "** has higher precedence\n"
};

?>
--EXPECT--
! has higher precedence
instanceof has higher precedence
** has higher precedence
25 changes: 25 additions & 0 deletions Zend/tests/switch/009.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
--TEST--
Switch expression multiple conditions per case
--FILE--
<?php

function is_working_day($day) {
return switch ($day) {
1, 7 => false,
2, 3, 4, 5, 6 => true,
};
}

for ($i = 1; $i <= 7; $i++) {
var_dump(is_working_day($i));
}

?>
--EXPECT--
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)
31 changes: 31 additions & 0 deletions Zend/tests/switch/010.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
--TEST--
Switch statement multiple conditions per case
--FILE--
<?php

function is_working_day($day) {
switch ($day) {
case 1, 7:
return false;
case 2, 3, 4, 5, 6:
return true;
};

return null;
}

for ($i = 0; $i <= 8; $i++) {
var_dump(is_working_day($i));
}

?>
--EXPECT--
NULL
bool(false)
bool(true)
bool(true)
bool(true)
bool(true)
bool(true)
bool(false)
NULL
40 changes: 40 additions & 0 deletions Zend/tests/switch/011.phpt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
--TEST--
Pretty printing for switch expression
--FILE--
<?php

assert(switch ('foo') { default => false });

assert(switch ('foo') {
'foo', 'bar' => false,
'baz' => false,
});

assert((function () {
switch ('foo') {
case 'foo', 'bar':
return false;
case 'baz':
return false;
}
})());

?>
--EXPECTF--
Warning: assert(): assert('foo' switch {
Copy link
Member Author

Choose a reason for hiding this comment

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

This syntax needs to be updated.

default => false,
}) failed in %s on line %d

Warning: assert(): assert('foo' switch {
'foo', 'bar' => false,
'baz' => false,
}) failed in %s on line %d

Warning: assert(): assert(function () {
switch ('foo') {
case 'foo', 'bar':
return false;
case 'baz':
return false;
}
}()) failed in %s on line %d
34 changes: 25 additions & 9 deletions Zend/zend_ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -1851,23 +1851,39 @@ static ZEND_COLD void zend_ast_export_ex(smart_str *str, zend_ast *ast, int prio
smart_str_appendc(str, '}');
break;
case ZEND_AST_SWITCH:
smart_str_appends(str, "switch (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
if (ast->attr & ZEND_SWITCH_EXPRESSION) {
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, " switch {\n");
} else {
smart_str_appends(str, "switch (");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ") {\n");
}
zend_ast_export_ex(str, ast->child[1], 0, indent + 1);
zend_ast_export_indent(str, indent);
smart_str_appendc(str, '}');
break;
case ZEND_AST_SWITCH_CASE:
zend_ast_export_indent(str, indent);
if (ast->child[0]) {
smart_str_appends(str, "case ");
zend_ast_export_ex(str, ast->child[0], 0, indent);
smart_str_appends(str, ":\n");
if (ast->attr & ZEND_SWITCH_EXPRESSION) {
if (ast->child[0]) {
zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent);
smart_str_appends(str, " => ");
} else {
smart_str_appends(str, "default => ");
}
zend_ast_export_ex(str, ast->child[1], 0, 0);
smart_str_appends(str, ",\n");
} else {
smart_str_appends(str, "default:\n");
if (ast->child[0]) {
smart_str_appends(str, "case ");
zend_ast_export_list(str, (zend_ast_list*)ast->child[0], 1, 0, indent);
smart_str_appends(str, ":\n");
} else {
smart_str_appends(str, "default:\n");
}
zend_ast_export_stmt(str, ast->child[1], indent + 1);
}
zend_ast_export_stmt(str, ast->child[1], indent + 1);
break;
case ZEND_AST_DECLARE:
smart_str_appends(str, "declare(");
Expand Down
1 change: 1 addition & 0 deletions Zend/zend_ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ enum _zend_ast_kind {
ZEND_AST_STMT_LIST,
ZEND_AST_IF,
ZEND_AST_SWITCH_LIST,
ZEND_AST_SWITCH_CASE_COND_LIST,
Copy link
Member Author

Choose a reason for hiding this comment

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

We could actually probably reuse the ZEND_AST_EXPR_LIST.

ZEND_AST_CATCH_LIST,
ZEND_AST_PARAM_LIST,
ZEND_AST_CLOSURE_USES,
Expand Down
Loading