I've created a very basic BBCode parser for my webpage (see attached). In my class I created a way to use [NOPARSE]

...

[/NOPARSE] as I would use on these message boards. Anything within these tags is highlighted with highlight_string() however highlight_string will not work if not provided an opening <?php tag. I thought about substr to see if the "code" within the php tags starts with <?php and prepend it if not, then remove it afterward.

The problem with this approach is what if it doesn't start with php code and there is an opening <?php tag later? Now it doesn't highlight properly! On the other hand there may be an opening tag AFTER a closing tag meaning the beginning is php code and should have the opening tag prepended... WHOA there! Basically what I'm looking for is a way to handle this so that the code can be highlighted properly without always having an opening tag at the start.

For example, the following both highlight properly on these boards, but not with my code:

function myFunction($var) {
   return $var;
}
<?php
function myFunction($var) {
   return $var;
}

Any thoughts how I should do this?

    First idea is to use strpos() to look for both "?>" and "<?php"; see which, if either, appears first and go from there.

      Thanks weedpacket, definitely the advice I was looking for, much appreciated! Here is the working function for those interested (opening tag added, because my code broke the boards highlighting)

      <?php
      	private function PHPFind() {
      		//*/ Set up replace array for PHP tags
      		if( preg_match_all(self::PHPPattern,$this->str,$matches) ) {
      			foreach( $matches[1] as $k => $v ) {
      				$added = FALSE;
      				$firstopen = strpos($v,'<?php');
      				$firstclose = strpos($v,'?>');
      				if( $firstopen === FALSE ) {
      					$v = '<?php '.$v;
      					$added = TRUE;
      				} elseif( $firstclose !== FALSE && $firstclose < $firstopen ) {
      					$v = '<?php '.$v;
      					$added = TRUE;
      				}
      				$v = highlight_string($v,TRUE);
      				if( $added === TRUE ) {
      					$open = substr($v,0,37);
      					$close = substr($v,87);
      					$v = $open.$close;
      				}
      				$this->PHPreplace[$k] = '<div class="code">'.$v.'</div>';
      			}
      		}
      	}

        Just thought I'd note that

        $added = FALSE;
        if( $firstopen === FALSE ) {
            $v = '<&#63;php '.$v;
            $added = TRUE;
        } elseif( $firstclose !== FALSE && $firstclose < $firstopen ) {
            $v = '<&#63;php '.$v;
            $added = TRUE;
        } 
        

        could be written as

        $added = ($firstopen === FALSE) || ( $firstclose !== FALSE && $firstclose < $firstopen );
        if($added)
        {
        	$v = '<&#63;php ' . $v;
        }
        

        Or indeed as

        if($firstclose === FALSE) $firstclose = INF;
        if($firstopen === FALSE) $firstopen = INF;
        $added = ($firstclose < $firstopen);
        if($added)
        {
        	$v = '<&#63;php ' . $v;
        }
        

        but that relies on an implementation detail (namely that, according to PHP, &#8734;<&#8734😉 which might or might not continue to hold.

          $added = ($firstopen === FALSE) || ( $firstclose !== FALSE && $firstclose < $firstopen ); 

          This line doesn't make sense to me, set it to a or b... how does it know which one to set it to?

            Derokorian;10991708 wrote:

            This line doesn't make sense to me, set it to a or b... how does it know which one to set it to?

            It doesn't matter. Either the entire expression is true, or it is false.

            Weedpacket;10991705 wrote:

            Just thought I'd note that

            $added = FALSE;
            if( $firstopen === FALSE ) {
                $added = TRUE;
            } elseif( $firstclose !== FALSE && $firstclose < $firstopen ) {
                $added = TRUE;
            } 
            
            Derokorian;10991708 wrote:
            $added = ($firstopen === FALSE) || ( $firstclose !== FALSE && $firstclose < $firstopen ); 

            (true || whatever) => true
            So, if $firstopen === false, the rest of the logical expression is not evaluated since it will always be true, and $added = true.

            If $firstopen === false is false, the rest of the expression has to be evaluated. $firstclose !== false will be true (as a result of the first part being false), which leaves $firstclose < $firstopen. Thus, if $firstclose is less than $firstopen, the entire expression becomes true and $added = true.
            If $firstopen !== false and $firstclose >= $firstopen, the entire expression is false and $added = false.

            That is, the expression is equivalent to the conditions in the if / elseif conditions.

            Also note that you could simply remove ($firstopen !== FALSE && and use the simpler and equivalent expression

            $added = ($firstopen === FALSE) || ($firstclose < $firstopen);
            

              Ok so basically its like doing a ternary, without the ternary?

              $added = (($firstopen === FALSE) || ($firstclose < $firstopen)) ? TRUE : FALSE; 

              Hmm never knew I could do that.... (goes to update lots of code)

                It's even simpler than that, really. You're simply assigning the value of the boolean expression to a variable. Using a ternary operator just adds an unnecessary layer that ends up looking like:

                $expression = TRUE;
                if($expression == TRUE)
                    $var = TRUE;
                else
                    $var = FALSE;

                  Yeah I was just trying to put it to something I've done before. I didn't think you could assign the boolean of an evaluated expression using the or operator || so in the past I used a ternary when I wanted a variable to be true if one of multiple options was true. So now I can update my code to basically remove the "? TRUE : FALSE" part of those lines since its, as you pointed out, superfluous.

                    This is what I have now, are there still any ways to improve it?

                    <?php
                    private function PHPHighlight($str) {
                    	// CHECK POSITION, IF EXISTS, OF THE FIRST OPENING AND CLOSING PHP TAGS
                    	$firstopen = strpos($str,'<?php');
                    	$firstclose = strpos($str,'?>');
                    
                    // CHECK WHETHER TO ADD AN OPENING TAG
                    $added = ($firstopen === FALSE) || ($firstclose < $firstopen);
                    if( $added ) $str = '<?php '.$str;
                    
                    // HIGHLIGHT THE INPUT
                    $str = highlight_string($str,TRUE);
                    
                    // REMOVE THE ADDED OPENING TAG, IF IT WAS ADDED
                    if( $added ) {
                    	$open = substr($str,0,37);
                    	$close = substr($str,87);
                    	$str = $open.$close;
                    }
                    
                    // RETURN THE HIGHLIGHTED STRING
                    return $str;
                    }
                      Derokorian wrote:

                      Yeah I was just trying to put it to something I've done before.

                      A more appropriate concept to relate boolean expressions back to would be arithmetic expressions. A boolean expression like [font=monospace]($firstopen === FALSE) || ($firstclose < $firstopen)[/font] or [font=monospace]$a && $b[/font] is exactly the same sort of thing as [font=monospace]$m + $n[/font] or [font=monospace]$a * $b[/font]; the only difference is the result's type (a boolean as opposed to a number). And of course, boolean values can be stored in variables just like any other value.

                          // CHECK WHETHER TO ADD AN OPENING TAG
                          $added = ($firstopen === FALSE) || ($firstclose < $firstopen); 

                      Your logic has changed from what you had earlier: Note

                      johanafm wrote:

                      Also note that you could simply remove ($firstopen !== FALSE && and use the simpler and equivalent expression

                      You didn't have any "$firstopen !== FALSE" to remove; you had "$firstclose !== FALSE" and that was removed instead. Your new code adds a "<?php" when there is no "?>" at all even if a "<?php" already exists.

                        Write a Reply...