We currently have a system, where we have a function that checks for date/time conflicts, that we manually populate all possible date/time conflicts as parameters to.
ie:
//session conflicts checker
function chkConflicts($sessions) {
if (!is_array($sessions)) {
$arrsessions = explode(',', $sessions);
} else {
$arrsessions = $sessions;
}
$conflictcount = 0;
foreach ($arrsessions as $thissession) {
if (($_POST[trim($thissession)] != '' && $_POST[trim($thissession)] != 0) || $_POST[trim($thissession) . '_faculty'] != '' && $_POST[trim($thissession) . '_faculty'] != 0) {
$conflictcount++;
}
}
if ($conflictcount > 1) {
return false;
} else {
return true;
}
}
used like so:
if (!chkConflicts('hours_5_p02_800, hours_5_p03_800, hours_5_p07_800, hours_5_p04_800')) {
$errmsg .= 'There is a conflict at 8:00am in the selections. ';
}
its very tedious, and time consuming to find these as well as manually populate the functions/params..
I need a new approach! I'm hoping to just get a list of the conflicts back and highlight the (table) row on the page with a message about you have conflicts in the highlighted areas (less specific then giving an exact time..etc, and still gets the job done for the user)
All the user interaction is done by checkboxes, that have a name that reflects the column in the table:
ie:
hours_5_p02_800, hours_5_p03_800, hours_5_p07_800,
hours_5_p04_800,hours_9_reg_session_300_845
(same names used in the chkConflicts function above)
I have (among others) two columns: sessiondate varchar(255) & presentationtime varchar(255) respectfully.
With the session date data looking like: 9/9/2015
And the presentation time data looking like: 8:45 AM - 9:05 AM (not sure if this matters, but including it for the sake of full disclosure)
I dont have ALOT of control over the database, but I could probably get the times split into two columns (start/end) if that would be best?
before ANY chkConflict function is called.. the 'selections' of the user are recorded/saved to the table.. AND THEN the conflict check is called.
//record hours for each day
function recordHours() {
$arrflds = explode(',', $_POST['fieldlist']);
$sql = "UPDATE {$this->eventcode}_cme SET";
foreach($arrflds as $key) {
$sql .= " " . addslashes(trim($key)) . " = '" . addslashes($_POST[trim($key)]) . "',";
}
$sql .= " lastupdated = '" . date('Y-m-d H:i:s') . "' WHERE id = '" . $_SESSION[$this->eventcode . '_id'] . "'";
$this->debugout .= ($this->debug) ? 'Record hours: ' . $sql . '<br>' . $this->crlf : '';
$result = mysql_query($sql) or exit('Error recording hours: ' . mysql_error());
}
*I'm updating things to PDO after I get the conflict stuff figured out. (thanks)
I dont mind this, because the chkConflict function (even though the choices have been saved) does NOT let the user move ahead until the error(s) message is taken care of (hence updating the table again when the conflicts are resolved)..
I'm thinking I'll need to no longer use the the chkConflict method and alter the recordHours function to not only update the table.. but because it has the 'fieldlist' array that was posted.. that I'll need to do the conflict checking there as well... or possibly call another function from withing recordHours and pass along the same fieldlist...
The column data is not really used for saving or (current) conflict checking of any sort... the column NAME is.
My problem is I'm not sure how to go do the date/time conflict check?
re-cap: fieldlist and column names are named like:
ie: hours_5_p02_800, hours_5_p03_800, hours_5_p07_800,hours_5_p04_800,hours_9_reg_session_300_845
(and is a 24 hour format for the time)
ex: hours_9_reg_session_300_845
9 = date
reg = event code
300 = session code
845 = session time(24-hour format)
Upon thinking more, its more like I need to do some sort of string parsing in PHP (on the fieldlist names) and do conversion/checking on that?
I need to take the string, break it down into its parts and do some sort of (concatenate/string building) comparison on it?
basically I get list of the fields being submitted that are formatted as above and match the table column names...
how can I pass this same fieldlist over to a new function (or whatever) to get any conflicts back?
Since you seem have the chance to change things a little I would suggest you to create the 2 fields but as timestamps field type. One for the start and one for the end.
You can look at timestamps fields as a date/time like 2015-10-17 08:45:00 or, using UNIX_TIMESTAMP('2015-10-17 08:45:00'), as an integer like 1445064300 which is exactly the same date/time info.
In both cases you can do things like
SELECT :yourdatetime BETWEEN date_time_start AND date_time_end;
or
SELECT :yourunixlikedatetime
NOT BETWEEN UNIX_TIMESTAMP(date_time_start) AND UNIX_TIMESTAMP(date_time_end);
for instance...
Related
I must be missing something regarding how simultaneous requests are handled by PHP/Symfony, or perhaps how potentially simultaneous queries on the DB are handled...
This code seems to be doing the impossible - it randomly (about once a month) creates a duplicate of the new entity at the bottom. I conclude that it must happen when two clients make the same request twice, and both threads execute the SELECT query at the same time, picking up the entry where stop == NULL, and then they both (?) set the stoptime for that entry, and they both write a new entry.
Here's my logical outline as far as I understand things:
Get all entries with NULL for stoptime
Loop over those entries
Only proceed if the day of the entry (UTC) is different from current day (UTC)
Set stoptime for the open entry to 23:59:59 and flush to DB
Build a new entry with the starttime 00:00:00 on the next day
Assert that there are no other open entries in that position
Assert that there are no future entries in that position
Only then - flush the new entry to DB
Controller autocloseAndOpen
//if entry spans daybreak (midnight) close it and open a new entry at the beginning of next day
private function autocloseAndOpen($units) {
$now = new \DateTime("now", new \DateTimeZone("UTC"));
$repository = $this->em->getRepository('App\Entity\Poslog\Entry');
$query = $repository->createQueryBuilder('e')
->where('e.stop is NULL')
->getQuery();
$results = $query->getResult();
if (!isset($results[0])) {
return null; //there are no open entries at all
}
$em = $this->em;
$messages = "";
foreach ($results as $r) {
if ($r->getPosition()->getACRGroup() == $unit) { //only touch the user's own entries
$start = $r->getStart();
//Assert entry spanning datebreak
$startStr = $start->format("Y-m-d"); //Necessary for comparison, if $start->format("Y-m-d") is put in the comparison clause PHP will still compare the datetime object being formatted, not the output of the formatting.
$nowStr = $now->format("Y-m-d"); //Necessary for comparison, if $start->format("Y-m-d") is put in the comparison clause PHP will still compare the datetime object being formatted, not the output of the formatting.
if ($startStr < $nowStr) {
$stop = new \DateTimeImmutable($start->format("Y-m-d")."23:59:59", new \DateTimeZone("UTC"));
$r->setStop($stop);
$em->flush();
$txt = $unit->getName() . " had an entry in position (" . $r->getPosition()->getName() . ") spanning datebreak (UTC). Automatically closed at " . $stop->format("Y-m-d H:i:s") . "z.";
$messages .= "<p>" . $txt . "</p>";
//Open new entry
$newStartTime = $stop->modify('+1 second');
$entry = new Entry();
$entry->setStart( $newStartTime );
$entry->setOperator( $r->getOperator() );
$entry->setPosition( $r->getPosition() );
$entry->setStudent( $r->getStudent() );
$em->persist($entry);
//Assert that there are no future entries before autoopening a new entry
$futureE = $this->checkFutureEntries($r->getPosition(),true);
$openE = $this->checkOpenEntries($r->getPosition(), true);
if ($futureE !== 0 || $openE !== 0) {
$txt = "Tried to open a new entry for " . $r->getOperator()->getSignature() . " in the same position (" . $r->getPosition()->getName() . ") next day but there are conflicting entries.";
$messages .= "<p>" . $txt . "</p>";
} else {
$em->flush(); //store to DB
$txt = "A new entry was opened for " . $r->getOperator()->getSignature() . " in the same position (" . $r->getPosition()->getName() . ")";
$messages .= "<p>" . $txt . "</p>";
}
}
}
}
return $messages;
}
I'm even running an extra check here with the checkOpenEntries() to see whether there is at this point any entries with stoptime == NULL in that position. Initially, I thought that to be superflous because I thought that if one request is running and operating on the DB, the other request will not start until the first is finished.
private function checkOpenEntries($position,$checkRelatives = false) {
$positionsToCheck = array();
if ($checkRelatives == true) {
$positionsToCheck = $position->getRelatedPositions();
$positionsToCheck[] = $position;
} else {
$positionsToCheck = array($position);
}
//Get all open entries for position
$repository = $this->em->getRepository('App\Entity\Poslog\Entry');
$query = $repository->createQueryBuilder('e')
->where('e.stop is NULL and e.position IN (:positions)')
->setParameter('positions', $positionsToCheck)
->getQuery();
$results = $query->getResult();
if(!isset($results[0])) {
return 0; //tells caller that there are no open entries
} else {
if (count($results) === 1) {
return $results[0]; //if exactly one open entry, return that object to caller
} else {
$body = 'Found more than 1 open log entry for position ' . $position->getName() . ' in ' . $position->getACRGroup()->getName() . ' this should not be possible, there appears to be corrupt data in the database.';
$this->email($body);
$output['success'] = false;
$output['message'] = $body . ' An automatic email has been sent to ' . $this->globalParameters->get('poslog-email-to') . ' to notify of the problem, manual inspection is required.';
$output['logdata'] = null;
return $this->prepareResponse($output);
}
}
}
Do I need to start this function with some kind of "Lock database" method to achieve what I am trying to do?
I have tested all functions and when I simulate all kinds of states (entry with NULL for stoptime even when it shouldn't be able to be etc.) it all works out. And the majority of time it all works nicely, but that one day somewhere in the middle of the month, this thing happens...
You can never guarantee sequential order (or implicit exclusive access). Try that and you will dig yourself deeper and deeper.
As Matt and KIKO mentioned in the comments, you could use constraints and transactions and those should help immensely as your database will stay clean, but keep in mind your app needs to be able to catch errors produced by the database layer. Definitely worth trying first.
Another way of handling this is to enforce database/application level locking.
Database-level locking is more coarse and very unforgiving if you somewhere forget to release a lock (in long-running scripts).
MySQL docs:
If the connection for a client session terminates, whether normally or abnormally, the server implicitly releases all table locks held by the session (transactional and nontransactional). If the client reconnects, the locks are no longer in effect.
Locking the entire table is usually a bad idea altogether, but it is doable. This highly depends on the application.
Some ORMs, out-of-box, support object versioning and throw exceptions if the version has changed during the execution. In theory, your application would get an exception and upon retrying it would find that someone else already populated the field and that is no longer a candidate for an update.
Application-level locking is more fine-grained, but all points in the code need to honor the lock, otherwise, you are back to square #1. And if your app is distributed (say K8S, or just deployed across multiple servers), your locking mechanism has to be distributed as well (not local to an instance)
I am making a meeting room booking system in which there should be no times within the start and end dates so in theory the validation should check for no dates/times within one start and end date time frame.
I have two tables, I can insert into it fine with both start and end dates so the only columns i am interested in at the moment are these
meetingrooms
|------------------------------------||- bookingtime -|-bookingend-|
I understand the principle behind the sanity check and the check i can do in psudocode. Here is the code i have got so far -
>
p4a_db::singleton()->query("INSERT INTO meetingrooms(location_id, bookingtime, bookingend, merono_id)
WHERE bookingtime < " . $date . " AND bookingend > " . $date . "
OR
bookingdate < " . $date . " AND bookingend > " . $dateend . "
VALUES(?,?,?,?)",
array($location, $date, $dateend, $merono));
I don't want to insert data directly into the statement but until i understand how to do this i am stuck, so the question,
How do i perform a sanity check before the data is inserted so that i don't get dates within booked times.
any help would be greatly appreciated.
Edit:
I've been overthinking my answer and I realized that the old solution will not work in your case since you need the time span, comparing the start and end date is useless.
My way of processing this would be:
Save the dates as int, use 24h system (7:40am is 740, 9:50pm is 2150)
Check for stored dates where: (Start<NewStart<End)
Check for stored dates where: (Start<NewEnd<End)
When processing several rooms, just store room number + time as int. That way you can still use the method from 2 and 3.
2 and 3 can be done in a sql query, check out this link.
Old answer (checking for duplicates)
This is an example of how to check for duplicates (in this case email) before inserting the text:
$emailexist = $mysqli->prepare("select email from users where email = ?");
$emailexist->bind_param('s', $email);
$emailexist->execute();
$emailexist->store_result();
if ($emailexist->num_rows > 0) {
$emailexist->close();
$mysqli->close();
return true;
}
else {
$emailexist->close();
$mysqli->close();
return false;
}
It checks if there are rows which contain the string. If so (if number of rows higher than 0) it returns true (which means, the date already exists).
You can just adapt this to you code.
However, you could also just set the columns to UNIQUE. Then you get an error when trying to insert it. It is easier and you won't have problems with concurrent connections.
after a long and intensive search, I have now got a working example of this method, along with a method of protecting against sql injection, here's the code;
if ($this->BookingValue == 1)
{
$sql = "SELECT COUNT(*) as num FROM meeting_room_bookings
WHERE
(
(? < start_at AND ? > start_at)
OR
(? > start_at AND ? < end_at)
)
AND
meeting_room_id = ?";
$result = p4a_db::singleton()->fetchRow($sql, array($date, $date, $date, $dateend, $merono));
if ( 0 == $result["num"] )
{
p4a_db::singleton()->query("INSERT INTO meeting_room_bookings (start_at, end_at, meeting_room_id)
VALUES
(?,?,?)", array($date, $dateend, $merono));
return true;
}
else
{
return false;
There isn't much to explain about this code, but in term of differences, (excluding the change in column names with the table) the query is now prepared before the value is set, then it is possible to use it in an if statement, thus allowing the validation to take place to filter results between different dates.
along with this i have added validation to stop dates from other meeting rooms being included within the statement via the AND statement where the meeting room id is limeted to a single value.
Although now, which will lead on to a separate question is another thrown error that comes from this statement, i know the insert is sound but something from this prepared statement causes the error:
SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens
File: Pdo.php, Line: 234
Although now i am looking into a error that is thrown from the prepared statement and will update this answer when there is a fix, thanks for the help.
I have php application which gets information from a SAML POST and creates a record in the MySQL database, if the record is already present it just updates it
Here is the code
//getMemberRecord returns true for successful insertion.
$row = $this->getMemberRecord($data);
if ($row) {
//if the row already exists
$this->updateMemberRecord($data)
} else {
// creates a new record
$this->setMemberRecord($data);
}
This code is causing double inserts in the database, we don't have a unique key for the table due to some poor design constraints, but I see two HTTP posts in the access logs happening at the same time.
The create date column is same or differs by a second for the duplicate record.
This issue is happening for only select few, it works for most of them.
The table is innoDB table and we can not use sessions on our architecture.
Any ideas of why this would happen
You said:
I see two HTTP posts in the access logs
You should try avoiding this and have just one http POST invocation
May be it is a problem related to concurrency and mutual exclusion. The provided code must be executed in a mutually exclusion zone, so you must use some semaphore / mutex to prevent simultaneous execution.
If you have two HTTP POST happening your problem is not on the PHP/MYSQL side.
One thing is allowing a second 'transparent' HTTP POST in the HTTP protocol. It's the empty url. If you have an empty GET url in the page most browsers will replay the request which rendered the page. Some recent browser are not doing it, but most of them are still doing it (and it's the official way of HTTP). An empty GET url on a page is for example <img src=""> or < script url=""> but also an url() in a css file.
The fact you have one second between the two posts make me think it's what's happening for you. The POST response page is quite certainly containing an empty Get that the browser fill by replaying the POST... I hate this behaviour.
I found that the double inserts were happening becuase double submits and our application doesnot handle double submits efficiently, I read up on some articles on this, here are some of the solutions
it always best to handle double posts at the server side
best solution is to set a UNIQUE KEY on the table or do a INSERT ON DUPLICATE KEY UPDATE
if you have sessions then use the unique token , one of the technique in this article
http://www.freeopenbook.com/php-hacks/phphks-CHP-6-SECT-6.html
or use can use the Post/Redirect/Get technique which will handle most double submit problems
http://en.wikipedia.org/wiki/Post/Redirect/Get
note: the Double submit problem only happens on a POST request, GET request is immune
public function setMemberRecord($data, $brand_id, $organization_id, $context = null)
{
global $gRegDbManager;
$sql = "insert into member ......"
$gRegDbManager->DbQuery($sql);
// Popuplate the iid from the insert
$params['iid'] = $gRegDbManager->DbLastInsertId();
$data = some operations
return (int)$data;
}
public function getMemberRecord($field, $id, $brand_id, $organization_id, $organization_level_account = null)
{
global $gRegDbManager;
$field = mysql_escape_string($field);
$id = mysql_escape_string($id);
$sql = "SELECT * FROM " . DB_REGISTRATION_DATABASE . ".member WHERE $field = '$id' ";
if($organization_level_account) {
$sql .= "AND organization_fk = " . $organization_id;
} else {
$sql .= "AND brand_fk = " . $brand_id;
}
$sql .= " LIMIT 1";
$results = $gRegDbManager->DbGetAll($sql);
if(count($results) > 0) {
return $results[0];
}
return;
}
/* * ******************************************************************************************************
* Updates member record in the member table
* *******************************************************************************************************
*/
public function updateMemberRecord($id, $changes)
{
global $gRegDbManager;
$id = mysql_escape_string($id);
if(!empty($changes)) {
$sql = "UPDATE " . DB_REGISTRATION_DATABASE . ".member SET ";
foreach($changes as $field => $value) {
$sql .= mysql_escape_string($field) . " = '" . mysql_escape_string($value) . "', ";
}
$sql = rtrim($sql, ", ");
$sql .= " WHERE iid = '$id'";
$gRegDbManager->DbQuery($sql);
} else {
return false;
}
}
I'm making a data-entry web page in PHP that validates and parses data for entry into a MySQL database.
There are more than 30 columns in the database that consist of MySQL SET or ENUM data.
If you don't know much SQL, an ENUM datatype column can only hold one value from a collection of ENUM variables that you define when you create the database. SET is also a collection of
variables that you define when you create the database, but the SET column can hold one or more of the
SET variables.
This is an ENUM datatype: car_titled_in_state = enum('Alabama','Alaska','Arkansas','California')
You can only title a car in one state. MySQL will not let you enter more than one state in
car_titled_in_state. (The error handling code for titling a car in more state is the function
prison_term(num_years). ;-)
This is a SET datatype: ice_cream_toppings = set('chocolate syrup','marsh mellow cream','cherries','butterscotch').
You can have any number of ice cream toppings, but each member of the set can only be entered and
counted once. (The error handling function for too many ice cream topping is body_weight = body_weight++.)
Radio buttons, which allow only one selection from the group, are suitable for ENUM variables.
Checkboxes, which allow for any number of selections from the group, are suitable for SET variables.
Making these menus line by line in my PHP would be a hassle because the groups of allowed variables
in many of these MySQL columns are going to change during beta and all the way into the early production months;
some of them will probably need to change again in 2011. These changes (lots of changes) would have to
made in the PHP code, plus all those requests would thicken the bandwidth between client and server.
I decided I would use a PHP function that queries the MySQL database and dynamically builds the menus.
This would need to use the MySQL command DESCRIBE, which, when used on ENUM or SET datatype columns,
returns the group of strings allowed in the column. (See above for their format.) Now, I am certain that
somebody, somewhere faced this problem before, but I searched the Internet twice for an hour each time,
and I did not find anything even close, especially not in PHP. So I decided to write my own, which took
me two days. It probably shouldn't have taken me that long, but I was working on my laptop in a van with
my wife and my in-laws, which was not an environment in compliance with Joel Spolsky's rules for
efficient programmer habitats.
I call my function choicemaker. (You will no doubt change this in your code.) It takes three
arguments: a MySQL table name ($table), a MySQL column name ($col_name) and an integer called
$row_length. The $row_length is a convenience. I have some very large SET and ENUM columns in
the database; one of the ENUMs lists every Federal Agency! I limit the number that display on each
line for the sake of neatness.
Choicemaker() inspects the results of the DESCRIBE query and uses them to decide whether to build
radio buttons (ENUM) or checkboxes (SET). This saves you having to put thid decision in code!
function choicemaker($table, $col_name, $row_length)
#connect to the database
{db_connect();
#build the string for the DESCRIBE query -- note the absence of SELECT
$qry_list="DESCRIBE " . $table . " " . $col_name . ";";
#submit the query
$listdisplay=mysql_query( $qry_list );
#get the query results
while ($row = mysql_fetch_array( $listdisplay )){
#there will only be one row of results
$make_list=$row[1];
#we need to get rid of some punctuation from the results
$remove = array("(", ")","'");
$replace = array("", "", "","");
#important -- we inspect the query results to find out if we
#have SET or ENUM variables and then assign a value to $choice_type and
#get ready to remove either 'set' or 'enum' from the results string
if (preg_match("/^set/", $make_list)){
array_unshift($remove, 'set');
$choice_type="checkbox";}
elseif (preg_match("/^enum/", $make_list)){
array_unshift($remove, 'enum');
$choice_type="radio";}
#here we clean up the query results
$arr_list = str_replace($remove, $replace, $make_list);
#make get redy to loop through the clean results
$arr_list = explode(',',$arr_list);
$row_pos=$row_length;
#finally, we get to make some buttons or boxes
foreach ($arr_list as $item) {
$str_choice = $item . ' ';
$str_choice .= '<input type="' . $choice_type . '" ';
$str_choice .= 'name="' . $col_name . '" ';
$str_choice .= 'value="' . $item . '" /> ';
#output our button / box here
echo $str_choice;
#decrement $row_pos to decide if it is time to output a line break
$row_pos--;
if ($row_pos == 0) {
echo('<br />');
#reset $row_pos after every break
$row_pos = $row_length;}}}
#Help stop connection pollution! Always close your connections!
mysql_close(); }
Here's the uncommented version for cut and paste:
function choicemaker($table, $col_name, $row_length)
{db_connect();
$qry_list="DESCRIBE " . $table . " " . $col_name . ";";
$listdisplay=mysql_query( $qry_list );
while ($row = mysql_fetch_array( $listdisplay )){
$make_list=$row[1];
$remove = array("(", ")", "'");
$replace = array("", "", "","");
if (preg_match("/^set/", $make_list)){
array_unshift($remove, 'set');
$choice_type="checkbox";}
elseif (preg_match("/^enum/", $make_list)){
array_unshift($remove, 'enum');
$choice_type="radio";}
$arr_list = str_replace($remove, $replace, $make_list);
$arr_list = explode(',',$arr_list);
$row_pos=$row_length;
foreach ($arr_list as $item) {
$str_choice = $item . ' ';
$str_choice .= '<input type="' . $choice_type . '" ';
$str_choice .= 'name="' . $col_name . '" ';
$str_choice .= 'value="' . $item . '" /> ';
echo $str_choice;
$row_pos--;
if ($row_pos == 0) {
echo('<br />');
$row_pos = $row_length;}}}
mysql_close(); }
Here's my question: given that this code will run about 30 times to build the page, would it really be any quicker
to do this in AJAX? It will take the users about 30 minutes at least, so there is no real performance gain associated
with having the top part built more quickly. I don't think the user experience will be much better. But what would an
AJAX version look like -- get the results back in JSOBN and parse them?
If I were you I'd cache this into a template or use the php file as a generator, unless there are quite a few table that will be queried.
What is the purpose of dynamically querying enum types?
Ajax won't make it load any faster, probably even slower, but might allow you to load the first 10, then the second 10, etc., so the user sees it actually working, and can interact with it before it is done completely loading.
I wouldn't think loading time would be a big problem though.
The reason I need to build the menu from enum and set datatypes is that for at least the first two years of my application, these datatypes will expand, and there are too many of them (more than 30) to make it efficient to keep adding new ones in code. Adding them in the database and letting the code generate the menus dynamically will save LOTS of time.
I want people to be able to "bump" what they've wrote in my database but at the same time only allow the input to be in the table ONCE at a time.
For Example:
Jim's code is 5555. Jim enters his code and it shoots to the very bottom of the table. After 34 minutes he tries to enter his code in again (Because various other people have inputted their code between now and then) but gets a display error letting him know he has 26 minutes to wait still.
Joe inputs his code and waits an hour and five minutes and is able to push his code back to the bottom again.
Basically, I'm displaying data from the bottom up in my table.
Is there any way to easily do this?
function some_more_custom_content() {
$output="<BR>";
ob_start();
if ($_REQUEST['code'] != "") {
$code = $_REQUEST['code'];
$query="INSERT INTO `fc` (`code`,`datetime`) values ('" . mysql_real_escape_string($code) . "', now())";
$result=mysql_query($query);
$entry['datetime'] = strtotime($entry['datetime']);
while ($fetch_array = mysql_fetch_array($result)) {
$seconds = time() - strtotime($fetch_array["datetime"]);
if ((time() - $entry['datetime']) < 60*60) {
echo ("The code " . htmlentities($code) ." was updated less than an hour ago.");
} else {
echo ("Inserted " . htmlentities($code) ." into the top.");
}
}
?>
I get a syntax error. Any idea where it is?
UPDATE: Getting error of:
Parse error: syntax error, unexpected $end
you should create a table with a unique index on the code field and then use a query like:
INSERT INTO CODES (code)
VALUES (555)
ON DUPLICATE KEY UPDATE lastUpdated =
case when NOW() - INTERVAL 5 MINUTE > lastUpdated
then NOW()
else lastUpdated end
this will update the lastUpdated field only in cases when it's older than 5 minutes
Not that hard:
Use a timestamp for the last "bump time".
When a code is entered, check if it already exists in the database.
If it doesn't, insert it and set the bump timestamp to now().
If it does exist, check if the timestamp was within the hour.
If it was, display error.
If it wasn't, reset it to now().
Sort your display data by bump timestamp.
EDIT:
$entry = /* get entry from database, assuming the case where it already exists */;
// depending on the format of timestamp you get from the database,
// you may have to convert it to a UNIX timestamp:
$entry['timestamp'] = strtotime($entry['timestamp']);
if ((time() - $entry['timestamp']) < 60*60) { // 60*60 is one hour in seconds
// display error
} else {
// reset bump
}