From d92c5a94a660bdf5a8db785638de1bbdcab7402f Mon Sep 17 00:00:00 2001 From: ralf Date: Sun, 24 Apr 2022 18:15:47 +0200 Subject: [PATCH] fix PHP 8.1 Deprecated by adding return type declarations --- api/src/Db/CallbackIterator.php | 18 +- api/src/Mail/Account.php | 8 +- api/src/Storage/Customfields.php | 2 +- calendar/inc/class.calendar_boupdate.inc.php | 4 +- calendar/inc/class.calendar_rrule.inc.php | 31 ++- ...-8.1-depricated-interfaces-return-vals.php | 215 ++++++++++++++++++ 6 files changed, 252 insertions(+), 26 deletions(-) mode change 100755 => 100644 api/src/Storage/Customfields.php create mode 100755 doc/fix-8.1-depricated-interfaces-return-vals.php diff --git a/api/src/Db/CallbackIterator.php b/api/src/Db/CallbackIterator.php index e86d169a09..21fbe97389 100644 --- a/api/src/Db/CallbackIterator.php +++ b/api/src/Db/CallbackIterator.php @@ -118,9 +118,9 @@ class CallbackIterator implements \Iterator /** * Return the current element * - * @return array + * @return mixed */ - public function current() + public function current(): mixed { if (is_a($this->rs,'iterator')) { @@ -136,7 +136,7 @@ class CallbackIterator implements \Iterator * * @return int */ - public function key() + public function key(): int { if (is_a($this->rs,'iterator')) { @@ -150,22 +150,22 @@ class CallbackIterator implements \Iterator /** * Move forward to next element (called after each foreach loop) */ - public function next() + public function next(): void { if (is_a($this->rs,'iterator')) { - return $this->rs->next(); + $this->rs->next(); } } /** * Rewind the Iterator to the first element (called at beginning of foreach loop) */ - public function rewind() + public function rewind(): void { if (is_a($this->rs,'iterator')) { - return $this->rs->rewind(); + $this->rs->rewind(); } } @@ -174,7 +174,7 @@ class CallbackIterator implements \Iterator * * @return boolean */ - public function valid () + public function valid (): bool { if (is_a($this->rs,'iterator')) { @@ -182,4 +182,4 @@ class CallbackIterator implements \Iterator } return false; } -} +} \ No newline at end of file diff --git a/api/src/Mail/Account.php b/api/src/Mail/Account.php index 65add695b2..6cb8815024 100644 --- a/api/src/Mail/Account.php +++ b/api/src/Mail/Account.php @@ -908,7 +908,7 @@ class Account implements \ArrayAccess * @param string $offset * @return mixed */ - public function offsetGet($offset) + public function offsetGet($offset): mixed { return $this->__get($offset); } @@ -919,7 +919,7 @@ class Account implements \ArrayAccess * @param string $offset * @return boolean */ - public function offsetExists($offset) + public function offsetExists($offset): bool { return $this->__isset($offset); } @@ -933,7 +933,7 @@ class Account implements \ArrayAccess * @param mixed $value * @throws Api\Exception\WrongParameter */ - public function offsetSet($offset, $value) + public function offsetSet($offset, $value): void { throw new Api\Exception\WrongParameter(__METHOD__."($offset, $value) No write access through ArrayAccess interface of Account!"); } @@ -946,7 +946,7 @@ class Account implements \ArrayAccess * @param string $offset * @throws Api\Exception\WrongParameter */ - public function offsetUnset($offset) + public function offsetUnset($offset): void { throw new Api\Exception\WrongParameter(__METHOD__."($offset) No write access through ArrayAccess interface of Account!"); } diff --git a/api/src/Storage/Customfields.php b/api/src/Storage/Customfields.php old mode 100755 new mode 100644 index fafa460512..29f2e2437c --- a/api/src/Storage/Customfields.php +++ b/api/src/Storage/Customfields.php @@ -94,7 +94,7 @@ class Customfields implements \IteratorAggregate * * @return Api\Db\CallbackIterator */ - function getIterator() + function getIterator(): Api\Db\CallbackIterator { return new Api\Db\CallbackIterator($this->iterator, function($_row) { diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index 52a7fcd690..b8302a077a 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -1401,7 +1401,7 @@ class calendar_boupdate extends calendar_bo $rrule->next_no_exception(); $occurrence = $rrule->current(); } - while ($rrule->valid($event['whole_day']) && ($enddate = $occurrence)); + while ($rrule->validDate($event['whole_day']) && ($enddate = $occurrence)); $enddate->modify(($event['end'] - $event['start']).' second'); $event['recur_enddate'] = $save_event['recur_enddate'] = $enddate->format('ts'); //error_log(__METHOD__."($event[title]) start=".Api\DateTime::to($event['start'],'string').', end='.Api\DateTime::to($event['end'],'string').', range_end='.Api\DateTime::to($event['recur_enddate'],'string')); @@ -3180,4 +3180,4 @@ class calendar_boupdate extends calendar_bo return $status_reset; } -} +} \ No newline at end of file diff --git a/calendar/inc/class.calendar_rrule.inc.php b/calendar/inc/class.calendar_rrule.inc.php index d14055a015..e28f6ace1e 100644 --- a/calendar/inc/class.calendar_rrule.inc.php +++ b/calendar/inc/class.calendar_rrule.inc.php @@ -385,9 +385,9 @@ class calendar_rrule implements Iterator /** * Return the current element * - * @return DateTime + * @return Api\DateTime */ - public function current() + public function current(): Api\DateTime { return clone $this->current; } @@ -397,7 +397,7 @@ class calendar_rrule implements Iterator * * @return int */ - public function key() + public function key(): int { return (int)$this->current->format('Ymd'); } @@ -476,9 +476,9 @@ class calendar_rrule implements Iterator } /** - * Move forward to next recurence, taking into account exceptions + * Move forward to next recurrence, taking into account exceptions */ - public function next() + public function next(): void { do { @@ -555,7 +555,7 @@ class calendar_rrule implements Iterator /** * Rewind the Iterator to the first element (called at beginning of foreach loop) */ - public function rewind() + public function rewind(): void { $this->current = clone $this->time; while ($this->valid() && @@ -569,20 +569,30 @@ class calendar_rrule implements Iterator /** * Checks if current position is valid * - * @param boolean $use_just_date =false default use also time + * @param boolean? $use_just_date true: use just date, false|null: use also time * @return boolean */ - public function valid($use_just_date=false) + public function validDate(bool $use_just_date=null): bool { if ($use_just_date) { return $this->current->format('Ymd') <= $this->enddate_ymd; } + return $this->valid(); + } + + /** + * Checks if current position is valid + * + * @return boolean + */ + public function valid(): bool + { return $this->current->format('ts') < $this->enddate_ts; } /** - * Return string represenation of RRule + * Return string representation of RRule * * @return string */ @@ -592,7 +602,7 @@ class calendar_rrule implements Iterator // Repeated Events if($this->type != self::NONE) { - $str = lang(self::$types[$this->type]); + $str = (string)lang(self::$types[$this->type]); $str_extra = array(); switch ($this->type) @@ -1186,6 +1196,7 @@ if (isset($_SERVER['SCRIPT_FILENAME']) && $_SERVER['SCRIPT_FILENAME'] == __FILE_ $rrule = new calendar_rrule($time,$_REQUEST['type'],$_REQUEST['interval'],$enddate,$weekdays,$exceptions); echo "

".$time->format('l').', '.$time.' ('.$tz->getName().') '.$rrule."

\n"; + $n = 0; foreach($rrule as $rtime) { $rtime->setTimezone(Api\DateTime::$user_timezone); diff --git a/doc/fix-8.1-depricated-interfaces-return-vals.php b/doc/fix-8.1-depricated-interfaces-return-vals.php new file mode 100755 index 0000000000..5458ad6a01 --- /dev/null +++ b/doc/fix-8.1-depricated-interfaces-return-vals.php @@ -0,0 +1,215 @@ +#!/usr/bin/php -qC +fix_depricated.php must NOT be called as web-page --> exiting !!!'); +} + +/** + * Interfaces which methods require return types to avoid deprecated warnings + * + * As 'mixed' return type is only available since PHP 8.0, we need to add '#[\ReturnTypeWillChange]' annotation instead, + * for EGroupware versions not requiring PHP 8.0+ / 21.1. + */ +$interfaces = [ + 'Iterator' => [ + 'current' => 'mixed', + 'key' => 'mixed', + 'next' => 'void', + 'rewind' => 'void', + 'valid' => 'bool', + ], + 'IteratorAggregate' => [ + 'getIterator' => 'Traversable', + ], + 'ArrayAccess' => [ + 'offsetExists' => 'bool', + 'offsetGet' => 'mixed', + 'offsetSet' => 'void', + 'offsetUnset' => 'void', + ], + 'Serializable' => [ + 'serialize' => '?string', + 'unserialize' => 'void', + ], + 'JsonSerializable' => [ + 'jsonSerialize' => 'mixed', + ], +]; + +/** + * Fix deprecated stuff in a given file + * + * @param string $file filename + * @param boolean $replace_file =false replace existing file if modifications are necessary, otherwise .php53 file is created + * @return boolean false on error + */ +function fix_depricated($file,$replace_file=false) +{ + global $interfaces; + $orig = $lines = file_get_contents($file); + if ($lines === false) return false; + global $prog; + if (basename($file) == $prog) return true; // dont fix ourself ;-) + + // match "variables" like: $var, $obj->attr, $arr['key'] + $variable = '\$[a-z_0-9\[\]\'>-]+'; + + foreach($interfaces as $interface => $methods) + { + // class Account implements \ArrayAccess + if (!preg_match('/class\\s+([a-z_0-9]+)\s+implements\s+(\\\\?[a-z_0-9]+\s*,\s*)*\\\\?'.preg_quote($interface, '/').'/i', $lines, $matches)) + { + //error_log("$file does NOT implement $interface: ".json_encode($matches)); + continue; + } + error_log("$file DOES implement $interface: ".json_encode($matches)); + //$phpdoc = "(\s*\\/\\*\\*.*\\*\\/\n)?"; + $phpdoc = ''; + $lines = preg_replace_callback("/^(\\s*((static|public|private|protected)\\s+)*function\\s+([a-z_0-9]+)\\s*)\\(\\s*([^)]*)\\s*\\)(\\s*:\\s*([a-z]+))?/msi", + static function(array $matches) use ($methods, $lines) + { + global $mixed_annotation; + if (isset($matches[6]) || !isset($methods[$matches[4]])) + { + //error_log($matches[0].' already fixed --> nothing to do'); + return $matches[0]; + } + //error_log(json_encode($matches)." --> need fixing"); + if ($methods[$matches[4]] !== 'mixed') + { + return $matches[0].': '.$methods[$matches[4]]; + } + //$phpdoc = "\s*\\/\\*\\*.*?(@return\s+([a-z]+)).*?\\*\\/\n"; + $phpdoc = "\s+\\*\s+@return\s+([a-z]+).*\n\s+\\*\\/\n"; + if (preg_match('/^'.$phpdoc.preg_quote($matches[0], '/').'/mi', $lines, $phpdoc_matches) && + $phpdoc_matches[1] !== 'mixed') + { + switch($type = $phpdoc_matches[1]) + { + case 'boolean': $type = 'bool'; break; + case 'integer': $type = 'int'; break; + case 'double': $type = 'float'; break; + } + return $matches[0].': '.$type; + } + if (!$mixed_annotation) + { + return $matches[0].': '.$methods[$matches[4]]; + } + return "\t#[\\ReturnTypeWillChange]\n".$matches[0]; + }, $lines); + } + + if ($lines !== $orig) + { + file_put_contents($file.'.new',$lines); + $ret = null; + system('/usr/bin/php -l '.$file.'.new',$ret); + system('/usr/bin/diff -u '.$file.' '.$file.'.new'); + if (!$ret && $replace_file) + { + unlink($file); + rename($file.'.new',$file); + } + return !$ret; + } + return true; +} + +/** + * Loop recursive through directory and call fix_depricated for each php file + * + * @param string $dir + * @param boolean $replace_file =false replace existing file if modifications are necessary, otherwise .php53 file is created + * @return boolean false on error + */ +function fix_depricated_recursive($dir,$replace_file=false) +{ + if (!is_dir($dir)) return false; + + foreach(scandir($dir) as $file) + { + if ($file == '.' || $file == '..') continue; + + if (is_dir($dir.'/'.$file)) + { + fix_depricated_recursive($dir.'/'.$file,$replace_file); + } + elseif(substr($file,-4) == '.php') + { + echo "\r".str_repeat(' ',100)."\r".$dir.'/'.$file.': '; + fix_depricated($dir.'/'.$file,$replace_file); + } + } + echo "\r".str_repeat(' ',100)."\r"; + return true; +} + +/** + * Give usage + * + * @param string $error =null + */ +function usage($error=null) +{ + global $prog; + echo "Usage: $prog [--replace] [--mixed-annotation] [-h|--help] file or dir\n\n"; + if ($error) echo $error."\n\n"; + exit($error ? 1 : 0); +} + +$args = $_SERVER['argv']; +$prog = basename(array_shift($args)); + +if (!$args) usage(); + +$replace_file = false; +$mixed_annotation = false; +while(($arg = array_shift($args))) +{ + switch($arg) + { + case '-h': + case '--help': + usage(); + break; + + case '--replace': + $replace_file = true; + break; + + case '--mixed-annotation': + $mixed_annotation = true; + break; + + default: + if ($args) // not last argument + { + usage("Unknown argument '$arg'!"); + } + break 2; + } +} + +if (!file_exists($arg)) usage("Error: $arg not found!"); + +if (!is_dir($arg)) +{ + fix_depricated($arg,$replace_file); +} +else +{ + fix_depricated_recursive($arg,$replace_file); +} \ No newline at end of file