Skip to content

Conversation

@haidubogdan
Copy link
Contributor

@haidubogdan haidubogdan commented Nov 17, 2025

This pull request tries to fix issue #7803 .

Php brace matcher used for highlighting (if, foreach ) statements like <?php if ($x): ?> ...<?php endif;?> loops through tokens until it finds the opening parenthesis.
The current implementation can trigger a infinite loop for a embedded usage of Php lexer as there is no exit mechanism beside the PhpTokenId.TOKEN check.

@troizet troizet added the PHP [ci] enable extra PHP tests (php/php.editor) label Nov 17, 2025
@mbien mbien added the ci:dev-build [ci] produce a dev-build zip artifact (7 days expiration, see link on workflow summary page) label Nov 17, 2025
@apache apache locked and limited conversation to collaborators Nov 17, 2025
@apache apache unlocked this conversation Nov 17, 2025
@troizet troizet requested review from junichi11 and tmysik November 18, 2025 02:28
Copy link
Member

@tmysik tmysik left a comment

Choose a reason for hiding this comment

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

Approved, thank you.

@matthiasblaesing
Copy link
Contributor

@haidubogdan It would be helpful to see an example that causes a loop. While I agree, that a test would be the best case, I think a review can substitute that. Infinite loops are really ugly to test for (at least the failing variant).

@haidubogdan
Copy link
Contributor Author

@matthiasblaesing
Sure, a mock module with php embedding would be enough?
I didn't work with functional test netbeans yet. But I can check some triggers action on a thread for brace matcher.

@matthiasblaesing
Copy link
Contributor

@haidubogdan I'd like to "see" the problem, so instructions how to reproduce (if necessary with code) would be great.

@haidubogdan
Copy link
Contributor Author

@matthiasblaesing, @tmysik

I've created this branch master...haidubogdan:netbeans:t_php_bracematcher_infinite_loop_test

It's just for simulating the infinite loop.

Context:

I have a custom language support (text/x-php-test).
I include embedding on the lexer site for some tokens using PHPTokenId.languageInPHP() interpolated in {{ }}
The snippet code is :{{(TestClass^:)}} .
I've put dump of a counter in PHPBracesMatcher on line 119
Doing the test for the class EmbeddedBraceMatchingTest will output the infinite counter progress.

@matthiasblaesing
Copy link
Contributor

@haidubogdan thank you. It makes sense that this was not caught before as for "normal" PHP situations this problem can't manifest. The scanning stops when the first token with a ID <> PHPTokenId.PHP_TOKEN is found. That will be PHP_OPENTAG. That will always be present as without it we don't get into PHP mode. This needs the embedding case to happen. The problem manifests because LexUtilities.findPreviousToken will report the first token if the target set is not found.

So the change makes sense to me. An alternative I would see is to guard the ts.movePrevious inside the do-while loop like this:

                        if(! ts.movePrevious()) {
                            break;
                        }

The movePrevious will report false and exit the loop for the first element in the token sequence.

What do you think?

@haidubogdan
Copy link
Contributor Author

@haidubogdan thank you. It makes sense that this was not caught before as for "normal" PHP situations this problem can't manifest. The scanning stops when the first token with a ID <> PHPTokenId.PHP_TOKEN is found. That will be PHP_OPENTAG. That will always be present as without it we don't get into PHP mode. This needs the embedding case to happen. The problem manifests because LexUtilities.findPreviousToken will report the first token if the target set is not found.

So the change makes sense to me. An alternative I would see is to guard the ts.movePrevious inside the do-while loop like this:

                        if(! ts.movePrevious()) {
                            break;
                        }

The movePrevious will report false and exit the loop for the first element in the token sequence.

What do you think?

@matthiasblaesing
Seems cleaner :) .
I will use this solution.

@haidubogdan haidubogdan force-pushed the issue_7803_php_braces_matcher branch 3 times, most recently from 2fc6ba1 to a318ee7 Compare December 8, 2025 12:11
@matthiasblaesing
Copy link
Contributor

@haidubogdan thanks for the update. Looks cleaner and I appreciate the added test!
@tmysik would you mind having a second look? The implementation changed and a test was added. I would be inclined to merge.

Copy link
Member

@tmysik tmysik left a comment

Choose a reason for hiding this comment

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

Looks good to me, just a minor comment added (related to the coding style). Thank you

…Php Lexer Language

closes apache#7803

Exit brace matcher loop for matching semi-colon-colon braces if token sequence can't movePrevious.
Add unit test with custom php embedded language for timeout check on braces matcher.
Max execution time 3000 ms.
@haidubogdan haidubogdan force-pushed the issue_7803_php_braces_matcher branch from a318ee7 to 0a3d075 Compare December 9, 2025 04:22
@mbien mbien added this to the NB29 milestone Dec 9, 2025
@matthiasblaesing
Copy link
Contributor

@haidubogdan Thank you!

@matthiasblaesing matthiasblaesing merged commit fb2aab3 into apache:master Dec 9, 2025
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ci:dev-build [ci] produce a dev-build zip artifact (7 days expiration, see link on workflow summary page) PHP [ci] enable extra PHP tests (php/php.editor)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Php Braces Matcher triggers an infinite loop for embedded php text with ":" character wrapped in parenthesis or brackets

5 participants