Thursday, April 19, 2012

How is this valid php code?


I'm modifying a wordpress template and I'm curious as to how this is a valid control structure in PHP. Anyone have any insight?




<?php if(condition): ?>
<?php if(!condition || !function()) ?>
<?php elseif(condition): ?>
<?php if(!condition || !function()) ?>
<?php endif; ?>



If I remove all the tags, I get (with more sane indentation):




<?php
if(condition):
if(!condition || !function())
elseif(condition):
if(!condition || !function())
endif;
?>



which is invalid because the indented if statements don't end. So how/why is this code valid if there are opening and closing php tags everywhere?





Edit for Kerrek SB. Make a php file and run it. It's valid:




<?php if(true): ?>
<?php if(true) ?>
<?php endif; ?>
<?php echo 'here'; ?>


Source: Tips4all

7 comments:

  1. Your (reduced) example code is equivalent to this:

    <?php
    if(condition):
    if(!condition || !function()) { }
    endif;
    ?>


    Or even:

    <?php
    if(condition):
    if(!condition || !function());
    endif;
    ?>


    By closing off the <?php tag, you appear to get an "empty statement" for free.

    Your real example could be one-lined like so:

    <?php if(true): if(true); endif; echo 'here'; ?>


    But note that an elseif makes this ambigous!

    <?php
    if(condition):
    if(!condition || !function());
    elseif(condition): // Bogus! which block does this belong to?
    if(!condition || !function());
    endif;
    ?>


    We'd have to disambiguate this:

    <?php
    if(condition):
    {
    if(!condition || !function());
    }
    elseif(condition):
    {
    if(!condition || !function());
    }
    endif;
    ?>


    Now it's clear, but now we could have spared ourselves the colon syntax altogether.

    Thanks to Lekensteyn for pointing this out!

    See the discussion below for further oddities.

    ReplyDelete
  2. Your code won't work because PHP removes a single newline after ?> if exist. ?>[newline]<?php would be the same as ?><?php

    Something like the below would make more sense (function replaced by fn since function is a keyword ;) ):

    <?php if(condition): ?>
    <?php if(!condition || !fn()) ?>
    some
    <?php elseif(condition): ?>
    <?php if(!condition || !fn()) ?>
    thing
    <?php endif; ?>


    It'd be interpreted as:

    <?php
    if(condition):
    if(!condition || !fn()) echo "some";
    elseif(condition):
    if(!condition || !fn()) echo "thing";
    endif;
    ?>


    Note that there is no : on two ifs, those will be treated like an if which expect a body next to it. Written in an other way:

    <?php
    if (condition) {
    if (!condition || !fn()) {
    echo "some";
    }
    } else if (condition) {
    if (!condition || !fn()) {
    echo "thing";
    }
    }
    ?>

    ReplyDelete
  3. If you are not using you need to follow up with: for logic after nested if(s).

    <?php
    function foobar() {
    return true;
    }
    function bar() {
    return false;
    }

    $foobar = true;
    $bar = false;

    if($foobar):
    if(!$bar || !foobar()):
    echo 'foobar';
    endif;
    elseif($bar):
    if(!$foobar || !bar()):
    echo 'bar';
    endif;
    endif;

    ReplyDelete
  4. "which is invalid because the indented if statements don't end"

    Yes they do. As stated at php.net, the mixing of both notation isn't supported, which means, you have a new code-block (the two if() without following double point) within your if():, elseif(): and endif;.

    if(condition):
    if(!condition || !function())
    // do nothing here, end of first inner if
    elseif(condition):
    if(!condition || !function())
    // also do nothing here, end of second inner if
    endif; // end of outer if/else

    ReplyDelete
  5. which is invalid because the indented if statements don't end


    Actually, they do. The ?> terminates the statement like any other, so:

    <?php
    if(condition):
    if(!condition || !function());
    elseif(condition):
    if(!condition || !function());
    endif;
    ?>


    Were this not the case, then the following would also be a syntax error:

    Lol <?php echo "cakes" ?> extravaganza <?php echo "innit" ?>

    ReplyDelete
  6. What I see is that you are mixing up the two possible form of building an 'if' construct in php.

    The first form is either:

    if ( condition ) instruction;

    if ( condition ) { more; than; one; instructions; }


    The second one is:

    if ( condition ): instruction; another; endif;


    When you write

    <?php if (condition) ?>


    ...without a '{' or a ':' before the closing '?>' tag, in fact you're truncating the if command omitting the instructions, and PHP accept this. When you'll open the php tag again, you're already outside the if.

    The same does NOT apply if you just "forget" the instruction whitin a single block, where an 'endif;' is not allowed as an instruction;

    So:

    <?php if (condition) ?>
    anything outside the if
    <?php instruction_outside_the_if; ?>


    ... is meaningless (because you elaborate an if but do nothing after it) but is not a strict error.

    <?php if (condition 1): ?> <!-- note the ':' in this if -->
    <?php if (condition 2) ?>
    anything outside the second if but inside the first
    <?php endif; ?> <!-- this closes the first if


    ...here you have a first if that works, but the second is still empty.

    <?php if (condition 1): ?> <!-- note the ':' in this if -->
    <?php if (condition 2) ?>
    anything outside the second if but inside the first
    <?php else: ?>
    still outside the second if but inside the first, in the ELSE part
    <?php endif; ?> <!-- this closes the first if


    ...this is more or less like your example, where the else (or elseif) belongs to the first if.



    <?php if (true) ?>
    <?php elseif(foo){ }; ?>


    That's another exception, as you're not putting anything between a closing tag and opening tag, so the parser "optimizes" them out (is not exactly like that, but it's a correct approximation). Try putting anything between the two php tags, like:

    <?php if (true) ?>
    anything
    <?php elseif(foo){ }; ?>


    ... and see the "syntax error, unexpected T_ELSEIF" appear.

    ReplyDelete
  7. Check out PHP.net's explaination

    ReplyDelete