php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #75138 Nested "IF" triggers Exception when it is before "else:" or "elseif:".
Submitted: 2017-08-30 15:31 UTC Modified: 2018-04-12 06:38 UTC
Votes:1
Avg. Score:1.0 ± 0.0
Reproduced:0 of 1 (0.0%)
From: jcmarchi at gmail dot com Assigned:
Status: Not a bug Package: *Programming Data Structures
PHP Version: Irrelevant OS: All
Private report: No CVE-ID: None
 [2017-08-30 15:31 UTC] jcmarchi at gmail dot com
Description:
------------
When using Block Alternative Syntax for IF statement (if: elseif: else: endif;) a nested "IF" will trigger a PHP EXCEPTION error saying: syntax error, unexpected ':' if it exists immediately BEFORE the "else:" or "elseif".

Test script:
---------------
Example 1:

<?php
$bar = 'bar';
$foo = 'foo';

if (isset($bar)):
   if (isset($foo)) echo "Both are set.";
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;
?>


Example 2:

<?php
$foo = 'foo';
$bar = 'bar';

if (isset($bar)):
   if (isset($foo)) {
     echo "Both are set.";
   }
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;
?>

Both examples fail with the same error.

Expected result:
----------------
Test Scripts fail. So, this is not a result but a "workaround" for the interpreter to stop failing.

It fixes the parsing error to add an extra semicolon AFTER the last semicolon (before the "elseif:" or "else:") as shown below:

<?php
$foo = 'foo';
$bar = 'bar';

if (isset($bar)):
  if (isset($foo)) echo "Both are set.";;
elseif (isset($foo)):
  echo "Only 'foo' is set.";
else:
  echo "Only 'bar' is set.";
endif;
?>

Or, for the Example 2 (nested traditional "if" with brackets), to add a semicolon AFTER the last closing bracket:

<?php
$foo = 'foo';
$bar = 'bar';

if (isset($bar)):
   if (isset($foo)) {
     echo "Both are set.";
   };
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;
?>

Ultimately, an empty line with a semicolon in it also do the trick, but none are "PHP standard" and create a huge trouble when debugging. Some people may even get mistaken with the statements and do something foolish like this broken logic (which is validated by the PHP interpreter, of course):

<?php
$foo = 'foo';
$bar = 'bar';

if (isset($bar)):
  if (isset($foo)): echo "Both are set.";
elseif (isset($foo)):
  echo "Only 'foo' is set.";
else:
  echo "Only 'bar' is set.";
endif;
?>




Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2017-08-30 15:57 UTC] peehaa@php.net
-Status: Open +Status: Not a bug
 [2017-08-30 15:57 UTC] peehaa@php.net
From the docs (http://php.net/manual/en/control-structures.alternative-syntax.php):

> Mixing syntaxes in the same control block is not supported.
 [2017-08-30 20:47 UTC] jcmarchi at gmail dot com
@peehaa. Did you actually READ the bug report????? I don't think so... 

IT IS A BUG! In the interpreter... 

The "mixed syntax" is merely an example to display the wrong requirement of the last ";" (and it WORKS). The reported bug fails regardless of the "mixed syntax". Check code examples before saying anything!

BTW, it is outrageous the lack of understanding some people has when RTFM! The statement "Mixing syntaxes in the same control block is not supported" refers to the SAME LEVEL BLOCK, not to NESTED BLOCKS (the reason why IT WORKS).

-- /me losing faith in the Technology world...  :'(
 [2017-08-30 20:59 UTC] nikic@php.net
The assessment of peehaa is correct. PHP is not a whitespace-sensitive language, so the way your code is actually interpreted is as follows:

if (isset($bar)):
   if (isset($foo)) echo "Both are set.";
   elseif (isset($foo)):
      echo "Only 'foo' is set.";
   else:
      echo "Only 'bar' is set.";
   endif;

This mixes both styles on the same if/elseif/else structure and is, as already noted, not permitted.

Please see https://en.wikipedia.org/wiki/Dangling_else for more information about this ambiguity.
 [2017-08-30 21:25 UTC] jcmarchi at gmail dot com
Sorry, but you are also missing the point... 

Try this:

<?php
$bar = 'bar';
$foo = 'foo';

if (isset($bar)):
   if (isset($foo)) echo "Both are set.";
   echo "Working now";
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;
?>

Now, try this (also works):

<?php
$bar = 'bar';
$foo = 'foo';

if (isset($bar)):
   if (isset($foo)) echo "Both are set.";
   ;  
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;
?>

Question is: WHY the semicolon after the last nested IF statement is needed ONLY when the last IF is immediately BEFORE an "else:" or "elseif:" statement?

Or, better said: why it only fails when the immediate last command BEFORE an  "else:" or "elseif:" is an IF statement without an extra (unnecessary, wrongly required) semicolon (;)?

Answer: IT IS A BUG in the interpreter/parser!

It is not related to "code style", or "indentation", or "coding rules", or "standards", or "language nuances", or "Solar eclipse", etc... It is related to a tested in uncountable scenarios (and real codes) where in all cases it fails exactly the same way (code example is just it: a code example).
 [2017-08-30 22:02 UTC] spam2 at rhsoft dot net
just take your code, remove any whitespace and look again at it
 [2017-08-30 22:38 UTC] jcmarchi at gmail dot com
OMG... Let me draw a picture!!!!

[ IF (CONDITION):
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "IF" CONDITION WHEN IT IS TRUE
   ]

ELSEIF (ANOTHER CONDITION): 
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "ELSEIF:" CONDITION WHEN IT IS TRUE
   ]

