php error checking mysql date - php

caveat: i am new to php
I'm reading in 2 dates from a form in a mysql format (i.e. YYYY-MM-DD) and trying to error check them both for validity and that one is less than the other
if(!empty($day1) && !empty($day2)){
$sndate=array();
$sndate = explode('-',$day1);
$sndtnum = implode($sndate);
$sndtnum = (int) $sndtnum;
$unsndate=array();
$unsndate = explode('-',$day2);
$unsndtnum = implode($unsndate);
$unsndtnum = (int) $unsndtnum;
if (!checkdate($sndate[1],$sndate[2],sndate[0]) || !checkdate($unsndate[1],$unsndate[2],unsndate[0]) || $unsndtnum<$sndtnum)
{ $error=True; $errtext .="Date field is filled in wrong \n";
}else {$error=False;}
}
This does not seem to work. I'm pretty sure it's because of the checkdate, but i'm not positive that there isn't also an issue with the implode/cast.
Any ideas on how to fix this?

Apart from the sndate[0] and unsndate[0] variables missing their dollar signs, your check will actually not work if an evil user put non-numeric chars in $day1 (e.g. 2015-09abc-01 will be considered as less than 2015-05-01).
The following approach validates the dates by transforming them from a known format into a DateTime object and then back again, making sure that the two formatted dates are equal:
$f = 'Y-m-d';
$ok1 = ($d1 = DateTime::createFromFormat($f, $day1)) && $d1->format($f) == $day1;
$ok2 = ($d2 = DateTime::createFromFormat($f, $day2)) && $d2->format($f) == $day2;
if ($ok1 && $ok2 && $d1 <= $d2) {
// Ok
} else {
// Error
}
Hope this helps :)

Related

PHP Auto Aging Task for MyBB

