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]
Related
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)
How do I get the path to a script that is calling a method of a class defined in another script, from within the class?
That is, I'd like to make a call to a class method - defined in b.php - from a.php as:
PHP code
# a.php
require 'b.php';
$obj = new AsyncDecorator('ClassName');
$obj->Call('methodName');
... with, as previously mentioned, the class being defined in b.php similarly to this snippet:
PHP code
# b.php
class AsyncDecorator
{
public function Call($method)
{
# Currently equals to b.php - I need it to be 'a.php'
$require = __FILE__;
}
}
That is, I need to know that the calling script was a.php, and I need to do it dynamically. If I'm creating and using the AsyncDecorator class in c.php, then $require should equal to 'c.php'.
A possible solution to this problem is making either the Call() method, or the initialization of the decorator to accept a $file_path parameter in which __FILE__ is passed:
PHP code
$obj = new AsyncDecorator('ClassName', __FILE__);
$obj->Call('methodName');
This has the minor downside of requiring the file path to be passed each time this object is created, which might add unnecessary parameters and not keep its use as simple and seamless as possible.
There is a gist here with a function to get the calling class.
I've been attempting to create a PHP loader class that'll take care of all of my directory issues. I've mostly gotten it to work, but it breaks when including global functions.
Here's what I'm using:
<?php
class Loader {
public function __construct() { ... }
private function check_if_file_exists($file) { ... }
public function load($file) {
$this->check_if_file_exists($file); //Throws fatal Exception if not a file
//The "important" stuff: works with global vars, not with global functions:
extract($GLOBALS, EXTR_REFS);
ob_start();
require_once "{$this->path}/$file";
return ob_get_clean();
}
}
This lets me do the following:
<?php
$loader = new Loader();
$loader->load('file.php'); //Class takes care of path stuff--don't worry about it
//This works:
print $variable_in_file_dot_php;
//This does NOT work:
function_in_file_dot_php();
How can I make it so that function_in_file_dot_php(); works?
Better you use AutoLoader class already available in php.Refer this url http://php.net/manual/en/language.oop5.autoload.php
i'm going to try to answer your question as a technical curiousity, but i strongly advise you not to do this.
Referring to the include/require documentation I see that variables defined inside included files will inherit the variable scope of the line that called require. In your case this will be the variable scope of Loader::load() method inside some instance of Loader class
Therefore $variable_in_file will not be available globally. unless you
define the $var before calling the include statement, thus giving it global scope.
declare it global inside your calling method (Loader::load())
You acomplish #2 with extract($GLOBALS...) however in order to do #1, you must have a priori knowledge of what is being included before you include it... invalidating your attempt at generalization with the Loader class.
function_in_file() however should be available in the global scope, I'd like to see your file.php and error message. Here's mine.
$cat foo.php
public function load($file) {
extract($GLOBALS,EXTR_REFS);
require_once $file;
}
}
$variable = 1;
$loader = new Loader();
$loader->load('file.php');
echo "\n" . $variable;
echo "\n" . method();
$cat file.php
<?php
function method() {
echo "hi i am a method";
}
outputs
$php foo.php
hello i am a variablehi i am a method
but seriously, don't do this. You seem to be trying to use includes() as a vector of code reuse, when it is mostly envisioned as a method for code separation. You are messing with phps' natural scoping in a hard to debug and unpredictable way. This is an anti-pattern.
Maybe I'm just tired or just am simply confused, but I'm having a strange issue dealing with some require_once() calls and ob_start().
Basic Structure:
Top of Main.php:
require_once 'config.php'; // includes variable $A = "bar", and Function "foo"
function getPage(){
ob_start();
include 'some_file.php';
$html = ob_get_clean();
echo $html;
die();
}
getPage();
some_file.php
require_once 'config.php'; // includes same config file
var_dump($A); // NULL
foo(); // runs, returns correct value
Config.php
$A = 'bar';
function foo(){
return "FOO";
}
So, what is wrong here? I'm including a file while buffering output. The required file config.php holds a variable and function. When including some_file.php during the buffer, the variable $A is apparently NOT set/accessible. The function foo CAN execute.
The documentation says:
When a file is included, the code it contains inherits the variable
scope of the line on which the include occurs. Any variables available
at that line in the calling file will be available within the called
file, from that point forward. However, all functions and classes
defined in the included file have the global scope.
Your provided code does not illustrate the problem that you're describing. When I run it as-is, it correctly shows that the variable is defined.
That being said, the thing to remember is that what looks like a global variable in an included file actually ends up in the scope of the function that's calling it. So if the first time require_once() is called is from a function, the $A variable is scoped to the function - and disappears when the function returns, just like any other variable defined inside the function.
If you absolutely must define a global variable inside an included file (are you sure? really?), make sure you only include that file from the global scope - not from within a function. If you need to access the variable from within a function, include the file outside the function, and then use the global keyword to access the variable from within the function.
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.