Using for loop within if statement in PHP - php

I am writing a rostering app. This statement checks that by booking an extra shift the user doesn't violate a rule whereby they have booked more than 7 night shifts in a row. This code runs fine but I am trying to find a more elegant way to write it, for instance using a for loop within the if statement. This snippet exists within a bigger while loop.
if (
$original_shift->night_shift==true &&
$p_lookback_night_7===[1,1,1,1,1,1,1] || $p_lookforward_night_7===[1,1,1,1,1,1,1] ||
($p_lookback_night_1===[1] && $p_lookforward_night_6===[1,1,1,1,1,1]) ||
($p_lookback_night_2===[1,1] && $p_lookforward_night_5===[1,1,1,1,1]) ||
($p_lookback_night_3===[1,1,1] && $p_lookforward_night_4===[1,1,1,1]) ||
($p_lookback_night_4===[1,1,1,1] && $p_lookforward_night_3===[1,1,1]) ||
($p_lookback_night_5===[1,1,1,1,1] && $p_lookforward_night_2===[1,1]) ||
($p_lookback_night_6===[1,1,1,1,1,1] && $p_lookforward_night_1===[1])
) {
return 'You can\'t do more than 7 night shifts in a row';
break;
}
The $p_look variables get populated by a loop looking either back of forward the specified number of days at the end of the variable name and returning an array of true or false for that number of days dependent on whether those are night shifts or not.

As an alternative to building several arrays and complex comparisons, this alternative just uses 2 arrays, one with the days prior and one looking forward. I'm not 100% sure if this includes the day they are trying to book off, but hopefully the idea is easy enough to adjust to your needs.
The basic concept is to look backwards through the $p_lookback_night list and count the 1's, stopping when it reaches a 0. It then does a similar thing through the $p_lookforward_night list. The end result is the number of 1's in a row...
$p_lookback_night = [0,0,0,0,1,1];
$p_lookforward_night = [1,1,1,1,0,0];
$run = 0;
foreach (array_reverse($p_lookback_night) as $test ) {
if ( $test == 1 ) {
$run++;
}
else {
break;
}
}
foreach ($p_lookforward_night as $test ) {
if ( $test == 1 ) {
$run++;
}
else {
break;
}
}
echo $run;
With the test data it gives 6, so you can use this to decide if they are trying to book 7 in a row.

Assuming all those arrays can only contain 1 in this case you can simply just count the values
&& count($p_lookback_night_7)===7 || ...
Maybe even use the int at the end dynamically but this will be probably more trouble that it is worth. Something like
for($i=1;$i<8;$i++){
if(count(${"p_lookback_night_".$i}) == $i && count(${"p_lookforward_night_".$i}) == $i ){
..wahtever
}
}

Related

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.

define variable within conditional whilst testing against in php

Is it possible to call a method within a conditional to test against but also store return in a var within the same conditional reducing the api calls?
ie something like this:
elseif(
($auth_type = \proj101\user::getUserAuthType( $user_profile['email'] ) )
&& $auth_type != AUTH_TYPE_GOOGLE
){
//$auth_type now equals the function return and not just 'true'
Yeah, you can do that, you just have to get the order of operations right. Since assignment has lower precedence than logical &&, you end up setting auth_type to the result of the && operation.
Consider this simple example:
<?php
const AUTH_TYPE_GOOGLE = 1;
function userAuthTypeStub( $x ){
return 2;
}
if( 0 ){
// who knows what to do?
}
elseif(
$auth_type = userAuthTypeStub( 0 )
&& $auth_type != AUTH_TYPE_GOOGLE
){
echo "$auth_type\n" . userAuthTypeStub(0) . "\n";
}
you get
1
2
but if you fix the order of operations, you get what you expect:
<?php
const AUTH_TYPE_GOOGLE = 1;
function userAuthTypeStub( $x ){
return 2;
}
if( 0 ){
// who knows what to do?
}
elseif(
( $auth_type = userAuthTypeStub( 0 ) )
&& ( $auth_type != AUTH_TYPE_GOOGLE )
){
echo "$auth_type\n" . userAuthTypeStub(0) . "\n";
}
2
2
Note I had to adjust the result of the function in both cases, because if $auth_type ends up being 0 in either case, the elseif evaluates to false and the output is never executed. That's one of the reasons I agree with most of the criticisms being offered that it would make the most sense to set $auth_type and then test its value.
Some languages (namely: C, if I'm not mistaken) don't promise anything about the order of operations of both sides of a logical &&, so it may be safe to say that depending on the second from the first is a bad practice. I think that may be for the optimizations possibly gained by reversing the order. This certainly isn't C so maybe it doesn't matter, but it sure makes debugging harder!
http://www.php.net/manual/en/language.operators.precedence.php

Tricky verification code on a form

I have an implemented control check on a form coded this way:
public function checkCittaResidenza() {
if (is_string($this->citta_residenza) && (strlen($this->citta_residenza) <= 45 || strlen($this->citta_residenza == 0))) {
$this->corretti['citta_residenza'] = htmlentities($this->citta_residenza, ENT_QUOTES);
} else {
$this->ErroriTrack('citta_residenza');
}
}
In this version, it simply checks if it is a string and check its lenght that should be less than 45 chars. It puts the string in an array corretti() if positive, else it initialize an error message specified above in an abstract class parent of the checking class.
What i'd love it to do is:
1) make a check on the string to see if it's not null.
2) if it's not null, do the check (that could be even more particular than the simple one shown here, but i don't have problems on this), put it in corretti() if correct and initializing the error if it's not, as the code now says.
3) if the string is null, the program should skip the check and directly write the null value into the array corretti(), because the form is imagined to be completed in different steps over the time, so it always happen that it's not fully filled.
I'm having problem on coding the if cycle for this last condition, every cycle i tried and imagined puts the empty condition as a cause for initializing an error.
Thank you!
Try this,
public function checkCittaResidenza() {
if(isset($this->citta_residenza)){
if ((is_string($this->citta_residenza) && (strlen($this->citta_residenza) <= 45) || $this->citta_residenza == "")) {
$this->corretti['citta_residenza'] = htmlentities($this->citta_residenza, ENT_QUOTES);
} else {
$this->ErroriTrack('citta_residenza');
}
} else {
$this->corretti['citta_residenza'] = "null";
}
}

