php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Bug #63156 using if ( expression ) requires functions to be declared before calls
Submitted: 2012-09-24 19:56 UTC Modified: 2012-09-25 16:27 UTC
From: herb at bobbingwide dot com Assigned:
Status: Duplicate Package: *Programming Data Structures
PHP Version: 5.3.17 OS: Windows XP
Private report: No CVE-ID: None
 [2012-09-24 19:56 UTC] herb at bobbingwide dot com
Description:
------------
When enclosing code between if ( expression ) { and } it appears that versions 
of PHP up to and including 5.3.17 require functions to be declared before they 
are used. I first noticed the problem when I tried to wrap a whole PHP source 
file like this
<?php
if ( !defined( 'CONSTANT' )) { 
define( 'CONSTANT', true ); 
// whole file here
} // end defined

I reduced the problem to the simplest; replacing !defined( 'CONSTANT') with true 
and got the same unexpected results. The code did not work unless the (top 
level) functions were declared before they were used. 
  
I found the problem on PHP 5.3.5 where the code produced a Fatal error: Call to 
undefined function. I have since reproduced the Fatal error on PHP 5.3.16 and 
5.3.17

If you remove the 'if test' (lines 2 and 7) the code works fine.
 
If you put the call to the function after the declaration that also works. 
This code, where the call to b() is before the declaration of function b, also 
works
<?php
if ( true ) {
  function a() {  
    echo "a" . PHP_EOL;
    b();
  }
  function b() {
      echo "b" . PHP_EOL;
  }
  a();
  
}  
producing
 
a
b



Test script:
---------------
<?php
if ( true ) {
  a();
  function a() {
    echo "a" . PHP_EOL;
  }
}  



Expected result:
----------------
a

Actual result:
--------------
Fatal error: Call to undefined function a() in C:\apache\htdocs\wordpress\wp-con
tent\plugins\play\defined.php on line 3

Patches

Pull Requests

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2012-09-25 02:40 UTC] laruence@php.net
it is good manner always declare function/class before using it.
 [2012-09-25 05:13 UTC] reeze@php.net
Top level declaration are available after compiling, but conditional declaration
can only be available after executed. (since PHP itself didn't do optimization, 
i`f(true) {doSomething();}` is not equal to `doSomething();` exactly )

the same rules for class declarations.

This could be classified as a documentation problem.
 [2012-09-25 09:46 UTC] laruence@php.net
-Status: Open +Status: Duplicate
 [2012-09-25 09:46 UTC] laruence@php.net
dup to #17055
 [2012-09-25 11:33 UTC] herb at bobbingwide dot com
I accept that this is a duplicate of #17055.

And since derick said, in response to #17055, "I don't see any fast 
implementation of this.", 10 years ago (May 2002), I'm inclined to agree that 
this will have to be dealt with as a documentation problem.

However, in my particular scenario the original response to the problem... "The 
only way to prevent this is to move out the function declaration out of the 
branch and in the main scope of the script." is not acceptable since the whole 
point of the encapsulating code was to prevent the functions from being multiply 
defined.

So I believe the documentation should provide a clear explanation of the parsing 
and interpretation processes involved.
 [2012-09-25 12:22 UTC] laruence@php.net
hmm, it's not the only way,  since you can get it works well by declare it 
before reference to it:
<?php
if ( true ) {
  function a() {
    echo "a" . PHP_EOL;
  }
  a();
}
 [2012-09-25 14:11 UTC] herb at bobbingwide dot com
laruence... agreed. That's how I solved it, as documented in my original report.
I was just commenting on the original response in #17055.
It was the unexpected behaviour that threw me more than anything.
 [2012-09-25 14:12 UTC] nikic@php.net
@herb: You are using a conditional function definitions, in which case PHP *can't* know which function will be used, at least not in the general case. Function hoisting only works with "top-level" function declarations.

This is also documented on http://php.net/manual/en/functions.user-defined.php. See in particular those two lines:

> Functions need not be defined before they are referenced, *except* when a function is conditionally defined as shown in the two examples below.

> When a function is defined in a conditional manner such as the two examples shown. Its definition must be processed *prior* to being called.

A very simple solution to your particular problem (which at the same time will yield cleaner code) is to abort early if the constant is defined:

<?php
if (defined('CONSTANT')) {
    return;
}
// the rest of the file here
 [2012-09-25 16:27 UTC] herb at bobbingwide dot com
@nicic thanks for the link to the documentation. 
I hadn't realised I could use return... having previously failed to get a 
working solution with exit :-)  
I'll most likely change my code to something like this.
<?php
 if ( defined( 'CONSTANT' )) return;
 define( 'CONSTANT', true );
 a(); 
 function a() {
   echo "a" . PHP_EOL;
 } 

Then I don't have to worry about the final } at the end of the file
So should #17055 be marked as Closed now, rather than Suspended?
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Wed Oct 09 14:01:27 2024 UTC