<?php

 
// Modified-Stempel (der aktuellen Datei) ermitteln & Last-Modified-Header senden
 
$aModifiedStamps=http_modified(TRUE);
 
// Modified-Status abgleichen, 304-Header senden & Script beenden
 
$bModifiedState=http_not_modified($aModifiedStamps['Last-Modified']);

 
// Dieser Script-Teil wird nur ausgefuehrt, wenn der Browser neue Daten braucht
 
header('Content-Type: text/html');

 echo 
'<title>Conditional Request - Last-Modified-Header</title>';
 echo 
'Last-Modified: '.gmdate('D, d M Y H:i:s',$aModifiedStamps['Last-Modified']).' GMT'.'<br>';
 echo 
'Actual Time: '.date('D, d M Y H:i:s').'<br>';


// ===========================================================================

/**
 * Ermitteln und ggf. Senden eines Last-Modified- und/oder ETag-Headers.
 *
 * <i>array http_modified(mixed time [, string data [, bool header_flag]])</i>
 *
 * @version 080807
 * @since 060201
 * @author Cybaer <cybaer@binon.net>
 * @copyright Copyright (c) 2006 Cybaer
 *
 * @category HTTP_LIBRARY
 * @access public
 *
 * @param mixed $mLastModified Ein Timestamp (int), ein Datum oder Dateiname (string) oder ein Flag (bool)
 * @param string $sETag Eine fertige Checksumme (Laenge <= 34 Bytes) oder Daten, ueber die ein MD5-Hash gebildet wird (Laenge > 34 Bytes)
 * @param bool $bSendHeader Wenn HTTP-Header gesendet werden soll TRUE (Default), sonst FALSE
 * @return array Assoz. Array mit den ermittelten Werten 'Last-Modified' und 'ETag'
 * Example: <code><?php
 *  $modifiedStamps=http_modified(TRUE);     // Last-Modified-Header mit Timestamp der angeforderten Resource
 *  $modifiedStamps=http_modified(__FILE__); // Last-Modified-Header mit Timestamp des aktuellen Scripts
 *  $modifiedStamps=http_modified('now');    // Last-Modified-Header mit aktuellem Timestamp
 *  $modifiedStamps=http_modified(time());   // Last-Modified-Header mit aktuellem Timestamp
 *  $modifiedStamps=http_modified(FALSE,'5415d-cb5-b780780'); // ETag-Header mit vorgegebener Checksumme "5415d-cb5-b780780"
 *  $modifiedStamps=http_modified(FALSE,'12345678901234567890123456789012345'); // ETag-Header mit MD5-Hash "cbe8cefc44dad51f7257da79c686df97"
 *  $modifiedStamps=http_modified(TRUE,NULL,FALSE); // Timestamp der angeforderten Resource ermitteln, aber keinen Header senden
 * ?></code>
 */
function http_modified($mLastModified=NULL,$sETag=NULL,$bSendHeader=TRUE) {
 
$bSendHeader=($bSendHeader && !headers_sent());
 
// Last-Modified-Angabe vorhanden (Angabe: z.B. Timestamp)
 
if($mLastModified!==NULL && $mLastModified!==FALSE) {
  if(
$mLastModified===TRUE) {
   
// Angabe: TRUE -> Timestamp der angeforderten Resource bzw. (falls keine passende Datei vorhanden) des ausfuehrenden Scripts
   
$file=$_SERVER['DOCUMENT_ROOT'].((!empty($_SERVER['REQUEST_URI']) && file_exists($_SERVER['DOCUMENT_ROOT'].$_SERVER['REQUEST_URI']))?$_SERVER['REQUEST_URI']:$_SERVER['PHP_SELF']);
   
$mLastModified=(file_exists($file) && @filemtime($file))?filemtime($file):time();
  } else if(
is_string($mLastModified)) {
   
// Angabe: String -> Dateiname oder Datum (Dateiname: PHP 4 -> FALSE, PHP 5 -> -1)
   
if(($iTimestamp=strtotime($mLastModified))>0) {
    
// String mit Datum im englischen Format ("now"/"10 December 2005"/"-1 week 2 days 4 hours 2 seconds"/"last Monday"/...)
    
$mLastModified=$iTimestamp;
   } else if(
realpath($mLastModified)) {
    
// String mit Dateiname
    
$mLastModified=filemtime(realpath($mLastModified));
   }
  }
  
// Ausgabe des Headers
  
if($bSendHeader) { header('Last-Modified: '.gmdate('D, d M Y H:i:s',$mLastModified).' GMT'); }
 }

 
// ETag-Angabe vorhanden
 
if($sETag) {
  
// Wenn Kennung groesser als 34 Bytes:
  
if(strlen($sETag)>34) {
   
// MD5-Hash ermitteln und verwenden
   
$sETag=md5($sETag);
  }

  
// Ggf. uebliche Anfuehrungszeichen ergaenzen
  
if($sETag[0]!='"') { $sETag='"'.$sETag.'"'; }

  
// Ausgabe des Headers
  
if($bSendHeader) { header('ETag: '.$sETag); }
 }

 
// Rueckgabe der verwendeten Werte
 
return array('Last-Modified' => $mLastModified'ETag' => $sETag);
}

// ---------------------------------------------------------------------------

/**
 * Ueberpruefung auf NOT_MODIFIED-Status und ggf. HTTP-Status "304" senden.
 *
 * <i>bool http_not_modified(mixed time [, string checksum [, bool exit_flag [, bool header_flag]]])</i>
 *
 * @version 060516
 * @since 060201
 * @author Cybaer <cybaer@binon.net>
 * @copyright Copyright (c) 2006 Cybaer
 *
 * @category HTTP_LIBRARY
 * @access public
 *
 * @param mixed $mLastModified Ein Timestamp (int) oder ein Datum (string)
 * @param string $sETag Ein ETag-Wert
 * @param bool $bExitScript Wenn Script bei Not-Modified beendet werden soll TRUE (Default), sonst FALSE
 * @param bool $bSendHeader Wenn HTTP-Header gesendet werden soll TRUE (Default), sonst FALSE
 * @return bool Wenn Datei nicht veraendert wurde TRUE, sonst FALSE
 * Example: <code><?php
 *  http_not_modified($timestamp); // Sendet einen 304-Header und bricht das Script ab, wenn $ts einen aelteren Timestamp beeinhaltet, als im Request-Header angefragt
 *  http_not_modified("last Monday"); // Sendet einen 304-Header und bricht das Script ab, wenn Timestamp im Request-Header frischer ist als "letzter Montag"
 *  http_not_modified("now"); // Sendet keinen 304-Header und faehrt mit dem Script fort (Timestamp ist ueblicherweise aktueller, als der im Request-Header angefragte)
 *  http_not_modified(NULL,'"5415d-cb5-b780780"'); // Sendet einen 304-Header und bricht das Script ab, wenn Checksumme mit der im Request-Header uebereinstimmt
 *  http_not_modified(NULL,'"5415d-cb5-b780780"',FALSE); // Sendet einen 304-Header, wenn Checksumme mit der im Request-Header uebereinstimmt - das Script wird nicht abgebrochen
 *  $notModified=http_not_modified(NULL,'"5415d-cb5-b780780"',FALSE,FALSE); // Wenn Checksumme mit der im Request-Header uebereinstimmt, ist $notModified TRUE - weder wird das Script abgebrochen, noch wird ein Header gesendet
 * ?></code>
 */
function http_not_modified($mLastModified=NULL,$sETag=NULL,$bExitScript=TRUE,$bSendHeader=TRUE) {
 
$bSendHeader=($bSendHeader && !headers_sent());
 
// Statusvariablen initialisieren
 
$bLastModifiedChanged=NULL;
 
$bETagChanged=NULL;

 
// Last-Modified auswerten
 
if($mLastModified && !empty($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
  
// Ggf. Pruefdatum-String in Timestamp umwandeln
  
if(is_string($mLastModified)) { $mLastModified=(($iTimestamp=strtotime($mLastModified))===-1)?time():$iTimestamp; }
  
// Changed-Status ist wahr, wenn Originalzeit aelter als Pruefzeit ist
  
$bLastModifiedChanged=(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])>=$mLastModified)?FALSE:TRUE;
 }
 
// ETag auswerten
 
if($sETag && !empty($_SERVER['HTTP_IF_NONE_MATCH'])) {
  
// Changed-Status ist wahr, wenn Checksummen unterschiedlich sind
  
$bETagChanged=($_SERVER['HTTP_IF_NONE_MATCH']==(get_magic_quotes_gpc())?addslashes($sETag):$sETag)?FALSE:TRUE;
 }

 
// Status zusammenfassen
 
$bNotModified=!($bLastModifiedChanged || $bETagChanged);

 
// Wenn Aenderungsstatus und Response-Header erwuenscht
 
if($bNotModified && $bSendHeader) {
  
header('Status: 304 Not Modified');
  
header($_SERVER['SERVER_PROTOCOL'].' 304 Not Modified'); 
  
header('Connection: close');
  
// Auf Wunsch Script beenden
  
if($bExitScript) { die(); }
 }

 
// NOT-MODIFIED-Status zurueckgeben
 
return $bNotModified;
}

?>