One script working one not, virtually identical

I found a javascript/ajax script that I used several years ago for inline editing of a customer list and it still works perfectly. I thought I would re-purpose this to make a bank ledger but for the life of me I can not get this thing to work. I took the original script and changed the variables to the new ones, created a new DB and table and updated the PHP part of the script, but it just doesn’t seem like the PHP file that actually writes the data is being accessed. I get no errors, it just reverts back to the old value when the screen refreshes. I have struggled for 4 days with this and thought, OK, I am just going to take the old script and modify it again from scratch. Same issue. I have checked the code over and over and over and see no mistakes. I have deleted the DB and recreated it. I have checked permissions on files and everything is the same. I just don’t have a clue where to even look next. Hope someone can help. The PHP file is scaled down and has no error correction, my main concern is just getting it to access it and write the data.

Thanks

<?php

if(session_status() == PHP_SESSION_NONE) {
    session_start();
}

//if(empty($_SESSION["admin"])) { header("location: index.php"); exit; }

include("include/connect.php");
include("include/define.php");
include("header.php");

$_SESSION['loggedin'] = true;
$_SESSION['id'] = "37";

$stmt = $pdo->prepare("SELECT account FROM customers WHERE id = ? Limit 1");
$stmt->execute([$_SESSION['id']]);
$data = $stmt->fetch();	
$stmt = null;

$account = $_SESSION['account'] = $data['account'];

?>

<div class="menu"><?php include("mainmenu.php"); ?></div>