So, I was wondering if anybody would mind checking over this task and correcting it? I'm very sure I've muddled Python in with what little PHP I know, and that there are open tags.
Basically, there'll be a field where the nasty decimal age goes ($age, which will later be replaced by the appropriate field id). Our site works in months for juveniles and then years and seasons for adults. Using the nasty age, I'm trying to calculate the rounded age values and then store them as a string value which will then be set as the value of the field that will display the age ($displayagefid, will be replaced later with the appropriate field id). Only certain usergroups will be updated (the list is huge, so I left it out).
I also have no idea how to set a variable as a string using both string and the value of another variable.
Please know that I'm a complete newbie to PHP.
This is intended to run as a task on a self-hosted MyBB forum.
Thank you in advance!
<?php
function task_age($task)
{
global $mybb, $db;
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
if ($year < 1) {
$display_age = $months, "mnths"
}
elseif ( ! filter_var($year, FILTER_VALIDATE_INT) ){
$display_age = $year, "yrs"
}
else {
$display age = $display_age = $years, "yrs", $seasons, "s"
};
$query = $db->query("
UPDATE mybb_userfields userfields
for ($usergroup = a || b || d || all that other crap) {
SET $dispalyagefid = $display_age;
};
");
add_task_log($task, "The age characters task successfully ran.");
I had a cursory look over your code and the first thing which sticks out is you have some of your variable assignments back to front:
$increment = 0.04167
$age = $age + $increment
floor($age*4) = $seasons
floor($age) = $years
floor($age*12) = $months
Whatever is on the left gets set to whatever is on the right, so your first two are OK but the last three need switching around.
Having said that it seems to me you are not approaching this correctly. I enter my decimal age into your site but how are you going to work out seasons? It might be my birthday tomorrow, it might have been my birthday yesterday.
You would be better off having the user enter a date of birth, from that calculate their age.
$birthday=date_create("2013-03-15");
$today=date_create('now');
$diff=date_diff($birthday,$today);
Now in the $diff variable you can check all the elements of a PHP date. So first check if they are under 18:
if ($diff->format("%y") < 18) {
$ageInMonths = ($diff->format("%y") * 12) + $diff->format("%m");
$age = "$ageInMonths months";
}
If they are over 18 you want age in years, then calculate seasons from the remaining months.
else {
$ageInYears = $diff->format("%y");
$ageInSeasons = floor($diff->format("%m") / 4);
if ($ageInSeasons > 0) {
$age = "$ageInYears years and $ageInSeasons seasons";
} else {
$age = "$ageInYears years";
}
}

Security of a simple numerical comparison

Ok I'm feeling like I'm going back not forward, can't even figure out by myself if a simple if statement is secure or not...
First of all let's say we get a variable from url GET method :
$my_number = $_GET['numb'];
Now we make this simple if statement:
if(($my_number >= 1) && ($my_number <= 12))
{
put $my_number in database without escaping it
}
So the question would be - Can user pass this if condition with something else besides 1-12, I mean using hex numbers, commenting, doing that kind of stuff?
To validate a number use intval()
$my_number = intval($_GET['numb']);
Nothing but a number will be allowed.
This will also insure the value will not create an error in the SQL.
I do not like >= or <=
if(($my_number >= 1) && ($my_number <= 12))
Change to:
if(($my_number > 0) && ($my_number < 13))
Your code is not fully secured. User can pass this if condition with something else besides 1-12.
You can test that with this simple code:
<?php
$my_number = $_GET['numb'];
if(is_numeric($my_number)){
if(($my_number >= 1) && ($my_number <= 12))
{
echo'User Can Pass';
}else{
echo'User Can Not Pass';
}
}else{
echo'User Can Not Pass';
}
?>
Now browse your site like http://example.com/?numb=8 or http://example.com/?numb=15 or http://example.com/?numb=7 Samurai
I think now you can find your answer. Thanks.

php date_parse("Feb 2010") gives day == 1

There is what I would call a bug in date_parse when there is no day. $d = date_parse("Feb 2010") will give $d["day"] == 1.
See the comment on this on the date_parse manual page.
Any nice workaround for this problem? :-)
UPDATE
The date comes from published research reports. Unfortunately this means that they could look in different ways. I want to convert them to more standard ISO format when displaying the references. To help the readers I want always to include just the given fields (years, month, date). So this should be valid (and just give me the year):
2010
This should be valid, but just give me 2010-02 so to say:
Feb 2010
UPDATE 2
So far I have seen two bugs here in date_parse. It can't parse 2010. And it gives a day though there is no day in Feb 2010.
I can of course write a fix for this, but surely someone has already done this, or???
The above bugfix routine is great, Leo, thanks. Unfortunately it still trips over January, thinking that 2014-01 is the same as 2014-01-01 --- we're eleven-twelfths of the way there.
The date formats that PHP can parse, that don't contain a day-of-month, appear to be (in php_src:date/lib/parse_date.re):
gnudateshorter = year4 "-" month;
datenoday = monthtext ([ .\t-])* year4;
datenodayrev = year4 ([ .\t-])* monthtext;
Very few, conveniently. We can run the same regexes on $dateRaw, essentially reverse-engineering what the parser had decided.
(Side observations: the above excludes formats like 5/2016, which is parsed as "20 May with some extra characters at the end"; they are also similar to day-of-year and week-of-year formats, so we'll try not to trip over those.)
function date_parse_bugfix($dateRaw) {
$dateRaw = trim($dateRaw);
// Check for just-the-year:
if (strlen($dateRaw) === 4 && preg_match("/\d{4}/", $dateRaw) === 1) {
$da = date_parse($dateRaw . "-01-01");
$da["month"] = false;
$da["day"] = false;
}
else {
$da = date_parse($dateRaw);
if ($da) {
// If we have a suspicious "day 1", check for the three formats above:
if ($da["day"] === 1) {
// Hat tip to http://regex101.com
// We're not actually matching to monthtext (which is looooong),
// just looking for alphabetic characters
if ((preg_match("/^\d{4}\-(0?[0-9]|1[0-2])$/", $dateRaw) === 1) ||
(preg_match("/^[a-zA-Z]+[ .\t-]*\d{4}$/", $dateRaw) === 1) ||
(preg_match("/^\d{4}[ .\t-]*[a-zA-Z]+$/", $dateRaw) === 1)) {
$da["day"] = false;
}
}
}
}
return $da;
}
No answers so I answer my own question. Here is a workaround the problems I saw.
// Work around for some bugs in date_parse (tested in PHP 5.5.19)
// http://php.net/manual/en/function.date-parse.php
//
// Date formats that are cannot be parsed correctly withoug this fix:
// 1) "2014" - Valid ISO 8061 date format but not recognized by date_parse.
// 2) "Feb 2010" - Parsed but gives ["day"] => 1.
function date_parse_5_5_bugfix($dateRaw) {
// Check "2014" bug:
$dateRaw = rtrim($dateRaw);
$dateRaw = ltrim($dateRaw);
if (strlen($dateRaw) === 4 && preg_match("/\d{4}/", $dateRaw) === 1) {
$da = date_parse($dateRaw . "-01-01");
$da["month"] = false;
$da["day"] = false;
} else {
$da = date_parse($dateRaw);
if ($da) {
if (array_key_exists("year", $da)
&& array_key_exists("month", $da)
&& array_key_exists("day", $da))
{
if ($da["day"] === 1) {
// Check "Feb 2010" bug:
// http://www.phpliveregex.com/
if (preg_match("/\b0?1(?:\b|T)/", $dateRaw) !== 1) {
$da["day"] = false;
}
}
}
}
}
return $da;
}
Some tests (visual ;-) )
$a = date_parse_5_5_bugfix("2014"); print_r($a);
$b = date_parse_5_5_bugfix("feb 2010"); print_r($b);
$c = date_parse_5_5_bugfix("2014-01-01"); print_r($c);
$d = date_parse_5_5_bugfix("2014-11-01T06:43:08Z"); print_r($d);
$e = date_parse_5_5_bugfix("2014-11-01x06:43:08Z"); print_r($e);
Can you try:
$dateTime = strtotime('February, 2010');
echo date('Y-m', $dateTime);

Shorthand to set var = whichever is greater of 2 variables

I have 2 different possible values I use in an equation. I want to select whichever one exists and is greater using the least amount of code. It's possible neither variable exists, in which case year = 0, but one or both might exist. I.e:
if(isset($this->average['year'] || isset($this->Listings['year']) {
$year = whichever is greater of the above.
} else {
$year = 0;
}
It seems like there must be a shorter/ less messy way to do this than:
if (isset($this->average['year']) && ($this->average['year'] > $this->Listings['year']) {
$year = $this->average['year'];
} elseif( isset($this->Listings['year'])) {
$year = $this->Listings['year'];
} else {
$year = 0;
}
Thanks
Using max and the ternary operator to do isset checks on both variables you can shorten it to this:
$year = max(array(
isset($this->average['year']) ? $this->average['year'] : 0,
isset($this->Listings['year']) ? $this->Listings['year'] : 0
));

PHP checkdate variants

I found a php function checkdate() , but strangely enough it only seems to accept data in format of int $month , int $day , int $year. However I am passing the date as a string (example "2012-06-13") so I came up with this workaround, because I would only allow date entered in such format. Unfortunately I am feeling this is both insecure and not a nice approach to the problem:
function CheckAdditional($value)
{
$data = explode("-", $value);
return checkdate($data[1], $data[2], $data[0]);
}
Question: is there a better way to check whether the date is valid?
You can try:
function checkDateFormat($date){
//match the format of the date
if (preg_match ("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date, $parts)) {
//check weather the date is valid of not
if(checkdate($parts[2],$parts[3],$parts[1]))
return true;
else
return false;
}
else
return false;}
Credits: http://roshanbh.com.np/2008/05/date-format-validation-php.html
Just in order to be safe you can do
date("Y-m-d", strtotime($yourdatestr));
In that way even if the format may be wrong it will in most cases correct it.
<?php
function CheckAdditional($value)
{
return date('Y-m-d', strtotime($value)) == $value;
}
?>
After several tests from both me and the people who tried to help me with my answer, I came up with this which suits me perfectly fine and is both easy and really reliable solution in my opinion as none was able to prove it wrong so far.
$jahr = (int) $_POST['jahr'];
$monat = (int) $_POST['monat'];
$tag = (int) $_POST['tag'];
$datum = "$tag. $monat. $jahr";
if (checkdate($monat, $tag, $jahr) == FALSE) {
$allesok = false;
$fehlermeldung .= "<p class='fehler'>Ungültiges Datum $datum!</p>";
}
If you limit the user inputs to be valid only in one format (what about localization?), then you just can parse the input by yourself, using a regexp-function or splitting the input by "-" and check whether it turns into an array with three digit values…
function DDC($dates){ // Date Day Control
$dy = substr($dates,0,4);
$dm = substr($dates,5,2);
$dd = substr($dates,8,2);
for($i=0; $i<3; $i++){
if(!checkdate($dm,$dd,$dy)){
$dd--;
}else{$i=3;}
}
return $dy.'.'.$dm.'.'.$dd;
}
echo DDC('2013.02.31');
//2013.02.28

Categories