diff --git a/e107_handlers/Shims/Internal/ReadfileTrait.php b/e107_handlers/Shims/Internal/ReadfileTrait.php new file mode 100644 index 000000000..8d1858337 --- /dev/null +++ b/e107_handlers/Shims/Internal/ReadfileTrait.php @@ -0,0 +1,64 @@ + '%m/%d/%y', '%T' => '%H:%M:%S',); + static $map_r = array( + '%S' => 'tm_sec', + '%M' => 'tm_min', + '%H' => 'tm_hour', + '%I' => 'tm_hour', + '%d' => 'tm_mday', + '%m' => 'tm_mon', + '%Y' => 'tm_year', + '%y' => 'tm_year', + '%W' => 'tm_wday', + '%D' => 'tm_yday', + '%u' => 'unparsed', + ); + + $fullmonth = array(); + $abrevmonth = array(); + + for ($i = 1; $i <= 12; $i++) + { + $k = strftime('%B', mktime(0, 0, 0, $i)); + $fullmonth[$k] = $i; + + $j = strftime('%b', mktime(0, 0, 0, $i)); + $abrevmonth[$j] = $i; + } + + + #-- transform $format into extraction regex + $format = str_replace(array_keys($expand), array_values($expand), $format); + $preg = preg_replace('/(%\w)/', '(\w+)', preg_quote($format)); + + #-- record the positions of all STRFCMD-placeholders + preg_match_all('/(%\w)/', $format, $positions); + $positions = $positions[1]; + + $vals = array(); + + #-- get individual values + if (preg_match("#$preg#", $date, $extracted)) + { + #-- get values + foreach ($positions as $pos => $strfc) + { + $v = $extracted[$pos + 1]; + #-- add + if (isset($map_r[$strfc])) + { + $n = $map_r[$strfc]; + $vals[$n] = ($v > 0) ? (int)$v : $v; + } + else + { + if (!isset($vals['unparsed'])) $vals['unparsed'] = ''; + $vals['unparsed'] .= $v . ' '; + } + } + + #-- fixup some entries + //$vals["tm_wday"] = $names[ substr($vals["tm_wday"], 0, 3) ]; + if ($vals['tm_year'] >= 1900) + { + $vals['tm_year'] -= 1900; + } + elseif ($vals['tm_year'] > 0) + { + $vals['tm_year'] += 100; + } + + if ($vals['tm_mon']) + { + $vals['tm_mon'] -= 1; + } + + if (!isset($vals['tm_sec'])) + { + $vals['tm_sec'] = 0; + } + + if (!isset($vals['tm_min'])) + { + $vals['tm_min'] = 0; + } + + if (!isset($vals['tm_hour'])) + { + $vals['tm_hour'] = 0; + } + + + if (!isset($vals['unparsed'])) + { + $vals['unparsed'] = ''; + } + + $unxTimestamp = mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], ($vals['tm_mon'] + 1), $vals['tm_mday'], ($vals['tm_year'] + 1900)); + + $vals['tm_wday'] = (int)strftime('%w', $unxTimestamp); // Days since Sunday (0-6) + $vals['tm_yday'] = (strftime('%j', $unxTimestamp) - 1); // Days since January 1 (0-365) + } + + return !empty($vals) ? $vals : false; + + } +} \ No newline at end of file diff --git a/e107_handlers/Shims/InternalShims.php b/e107_handlers/Shims/InternalShims.php index 676b87255..207868a20 100644 --- a/e107_handlers/Shims/InternalShims.php +++ b/e107_handlers/Shims/InternalShims.php @@ -2,7 +2,7 @@ /** * e107 website system * - * Copyright (C) 2008-2018 e107 Inc (e107.org) + * Copyright (C) 2008-2020 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * diff --git a/e107_handlers/Shims/InternalShimsTrait.php b/e107_handlers/Shims/InternalShimsTrait.php index dfaaf6b15..9ae20ceb3 100644 --- a/e107_handlers/Shims/InternalShimsTrait.php +++ b/e107_handlers/Shims/InternalShimsTrait.php @@ -2,7 +2,7 @@ /** * e107 website system * - * Copyright (C) 2008-2018 e107 Inc (e107.org) + * Copyright (C) 2008-2020 e107 Inc (e107.org) * Released under the terms and conditions of the * GNU General Public License (http://www.gnu.org/licenses/gpl.txt) * @@ -13,51 +13,6 @@ namespace e107\Shims; trait InternalShimsTrait { - /** - * Outputs a file - * - * Resilient replacement for PHP internal readfile() - * - * @see https://github.com/e107inc/e107/issues/3528 Why this method was implemented - * @param string $filename The filename being read. - * @param bool $use_include_path You can use the optional second parameter and set it to TRUE, - * if you want to search for the file in the include_path, too. - * @param resource $context A context stream resource. - * @return int|bool Returns the number of bytes read from the file. - * If an error occurs, FALSE is returned. - */ - public static function readfile($filename, $use_include_path = FALSE, $context = NULL) - { - $output = @readfile($filename, $use_include_path, $context); - if ($output === NULL) - { - return self::readfile_alt($filename, $use_include_path, $context); - } - return $output; - } - - /** - * Outputs a file - * - * Alternative implementation using file streams - * - * @param $filename - * @param bool $use_include_path - * @param resource $context - * @return bool|int - */ - public static function readfile_alt($filename, $use_include_path = FALSE, $context = NULL) - { - // fopen() silently returns false if there is no context - if (!is_resource($context)) $context = stream_context_create(); - - $handle = @fopen($filename, 'rb', $use_include_path, $context); - if ($handle === FALSE) return FALSE; - while (!feof($handle)) - { - echo(fread($handle, 8192)); - } - fclose($handle); - return filesize($filename); - } + use Internal\ReadfileTrait; + use Internal\StrptimeTrait; } \ No newline at end of file diff --git a/e107_handlers/date_handler.php b/e107_handlers/date_handler.php index 0a6ef9b53..ea47fe9f5 100644 --- a/e107_handlers/date_handler.php +++ b/e107_handlers/date_handler.php @@ -770,184 +770,22 @@ class e_date /** - * This work of Lionel SAURON (http://sauron.lionel.free.fr:80) is licensed under the - * Creative Commons Attribution-Noncommercial-Share Alike 2.0 France License. - * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/fr/ - * or send a letter to Creative Commons, 171 Second Street, Suite 300, San Francisco, California, 94105, USA. - * - * http://snipplr.com/view/4964/emulate-php-5-for-backwards-compatibility/ - * - * Parse a date generated with strftime(). - * - * @author Lionel SAURON and reworked by e107 Inc. for month names. - * @version 1.0 - * @public + * Parse a time/date generated with strftime() + * With extra output keys for localized month * + * @deprecated Use eShims::strptime() instead + * @see eShims::strptime() * @param string $str date string to parse (e.g. returned from strftime()). * @param $format * @return array|bool Returns an array with the $str parsed, or false on error. */ public function strptime($str, $format) { - if(STRPTIME_COMPAT !== TRUE && function_exists('strptime')) // Unix Only. - { - $vals = strptime($str,$format); // PHP5 is more accurate than below. - $vals['tm_amon'] = strftime('%b', mktime(0,0,0, $vals['tm_mon'] +1) ); - $vals['tm_fmon'] = strftime('%B', mktime(0,0,0, $vals['tm_mon'] +1) ); - return $vals; - } - - // Below is for Windows machines. (XXX TODO - Currently not as accurate as Linux PHP5 strptime() function above. ) - - static $expand = array('%D'=>'%m/%d/%y', '%T'=>'%H:%M:%S', ); - - $ampm = (preg_match("/%l|%I|%p|%P/",$format)) ? 'true' : 'false'; - - static $map_r = array( - '%S' =>'tm_sec', - '%M' =>'tm_min', - '%H' =>'tm_hour', - '%I' =>'tm_hour', - '%d' =>'tm_mday', - '%m' =>'tm_mon', - '%Y' =>'tm_year', - '%y' =>'tm_year', - '%W' =>'tm_wday', - '%D' =>'tm_yday', - '%B' =>'tm_fmon', // full month-name - '%b' =>'tm_amon', // abrev. month-name - '%p' =>'tm_AMPM', // AM/PM - '%P' =>'tm_ampm', // am/pm - '%u' =>'unparsed', - - ); - - $fullmonth = array(); - $abrevmonth = array(); - - for ($i = 1; $i <= 12; $i++) - { - $k = strftime('%B',mktime(0,0,0,$i)); - $fullmonth[$k] = $i; - - $j = strftime('%b',mktime(0,0,0,$i)); - $abrevmonth[$j] = $i; - } - - - - #-- transform $format into extraction regex - $format = str_replace(array_keys($expand), array_values($expand), $format); - $preg = preg_replace('/(%\w)/', '(\w+)', preg_quote($format)); - - #-- record the positions of all STRFCMD-placeholders - preg_match_all('/(%\w)/', $format, $positions); - $positions = $positions[1]; - - $vals = array(); - - #-- get individual values - if (preg_match("#$preg#", $str, $extracted)) - { - #-- get values - foreach ($positions as $pos => $strfc) - { - $v = $extracted[$pos + 1]; - #-- add - if (isset($map_r[$strfc])) - { - $n = $map_r[$strfc]; - $vals[$n] = ($v > 0) ? (int) $v : $v; - } - else - { - $vals['unparsed'] .= $v.' '; - } - } - - #-- fixup some entries - //$vals["tm_wday"] = $names[ substr($vals["tm_wday"], 0, 3) ]; - if ($vals['tm_year'] >= 1900) - { - $vals['tm_year'] -= 1900; - } - elseif ($vals['tm_year'] > 0) - { - $vals['tm_year'] += 100; - } - - if ($vals['tm_mon']) - { - $vals['tm_mon'] -= 1; - } - else - { - - if(isset($fullmonth[$vals['tm_fmon']])) - { - $vals['tm_mon'] = $fullmonth[$vals['tm_fmon']]; - } - elseif(isset($abrevmonth[$vals['tm_amon']])) - { - $vals['tm_mon'] = $abrevmonth[$vals['tm_amon']]; - } - - } - - if($ampm && isset($vals['tm_hour'])) - { - if($vals['tm_hour'] == 12 && ($vals['tm_AMPM'] == 'AM' || $vals['tm_ampm'] == 'am')) - { - $vals['tm_hour'] = 0; - } - - if($vals['tm_hour'] < 12 && ($vals['tm_AMPM'] == 'PM' || $vals['tm_ampm'] == 'pm')) - { - $vals['tm_hour'] = intval($vals['tm_hour']) + 12; - } - - } - - //$vals['tm_sec'] -= 1; always increasing tm_sec + 1 ?????? - - #-- calculate wday/yday - //$vals['tm_mon'] = $vals['tm_mon'] + 1; // returns months from 0 - 11 so we need to +1 - - if (!isset($vals['tm_sec'])) - { - $vals['tm_sec'] = 0; - } - - if (!isset($vals['tm_min'])) - { - $vals['tm_min'] = 0; - } - - if (!isset($vals['tm_hour'])) - { - $vals['tm_hour'] = 0; - } - - - if (!isset($vals['unparsed'])) - { - $vals['unparsed'] = ''; - } - - $unxTimestamp = mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], ($vals['tm_mon'] + 1), $vals['tm_mday'], ($vals['tm_year'] + 1900)); - - $vals['tm_amon'] = strftime('%b', mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], $vals['tm_mon'] + 1)); - $vals['tm_fmon'] = strftime('%B', mktime($vals['tm_hour'], $vals['tm_min'], $vals['tm_sec'], $vals['tm_mon'] + 1)); - $vals['tm_wday'] = (int) strftime('%w', $unxTimestamp); // Days since Sunday (0-6) - $vals['tm_yday'] = (strftime('%j', $unxTimestamp) - 1); // Days since January 1 (0-365) - - - //var_dump($vals, $str, strftime($format, $unxTimestamp), $unxTimestamp); - } - - return !empty($vals) ? $vals : false; - - } + $vals = eShims::strptime($str, $format); // PHP5 is more accurate than below. + $vals['tm_amon'] = strftime('%b', mktime(0, 0, 0, $vals['tm_mon'] + 1)); + $vals['tm_fmon'] = strftime('%B', mktime(0, 0, 0, $vals['tm_mon'] + 1)); + return $vals; + } diff --git a/e107_handlers/php_compatibility_handler.php b/e107_handlers/php_compatibility_handler.php index e26abfe77..21eac532e 100644 --- a/e107_handlers/php_compatibility_handler.php +++ b/e107_handlers/php_compatibility_handler.php @@ -26,17 +26,12 @@ if (!defined('e107_INIT')) */ - - if (!function_exists('strptime')) { - - define('STRPTIME_COMPAT', true); - function strptime($str, $format) + function strptime($date, $format) { - return e107::getDate()->strptime($str,$format); - } - + return eShims::strptime($date, $format); + } } //PHP < 5.2 compatibility diff --git a/e107_tests/tests/unit/e107/Shims/StrptimeTest.php b/e107_tests/tests/unit/e107/Shims/StrptimeTest.php new file mode 100644 index 000000000..0623d6922 --- /dev/null +++ b/e107_tests/tests/unit/e107/Shims/StrptimeTest.php @@ -0,0 +1,94 @@ +testStrptimeImplementation([\e107\Shims\InternalShims::class, 'strptime']); + } + + public function testStrptimeDefaultLegacy() + { + $this->testStrptimeImplementation([\eShims::class, 'strptime']); + } + + public function testStrptimeAlt() + { + $this->testStrptimeImplementation([\e107\Shims\InternalShims::class, 'strptime_alt']); + } + + protected function testStrptimeImplementation($implementation) + { + $this->testStrptimeDateOnly($implementation); + $this->testStrptimeDateTime($implementation); + $this->testStrptimeUnparsed($implementation); + $this->testStrptimeInvalid($implementation); + } + + protected function testStrptimeDateOnly($implementation) + { + $actual = call_user_func($implementation, '2018/05/13', '%Y/%m/%d'); + $expected = array( + 'tm_year' => 118, + 'tm_mon' => 4, + 'tm_mday' => 13, + 'tm_sec' => 0, + 'tm_min' => 0, + 'tm_hour' => 0, + 'unparsed' => '', + 'tm_wday' => 0, + 'tm_yday' => 132, + ); + $this->assertEquals($expected, $actual); + } + + protected function testStrptimeDateTime($implementation) + { + $actual = call_user_func($implementation, '2018/05/13 20:10', '%Y/%m/%d %H:%M'); + $expected = array( + 'tm_year' => 118, + 'tm_mon' => 4, + 'tm_mday' => 13, + 'tm_hour' => 20, + 'tm_min' => 10, + 'tm_sec' => 0, + 'unparsed' => '', + 'tm_wday' => 0, + 'tm_yday' => 132, + ); + $this->assertEquals($expected, $actual); + } + + protected function testStrptimeUnparsed($implementation) + { + $actual = call_user_func($implementation, '1607-09-04 08:10 PM', '%Y-%m-%d %l:%M %P'); + $expected = array( + 'tm_year' => 1707, + 'tm_mon' => 8, + 'tm_mday' => 4, + 'tm_hour' => 0, + 'tm_min' => 10, + 'tm_sec' => 0, + 'unparsed' => '08 PM ', + 'tm_wday' => 2, + 'tm_yday' => 246, + ); + $this->assertEquals($expected, $actual); + } + + protected function testStrptimeInvalid($implementation) + { + $actual = call_user_func($implementation, 'garbage', '%Y-%m-%d'); + $this->assertFalse($actual); + } +} \ No newline at end of file