<div class="content">
		<?php if(isset($_SESSION['message'])) { echo  "<div class='sessionmessage'>".$_SESSION['message']."</div>"; unset($_SESSION['message']); } else { echo "<div class='pageheading'><b>Manage Customers</b></div>"; } ?>
		
		<div class="tableheading"><b>Add Record</b></div>
		<table class="tables nosort-add">
		<tr>
			<th style="width: 125px"><strong>Date</strong></th>
			<th style="width: 45px"><strong>CHQ</strong></th>
			<th style="width: 390px"><strong>Payee</strong></th>
			<th style="width: 390px" class="tcenter"><strong>Category</strong></th>
			<th style="width: 95px" class="tcenter"><strong>Credit</strong></th>
			<th style="width: 95px" class="tcenter"><strong>Debit</strong></th>
			<th style="width: 20px" class="tcenter"><strong>CLR</strong></th>
			<th style="width: 20px" class="tcenter"><strong>Save</strong></th>
		</tr>		
		<tr>  
			<td style="width: 125px" id="thisdate" contenteditable></td>  
			<td style="width: 45px" id="cheque" contenteditable></td>  
			<td style="width: 390px" id="payee" contenteditable></td>  
			<td style="width: 390px" id="category" contenteditable></td> 
			<td style="width: 95px" id="credit" contenteditable></td>
			<td style="width: 95px" id="debit" contenteditable></td>  
			<td style="width: 20px" id="cleared" contenteditable></td> 
			<td class="tcenter" style="width: 20px"><div class="tooltip"><button type="button" name="btn_add" id="btn_add"><i class="fa fa-plus" style="color: green; font-size: 16px"></i></button><span class="tooltiptext">Add Customer</span></div></td>  
		</tr> 			
		</table>
		
		<br><br>		
		
		<div style="width: 1180px; margin: auto auto">
				<?php
				if(isset($_POST['limits'])) { $limit = $_POST['limits']; } else { $limit = 10;  } 
				
				if (isset($_GET["page"])) { $page  = $_GET["page"]; } else { $page = 1; };  
				$start_from = ($page-1) * $limit;  
				
				$data = $pdo->query('select count(*) from '.$account)->fetchColumn(); 
				$total_records = $data; 
				$total_pages = ceil($total_records / $limit);   
				
				if(!$total_pages) { $total_pages = 1; }
				elseif($page > $total_pages) { header("location: index.php"); }
				
				
				$pageLink = "<div class='srt'>&nbsp;Pages&nbsp&nbsp";  
				for ($i=1; $i <= $total_pages; $i++) {  
					$pageLink .= "<a href='editcustomers.php?page=".$i."'>".$i."&nbsp;</a>";  
				}; 
				?>
		
				<?php echo $pageLink . "<span style='color: #83A1CD; font-size: 11px'> | Click heading to sort by that column</span></div>";	?><br>

				
				<div style="float: left">
				<form action="editcustomers.php" method="post">				
				<select name="limits" id="limits">
				    <option  selected value="0" disabled>Posts per Page</option>
				    <option value="10">10</option>
				    <option value="20">20</option>
				    <option value="30">30</option>
					<option value="40">40</option>
					<option value="50">50</option>
				</select>
				</form>						  
				</div>
						
				<div style="float: right">
				<form action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]); ?>" method="post">
			    <input class="search form-control" type="text" name="search" placeholder="Search" value="">
				<input name="submit" value="Submit" type="image" src="images/search.png" width="20" height="20" class="magnify">
				</form>
				</div>
				<div style="clear: both"></div>
		</div>
		
		<div class="tableheading"><b>Edit / Delete Record</b></div>
		
	     <table class="tables sortable">
		 <thead>
		 <tr>
			<th style="width: 105px"><strong>Date</strong></th>
			<th style="width: 45px"><strong>CHQ</strong></th>
			<th style="width: 360px"><strong>Payee</strong></th>
			<th style="width: 360px" class="tcenter"><strong>Category</strong></th>
			<th style="width: 95px" class="tcenter"><strong>Credit</strong></th>
			<th style="width: 95px" class="tcenter"><strong>Debit</strong></th>
			<th style="width: 95px" class="tcenter"><strong>BAL</strong></th>
			<th style="width: 20px" class="tcenter"><strong>CLR</strong></th>
			<th class="no-sort" style="width: 20px" class="tcenter"><strong>Action</strong></th>
		</tr>
		</thead>
		<tbody>
		<?php 
		
		$thisdate=$cheque=$payee=$category=$credit=$debit=$cleared=$total = "";
		
		if(isset($_POST['search'])) {
			$search = filter_var($_POST['search'], FILTER_SANITIZE_STRING);		
			$stmt = $pdo->prepare("SELECT * FROM ".$account." WHERE thisdate LIKE ('%".$search."%') or cheque LIKE ('%".$search."%') or payee LIKE ('%".$search."%') or category LIKE ('%".$search."%') or credit LIKE ('%".$search."%') or debit LIKE ('%".$search."%') ORDER BY id ASC LIMIT $start_from, $limit"); 
		}
		else {	
			$stmt = $pdo->prepare("SELECT * FROM ".$account." WHERE year(thisdate) = year(NOW()) and month(thisdate) = month(NOW()) ORDER BY id ASC LIMIT $start_from, $limit"); 
		}
		
		$stmt->execute();
		$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
		$stmt = null;		
						
 		foreach($row as $row) { 
		?>
		<tr>
			<td class="thisdate" data-id1="<?php echo $row["id"]; ?>" contenteditable style="width: 105px"><?php echo $row["thisdate"]; ?></td>
			<td class="cheque" data-id2="<?php echo $row["id"]; ?>" contenteditable style="width: 45px"><?php echo $row["cheque"]; ?></td>
			<td class="payee" data-id3="<?php echo $row["id"]; ?>" contenteditable style="width: 360px"><?php echo $row["payee"]; ?></td>
			<td class="category" data-id4="<?php echo $row["id"]; ?>" contenteditable style="width: 360px"><?php echo $row["category"]; ?></td>
			<td class="credit" data-id5="<?php echo $row["id"]; ?>" contenteditable style="width: 95px"><?php echo $row["credit"]; ?></td>
			<td class="debit" data-id6="<?php echo $row["id"]; ?>" contenteditable style="width: 95px"><?php echo $row["debit"]; ?></td>
			<td class="total" data-id7="<?php echo $row["id"]; ?>" contenteditable style="width: 95px"><?php echo $row["total"]; ?></td>
			<td class="cleared" data-id8="<?php echo $row["id"]; ?>" contenteditable style="width: 20px"><?php echo $row["cleared"]; ?></td>			
			<td class="tcenter" style="width: 20px"><div class="tooltip"><button type='button' name='delete_btn' data-id9=<?php echo $row["id"]; ?> class='btn btn-xs btn-danger btn_delete'><i class="fas fa-trash-alt" style="color: maroon; font-size: 16px; font-weight: normal"></i></button><span class="tooltiptext">Delete Record</span></div></td>  
		</tr>
		<?php }?>
		</tbody>
		</table>

		<br>

		<?php echo "<div style='width: 1180px; margin: auto auto'><b>Page ".$page." of  ".$total_pages."</b></div><br><br>"; ?>

