Say I have this b.php file
<?php
function ff() { echo "B"; }
and this a.php file
<?php
if ( true ) {
include ("b.php");
ff();
exit;
}
function ff() { echo "A"; }
Running php a.php gives
PHP Fatal error: Cannot redeclare ff()
Hmm, ok. So what if I re-run php a.php with this a.php code
<?php
if ( true ) {
include ("b.php");
ff();
exit;
}
else {
function ff() { echo "A"; }
}
This time that works, and I get B as expected.
In the first case, PHP reads and parse the whole file. Meaning, during compilation it sees the 2 same functions declarations, though the exit guarantees that at runtime we won't get up to the 2nd ff().
However in the 2nd case, it sees it at run time, but the compiler is not upset about seeing this function declared twice...
Seems the compiler behaves differently based on if the function is top-level or not... while
All functions and classes in PHP have the global scope
(the doc)
Now, how to turn that into a non opinion-based question :)
So it seems the options are either to rename ff to something like ffa and ffb, or add a long else { ... }. or change the whole structure...
Is this how PHP works, or that can be changed in the settings?
Related
File b.php
<?php
if(defined('Yes')) return;
define('Yes', 1);
function f(){
return 0;
}
?>
File a.php
<?php
include 'b.php';
include 'b.php';
?>
I expect the above code should not result an error. But, error message says function f is already defined.
But if b.php is modified like
<?php
if(!defined('Yes')){
define('Yes',1);
function f(){
return 0;
}
}?>
It works. What is this behaviour to be understood?
PHP "looks ahead" for top-level function and class definitions, so that you can write code like this:
do_stuff();
do_more_stuff();
exit;
function do_stuff() {
// lots of code here
}
function do_more_stuff() {
// even more code
}
In order to do that, as soon as the file is compiled, function declarations are processed, and "forward declared", so that you don't get an "undefined function" error. In this example, the lines where the functions are declared are never even reached, but the declarations are processed at compile time, so it runs fine.
This is what happens in your first code: the function declaration is processed as soon as you include the file, before PHP even looks at the if statement.
However, when you put the whole block inside a condition like this ...
do_stuff();
do_more_stuff();
exit;
if ( true ) {
function do_stuff() {
// lots of code here
}
function do_more_stuff() {
// even more code
}
}
... the compiler spots that the declaration is conditional, and waits until that code is actually executed, when the run-time can check the condition. Even though the condition above is always true, the code gives an "undefined function" error, because the functions weren't declared yet when we tried to run them.
This is what happens in your second code: PHP waits until the if statement has run, and only declares the function when and if it reaches that line.
As an aside, this whole pattern of "if some constant is defined then exit" is very rare in PHP, because you can just use include_once or require_once, and it will handle it all for you.
I have a question on the way that functions are declared in PHP.
First test :
File "functions.php" => functions toto(){ return "1"; }
Main File
include("functions.php")
functions toto(){ return "main"; }
echo toto();
Second test
File "functions.php" => functions toto(){ return "1"; }
File "functions2.php" => functions toto(){ return "2"; }
Main File
include("functions.php")
include("functions2.php")
echo toto();
Results
The first test work and echo "main"
The second test doesn't work => fatal error "function toto already define"
I make complementary tests :
in first test : put the functions toto() before include doesn't change the result.
Create twice functions toto() in the main file create Fatal Error
Someone can explain me how exactly this work ?
Thanks for reading
The PHP statements from the include family do not copy-paste the content of the included file in the context of the includer. The inclusion happens at runtime.
In your first example, the function toto() defined in the main file is created during the compilation. Then, on the execution, the functions.php file is read and parsed. It generates an error because it attempts to define the function toto() that is already defined.
The same happens in the second example during the inclusion of functions.php. Also, you get the same error if you declare the function toto() two times in the main script.
Either way, the PHP functions and constants cannot be re-declared.
A quick quote from the documentation:
PHP does not support function overloading, nor is it possible to undefine or redefine previously-declared functions.
You can check if a function is already defined (to avoid defining it again) by using the function_exists() PHP function:
function toto() { return 1; }
if (! function_exists('toto')) {
function toto() { return 2; }
}
Points to the topic
You can only name one function toto() else you get Fatal error: Cannot redeclare You can use if(!function_exists('toto')){ /*declaration*/ } to prevent that.
php complies a file complete before including next files, means all functions in a php will be declared, then includes are made. So if the first line includes a file that declares toto(), but the next line declares also toto(). The declaration in the include throw the Fatal error.
The only way to prevent this is for example wrap in the first file if(1){ } around the declaration, so know the Fatal Error comes not from the included file.
Test Case:
//file1.php
<?php
function toto(){ echo __FILE__; }
//file2.php
<?php
include 'file1.php';
function toto(){ echo __FILE__; }
toto();
Call:
php file2.php
Result:
Fatal error: Cannot redeclare toto() (previously declared in file2.php)
//file1.php
<?php
function toto(){ echo __FILE__; }
//file2.php
<?php
include 'file1.php';
if(1){
function toto(){ echo __FILE__; }
}
toto();
Call:
php file2.php
Result:
Fatal error: Cannot redeclare toto() (previously declared in file1.php)
I'm building a class, which has some of its methods' bodies defined in other php files.
I have to do this, because although most functions are small, sane, and written by hand, a couple functions need to be auto-generated by another script I made.
The problem is that although the methods get called, the return statements in those methods don't seem to execute.
The general structure is like this:
MyClass.php:
<?php
class MyClass
{
public function foo()
{
include "myclass-foo-body.php";
}
}
?>
myclass-foo-body.php:
<?php
echo "foo()"; // This executes and outputs as normal.
return 42; // This does not appear to actually execute or return anything.
?>
test.php:
<?php
include_once "MyClass.php";
$bar = new MyClass();
$foo = $bar->foo();
var_dump($foo); // Ends up being NULL instead of 42.
?>
So, what am I doing wrong here?
Are function/method bodies not actually supposed to be included from another php file?
I appear to have followed the documentation for php's include, but I seem to be missing crucial information.
(I couldn't find any existing questions on this subject, so hopefully this isn't a dupe!)
Thanks!
Return value of your body file will be the return value of your include call so you should do it like below.
public function foo()
{
return include "myclass-foo-body.php";
}
Your method foo is not returning anything.
A return in the included file terminates the processing of the included file.
PHP Manual
It is possible to execute a return statement inside an included file
in order to terminate processing in that file and return to the script
which called it.
Background:
I'm building an automated test framework for a PHP application, and I need a way to efficiently "stub out" classes which encapsulate communication with external systems. For example, when testing class X that uses DB wrapper class Y, I would like to be able to "swap in" a "fake" version of class Y while running automated tests on class X (this way I don't have to do full setup + teardown of the state of the real DB as part of the test).
Problem:
PHP allows "conditional includes", which means basically that include/require directives are handled as part of processing the "main" logic of a file, e.g.:
if (condition) {
require_once('path/to/file');
}
The problem is that I can't figure out what happens when the "main" logic of the included file calls "return". Are all of the objects (defines, classes, functions, etc.) in the included file imported into the file which calls include/require? Or does processing stop with the return?
Example:
Consider these three files:
A.inc
define('MOCK_Z', true);
require_once('Z.inc');
class Z {
public function foo() {
print "This is foo() from a local version of class Z.\n";
}
}
$a = new Z();
$a->foo();
B.inc
define('MOCK_Z', true);
require_once('Z.inc');
$a = new Z();
$a->foo();
Z.inc
if (defined ('MOCK_Z')) {
return true;
}
class Z {
function foo() {
print "This is foo() from the original version of class Z.\n";
}
}
I observe the following behavior:
$ php A.inc
> This is foo() from a local version of class Z.
$ php B.inc
> This is foo() from the original version of class Z.
Why This is Strange:
If require_once() included all of the defined code objects, then "php A.inc" ought to complain with a message like
Fatal error: Cannot redeclare class Z
And if require_once() included only the defined code objects up to "return", then "php B.inc" ought to complain with a message like:
Fatal error: Class 'Z' not found
Question:
Can anyone explain exactly what PHP is doing, here? It actually matters to me because I need a robust idiom for handling includes for "mocked" classes.
According to php.net, if you use a return statement, it'll return execution to script that called it. Which means, require_once will stop executing, but the overall script will keep running. Also, examples on php.net show that if you return a variable within an included file, then you can do something like $foo = require_once('myfile.php'); and $foo will contain the returned value from the included file. If you don't return anything, then $foo is 1 to show that require_once was successful. Read this for more examples.
And I don't see anything on php.net that says anything specifically about how the php interpreter will parse included statements, but your testing shows that it first resolves class definitions before executing code in-line.
UPDATE
I added some tests as well, by modifying Z.inc as follows:
$test = new Z();
echo $test->foo();
if (defined ('MOCK_Z')) {
return true;
}
class Z {
function foo() {
print "This is foo() from the original version of class Z.\n";
}
}
And then tested on the command line as follows:
%> php A.inc
=> This is foo() from a local version of class Z.
This is foo() from a local version of class Z.
%> php B.inc
=> This is foo() from the original version of class Z.
This is foo() from the original version of class Z.
Obviously, name hoisting is happening here, but the question remaining is why there are no complaints about re-declarations?
UPDATE
So, I tried to declare class Z twice in A.inc and I got the fatal error, but when I tried to declare it twice in Z.inc, I didn't get an error. This leads me to believe that the php interpreter will return execution to the file that did the including when a fatal runtime error occurs in an included file. That is why A.inc did not use Z.inc's class definition. It was never put into the environment, because it caused a fatal error, returning execution back to A.inc.
UPDATE
I tried the die(); statement in Z.inc, and it actually does stop all execution. So, if one of your included scripts has a die statement, then you will kill your testing.
Okay so behavior of the return statement in PHP included files is to return control to the parent in execution. That means the classes definitions are parsed and accessible during the compile phase. For instance, if you change the above to the following
a.php:
<?php
define('MOCK_Z', true);
require_once('z.php');
class Z {
public function foo() {
print "This is foo() from a local version of class Z in a.php\n";
}
}
$a = new Z();
$a->foo();
?>
b.php:
<?php
define('MOCK_Z', true);
require_once('z.php');
$a = new Z();
$a->foo();
?>
z.php:
<?php
if (defined ('MOCK_Z')) {
echo "MOCK_Z definition found, returning\n";
return false;
}
echo "MOCK_Z definition not found defining class Z\n";
class X { syntax error here ; }
class Z {
function foo() {
print "This is foo() from the original version of class Z.\n";
}
}
?>
then php a.php and php b.php will both die with syntax errors; which indicates that the return behavior is not evaluated during compile phase!
So this is how you go around it:
z.php:
<?php
$z_source = "z-real.inc";
if ( defined(MOCK_Z) ) {
$z_source = "z-mock.inc";
}
include_once($z_source);
?>
z-real.inc:
<?php
class Z {
function foo() {
print "This is foo() from the z-real.inc.\n";
}
}
?>
z-mock.inc:
<?php
class Z {
function foo() {
print "This is foo() from the z-mock.inc.\n";
}
}
?>
Now the inclusion is determined at runtime :^) because the decision is not made until $z_source value is evaluated by the engine.
Now you get desired behavior, namely:
php a.php gives:
Fatal error: Cannot redeclare class Z in /Users/masud/z-real.inc on
line 2
and php b.php gives:
This is foo() from the z-real.inc.
Of course you can do this directly in a.php or b.php but doing the double indirection may be useful ...
NOTE
Having SAID all of this, of course this is a terrible way to build stubs hehe for unit-testing or for any other purpose :-) ... but that's beyond the scope of this question so I shall leave it to your good devices.
Hope this helps.
It looks like the answer is that class declarations are compile-time, but duplicate class definition errors are run-time at the point in the code that the class is declared. The first time a class definition is in a parsed block, it is immediately made available for use; by returning from an included file early, you aren't preventing class declaration, but you are bailing out before the error is thrown.
For example, here are a bunch of class definitions for Z:
$ cat A.php
<?php
error_reporting(-1);
$init_classlist = get_declared_classes();
require_once("Z.php");
var_dump(array_diff(get_declared_classes(), $init_classlist));
class Z {
function test() {
print "Modified Z from A.php.\n";
}
}
$z = new Z();
$z->test();
return;
class Z {
function test() {
print "Another Z from A.php.\n";
}
}
$ cat Z.php
<?php
echo "In Z.php!\n";
return;
class Z {
function test() {
print "Original Z.\n";
}
}
When A.php is called, the following is produced:
In Z.php!
array(0) {
}
Modified Z from A.php.
This shows that the declared classes don't change upon entering Z.php - the Z class is already declared by A.php further down the file. However, Z.php never gets a change to complain about the duplicate definition due to the return before the class declaration. Similarly, A.php doesn't get a chance to complain about the second definition in the same file because it also returns before the second definition is reached.
Conversely, removing the first return; in Z.php instead produces:
In Z.php!
Fatal error: Cannot redeclare class Z in Z.php on line 4
By simply not returning early from Z.php, we reach the class declaration, which has a chance to produce its run-time error.
In summary: class declaration is compile-time, but duplicate definition errors are run-time at the point the class declaration appears in the code.
(Of course, having not confirmed this with the PHP internals, it might be doing something completely different, but the behavior is consistent with my description above. Tested in PHP 5.5.14.)
This is the closest thing I could find in the manual:
If there are functions defined in the included file, they can be used in the main file independent if they are before return() or after. If the file is included twice, PHP 5 issues fatal error because functions were already declared, while PHP 4 doesn't complain about functions defined after return().
And this is true regarding functions. If you define the same function in A and Z (after the return) with PHP 5, you'll get a fatal error as you expect.
However, classes seem to fall back to PHP 4 behavior, where it doesn't complain about functions defined after return. To me this seems like a bug, but I don't see where the documentation says what should happen with classes.
I've thought about this for a while now, and nobody has been able to point me to a clear and consistent explanation for the way PHP (up to 5.3 anyway) processes includes.
I conclude that it would be better to avoid this issue entirely and achieve control over "test double" class substitution via autoloading:
spl-autoload-register
In other words, replace the includes at the top of each PHP file with a require_once() which "bootstraps" a class which defines the logic for autoloading. And when writing automated tests, "inject" alternative autoloading logic for the classes to be "mocked" at the top of each test script.
It will naturally require a good deal of effort to modify existing code to follow this approach, but the effort appears to be worthwhile both to improve testability and to reduce the total number of lines in the codebase.
I've been implementing a certain plugin (dtabs) on my page in Wordpress but after upgrading to the latest version, I found that I now have an error the 2nd time I call the main function called dtab_list_tabs().
The way it works is, the plugin gets include_once'd but the main function is called however many times you want to place tabs in your layout. I have 2 such calls to dtab_list_tabs().
Now, the problem is, for whatever reason the developer decided to include another function directly inside dtab_list_tabs() called current_tab(). Because it's declared within a function, apparently PHP tries to redeclare it as soon as you call the parent function the 2nd time, which doesn't make any sense to me.
PHP Fatal error: Cannot redeclare current_tab() (previously declared in .../wp-content/plugins/dtabs/dtabs.php:1638) in .../wp-content/plugins/dtabs/dtabs.php on line 1638
The code for that revision is at http://plugins.svn.wordpress.org/!svn/bc/208481/dtabs/trunk/dtabs.php
What I'm trying to figure out is whether there is a way to tell PHP that yeah... it has an internal function, which is a perfectly valid PHP paradigm as far as I know, so don't redeclare it and fail.
As for the situation at hand, I have removed current_tab() as it doesn't appear to be used.
You can use function_exists() to test if a function with that name has already been defined. If you make the definition conditional ( if(something) { function foo() {...} } ) php will "evaluate" the definition only when the condition is met.
function foo() {
if ( !function_exists('bar') ) {
function bar() {
echo 'bar ';
}
}
bar();
}
foo();
foo();
see also: http://docs.php.net/functions.user-defined
(But I'd try to avoid such things all together)
You can wrap your function declaration in an if statement. Use function_exists() to see if the function has been previously declared or not.
if(!function_exists('current_tab')) {
function current_tab() {
myMagicCode();
}
}
You can try this:
if (!function_exists('my_function')) {
function my_function() {
}
}
function_exists() - Return TRUE if the given function has been defined