Page 1 of 2 12 LastLast
Results 1 to 10 of 12

Thread: setcookie before content????

  1. #1
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default setcookie before content????

    Quote Originally Posted by PHP.net
    setcookie() defines a cookie to be sent along with the rest of the HTTP headers. Like other headers, cookies must be sent before any output from your script (this is a protocol restriction). This requires that you place calls to this function prior to any output, including <html> and <head> tags as well as any whitespace.
    As some of you may know I recently authored my first real all PHP script on quotes.

    Anyways, it was suggested that I use COOKIE instead of SESSION to preserve the history. I had thought about that and knew from javascript that cookies are tricky and that in PHP that they're easier in some respects and trickier in others.

    It took me a little while to iron out the issues. However, in the process I discovered that the above is apparently not true. I hadn't really paid it any real attention until after I got things working. It was in the back of my mind though, so after things were working I reread it.

    It just seems wrong. I'm using PHP 5.30 on a localhost Apache server via WAMP. I set the PHP cookies in the body of the page, and they work!

    They also work on my live demo:

    http://jscheuer1.comli.com/quotes/quote_c.php

    Any ideas as to why?
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  2. #2
    Join Date
    Mar 2011
    Posts
    2,144
    Thanks
    59
    Thanked 116 Times in 113 Posts
    Blog Entries
    4

    Default

    Can we see the source of the new page?
    It shouldn't work if you've already sent the headers.... do you have warnings and errors turned on in you php.ini?

    I actually experienced something similar to this; without realising, I put a header('location:url'); on my site after the content was sent. It worked fine on wamp (I have warnings disabled), but as soon as I uploaded it to a server (with warnings enabled), my page stopped working....
    Last edited by keyboard; 08-13-2012 at 10:19 PM. Reason: oops, unintentional smiley face

  3. #3
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    Warnings and errors are on for the localhost server. I don't know about the live server. But clearly I'm setting the two cookies in the body:

    PHP Code:
    <?php
    header
    ('Content-type: text/html; charset=utf-8');
    session_start();
    ?>
    <!DOCTYPE html>
    <html>
    <head>
    <title>Quote Demo</title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <link rel="stylesheet" href="quote.css" type="text/css">
    <!-- 
    // Quote Fetching Script (c)2012 John Davenport Scheuer
    // as first seen in http://www.dynamicdrive.com/forums/
    // username: jscheuer1 - This Notice Must Remain for Legal Use
     -->
    </head>
    <body>
    <div class=quotecontent>
    <div class="quotecontainer">
    <?php 
    if(isset($_GET['clear'])){
        
    setcookie('quoteIndexes'""time() - 3600); 
        
    setcookie('bigIndexRand'""time() - 3600); 
    }
    $qpattern '/[^a-z]+$/i'
    $quotefile 'quotations_num.php'
    $quoterights false
    $lastUpdate date"j F Y"filemtime($quotefile)); 
    include 
    $quotefile
    $noAuthor "&nbsp;"
    $theCount count($quotations);
    $numQuotes =  $theCount 2
    $maxlength strlen($numQuotes); 
    $hqnum = isset($_GET['hqnum'])? abs($_GET['hqnum']) : '';
    if(
    $hqnum and is_nan($hqnum)){
        
    $hqnum '';
    }
    $bigIndexRand = isset($_COOKIE['bigIndexRand'])? explode('.'$_COOKIE['bigIndexRand']) : array(); 
    if(isset(
    $_GET['qnum'])){
        
    $bigIndex = ($_GET['qnum'] + 2) % $theCount & ~1
    } elseif (
    $hqnum){
        
    $bigIndex = (($hqnum -1) * 2) % $theCount & ~1
    } elseif (isset(
    $_GET['pnum'])){
        
    $bigIndex = ($_GET['pnum'] - $theCount) % $theCount & ~1
    } else {
        
    $bigIndex mt_rand(0$theCount 1) & ~1
        if(!isset(
    $_GET['clear']) and isset($_COOKIE['bigIndexRand'])){
            
    setcookie('bigIndexRand'$_COOKIE['bigIndexRand'] . ".$bigIndex"time() + 3600 24 365);
        } else {
            
    setcookie('bigIndexRand'$bigIndextime() + 3600 24 365);
        }
        
    $bigIndexRand[] = $bigIndex;
    }
    if(!isset(
    $_GET['clear']) and isset($_COOKIE['quoteIndexes'])){
        
    $quoteIndexes $_COOKIE['quoteIndexes'] . '.' . ($bigIndex 1);
        
    $quoteIndexes explode('.'$quoteIndexes);
    } else {
        
    $quoteIndexes $bigIndex 1;
        
    $quoteIndexes = array($quoteIndexes);
    }
    echo (
    $bigIndex 1) . ' of ' $numQuotes
    ?>
    <br /><br /><div class="quotations">
    <?php
    echo $quotations [$bigIndex]; 
    ?>
    <br /><br /><div class="byline">
    <?php
    if ($quotations [$bigIndex 1] != $noAuthor) { 
        echo 
    "-&nbsp; {$quotations [$bigIndex 1]}<br /><br />"

    ?> 
    </div></div>

    <div class="clear">Quote File dated: <?php echo $lastUpdate?></div>
    <div class="center">
        <a href="<?php echo "{$_SERVER['PHP_SELF']}?pnum=$bigIndex?>" title="Previous in Numeric Order">Prev Quote</a>  
        <a href="<?php echo $_SERVER['PHP_SELF']; ?>" title="Get a Random Quote">Random Quote</a>  
        <a href="<?php echo "{$_SERVER['PHP_SELF']}?qnum=$bigIndex?>" title="Next in Numeric Order">Next Quote</a>  
    </div>
    <form action="<?php echo $_SERVER['PHP_SELF']; ?>">
    Go To #: <input type="text" name="hqnum" maxlength=<?php echo $maxlength?> size=<?php echo $maxlength 1?> title="Input Number from 1 to <?php echo $numQuotes?>" />
    </form>
    <div class="clear"></div>
    <div><b>History</b>:<span class="center">
    <a href="<?php echo "{$_SERVER['PHP_SELF']}?clear=clear" ?>" title="Clear History, Reload with a Random Quote">
    Clear</a></span>
    <div id="history"><?php
    $quoteIndexes 
    array_unique(array_reverse($quoteIndexes));
    function 
    formatval($v){
        
    $v trim($v);// . '';
        
    $v str_pad($v3' 'STR_PAD_LEFT);
        return 
    $v;
    }
    foreach (
    $quoteIndexes as $val){
        
    $bval = ($val 1) * 2
        
    $asterisk in_array($bval$bigIndexRand)? '<span class="asterisk">*</span>' '<span class="asteriskhidden">*</span>';
        echo 
    "<a href='{$_SERVER['PHP_SELF']}?hqnum=$val'><pre>" .
        
    formatval($val) . "</pre></a> - $asterisk.
        
    preg_replace($qpattern''substr(strip_tags(html_entity_decode($quotations [$bval], ENT_QUOTES'UTF-8')), 020)) .
        
    " . . .<br/>\n";
    }
    setcookie('quoteIndexes'implode('.'array_reverse($quoteIndexes)), time() + 3600 24 365); 
    ?></div><pre>   </pre> <span class="asteriskhidden">-</span> 
    <span class="asterisk">*</span><span class="smaller">a quote chosen at random at some point</span></div>
    </div>
    </div>
    <?php if($quoterights){echo "<div class='quoterights'>$quoterights</div>";}; ?> 
    <!-- The below HTML and PHP may be used (be careful, backup what you've got) to generate -->
    <!-- a renumbered/reformated version of the quotes section of the quotes file -->

    <!-- <textarea name="" cols="50" rows="5" wrap="off"> --><?php
    /* foreach ($quotations as $key => $val){
    echo ($key % 2 == 0)? "\n//#" . ($key / 2 + 1) . "\n" : '';
    echo '"' . str_replace('"', '\"', $val) . "\",\n";
    } */
    ?><!-- </textarea> -->

    <!-- End Optional Renumbering/Reformatting Routine -->
    <?php
    //to see what the cookies are doing, uncomment the below line:
    //print_r($_COOKIE);
    ?>
    </body>
    </html>
    Note: The first two are for clearing the cookies if that link is clicked. The other three (even farther down in the code) are for setting the cookies. All five are in the body of the page.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  4. #4
    Join Date
    Mar 2006
    Location
    Illinois, USA
    Posts
    12,164
    Thanks
    265
    Thanked 690 Times in 678 Posts

    Default

    Sending headers after content is sent shouldn't work. I don't know about WAMP, but I'd assume it's just an exception because it's running locally. Maybe even some servers could work like that, but it's well known that it's not reliable in PHP, and I've never seen it work (but I'm not doubting that it did for you).

    By the way, I suggest always using sessions instead of cookies, unless you need to store long-term information, in which case cookies will last longer (with the same limitations as in Javascript) than a session (which lasts... one session). But sessions are more reliable for important information and tracking anything while the user is on the site. So a really good solution would be to use sessions and also cookies, so the cookie acts as a long-term backup for the session, while the session still will work even if cookies are disabled.
    Daniel - Freelance Web Design | <?php?> | <html>| español | Deutsch | italiano | português | català | un peu de français | some knowledge of several other languages: I can sometimes help translate here on DD | Linguistics Forum

  5. #5
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    It might depend upon the type of cookie. I know from javascript cookies that they may be set and read at any time. However PHP cookies cannot be read until the page is reloaded. That's partly due to the way PHP works. But it fails the logic test in that I see no reason why it has to be that way for insecure PHP cookies that are also available to javascript. You can make a secure PHP cookie that's not available to javascript. I can see how that type of cookie might not be able to be updated so easily.

    In any case, I had originally written the code with $_SESSION. So it should be easy enough to bring that back. In fact, using cookies I found I had to make a parallel array for each cookie anyway for use while the page was being parsed by the server, those could as easily be session arrays as they were in the original code.

    Now I'm wondering if the manual has the same restriction(s) on session though. I seem to recall something about that, I'll have to check. It was working though with session being set in the body.

    And, without looking at the code too closely right at the moment, I'm pretty sure all of that (cookie and/or session activity) could be done before any content is sent/parsed.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  6. #6
    Join Date
    Mar 2006
    Location
    Illinois, USA
    Posts
    12,164
    Thanks
    265
    Thanked 690 Times in 678 Posts

    Default

    It might depend upon the type of cookie.
    No, it shouldn't--
    I know from javascript cookies that they may be set and read at any time.
    This is the fundamental difference between clientside and serverside languages. Javascript is always active; PHP is only active while processing the code, then the HTML is sent as text to the browser-- after all headers are sent. This is simply how PHP works.

    However PHP cookies cannot be read until the page is reloaded.
    That's absolutely true and due to the cookies being sent during the request. There's no way around that at all.
    In the case of setting cookies, it's theoretically imaginable that a serverside language could set cookies several times-- that is, it could send headers several times, even as the page is being served. But that's not how it works or how PHP (or the internet?) is designed. Headers are sent first, and others are ignored.
    Technically when PHP yells at you and says "Cannot do X. Headers already sent.", it's lying. It can do that. But the browser will ignore it. So it's not configured to do it. In short, it can't.

    But it fails the logic test in that I see no reason why it has to be that way for insecure PHP cookies that are also available to javascript. You can make a secure PHP cookie that's not available to javascript. I can see how that type of cookie might not be able to be updated so easily.
    It's not about cookie security. Cookies, as far as I know, aren't actually secure. They simply have a setting to be sent only to PHP and not readable by JS. Otherwise they're just normal. (I'm not positive about that, so if someone else knows more, jump in to correct me.)
    This all ends up being the order of operations problem described above.

    In any case, I had originally written the code with $_SESSION. So it should be easy enough to bring that back. In fact, using cookies I found I had to make a parallel array for each cookie anyway for use while the page was being parsed by the server, those could as easily be session arrays as they were in the original code.
    If I'm imagining it correctly, you could probably store the array keys in CSV format (or look into PHP's serialize() function to store an array as a string) and use that in the cookie. Then combine that with the session method. The session would work as it is now. You'd just also have a cookie to store that data for later in case the user left. So the cookie would only be used if no session info existed and a cookie was detected-- returning user. Otherwise, use the session by default. (I mentioned that in the last post, same idea, just a little more worked out now.)

    Now I'm wondering if the manual has the same restriction(s) on session though. I seem to recall something about that, I'll have to check. It was working though with session being set in the body.
    It's exactly the same restriction. Sessions work with a session ID cookie, so the session must be started before headers are sent. But unlike cookies, sessions (that is: $_SESSION) can be used at any time, regardless of headers or not. So you must simply include <?php session_start(); ?> as the very first item in your script and never worry about it again. All that matters is that the cookie is set to link the user to the session. All session data is stored on the server in a database (automatically configured by PHP). In fact, sessions are completely secure-- only when you decide to show the user some variable from it can they see it-- not like cookies where they are stored on the computer. So sessions are often better for security as well, either to not reveal everything and/or prevent the user from changing a value (for example, setting a progress level on a quiz, without letting the user change that number to move ahead and cheat), or to keep that information on the server instead of leaving it vulnerable on the user's computer for someone else to look at later.

    (Actually, so I'm not skipping any details here, there are several other things about sessions that must be placed in the 'headers' part of the PHP, including when you want to end/destroy a session or change the session ID. In those cases, you need to do that at the top, too. But only advanced uses of sessions would ever get you to that point. The first time you'll encounter it is if you want to create a logout option in a login system. But another very easy way to never deal with this, and to avoid the headers problem is simply to use unset($_SESSION), which will destroy all data while keeping the session itself (just a shell then) open. The $_SESSION array will be reset the next time the page loads, or you can recreate an entry manually if you want. Just be sure to include isset() if you want to access any values in it after the data may be destroyed, or you'll find that the array index you're looking for isn't set.)

    And, without looking at the code too closely right at the moment, I'm pretty sure all of that (cookie and/or session activity) could be done before any content is sent/parsed.
    Not required in this case. But while the topic is relevant (because it can come up, often in the case of trying to add code to the <head> section, after you're already processing the <body>), here's some quick info.
    Sometimes with some basic logic (separating logic and content from layout), it's easy to avoid the problem. But sometimes it's not. When it's not:
    The lazy method is to use an output buffer (see php.net). It's easy, but "bad" and harder on the server. It also means you don't really know how to properly work around it. But sometimes it can be useful. Very very rarely are output buffers necessary-- the only time I've ever found was when I was creating images using the GD library and one of the options didn't allow saving the file instead of outputting it.
    The real method is to use some form of templating. It makes your job a lot harder, but it's worth setting up a basic templating system (whatever that means in your specific case) instead of trying to awkwardly rearrange everything to be in the right order, using lots of individually pre-processed HTML chunks, etc.
    Daniel - Freelance Web Design | <?php?> | <html>| español | Deutsch | italiano | português | català | un peu de français | some knowledge of several other languages: I can sometimes help translate here on DD | Linguistics Forum

  7. #7
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    The manual says not to serialize for cookies as it can lead to errors. I'm already using PSV (period separated values) as opposed to CSV because the period character doesn't need to be encoded, while the comma does - an old trick I learned from javascript cookies, saving 2 characters per separator in the cookie value (allowing for more data to be stored), and making the cookie more human legible (that part has minor trade offs). It could be mistaken by code as a decimal point, but not the way I'm using it. If that were a problem (if the numbers included the decimal point) and the content was otherwise numbers only, an ordinary letter could probably be used instead.

    And it's my understanding session requires cookies unless measures are taken to use a query string backup.
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  8. #8
    Join Date
    Mar 2006
    Location
    Illinois, USA
    Posts
    12,164
    Thanks
    265
    Thanked 690 Times in 678 Posts

    Default

    A session requires the session ID is sent. It can be sent via cookies or get/post methods. I'm not exactly sure how that works, and my impression was that it was automatic. But perhaps not.

    As for the PSV method, that's fine. I just meant in general that you can look into something like that. You could use serialize and some filter to make it safe, if you need something more than a simple list. But I've had some trouble with serialize too, so be careful with it if you do.
    Daniel - Freelance Web Design | <?php?> | <html>| español | Deutsch | italiano | português | català | un peu de français | some knowledge of several other languages: I can sometimes help translate here on DD | Linguistics Forum

  9. #9
    Join Date
    Mar 2005
    Location
    SE PA USA
    Posts
    30,495
    Thanks
    82
    Thanked 3,449 Times in 3,410 Posts
    Blog Entries
    12

    Default

    Now that I'm thinking of it, it was security:

    Cookies names can be set as array names and will be available to your PHP scripts as arrays but separate cookies are stored on the user's system. Consider explode() to set one cookie with multiple names and values. It is not recommended to use serialize() for this purpose, because it can result in security holes.
    I had already chosen implode/explode for storing/retrieving arrays in/from cookies. That part is just like javascript's join/split for the same purposes.

    And the thing on session id without cookies, I'm not sure. I know there's at least an option to manually define and pass it (somepage.php?myvar=[SID], something like that) as a part of the query string, I think regardless of whether or not cookies are enabled. I'll have to reread that part.

    What I didn't know is that the rest of the session data is on the server, that's pretty cool.

    Also, from playing with this it looked like I could store an array as an array in a session variable. What's you're take on that?
    - John
    ________________________

    Show Additional Thanks: International Rescue Committee - Donate or: The Ocean Conservancy - Donate or: PayPal - Donate

  10. #10
    Join Date
    Apr 2008
    Location
    So.Cal
    Posts
    3,643
    Thanks
    63
    Thanked 516 Times in 502 Posts
    Blog Entries
    5

    Default

    Quote Originally Posted by jscheuer1 View Post
    Also, from playing with this it looked like I could store an array as an array in a session variable. What's you're take on that?
    absolutely - global variables (like $_SESSION) function just like any other php variable, with the exception that they are available in all scopes and can have values preset by the php engine. I can't imagine $_SESSION would be very useful at all if you couldn't use multi-dimensional arrays.

    Also, I'm betting that your setcookie() is working (even though it should not be) because php does a limited amount of caching on its own - even when you're out side of ?><?php tags, it doesn't output to the browser byte by byte, it waits for a decent amount of data to build up and then flushes it. So, even though it's after the output "started," your cookie is probably being set before the first actual output to the browser. I wouldn't have expected that, however, and I certainly wouldn't rely on it.

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •