Koekjes-probleem

Status
Niet open voor verdere reacties.

csshunter

Meubilair
Lid geworden
4 aug 2009
Berichten
6.465
Hoi-hoi!
Ik ben wat aan het rommelen met cookies in javascript en php.

Fase 1
Ik plaats een cookie met javascript, en bij een page-refresh/reload (of op een andere pagina) kan php 'm uitlezen.
PHP:
<p>PHP zegt bij openen:
	<?php 
		if ( !isset($_COOKIE["koekje"]))
			{
			echo "geen koekje te zien";}
		else {
		echo "koekje aanwezig";}
	?>
</p>
Test 1: www.bliksekaters.nl/tests/koekjes.php
So far, so good: alles naar verwachting.

Fase 2
Nu ga ik het koekje met javascript plaatsen bij een onunload. Voor de rest is alles hetzelfde.
Wat gebeurt er nu?
Bij een refresh/reload (of bekijken op een nieuwe pagina) zegt javascript dat het koekje er is, maar php zegt dat er géén koekje is!
  • Pas bij een herhaalde refresh ziet php het koekje ook.
  • Of als je van de nieuwe pagina (geen cookie door php gezien) weer teruggaat naar pagina A, dan ziet php het koekje weer wel op pagina A.
  • Daarna blijft het goed gaan.
  • Dit alles is browser-onafhankelijk, dus daar kan het niet aan liggen.
Test 2: www.bliksekaters.nl/tests/koekjes-2A.php

:shocked:

Je voelt de vragen al aankomen:
  1. Zie ik hier iets over het hoofd, of is er een andere verklaring voor?
  2. Hoe kan ik ervoor zorgen dat een via een onunload geplaatst koekje de volgende ronde wel direct door php gezien kan worden?
Met vriendelijke groet,
CSShunter
___________
NB: Bij de experimenten van Fase 2 altijd wel zowel de cache als de buffer legen, voordat je schoon opnieuw kunt beginnen! :)
 
Hoi,


Ik heb zelf ook even lopen experimenteren. Eerst even 'uit het hoofd': dit zou er moeten gebeuren:

  • client call naar het unload event, set client cookie.
  • refresh (of navigeren van/naar etc)
  • client http request naar pagina met cookies
  • server ontvangt http request met cookie

testpagina: http://that-guy.net/wokflok/test/koektrommel.php
En nou... bij mij doet 'ie t dus wel als je het tabblad sluit! Maar bij een refresh niet.


Hoe kan dit? Nou, het event heet unload - een quote uit de w3 docs:

The onunload event occurs when the user agent removes a document from a window or frame. [...]

Klaarblijkelijk telt een 'refresh' dus niet, maar het opnieuw openen van een pagina wel. Wat het aller raarste is: als je refresht wordt het unload event WEL gecalled (je krijgt immers een alert). Waarom? Ik heb geen flauw idee. Duid dit er niet op dat bij een refresh geen nieuwe cookies worden gestuurd?


code van de pagina:
PHP:
<?php

   error_reporting(E_ALL);
   ini_set('display_errors', '1');

?><!doctype html>


<html lang='en'>


   <head>

      <meta charset='utf-8' />

      <title>De Koekjestrommel</title>

   </head>

   <body>

      <pre id='output'></pre>

      <button id='wis'>wis koekje</button>
      <button id='bak'>bak koekje</button>
      <button id='set'>maak unload koekje</button>

<?php

   function isErEenKoekje()
   {
      return isset($_COOKIE['koekjestrommel']);
   }

?>


      <script>

function out(str)
{
   document.getElementById('output').innerHTML += str + '<br />';
}


function bakKoekje()
{
   alert('koekje wordt gebakken!');
   document.cookie = 'koekjestrommel=vol;expires=' + new Date(new Date().getTime() + (1000 * 60 * 60 * 24 * 1)).toGMTString() + ';path=/';
}

function wisKoekje()
{
   document.cookie = 'koekjestrommel=leeg;expires=' + new Date(new Date().getTime() - 1000).toGMTString() + ';path=/';
}

