PHP accessing variable from a file included by a class - php

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.

Related

Are static functions any better than globals? [duplicate]

I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.

Global vs static variables in PHP

I'm creating a basic framework in PHP. I need to pass data for the current page into different functions, allow them to modify and save it, and then pass it back to the page to be displayed. I was originally planning on storing the data in a global variable like $GLOBALS['data'], but I'm starting to think that using a global is a bad idea. So I'm thinking that instead I will put a static variable in the system class, and access it using system::$data. So, my question is, which would be better and why?
This:
$GLOBALS['data'] = array();
$GLOBALS['data']['page_title'] = 'Home';
echo $GLOBALS['data']['page_title'];
Or this:
class system
{
public static $data = array()
}
function data($new_var)
{
system::$data = array_merge(system::$data, $new_var);
}
data(array('page_title' => 'Home'));
echo system::$data['page_title'];
There really is no difference between a global variable and a public static variable. The class variable is namespaced a tiny bit better, but that hardly makes any difference. Both are accessible anywhere at any time and both are global state.
As it happens, I just wrote an exhaustive article on the subject:
How Not To Kill Your Testability Using Statics
So, my question is, which would be better and why?
You already sense that there is some problem putting this all into globals. Although you have developed some thoughts to encapsulate things into a class.
I think that is a good starting point. Let's add some more spice to the cooking to get this more fluent at the beginning:
$data = new ArrayObject(array());
$data['page_title'] = 'Home';
You have created an object now that you can pass along containing your data. Just pass $data to the area's where it's needed. No global or global static variable needed.
You can even make that type more concrete later on by extending from ArrayObject with your own type.
For the record.
Pro of static:
Clarity of the code. For example:
function fn() {
System::data()
}
versus
function fn() {
global $system;
$system->data()
}
Cons of static:
If you are using psr-4 then you must add (and include) a new class (and a new file). It impacts the performance even if you use opcache (opcache aleviates it but it's not magic).
You must define a block of code.

When and How to make class variables with PHP?

Newbie question, i have variables inside my class method, do i have to make them class variables where i can access them using $this? If no, please explain when do i use or make a class variables?
private function is_valid_cookie()
{
$securedtoken = $this->input->cookie('securedtoken');
// Checks if the cookie is set
if (!empty($securedtoken)) {
// Checks if the cookie is in the database
$s = $this->db->escape($securedtoken);
$query = $this->db->query("SELECT cookie_variable FROM jb_login_cookies WHERE cookie_variable=$s");
if ($query->num_rows() != 0) {
// Now let us decrypt the cookie variables
$decoded = unserialize($this->encrypt->decode($securedtoken));
$this->login($decoded['username'], $decoded['password']);
return true;
} else {
return false;
}
} else {
return false;
}
}
as you guys can see, i have variables $securedtoken and $decoded = array(), i cant decide if i have to make them class variables and just access them with $this
I actually try to minimize use of class-level variables to cases where they are going to be common amongst multiple methods, or they are going to be referenced from code outside the class (either directly or via getters/setters). If the variable is just needed in local scope for a method, do not pollute the class with it.
You'll want to make class variables when you are trying to share those variables throughout different functions in the class. You'll then need different Access Modifiers (public, private, protected) for these properties depending on whether or not outside code can view them, child classes can view them, or nothing at all.
You do not have to make them instance variables. You can make them static variables too, or constant variables! You use a class variable to describe attributes of a class. ie what a class has.
Its important to get your terminology correct too. You are asking about making the variable and instance variable. A class variable (http://en.wikipedia.org/wiki/Class_variable) refers to a static variable
For your specific example if your two variables are only used in that function you should not make them instance variables. There is no reason to share them accross the class
On the other hand if you need to use them again in other methods or in other places than yes. you should.
Deciding what kind of variable you want and what kind of access is a design decision.
Good places to start are object oriented php overview. http://php.net/manual/en/language.oop5.php
And basic beginner tutorials
http://www.killerphp.com/tutorials/object-oriented-php/
You do, yes. You can declare class variables like this:
class Dog
{
protected $name = 'Spot';
public function getName()
{
return $this->name;
}
}
You can read more about properties (member variables) in the documentation.

Function scopes in regards with includes in PHP

I'm having a bit of trouble understanding includes and function scopes in PHP, and a bit of Googling hasn't provided successful results. But here is the problem:
This does not work:
function include_a(){
// Just imagine some complicated code
if(isset($_SESSION['language'])){
include 'left_side2.php';
} else {
include 'left_side.php';
}
// More complicated code to determine which file to include
}
function b() {
include_a();
print_r($lang); // Will say that $lang is undefined
}
So essentially, there is an array called $lang in left_side.php and left_side2.php. I want to access it inside b(), but the code setup above will say that $lang is undefined. However, when I copy and paste the exact code in include_a() at the very beginning of b(), it will work fine. But as you can imagine, I do not wish to copy and paste the code in every function that I need it.
How can I alleviate this scope issue and what am I doing wrong?
If the array $lang gets defined inside the include_a() function, it is scoped to that function only, even if that function is called inside b(). To access $lang inside b() you need to call it globally.
This happens because you include 'left_side2.php'; inside the include_a() function. If there are several variables defined inside the includes and you want them to be at global scope, then you will need to define them as such.
Inside the left_side.php, define them as:
$GLOBALS['lang'] = whatever...;
Then in the function that calls them, try this:
function b() {
include_a();
print_r($GLOBALS['lang']); // Now $lang should be known.
}
It is considered 'bad practice' to use globals where you don't have to (not a consideration I subscribe to, but generally accepted). The better practice is to pass by reference by adding an ampersand in front of the passed variable so you can edit the value.
So inside left_side or left_side2 you would have:
b($lang);
and b would be:
function b(&$lang){...}
For further definitions on variable scopes check this out

Calling objects of other class in php?

I am working on a website with php/mysql.
I have 3 files config.php, utils.php and member.php. code of the files are as below,
config.php - $objPath->docrootlibs is fine, I am sure there is no problem with this.
/* Other library files & their object */
require($objPath->docrootlibs.'/utils.php');
$objUtils = new utils();
require($objPath->docrootlibs.'/member.php');
$objMember = new member();
utils.php
class utils{
function getCurrentDateTimeForMySQL(){
return date("Y-m-d H:i:s");
}
}
members.php
class member{
var $userid;
var $username;
function __construct(){
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
}
Now when I am including the config.php inside a page home.php with simple php include statement and running that page then it gives me following error.
Notice: Undefined variable: objUtils in D:\wamp\www\site\libs\member.php on line 17
Fatal error: Call to a member function getCurrentDateTimeForMySQL() on a non-object in D:\wamp\www\site\libs\member.php on line 17
Line numbers in error above are different, I just copied the specific part from the code here.
I am not understanding why its giving error, because objects of utils class is defined on config page before including the member class. It should detect that object.
Please check and help me to understand and correct this error.
Thanks!
One Solution
Unlike JavaScript PHP will not bubble up through scopes, which means
public function __construct(){
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
does not know what $objUtils is, because there is no such object defined in the local scope. This means, you have to make the object available inside that scope. The cleanest and most maintainable way to do that is to inject the utils instance to the member instance, e.g.
public function __construct($utils){
$this->lastactivity = $utils->getCurrentDateTimeForMySQL();
}
However, since you seem to be using that value on construction only anyway, there is no reason why your member instance has to know how to use the utils object. So why not just insert the actual value right from the start, e.g.
public function __construct($lastActivity){
$this->lastactivity = $lastActivity;
}
// then do
$utils = new utils();
$member = new member($utils->getCurrentDateTimeForMySQL());
On globals
You definitely do not want to use the global keyword or static methods. Both couple back to the global scope. This means you can no longer use the member class without the global scope. This makes maintaining, reusing, refactoring and testing harder. OOP is about encapsulation and by reaching out from the class to the global scope you are breaking that encapsulation. Use Dependency Injection as shown above instead.
Apart from that, using globals will also make your code harder to read. A developer looking at the ctor signature of member is likely to assume there is nothing else to do than just call new member. That's a lie, because she also has to setup the utils instance. In other words, you are hiding dependencies. The developer has to look at the actual code to understand what's going on. Thus, make dependencies explicit.
Some more resources:
http://c2.com/cgi/wiki?GlobalVariablesAreBad
http://misko.hevery.com/code-reviewers-guide/flaw-brittle-global-state-singletons/
EDITs after comments
If you really need that utils object, inject the instance and assign it to a property inside the member instance in the ctor. Then you can access it with $this->utils->foo() from anywhere else inside the member instance.
However, Utility classes are almost always a sign of bad design. It is much more likely that they should be broken apart and divided into/onto other objects. Try to find out the reponsibilities. Maybe Member should not use Utils, but Utils should be something else that uses Members.
Out of curiosity, why do you need a utility method for MySql anyway? If you use a Timestamp column in MySql for lastActivity, it will automatically update whenever the row is updated. I am assuming you are setting the lastActivity and then store the member data?
Regarding performance: you should not bother about performance. Write readable and maintainable code first and foremost. If you think your performance is not good enough, profile the application with XDebug to see what is really making an impact.
As another comment states, use dependency injection. Insert the utilities object into the constructor. Do not introduce variables over the global scope, especially between different files. This gets very confusing and creates a mandatory order of some file includes.
class member {
...
public function __construct(utils $objUtils) {
$this->objUtils = $objUtils;
...
}
}
In calling code:
$member = new member(new utils);
As an aside, I find it humorous that you have a macro with a name that is longer than the operation it performs.
As another aside, do you need a utilities class? Can the utilities just be functions?
It sounds like config.php is in the global scope. You need to use the global keyword when using $objUtils
function __construct(){
global $objUtils;
$this->lastactivity = $objUtils->getCurrentDateTimeForMySQL();
}
Since your getCurrentDateForMySQL doesn't depend on anything else inside your utils object, why not make it a static function? That way you can turn your member() method into:
function __construct(){
$this->lastactivity = utils::getCurrentDateTimeForMySQL();
}

Categories