Merge pull request #61 from getformwork/feature/stricter-date-parsing

Make date parsing stricter deprecating the use of invalid formats
This commit is contained in:
Giuseppe Criscione 2020-12-06 16:45:21 +01:00 committed by GitHub
commit bbf0446d38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 80 additions and 16 deletions

View File

@ -2,10 +2,11 @@
namespace Formwork\Admin\Fields;
use Formwork\Core\Formwork;
use Formwork\Data\Collection;
use Formwork\Data\DataGetter;
use DateTime;
use Formwork\Utils\Date;
use Formwork\Utils\Str;
use InvalidArgumentException;
class Validator
{
@ -112,14 +113,11 @@ class Validator
return null;
}
$format = Formwork::instance()->option('date.format');
$date = date_create_from_format($format, $value) ?: date_create($value);
if ($date instanceof DateTime) {
return date_format($date, 'Y-m-d');
try {
return date('Y-m-d', Date::toTimestamp($value, null, true));
} catch (InvalidArgumentException $e) {
throw new ValidationException('Invalid value for field "' . $field->name() . '" of type "' . $field->type() . '":' . Str::after($e->getMessage(), ':'));
}
throw new ValidationException('Invalid date format for field "' . $field->name() . '" of type "' . $field->type() . '"');
}
/**

View File

@ -4,6 +4,7 @@ namespace Formwork\Admin;
use Formwork\Admin\Utils\IPAnonymizer;
use Formwork\Admin\Utils\Registry;
use Formwork\Utils\Date;
use Formwork\Utils\FileSystem;
use Formwork\Utils\HTTPRequest;
use Formwork\Utils\Visitor;
@ -151,7 +152,7 @@ class Statistics
$uniqueVisits = array_slice($uniqueVisits, -$limit, null, true);
$label = static function (string $day): string {
$time = strtotime($day);
$time = Date::toTimestamp($day, self::DATE_FORMAT);
$month = Admin::instance()->label('date.months.short')[date('n', $time) - 1];
$weekday = Admin::instance()->label('date.weekdays.short')[date('N', $time) % 7];
$day = date('j', $time);

View File

@ -5,6 +5,7 @@ namespace Formwork\Admin;
use Formwork\Admin\Utils\Registry;
use Formwork\Core\Formwork;
use Formwork\Parsers\JSON;
use Formwork\Utils\Date;
use Formwork\Utils\FileSystem;
use Formwork\Utils\Str;
use RuntimeException;
@ -253,7 +254,7 @@ class Updater
$this->release = [
'name' => $data['name'],
'tag' => $data['tag_name'],
'date' => strtotime($data['published_at']),
'date' => Date::toTimestamp($data['published_at'], DATE_ISO8601),
'archive' => $data['zipball_url']
];

View File

@ -10,6 +10,7 @@ use Formwork\Parsers\YAML;
use Formwork\Router\RouteParams;
use Formwork\Router\Router;
use Formwork\Traits\SingletonTrait;
use Formwork\Utils\Date;
use Formwork\Utils\FileSystem;
use Formwork\Utils\Header;
use Formwork\Utils\HTTPRequest;
@ -336,8 +337,8 @@ final class Formwork
return $this->site->errorPage();
}
if ($this->option('cache.enabled') && ($page->has('publish-date') || $page->has('unpublish-date'))) {
if (($page->published() && !$this->site->modifiedSince((int) strtotime($page->get('publish-date'))))
|| (!$page->published() && !$this->site->modifiedSince((int) strtotime($page->get('unpublish-date'))))) {
if (($page->published() && !$this->site->modifiedSince(Date::toTimestamp($page->get('publish-date'))))
|| (!$page->published() && !$this->site->modifiedSince(Date::toTimestamp($page->get('unpublish-date'))))) {
// Clear cache if the site was not modified since the page has been published or unpublished
$this->cache->clear();
FileSystem::touch($this->option('content.path'));

View File

@ -7,6 +7,7 @@ use Formwork\Metadata\Metadata;
use Formwork\Parsers\Markdown;
use Formwork\Parsers\YAML;
use Formwork\Template\Template;
use Formwork\Utils\Date;
use Formwork\Utils\FileSystem;
use Formwork\Utils\Header;
use Formwork\Utils\Str;
@ -288,7 +289,7 @@ class Page extends AbstractPage
$format = Formwork::instance()->option('date.format');
}
if ($this->has('publish-date')) {
return date($format, strtotime($this->data['publish-date']));
return date($format, Date::toTimestamp($this->data['publish-date']));
}
return parent::date($format);
}
@ -572,11 +573,11 @@ class Page extends AbstractPage
$this->published = $this->data['published'];
if ($this->has('publish-date')) {
$this->published = $this->published && strtotime($this->get('publish-date')) < time();
$this->published = $this->published && Date::toTimestamp($this->get('publish-date')) < time();
}
if ($this->has('unpublish-date')) {
$this->published = $this->published && strtotime($this->get('unpublish-date')) > time();
$this->published = $this->published && Date::toTimestamp($this->get('unpublish-date')) > time();
}
$this->routable = $this->data['routable'];

62
formwork/Utils/Date.php Normal file
View File

@ -0,0 +1,62 @@
<?php
namespace Formwork\Utils;
use Formwork\Core\Formwork;
use DateTime;
use Exception;
use InvalidArgumentException;
class Date
{
/**
* Parse a date according to a given format (or the default format if not given) and return the timestamp
*
* @param $throwOnError Whether to throw an Exception when the method fails or trigger a deprecation error (@internal)
*/
public static function toTimestamp(string $date, string $format = null, bool $throwOnError = false): int
{
$isFormatGiven = $format !== null;
if (!$isFormatGiven) {
$format = Formwork::instance()->option('date.format');
}
$dateTime = DateTime::createFromFormat($format, $date);
if ($dateTime === false) {
if ($isFormatGiven) {
throw new InvalidArgumentException('Date "' . $date . '" is not formatted according to the format "' . $format . '": ' . static::getLastDateTimeError());
}
try {
$dateTime = new DateTime($date);
} catch (Exception $e) {
$message = 'Invalid date "' . $date . '": ' . static::getLastDateTimeError();
// TODO: this will always be the case in 2.0
if ($throwOnError) {
throw new InvalidArgumentException($message, $e->getCode(), $e->getPrevious());
}
trigger_error('Using invalid dates is deprecated since Formwork 1.11.0. ' . $message, E_USER_DEPRECATED);
}
}
return $dateTime instanceof DateTime ? $dateTime->getTimestamp() : strtotime($date);
}
/**
* Return a human-readable string containing details about last DateTime error
*/
protected static function getLastDateTimeError(): string
{
$result = [];
$lastError = null;
if (($errors = DateTime::getLastErrors()) !== false) {
foreach ($errors['errors'] as $position => $error) {
$currentError = lcfirst(rtrim($error, '.'));
$result[] = ($currentError !== $lastError ? $currentError . ' at position ' : '') . $position;
$lastError = $currentError;
}
}
return implode(', ', $result);
}
}