function isErEenKoekje()
{
   var i,
       crumble = document.cookie.split(';'),
       len = crumble.length;

   for(i = 0; i < len; i++)
   {
      if(crumble[i].indexOf('koekjestrommel=vol') != -1)
      {
         return true;
      }
   }

   return false;
}



document.getElementById('wis').addEventListener('click', wisKoekje, false);
document.getElementById('bak').addEventListener('click', bakKoekje, false);
document.getElementById('set').addEventListener('click', function()
{
   window.addEventListener('unload', bakKoekje, false);
   out('on unload wordt nu een koekje gebakken!');
});



out('Javascript: op het moment is er <strong>' + (isErEenKoekje() === true ? 'wel' : 'niet')  + '</strong> een koekje aanwezig');
out('PHP: op het moment is er <strong><?php echo (isErEenKoekje() === true ? 'wel' : 'niet'); ?></strong> een koekje aanwezig');



      </script>

   </body>

</html>
 
Laatst bewerkt:
Hoi That Guy!
Ik heb zelf ook even lopen experimenteren.
Bedankt voor je compassie! 't Was ook al zo verdacht stil. ;)

Eerst werkte bij mij (onder WinXP, alle browsers) je testpagina niet; wel een foutmelding van de Javascript-console. Na even studderen:
  • De addEventListener op de set-button miste op het eind de "false" parameter.
  • In de twee out('...') aanroepen op het eind van het script waren dezelfde ''s (hier staat: enkelvoudige apostrof's) gebruikt voor zowel de functie-string zelf als de inwendige wel/niet-opties.
  • Even hersteld, en voor de compleetheid nog was out()'s toegevoegd bij het zetten/wissen van de koekjes.
En daar komt je koekjestrommel in volle glorie: koekjestrommel-nw.php

Gauw testen!
Ja, voor de refresh exact hetzelfde resultaat als in mijn testpagina's.
Ik had trouwens bij mij een "harde reload" ingebouwd: heroproepen van de pagina-url (met passeren van de cache/buffer); maar dat maakt geen verschil...!
Des te merkwaardiger dat er wel een verschil is bij sluiten van een tabblad en dan weer openen. Die variant had ik nog niet uitgeprobeerd.
Ik dacht in mijn onschuld altijd dat een "harde reload" hetzelfde was als pagina afsluiten (tabblad sluiten) en opnieuw loaden (tabblad openen)...

Dat vraagt om een andere check: koekje bij unload er in zetten, pagina aan laten staan, pagina ook in nieuwe tab openen.
Resultaat: in nieuwe pagina natuurlijk geen koekje te zien (noch php, noch js).
Terug naar eerste pagina, en die afgesloten.
Terug naar nieuwe pagina, refresh: ja hoor, twee keer koekje!

Klaarblijkelijk telt een 'refresh' dus niet, maar het opnieuw openen van een pagina wel.
Ja, voor php geldt dat, maar voor js niet. En daar kan ik met mijn pet niet bij.
Want bij het zetten van een koekje tijdens de rit (dus niet met onunload) geeft php na een refresh/reload wel het koekje!
  • In de test: wis koekje > refresh > 2x geen koekje > bak koekje > refresh > 2x wel koekje!

... Duid dit er niet op dat bij een refresh geen nieuwe cookies worden gestuurd?
Nee, dat kan niet, want anders zou (a) de test hierboven niet opgaan, en (b) zou javascript de volgende ronde óók niet het koekje kunnen ontdekken.
Het verschil js/php moet 'm in de onunload zitten.

Het enige dat overblijft, is volgens mij de baktijd: op moment van unloaden begint het javascript het koekje te bakken en weg te zetten op de kast van de bezoeker. Dat duur een paar milliseconden. Als tegelijkertijd de pagina gerefreshed wordt, en php de koekjestrommel gaat opzoeken (vóórdat de pagina opnieuw richting bezoeker gaat), zit er nog niets in, want js is nog bezig.
Maar als de pagina gedownload is, en javascript (client side) koekjes gaat zoeken, is het er wel (al was het maar omdat js zijn vorige klusje eerst moest afmaken).
Dit kan ook verklaren waarom php bij een tweede refresh het koekje wel ziet.
Het koekje was onderweg van de bakplaat naar de koektrommel! :)

