is it acceptable to recycle or reuse variables? - php

I've looked everywhere, and I can't seem to find an answer one way or another. Is it acceptable (good or bad practice) to reuse or recycle a variable? It works, and I've used this method a few times, but I don't know if I should be doing this or not. I'm trying to get away from using static methods, and moving into dependency injection.
in this example, the $table_name is set elsewhere.
class DbObject {
private $db = NULL;
protected $table_name;
public function __construct($dbh, $item) {
$this->db = $dbh;
$this->$table_name = $item;
}
// counts items in database //
public function count_all() {
try {
$sql = 'SELECT COUNT(*) FROM ' . $this->table_name;
$stmt = $this->db->query($sql);
$stmt->setFetchMode(pdo::FETCH_COLUMN, 0);
$result = $stmt->fetchColumn();
return $result;
} catch (PDOException $e) {
echo $e->getMessage());
}
}
To use this I would use it like this:
$total_count = new DbObject(new Database(), 'items');
$total_count = $total_count->count_all();
Is this an acceptable way to code?
Thanks for your help.

Reusing variables for different purposes is a maintenance error waiting to happen.
It's bad practice. A well named variable can do wonders for aiding code comprehension.
Reusing variables is especially fragile in weakly typed dynamic languages such as PHP.
[In the past, I have been guilty of errors in code when reusing local loop variables like i and j ...]

The main reason to avoid reusing variables is that if you reuse a variable without properly re-initializing it, the old value will "leak" through, causing unpredictable effects and even security vulnerabilities. For example:
$foo = $_GET['input'];
# use $foo
if ($a == $b) {
$foo = 1;
} else {
# $foo = 2; # Commented out for some reason
}
# Value $foo supplied in URL leaks through to here
Generally, reuse of variables will not damage performance if the compiler uses single-static assignment form (SSA) as an intermediate form during optimization (part of what SSA does is giving separate names to such reused variables). So don't worry about performance - worry about maintainability.

What about when you need call another method of the DbObject ?
I prefer give the variable name what it is:
$dbo = new DbObject(new Database(), 'items');
$total_count = $dbo->count_all();
//so you call still do things next
$result = $dbo->get_all();

If you like using common names often through out a script (e.g. $i for counting iterations in a loop) you should have no problems if you make it a habit to call unset() whenever you're done with using a variable in a particular case.

People tend to re-use "throw-away" variables like i and j, but it is usually bad form to reuse other local variables for a different purpose. That leads to reader confusion about where a variable is initialized and whether updates in one part of the code will affect another part of the code. There may also be ill-effects on a compiler's optimization suite (perhaps suggesting a variable should be saved when it isn't necessary).

Sometimes the variables could conflict or you could get them mixed out. So I would only reuse $i and $row unless you unset() the variable when you are done.

Related

Function assign a parameter that isn't null

