Local/Global vars in PHP - php

I'm a newb. I have several tables to store forms and I want the next user's input to be stored the table with the least responses (each form is different).
I've taken the rowcounts from the sql db and they are working. I use the code below detect how many responses there are and hence set $testnumber accordingly.
The code fails. Essentially, when I echo $testnumber, it doesn't matter what values the row counts are, it just randomises according to the first if statement.
When I delete the first if statement, I get an error saying that $testnumber is undefined regardless of the values of rowcounts.
I am absolutely confused the hell out. In my head the var $testnumber is local in all of the statements (they are not defined elsewhere) so they should all either work or not work.
I would appreciate some help. I know my if statements are crap and doesn't cover all cases so any help here would be useful but most IMPORTANTLY can you explain why my other statements are being ignored and why the first one isnt?
Thank you
if ($rowcount1 = $rowcount2 = $rowcount3 = $rowcount4){ // if all rowcounts are equal
$testnumber = mt_rand(1,4);
}
if ($rowcount1 < $rowcount2){ //rowcount for 1 is lowest
if($rowcount1 < $rowcount3){
if($rowcount1 < $rowcount4){
$testnumber = 1;
}
}
};
if ($rowcount2 < $rowcount1){ // rowcount for 2 is lowest
if($rowcount2 < $rowcount3){
if($rowcount2 < $rowcount4){
$testnumber = 2;
}
}
};
if ($rowcount3 < $rowcount1){ // rowcount for 3 is lowest
if($rowcount3 < $rowcount2){
if($rowcount3 < $rowcount4){
$testnumber = 3;
}
}
};
if ($rowcount4 < $rowcount1){ //rowcount for exp2 is lowest
if ($rowcount4 < $rowcount2){
if ($rowcount4 < $rowcount3){
$testnumber = 4;
}
}
};
echo "Final Testnumber: " . $testnumber;

