From a8ac90c4664695d19cd1ae9deedad62d963ccc0b Mon Sep 17 00:00:00 2001 From: onli Date: Tue, 26 Apr 2016 22:34:59 +0000 Subject: [PATCH] Init php 7 compatibility (#399) A first approach at fixing s9y for php 7, which makes it possible to write an entry without any error message. The specific changes are: 1. __construct for the plugin classes 2. Update Cache Lite to a modern version to fix its similar constructor problem 3. Remove the session_regenerate_id call from the session destructor (should get re-added to session creation where necessary) 4. Remove error handler to prevent silenced warnings from becoming fatal exceptions --- bundled-libs/Cache/Lite.php | 85 ++++++++++--- bundled-libs/Cache/Lite/File.php | 90 +++++++++++++ bundled-libs/Cache/Lite/Function.php | 5 +- bundled-libs/Cache/Lite/NestedOutput.php | 56 +++++++++ bundled-libs/Cache/Lite/Output.php | 6 +- include/compat.inc.php | 154 ----------------------- include/functions_config.inc.php | 1 - include/plugin_api.inc.php | 4 +- serendipity_config.inc.php | 26 +--- 9 files changed, 219 insertions(+), 208 deletions(-) create mode 100644 bundled-libs/Cache/Lite/File.php create mode 100644 bundled-libs/Cache/Lite/NestedOutput.php diff --git a/bundled-libs/Cache/Lite.php b/bundled-libs/Cache/Lite.php index c74eb243..0ca762d3 100644 --- a/bundled-libs/Cache/Lite.php +++ b/bundled-libs/Cache/Lite.php @@ -19,8 +19,8 @@ * * @package Cache_Lite * @category Caching -* @version $Id: Lite.php,v 1.54 2009/07/07 05:34:37 tacker Exp $ * @author Fabien MARTY +* @author Markus Tacker */ define('CACHE_LITE_ERROR_RETURN', 1); @@ -247,6 +247,12 @@ class Cache_Lite * @var boolean */ var $_errorHandlingAPIBreak = false; + + var $_hashedDirectoryGroup = NULL; + + var $_cacheFileMode = NULL; + + var $_cacheFileGroup = NULL; // --- Public methods --- @@ -272,7 +278,18 @@ class Cache_Lite * 'hashedDirectoryLevel' => level of the hashed directory system (int), * 'hashedDirectoryUmask' => umask for hashed directory structure (int), * 'errorHandlingAPIBreak' => API break for better error handling ? (boolean) + * 'hashedDirectoryGroup' => group of hashed directory structure (int | string) (see function chgrp) + * 'cacheFileMode' => filesystem mode of newly created cache files (int) + * 'cacheFileGroup' => group of newly created cache files (int | string) (see function chgrp) * ); + * + * If sys_get_temp_dir() is available and the + * 'cacheDir' option is not provided in the + * constructor options array its output is used + * to determine the suitable temporary directory. + * + * @see http://de.php.net/sys_get_temp_dir + * @see http://pear.php.net/bugs/bug.php?id=18328 * * @param array $options options * @access public @@ -282,6 +299,9 @@ class Cache_Lite foreach($options as $key => $value) { $this->setOption($key, $value); } + if (!isset($options['cacheDir']) && function_exists('sys_get_temp_dir')) { + $this->setOption('cacheDir', sys_get_temp_dir() . DIRECTORY_SEPARATOR); + } } /** @@ -295,7 +315,7 @@ class Cache_Lite */ function setOption($name, $value) { - $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode'); + $availableOptions = array('errorHandlingAPIBreak', 'hashedDirectoryUmask', 'hashedDirectoryLevel', 'automaticCleaningFactor', 'automaticSerialization', 'fileNameProtection', 'memoryCaching', 'onlyMemoryCaching', 'memoryCachingLimit', 'cacheDir', 'caching', 'lifeTime', 'fileLocking', 'writeControl', 'readControl', 'readControlType', 'pearErrorMode', 'hashedDirectoryGroup', 'cacheFileMode', 'cacheFileGroup'); if (in_array($name, $availableOptions)) { $property = '_'.$name; $this->$property = $value; @@ -329,7 +349,7 @@ class Cache_Lite } if ($this->_onlyMemoryCaching) { return false; - } + } } if (($doNotTestCacheValidity) || (is_null($this->_refreshTime))) { if (file_exists($this->_file)) { @@ -376,8 +396,8 @@ class Cache_Lite } } if ($this->_automaticCleaningFactor>0 && ($this->_automaticCleaningFactor==1 || mt_rand(1, $this->_automaticCleaningFactor)==1)) { - $this->clean(false, 'old'); - } + $this->clean(false, 'old'); + } if ($this->_writeControl) { $res = $this->_writeAndControl($data); if (is_bool($res)) { @@ -387,7 +407,7 @@ class Cache_Lite // if $res if false, we need to invalidate the cache @touch($this->_file, time() - 2*abs($this->_lifeTime)); return false; - } + } } else { $res = $this->_write($data); } @@ -534,7 +554,7 @@ class Cache_Lite */ function raiseError($msg, $code) { - include_once dirname(__FILE__) . '/../PEAR.php'; + include_once('PEAR.php'); return PEAR::raiseError($msg, $code, $this->_pearErrorMode); } @@ -599,7 +619,7 @@ class Cache_Lite $motif = ($group) ? 'cache_'.$group.'_' : 'cache_'; } if ($this->_memoryCaching) { - foreach($this->_memoryCachingArray as $key => $v) { + foreach($this->_memoryCachingArray as $key => $v) { if (strpos($key, $motif) !== false) { unset($this->_memoryCachingArray[$key]); $this->_memoryCachingCounter = $this->_memoryCachingCounter - 1; @@ -613,7 +633,7 @@ class Cache_Lite return $this->raiseError('Cache_Lite : Unable to open cache directory !', -4); } $result = true; - while ($file = readdir($dh)) { + while (($file = readdir($dh)) !== false) { if (($file != '.') && ($file != '..')) { if (substr($file, 0, 6)=='cache_') { $file2 = $dir . $file; @@ -654,7 +674,19 @@ class Cache_Lite } return $result; } - + + /** + * Touch the cache file while are recreating it to avoid + * launch this task more then once when necessary + * When the cache recreated and Added in Cache Memory + * @return void + * @access private + */ + function _touchCacheFile(){ + if (file_exists($this->_file)) { + @touch($this->_file); + } + } /** * Add some date in the memory caching array * @@ -663,6 +695,7 @@ class Cache_Lite */ function _memoryCacheAdd($data) { + $this->_touchCacheFile(); $this->_memoryCachingArray[$this->_file] = $data; if ($this->_memoryCachingCounter >= $this->_memoryCachingLimit) { list($key, ) = each($this->_memoryCachingArray); @@ -707,8 +740,8 @@ class Cache_Lite function _read() { $fp = @fopen($this->_file, "rb"); - if ($this->_fileLocking) @flock($fp, LOCK_SH); if ($fp) { + if ($this->_fileLocking) @flock($fp, LOCK_SH); clearstatcache(); $length = @filesize($this->_file); $mqr = get_magic_quotes_runtime(); @@ -718,9 +751,13 @@ class Cache_Lite if ($this->_readControl) { $hashControl = @fread($fp, 32); $length = $length - 32; - } + } + if ($length) { - $data = @fread($fp, $length); + $data = ''; + // See https://bugs.php.net/bug.php?id=30936 + // The 8192 magic number is the chunk size used internally by PHP. + while(!feof($fp)) $data .= fread($fp, 8192); } else { $data = ''; } @@ -760,13 +797,29 @@ class Cache_Lite for ($i=0 ; $i<$this->_hashedDirectoryLevel ; $i++) { $root = $root . 'cache_' . substr($hash, 0, $i + 1) . '/'; if (!(@is_dir($root))) { - @mkdir($root, $this->_hashedDirectoryUmask); + if (@mkdir($root)) + { + @chmod($root, $this->_hashedDirectoryUmask); + if (! is_null($this->_hashedDirectoryGroup)) + @chgrp($root, $this->_hashedDirectoryGroup); + } } } } + // if both _cacheFileMode and _cacheFileGroup is null, then we don't need to call + // file_exists (see below: if ($is_newfile) ...) + $is_newfile = (! is_null($this->_cacheFileMode) || !is_null($this->_cacheFileGroup)) + && ! @file_exists($this->_file); $fp = @fopen($this->_file, "wb"); if ($fp) { if ($this->_fileLocking) @flock($fp, LOCK_EX); + if ($is_newfile) + { + if (! is_null($this->_cacheFileMode)) + @chmod($this->_file, $this->_cacheFileMode); + if (! is_null($this->_cacheFileGroup)) + @chgrp($this->_file, $this->_cacheFileGroup); + } if ($this->_readControl) { @fwrite($fp, $this->_hash($data, $this->_readControlType), 32); } @@ -781,7 +834,7 @@ class Cache_Lite if ($this->_fileLocking) @flock($fp, LOCK_UN); @fclose($fp); return true; - } + } return $this->raiseError('Cache_Lite : Unable to write cache file : '.$this->_file, -1); } @@ -831,5 +884,3 @@ class Cache_Lite } } - -?> diff --git a/bundled-libs/Cache/Lite/File.php b/bundled-libs/Cache/Lite/File.php new file mode 100644 index 00000000..2cb2758b --- /dev/null +++ b/bundled-libs/Cache/Lite/File.php @@ -0,0 +1,90 @@ + +*/ + +require_once('Cache/Lite.php'); + +class Cache_Lite_File extends Cache_Lite +{ + + // --- Private properties --- + + /** + * Complete path of the file used for controlling the cache lifetime + * + * @var string $_masterFile + */ + var $_masterFile = ''; + + /** + * Masterfile mtime + * + * @var int $_masterFile_mtime + */ + var $_masterFile_mtime = 0; + + // --- Public methods ---- + + /** + * Constructor + * + * $options is an assoc. To have a look at availables options, + * see the constructor of the Cache_Lite class in 'Cache_Lite.php' + * + * Comparing to Cache_Lite constructor, there is another option : + * $options = array( + * (...) see Cache_Lite constructor + * 'masterFile' => complete path of the file used for controlling the cache lifetime(string) + * ); + * + * @param array $options options + * @access public + */ + function Cache_Lite_File($options = array(NULL)) + { + $options['lifetime'] = 0; + $this->Cache_Lite($options); + if (isset($options['masterFile'])) { + $this->_masterFile = $options['masterFile']; + } else { + return $this->raiseError('Cache_Lite_File : masterFile option must be set !'); + } + if (!($this->_masterFile_mtime = @filemtime($this->_masterFile))) { + return $this->raiseError('Cache_Lite_File : Unable to read masterFile : '.$this->_masterFile, -3); + } + } + + /** + * Test if a cache is available and (if yes) return it + * + * @param string $id cache id + * @param string $group name of the cache group + * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested + * @return string data of the cache (else : false) + * @access public + */ + function get($id, $group = 'default', $doNotTestCacheValidity = false) + { + if ($data = parent::get($id, $group, true)) { + if ($filemtime = $this->lastModified()) { + if ($filemtime > $this->_masterFile_mtime) { + return $data; + } + } + } + return false; + } + +} diff --git a/bundled-libs/Cache/Lite/Function.php b/bundled-libs/Cache/Lite/Function.php index 9e1ce28f..6c4861a4 100644 --- a/bundled-libs/Cache/Lite/Function.php +++ b/bundled-libs/Cache/Lite/Function.php @@ -11,12 +11,11 @@ * Technical choices are described in the 'docs/technical' file * * @package Cache_Lite -* @version $Id: Function.php,v 1.11 2006/12/14 12:59:43 cweiske Exp $ * @author Sebastian BERGMANN * @author Fabien MARTY */ -require_once dirname(__FILE__) . '/../Lite.php'; +require_once('Cache/Lite.php'); class Cache_Lite_Function extends Cache_Lite { @@ -207,5 +206,3 @@ class Cache_Lite_Function extends Cache_Lite } } - -?> diff --git a/bundled-libs/Cache/Lite/NestedOutput.php b/bundled-libs/Cache/Lite/NestedOutput.php new file mode 100644 index 00000000..81ece30d --- /dev/null +++ b/bundled-libs/Cache/Lite/NestedOutput.php @@ -0,0 +1,56 @@ + +*/ + +require_once('Cache/Lite/Output.php'); + +class Cache_Lite_NestedOutput extends Cache_Lite_Output +{ + private $nestedIds = array(); + private $nestedGroups = array(); + + /** + * Start the cache + * + * @param string $id cache id + * @param string $group name of the cache group + * @param boolean $doNotTestCacheValidity if set to true, the cache validity won't be tested + * @return boolean|string false if the cache is not hit else the data + * @access public + */ + function start($id, $group = 'default', $doNotTestCacheValidity = false) + { + $this->nestedIds[] = $id; + $this->nestedGroups[] = $group; + $data = $this->get($id, $group, $doNotTestCacheValidity); + if ($data !== false) { + return $data; + } + ob_start(); + ob_implicit_flush(false); + return false; + } + + /** + * Stop the cache + * + * @param boolen + * @return string return contents of cache + */ + function end() + { + $data = ob_get_contents(); + ob_end_clean(); + $id = array_pop($this->nestedIds); + $group = array_pop($this->nestedGroups); + $this->save($data, $id, $group); + return $data; + } + +} diff --git a/bundled-libs/Cache/Lite/Output.php b/bundled-libs/Cache/Lite/Output.php index 37cba664..87d7c19d 100644 --- a/bundled-libs/Cache/Lite/Output.php +++ b/bundled-libs/Cache/Lite/Output.php @@ -7,11 +7,10 @@ * Technical choices are described in the 'docs/technical' file * * @package Cache_Lite -* @version $Id: Output.php,v 1.4 2006/01/29 00:22:07 fab Exp $ * @author Fabien MARTY */ -require_once dirname(__FILE__) . '/../Lite.php'; +require_once('Cache/Lite.php'); class Cache_Lite_Output extends Cache_Lite { @@ -67,6 +66,3 @@ class Cache_Lite_Output extends Cache_Lite } } - - -?> diff --git a/include/compat.inc.php b/include/compat.inc.php index ea057267..8e3dfcbe 100644 --- a/include/compat.inc.php +++ b/include/compat.inc.php @@ -62,160 +62,6 @@ function memSnap($tshow = '') { return '[' . date('d.m.Y H:i') . '] ' . number_format($current - $memUsage, 2, ',', '.') . ' label "' . $tshow . '", totalling ' . number_format($current, 2, ',', '.') . '
' . "\n"; } - -/** - * Make readable error types for debugging error_reporting levels - * - * @access public - * @param int error value - * @return string constant error string - */ -function debug_ErrorLevelType($type) -{ - switch($type) - { - case E_ERROR: // 1 // - return 'E_ERROR'; - case E_WARNING: // 2 // - return 'E_WARNING'; - case E_PARSE: // 4 // - return 'E_PARSE'; - case E_NOTICE: // 8 // - return 'E_NOTICE'; - case E_CORE_ERROR: // 16 // - return 'E_CORE_ERROR'; - case E_CORE_WARNING: // 32 // - return 'E_CORE_WARNING'; - case E_COMPILE_ERROR: // 64 // - return 'E_COMPILE_ERROR'; - case E_COMPILE_WARNING: // 128 // - return 'E_COMPILE_WARNING'; - case E_USER_ERROR: // 256 // - return 'E_USER_ERROR'; - case E_USER_WARNING: // 512 // - return 'E_USER_WARNING'; - case E_USER_NOTICE: // 1024 // - return 'E_USER_NOTICE'; - case E_STRICT: // 2048 // - return 'E_STRICT'; - case E_RECOVERABLE_ERROR: // 4096 // - return 'E_RECOVERABLE_ERROR'; - case E_DEPRECATED: // 8192 // - return 'E_DEPRECATED'; - case E_USER_DEPRECATED: // 16384 // - return 'E_USER_DEPRECATED'; - } - return ""; -} - - -/** - * Set our own exeption handler to convert all errors into exeptions automatically - * function_exists() avoids 'cannot redeclare previously declared' fatal errors in XML feed context. - * - * See Notes about returning false - * - * @access public - * @param standard - * @return null - */ -if (!function_exists('errorToExceptionHandler')) { - function errorToExceptionHandler($errNo, $errStr, $errFile = '', $errLine = NULL, $errContext = array()) { - global $serendipity; - $exit = false; - switch ( $errNo ) { - case E_ERROR: - case E_USER_ERROR: - $type = 'Fatal Error'; - $exit = true; - break; - case E_USER_WARNING: - case E_WARNING: - $type = 'Warning'; - break; - case E_USER_NOTICE: - case E_NOTICE: - case @E_STRICT: - case @E_DEPRECATED: - case @E_USER_DEPRECATED: - $type = 'Notice'; - break; - case @E_RECOVERABLE_ERROR: - $type = 'Catchable'; - break; - default: - $type = 'Unknown Error'; - $exit = true; - break; - } - $rep = ini_get('error_reporting'); - $args = func_get_args(); - - // respect user has set php error_reporting to not display any errors at all - if (!($rep & $errStr)) { return false; } - // user used @ to specify ignoring all errors or $php_errormsg messages returned with error_reporting = 0 - if ($rep == 0) { return false; } - // if not using Serendipity testing and user or ISP has set PHPs display_errors to show no errors at all, respect - if ($serendipity['production'] === true && ini_get('display_errors') == 0) { return false; } - // Several plugins might not adapt to proper style. This should not completely kill our execution. - if ($serendipity['production'] !== 'debug' && preg_match('@Declaration.*should be compatible with@i', $args[1])) { - #if (!headers_sent()) echo "Compatibility warning: Please upgrade file old '{$args[2]}', it contains incompatible signatures.
Details: {$args[1]}
"; - return false; - } - // any other errors go here - throw errors as exception - if ($serendipity['production'] === 'debug') { - - // We don't want the notices - but everything else ! - echo " == FULL DEBUG ERROR MODE == \n"; - echo '
';
-            // trying to be as detailled as possible - but avoid using args containing sensibel data like passwords
-            if (function_exists('debug_backtrace') && version_compare(PHP_VERSION, '5.3.6') >= 0) {
-                if ( version_compare(PHP_VERSION, '5.4') >= 0 ) {
-                    $debugbacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 8);
-                } else {
-                    $debugbacktrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
-                }
-                print_r($debugbacktrace);
-            }
-            //print_r($args); // debugging [Use with care! Not to public, since holding password and credentials!!!]
-            // debugbacktrace is nice, but additional it is good to have the verbosity of SPL EXCEPTIONS, except for db connect errors
-        }
-        if ($serendipity['production'] === false) {
-            echo " == TESTING ERROR MODE == \n";
-        }
-        if ($serendipity['production'] !== true) {
-            if (!$serendipity['dbConn'] || $exit) {
-                echo '

' . $type.': '.$errStr . ' in ' . $errFile . ' on line ' . $errLine . '

'; - } else { - echo '
';
-                throw new \ErrorException($type.': '.$errStr, 0, $errNo, $errFile, $errLine); // tracepath = all, if not ini_set('display_errors', 0);
-                echo '
'; // if using throw new ... this ending tag will not be send and displayed, but it still looks better and browsers don't really care - } - if (!$serendipity['dbConn'] || $exit) exit; // make sure to exit in case of database connection errors. - } else { - if( $serendipity['serendipityUserlevel'] >= USERLEVEL_ADMIN ) { - // ToDo: enhance for more special serendipity error needs - $str = " == SERENDIPITY ERROR == "; - $str .= '

' . $errStr . ' in ' . $errFile . ' on line ' . $errLine . '

'; - #var_dump(headers_list()); - if (headers_sent()) { - serendipity_die($str); // case HTTP headers: needs to halt with die() here, else it will path through and gets written underneath blog content, or into streamed js files, which hardly isn't seen by many users - } else { - // see global include of function in plugin_api.inc.php - // this also reacts on non eye-displayed errors with following small javascript, - // while being in tags like