1
0
mirror of https://github.com/e107inc/e107.git synced 2025-01-17 20:58:30 +01:00

Replaced e_date::strptime() with eShims::strptime()

- NEW: Added \e107\Shims\Internal\StrptimeTrait, which implements PHP internal function
       strptime(). On not-Windows, the built-in function is called. If that function fails
       or if the operating system is Windows, the alternative pure PHP implementation is
       attempted. The first successful call is returned, or false if none are successful.
- MOD: Deprecated e_date::strptime() in favor of eShims::strptime()
- FIX: License misatributed for e_date::strptime() (now eShims::strptime()). The library
       used was public domain, not CC BY-NC-SA 2.0 FR by Lionel Sauron.
- MOD: Removed STRPTIME_COMPAT constant now that eShims::strptime() exists
- MOD: Removed support for calling e_date::strptime() with:
       - a localized full month name
       - a localized abbreviated month name
       - AM or PM
       - am or pm
       because these features were only implemented in Windows mode (STRPTIME_COMPAT).
- MOD: php_compatibility_handler.php now defines global strptime() using the
       eShims::strptime() implementation
- NEW: Test all(?) the possibilities of eShims::strptime()
This commit is contained in:
Nick Liu 2020-01-22 09:07:00 +01:00
parent f93ab61372
commit f23aec7395
No known key found for this signature in database
GPG Key ID: 1167C5F9C9897637
5 changed files with 270 additions and 179 deletions

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

@ -14,4 +14,5 @@ namespace e107\Shims;
trait InternalShimsTrait
{
use Internal\ReadfileTrait;
use Internal\StrptimeTrait;
}

View File

@ -770,183 +770,21 @@ 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);
}
}