ELSE: 
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "ELSE:" CONDITION WHEN IT IS TRUE
   ]
]

Now, based on the nested blocks above, it should NOT fail when you NEST "whatever" statement inside!

Each nested block is initiated by the ":" and end when it reaches an "ELSEIF" or an "ELSE" (each one of those having its own nested blocks again after their own ":"), or an "ENDIF" (when the first "IF" ends).

It said such code should NOT fail:

[ IF (CONDITION):
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "IF" CONDITION WHEN IT IS TRUE

   IF (ANOTHER CONDITION) [ DO THIS; ] <--- THIS SHOULD BE NESTED INTO THE SAME LINE "IF"
   ]

ELSEIF (ANOTHER CONDITION): 
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "ELSEIF:" CONDITION WHEN IT IS TRUE
   ]

ELSE: 
   [ 
    ANYTHING IN HERE SHOULD BE A NESTED BLOCK FOR THE "ELSE:" CONDITION WHEN IT IS TRUE
   ]
]

Or, based on your comment, this following logic should run, but provide a wrong result (and it actually works as expected):

$bar = 'bar';
$foo = 'foo';

if (isset($bar)):
   if (isset($foo)) echo "Both are set.";
   echo " It actually works.";
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;

The above code will return: "Both are set. It actually works.", while based on your "thinking" it should return blank.

Also, just for thinking purposes, try this (it works):

$bar = 'bar';
$foo = 'foo';

if (isset($bar)):
   echo "It actually works. ";
   { if (isset($foo)) echo "Both are set."; }
elseif (isset($foo)):
   echo "Only 'foo' is set.";
else:
   echo "Only 'bar' is set.";
endif;

Does not matter how you look at it, if a nested IF statement (any coding style) precedes an "else:" or an "elseif:" statement, a DOUBLE semicolon is required to make it work (or sub-enclosure it under curly brackets).

I can't believe you cannot see it. More (simplified) examples:

// FAIL:
$bar = 'bar';
if (true):
   if ($bar == 'bar') echo "Got 'bar'.";
else:
   echo "Didn't get 'bar'.";
endif;

// WORKS:
$bar = 'bar';
if (true):
   if ($bar == 'bar') echo "Got 'bar'.";;
else:
   echo "Didn't get 'bar'.";
endif;

// WORKS:
$bar = 'bar';
if (true):
   { if ($bar == 'bar') echo "Got 'bar'."; }
else:
   echo "Didn't get 'bar'.";
endif;

// FAIL:
$bar = 'bar';
if (true):
   if ($bar == 'bar') { echo "Got 'bar'."; }
else:
   echo "Didn't get 'bar'.";
endif;

// WORKS:
$bar = 'bar';
if (true):
   if ($bar == 'bar') { echo "Got 'bar'."; };
else:
   echo "Didn't get 'bar'.";
endif;
 [2017-08-30 22:58 UTC] spam2 at rhsoft dot net