</div>

			
  <?php include("footer.php"); ?>
  
</body>
 </html>
 
 <script type="text/javascript">
  jQuery(function() {
    jQuery('#limits').change(function() {
        this.form.submit();
    });
});
</script>
 

<script>  
$(document).ready(function(){  
    function fetch_data()  
    {  
        $.ajax({  
            url:"editcustomers.php",  
            method:"POST"   
        });  
    }   
	
    fetch_data(); 
    $(document).on('click', '#btn_add', function(){  
        var thisdate = $('#thisdate').text();  
        var cheque = $('#cheque').text(); 
		var payee = $('#payee').text(); 
		var category = $('#category').text(); 
		var credit = $('#credit').text();
		var debit = $('#debit').text();
		var total= $('#total').text();
		var cleared = $('#cleared').text();		
 
        $.ajax({  
            url:"inupdelete.php?action=insert",  
            method:"POST",  
            data:{thisdate:thisdate, cheque:cheque, payee:payee, category:category, credit:credit, debit:debit, total:total, cleared:cleared},  
            dataType:"text",  
            success:function(data)  
            {  
                location.reload();
            }  
        })  
    });  
    
	function edit_data(id, text, column_name)  
    {  
        $.ajax({  
            url:"inupdelete.php?action=update",  
            method:"POST",  
            data:{id:id, text:text, column_name:column_name},  
            dataType:"text",  
            success:function(data)
			{  
				location.reload();
            }  
        });  
    }  
	
    $(document).on('blur', '.thisdate', function(){  
        var id = $(this).data("id1");  
        var thisdate = $(this).text();  
        edit_data(id, thisdate, "thisdate");  
    });  	
    $(document).on('blur', '.cheque', function(){  
        var id = $(this).data("id2");  
        var cheque = $(this).text();  
        edit_data(id, cheque, "cheque");  
    }); 
    $(document).on('blur', '.payee', function(){  
        var id = $(this).data("id3");  
        var payee = $(this).text();  
        edit_data(id, payee, "payee");  
    }); 
    $(document).on('blur', '.category', function(){  
        var id = $(this).data("id4");  
        var category = $(this).text();  
        edit_data(id, category, "category");  
    }); 
    $(document).on('blur', '.credit', function(){  
        var id = $(this).data("id5");  
        var credit = $(this).text();  
        edit_data(id, credit, "credit");  
    }); 	
	$(document).on('blur', '.debit', function(){  
        var id = $(this).data("id6");  
        var debit = $(this).text();  
        edit_data(id, debit, "debit");  
    }); 	
	$(document).on('blur', '.total', function(){  
        var id = $(this).data("id7");  
        var total = $(this).text();  
        edit_data(id, total, "total");  
    }); 		
    $(document).on('blur', '.cleared', function(){  
        var id = $(this).data("id8");  
        var cleared = $(this).text();  
        edit_data(id, cleared, "cleared");  
    }); 		
	
    $(document).on('click', '.btn_delete', function(){  
        var id=$(this).data("id9");  
		if(confirm("Are you sure you want to delete this record?"));
            $.ajax({  
                url:"inupdelete.php?action=delete",  
                method:"POST",  
                data:{id:id},  
                dataType:"text",  
                success:function(data){  
                	 location.reload();
                }  
            });  
    });  
});  
</script>

 <?php

if(session_status() == PHP_SESSION_NONE){
    session_start();
}

$action = $_GET["action"];  $account = $_SESSION["account"];  

if($action === "update") { UpdateColumn($account); }
if($action === "delete") { DeleteRecord($account); }
if($action === "insert") { InsertRecord($account); }


function UpdateColumn($account) {
		$stmt = $pdo->prepare("UPDATE ".$account." SET ".$thiscolumn." = '".$thisvalue."' WHERE id = ?");  
		if($stmt->execute([$_POST['id']])) { 
				$_SESSION['message'] = "Column updated successfully.";
		}
		else {
				$_SESSION['message'] = "Error updating Record";
		}
}

