1
0
mirror of https://github.com/e107inc/e107.git synced 2025-08-03 13:17:24 +02:00

Merge pull request #4077 from Deltik/fix-4076

Have e_date::strptime() use eShims::strptime()
This commit is contained in:
Cameron
2020-01-22 12:06:42 -08:00
committed by GitHub
7 changed files with 337 additions and 228 deletions

View File

@@ -0,0 +1,64 @@
<?php
/**
* e107 website system
*
* 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)
*
* Shims for PHP internal functions
* readfile()
*/
namespace e107\Shims\Internal;
trait ReadfileTrait
{
/**
* 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);
}
}

View File

@@ -0,0 +1,163 @@
<?php
/**
* e107 website system
*
* 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)
*
* Shims for PHP internal functions
* strptime()
*/
namespace e107\Shims\Internal;
trait StrptimeTrait
{
/**
* Parse a time/date generated with strftime()
*
* Resilient replacement for PHP internal strptime()
*
* @see https://www.php.net/manual/en/function.strptime.php what this function approximates
* @param string $date The string to parse (e.g. returned from strftime()).
* @param string $format The format used in date (e.g. the same as used in strftime()).
* @return array|bool Returns FALSE on failure.
* The following parameters are returned in the array:
* "tm_sec" Seconds after the minute (0-61)
* "tm_min" Minutes after the hour (0-59)
* "tm_hour" Hour since midnight (0-23)
* "tm_mday" Day of the month (1-31)
* "tm_mon" Months since January (0-11)
* "tm_year" Years since 1900
* "tm_wday" Days since Sunday (0-6)
* "tm_yday" Days since January 1 (0-365)
* "unparsed" the date part which was not recognized using the specified format
*/
public static function strptime($date, $format)
{
$result = false;
if (function_exists('strptime'))
$result = strptime($date, $format);
if (!is_array($result))
$result = self::strptime_alt($date, $format);
return $result;
}
/**
* Parse a time/date generated with strftime()
*
* Alternative implementation based on public domain library:
* https://github.com/Polycademy/upgradephp/
*
* @param string $date
* @param string $format
* @return array|bool
*/
public static function strptime_alt($date, $format)
{
static $expand = array('%D' => '%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;
}
}

View File

@@ -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)
*

View File

@@ -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;
}

View File

@@ -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 <code>$str</code> parsed, or <code>false</code> 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;
}

View File

@@ -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

View File

@@ -0,0 +1,94 @@
<?php
/**
* e107 website system
*
* 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)
*
*/
namespace e107\Shims;
class StrptimeTest extends \Codeception\Test\Unit
{
public function testStrptimeDefault()
{
$this->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);
}
}