This is the follow up question on my previous question.
Run a function without a parameter but needed a variable outside of the function - PHP
I recieved this answer
function foo($arg1 = null, $arg2 = null) {
// if they are not set retrieve from a session
$arg1 = ($arg1 !== null) ? $arg1 : $_SESSION['arg1'];
// rinse repeat.
}
So I've thought, maybe I can reduce the line of codes by preventing to call the same variable on every single function.
I've come up with this one.
function foo($arg1 = $_SESSION['user_id'], $arg2 = $_SESSION['campaign_id']) {
//do something
}
Unfortunately, I recieved this error.
Parse error: syntax error, unexpected T_VARIABLE
Is there a reason why it results to this one? I just replaced the null ones with a variable that has a value in it.
Is there any way to assign a function with parameter that has already a value instead of assigning a null ones?
P.S.
I'm trying to output the data using echo $campaign->getName(), so there's no parameter provided when calling the function.
So I'm trying to call a function something like this
function foo($arg1 = $_SESSION['user_id'], $arg2 = $_SESSION['campaign_id']) {
//do something
}
If there's any way to do it without calling the same variable over and over again.
Any help would be appreciated. :)
tl;dr
Don't try that (it doesn't really even work as you see), use proper DI (dependency injection) and pass your external (global) variables as parameters:
// functions should be deterministic — require the arguments in favour of using dynamic defaults
function foo($arg1 /* require it */, $arg2 = 1 /* static scalars as default is no problem */) {
// your code
}
foo($_SESSION["user_id"]); // always pass dynamic arguments, don't fetch them in the function
Intro
It isn't possible to use anything else as default as the so called static scalars. These are literals (strings, integers etc.), constants and (since PHP 5.6) operations on these literals/constants.
So...
I had the same great idea more than two years ago and filed a bug report ... https://bugs.php.net/bug.php?id=60544
Today I think it's a not-so-great idea. Default values are supposed to be static by the language. I doubt the ones who designed PHP hadn't had their reasons.
But there's still a way...
How you can do it (not recommended)
The only workaround here is to use constants.
function foo($arg = ARG) {
var_dump($arg);
}
At the moment where you're sure that the value won't change anymore, define the constant before the first call of the function:
define("ARG", $_SESSION["user_id"]);
Then you can later just call it via foo();.
That's possible due to lazy evaluation of constants here.
The drawback of this method is that you have to define the constant once and then it'll remain immutable for the rest of the scripts run-time.
But I still strongly not recommend it. It's hackish and has major disadvantages.
How you could do it better
You have shown us a way how to do it:
$arg1 = ($arg1 !== null) ? $arg1 : $_SESSION['arg1'];
That absolutely is the correct way. If you really want to do it.
But you still shouldn't do that. You're accessing global scope (superglobals) from a function.
How you should do it
There's one reason why the language designers didn't implement this possibility and it's the same why we shouldn't try to circumvent that in any way by constants or global scope access in the functions.
You really should properly use dependency injection and pass external (global) values to the functions via parameter. (see example in tl;dr section)
This genre of code you're proposing can easily lead to hardly debuggable code as people might not expect that global state is accessed by genuine functions.
That's why functions should have deterministic output for a given input (except if they're explicitly supposed to be non-deterministic, e.g. a function to read a file), that dramatically increases testability and portability.
You can't.
You can only assign literals to function defaults, constant values that cannot change in runtime. That means, "actual strings", numbers (42), hardcoded arrays ([1, 2, 3]), NULL. And not $variables, function() {} or anything else that may be changed at runtime.
Nor you should.
If you function depends on a user ID and a campaign ID, you should pass them into your function, and not rely on them being in SESSION because you can't know if they actually are in there!.
Do not rely on globals.
Why not pass the parameters to the function? It needs them, doesn't it? Would you expect the following function to work without parameters?
add($x, $y)
And just because those parameters are brought from SESSION, database, or the world beyond?
No. The function needs two parameters, and as such, it should ask for and given two parameters.
I have bad news for you, in the form of shorter isn't always better. This is mainly because you're referring to the $_SESSION values as constants, and that's not really good.
A good, solid code is longer because of all the checking and validation that helps avoid unnecessary errors. This is basically what you need to do:
function foo($arg1 = null, $arg2 = null) {
// if condition
if (!$arg1 && isset($_SESSION['arg1']))
$arg1 = $_SESSION['arg1'];
// tenary condition
$arg2 = (!$arg2 && isset($_SESSION['arg2'])) ? $arg2 : $_SESSION['arg2'];
}
If you really want to, you can look for something that will shorten this code by a few letters but it won't really be much different. All those validations are a must and shouldn't be ignored.
Looking at your previous question, I'm guessing you have a number of methods that require the campaign ID and user ID. I would suggest you pass the campaign ID and user ID to the constructor of the Campaign class. Then you can call whatever methods and always have access to them:
<?php
$campaign = new Campaign($db, $_SESSION['user_id'], $_SESSION['campaign_id']);
class Campaign {
private $db;
private $user_id;
private $campaign_id;
public function __construct($db, $user_id, $campaign_id) {
$this->db = $db;
$this->user_id = $user_id;
$this->campaign_id = $campaign_id;
}
public function getName() {
$query = $this->db->prepare("SELECT name FROM campaign WHERE campaign_id = :campaign_id AND user_id = :user_id");
$status = $query->execute(array(':campaign_id' => $this->campaign_id, ':user_id' => $this->user_id));
return ($query->rowCount() == 1) ? $query->fetchObject()->name : false;
}
}