function DeleteRecord($account) {

	include "include/connect.php";  
	
	$stmt = $pdo->prepare("DELETE FROM ".$account."  WHERE id = ? LIMIT 1");
    $stmt->execute([$_POST['id']]);
	if ($stmt->rowCount()) {	
			$_SESSION['message'] = "Record deleted successfully.";
	}
	else { $_SESSION['message'] = "Record not deleted, try again.";
}


function InsertRecord($account) { echo "TESTING";
		
		include "include/connect.php";
		
		$thisdate = $_POST["thisdate"];
		if(empty($_POST["cheque")) { $cheque = NULL; } else { $cheque = $_POST["cheque"]; }
		$thisdate = $_POST['payee'];
		$thisdate = $_POST['category'];
		if(empty($_POST["credit")) { $credit = NULL; } else { $credit = $_POST["credit"]; }
		if(empty($_POST["debit")) { $debit = NULL; } else { $debit = $_POST["credit"]; }
		$cleared = $_POST['cleared'];
		$total = NULL;

		$msql = "INSERT INTO ".$account." (thisdate, cheque, payee, category, credit, debit, total, cleared) VALUES (?,?,?,?,?,?,?,?)";
		$stmt= $pdo->prepare($msql);
		$stmt->execute([$thisdate, $cheque, $payee, $category, $credit, $debit, $cleared, $total);
		if ($stmt->rowCount()) {
					$_SESSION['message'] = "Record added successfully.";
		}
		else {
		    		$_SESSION['message'] = "There was an error creating this record, please try again!";
		}	 
		$stmt = null;	
}
?>

You have a (several) php syntax error(s) in the inupdelete.php code, and the code never runs. Are you developing and debugging this on a system that has php’s error_reporting set to E_ALL and display_errors set to ON, in the php.ini on your system, so that php will report and display all the errors it detects?

$thiscolumn and $thisvalue are not initialised / extracted from the $_POST array in the UpdateColumn method.

I have a large number of points about the posted code (taken from the last, now deleted, thread). A few of these are security related. A few correct the operation of the code. Most of them help to make code general purpose and eliminate unnecessary typing.

Security -

  1. Correctly use a prepared query for the search input. Start by removing the filter_var() call. This has nothing to do with making it safe to put a value into an sql query statement. In fact, FILTER_SANITIZE_STRING is now deprecated and will be removed from php in the future. Don’t attempt to sanitize data, then use it, as this changes the meaning of the data. You need to validate data. If it is valid, use it. If it is not valid, let the user know what is wrong with it, let them fix the problem and resubmit the data.
  2. You need to apply htmlentities() to all dynamic values right before or as you are outputting them on a web page.

Operational -

  1. The query to get the total number of pages must have the same WHERE … clause as the data retrieval query. You should build the WHERE clause and any prepared query input parameters in php variables, so that you can use them with both queries.
  2. Both the posts per page/limits and search inputs need to be in the same GET method form. They also need to be ‘sticky’ and reselect/repopulate the fields with any existing choice or value.
  3. The pagination links need to include any exiting get parameters, so that when you switch pages, you don’t loose these inputs. You also need to use http_build_query() when building the query string part of the urls.

Things that will help organize and simplify the code -

  1. The code for any page should be laid out in this general order – 1) initialization, 2) post method form processing, 3) get method business logic – get/produce data needed to display the page, 4) html document.
  2. There’s no good point in making session_start() conditional. Your code should be organized so that you know if you have executed a session_start or not on any page.
  3. Many things have ids. The session variable that indicates who the logged in user is should be more uniquely named, such as ‘user_id’.
  4. You only need one session variable to indicate both if there is and who the logged in user is.
  5. If you set the ‘fake’ session variable(s) before any code that uses them, you don’t need to comment out any code.
  6. Use ‘require’ for things your code must have.
  7. Include/require are not functions. The () around the path/filename do nothing and should be removed.
  8. You need to build the posts per page/limits option choices dynamically, by defining an array of the choices. This will let you easily preselect any existing choice when you output the options and you can also use the array to validate that the submitted value is one of the permitted choices.
  9. The id column in the database table is/should be an index. If so, the limit 1 in the query does nothing and should be removed.
  10. When you only need to fetch a single column from a single row of data, use fetchColumn(). You have some cases where you are and where you are not.
  11. You apparently have an account table for each customer. Don’t do this. You should have one account table with an account_id/user_id column.
  12. If a user doesn’t have an account, you should setup and display a message stating so, and not run any of the code that’s dependent on having an account.
  13. Don’t copy variables to other variables for nothing. Just use the original variable that data is in.
  14. Every redirect needs an exit/die statement to stop php code execution. Again, you have some with and some without.
  15. To get a form to submit to the same page it is on, simply leave out the entire action attribute.
  16. You should build sql query statements in a php variable. This makes debugging easier and helps prevent typo mistakes by separating the sql query syntax as much as possible for the php syntax.
  17. When there are validation/data errors, such as $page > $total_pages, you should not just redirect elsewhere, as the provides a poor User eXperience (UX). You should setup and display a message or take an appropriate default action. This particular case would be either due to to a programming mistake or someone feeding their own data to your site. It would be appropriate to simply set page to total_pages.
  18. You can use a relative/blank page name in the pagination links, so that you don’t need to keep editing the code every time the name of the page changes.
  19. Supply the offset and limit to the query via prepared query place-holders.
  20. The default ORDER BY direction is ASC. You don’t need to specify it.
  21. If you set the default fetch mode to assoc when you make the database connection, you won’t need to specify it in each fetch statement.
  22. There’s generally no need to destroy prepared query statement handles, free up result sets, or close database connections in your code since php destroys all resources when your script ends.
  23. If a query matches no data, you need to setup and output a message starting so, instead of outputting nothing or continuing on to use nonexistent data and produce follow-on errors.
  24. If you use php’s short-open-print tag <?= and leave out the ; right before a closing ?> tag, you can use syntax like <?=$var?> to echo things in the html document.
  25. type="text/javascript" is no longer used in the <script> tag.
  26. User entered values need to be trimmed, mainly so that you can detect if all white-space characters were entered, before validating them.
  27. All inputs must be validated before using them.
  28. Session variables are inputs to your code. you must validate them before using them.
  29. You need to validate the resulting web pages at validator.w3.org
  30. You can put php variables directly inside double-quoted strings.
  31. You can use php’s ternary operator to simplify if/else conditional logic.
  32. You can use php’s null coalescing operator ?? to simplify setting default values for variables that might not exist.

I’m not sure why you have the fetch_data() javascrpt function, that makes a post method ajax request to the current page, with no post data, then doesn’t use anything that is returned in the response. When I tested this code, I commented this out.

Lastly, you can eliminate all the bespoke javscript, written out for every field/column, if you use class selectors to identify which fields are for the add record and for the update record operations.

Thanks for your reply, but as I said, the original code I have is identical to the code I posted, except for variable names, and it runs perfectly. I know there are issues with the inupdelete.php, but the version you see here I just scaled down in order to try and troubleshoot, the original one again works perfectly. so I was just trying to figure out why one works and one doesn’t when they are as close as being exact as can be.

Finally. I meticulously went through each line to make sure the context and syntax was right in both files and did find some errors in the Apache logs, these were mostly missing ({ or [ and all in the php inupdelete file. I also had to change some code as it wasn’t working, but had no errors. The calling file was error free as suspected.

PHDR. One thing regarding your list, htmlentities(), I have read many sites regarding security and there are so many conflicting information on this out there , It’s hard to figure what’s right. I know FILTER_SANITIZE_STRING is deprecated, just hadn’t removed it yet from code. The ‘fake’ session variable(s) were only for testing as the real data comes from another page which I was just bypassing for testing. Thanks for the tips though.

The posted html/javascript ‘works’, and submits expected data to inupdelete.php. Here is what it submits for an insert and an update test -

Get:Array
(
    [action] => insert
)
Post:Array
(
    [thisdate] => d
    [cheque] => 
    [payee] => 
    [category] => 
    [credit] => 
    [debit] => 
    [total] => 
    [cleared] => 
)
Get:Array
(
    [action] => update
)
Post:Array
(
    [id] => 2
    [text] => dd
    [column_name] => thisdate
)

As already replied, the posted inupdelete.php code never runs due to php syntax errors in it -

If you read the php documentation, instead of what is written on the internet, you will find what it does -

Convert all applicable characters to HTML entities

You apply it to dynamic values when you output them in a html context (web page, email) to prevent any html entities in a value from being rendered by the browser, which is how cross site scripting is accomplished.

Sponsor our Newsletter | Privacy Policy | Terms of Service