Skip to content
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

PHP tokenizer change broke my sniff in 2.5.2 #893

Closed
aik099 opened this issue Feb 13, 2016 · 6 comments
Closed

PHP tokenizer change broke my sniff in 2.5.2 #893

aik099 opened this issue Feb 13, 2016 · 6 comments

Comments

@aik099
Copy link
Contributor

aik099 commented Feb 13, 2016

In the 2.5.2 version (current dev-master, not released) something was changed in PHP tokenizer. That resulted in failing tests in my coding standard https://travis-ci.org/aik099/CodingStandard/jobs/109055550 in general and https://github.com/aik099/CodingStandard/blob/master/CodingStandard/Sniffs/WhiteSpace/ControlStructureSpacingSniff.php sniff in particular.

Currently I have no idea how to correlate PHP tokenizer changes to failing unit test in my standard.

@gsherwood
Copy link
Member

By running -vv over some code and checking the output, I've found that this code:

<?php
switch ( $a ) {
    default:
        do {
            $a = 'b';
        } while ( $a );
        return 5;

    default:
        foreach ( $a as $b ) {
            $e = 'b';
        }
        return 5;
}

is getting tokenized incorrectly in the new version.

The old version tokenized it like this:

Start scope map at 1:T_SWITCH => switch
=> Begin scope map recursion at token 1 with depth 1
Process token 2 on line 2 []: T_WHITESPACE => ·
Process token 3 on line 2 []: T_OPEN_PARENTHESIS => (
Process token 4 on line 2 []: T_WHITESPACE => ·
Process token 5 on line 2 []: T_VARIABLE => $a
Process token 6 on line 2 []: T_WHITESPACE => ·
Process token 7 on line 2 []: T_CLOSE_PARENTHESIS => )
Process token 8 on line 2 []: T_WHITESPACE => ·
Process token 9 on line 2 []: T_OPEN_CURLY_BRACKET => {
=> Found scope opener for 1:T_SWITCH
Process token 10 on line 2 [opener:9;]: T_WHITESPACE => \n
Process token 11 on line 3 [opener:9;]: T_WHITESPACE => ····
Process token 12 on line 3 [opener:9;]: T_DEFAULT => default
* token is an opening condition *
* searching for opener *
=> Begin scope map recursion at token 12 with depth 1
Process token 13 on line 3 []: T_COLON => :
=> Found scope opener for 12:T_DEFAULT
Process token 14 on line 3 [opener:13;]: T_WHITESPACE => \n
Process token 15 on line 4 [opener:13;]: T_WHITESPACE => ········
Process token 16 on line 4 [opener:13;]: T_DO => do
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 16 with depth 2
    Process token 17 on line 4 []: T_WHITESPACE => ·
    Process token 18 on line 4 []: T_OPEN_CURLY_BRACKET => {
    => Found scope opener for 16:T_DO
    Process token 19 on line 4 [opener:18;]: T_WHITESPACE => \n
    Process token 20 on line 5 [opener:18;]: T_WHITESPACE => ············
    Process token 21 on line 5 [opener:18;]: T_VARIABLE => $a
    Process token 22 on line 5 [opener:18;]: T_WHITESPACE => ·
    Process token 23 on line 5 [opener:18;]: T_EQUAL => =
    Process token 24 on line 5 [opener:18;]: T_WHITESPACE => ·
    Process token 25 on line 5 [opener:18;]: T_CONSTANT_ENCAPSED_STRING => 'b'
    Process token 26 on line 5 [opener:18;]: T_SEMICOLON => ;
    Process token 27 on line 5 [opener:18;]: T_WHITESPACE => \n
    Process token 28 on line 6 [opener:18;]: T_WHITESPACE => ········
    Process token 29 on line 6 [opener:18;]: T_CLOSE_CURLY_BRACKET => }
    => Found scope closer (29:T_CLOSE_CURLY_BRACKET) for 16:T_DO
Process token 30 on line 6 [opener:13;]: T_WHITESPACE => ·
Process token 31 on line 6 [opener:13;]: T_WHILE => while
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 31 with depth 2
    Process token 32 on line 6 []: T_WHITESPACE => ·
    Process token 33 on line 6 []: T_OPEN_PARENTHESIS => (
    Process token 34 on line 6 []: T_WHITESPACE => ·
    Process token 35 on line 6 []: T_VARIABLE => $a
    Process token 36 on line 6 []: T_WHITESPACE => ·
    Process token 37 on line 6 []: T_CLOSE_PARENTHESIS => )
    Process token 38 on line 6 []: T_SEMICOLON => ;
    Process token 39 on line 6 []: T_WHITESPACE => \n
    Process token 40 on line 7 []: T_WHITESPACE => ········
    Process token 41 on line 7 []: T_RETURN => return
    Process token 42 on line 7 []: T_WHITESPACE => ·
    Process token 43 on line 7 []: T_LNUMBER => 5
    Process token 44 on line 7 []: T_SEMICOLON => ;
    Process token 45 on line 7 []: T_WHITESPACE => \n
    Process token 46 on line 8 []: T_WHITESPACE => \n
    Process token 47 on line 9 []: T_WHITESPACE => ····
    Process token 48 on line 9 []: T_DEFAULT => default
    => Found new opening condition before scope opener for 31:T_WHILE, bailing
Process token 32 on line 6 [opener:13;]: T_WHITESPACE => ·
Process token 33 on line 6 [opener:13;]: T_OPEN_PARENTHESIS => (
Process token 34 on line 6 [opener:13;]: T_WHITESPACE => ·
Process token 35 on line 6 [opener:13;]: T_VARIABLE => $a
Process token 36 on line 6 [opener:13;]: T_WHITESPACE => ·
Process token 37 on line 6 [opener:13;]: T_CLOSE_PARENTHESIS => )
Process token 38 on line 6 [opener:13;]: T_SEMICOLON => ;
Process token 39 on line 6 [opener:13;]: T_WHITESPACE => \n
Process token 40 on line 7 [opener:13;]: T_WHITESPACE => ········
Process token 41 on line 7 [opener:13;]: T_RETURN => return
=> Found scope closer (41:T_RETURN) for 12:T_DEFAULT
Process token 14 on line 3 [opener:9;]: T_WHITESPACE => \n
Process token 15 on line 4 [opener:9;]: T_WHITESPACE => ········
Process token 16 on line 4 [opener:9;]: T_DO => do
* token is an opening condition *
* already processed, skipping *
Process token 30 on line 6 [opener:9;]: T_WHITESPACE => ·
Process token 31 on line 6 [opener:9;]: T_WHILE => while
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 31 with depth 2
    Process token 32 on line 6 []: T_WHITESPACE => ·
    Process token 33 on line 6 []: T_OPEN_PARENTHESIS => (
    Process token 34 on line 6 []: T_WHITESPACE => ·
    Process token 35 on line 6 []: T_VARIABLE => $a
    Process token 36 on line 6 []: T_WHITESPACE => ·
    Process token 37 on line 6 []: T_CLOSE_PARENTHESIS => )
    Process token 38 on line 6 []: T_SEMICOLON => ;
    Process token 39 on line 6 []: T_WHITESPACE => \n
    Process token 40 on line 7 []: T_WHITESPACE => ········
    Process token 41 on line 7 []: T_RETURN => return
    Process token 42 on line 7 []: T_WHITESPACE => ·
    Process token 43 on line 7 []: T_LNUMBER => 5
    Process token 44 on line 7 []: T_SEMICOLON => ;
    Process token 45 on line 7 []: T_WHITESPACE => \n
    Process token 46 on line 8 []: T_WHITESPACE => \n
    Process token 47 on line 9 []: T_WHITESPACE => ····
    Process token 48 on line 9 []: T_DEFAULT => default
    => Found new opening condition before scope opener for 31:T_WHILE, bailing
Process token 32 on line 6 [opener:9;]: T_WHITESPACE => ·
Process token 33 on line 6 [opener:9;]: T_OPEN_PARENTHESIS => (
Process token 34 on line 6 [opener:9;]: T_WHITESPACE => ·
Process token 35 on line 6 [opener:9;]: T_VARIABLE => $a
Process token 36 on line 6 [opener:9;]: T_WHITESPACE => ·
Process token 37 on line 6 [opener:9;]: T_CLOSE_PARENTHESIS => )
Process token 38 on line 6 [opener:9;]: T_SEMICOLON => ;
Process token 39 on line 6 [opener:9;]: T_WHITESPACE => \n
Process token 40 on line 7 [opener:9;]: T_WHITESPACE => ········
Process token 41 on line 7 [opener:9;]: T_RETURN => return
Process token 42 on line 7 [opener:9;]: T_WHITESPACE => ·
Process token 43 on line 7 [opener:9;]: T_LNUMBER => 5
Process token 44 on line 7 [opener:9;]: T_SEMICOLON => ;
Process token 45 on line 7 [opener:9;]: T_WHITESPACE => \n
Process token 46 on line 8 [opener:9;]: T_WHITESPACE => \n
Process token 47 on line 9 [opener:9;]: T_WHITESPACE => ····
Process token 48 on line 9 [opener:9;]: T_DEFAULT => default
* token is an opening condition *
* searching for opener *
=> Begin scope map recursion at token 48 with depth 1
Process token 49 on line 9 []: T_COLON => :
=> Found scope opener for 48:T_DEFAULT
Process token 50 on line 9 [opener:49;]: T_WHITESPACE => \n
Process token 51 on line 10 [opener:49;]: T_WHITESPACE => ········
Process token 52 on line 10 [opener:49;]: T_FOREACH => foreach
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 52 with depth 2
    Process token 53 on line 10 []: T_WHITESPACE => ·
    Process token 54 on line 10 []: T_OPEN_PARENTHESIS => (
    Process token 55 on line 10 []: T_WHITESPACE => ·
    Process token 56 on line 10 []: T_VARIABLE => $a
    Process token 57 on line 10 []: T_WHITESPACE => ·
    Process token 58 on line 10 []: T_AS => as
    Process token 59 on line 10 []: T_WHITESPACE => ·
    Process token 60 on line 10 []: T_VARIABLE => $b
    Process token 61 on line 10 []: T_WHITESPACE => ·
    Process token 62 on line 10 []: T_CLOSE_PARENTHESIS => )
    Process token 63 on line 10 []: T_WHITESPACE => ·
    Process token 64 on line 10 []: T_OPEN_CURLY_BRACKET => {
    => Found scope opener for 52:T_FOREACH
    Process token 65 on line 10 [opener:64;]: T_WHITESPACE => \n
    Process token 66 on line 11 [opener:64;]: T_WHITESPACE => ············
    Process token 67 on line 11 [opener:64;]: T_VARIABLE => $e
    Process token 68 on line 11 [opener:64;]: T_WHITESPACE => ·
    Process token 69 on line 11 [opener:64;]: T_EQUAL => =
    Process token 70 on line 11 [opener:64;]: T_WHITESPACE => ·
    Process token 71 on line 11 [opener:64;]: T_CONSTANT_ENCAPSED_STRING => 'b'
    Process token 72 on line 11 [opener:64;]: T_SEMICOLON => ;
    Process token 73 on line 11 [opener:64;]: T_WHITESPACE => \n
    Process token 74 on line 12 [opener:64;]: T_WHITESPACE => ········
    Process token 75 on line 12 [opener:64;]: T_CLOSE_CURLY_BRACKET => }
    => Found scope closer (75:T_CLOSE_CURLY_BRACKET) for 52:T_FOREACH
Process token 76 on line 12 [opener:49;]: T_WHITESPACE => \n
Process token 77 on line 13 [opener:49;]: T_WHITESPACE => ········
Process token 78 on line 13 [opener:49;]: T_RETURN => return
=> Found scope closer (78:T_RETURN) for 48:T_DEFAULT
Process token 50 on line 9 [opener:9;]: T_WHITESPACE => \n
Process token 51 on line 10 [opener:9;]: T_WHITESPACE => ········
Process token 52 on line 10 [opener:9;]: T_FOREACH => foreach
* token is an opening condition *
* already processed, skipping *
Process token 76 on line 12 [opener:9;]: T_WHITESPACE => \n
Process token 77 on line 13 [opener:9;]: T_WHITESPACE => ········
Process token 78 on line 13 [opener:9;]: T_RETURN => return
Process token 79 on line 13 [opener:9;]: T_WHITESPACE => ·
Process token 80 on line 13 [opener:9;]: T_LNUMBER => 5
Process token 81 on line 13 [opener:9;]: T_SEMICOLON => ;
Process token 82 on line 13 [opener:9;]: T_WHITESPACE => \n
Process token 83 on line 14 [opener:9;]: T_CLOSE_CURLY_BRACKET => }
=> Found scope closer (83:T_CLOSE_CURLY_BRACKET) for 1:T_SWITCH
*** END SCOPE MAP ***