Wat denk je, zou dat het zijn?
Of zou het ook nog in een of andere sessie-toestand van php kunnen zitten? (daar weet ik nulkommapoffertje van)
Maar wel jammer dat het werkt zoals het werkt, want ik had met die onunload iets willen gaan doen... :rolleyes:
 
Laatst bewerkt:
Nu heeft php een sleep()-functie, kwam ik achter, waarmee executie vertraagd kan worden.
Dus die ook maar eens geprobeerd, want dan zou het javascript wat meer tijd krijgen om het koekje in de trommel te doen:
Resultaat:
  • Geen effect, php kan het koekje nog steeds niet zien.
  • Tot mijn oprechte verwondering, dus :shocked:, wacht de nieuwe slaap-php pagina niet zelf met het doorgeven van de pagina, maar wordt het triggeren van het javascript-alert "koekje wordt gebakken!" c.q. het hele unload-gebeuren van de afzendende pagina in de slaapstand gezet!
Zou daar een plausibele verklaring voor zijn?

Met vriendelijke groet,
CSShunter
 
Hmmm....
het hele unload-gebeuren van de afzendende pagina in de slaapstand gezet!

Dit zou er op kunnen wijzen dat de pagina eerst wordt opgehaald - en pas als 'ie opgehaald* is en klaar om te laten zien, de unload wordt getriggerd. Wat nogal apart is. Volgens mij is dit een soortgelijk probleem.

Ik quote ook van hier:
The exact handling of the unload event has varied from version to version of browsers. For example, some versions of Firefox trigger the event when a link is followed, but not when the window is closed. In practical usage, behavior should be tested on all supported browsers, and contrasted with the proprietary beforeunload event.

Misschien kan je eens kijken of het met 't onbeforeunload van IE wel werkt. Natuurlijk is een IE-alleen geen goede oplossing, maargoed.


:thumb:





* het HTTP request van de browser is klaar. Niet alleen de headers worden opgehaald, maar alles (anders zou de sleep() 't niet ophouden).
 
Laatst bewerkt:
Dit zou er op kunnen wijzen dat de pagina eerst wordt opgehaald - en pas als 'ie opgehaald* is en klaar om te laten zien, de unload wordt getriggerd. Wat nogal apart is.
Ja, daar kwam ik ook op uit.
Maar bij nader inzien misschien toch niet zo vreemd: hierdoor wordt vermeden dat er een gat ontstaat tussen het showen van de twee pagina's. Gebruikelijk is, dat de oude pagina op scherm blijft staat totdat deze soepel wordt overschreven door het renderen van de nieuwe. Als de eerste geunload wordt, en de tweede nog niet klaar is (om welke reden dan ook: door een slaapstand, omdat de html-download lang duurt, enz.) zou er een tijd lang niets (een zwart gat of een wit gat) te zien zijn op het scherm.
Kennelijk wordt daarom inderdaad de unload en de onunload vastgehouden.

=====
Volgens mij is dit een soortgelijk probleem.
Volgens mij ook! - Maar ik zie er ook geen oplossing bij staan.

=====
Ik quote ook van hier:
Tja, dat maakt het er ook niet handiger op. :rolleyes:

=====
Misschien kan je eens kijken of het met 't onbeforeunload van IE wel werkt. Natuurlijk is een IE-alleen geen goede oplossing, maargoed.
Ook aan gedacht - maar omdat ik een crossbrowser methode zoek, niet die weg ingeslagen. Misschien toch nog eens naar kijken, dat vangt als het werkt toch zo'n 50% van het publiek af.
 
Zo'n haast met proberen, vorige bericht niet eens verzonden. ;)
Hier istie met de onbeforeunload:
Bijna was ik hoera aan 't roepen, want behalve IE reageren ook Firefox, Chrome en Safari hier positief op, hoewel onreglementaire proprietaire code: dat doen ze goed! :)
Maar helaas: Opera houdt zich aan de regels, en doet niets. :rolleyes:

Al met al: toch weer een paar stapjes gevorderd!
Nu nog even de 53.000 Googles over "?q=onbeforeunload+in+Opera" doornemen. Wie weet zit er een workaround tussen.

Met vriendelijke groet,
CSShunter
 
Na een setje Googles over de Opera-weigering kreeg ik plots de inval om dan maar als workaround een onclick="setCookie" aan elke link vast te plakken. Met automatiekje:
[JS]function createCookieFunctionOnAllLinks(){
var allLinks = document.getElementsByTagName('a');
for (var i=0; i<allLinks.length; i++){
if (!allLinks.getAttribute('onclick')){
allLinks.setAttribute('onclick',"createCookie('koekje73','yes'); return true");
}
}
}[/JS]
Niet zo fraai, maar het gaat in ieder geval vooraf aan de unload.
Die doet het keurig in Opera, Firefox, Chrome en Safari.
Maar nu weer niet in Internet Explorer (nr. 7 iig niet). *)
Dan zou het een combi van de onbeforeunload en de onclick moeten worden... pfff!