frankly, i consider to propose a RFC to disallow such coding styles at all and would fire anybody writing such code immediately

the same for
if($× === $y) do_something;
without {}
 [2017-08-30 23:12 UTC] yohgaki@php.net
Searching "dangling else" would help to understand this issue. It's common issue for languages/programs.
 [2017-08-30 23:15 UTC] jcmarchi at gmail dot com
If you are trying to (childishly) offend me, well... Sorry to disappoint you. Why? Because I agree with you! Some "freedoms" while coding should be chopped out. However, while language allows it, it should be (at least) bug-free.

What is unbelievable, however, is people that keep focusing on the "wrong wrongs" simply to ignore the problem at hand. Those are the ones I DO fire (the ones that go to the extreme of self-blindness simply to avoid accepting they are wrong).

I bet you didn't even play with the code examples... Or even created your own... Did you?

/me -- All hopes for the technology world are lost, forever!
 [2017-08-30 23:19 UTC] spam2 at rhsoft dot net
no, i didn't play with the samples on my smartphone BUT DID YOU read the link about "dangling else"?
 [2017-08-30 23:36 UTC] jcmarchi at gmail dot com
@yohgaki, thank you for providing valuable insight to the (so far) useless discussion!

The parsing issue in the PHP case can be easily observed in the "double semicolon" example. The question is: why, when adding an extra semicolon before the "else:" or "elseif:" magically resolves the "nested IF" problem? After all, in PHP, blank lines should not affect the logic, right? Well, in this case, IT DOES!

