In some code I'm working on I have noticed that several variables are being accessed from outside of foreach loops.
This is seen in codeigniter view files of this particular app.
For example, a view file:
<?php
foreach ($items as $row):
endforeach;
// HTML / PHP code...
<td>
<?php echo form_checkbox('option_1','1', FALSE); ?>
<?php echo form_hidden('weight_unit', $row->weight_unit); ?>
</td>
// etc...
This works (ie, no errors) but I wonder if this would be considered a bad practice and if so, why? (scope, etc)
Does anyone have an opinion on this and should variables only be called inside their corresponding loops?
Another issue I've noticed is if a variable is required in several parts of a view file: should I refactor to have multiple loops or should there be a single foreach / endforeach and the begin / end of the file.
Any suggestions are much appreciated. Thanks.
The only reason I could think of doing that is to seek to the end of the array so you can store its last member in a variable.
You can do this clearer with end().
$row = end($items);
$row will be the last item in the array, or unset, when it reaches your other code.
Is that want you want? If so, then it's not bad practice, per se. But you should at least document that the behavior is intended, because it's not intuitive.
Better practice is something like:
foreach ($foo as $bar)
{
// do something
}
$last_bar = isset($bar) ? $bar : null;
There it's obvious that you mean to do something with $last_bar.
Another issue I've noticed is if a variable is required in several parts of a view file
If you must iterate over your loop in different places in your view, then that's okay.
But if you just need a certain piece of information deep inside some array, then you should stick it into an easily accessible variable and use that instead.
This is a bad practice. If the recordset is more than one item long then you are looping through all the records (even though you do nothing in the loop) and then just using the last record. I dont use CI but if this is a recordset object there ought to be someway to access the first/last record or any other index position in the RS. You should use those if youre after a particular record. If its just an array you could use array_pop if you dont need to keep the array intact, or end if you do.
Additionally, in the same context of a lengthy set if its not really the last record your after this could have unforseen consequences if the recordset length changes.
I'd say this is really bad practice - what happens if later (in a few weeks) you come across the same piece of code and you need another foreach on totally different record types between the actual foreach and your table? - your $row variable will then have a totally different meaning - this piece of code is very susceptible to side effects when more code is added.
Also, there is a high possibility that in the future this behavior will not be supported by PHP anymore (this is just an assumption of mine, because, as you mentioned, the $row variable is out of scope where you are using it).
As some general principles:
use as few as possible global variables - they are hard to track, maintain and assure correct values
minimize the scope of the variables as much as possible and use them only in very clear scopes
avoid marginal use cases and so called features that are totally unnatural in other languages
last, but not least, avoid the principle if code was hard to write it should be hard to read - this principle won't secure you the job, only the hate of your coworkers
Related
So I am utilizing codeigniter.
To allow reusability of code i have a number of functions in my model which get different things.
e.g get_details, get_features, get_products
I then have a function called get_all which calls all these methods, so If i want i can get them all but otherwise i can use them individually.
So I have my data, and I pass it to my view. My view loops through each establishment and displays various data in a table row.
At present I use if.. else statements to discern if a value is empty for example.
So if an establishment has not had its features added yet I use:
if(!empty($features['feature1'])){//DO STUFF e.g output 'YES'}
Anyway, my views code is no getting rather long and complicated because essentially for each and every key of each array returned using get_all I am using an if..else statement to output a "-" if it is not set.
It works, it just seems repetitive.
The work around I thought of is to simply set a default array whereby everything is by default set to "-", then if the data does exist it is overwritten, but then I just have to write/initiate a large default array..
So my question is not a life threatening one, nor is it particularly hard.. I am simply curious as to how one achieves such functionality without ugly code.
Cheers
Maybe you can "adjust" the array in the controller, by setting its empty values to -:
$features = array_map(function($value) {
return empty($value) ? '-' : $value;
}, $features);
without you posting your actual code i can only provide some general advice. To usually handle this, and consolidate your code to remove all the conditionals put the keys in an array.
$keys_to_check = array('feature1', 'feature2', 'etc.....');
foreach ($keys_to_check as $key) {
if (!empty($features[$key])) {
// do something
}
}
This refactors out all those conditional statements into somethign that is more maintainable.
also in your model code when you are providing a general function get_all that calls 3 subfunctions it is extremely important to make sure that unecessary queries aren't being executed. it seems it would be good software design to not repeat yourself, and group 3 functions into 1 but if those three functions are each executing similar queries then it is horrible for performance.
I have a variable, for example $total1. It has a value from the database, for example 6.
Now I do an SQL query and it gets some numbers from tables. For example this:
$subtraction1=5, but it is in a while loop so the second time it could be $subtraction1=10 or something like that. Every time in the while loop the $subtraction_total variable would be $subtraction_total1+$subtraction1, because at the bottom of the page I would like to show the $total minus the $subtraction_total1.
But the first time in the while loop I must check if $subtraction_total already exists. I know to options to do that, but is there a shorter way?
Option 1 is to define the variable $subtraction_total1 before the while loop.
Option 2 is to do this:
(!isset($total_subtraction1)){$total_subtraction1=0;}$total_subtraction1=$total1-$subtraction1;
Now you think: well, just do option 1: define 1 variable, but it is for around 15 variables and I was just wondering if there is a shorter way;-)
Hope you understand me, my English is not very good;-)
Define and initialize all your variables before use. I think Option 1 follows logically from that.
Don't try to write code that is short, or fast or tricky. Write code that is easy to read and maintain.
I would definitely advocate defining the variable(s) before the loop. Repeatedly calling isset (or any other function) over and over inside a loop is wasteful if the same functionality can be achieved pre-loop.
If you're simply looking to define a large number of variables without having to explicitly declare each one before your loop you might try listdocs or programmatically create your variables in a loop.
$sub_total = 0;
while ($sub = mysql_get_row(...)) {
$sub_total += $sub;
}
This way you don't execute the same code again in every iteration (which is good practice and good performance wise), and it has the added advantage that if you have no result from mysql, $sub_total is defined with a default value.
Solution?
Apparently there isn't a faster way, I'm okay with that.
I am just learning php and I am trying to figure out some good tips and tricks so I don't get into a bad habit and waste time.
I am passing in values into a php script. I am using $_GET so the URL looks like this:
/poll_results.php?Sports=tennis&cat=Sports&question=Pick+your+favorite+sports
Now I know how to accept those values and place them into variables like so:
$sports = $_GET['Sports'];
$cat = $_GET['cat'];
$question = $_GET['question'];
Super simple yet if I am passing 5 - 6 things it can get bothersome and I don't like typing things out for every single variable, that's the only reason. I know there is a better way of doing this. I have tried list($var, $var, $var) = $_GET but that doesn't work with an associative array just indexed ones (i think).
I also tried variable variables like so:
foreach($_GET as $value) {
$$values = $value;
echo $$values;
}
But that gave me a Notice: Undefined variable: values in poll_results.php on line 14. Line 14 is the $$values = $value. I don't know if that's a big deal or not... but I'm not turning off error reporting as I am still in the process of building the script. It does do what I want it to do though...
Any answers will be copied and pasted into my question so the next person knows :D
Thanks guys!
Your second bit of code is wrong. It ought to be like
foreach ($_GET as $key => $value) {
$$key = $value;
}
if i understand your intent. However, you're basically reinventing register_globals, which....eh. That'll get ya hacked.
If you have certain variables you want to get, you could do like
foreach (array('Sports', 'cat', 'question') as $key)
{
$$key = $_GET[$key];
}
which is less likely to overwrite some important variable (whether by accident or because someone was messing around with URLs).
Use parse_url() to extract the query string from a URL you've got in a string, then parse_str() to extract the individual arguments of the query string.
If you want to pollute your script with the contents of the superglobals, then you can use extract(). however, be aware that this is basically replicating the hideous monstrosity known as "register_globals", and opens all kinds of security vulnerabilities.
For instant, what if one of the original query arguments was _GET=haha. You've now trashed the $_GET superglobal by overwriting it via extract().
I am just learning php and I am trying to figure out some good tips and tricks so I don't get into a bad habit and waste time.
If I am passing 5 - 6 things it can get bothersome and I don't like typing things out for every single variable, that's the only reason.
What you are trying to do will, unless curbed, become a bad habit and even before then is a waste of time.
Type out the variables: your digits like exercise and your brain can take it easy when it doesn't have to figure out which variables are available (or not, or maybe; which would be the case when you use variable variables).
You can use
foreach($_GET as $key => $value)
To preserve the key and value associativity.
Variable variables (the $$value) are a bad idea. With your loop above say you had a variable named $password that is already defined from some other source. Now I can send $_GET['password'] and overwrite your variable! All sorts of nastiness can result from this. It's the same reason why PHP abandoned register_globals which essentially does the same thing.
My advice: use $_POST when possible. It keeps your URLs much cleaner for one thing. Secondly there's no real reason to assign the array to variables anyway, just use them where you need them in the program.
One good reason for this, especially in a large program, is that you'll instantly know where they came from, and that their data should not be trusted.
I am not really clear about declaring functions in php, so I will give this a try.
getselection();
function getselection($selection,$price)
{
global $getprice;
switch($selection)
{
case1: case 1:
echo "You chose lemondew <br />";
$price=$getprice['lemondew'].'<br>';
echo "The price:".$price;
break;
Please let me know if I am doing this wrong, I want to do this the correct way; in addition, php.net has examples but they are kind of complex for a newb, I guess when I become proficient I will start using their documentation, thank you for not flaming.
Please provide links that might also help me clear this up?
Your example seems valid enough to me.
foo('bar');
function foo($myVar)
{
echo $myVar
}
// Output: bar
See this link for more info on user-defined functions.
You got off to a reasonable start. Now all you need to do is remove the redundant case 1:, close your switch statement with a } and then close your function with another }. I assume the global array $getprice is defined in your code but not shown in the question.
it's good practice to declare functions before calling them. It'll prevent infrequent misbehavior from your code.
The sample is basically a valid function definition (meaning it runs, except for what Asaph mentions about closing braces), but doesn't follow best practices.
Naming conventions: When a name consists of two or more words, use camelCase or underscores_to_delineate_words. Which one you use isn't important, so long as you're consistent. See also Alex's question about PHP naming conventions.
Picking a good name: a "get" prefix denotes a "getter" or "accessor"; any method or function of the form "getThing" should return a thing and have no affects visible outside the function or object. The sample function might be better called "printSelection" or "printItem", since it prints the name and price of the item that was selected.
Globals: Generally speaking, globals cause problems. One alternative is to use classes or objects: make the variable a static member of a class or an instance member of an object. Another alternative is to pass the data as an additional parameter to the function, though a function with too many parameters isn't very readable.
Switches are very useful, but not always the best choice. In the sample, $selection could easily hold the name of an item rather than a number. This points to one alternative to using switches: use an index into an array (which, incidentally, is how it's done in Python). If the cases have the same code but vary in values used, arrays are the way to go. If you're using objects, then polymorphism is the way to go--but that's a topic unto itself.
The $price parameter appears to serve no purpose. If you want your function to return the price, use a return statement.
When you called the function, you neglected to pass any arguments. This will result in warnings and notices, but will run.
Recently, I have identified very strong code smell in one of the project, I'm working for.
Especially, in its cost counting feature. To count the total cost for some operation, we have to pass many kind of information to the "parser class".
For example:
phone numbers
selected campaigns
selected templates
selected contacts
selected groups
and about 2-4 information types more
Before refactoring there were all this parameters were passed through constructor to the Counter class(8 parameters, you can image that..).
To increase readability I have introduced a Data class, named CostCountingData with readable getters and setters to all this properties.
But I don't think, that that code became much readable after this refactoring:
$cost_data = new CostCountingData();
$cost_data->setNumbers($numbers);
$cost_data->setContacts($contacts);
$cost_data->setGroups($groups);
$cost_data->setCampaigns($campaigns);
$cost_data->setUser($user);
$cost_data->setText($text);
$cost_data->setTotalQuantity($total_quantity);
$CostCounter = new TemplateReccurentSendingCostCounter($cost_data);
$total_cost = $CostCounter->count();
Can you tell me whether there is some problem with readability of this code snippet?
Maybe you can see any code smells and can point me at them..
The only idea I have how to refactore this code, is to split this big data object to several, consisting related data types. But I'm not sure whether should I do this or not..
Whay do you think about it?
If what you want is named parameters (which it looks to me is what you are trying to achieve), would you ever consider just passing in an associative array, with the names as keys? There is also the Named Parameter Idiom, I can only find a good reference for this for C++ though, perhaps it's called something else by PHP programmers.
For that, you'd change your methods on CostCountingData so they all returned the original object. That way you could rewrite your top piece of code to this:
$cost_data = new CostCountingData();
$cost_data
->setNumbers($numbers)
->setContacts($contacts)
->setGroups($groups)
->setCampaigns($campaigns)
->setUser($user)
->setText($text)
->setTotalQuantity($total_quantity);
So there's two options. I would probably go for the named parameter idiom myself as it is self-documenting, but I don't think the difference is too great.
There are a few possibilities here:
If you only need a few of the parameters each time, the named parameter approach suggested elsewhere in this answer is a nice solution.
If you always need all of the parameters, I would suggest that the smell is coming from a broken semantic model of the data required to calculate a cost.
No matter how you dress up a call to a method that takes eight arguments, the fact that eight supposedly unrelated pieces of information are required to calculate something will always smell.
Take a step back:
Are the eight separate arguments really all unrelated, or can you compose intermediate objects that reflect the reality of the situation a little better? Products? invoice items? Something else?
Can the cost method be split into smaller methods that calculate part of the cost based on fewer arguments, and the total cost produced from adding up the component costs?
If I needed the data object (and classes called "data" are themselves a smell) I'd set its values in its constructor. But the interesting question is where are these values coming from? The source of the values should itself be an object of some sort, and its probably the one you are really interested in.
Edit: If the data is coming from POST, then I would make the class be wrapper around the POST data. I would not provide any setter functions, and would make the read accessors look like this (my PHP is more than a little rusty):
class CostStuff {
constructor CostStuff( $postdata ) {
$mypost = $postdata;
}
function User() {
return $mypost[ "user_name" ];
}
...
}
What do you think is wrong with your code snippet? If you have several variables that you've already used that you now need to get into $cost_data, then you'll need to set each one in turn. There's no way around this.
A question though, is why do you have these variables $numbers, $contracts, etc. that you're now deciding you need to move into $cost_data ?
Is the refactoring you're looking for possibly that the point in the code where you set $numbers should actually be the point you're setting $cost_data->numbers, and having the $numbers variable at all is superfluous?
E.g.
$numbers=getNumbersFromSomewhere() ;
// do stuff
$contracts=getContracstFromSomewhere() ;
// do stuff
$cost_data=new dataObject() ;
$cost_data->setNumbers($numbers);
$cost_data->setContracts($contracts) ;
$cost_data->someOperation() ;
becomes
$cost_data=new dataObject() ;
$cost_data->setNumbers(getNumbersFromSomewhere()) ;
// do stuff
$cost_data->setContracts(getContractsFromSomewhere()) ;
// do stuff
$cost_data->someOperation() ;