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)
Related
This question already has answers here:
Is it possible to overwrite a function in PHP
(10 answers)
Closed 9 years ago.
I have a main php file in the root which is included by several others on different directories ,I don't want to allow few functions written in main files to execute if included by another file.
I ran an test code and tried overwriting an function by re-declaring it on including but it returned an error:
a.php
<?php
function show(){echo "a";}//This is what I want to over-ride with
include 'b.php';
b.php
<?php
function show(){echo "b";}//This is the function i want to restrict.
show();
Fatal error: Cannot redeclare abc() (previously declared in C:\xampp\htdocs\abc.php:3) in C:\xampp\htdocs\xyz.php on line xx.
You can do this with anonymous functions (PHP5.3)
$show = function(){
echo "a";
};
$show = function(){
echo "b";
};
$show();
Will now echo "b".
You can't redeclare a function. You need to use unique names for each function.
The functions won't execute just because you include the file they're in. That is unless you're actively calling them inside the file.
I'd suggest evaluating the way you include files to resolve your issue. You may also want to look into include_once / require_once and the function_exists conditional.
E.g.
if ( ! function_exists( 'my_function' ) ) :
function my_function() {
}
endif;
The best way to do this would be to use PHP's OOP features, which allow you to specify visibility of methods.
You can check within a function if the "main file" is included or not, try something like this:
function someFunction() {
if (basename(__FILE__) == basename($_SERVER['SCRIPT_FILENAME'])) {
echo "Not included.";
} else {
echo "Included. No access.";
}
}
Or place the condition "outside" the function(s) definition, so it won't be defined if you include that file.
But I would go with an OOP approach.
I get a parse error when trying to use a name space inside my own function
require('/var/load.php');
function go(){
use test\Class;
$go = 'ok';
return $go;
}
echo go();
From Scoping rules for importing
The use keyword must be declared in the outermost scope of a file (the
global scope) or inside namespace declarations. This is because the
importing is done at compile time and not runtime, so it cannot be
block scoped
So you should put like this, use should specified at the global level
require('/var/load.php');
use test\Class;
function go(){
$go = 'ok';
return $go;
}
echo go();
Check the example 5 in the below manual
Please refer to its manual at http://php.net/manual/en/language.namespaces.importing.php
From the manual:
The use keyword must be declared in the outermost scope of a file (the global scope) or inside namespace declarations.
From what I gather a lot of people are getting this when including a separate function file and trying to use the static method inside that function.. For example in index.php
namespace foo/bar
require('func.php')
f();
and in func.php
function f() {
StaticClass::static_method();
}
you simply need to declare namespace foo/bar in func.php (same like how you declared it in index.php) so instead of the above it should look like:
namespace foo\bar
function f() {
StaticClass::static_method();
}
to avoid errors like:
Fatal error: Uncaught Error: Class 'StaticClass' not found in func.php
It's obvious now but I was confused why func.php does not carry over the namespace declaration inside the file that 'requires' func.php
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 am having an unexpected issue with scope. The include documentation (also applies to require_once) says the required file should have access to all variable at the line it was required.
For some reason I am not able to access a class instantiated with global scope inside a function that was required in.
Would anyone know why? I am obviously missing something.
I got it working through a reference to $GLOBALS[], but I still want to know why it is not working.
UPDATE:
The error I am getting is:
Fatal error: Call to a member function isAdmin() on a non-object in <path>.php on <line>
Code:
$newClass = new myClass();
require_once("path to my file");
----- inside required file -----
function someFunction() {
$newClass->someMethod(); // gives fatal error. (see above).
}
Functions define a new scope, so inside a function you cannot access variables in the global scope.
Variable Scope
within user-defined functions a local
function scope is introduced. Any
variable used inside a function is by
default limited to the local function
scope
About included files, the manual states:
When a file is included, the code it
contains inherits the variable scope
of the line on which the include
occurs.
So if you include something in a function, the included file's scope will be that of the function's.
UPDATE: Looking at your code example edited into the question, global $newClass; as the first line of the function should make it working.
$newClass = new myClass();
require_once("path to my file");
----- inside required file -----
function someFunction() {
global $newClass;
$newClass->someMethod();
}
Be aware though that using global can quickly make your code more difficult to maintain. Don't rely on the global scope, you can pass the object to the function as a parameter, or use a Singleton/Registry class (some tend to argue against the latter, but depending on the case it can be a cleaner solution).
The included code doesn't have a scope different than the code surrounding it. For example:
function a() {
echo $b;
}
This will fail even if echo $b is in an included file. If you replace the above with:
function a() {
include 'file.php';
}
... and file.php contains:
echo $b;
... then it's the same thing as if you wrote:
function a() {
echo $b;
}
Think of it this way: whenever you use include / require, the contents of the included file is going to replace the include / require statement, just as if you removed the statement and pasted the contents of the file in its place.
It doesn't do anything else as far as scope is concerned.
Simple question: Is the scope of require_once global?
For example:
<?PHP
require_once('baz.php');
// do some stuff
foo ($bar);
function foo($bar) {
require_once('baz.php');
// do different stuff
}
?>
When foo is called, does it re-parse baz.php? Or does it rely on the already required file from the main php file (analagous to calling require_once twice consecutively for the same include file)?
I saw this thread before, but it didn't quite answer the question:
Should require_once "some file.php" ; appear anywhere but the top of the file?
Thanks for your help!
require_once() basically relies on the physical file to determine whether or not it's been included. So it's not so much the context that you're calling require_once() in, it's whether or not that physical file has previously been required.
In your code above, your foo() function would not re-parse baz.php, since it is going to be the same file as was previously included at the top.
However, you will get different results based on whether you included it inside foo(), or included it at the top, as the scoping will apply when require_once() does succeed.
It does not. require_once's tracking applies to inside functions.
However, the following scripts produce an error:
a.php
<?php
require_once('b.php');
function f() { require_once('b.php'); echo "inside function f;"; }
?>
b.php
<?php
f();
?>
because function f() is not pre-defined to b.php.
To more specifically answer your question, the second time you call require_once on that file, it won't do anything, because it's already be included.
If your include has functions etc. in it, then you would have issues including it inside a function anyway, so scope is irrelevant. If it's just variables being defined or processed, then you can just use require instead of require_once if you want it to be included again, thereby redefining the variables in your scope.
At least in PHP 7.3, require_once has global scope.
If it would not, both x.php and z.php should throw errors as y.php does:
a.php
<?php function a() { require_once 'b.php'; b(); }
b.php
<?php function b() { debug_print_backtrace(); }
x.php
<?php
require_once 'a.php';
a();
b();
-->
#0 c() called at [/var/www/b.php:2]
#1 b() called at [/var/www/a.php:3]
#0 c() called at [/var/www/a.php:4]
y.php
<?php
require_once 'a.php';
b();
--> Fatal error: Uncaught Error: Call to undefined function b() in /var/www/y.php on line 3
z.php
<?php
require_once 'a.php';
require_once 'b.php';
b();
-->
#0 b() called at [/var/www/z.php:4]