Pre-declare all private/local variables?

This may be a basic question, but it has kept me wondering for quite some time now.
Should I declare all private/local variables being private? Or is this only necessary for "important" variables?
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
Hope someone can point this out.
Since you're talking about private, protected and public I take it you're talking about properties, instead of variables.
In that case: yes, you should declare them beforehand.
Because of how PHP objects are designed, an array (properties_table) is created on compile time. This array ensures that accessing a given property is as fast as possible. However, if you add properties as you go along, PHP needs to keep track of this, too. For that reason, an object has a simple properties table, too.
Whereas the first (properties_table) is an array of pointers, the latter is a simple key => value table.
So what? Well, because the properties_table contains only pointers (which are of a fixed size), they're stored in a simple array, and the pointers are fetched using their respective offsets. The offsets are stored in yet another HashTable, which is the ce->properties_info pointer.
As bwoebi pointed out to me in the comments: getting the offset (HashTable lookup) is a worst-case linear operation (O(n)) and predefined property lookups are constant-time complex operations (O(1)). Dynamic properties, on the other hand need another HashTable lookup, a worst-case linear operation (O(n)). Which means that, accessing a dynamic property takes in average about twice as long. Authors of the Wikipedia can explain Time-Complexity far better than I can, though.
At first, access modifiers might seem irrelevant. As you go along, you'll soon find that sometimes, you just don't want to take the chance that some property of some object gets modified by some bit of code. That's when you see the value of private.
If an object contains another object, that holds all sorts of settings that your code will rely upon, for example, you'll probably use a getter method to access those settings from the outside, but you'll leave that actual property tucked away nicely using private.
If, further down the line, you're going to add data models and a service layer to your project, there's a good change you'll write an (abstract) parent class, if only for type-hinting.
If those service instances contain something like a config property, you'll probably define that getter in the parent class (to only define it once). private means that only the current class has access to a property, but since you're not going to have an instance of the parent to work with, but an instance of the child, you'll see why protected is invaluable when dealing with larger projects, too.
As far as temporary variables are concerned, be it in methods, functions or anywhere else, you don't have to predeclare them, except for, in certain cases arrays:
public function foo()
{
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
Is perfectly valid, and wouldn't work any better if you were to write
public function foo()
{
$temp;// or $temp = null;
$temp = $this->getSomeValue();
return $temp ? $temp +1 : null;
}
However, it's not uncommon to see simething like this:
public function bar($length = 1)
{
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
This code relies on PHP being kind enough to create an array, and assign it to $return when the $return[] = rand(); statement is reached. PHP will do so, but setting your ini to E_STRICT | E_ALL will reveal that it doesn't do so without complaining about it. When passing 0 to the method, the array won't be created, and PHP will also complain when it reaches the return $return; statement: undeclared variable. Not only is it messy, it's also slowing you down! You're better off declaring $return as an array at the top of the scope:
public function bar($length = 1)
{
$return = array();//that's it
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
To be on the safe side, I'd also check the argument type:
/**
* construct an array with random values
* #param int $length = 1
* #return array
**/
public function bar($length = 1)
{
$length = (int) ((int) $length > 0 ? $length : 1);//make length > 0
$return = array();
for ($i=0;$i<$length;$i++)
{
$return[] = rand($i+1, $length*10);
}
return $return;
}
In most if not all cases: yes.
If the variables are class properties they absolutely should be declared before use.
If the variable is local to a function, declare it in that function before you use it. Function variables are confined to the function's scope (local variables). They don't have to be declared before use but it's good practice to do so, and it gets rid of a warning message if you do. If they are not used anywhere else, they should not be properties though,
If you are using it in the context of the whole class, then yes, you should define your variable as a member of the class.
However, if you are talking about a local variable within the context of a single function and the variable does not need to be used elsewhere (or is not returned), then no.
Essentially you need to determine the importance and scope of your variable before deciding whether to make it a class property or not.
For example:
<?php
class Test {
private $test; // Private property, for use in the class only
public $public_test; // Public Property, for use both internally and external to the class as a whole
public function testing() {
$local = 5; // Local variable, not needed outside of this function ever
$this->test = rand(1, 5);
$calc = $local * $this->test; // Local variable, not needed outside of this function ever
$this->public_test = $calc / 2; // The only thing that the whole class, or public use cares about, is the result of the calculation divided by 2
}
}
It's generally a good rule of thumb for variables to define and initialize them before use. That includes not only definition and initial value but also validation and filtering of input values so that all pre-conditions a chunk of code is based on are established before the concrete processing of the data those variables contain.
Same naturally applies to object members (properties) as those are the variables of the whole object. So they should be defined in the class already (by default their value is NULL in PHP). Dynamic values / filtering can be done in the constructor and/or setter methods.
The rule for visibility is similar to any rule in code: as little as necessary (the easy rule that is so hard to achieve). So keep things local, then private - depending if it's a function variable or an object property.
And perhaps keep in the back of your mind that in PHP you can access private properties from within the same class - not only the same object. This can be useful to know because it allows you to keep things private a little bit longer.
For instance, I have the (temporary) result of a calculation. Should I pre-declare this variable?
This is normally a local variable in a function or method. It's defined when it receives the return value of the calculation method. So there is no need to pre-declare it (per-se).
...
function hasCalculation() {
$temp = $this->calculate();
return (bool) $temp;
}
...
If the calculation is/was expensive it may make sense to store (cache) the value. That works easily when you encapsulate that, for example within an object. In that case you'll use a private property to store that value once calculated.
Take these rule with a grain of salt, they are for general orientation, you can easily modify from that, so this is open to extend, so a good way to keep things flexible.

Is it bad practice to use variable variables in PHP?

For example, a simple MVC type system:
/api/class/method rewritten into PHP variables using .htaccess/nginx.conf, then doing something like:
<?php
// Set up class + method variables
$className = some_class_filter($_GET['class']);
$method = some_method_filter($_GET['method']);
// Check if class exists and execute
if(file_exists(BASE . "/controllers/" . $className . ".class.php")) {
require BASE . "/controllers/" . $className . ".class.php";
$$className = new $className();
// Execute the method
$$className->$method();
} else {
// Spit out some error based on the problem
}
?>
Is this horribly bad practice? If it is bad practice, can someone explain exactly why? And if so, is there a better way to do what I'm doing?
EDIT Essentially the reason I'm using variable variables is to make it simple to expand the core system - i.e. - adding in a new controller is nice and simple. I definitely understand the security risks of allowing essentially any function or class to be instantiated without some kind of filter.
The 'some_filter_here' could be a list of controllers that are allowed - whitelist as some here have mentioned.
Yes, this is rather bad practise. Do you need a variable variable for that instance? In other words, do you need more than one class & method to be instantiated in a given request? Your URI structure suggests not. If not, you could just use:
$object = new $className();
$object->$method();
Otherwise, you might want to do:
$objects = array();
$objects[$className] = new $className();
$objects[$className]->$method();
This avoids polluting the scope with variable variables, which are harder to track.
As far as the existence checks for your class in a given directory, this should be a sufficient whitelist (presuming an attacker cannot write to that directory).
EDIT: As a further check, you may want to consider checking method_exists on the object before calling the method.
Since you're writing the "some_class_filter" and "some_method_filter" code, I'd say it's OK. You also have a error or default handler I see, so in the end, I'd say it's alright.
I believe many MVC frameworks operate in a similar fashion anyway.
They're not desirable, but it's fine to use them how you have.
A couple of pointers, though: your code does have a vulnerability where an attacker could traverse your directory with $_GET parameters like ?class=../base. If that file exists, your file_exists() call will return true and your application will attempt to include it and instantiate it as a class.
The safe scenario would be to whitelist those parameters to be letters, numbers and underscores only (if you separate words with underscores, i.e. .php).
Also, I prefer the syntax of using call_user_func and call_user_func_array. Using these functions in your code would look as follows:
<?php
$class_name = $_GET['class'];
$method_name = $_GET['method'];
$parameters = $_GET;
unset($parameters['class'], $parameters['method']); // grabs any other $_GET parameters
if (file_exists(BASE.'/controllers/'.$class_name.'.class.php')) {
require BASE.'/controllers/'.$class_name.'.class.php';
$controller = new $class_name();
$response = call_user_func_array(array($controller, $action_name), $parameters);
}
else {
header('HTTP/1.1 404 Not Found');
// ...and display an error message
}

Accessing a global variable inside nested functions

Here's my scenario:
$foo = bar;
one()
{
two()
{
three()
{
// need access to $foo here
}
}
}
I know I could pass $foo down all three functions, but that isn't really ideal, because you may or may not require it, besides, there are other parameters already and I don't want to add to that list if I can prevent it. It's only when you get to three() do you need $foo. I know I could also specify global $foo; in three(), but I read that this wasn't a good practice.
Is there another way $foo can be made available to three() without doing either of those?
To provide a little more info, $foo in this case is info on how to connect to the database e.g. server, user, pass, but only in three() does it actually need that info. Of course I could connect to the database right as the document loads, but that feels unecessary if the document might not need it.
Any ideas?
UPDATE: I'm sorry, I didn't mean to have "function" at the beginning of each function implying that I was created them in a nested way.
Yes, you can use global, or pass it through as a parameter. Both are fair ways. However, I'd do the latter. Or, you can also use closures and the use keyword:
$foo = 'bar';
$one = function() use($foo) {
$two = function() use($foo) {
$three = function () use($foo) {
// access to $foo here
};
};
};
Can also be use(&$foo) if you need to modify the top $foo's value inside of $three()
Using global is not a bad practice if used properly. This case you would use global and it will be totally fine.
Since global makes functions get access to global variables, there are data corruption risks.
Just make sure to use with caution and you know what you're doing (i.e. not accidentally change the data that you don't want it to change. This sometimes causes some nasty bugs that's hard to track.)
Summarize: global in this case is perfectly fine.
What's really bad is nested functions, at least in PHP.
Don't listen anybody, who are speaking about global.
Read books about OOP (e.g. this one), read about Dependency Injection, Factory-patterns.
Dependency Injection is the best way to create flexible and easy-to-maintain code, and by DI philosophy you will pass $foo from first to third function in arguments, or will pass Context object with all necessary variables (if you want to minimize count of arguments).
More about DI you can read here: http://components.symfony-project.org/dependency-injection/trunk/book/00-Introduction

Push a variable into global scope?

We use instantiate and put system critical objects in $GLOBALS for easy access from anywhere (e.g. DB, Cache, User, etc.).
We use $GLOBALS so much that it would (yes, really) drop the amount of code quite a bit if I could reference it like $G = &$GLOBALS for a shorthand call.
The problem is that, per my experience and several hours of Googling, I have not found any construct in PHP which allows you to 'flag' a var as global, making $GLOBALS first class, and everything else second class.
Am I missing something? Is this possible?
<?php
function &G($name) {
if(func_num_args() > 1) {
$GLOBALS[$name] = func_get_arg(1);
}
return $GLOBALS[$name];
}
G('test', 'hey');
echo G('test'); // outputs hey
echo $test; // outputs hey
$b =& G('test');
$b = 'hello';
echo G('test'); // outputs hello
echo $test; // outputs hello
Instead of putting everything into $GLOBALS you might want to take a look into the registry concept that is widespread in the php world.
However, having many global variables/objects is a sign of bad design and high coupling. Using something like $G guarantees spaghetti code and a soon-to-come maintenance nightmare. Who cares if you can drop the amount of code by a few characters?
No, but you can use a little trick. Create a global function by the same name as your global var, and have it return the global instance. Eg.:
function db() {
return $GLOBALS['db'];
}
You can now write your code as:
...
$stuffs = db()->query("select * from stuff");
...
You may recognise this as a variant over the singleton pattern, but a syntactically much more pleasant one.
Others have mentioned it, but you should also consider not using global objects in the first place. I generally prefer to pass objects in to there where it's needed (dependency injection). I'm not overly found of the registry pattern though.
global $variable; //?
In addition to the registry concept Middus points out, there are several approaches and concepts around this, some of which you can find in the answers to this question:
In a PHP project, how do you organize and access your helper objects?

Categories