Maar nu even genoeg koekjes verorberd: trek in iets hartigs! :)
___________
*) Ook weer zo wonderbaarlijk: IE7 reageert totaal niet op deze met js geïmplanteerde onclick's (die ook bij IE gewoon in de gegenereerde broncode terechtkomen), maar wel op exact dezelfde onclick's als je die direct in de html zet.
Ook geprobeerd met createAttribute() en setAttributeNode(), dat volgens ppk van quirksmode.org iets betrouwbaarder is. Maar niet voor IE7: geen verschil.
 
Beste mensen,
Dit koekjesprobleem op het snijvlak van javascript en php is kennelijk niet 100% op te lossen. Ik ben wel wat stapjes verder gekomen!

Maar misschien kan iemand uit de php-hoek hier nog een verhelderend licht op laten schijnen.
Een verblindende oplossing zou helemaal mooi zijn. :)

Vandaar heb ik de mods gevraagd dit topic in het forum te verplaatsen van "Programmeren > Javascript" naar "Programmeren > PHP".

Wie weet!
Met vriendelijke groet,
CSShunter
 
misschien een stom idee, maar zou je niet eerst een functie kunnen starten die kijkt welke browser, dus welke functie je naar de browser moet sturen.
Dus een soort browser switch die de cookiefunctie voor je regelt.
 
Hoi Phobia,
Kijk, dat is snel van onze alerte mods: rap na mijn verzoek verplaatst! Lijkt Overtoom wel! :thumb:
En snelle reactie ook: dank!

- Ja, helemaal niet zo'n stom idee: zo'n browser-snif is de uitvoering van waar ik op uit kwam in nr.#8. Waarschijnlijk kan dat wel met een Conditional Comment geregeld worden, omdat IE in deze workaround de enige uitzondering is.
Maar ik vindt de hele onclick="setCookie" workaround wel een omslachtige methode. Daar komt nog bij:
  • Om bestaande pagina's te kunnen bedienen, die er toevallig al andere onclick="..." 's in hebben zitten, zou m'n createCookieFunctionOnAllLinks() uitgebreid moeten worden met een detectie van in werking zijnde onclick's, en daar dan deze nieuwe aan toevoegen.
En je hebt gelijk:
je kennis begint op Google!
Zou ik toch nog eens verder moeten kijken op de 53.000 Googles over onbeforeunload in Opera?
Opera gewoon laten vallen kan natuurlijk ook altijd! ;)

Of is er in php behalve de sleep-functie nog een andere manier om de php-executie even vast te houden, zodat een cookie via een gewoon onunload-commando met javascript tijd heeft om door te dringen?

Met vriendelijke groet,
CSShunter
 
Laatst bewerkt:
Status
Niet open voor verdere reacties.
Terug
Bovenaan Onderaan