While writing this question I already solved my problem, but I still have another question about it. Basically I guess I have variable scope understanding problems here but at the moment I don't see why this didn't work. Could somebody point it out for me?
I have this in index.php:
spl_autoload_register(function($class) { include_once("./Class/{$class}.php")); });
Site::Page("page");
The Site.php contains a class, the called methods basically include another files:
class Site {
public static function Page($name = null) {
if ($name) $inc = @include_once("./Page/{$name}.php");
}
public static function Dialog($name = null) {
if ($name) $inc = @include_once("./Page/Dialogs/{$name}.php");
}
}
page.php contains this:
$DB = DB::GetInstance();
Site::Dialog("dialog");
and dialog.php contains this:
$Stuff = $DB->Query("Some SQL query here");
if ($Stuff) {
// Processing result
}
The problem I had was PHP gave me the error about $DB is null so it couldn't call the Query method in dialog.php. I expected it to be globally available, because I just included another file but clearly this is not the case.
The DB class is a singleton object that manages the DB connection, and I solved the problem with one more line in the dialog.php, I called GetInstance() again and assigned it to $DB.
But what if I wanted another (not singleton) class instance from index.php for example? How could I access it and why this method not working?
You are right, this is an issue regarding variable scope. The using static methods to access your classes you wont have this problem, as static methods are kind of globally available.
But if you want to use variables that are not fetched from a static method, you are in for a little more work. The easiest solution I can think of is passing an array of variables to include.
public static function import($file, array $variables = [])
{
if(!file_exists($file)) {
throw new RuntimeException('Cannot find file: ' . $file);
}
extract($variables); // <- This is important
include $file;
}
The extract()
function takes an associative array. The array keys are then converted into variable identifiers/names and the corresponding value is the variable value. You would now have access to any variables passed.
But also has its fair share of problems. The most important issue to know about is the opportunity for locally defined variables to be overwritten. By default extract()
imports all variables directly into the current scope. Imagine you had a variable named $user
defined before you call extract()
and the extract()
function receives an array with a key called user
. The original $user
variable would be overwritten and any value stored within is lost. This can lead to serious bugs.
But there are also solutions for this. The extract()
function also takes flags, which tells it how to define new variables inside the scope. I would use the flag called EXTR_PREFIX_ALL
. This ensures all extracted variables are prefixed with a name you know beforehand.
extract($variables, EXTR_PREFIX_ALL, 'prefix_');
Now when you use the method import()
you can do the following:
Site::import('filename.php', [
'db' => DB::getInstance()
]);
// Inside: filename.php
$stuff = $prefixed_db->query('...');
Hope this helps, happy coding.
Bonus info:
Be aware that extract()
also has a security issue. If the array passed contains input from a client/user, if may be possible for a malicious user to overwrite important variables inside your script. Imagine a malicious user overwriting the variable $loggedin
from false
to true
. He has now gained access to your restricted area through exploiting extract()
. The prefix flag helps mitigate this as long as the prefix is unique.