Jump to content

mac_gyver

Staff Alumni
  • Posts

    5,510
  • Joined

  • Days Won

    185

Everything posted by mac_gyver

  1. to convert old mysql_ based code, you need to 1) convert the database extension to a currently supported one, 2) provide protection against sql special characters in a value being able to break the sql query syntax, which is how sql injection is accomplished, and 3) handle database statement errors. for item #1, the PDO extension is much simpler and more modern then the mysqli extension. for item #2, the simplest way of doing this is to use prepared queries, which provides protection for all data types. converting any query to a prepared query is straightforward - remove the php variables (keep these for later) and any single-quotes, {}, and quotes/concatenation dots that were used to get the variables into the sql query statement. put a ? place-holder into the sql query statement for each variable you removed. prepare the query. supply an array of the variables to the ->execute([...]) call. note: any wild-card search characters, typically used with a LIKE comparison, are part of the value, not part of the sql query statement. for item #3, as of php8, both the mysqli and PDO extensions use exceptions by default for all the database statements that can fail - connection, query, exec, prepare, and execute. this simplifies your code, since you can remove any existing error handling logic. the only database exceptions you should catch and handle in your code are for user recoverable errors, such as when inserting/updating user submitted data (which you are not doing in this case.) in all other cases, simply let php catch and handle any database exceptions, where php will use its error related settings to control what happens with the actual error information (database statement errors will 'automatically' get displayed/logged the same as php errors.)
  2. you would not UPDATE amounts in a table to track this data, since that doesn't provide an accounting trail should a programming mistake, duplicate entry, or nafarious activity alter a value. these are derived amounts that you would calculate when needed. you would have an invoice_accounting (or similar named) table, that gets a row inserted for every amount that affects an invoice amount. you would query when needed (a UNION between the invoice(s) table and the invoice accounting table) to determine the current total +/-/0 for any invoice.
  3. programming already is a tedious typing task. don't make it harder than it needs to be. buried within the unnecessary echoing of static html markup, is the problem. form fields must have a name='...' attribute for the value to be included in the submitted form data. here's a list of practices the will simplify and help secure the code - use 'require' for things your code must have for it to work to get a form to submit to the same page it is on, leave out the entire action attribute don't pass display messages or message flag values through the url, as this opens your site to phishing attacks any dynamic value you output in a html context needs htmlentities/htmlspecialchars applied to it to help prevent cross site scripting. you have some cases using this and some that are not the search form needs to be a get method form, it needs to be sticky and repopulate the field value with any existing data so that the user can just alter the value if a search didn't produce the result they were looking for the code for any page needs to 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 don't copy variables to other variables for nothing. just use the original variables you need to trim all user entered data, mainly so that you can detect if it was all white-space characters you need to validate all inputs before using them don't echo static css/html/javascript functions should return the result they produce, i.e. don't echo output inside a function, return it to the calling code don't use php functions, with css/html/javascript in them to build the html document use implicit binding and simply supply an array of input values to the PDOstatement ->execute([...]) call set the default fetch mode to assoc when you make the PDO connection so that you don't need to specify it in each fetch statement require/include are not functions. the () around the path/filename do nothing and should be removed
  4. you should use a data-driven design, with an array that maps the data field to the database column, then dynamically build the sql query statement and get the data values, by looping over the defining array. this will prevent typos, mismatched entries,... and allow you to change what the code does simply by changing the defining array.
  5. you also need to profile your code (calculate differences in microtime(true) values for the different operations on the page) to determine where it spends its time. i suspect that the time taken on the server isn't where the problem is, but in the execution of the javascript in the browser.
  6. the code for any page should be laid out in this general order - initialization post method form processing get method business logic - get/produce data needed to display the page html document all the database specific code you have on this page should be in section #3 in this layout. this makes it easier to write, test, and debug your code and query(ies), and now that you need to modify the code/query(ies), puts it all in one place. if you are only getting a count of the number of matching rows, don't select all the columns (SELECT *) and all the rows. use a SELECT COUNT(*) ... query instead, then fetch the count value. the three queries getting a count of the number of matching rows with status = 0, 1, and 2 can be done in a single query. you can get the SUM(amount) and COUNT(*), where the rest of the query is the same, in a single query. lastly, the json_encode() should not be inside the loops. this is repeatedly encoding the array after each row is added, leaving only the last result. this probably where the most time is being taken. the json_encode() statements should be after the end of the relevant loop.
  7. there's a setting - https://www.php.net/manual/en/ini.core.php#ini.max-file-uploads
  8. there are xAMP pre-built systems you can download and install on windows, e.g. https://www.apachefriends.org/
  9. from a reply in your previous thread - also in your previous thread, two different forum members (ignoring the chatbot replies by the 3rd respondent in that thread) pointed out that you need to change all the short opening php tags to full <?php tags. i also use windows (10) for development, with PDO and php8.
  10. the key/index for the 'a+' entry is 0. this is a boolean false value, so the if() logic branch is skipped. you need to perform an exact match for a non-false value. e.g. !== false
  11. at about line 105 in Statement.php is a short opening tag.
  12. you are seeing the raw php code in the browser because of the short opening <? tag. always use full opening <?php tags. you should not make a new database connection in every function. your main code should make one database connection, then supply it as a call-time parameter to any function that needs it. if doing this requires too much restructuring of the existing code, the connectDB() function should define a static variable to hold the connection, then test for and only make a new connection if there is not already one. the default setting in php8+ for PDO errors is to use exceptions for all the database statements that can fail. you should only catch and handle database exceptions for user recoverable errors, such as when inserting/updating duplicate or out of range data. in all other cases, simply let php catch and handle any database exceptions, where php will use its error related settings to control what happens with the actual error information, via an uncaught exception error (database statement errors will 'automatically' get displayed/logged the same as php errors.)
  13. when you successfully complete the post method form processing code, you should perform a redirect to the exact same URL of the current page, the page the form processing code and the form is on, to cause a get request for that page. this is called - Post, Redirect, Get (PRG) - https://en.wikipedia.org/wiki/Post/Redirect/Get this will prevent the browser from resubmitting the form data should that page get reloaded or browsed away from and back to. to display a one-time success message/content, store a value in a session variable, then test for the session variable, display the variable/content, and clear that session variable, at the appropriate location in the html document on that page. to allow the visitor to go to any other page, provide navigation links.
  14. form fields need a name="..." attribute, e.g. name="firstname", ... if you are just starting out, forget about using the overly complicated and inconsistent mysqli extension. instead, use the much simpler and more modern PDO extension, and use prepared queries. you should also put the form and form processing code on the same page, as this results in the simplest code. 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.
  15. 1st of all, all the post data you send in the request will be set (is_category, sales, material, salesoffice), so there's no point in testing if it is set, because it will be. if using a post request, just detect if the REQUEST_METHOD is 'POST' you should actually be using a get method request when determining what will be displayed on a page. while I'm pretty sure this has already been covered in a previous thread, you should be using a data-driven design (which is probably what the $column array is part of.) so what are you actually trying to accomplish? include a column/value in the WHERE clause if there is a non-empty value for that input? to do this, you don't need to write out code for every possible combination. regardless of using a data-driven design or conditional logic (once) for each input, you only need code that includes a column/value if there is a non empty value for that input. // using a data-driven design, define permitted WHERE columns and corresponding inputs $where_columns = ['sales_doc_type'=>'sales', 'material'=>'material', 'sales_office'=>'salesoffice']; // array to hold where terms $where_terms = []; // array to hold prepared query input parameters $params = []; // build the where terms foreach($where_columns as $col=>$input) { if($_POST[$input] ==! '') { $where_terms[] = "`$col`=?"; $params[] = "$_POST[$input]"; } } // build the WHERE clause $where = ''; if(!empty($where_terms)) { $where = 'WHERE ' . implode(' AND ',$where_terms); } // build the sql query $sql = "SELECT * FROM billing $where"; // examine the result echo $sql; echo '<pre>'; print_r($params); echo '</pre>';
  16. getting the current highest MAX() value and incrementing it in your code is not concurrent safe. if there are multiple instances of your script running at the same time (you must assume that this is possible), they will all get the same starting value, resulting in duplicate ids. the correct way of doing this is to have a table with an autoincrement primary index, insert a row of 'main' data, get the last insert id from that query and use it when inserting the data related to each 'main' row of data.
  17. once you switch to using a .csv file, you need to use a data-driven design, where you simply define a data structure (array) that controls what general purpose code does. see the following (untested, but should work) example - <?php /* data used - [0] => Sales Document Type [2] => Billing Date [11] => Material [25] => Gross Amount [45] => Sales Office [47] => Plant */ /* note: if you insure that the order in the following definition matches the order in the data file, you can use the array intersection method in the following code. otherwise, you must use the (commented out) loop method in the following code. */ // define mapping of data index to database column $db_col = [0=>'Sales_Document_Type', 2=>'Billing_Date', 11=>'Material', 25=>'Gross_Amount', 45=>'Sales_Office', 47=>'Plant']; // database table name $table = 'billing'; // number of initial lines to skip in the file $skip = 1; // for demonstration purposes, the file is hard-coded to be the converted csv of the example . xlsx file // in the actual code, this would be set to the temporary uploaded file $file = 'Aug Spares.XLSX - Sheet1.csv'; // open file if (($handle = fopen($file, "r")) !== FALSE) { // dynamically build the sql query $sql = "INSERT INTO `$table` (`".implode('`,`',array_values($db_col))."`) VALUES (".implode(',',array_fill(0,count($db_col),'?')).")"; // this example uses the much simpler and more modern PDO database extension $stmt = $pdo->prepare($sql); // start line counter at 1 $line = 1; // read lines from file while (($row = fgetcsv($handle, 1000, ",")) !== FALSE) { // process lines after initial skipped lines if($line > $skip) { // special processing // convert Billing_Date to standard format $row[2] = date('Y-m-d', strtotime($row[2])); // remove comma separator from Gross_Amount $row[25] = str_replace(',', '', $row[25]); // build array of value using array intersection method $params = array_values(array_intersect_key($row,$db_col)); /* // build array of values using a loop $params = []; foreach(array_keys($db_col) as $key) { $params[] = $row[$key]; } */ $stmt->execute($params); } $line++; } fclose($handle); } else { die('unable to open uploaded (temporary) file'); }
  18. the session variable is a 'required' input to your code. you must 'validate' it before using it, i.e. the current user must be logged in, in order to be able to see the team creation form and process the form data. if there is not a logged in user, you must take an appropriate action and not allow the current user to see the form or be able to run the form processing code. next, the only piece of user data you should store in a session variable upon successful login is the user_id. you should query on each page request to get any other user data, such as the username. this will allow this other user data to be edited and the change will take effect on the very next page request. likewise, you should not store the username in the players table. this is duplicating data that's defined/stored elsewhere. if you have the player's user_id, you would query when needed to get the player's username. if you were doing this for real, you would need to run the set of INSERT queries as part of a transaction, so that if either query failed with an error, they can both be rolled back. you also need to define the teams teamname and tag columns as unique indexes, to prevent duplicates, and have error handling that tests the result of the teams insert query for a duplicate index error (number.) the players userid and team_id columns must be defined as a composite unique index, to prevent duplicates (assuming that a player can only be on one team at a time), and have error handling that tests the result of the players insert query for a duplicate index error (number.)
  19. nested forms are invalid. what is the form markup? only checked checkboxes are included in the submitted form data. since you are using $row['CODE'] as the checkbox's array name index, that's all you need. you would just get and use the array indexes inside the post method form processing code. the checkboxes are the only valid form fields in the posted code. the rest of that isn't valid, but isn't needed anyways. you should only display the cost on the web page. you should not pass the cost through the web page, where it can be manipulated and be set to anything. you should get the cost in the post method form processing code if you need it. you would only store the cost with the selected items if the cost can change over time and you haven't setup a table to hold the cost history data. you don't need id attributes in the markup unless you are referencing the individual elements in the browser. ids must also be unique, therefore the use of the same id='CODE' and id='COST' inside the loop doesn't work properly. the post method form processing should detect is a post method form was submitted, before referencing any of the form data.
  20. the message you cited occurs when you do a 'view source' of the page in the browser. any get parameters you have in the URL should only determine what content is gotten and displayed on the page, not what post method form action is performed in the server-side code. if the last request the browser has for a URL is a html post method form submission, the form data will get resubmitted when you do a 'view source' of the page (even if you have prevented the form itself from being redisplayed on that page.) your first post in this thread indicated that you know you could use ajax, not that you were already using it. the reply you got is based on how you would normally do this, using a html post method form submitted to server-side post method form processing code, on the same page, to prevent the browser from trying to resubmit the post method form data should that page get reloaded, a 'view source' performed on it, or the URL is browsed away from and back to, by instructing the browser to perform a get request for the exact (including any existing get parameters) same URL of the current page. the main point of using ajax on a web page is so that you don't reload the whole page. if what you are doing means that you are going to reload the whole page anyways, you shouldn't be using ajax to do this. when you do use ajax to submit a post method form, the ajax response code needs to deal with any server-side success/failure response (validation errors, duplicate data errors, ...) that are produced and output by the server-side code.
  21. upon searching, this is due to the MySql server reporting a value back to the client (php in this case) that php doesn't recognize for the character set setting. is this just an error that is getting logged or does it prevent the connection from being used? here's a bug report that suggests a work-around of setting a character-set-server value in the MySql's my.cnf - https://bugs.mysql.com/bug.php?id=85946
  22. try using utf8mb4 for the setting. utf8 is (was) an alias for utf8mb4, which i'm guessing is no longer valid.
  23. you have jumped to an incorrect conclusion about what is occurring. an sql query that has no programming mistake doesn't produce any php errors when accessing the result from that query, regardless of php version. you are getting php errors because there's something wrong with the specific sql query in question with the code you are posting. if you do what has already been suggested and add error handling, you can find out what is wrong with the query. if you want anyone here to help, you will need to post the sql query. i'm going to guess that you are putting a value, in a php variable, directly into the sql query statement, in a numerical context, such as an id, but the value is empty, resulting in an sql syntax error. this is a programming mistake. the correct way to fix this is to validate inputs, only execute a query when 'required' inputs have an expected value, and to use a prepared query so that any sql special characters in a value cannot break the sql query syntax.
  24. this code is attempting to test the number of rows in a result set for a successful (no execution error) query. (you should actually just fetch the data and test if there was fetched data, since you want to use the data if it exists, simplifying the code.) the php errors you are getting mean that the query didn't execute at all, due to things like an sql syntax error, wrong database or no database selected, incorrect table or column names, incorrect use of aggerate functions, ... having error handling for the database statements that can fail, will mean that this code, testing the number of rows in a result set, will only get executed if the query executed successfully, without errors. this is one of the great points of using exceptions for error handling. your main code will only see and deal with error free execution of statements that can throw exceptions, since execution transfers to the nearest correct type of exception handling upon an error or to php if there is no correct type exception handling in your code.
  25. the error you are getting is a follow-on error, because there was no error handling at the query() call that failed, and the code continued to run and tried to use the a result that doesn't exist. you ALWAYS need error handling for statements that can fail. for database statements that can fail - connection, query, exec, prepare, and execute, the SIMPLEST way of adding error handling, without adding logic at each statement, is to use exceptions for errors and in most cases simply let php catch and handle the exception, where php will use its error related settings to control what happens with the actual error information (database statement errors will 'automatically' get displayed/logged the same as php errors.) the exception to this rule is when inserting/updating duplicate or out of range user submitted data. in this case, your code should catch the exception, test if the error number is for something that your code is designed to handle, then setup a message telling the user what was wrong with the data that they submitted. for all other error numbers, just re-throw the exception and let php handle it. by turning off exceptions for the mysqli error handling, you must now handle errors for each of these statements, which means you must write conditional logic testing the result from each statement call and take an appropriate action, such as preventing the following code from being executed if a statement call failed.
×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.