is_object and !is_object questions

minifairy

I'm trying to upgrade everything to eliminate all the warnings I'm getting.
So for results of queries I've been asking if the $result is an object if(is_object($result))
and if I want to first post a notice if it isn't valid I was using

if(!is_object($result)){
echo "This is invalid or some such notice";
send the program off with a header statement
break;
}

Then following with the processing of the $result which should logically be an object since we just checked for it not being an object. (Am I missing something?) but I am still getting notices "Trying to access a property of a non-object"

(previously I just asked if($result->num_rows<1).... to trigger the error messages. But now that triggers the property of a non-object warning. I figured replacing it with if(!is_object($result)) would replace it but it doesn't seem to.)

Since frequently the processing of the $result is quite long it is much easier to process the error info first.

what am I missing here?

Weedpacket

minifairy if(!is_object($result)){
echo "This is invalid or some such notice";
send the program off with a header statement
break;
}

If the header statement is all you're doing you're not "sending the program off" anywhere, you're directing the browser to another page. That doesn't stop this program from continuing to do stuff. The effect of the break statement would just end whatever loop this code is inside.

That's pretty crude, though, and there are many ways to skin that cat. You might want to create a general exception-handler function that will log the exception info and exit in a nicer, more user-friendly way. Then within your code, you might simply do:

NogDog

Weedpacket If you're using PDO then you can have it throw the exception when it encounters an error, instead of letting it return false and manually throwing an exception if it does.

Yep, was just kind of generalizing.

minifairy

Ok thanks. I get it. I thought the break would stop the program execution, the header having sent it off to some other page. So it only stops the loop it is in ie: while or if or foreach ,etc? so you have to have the entire case contained in an if statement if you want to only execute based on certain things? How would I be able to exit a case if I tested with a !is_object($result) and say send it back to the default case? I thought that was what I was doing with the header then break; Is there any shorter way than encasing the entire case in the if? ie....
if(!is_object($result)){
go away and exit case
}else{
execute rest of case
}

Note that the header() call won't take effect if headers have already been sent, such as by anything having already been output. You can use the headers_sent() test if you want to check for that.

minifairy

NogDog
Thanks. Yes I realize that the header won't execute if anything has been already sent to the browser. When I want to put an error message or whatever out there I just want it to quit when I'm done. I post the headers, the error or notice, and then the page footer then want it to quit. Either that or I'll pass a code in the headers for the program to post the message when it gets to where the header is taking it.
So I'll use exit; then and that will totally leave the case, program, loop whatever and be done.

won't closing the session screw things up if there are session variables that you want to carry along? For example if they are collecting stuff in a shopping cart. Wouldn't closing the session make that in accessible when they went to say another category or the shop front?

One other Question. If you get a true result from is_object($result) does that mean that there is then one or more $rows available if you do a $row=$result->fetch_object ? or do you still need to ask how many rows there are?

minifairy

dalecosp Thanks I'll have a look at that. Typically the if/else case is where there should be a result from querying the database and if it doesn't find what I'm looking for because someone is trying something that isn't what I'm looking for or they say misstyped an item number or the item they are looking for is not in stock then I want to leave the routine that is going to process the thing if it is right. Did that make sense? ie:
if(item is found){
we display the info about said item.
}else{
we go back to the beginning page and you can try again.
}
Also some places there are a couple of choices that could be made for different things. Those are typically you chose the thing or not, which may add up to a variety of choices but the are usually like y/n kind of thing. which then shapes the way the output is formatted or what is included. not really multiple processes. Mostly pretty simple logic. Like did you want to view thumbnails or a list. The process is the same just one instance you see pics the other not. Same query, same results just small differences in the output.

From what I found on the subject it is addressing making what we used to call spagetti code. I really try not to do that.

NogDog

The session_write_close() does not destroy the session, it just ensures that the current session data is saved before the header()/exit. I'm not sure why, but I've had issues where the $_SESSION does not show any changes made by the redirecting script if I don't do that.

A true response will only mean that the query successfully ran without any errors, but says nothing about whether anything in the DB actually matched -- so it could be anything between 0 - n rows. You'd still need a row-count check or such, if you need to know that. (That's one reason you might want to treat a false for the is_object() as an exception, since in theory it should never happen unless there's a bug, as opposed to a potentially valid result of no rows being returned.)

minifairy

NogDog
Ok thanks for that it does clear up a few things. For the most part in the past I just asked if the $result->num_rows>='1' so now that generates the warning ...property of non-object or some such so I added the is_object($result) before that. I was just curious if I still needed the num_rows bit. I haven't removed it but was curious.

ok so the session_write_close() does not destroy the session that's cool. (something more to add lol) how about secure sessions do they persist if you say go from a secure to non-secure page then say back to a different page that is secure? Would the previous $SESSION variables from the secure session still be intact, assuming the same browser, and it hasn't been shut down or anything yet. Just curious I like the $SESSION variables they make a lot of stuff easier.

NogDog

If you're using PHP's session stuff (i.e. using session_start(), I think the only gotcha you need to look out for is the domain and sub-domain being used to access the pages in question. For instance, if one uses "example.com" and another uses "www.example.com" (or "secure.www.example.com", etc.), the session cookie might not be sent by the browser. Therefore, if you in fact want the session to persist across all possible sub-domains, you may want to either use session_set_cookie_params() to set the domain a ".example.com" (note leading dot), or do so in your PHP environment via the session.cookie_domain setting.

minifairy

One thing that I am finding confusing of late chasing down these php 7 notices. Getting the undefined index on $POST variables I thought that if it was POSTed from a form that the variable was defined but if nothing was entered it would be empty ie '' unless specifically set to NULL is this not true? I have always tested for empty $POST values if it was something I needed to know but expected them to be empty if not entered. Now I'm getting these warnings if I say try to insert them in a query to a file or various other things if they are not set to an actual value.

So if I need to ask if(isset($_POST['xxx']) does this follow with the is_object from above that if it isset it doesn't necessarily have a value??? Seems if I have to ask if it isset that should mean it does have a value?

NogDog

One "gotcha" is checkboxes: if unchecked, they're not sent at all, so you'll need to do an isset() or !empty() check on them before trying to use them. In general, it's probably good to check all of the $_POST elements you want to check; since bots, malicious users, incorrect HTML, etc. may cause things to not be as you would normally expect them to be. Where feasible, maybe try to put such checks into a function or such to keep the code D.R.Y.

Weedpacket

As NogDog said; you can't assume anything about the contents of $_POST nor whatever elements it may or may not have; it's user input: it could be anything.

Well, you can assume that it's given to you as an array of strings. isset($_POST['foo']) is enough to determine if an element named foo is present; its value won't be null, because that isn't a string, and that is the only way the array could contain an element that isset would return false for (think of null as meaning "value explicltly absent").

My rule of thumb is: if I have to check the type of a variable in order to avoid an error, then I should look upstream to see if I can avoid assigning the variable a value of the wrong type in the first place. Treat the cause rather than the symptom.

minifairy

Thanks guys.

I do try to check what is coming in and to use the escape function on everything but I was sort of confused when I'm trying to check some $POST things and I'm getting an undefined index warning. Like I might have something like if($POST['xxx']=='print') do such and such. Well if its not 'print' I'm typically throwing it away or doing a display page in this case but if I'm getting an undefined index when I know that 'xxx' was in the form it was just weird I thought it would be empty or set to something. The check boxes thing could be the case in some of them.

The function idea is a good one I do try to put things in functions when its something that I re-use. I'm learning a lot of stuff on this quest to eliminate warnings. Hopefully the code will be better in the end, but it does help with the logic if I understand it better.

Thanks again.

minifairy

Ok still plugging away on what is set/defined and what is not.
I'm updating a table in one db based on info from another db. In this case I'm updating a mailing list based on customers that have recently purchased. So I collect the customer info (which was already checked and validated) from the db that keeps the sales, customers, etc. And I put it into an array incrementing $x as I go.

then I close that connection and open the connection to the other db and step through all the $x's in a for loop.
exploding each $mlist[$x] on '|' $cust=explode('|',$mlist[$x])

Now the question comes. I've just loaded 6 elements into this array. Some of them could be NULL (ie bonus doesn't apply to everyone) , however, the index should be defined should it not? ie: $cust[4] should be defined although it could be 5.00 or could be 0 or could be NULL. Is this not correct? Yet I'm getting notices like index 4 not defined

NogDog

We may need to see the exact error message text, along with the line of code it references with any preceding lines we may need to see how/where that index (hypothetically) was set -- preferably wrapped in this forum's [code]...[/code] tags for readability. Maybe you just need a foreach() loop instead of a for() loop, so that you only iterate on existing indexes?

PS: not sure if this might be the case without some debugging, but depending on how you are testing and what the PHP type is of the value in question, a value of "0" will return true from empty() and true from isset(), but an actual null will be true from empty() but false from isset() -- however, it should still be "defined", i.e. not throwing a warning.

minifairy

NogDog
Thanks. Yes that is what I thought it should not throw a warning. I was looking for that code bit but didn't see it as an option thought maybe it had been removed on this new update of the forum. So here is the code that I was working on at the time I asked that question.

as for the exact warning. Well I've been trying to eliminate these warnings and I delete the error log as I go so all I can tell you is as near as I recall. It said "index 5 undefined" or "index 4 undefined" either of which could easily be NULL or ''. As I recall I eliminated the warning by adding another | to the end of the array load. So would it not comeback as set if it is the last item in the explode and is say NULL or ''?

bonus Index decimal(6,2) can be NULL
wdisc Index float(5,2) defaults to 0.00 but also set as can be NULL
pdate date can be NULL
maillist date can be NULL

OH I'm running Bitnami on my local machine. PHP version: 7.1.19

I always get the weird stuff.

Weedpacket

The join-explode thing is a waste of effort - taking pieces and putting them together, but the only thing being done with the string is taking it apart and getting the pieces back.

Even better, the whole "convert wdisc to custtype" thing could be done by the database, and then the oddity of adding $custtype to the record separately can be avoided. The row object itself can be stored in the array. Then, apart from the update bit (doing a query inside a loop is a code smell), the entire loop collapses into a single fetch_all call. Then iterate over the resulting array.

As for the update bit, "Update bonus set maillist='".$tday."' where id in (Select b.id from bonus b, cust c where (b.pdate>b.maillist or b.maillist is null) and b.custid=c.id and b.shop='".$custshop."' and c.email<>'') looks like it would do the job but at this stage I wouldn't quote me on that.

Couple more things from the lines like this: is_object($result)and $result->num_rows>='1'and doesn't necessarily mean the same thing as &&, and if you're comparing numbers you should be comparing with numbers, not strings.

More significantly, in if(($row->pdate>$row->maillist )| $row->maillist==NULL){ the | should be ||.

minifairy

Weedpacket Thank you
The logical operators tend to ellude me sometimes. In the php documentation I don't see the difference between '&&' and 'and' in the table of operators and precedence they are both listed as left and logical. The || is listed as logical as is the 'or' the | as bitwise. I don't exactly understand the bitwise one. I comprehend that it is compared a bit at a time but that doesn't really help me much. I for sure should be using the logical one.

As for the wdisc bit I'm basically converting that because the database is storing a discount value and it can be different for different customers but they are still wholesale customers. Their status is stored by the database as in the amount of the discount, but suffice it to say that if they have a discount larger that 1 they are wholesale otherwise they are retail. In the mailing list I want to be able to pick out wholesale and retail not have to choose by various discount levels. The routine works but I was just getting warnings on some of the indexes in the exploded array. I would guess that whether I loaded it into a multi dimensional array in the beginning or exploded the array as I step through it the result in that case would be the same. For me loading a single array with the pipe delimiters (|) is easy, and I find it easier to follow in the code. I suppose there is one extra step the exploding but I don't have to worry about overlapping variables with variables already used etc. or come up with something short and sweet to call the secondary keys.

update bit (doing a query inside a loop is a code smell), the entire loop collapses into a single fetch_all call. Then iterate over the resulting array.

Not sure what you meant by this? the mailing list is a separate db with a separate connect and password not a table in the same db. So not sure how I could fetch it all at once for updating. Which is why I created the first array $mlist and closed the connection before looking for records in the mailing list.