Your first comparison statement isn't a comparison at all: single = sets a variable, double == tests for equality. Thus, your conditional will always evaluate to TRUE, because it's setting your variables correctly.
On top of that, you need to expand out your statements, as equality comparisons don't work this way in PHP.
if (($rowcount1 == $rowcount2) && ($rowcount2 == $rowcount3) && ($rowcount3 == $rowcount4)){ // if all rowcounts are actaully equal
Check out the relevant PHP docs: Assignment Operators, Comparison Operators
Edit: Your next conditional statements are hampered by the fact that it appears you're running into issues with Variable Scope. Basically, in order to access the $testnumber variable, you'll need to define it outside the "scope" of a conditional block.
$testnumber = 0;
if (($rowcount1 == ...
While outside the scope of the question: based on what you've provided here, it may be worthwhile to check out the PHP documentation on arrays, it will help you immensely as you start to scale your code up.

Related

Pass variable value through nested if() statements - PHP

I want to be able to do the below but unable to since the values are not passed to the next if statement.
if(something){
$x=SomeValue;
if(something){
$y=SomeValue;
if($x==$y){
echo"x & y matches";
}
}
}
I also tried $GLOBALS in the respective if statements but still doesn't work.
How do I pass the values of $x & $y to the third if() statement
Actual code
if($i%2) {
$dx1 = $row['sec1'];
echo $dx1;
if($i%2==0){
$dx2 = $row['sec2'];
echo $dx2;
if($dx1==$dx2){echo $dx1." do not match ".$dx2;}
}
}
}
In PHP, any value which is not "empty" is considered "truthy"; in the case of an integer, that means 0 is false, and everything else is true. So if($i%2) means the same as if( $i % 2 != 0 ).
Later, you nest inside that if a similar-looking condition, if($i%2==0), which is actually the exact opposite. To reach the inner if, you have to first have gone into the outer if, so both conditions need to be true to reach the innermost code.
As you say, the conditions are equivalent to "$i is odd" and "$i is even". To get into the first if statement, $i must be odd; but to get into the second, it must also be even. No value of $i will ever meet both conditions, so the innermost code will never be reached.

How to add integers to a PHP variable

I'm trying to create a sorting algorithm based off a query. Essentially the user would input values for Height, Weight, and a few other attributes. I am then querying our database of models to match that person with someone who has the closest measurements to those that the user inputted. I'm trying to do this based off a weighted variable that I am assigning within the "while" statement of the query and then returning that variable's value at the end. Here is some code to show you what I mean:
$userHeight=$_POST['height'];
$userWeight=$_POST['weight'];
$userShoulder=$_POST['shoulder'];
$userWaist=$_POST['waist'];
$userInseam=$_POST['inseam'];
$heightMatchMultiple=0;
$heightMatchMultiple=0;
$weightMatchMultiple=0;
$shoulderMatchMultiple=0;
$waistMatchMultiple=0;
$inseamMatchMultiple=0;
function matchValues($array){
if(mysqli_num_rows($array) > 0) {
while($row=mysqli_fetch_array($array)){
$heightMatchMultiple=0;
if(isset($row['modelHeight'])){
if($userHeight==$row['modelHeight']){
$heightMatchMultiple=10;
}
elseif($userHeight==$row['modelHeight']+1 || $userHeight==$row['modelHeight']-1){
$heightMatchMultiple=9;
}
elseif($userHeight==$row['modelHeight']+2 || $userHeight==$row['modelHeight']-2){
$heightMatchMultiple=8;
}
elseif($userHeight==$row['modelHeight']+3 || $userHeight==$row['modelHeight']-3){
$heightMatchMultiple=7;
}
else{
$heightMatchMultiple=1;
}
}
echo "Model Match Multiple: " . $heightMatchMultiple . "<br>";
}
}//end of if num rows
else {
echo "No results to display.";
}
}//end of function
When I run this function, it is returning the else statement's value for heightMatchMultiple of 1 instead of 10 because there is a model in the database with a height of 69 which is what I used as user input.
Can I add +1 or +2 directly to the $row['modelHeight'] variable as I did or is there a better way to do this.
EDIT:
Some people were asking where I initialized the variable $userHeight so i added it to the code. I tried creating global variables for $heightMatchMultiple=0 as a way to make those variables usable within my matchValues function. Is this correct?
Where is $userHeight being initialized/passed in? I think the reason you're currently getting 69 is because $userHeight has not been set, hence it gets a value of zero by default, which means that 1 will be the correct answer if $row['modelHeight'] is 69.
To answer your real question, which is really about order of operations: yes, what you have written should do what I believe you are trying to achieve. However, you should probably add parenthesis, even though they aren't strictly necessary, just as a hint to your future self that you really did intend for the calculations to happen in a certain order.
For example, instead of this:
elseif($userHeight == $row[ 'modelHeight' ] + 1 || $userHeight == $row[ 'modelHeight' ] - 1)
you could write this:
elseif(($userHeight == ( $row[ 'modelHeight' ] + 1 )) || ( $userHeight == ( $row[ 'modelHeight' ] - 1)))
Or you could re-write each of the checks using abs() like this:
elseif(abs($userHeight - $row['modelHeight']) == 1 )
This is arguably more descriptive, since what you're really trying to say is "if the difference is 1".
But yes, you can use the + 1 and +2 etc as in your original code, but you do need to set or pass in the $userHeight variable at some point.

Should calculations be repeated in if statements?

If a calculation is used in the evaluation expression of an if statement and then again in the execution statement(s), is it more or less efficient to perform the calculation twice or to introduce a new variable?
In other words, is
if ($x <> some_complex_calculation()) {
...
$x = some_complex_calculation();
}
better than this:
$result = some_complex_calculation();
if ($x <> $result) {
...
$x = $result;
}
Or, is this:
if ($x <> get_something_from_database()) {
...
$x = get_something_from_database();
}
better than this:
$result = get_something_from_database();
if ($x <> $result) {
...
$x = $result;
}
Of course it will always be more efficient to save the results of the calculation, no matter how trivial it may be, so if that is all you care about using a temporary is the only "correct" option.
That said, the performance aspect would only be important if the calculation takes up a sizeable amount of your total running time, which is unusual in practice. On the other hand, there are other considerations that can influence or even dictate your decision:
Correctness: If you do the calculation twice there is the possibility that the application state will have changed between the two calculations. So you would enter a branch because e.g. get_something() > 5 but then inside the branch it could be that get_something() <= 5 because someone else has modified the data in the meantime. Does it matter to you? How do you want the app to behave in this case? These are questions far more important than "is it going to run faster".
Understandability: Whenever I see a variable being defined, I usually have to allocate a "mental slot" to tracking that variable and its usages until I have figured out how the function it is defined in works as a whole. If the code is using too many temporaries then the cognitive load to the reader is increased. Does it matter to you? What is the relative impact vs the performance difference?
If the calculation has different arguments at each call and thus different results, then you will need to call it each time. For example:
function add( a, b )
{
return a + b;
}
And called like so:
if( add( 1, 2) > a )
else if ( add( 4,5 ) > c )
This would return different values each time so you would need to compute it in the if.
However if it is the same comparison value each time, it better to store the result if used more than once:
result = add(5, 6)
if( result > 10 )
else if( result > 12 )

Create a Counter within a For-Loop?

I am a novice programmer and apologize upfront for the complicated question.
I am trying to create a lexical decision task for experimental research, in which respondents must decide if a series of letters presented on the screen make a "word" or "not a word". Everything works reasonably well except for the bit where I want to randomly select a word (category A) or nonword (category B) for each of 80 trials from a separate input file (input.txt). The randomization works, but some elements from each list (category A or B) are skipped because I have used "round.catIndex = j;" where "j" is a loop for each successive trial. Because some trials randomly select from Category A and other from Category B, "j" does not move successively down the list for each category. Instead, elements from the Category A list may be selected from something like 1, 2, 5, 8, 9, 10, and so on (it varies each time because of the randomization).
To make a long story short(!), how do I create a counter that will work within the for-loop for each trial, so that every word and nonword from Category A and B, respectively, will be used for the lexical decision task? Everything I have tried thus far does not work properly or breaks the javascript entirely.
Below is my code snippet and the full code is available at http://50.17.194.59/LDT/trunk/LDT.js. Also, the full lexical decision task can be accessed at http://50.17.194.59/LDT/trunk/LDT.php. Thanks!
function initRounds()
{
numlst = [];
for (var k = 0; k<numrounds; k++)
{
if (k % 2 == 0) numlst[k] = 0;
else numlst[k] = 1;
}
numlst.sort(function() {return 0.5 - Math.random()})
for (var j = 0; j<numrounds; j++)
{
var round = new LDTround();
if (numlst[j] == 0)
{
round.category = input.catA.datalabel;
}
else if (numlst[j] == 1)
{
round.category = input.catB.datalabel;
}
// pick a category & stimulus
if (round.category == input.catA.datalabel)
{
round.itemtype = input.catA.itemtype;
round.correct = 1;
round.catIndex = j;
}
else if (round.category == input.catB.datalabel)
{
round.itemtype = input.catB.itemtype;
round.correct = 2;
round.catIndex = j;
}
roundArray[i].push(round);
}
return roundArray;
}
You can use the comma operator to declare multiple variables and execute multiple statements within a single for loop.
In your case, you could do something like:
for(var CatAIndex = 0, CatBIndex = 0; CatAIndex+CatBIndex < numrounds; incrementA ? CatAIndex++ : CatBIndex++) {
// Insert your code here
}
I chose those verbose variable names to make it more clear. You'd have two separate indices for category A and B, and you compare the sum of the two versus the number of rounds you want to run. Then inside of your for loop somewhere, you set the boolean incrementA to either true or false to indicate which one to increment.
That roughly matches what you're asking for, but I think what you'd prefer is to use a combination of Math.random, <array>.splice and <array>.length to get a random word/nonword from each list, rather than producing a predictable order for selection. Then you don't even care what the indices are for the two categories and you can go back to a simple for(var i = 0; i < numrounds; i++) type of loop.
If the latter is what you really want, leave a comment on this answer and I'll update it with another example.
EDIT:
Okay, I'm assuming that the actual number and order of words and non-words is not really defined by your test, because otherwise a user could pick up the word/non-word pattern and Christmas Tree the test. I'm also assuming that you have two array of words and non-words called catA and catB in the global scope. Below is a function that will do the following:
Randomly pick a word or non-word.
Never repeat a word or non-word pick (meaning that technically it becomes more deterministic the closer to the end of the list you are.
Until all words are exhausted, at which point it will automatically "refresh" its list from the catA and catB arrays. (So you can set numrounds to +inf if you like.)
.
var pickAWord = (function outerScope() {
var allWords = [];
return function innerClosure() {
if(allWords.length == 0) {
allWords = [].concat(catA, catB);
}
return allWords.splice(Math.floor(Math.random()*allWords.length), 1)[0];
};
})();
The function is using the functional programming concept of closures to create a persisted "global-like" variable, allWords that only it can see. The function automatically refreshes the array with all of the words when the length of the array reaches zero (like it is from the start) using the globals catA and catB. To use it in a for loop, simply:
for(var i = 0; i < numrounds; i++) {
var wordToUse = pickAWord();
// Do something
}
If you need to guarantee that an equal number of catA and catB words are used, the outerScope function will need to keep track of three variables: copies of catA and catB, and an array the same size as numrounds, half of which are true and half false. splice randomly from this true/false array, and then splice randomly from either catA or catB depending on whether it's true or false. Then you function will need code to "refresh" all of these closure variables, but it would be essentially the same as how the function is written above.
Sorry if the function is a bit complex, but you see how easy it is to use, right? :)
I'm not entirely sure I understand your problem. Here is my answer based on this possible interpretations of your question:
You would like to use a for loop to process all of the Category A elements (and similarly another loop to process all Category B elements). In this case you can loop through the roundArray and treat the elements according to their category:
for (var j=0; j < numrounds, j++) {
var round = roundArray[i][j];
// you might want to use a test better suiting the context if input is not available at the
// time when round is processed, I am using this based on the code sample you provided
if (round.itemType == input.catA.itemType) {
// process round as Category A
// use numlst[round.catIndex] to access the corresponding element in numlst
} else {
// process round as Category B
// use numlst[round.catIndex] to access the corresponding element in numlst
}
}
// alternatively, you can break the loop into two and process only Category A in one instance
// and only Category B in the other (the if branch corresponding to the other category would be
// empty)
If this is not your intention, please clarify.

Is it bad practice to stuff garbage into $array[-1] to prevent OBOB?

I am trying to write a bunch of nested if statements that are somewhat as follows:
if (strcmp($data['reports'][$i][$j],$data['reports'][$i][$j-1])){
but as you all know, you have to write a separate if statement if it is 0, because 0-1 doesn't exist.
if ($j == 0){
//First report, to prevent OBOB
echo "<br/>".$data['reports'][$i][$j]."";
#-> You would have to write two more statements here to test for province...
}
if ($j >= 1){
//All subsequent reports
if (strcmp($data['reports'][$i][$j],$data['reports'][$i][$j-1])){
echo "<br/>".$data['reports'][$i][$j]."";
#-> You would have to write two more statements here to test for province...
}
Now, imagine if you have to test other dependent values for sequential identicality?
It goes from 4 to 8 to 16 separate statements to just test for 4 of these...
So, I just had the idea to stuff crap into $j = -1.
$data['reports'][$i][-1]='aaaaaaaaaa';
// This would depend on the type of data you are
// comparing I suppose it could be zeros.
$data['provinces'][$i][-1]='aaaaaaaaa';
Is there a better solution?
Why don't you just start your loop with 1 instead of 0? That is, instead of this:
for ($j = 0; $j < count($data['reports'][$i]); $j++ )
do this:
for ($j = 1; $j < count($data['reports'][$i]); $j++ )
It's pointless to compare the first array element to the previous one anyway, so just skip it.
Unless you need to do something with the first element, Vilx's answer is the way to go. By the looks of your code, you always echo the first element, and the others need an if condition. I'm guessing your concerned about duplicate code (good), just move the code that echos a row into a new method and call it conditionally.

Categories