It is not exactly ambiguity problem as no more than one correct parse tree actually exist (only in the eye of the blind ones). If an "IF" statement nested block begins with ":" and ends when an "else:" or "elseif:" is found, the "nested IFs" should all work or fail equivalently, but just the last "IF" fails (and it doesn't have a ":" to create ambiguity).

It is crazy to see people who should be going to the PHP Source Code and look for answers (or solutions), or even bring something factual to the table, discussing the quality of "code samples". The only things that matter in those samples are the ";;" and "};", which fixes the "nested IF" issue, and AFAIK such approach is not even part of the PHP coding principle.

It is depressing, if not tragic!
 [2017-08-30 23:56 UTC] jcmarchi at gmail dot com
@spam2, YES, I read it. More than that, I analyzed what is said there, I  read more about "scannerless parsing" (not the case because it doesn't apply to PHP, which is bytecode by Zend Engine) and other processes discussed there, and I understood the whole discussion around the "dangling else" condition for the languages that are affected by it (PHP is not one of them).

As much as it would be easy to simply accept that, read my answer to @yohgaki. He brought material to research and added good points to the discussion, but sadly it didn't explain what I replied to him, neither excused PHP from behaving the way it behaves for in the proposed conditions.

You should really try some code samples. Create your own! Target the ";;" and "};" specific examples as reference. Remove these elements, redesign the logic without "unnesting" the problematic code. Prove me wrong about the bug or explain how  ";;" and "};" fixes the issue even when it is not part of PHP concept and remember: "blank lines" should not affect the logic in place.

Then this discussion may get to a valuable conclusion or an acceptance of the BUG existence. :)
 [2017-08-31 14:20 UTC] jcmarchi at gmail dot com
@peehaa, you should re-open this BUG report because IT IS a bug! Or, at least, have the decency to gather a second opinion about it. You came to a conclusion too quickly and based on a sole visual analysis of the code samples (I bet you didn't even try some scenarios, did you?), and also by misinterpreting the PHP manual guidance... :S

If a double semicolon ";;" or a semicolon after the closing curly bracket "};" requirement to fix a code parsing problem is not considered a bug, then I don't know what will ever be.

By keeping this BUG REPORT open, other PHP developers (more willing to really make PHP better), will have a chance to look into it and come up with a real solution.

Thank you.
 [2017-08-31 14:34 UTC] requinix@php.net
If you want a fifth opinion, I agree with peehaa, nikic, rhsoft, and yohgaki: this isn't a bug.

The second semicolon you're making a fit about changes the meaning of the structure. It introduces another statement (even if empty) and breaks the if/else pair apart, thus avoiding the dangling else problem. https://3v4l.org/0vsMV
It's not a workaround. It's syntax.
 [2017-08-31 16:15 UTC] jcmarchi at gmail dot com
Ok. Very well... I can accept it as not being a "BUG", but a "NEW SYNTAX requirement" (rules are rules). Next, it must be added to the language manual definition in regards to semicolon usage and purpose.

I then propose the following addition to the PHP official documentation:

====================
When using "alternative syntax for control structures" observe the fact that when an "IF" statement is required to be nested into another IF-ELSEIF-ELSE block and it casually ends up being the last statement of the nested block, it is a LANGUAGE REQUIREMENT to terminate it with double semicolon (;;) to prevent the "dangling else" effect.
====================

Or, better, we can add it to the same (miss)interpretation concept, keeping documentation standards:

====================
Note:
Mixing syntaxes in the same control block is not supported.

Note:
A nested "if" statement immediately before an "else:" or "elseif: " statement in control block is not supported.
====================

Or, even better! Let's simply ignore it all and leave it as it is. What harm can an extra ";" do anyway, right?

Well done everyone. Good job!
 [2017-08-31 16:15 UTC] peehaa@php.net
I am sorry for you, but as several people by now have stated: this is not a bug.

No matter how many personal attacks on volunteers or how loud you are shouting it will not the status of this bug report. :-)

At this point I see no reason to keep going at it.

So I would suggest you to do just that.

Sorry, but your problem does not imply a bug in PHP itself.  For a
list of more appropriate places to ask for help using PHP, please
visit http://www.php.net/support.php as this bug system is not the
appropriate forum for asking support questions.  The support channels will be able to provide an explanation
for you.

Thank you for your interest in PHP.
 [2018-04-11 10:44 UTC] edelgrande at brennerplatz dot com
Others already wrote in different ways what I think.

Why should you write such a bad code?
In order to be consistent, please use the same syntax in all the snippet. I could be a colleague who should put his hands over your code!
This way, you can see there is much more clarity and no complex behaviour difficult to understand and analyse, be it a bug or not.

Keep it simple, there are real PHP bugs out there we already have to cope with!

<?php
if (isset($bar)):
	if (isset($foo)):
		echo "Both are set.";
	endif;
elseif (isset($foo)):
	echo "Only 'foo' is set.";
else:
	echo "Only 'bar' is set.";
endif;
?>
 [2018-04-12 05:45 UTC] jcmarchi at gmail dot com
Why are you replying to a closed dead thread from last year? Lack of attention? Boredom? Trolling for fun? Does someone need a hug? Hum?

You missed the party. It is all resolved. @requinix already answered it all when he pointed that the second semicolon changes the meaning of the structure by introducing another (empty) statement thus breaking the if/else pair apart, avoiding the "dangling else" problem. He also said: it's syntax. No bug, no problem, no issue. Simply "a syntax" (whatever it may mean)!

Problem solved, OK? I am good with that and I'd already moved on... News flash: There is life after PHP, did you know? You can move on too... 

Notwithstanding, let take this opportunity to apologize for the presented "code sample". Now I understand how beneath your "impeccable ultra-high standard coding levels" it was. It should have been a simple "code sample", something to pinpoint the issue and allow a quick TRY OUT of the proposed problem (NOT A BUG, NOT A BUG!!!!), and I failed to understand the rule that it should have been written as a poetry.

Silly me... I don't know why I assumed people here would have enough intelligence to understand what a "code sample" is: a piece of code NOT extracted from an application but built to be a simple PROBLEM DEMONSTRATION. Next time I promise I will try to raise my standards to show off my coding skills (which is what this place is all about, right?). I promise nothing less than a couple of hundred lines of code properly namespaced, with Classes and inheritances, Traits, a few recursions here and there, some special methods to make it nice and callables all over the place to make it fancy, etc.. All nine yeards... (Ops... No no no!!! There will be no next time. I AM OUT OF HERE!!!).

Let's do better: while I am writing a rule in my email to send messages from this thread to /dev/null, pretending I give a damn for whatever you say or think, you should stop replying to dead threads and go back pretending you actually do anything about "chopping real bugs"... go... go... 

P.S.: "Why should you write such a bad English?"
 [2018-04-12 06:38 UTC] requinix@php.net
-Block user comment: No +Block user comment: Yes
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Thu Mar 28 09:01:26 2024 UTC