Coding style - how do I format a long if condition to make it readable

I have a long if condition as follows. There are two conditions that both have to not be met, for the statement to evaluate. I did have it as a one liner with a lot of && and ! but it became unreadable. I have tried splitting it into an if elsif else, which is more readable but doesn't read well, as the first if elsif blocks have no code in them.
What would be the best practice to tidy this code block?
if ($instructionObject->instruction=='nesting_grammar' && $instructionObject->match=='>'){ //if instruction is a '>' child indicator
//don't change the child depth
}else if ($instructionObject->instruction=='selector' && is_object($this->instructions[$key+1]) && $this->instructions[$key+1]->instruction == 'nesting_grammar' && $this->instructions[$key+1]->match == '>'){ //if instruction is a selector followed by a '>'
//don't change the child depth
}else{
$insertOffset += $childDepth;
unset($childDepth);
}
You can use "extract method" refactoring. Replace your conditions to new methods.
if ($this->isInstructionNestingGrammar($instructionObject)){
//don't change the child depth
}else if ($this->isIntructionSelect($instructionObject)){
//don't change the child depth
}else{
$insertOffset += $childDepth;
unset($childDepth);
}
In new methods put every compare to separate line.
P.S. Don't be afraid of long name of methods.
Just negate the conditions and skip the if and else if parts as the two initial conditions don't do anything...
if (
!($instructionObject->instruction=='nesting_grammar' &&
$instructionObject->match=='>')
|| !($instructionObject->instruction=='selector'
&& is_object($this->instructions[$key+1])
&& $this->instructions[$key+1]->instruction == 'nesting_grammar'
&& $this->instructions[$key+1]->match == '>')
) {
$insertOffset += $childDepth;
unset($childDepth);
}
Not directly answering your question, but what about something like:
if (my_check($instructionObject) || $instructionObject->instruction=='selector' && my_check($this->instructions[$key+1])) {
} else {
$insertOffset += $childDepth;
unset($childDepth);
}
function my_check($obj) {
return is_object($obj) && $obj->instruction == 'nesting_grammar' && $obj->match == '>';
}
-- you are basically doing the same thing twice, time to think about a function for that.
Personally if i'm going to span the check across multiple lines i lay it out similar to how i'd lay out a JavaScript object;
if (
great big long check line goes in here &&
another really long ugly check line goes in here too
) {
// Do this code
}
else if (
check 3 &&
check 4
) {
//Do this code
}
Pull out sub-expressions into variables. Pseudo-example:
flibjit = FlibjitManager.FlibjitInstance(this);
isFrob =
(flibjit.Froblocity >= FlibjitManager.FrobThreshold) &&
(flibjit.Type == FlibjitTypes.Frobby);
if (isFrob) {
// ...

Is this the way to do this simple IF OR OR statement?

I have an if statement with a few or's in it.
i.e.
if($count == 1 || $count == 3 || $count == 7) {
// do stuff
}
I'm just curious - is this the best way to do this? With this simple example above, is there a faster way to do this, and if so, what is it?
Your code works fine. Alternately, you can use in_array(), which is a bit cleaner and scales better:
if (in_array($count, array(1,3,7))) { ... }
The code you've written is fine. As Paul Schreiber says, there are various other options that are a little neater.
One thing you may want to think about (and I know this is just an example) is why the values you're checking are important. Do they all have some property in common that you're checking? If so, then stating the property symbolically may make the code easier for someone to understand. For example:
if (is_odd($x) && $x < 10) {
//...
}
rather than
if ($x == 1 || $x == 3 || $x == 5 || $x == 7 || $x == 9 ) {
//...
}
This is quite a contrived example, but hopefully you see what I'm getting at.
As a more concrete example, instead of doing something like:
if ($user->age > 65
|| $user->years_of_custom > 3
|| $num_items > 5 ) {
// Give this user a discount ....
}
you might want to do:
if (eligible_for_discount($user, $num_items) ) {
// Give this user a discount
}
Even if you only use the function in this one place, this could increase the readability of the code. Obviously you have to use your judgment though, because you're increasing readability at the expense of having more lines of code to maintain, and that isn't always the right choice. If the conditions have little to do with each other, binding them up into a separate function might make no sense and make your code harder to follow, not easier. Focus on what your code actually means, and how a human being should understand it.
You can assign all possible value in an array and check using array_search function
$array=array(1,3,7);
if (array_search($count,$array) !== FALSE)
{
//do stuff
}
Wouldn't the switch statement be better?
switch ($count) {
case 1:
case 3:
case 7:
echo "do stuff";
break;
}

Categories