PHP 7 doing weird stuff with variables

tekmunkey

New member
PHP 7, Apache 2, Debian 9 amd64

A few days ago I discovered that a classless function was not always but occasionally not seeing variables defined in the same file.

Code:
<?php
    $someArr = array( 'def0' => 'val0', 'def1' => 'val1', 'def2' => 'val2' );
    
    function SomeFunc( $someParam )
    {
        global $someArr;
        
        /*
            Search into $someParam for keys found in $someArr and perform replacements.
            
            Sometimes $someArr is empty, sometimes $someArr is unset.
            
            The problem depends on which PHP file is calling it, and yes I am anal about require_once.
            
            The fix for this was to change the function signature and then pass $someArr into the function 
            every time, as a parameter, which worked fine from every consumer.
        */
    }
?>

So now I have a very similar but even weirder problem in a static function inside a class container. Before you ask, I am writing this code because DOMDocument is a broken joke which should not even be in PHP Distributions in its current state.

Code:
<?php
    class HTML_Element
    {
        public static function CreateElementsFromHtml( $htmlString )
        {
echo 'htmlString is ' . $htmlString . '<br/>';  // -> $htmlString is always empty         
            $r = array();
  
            $htmlChars = str_split( $htmlString );
print_r( $htmlChars ); // -> This works fine, spits out all the characters which were input into $htmlString
echo '<br />';
            $tagStack = array();
echo 'htmlString is ' . $htmlString . '<br/>'; // -> $htmlString is always empty
            /*
                Attempting to increment or manually set the index in a for-loop 
                just hangs in PHP.
                
                So I'll use my own index and do/while
                
                As you read you may note that the same thing is happening in do/while...
            */
            $i = 0;
            do
            {
                $c = $htmlChars[ $i ];
                if ( $c === '<' )
                {
echo 'opener found<br/>'; // -> Because the array is always right, this works fine
                    $end = array_search( '>', $htmlChars, true ); // -> Because the array is always right, this works fine
                    if ( isset( $end ) && ( $end !== false ) && ( $end > -1 ) )
                    {
                        /*
                            Include the found character
                        */
                        $length = ( ( $end + 1) - $i );
echo 'ender found<br/>';
echo 'index is ' . $i . '<br/>';
echo 'end is ' . $end . '<br/>';
echo 'length is ' . $length . '<br/>';
echo 'htmlString is ' . $htmlString . '<br/>'; // -> $htmlString is always empty
                        $tag = substr( $htmlString, $i, $length ); // -> $htmlString is always empty, so $tag is always empty
echo 'tag is ' . $tag . '<br/>'; // -> See line above

                        /*
                            Finally, increment $i past the end of the tag
                        */
                        //$i += $length; // -> This causes PHP to hang indefinitely, both in for ( $i = 0; $i < count( $htmlChars ); $i++ ) and in do/while as shown
                        /*
                            When this hang occurs, PHP occasionally (but not always) spits out an 
                            error log entry indicating that processing time took too long.
                        */
//echo 'i is now ' . $i . '<br/><br/>'; // ->This line is ever ever reached
                        //continue;

                    }
                }
            
                /*
                    If we didn't reset $i earlier and continue, then 
                    increment $i now
                */
                $i++; // -> Works fine when reached
echo 'i is now ' . $i . '<br/><br/>'; // -> Works fine when reached
            } while ( $i < count( $htmlChars ) );
            

            
            return $r;
        }
    }
    
    HTML_Element::CreateElementsFromHtml( '<html><head></head><body></body></html>' );
?>
 
In first example you have a variable and function, in the function you are trying to read a variable that is outside. The availability depends of the scope where the function was ran. So the variable may be inaccessible even it was declared next to the function. Try to read the variable outside the function, but next to place where you run the SomeFunc:
Code:
definition.php
<?php
    $someArr = array( 'def0' => 'val0', 'def1' => 'val1', 'def2' => 'val2' );
    
    function SomeFunc( $someParam )
    {
        global $someArr;
        echo 'inside function: ';
        print-r($someArr);
    }
?>

index.php
<?php

include 'definition.php';
echo 'inside function: ';
print-r($someArr);
SomeFunc(123);


To the second code I have a question to this part:
Code:
echo 'htmlString is ' . $htmlString . '<br/>';  // -> $htmlString is always empty
Is the $htmlString always empty here?

And I have some suggestions:
1. You don't need to split the string to array, you can manipulate the string almost like an array, even better:
Code:
            $c = $htmlString[ $i ];
            if ( $c === '<' )
            {
                echo 'opener found<br/>'; // -> Because the array is always right, this works fine
                $end = strpos( $htmlString, '>', $i); // -> Because the array is always right, this works fine
                if ( isset( $end ) && ( $end !== false ) && ( $end > -1 ) )
2. Use the strpos function with offset to find next not first character that match
3. Do not use function in loop condition it it i not necessary (probably always), assign the count to a variable and use it there
Code:
	// bad:
        } while ( $i < count( $htmlChars ) );
        
        // good:
        $count = count( $htmlChars );
        do
        {
          ...
        } while ( $i < $count );
 
Edit: As to your first part, that makes perfect sense and is probably the underlying issue. You are saying that I called the function from inside another function, so the first consumer also needed the global $someArr statement. Thank you for that!

The rest pertains to the second part, where 'your Q' was: "Is $htmlString empty here." It was never empty. I just derped. Big rush, big annoyance, dumb question. Sorry!


I realized all that and then some, several hours ago. I hit Delete on the post, did not realize that I was prompted for confirmation, so just now looked back at the browser tab and confirmed, only to be told I cannot delete due to an existing reply.

To your Q: Nope! It contains HTML, which is rendered by the browser rather than displayed as text. DERP!

Also, the search method I was using was always finding the first > and never the next one after the current value of $i. DERP!

Finally, the $i += $length hang issue had to do with $length being negative, because the first > was always at index 6 and after the first pass $i > 6.

I had even converted to using strpos with substr before I saw your reply. Perils of having to deal with phone calls and other distractions while writing code to fix a problem I just discovered yesterday, vis a vis DOMDocument making a giant sucking sound.
 
Back
Top