and the new code does this:

*** START SCOPE MAP ***
Start scope map at 1:T_SWITCH => switch
=> Begin scope map recursion at token 1 with depth 1
Process token 2 on line 2 []: T_WHITESPACE => ·
Process token 3 on line 2 []: T_OPEN_PARENTHESIS => (
Process token 4 on line 2 []: T_WHITESPACE => ·
Process token 5 on line 2 []: T_VARIABLE => $a
Process token 6 on line 2 []: T_WHITESPACE => ·
Process token 7 on line 2 []: T_CLOSE_PARENTHESIS => )
Process token 8 on line 2 []: T_WHITESPACE => ·
Process token 9 on line 2 []: T_OPEN_CURLY_BRACKET => {
=> Found scope opener for 1:T_SWITCH
Process token 10 on line 2 [opener:9;]: T_WHITESPACE => \n
Process token 11 on line 3 [opener:9;]: T_WHITESPACE => ····
Process token 12 on line 3 [opener:9;]: T_DEFAULT => default
* token is an opening condition *
* searching for opener *
=> Begin scope map recursion at token 12 with depth 1
Process token 13 on line 3 []: T_COLON => :
=> Found scope opener for 12:T_DEFAULT
Process token 14 on line 3 [opener:13;]: T_WHITESPACE => \n
Process token 15 on line 4 [opener:13;]: T_WHITESPACE => ········
Process token 16 on line 4 [opener:13;]: T_DO => do
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 16 with depth 2
    Process token 17 on line 4 []: T_WHITESPACE => ·
    Process token 18 on line 4 []: T_OPEN_CURLY_BRACKET => {
    => Found scope opener for 16:T_DO
    Process token 19 on line 4 [opener:18;]: T_WHITESPACE => \n
    Process token 20 on line 5 [opener:18;]: T_WHITESPACE => ············
    Process token 21 on line 5 [opener:18;]: T_VARIABLE => $a
    Process token 22 on line 5 [opener:18;]: T_WHITESPACE => ·
    Process token 23 on line 5 [opener:18;]: T_EQUAL => =
    Process token 24 on line 5 [opener:18;]: T_WHITESPACE => ·
    Process token 25 on line 5 [opener:18;]: T_CONSTANT_ENCAPSED_STRING => 'b'
    Process token 26 on line 5 [opener:18;]: T_SEMICOLON => ;
    Process token 27 on line 5 [opener:18;]: T_WHITESPACE => \n
    Process token 28 on line 6 [opener:18;]: T_WHITESPACE => ········
    Process token 29 on line 6 [opener:18;]: T_CLOSE_CURLY_BRACKET => }
    => Found scope closer (29:T_CLOSE_CURLY_BRACKET) for 16:T_DO
Process token 30 on line 6 [opener:13;]: T_WHITESPACE => ·
Process token 31 on line 6 [opener:13;]: T_WHILE => while
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 31 with depth 2
    Process token 32 on line 6 []: T_WHITESPACE => ·
    Process token 33 on line 6 []: T_OPEN_PARENTHESIS => (
    Process token 34 on line 6 []: T_WHITESPACE => ·
    Process token 35 on line 6 []: T_VARIABLE => $a
    Process token 36 on line 6 []: T_WHITESPACE => ·
    Process token 37 on line 6 []: T_CLOSE_PARENTHESIS => )
    Process token 38 on line 6 []: T_SEMICOLON => ;
    Process token 39 on line 6 []: T_WHITESPACE => \n
    Process token 40 on line 7 []: T_WHITESPACE => ········
    Process token 41 on line 7 []: T_RETURN => return
    Process token 42 on line 7 []: T_WHITESPACE => ·
    Process token 43 on line 7 []: T_LNUMBER => 5
    Process token 44 on line 7 []: T_SEMICOLON => ;
    Process token 45 on line 7 []: T_WHITESPACE => \n
    Process token 46 on line 8 []: T_WHITESPACE => \n
    Process token 47 on line 9 []: T_WHITESPACE => ····
    Process token 48 on line 9 []: T_DEFAULT => default
    => Found new opening condition before scope opener for 31:T_WHILE, bailing
Process token 48 on line 9 [opener:13;]: T_DEFAULT => default
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 48 with depth 2
    Process token 49 on line 9 []: T_COLON => :
    => Found scope opener for 48:T_DEFAULT
    Process token 50 on line 9 [opener:49;]: T_WHITESPACE => \n
    Process token 51 on line 10 [opener:49;]: T_WHITESPACE => ········
    Process token 52 on line 10 [opener:49;]: T_FOREACH => foreach
    * token is an opening condition *
    * searching for opener *
        => Begin scope map recursion at token 52 with depth 3
        Process token 53 on line 10 []: T_WHITESPACE => ·
        Process token 54 on line 10 []: T_OPEN_PARENTHESIS => (
        Process token 55 on line 10 []: T_WHITESPACE => ·
        Process token 56 on line 10 []: T_VARIABLE => $a
        Process token 57 on line 10 []: T_WHITESPACE => ·
        Process token 58 on line 10 []: T_AS => as
        Process token 59 on line 10 []: T_WHITESPACE => ·
        Process token 60 on line 10 []: T_VARIABLE => $b
        Process token 61 on line 10 []: T_WHITESPACE => ·
        Process token 62 on line 10 []: T_CLOSE_PARENTHESIS => )
        Process token 63 on line 10 []: T_WHITESPACE => ·
        Process token 64 on line 10 []: T_OPEN_CURLY_BRACKET => {
        => Found scope opener for 52:T_FOREACH
        Process token 65 on line 10 [opener:64;]: T_WHITESPACE => \n
        Process token 66 on line 11 [opener:64;]: T_WHITESPACE => ············
        Process token 67 on line 11 [opener:64;]: T_VARIABLE => $e
        Process token 68 on line 11 [opener:64;]: T_WHITESPACE => ·
        Process token 69 on line 11 [opener:64;]: T_EQUAL => =
        Process token 70 on line 11 [opener:64;]: T_WHITESPACE => ·
        Process token 71 on line 11 [opener:64;]: T_CONSTANT_ENCAPSED_STRING => 'b'
        Process token 72 on line 11 [opener:64;]: T_SEMICOLON => ;
        Process token 73 on line 11 [opener:64;]: T_WHITESPACE => \n
        Process token 74 on line 12 [opener:64;]: T_WHITESPACE => ········
        Process token 75 on line 12 [opener:64;]: T_CLOSE_CURLY_BRACKET => }
        => Found scope closer (75:T_CLOSE_CURLY_BRACKET) for 52:T_FOREACH
    Process token 76 on line 12 [opener:49;]: T_WHITESPACE => \n
    Process token 77 on line 13 [opener:49;]: T_WHITESPACE => ········
    Process token 78 on line 13 [opener:49;]: T_RETURN => return
    => Found scope closer (78:T_RETURN) for 48:T_DEFAULT
Process token 50 on line 9 [opener:13;]: T_WHITESPACE => \n
Process token 51 on line 10 [opener:13;]: T_WHITESPACE => ········
Process token 52 on line 10 [opener:13;]: T_FOREACH => foreach
* token is an opening condition *
* already processed, skipping *
Process token 76 on line 12 [opener:13;]: T_WHITESPACE => \n
Process token 77 on line 13 [opener:13;]: T_WHITESPACE => ········
Process token 78 on line 13 [opener:13;]: T_RETURN => return
=> Found scope closer (78:T_RETURN) for 12:T_DEFAULT
Process token 14 on line 3 [opener:9;]: T_WHITESPACE => \n
Process token 15 on line 4 [opener:9;]: T_WHITESPACE => ········
Process token 16 on line 4 [opener:9;]: T_DO => do
* token is an opening condition *
* already processed, skipping *
Process token 30 on line 6 [opener:9;]: T_WHITESPACE => ·
Process token 31 on line 6 [opener:9;]: T_WHILE => while
* token is an opening condition *
* searching for opener *
    => Begin scope map recursion at token 31 with depth 2
    Process token 32 on line 6 []: T_WHITESPACE => ·
    Process token 33 on line 6 []: T_OPEN_PARENTHESIS => (
    Process token 34 on line 6 []: T_WHITESPACE => ·
    Process token 35 on line 6 []: T_VARIABLE => $a
    Process token 36 on line 6 []: T_WHITESPACE => ·
    Process token 37 on line 6 []: T_CLOSE_PARENTHESIS => )
    Process token 38 on line 6 []: T_SEMICOLON => ;
    Process token 39 on line 6 []: T_WHITESPACE => \n
    Process token 40 on line 7 []: T_WHITESPACE => ········
    Process token 41 on line 7 []: T_RETURN => return
    Process token 42 on line 7 []: T_WHITESPACE => ·
    Process token 43 on line 7 []: T_LNUMBER => 5
    Process token 44 on line 7 []: T_SEMICOLON => ;
    Process token 45 on line 7 []: T_WHITESPACE => \n
    Process token 46 on line 8 []: T_WHITESPACE => \n
    Process token 47 on line 9 []: T_WHITESPACE => ····
    Process token 48 on line 9 []: T_DEFAULT => default
    => Found new opening condition before scope opener for 31:T_WHILE, bailing
Process token 48 on line 9 [opener:9;]: T_DEFAULT => default
* token is an opening condition *
* already processed, skipping *
Process token 49 on line 9 [opener:9;]: T_COLON => :
Process token 50 on line 9 [opener:9;]: T_WHITESPACE => \n
Process token 51 on line 10 [opener:9;]: T_WHITESPACE => ········
Process token 52 on line 10 [opener:9;]: T_FOREACH => foreach
* token is an opening condition *
* already processed, skipping *
Process token 76 on line 12 [opener:9;]: T_WHITESPACE => \n
Process token 77 on line 13 [opener:9;]: T_WHITESPACE => ········
Process token 78 on line 13 [opener:9;]: T_RETURN => return
Process token 79 on line 13 [opener:9;]: T_WHITESPACE => ·
Process token 80 on line 13 [opener:9;]: T_LNUMBER => 5
Process token 81 on line 13 [opener:9;]: T_SEMICOLON => ;
Process token 82 on line 13 [opener:9;]: T_WHITESPACE => \n
Process token 83 on line 14 [opener:9;]: T_CLOSE_CURLY_BRACKET => }
=> Found scope closer (83:T_CLOSE_CURLY_BRACKET) for 1:T_SWITCH
*** END SCOPE MAP ***

The real problem is that
=> Found scope closer (41:T_RETURN) for 12:T_DEFAULT (old)
and
=> Found scope closer (78:T_RETURN) for 12:T_DEFAULT (new)
are different. So the new code is not assigning the correct closer.

gsherwood added a commit that referenced this issue Feb 15, 2016
…or bug #879 broke tokenizing for some case statements (bug #893)
@gsherwood
Copy link
Member

Can you try again now that I've pushed a fix for this tokenizing issue. It might have been the only cause.

@aik099
Copy link
Contributor Author

aik099 commented Feb 15, 2016

Yes it did fix the issue and didn't broke any of the other sniffs. Thanks.

But what PHP_CodeSniffer commit created such a problem?

@aik099
Copy link
Contributor Author

aik099 commented Feb 15, 2016

Ah, I see you've specified it in commit message for a fix.

@gsherwood
Copy link
Member

Thanks for testing the fix.

gsherwood added a commit that referenced this issue May 12, 2016
… to fix error when comment is not a docblock
@gsherwood
Copy link
Member

Note that commit 0434c48 has nothing to do with this issue. It should have been bug #983 in the commit message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants