Browse Source

PHPStan 9 for lib/http-conditional.php (#5277)

Contributes to https://github.com/FreshRSS/FreshRSS/issues/4112
Alexandre Alapetite 3 years ago
parent
commit
90bf0ecd81
2 changed files with 137 additions and 138 deletions
  1. 137 137
      lib/http-conditional.php
  2. 0 1
      tests/phpstan-next.txt

+ 137 - 137
lib/http-conditional.php

@@ -6,43 +6,35 @@
  - If the client already has the same version in its cache, avoid transferring data again (304 Not Modified).
  - If the client already has the same version in its cache, avoid transferring data again (304 Not Modified).
  - Possibility to control cache for client and proxies (public or private policy, life time).
  - Possibility to control cache for client and proxies (public or private policy, life time).
  - When $feedMode is set to true, in the case of a RSS/ATOM feed,
  - When $feedMode is set to true, in the case of a RSS/ATOM feed,
-   it puts a timestamp in the global variable $clientCacheDate to allow the sending of only the articles newer than the client's cache.
+   it puts a timestamp in the global variable $clientCacheDate to allow the sending of only the articles newer than the clients cache.
  - When $compression is set to true, compress the data before sending it to the client and persistent connections are allowed.
  - When $compression is set to true, compress the data before sending it to the client and persistent connections are allowed.
  - When $session is set to true, automatically checks if $_SESSION has been modified during the last generation the document.
  - When $session is set to true, automatically checks if $_SESSION has been modified during the last generation the document.
 
 
- Interface:
- - function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMode=false,$compression=false)
-  [Required] $UnixTimeStamp: Date of the last modification of the data to send to the client (Unix Timestamp format).
-  [Implied] $cacheSeconds=0: Lifetime in seconds of the document. If $cacheSeconds<0, cache is disabled. If $cacheSeconds==0, the document will be revalidated each time it is accessed. If $cacheSeconds>0, the document will be cashed and not revalidated against the server for this delay.
-  [Implied] $cachePrivacy=0: 0=private, 1=normal (public), 2=forced public. When public, it allows a cashed document ($cacheSeconds>0) to be shared by several users.
-  [Implied] $feedMode=false: Special RSS/ATOM feeds. When true, it sets $cachePrivacy to 0 (private), does not use the modification time of the script itself, and puts the date of the client's cache (or a old date from 1980) in the global variable $clientCacheDate.
-  [implied] $compression=false: Enable the compression and allows persistent connections (automatic detection of the capacities of the client).
-  [implied] $session=false: To be turned on when sessions are used. Checks if the data contained in $_SESSION has been modified during the last generation the document.
-  Returns: True if the connection can be closed (e.g.: the client has already the latest version), false if the new content has to be send to the client.
-
  Typical use:
  Typical use:
- <?php
-  require_once('http-conditional.php');
-  //Date of the last modification of the content (Unix Timestamp format).
-  //Examples: query the database, or last modification of a static file.
-  $dateLastModification=...;
-  if (httpConditional($dateLastModification))
-  {
-   ... //Close database connections, and other cleaning.
-   exit(); //No need to send anything
-  }
-  //Do not send any text to the client before this line.
-  ... //Rest of the script, just as you would do normally.
- ?>
-
- Version 1.8 beta, 2016-08-07, http://alexandre.alapetite.fr/doc-alex/php-http-304/
+
+```php
+<?php
+	require_once('http-conditional.php');
+	//Date of the last modification of the content (Unix Timestamp format).
+	//Examples: query the database, or last modification of a static file.
+	$dateLastModification = ...;
+	if (httpConditional($dateLastModification)) {
+		... //Close database connections, and other cleaning.
+		exit(); //No need to send anything
+	}
+	//Do not send any text to the client before this line.
+	... //Rest of the script, just as you would do normally.
+?>
+```
+
+ Version 1.9, 2023-04-08, https://alexandre.alapetite.fr/doc-alex/php-http-304/
 
 
  ------------------------------------------------------------------
  ------------------------------------------------------------------
- Written by Alexandre Alapetite, http://alexandre.alapetite.fr/cv/
+ Written by Alexandre Alapetite in 2004, https://alexandre.alapetite.fr/cv/
 
 
- Copyright 2004-2016, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
- http://creativecommons.org/licenses/by-sa/2.0/fr/
- http://alexandre.alapetite.fr/divers/apropos/#by-sa
+ Copyright 2004-2023, Licence: Creative Commons "Attribution-ShareAlike 2.0 France" BY-SA (FR),
+ https://creativecommons.org/licenses/by-sa/2.0/fr/
+ https://alexandre.alapetite.fr/divers/apropos/#by-sa
  - Attribution. You must give the original author credit
  - Attribution. You must give the original author credit
  - Share Alike. If you alter, transform, or build upon this work,
  - Share Alike. If you alter, transform, or build upon this work,
    you may distribute the resulting work only under a license identical to this one
    you may distribute the resulting work only under a license identical to this one
@@ -53,164 +45,172 @@
    in order to improve this file for the benefit of everybody
    in order to improve this file for the benefit of everybody
 
 
  If you want to distribute this code, please do it as a link to:
  If you want to distribute this code, please do it as a link to:
- http://alexandre.alapetite.fr/doc-alex/php-http-304/
+ https://alexandre.alapetite.fr/doc-alex/php-http-304/
 */
 */
 
 
-//In RSS/ATOM feedMode, contains the date of the clients last update.
-$clientCacheDate=0;	//Global public variable because PHP4 does not allow conditional arguments by reference
-$_sessionMode=false;	//Global private variable
-
-function httpConditional($UnixTimeStamp,$cacheSeconds=0,$cachePrivacy=0,$feedMode=false,$compression=false,$session=false)
-{//Credits: http://alexandre.alapetite.fr/doc-alex/php-http-304/
-	//RFC2616 HTTP/1.1: http://www.w3.org/Protocols/rfc2616/rfc2616.html
-	//RFC1945 HTTP/1.0: http://www.w3.org/Protocols/rfc1945/rfc1945.txt
-
+/**
+ * In RSS/ATOM feedMode, contains the date of the clients last update.
+ * Global public variable because PHP4 did not allow conditional arguments by reference
+ * @var int
+ */
+$clientCacheDate = 0;
+
+/**
+ * Global private variable
+ * @var bool
+ */
+$_sessionMode = false;
+
+/**
+ * RFC2616 HTTP/1.1: https://www.w3.org/Protocols/rfc2616/rfc2616.html
+ * RFC1945 HTTP/1.0: https://www.w3.org/Protocols/rfc1945/rfc1945.txt
+ * Credits: https://alexandre.alapetite.fr/doc-alex/php-http-304/
+ *
+ * @param int $UnixTimeStamp: Date of the last modification of the data to send to the client (Unix Timestamp format).
+ * @param int $cacheSeconds (default 0) Lifetime in seconds of the document. If $cacheSeconds<0, cache is disabled.
+ *	If $cacheSeconds==0, the document will be revalidated each time it is accessed. If $cacheSeconds>0, the document will be cashed and not revalidated against the server for this delay.
+ * @phpstan-param 0|1|2 $cachePrivacy
+ * @param int $cachePrivacy (default 0) 0=private, 1=normal (public), 2=forced public. When public, it allows a cashed document ($cacheSeconds>0) to be shared by several users.
+ * @param bool $feedMode (default false) Special RSS/ATOM feeds.
+ *	When true, it sets $cachePrivacy to 0 (private), does not use the modification time of the script itself, and puts the date of the client’s cache (or a old date from 1980) in the global variable $clientCacheDate.
+ * @param bool $compression (default false) Enable the compression and allows persistent connections (automatic detection of the capacities of the client).
+ * @param bool $session (default false) To be turned on when sessions are used. Checks if the data contained in $_SESSION has been modified during the last generation the document.
+ * @return bool True if the connection can be closed (e.g.: the client has already the latest version), false if the new content has to be send to the client.
+ */
+function httpConditional(int $UnixTimeStamp, int $cacheSeconds = 0, int $cachePrivacy = 0, bool $feedMode = false, bool $compression = false, bool $session = false): bool {
 	if (headers_sent()) return false;
 	if (headers_sent()) return false;
 
 
-	if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName=$_SERVER['SCRIPT_FILENAME'];
-	elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName=$_SERVER['PATH_TRANSLATED'];
+	if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName = $_SERVER['SCRIPT_FILENAME'];
+	elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName = $_SERVER['PATH_TRANSLATED'];
 	else return false;
 	else return false;
 
 
-	if ((!$feedMode)&&(($modifScript=filemtime($scriptName))>$UnixTimeStamp))
-		$UnixTimeStamp=$modifScript;
-	$UnixTimeStamp=min($UnixTimeStamp,time());
-	$is304=true;
-	$is412=false;
-	$nbCond=0;
+	if ((!$feedMode) && (($modifScript = (int)filemtime($scriptName)) > $UnixTimeStamp))
+		$UnixTimeStamp = $modifScript;
+	$UnixTimeStamp = (int)min($UnixTimeStamp, time());
+	$is304 = true;
+	$is412 = false;
+	$nbCond = 0;
 
 
 	//rfc2616-sec3.html#sec3.3.1
 	//rfc2616-sec3.html#sec3.3.1
-	$dateLastModif=gmdate('D, d M Y H:i:s \G\M\T',$UnixTimeStamp);
-	$dateCacheClient='Thu, 10 Jan 1980 20:30:40 GMT';
+	$dateLastModif = gmdate('D, d M Y H:i:s \G\M\T', $UnixTimeStamp);
+	$dateCacheClient = 'Thu, 10 Jan 1980 20:30:40 GMT';
 
 
 	//rfc2616-sec14.html#sec14.19 //='"0123456789abcdef0123456789abcdef"'
 	//rfc2616-sec14.html#sec14.19 //='"0123456789abcdef0123456789abcdef"'
-	if (isset($_SERVER['QUERY_STRING'])) $myQuery='?'.$_SERVER['QUERY_STRING'];
-	else $myQuery='';
-	if ($session&&isset($_SESSION))
-	{
+	if (isset($_SERVER['QUERY_STRING'])) $myQuery = '?' . $_SERVER['QUERY_STRING'];
+	else $myQuery = '';
+	if ($session && isset($_SESSION)) {
 		global $_sessionMode;
 		global $_sessionMode;
-		$_sessionMode=$session;
-		$myQuery.=print_r($_SESSION,true).session_name().'='.session_id();
+		$_sessionMode = $session;
+		$myQuery .= print_r($_SESSION, true) . session_name() . '=' . session_id();
 	}
 	}
-	$etagServer='"'.md5($scriptName.$myQuery.'#'.$dateLastModif).'"';
+	$etagServer = '"' . md5($scriptName . $myQuery . '#' . $dateLastModif) . '"';
 
 
 	// @phpstan-ignore-next-line
 	// @phpstan-ignore-next-line
-	if ((!$is412)&&isset($_SERVER['HTTP_IF_MATCH']))
-	{//rfc2616-sec14.html#sec14.24
-		$etagsClient=stripslashes($_SERVER['HTTP_IF_MATCH']);
-		$etagsClient=str_ireplace('-gzip','',$etagsClient);
-		$is412=(($etagsClient!=='*')&&(strpos($etagsClient,$etagServer)===false));
+	if ((!$is412) && isset($_SERVER['HTTP_IF_MATCH'])) { //rfc2616-sec14.html#sec14.24
+		$etagsClient = stripslashes($_SERVER['HTTP_IF_MATCH']);
+		$etagsClient = str_ireplace('-gzip', '', $etagsClient);
+		$is412 = (($etagsClient !== '*') && (strpos($etagsClient, $etagServer) === false));
 	}
 	}
 	// @phpstan-ignore-next-line
 	// @phpstan-ignore-next-line
-	if ($is304&&isset($_SERVER['HTTP_IF_MODIFIED_SINCE']))
-	{//rfc2616-sec14.html#sec14.25 //rfc1945.txt
+	if ($is304 && isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) { //rfc2616-sec14.html#sec14.25 //rfc1945.txt
 		$nbCond++;
 		$nbCond++;
-		$dateCacheClient=$_SERVER['HTTP_IF_MODIFIED_SINCE'];
-		$p=strpos($dateCacheClient,';');
-		if ($p!==false)
-			$dateCacheClient=substr($dateCacheClient,0,$p);
-		$is304=($dateCacheClient==$dateLastModif);
+		$dateCacheClient = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
+		$p = strpos($dateCacheClient, ';');
+		if ($p !== false)
+			$dateCacheClient = substr($dateCacheClient, 0, $p);
+		$is304 = ($dateCacheClient == $dateLastModif);
 	}
 	}
-	if ($is304&&isset($_SERVER['HTTP_IF_NONE_MATCH']))
-	{//rfc2616-sec14.html#sec14.26
+	if ($is304 && isset($_SERVER['HTTP_IF_NONE_MATCH'])) { //rfc2616-sec14.html#sec14.26
 		$nbCond++;
 		$nbCond++;
-		$etagClient=stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
-		$etagClient=str_ireplace('-gzip','',$etagClient);
-		$is304=(($etagClient===$etagServer)||($etagClient==='*'));
+		$etagClient = stripslashes($_SERVER['HTTP_IF_NONE_MATCH']);
+		$etagClient = str_ireplace('-gzip', '', $etagClient);
+		$is304 = (($etagClient === $etagServer) || ($etagClient === '*'));
 	}
 	}
-	if ((!$is412)&&isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE']))
-	{//rfc2616-sec14.html#sec14.28
-		$dateCacheClient=$_SERVER['HTTP_IF_UNMODIFIED_SINCE'];
-		$p=strpos($dateCacheClient,';');
-		if ($p!==false)
-			$dateCacheClient=substr($dateCacheClient,0,$p);
-		$is412=($dateCacheClient!==$dateLastModif);
+	if ((!$is412) && isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) { //rfc2616-sec14.html#sec14.28
+		$dateCacheClient = $_SERVER['HTTP_IF_UNMODIFIED_SINCE'];
+		$p = strpos($dateCacheClient, ';');
+		if ($p !== false)
+			$dateCacheClient = substr($dateCacheClient, 0, $p);
+		$is412 = ($dateCacheClient !== $dateLastModif);
 	}
 	}
-	if ($feedMode)
-	{//Special RSS/ATOM
+	if ($feedMode) { //Special RSS/ATOM
 		global $clientCacheDate;
 		global $clientCacheDate;
-		$clientCacheDate=@strtotime($dateCacheClient);
-		$cachePrivacy=0;
+		$clientCacheDate = @strtotime($dateCacheClient);
+		$cachePrivacy = 0;
 	}
 	}
 
 
-	if ($is412)
-	{//rfc2616-sec10.html#sec10.4.13
+	if ($is412) { //rfc2616-sec10.html#sec10.4.13
 		header('HTTP/1.1 412 Precondition Failed');
 		header('HTTP/1.1 412 Precondition Failed');
 		header('Cache-Control: private, max-age=0, must-revalidate');
 		header('Cache-Control: private, max-age=0, must-revalidate');
 		header('Content-Type: text/plain');
 		header('Content-Type: text/plain');
 		echo "HTTP/1.1 Error 412 Precondition Failed: Precondition request failed positive evaluation\n";
 		echo "HTTP/1.1 Error 412 Precondition Failed: Precondition request failed positive evaluation\n";
 		return true;
 		return true;
-	}
-	elseif ($is304&&($nbCond>0))
-	{//rfc2616-sec10.html#sec10.3.5
+	} elseif ($is304 && ($nbCond > 0)) { //rfc2616-sec10.html#sec10.3.5
 		header('HTTP/1.0 304 Not Modified');
 		header('HTTP/1.0 304 Not Modified');
-		header('Etag: '.$etagServer);
+		header('Etag: ' . $etagServer);
 		if ($feedMode) header('Connection: close'); //Comment this line under IIS
 		if ($feedMode) header('Connection: close'); //Comment this line under IIS
 		return true;
 		return true;
-	}
-	else
-	{//rfc2616-sec10.html#sec10.2.1
+	} else { //rfc2616-sec10.html#sec10.2.1
 		//rfc2616-sec14.html#sec14.3
 		//rfc2616-sec14.html#sec14.3
 		if ($compression) ob_start('_httpConditionalCallBack'); //Will check HTTP_ACCEPT_ENCODING
 		if ($compression) ob_start('_httpConditionalCallBack'); //Will check HTTP_ACCEPT_ENCODING
 		//header('HTTP/1.0 200 OK');
 		//header('HTTP/1.0 200 OK');
-		if ($cacheSeconds<0)
-		{
-			$cache='private, no-cache, no-store, must-revalidate';
+		if ($cacheSeconds < 0) {
+			$cache = 'private, no-cache, no-store, must-revalidate';
 			//header('Expires: 0');
 			//header('Expires: 0');
 			header('Pragma: no-cache');
 			header('Pragma: no-cache');
-		}
-		else
-		{
-			if ($cacheSeconds===0)
-			{
-				$cache='private, must-revalidate, ';
+		} else {
+			if ($cacheSeconds === 0) {
+				$cache = 'private, must-revalidate, ';
 				//header('Expires: 0');
 				//header('Expires: 0');
-			}
-			elseif ($cachePrivacy===0) $cache='private, ';
-			elseif ($cachePrivacy===2) $cache='public, ';
-			else $cache='';
-			$cache.='max-age='.floor($cacheSeconds);
+			} elseif ($cachePrivacy === 0) $cache = 'private, ';
+			elseif ($cachePrivacy === 2) $cache = 'public, ';
+			else $cache = '';
+			$cache .= 'max-age=' . floor($cacheSeconds);
 		}
 		}
 		//header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T',time()+$cacheSeconds)); //HTTP/1.0 //rfc2616-sec14.html#sec14.21
 		//header('Expires: '.gmdate('D, d M Y H:i:s \G\M\T',time()+$cacheSeconds)); //HTTP/1.0 //rfc2616-sec14.html#sec14.21
-		header('Cache-Control: '.$cache); //rfc2616-sec14.html#sec14.9
-		header('Last-Modified: '.$dateLastModif);
-		header('Etag: '.$etagServer);
+		header('Cache-Control: ' . $cache); //rfc2616-sec14.html#sec14.9
+		header('Last-Modified: ' . $dateLastModif);
+		header('Etag: ' . $etagServer);
 		if ($feedMode) header('Connection: close'); //rfc2616-sec14.html#sec14.10 //Comment this line under IIS
 		if ($feedMode) header('Connection: close'); //rfc2616-sec14.html#sec14.10 //Comment this line under IIS
-		return $_SERVER['REQUEST_METHOD']==='HEAD'; //rfc2616-sec9.html#sec9.4
+		return $_SERVER['REQUEST_METHOD'] === 'HEAD'; //rfc2616-sec9.html#sec9.4
 	}
 	}
 }
 }
 
 
-function _httpConditionalCallBack($buffer,$mode=5)
-{//Private function automatically called at the end of the script when compression is enabled
-	//rfc2616-sec14.html#sec14.11
-	//You can adjust the level of compression with zlib.output_compression_level in php.ini
-	if (extension_loaded('zlib')&&(!ini_get('zlib.output_compression')))
-	{
-		$buffer2=ob_gzhandler($buffer,$mode); //Will check HTTP_ACCEPT_ENCODING and put correct headers such as Vary //rfc2616-sec14.html#sec14.44
-		if (strlen($buffer2)>1) //When ob_gzhandler succeeded
-			$buffer=$buffer2;
+/**
+ * Private function automatically called at the end of the script when compression is enabled.
+ * One can adjust the level of compression with zlib.output_compression_level in php.ini
+ * Reference rfc2616-sec14.html#sec14.11
+ */
+function _httpConditionalCallBack(string $buffer, int $mode = 5): string {
+	if (extension_loaded('zlib') && (!ini_get('zlib.output_compression'))) {
+		$buffer2 = ob_gzhandler($buffer, $mode) ?: ''; //Will check HTTP_ACCEPT_ENCODING and put correct headers such as Vary //rfc2616-sec14.html#sec14.44
+		if (strlen($buffer2) > 1) //When ob_gzhandler succeeded
+			$buffer = $buffer2;
 	}
 	}
-	header('Content-Length: '.strlen($buffer)); //Allows persistent connections //rfc2616-sec14.html#sec14.13
+	header('Content-Length: ' . strlen($buffer)); //Allows persistent connections //rfc2616-sec14.html#sec14.13
 	return $buffer;
 	return $buffer;
 }
 }
 
 
-function httpConditionalRefresh($UnixTimeStamp)
-{//Update HTTP headers if the content has just been modified by the client's request
-	//See an example on http://alexandre.alapetite.fr/doc-alex/compteur/
-	if (headers_sent()) return false;
+/**
+ * Update HTTP headers if the content has just been modified by the client’s request.
+ * See an example on https://alexandre.alapetite.fr/doc-alex/compteur/
+ */
+function httpConditionalRefresh(int $UnixTimeStamp): void {
+	if (headers_sent()) return;
 
 
-	if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName=$_SERVER['SCRIPT_FILENAME'];
-	elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName=$_SERVER['PATH_TRANSLATED'];
-	else return false;
+	if (isset($_SERVER['SCRIPT_FILENAME'])) $scriptName = $_SERVER['SCRIPT_FILENAME'];
+	elseif (isset($_SERVER['PATH_TRANSLATED'])) $scriptName = $_SERVER['PATH_TRANSLATED'];
+	else return;
 
 
-	$dateLastModif=gmdate('D, d M Y H:i:s \G\M\T',$UnixTimeStamp);
+	$dateLastModif = gmdate('D, d M Y H:i:s \G\M\T', $UnixTimeStamp);
 
 
-	if (isset($_SERVER['QUERY_STRING'])) $myQuery='?'.$_SERVER['QUERY_STRING'];
-	else $myQuery='';
+	if (isset($_SERVER['QUERY_STRING'])) $myQuery = '?' . $_SERVER['QUERY_STRING'];
+	else $myQuery = '';
 	global $_sessionMode;
 	global $_sessionMode;
-	if ($_sessionMode&&isset($_SESSION))
-		$myQuery.=print_r($_SESSION,true).session_name().'='.session_id();
-	$etagServer='"'.md5($scriptName.$myQuery.'#'.$dateLastModif).'"';
+	if ($_sessionMode && isset($_SESSION))
+		$myQuery .= print_r($_SESSION, true) . session_name() . '=' . session_id();
+	$etagServer = '"' . md5($scriptName . $myQuery . '#' . $dateLastModif) . '"';
 
 
-	header('Last-Modified: '.$dateLastModif);
-	header('Etag: '.$etagServer);
+	header('Last-Modified: ' . $dateLastModif);
+	header('Etag: ' . $etagServer);
 }
 }

+ 0 - 1
tests/phpstan-next.txt

@@ -17,7 +17,6 @@
 ./cli/i18n/I18nData.php
 ./cli/i18n/I18nData.php
 ./cli/i18n/I18nFile.php
 ./cli/i18n/I18nFile.php
 ./cli/i18n/I18nValue.php
 ./cli/i18n/I18nValue.php
-./lib/http-conditional.php
 ./lib/Minz/Dispatcher.php
 ./lib/Minz/Dispatcher.php
 ./lib/Minz/Migrator.php
 ./lib/Minz/Migrator.php
 ./lib/Minz/Paginator.php
 ./lib/Minz/Paginator.php