php.net |  support |  documentation |  report a bug |  advanced search |  search howto |  statistics |  random bug |  login
Request #67817 RFE: permit repeat declaration of the same function to skip rather than fail
Submitted: 2014-08-09 21:07 UTC Modified: 2014-08-09 23:59 UTC
From: php at richardneill dot org Assigned:
Status: Not a bug Package: Scripting Engine problem
PHP Version: 5.5.15 OS:
Private report: No CVE-ID: None
View Add Comment Developer Edit
Welcome! If you don't have a Git account, you can't do anything here.
You can add a comment by following this link or if you reported this bug, you can edit this bug over here.
(description)
Block user comment
Status: Assign to:
Package:
Bug Type:
Summary:
From: php at richardneill dot org
New email:
PHP Version: OS:

 

 [2014-08-09 21:07 UTC] php at richardneill dot org
Description:
------------
At the moment, PHP will not let us re-declare an existing function, but instead has the error: "Cannot redeclare FUNCNAME() (previously declared in FILE:LINE)"

That's fine, and I understand why, unlike, say bash, we can't/shouldn't be able to change a function definition.

However, it's rather a pain if you end up with multiple indirectly include()d files some of which get included more than once, and you forgot to wrap every single function definition with:

if (!function_exists("FUNCNAME")){
function FUNCNAME(...){
...
}}

My request is that, rather than throwing an error the second time, if PHP is encountering the *same* function in the *same* file, it should simply ignore the duplicate definition (and throw an E_NOTICE).

This ought to be easy to implement (since PHP already knows the filename/line-num of the original definition at the time it reaches the duplicate), and the parser need merely ignore the duplicate. It should also not violate the principle of least-surprise, because if the function is identical, then even if it were re-defined, the programmer would expect it to do the same thing. So I believe this is an easy and uncontroversial win for the common case.

Thank you for your consideration.


Test script:
---------------
----- begin file inner.php ---
<?php
function a(){
        echo "A\n";
}
a();
?>
------ end file --------------

----- begin file main.php ---
<?php
include ("inner.php");
include ("inner.php");
?>
------ end file --------------

Expected result:
----------------
This should print:

A
A




Actual result:
--------------
A
PHP Fatal error:  Cannot redeclare a() (previously declared in inner.php.3) in inner.php on line 4


Patches

Add a Patch

Pull Requests

Add a Pull Request

History

AllCommentsChangesGit/SVN commitsRelated reports
 [2014-08-09 22:00 UTC] pajoye@php.net
-Status: Open +Status: Not a bug
 [2014-08-09 22:00 UTC] pajoye@php.net
That's why include/require_once exists.
 [2014-08-09 23:17 UTC] php at richardneill dot org
Thanks for your comment; I did already know about require_once(). Sorry if my test case was too simple; there are many better reasons for doing this, notably if the files to be included (or their children) contain a mix of functions and other code, in which case we only want to define the functions once, but we need to invoke the rest of the code every time. 


Here is a real example. I have a (complicated) website written in PHP, with multiple users. Each page is dynamically generated, according to both the query-string and the credentials saved in $_SESSION.

A user wants to export his data, essentially crawling part of the site, while logged in. Thus, we have something like the following (simplified) in export.php:

function capture_html ($page, $get){
   $_GET = $get; $_POST=array();   
   //but, we explicitly want to keep $_SESSION
   ob_start();
   include ($page);
   $html = ob_get_contents();
   ob_end_clean;
   return ($html);
}
   
//iterate over multiple sets of pages and files:
file_put_contents("1.html",capture_html("details.php", array("id" => "5") ));
file_put_contents("2.html",capture_html("details.php", array("id" => "6") )); 
file_put_contents("3.html",capture_html("messages.php",array("id" => "532"))); 
...
//and now zip the archive and offer the .zip to the user.
  

This is admittedly ugly in terms of recursion. The problem arises when the same $page is captured twice with different parameters: all the functions which normally don't clash then conflict with themselves. In this example, repeated use of file_get_contents($url) would be no good, because the view of ($url) would be for a non-loggedin user. 

I hope that makes sense.
 [2014-08-09 23:59 UTC] requinix@php.net
That's a problem of architecture: you've clearly written your pages so that they cannot all be executed at once, but that's exactly what you're trying to do with them.
The standard practice of putting classes and functions in separate files, away from the "presentation" files, would save you from this problem.
 [2014-08-10 01:01 UTC] php at richardneill dot org
I do see your point about the architecture, although even if I had put all the functions in separate pages, so that, (in my example), 

  details.php (the presentation layer) contains no functions, but itself
  requires header.php and functions.php (which contain only functions).

this still wouldn't help, unless every single function within the other files is also wrapped in function_exists().

I admit my example is a quick and dirty hack, trying to write a 1 hour wrapper around a 3-man-year website, which is reasonably well architected and factored, but by no means perfect, and without the time for a major job of refactoring.

Also, while important functions and classes do go into separate files, I can't believe most programmers are immune from having the occasional specific 3-line function within their presentation layer. 

Furthermore, this is an interesting way to attract bugs: it is relatively rare to test the convoluted code-path, whereas the normal "straight through" path is common. Adding a new function would work perfectly well in the common case, but would break the rare case (and most of us are good, but not 100% perfect at testing every last permutation).

Anyway, it seems to me that my proposal would be simple to implement, have no negative side effects, and eliminate a common frustration of wasted time, and sometimes corner-case crashes. Since the intent of the programmer is unambiguous, why not do it?  It would also be compatible with bash, perl,and python, all of which permit this. 

I'm sorry that I don't know the PHP internals well enough to offer a patch - but I'd have thought it's quite straightforward, changing the parser's logic on encountering a function from:

if (function F is already defined){
   print error message (earlier_file_linenum, this_file_linenum)
   die()
}else{
   parse function F
}

to:

if (function F is already defined){
   if (this_file_linenum ==  earlier_file_linenum
         skip_to_end_of F
         raise E_NOTICE
   }else{
        print error message (earlier_file_linenum, this_file_linenum)
        die()
   }
}else{
   parse function F
}
 
PHP Copyright © 2001-2024 The PHP Group
All rights reserved.
Last updated: Fri Apr 19 02:01:29 2024 UTC