Jump to content

mac_gyver

Staff Alumni
  • Posts

    5,536
  • Joined

  • Days Won

    192

Everything posted by mac_gyver

  1. the boolean value returned by the exaction of a SELECT query only indicates that the query executed without error (a true value) or failed due to a query error (a false value.) it doesn't indicate that the query match any data. a query that doesn't match any data is a successful query, but returns an empty result set. there's a single query that does what you want. an INSERT ... ON DUPLICATE KEY UPDATE ... query. you would define the uid column as a unique index if it is not already defined that way. there's no need to try to select data in order to decide if you should insert new data or update existing data and in fact, there's a race condition in the existing code where multiple concurrent instances of your script can all find that data doesn't exist and will all attempt to insert the same data, resulting in duplicates or in errors. also, use a prepared query when supplying external, unknown, dynamic values to a query when it gets executed. there's two reasons for doing this - 1) addslashes() doesn't protect against every possible character that can break the sql query syntax, since it doesn't take into account the characters set of your database tables, which is why php remove its ridiculous magic_quotes a long time ago, and 2) it will provide a performance increase when executing the same query more than once within one instance of your script.
  2. this code can be greatly simplified, because writing out code for every input or combination of values is not effective programming. switching to the much simpler and more modern PDO database extension will eliminate about half of the database statements. this alone will answer your database questions since the PDO extension was designed to be simple and usable. here's a specific list of practices, most of which will simplify and clean up the code - put the php error settings in the php.ini on your system, so that you can set/change them at a single point. you cannot set display_startup_errors in your code because php has already started when your code is being executed. 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. most of the php code needed for searching/pagination goes in the get method business logic section. post method forms are used when performing an action on the server, such as inserting, updating, deleting data, sending an email, writing/updating a field. get method forms/links are used when determining what will be displayed on a page, e.g. searching, pagination. the second entry in the $tbls array contains a $. did you actually use this array to validate the $tbl input? don't use the root database user in applications. create a specific database user, with a password, with only the necessary permissions for use in an application. don't output raw database errors onto a wab page. this only helps hackers. instead, use exceptions for database statement errors (this is the default for both the mysqli and PDO extension in php8+) and in most cases simply let php catch and handle any database statement error, 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 would catch the exception, test if the error number is for something your code is designed to handle, and setup a unique and helpful error message for the user. for all other error numbers, just re-throw the exception and let php handle it. this will let you remove all the existing database error handling logic, simplifying the code. btw - mysqli_stmt_execut() can fail with an error but you don't have error handling logic for it, but since you will be removing all the existing error handling logic when using exceptions for database statement errors, you won't have to remove this non-existent code. if you are going to continue to use the mysqli extension, don't use mysqli_stmt_init() followed by mysqli_stmt_prepare(), just use mysqli_prepare(). if you are going to continue to use the mysqli extension, use the OOP notation. it is both simpler and because the php error response is different between the procedural and OOP notation, things that are fatal problems will produce fatal php errors when using OOP, but only produce warnings when using procedural notation. in most cases, you don't need to free prepared statements, result sets, or close database connections since php will destroy all resources created on a page when your script ends. you should keep the input data as a set in a php array variable, then operate on elements in this array variable throughout the rest of the code, i.e. don't write out line after line of code that's basically copying each input to another variable. you should trim all input data before validating it. after you do the above item, you can accomplish this with one single line of php code. related to the above item, don't copy fetched data to other variables for nothing. just use the original variables that the data is in. you should store user/validation errors in an array, using the field/input name as the main array index. after the end of all the validation logic, if there are no errors (the array holding the errors will be empty), use the submitted data. if there are errors, the code will continue on to display the html document, test and display any errors, re-display any form, populating the form field values with any existing data so that the user doesn't need to keep reentering/selecting data over and over. any dynamic value you output in a html context needs to have htmlentities() applied to it to help prevent cross site scripting. at the point of dynamically building the sql query statement, the table and columns names can technically be anything and should be enclosed by back-ticks so as to not produce sql errors. are you sure you actually tested this? the definition for $links_table_columns doesn't exist in the code and even if it did, it is apparently permitted column names, so the error message would be for an invalid column, not an invalid table. pagination involves two similar queries. the first query is to get the total number of matching rows. the second query is to get the logical page of data. the common part of both of these queries should be built once, in a php variable, with a corresponding array of input parameters. the common part of the query is from the FROM term ... through to any HAVING term. the query to get the total number of matching rows should use SELECT COUNT(*) followed by the common part of the query, i.e. it should NOT select all the data just to get a count of the number of rows of data. the data retrieval query would instead SELECT the columns you want, followed by the common part of the sql query, followed by an ORDER BY term, and then the LIMIT term. you should supply the two LIMIT values via prepared query place holders and add (append/merge) the two values to the array of input values. since you will be using a SELECT COUNT(*) ... query to get the total number of matching rows, you won't need to deal with mysqli's mysqli_stmt_store_result() and mysqli_stmt_num_rows() don't use a series of name_x variables. use an array. arrays are for sets of data where you will operate on each member in the set in the same/similar way. you can then use php's array functions to operate on the arrays of data. the pagination link markup should be built in a php variable so that you can output the result wherever and however many times you want. use relative urls in the pagination links. this will eliminate PHP_SELF. to build the query string part of the pagination links, get an existing copy of any $_GET parameters (this will automatically include any table, input, col, bool, and limit values so that you don't need to write out code for each one) set/modify the page (pg) element, then use http_build_query() to build the query string. this will take care of urlencodeing the values.
  3. what symptom or error are you getting that leads you to believe that? btw - the only redirect you should have in your post method form processing code should be to the exact same url of the form processing code to cause a get request for that page. this will prevent the browser from trying to resubmit the form data should the user reload the page or navigate away from and back to that page, which will let someone use the developer console network tab to see what the username and password is. if you want the user to be able to go to some other page, provide navigation links.
  4. first of all, don't bother with the mysqli extension. it is overly complicated and inconsistent, especially when dealing with prepared queries, which you need to use to prevent any sql special characters in a value from being able to break the sql query syntax, which is how sql injection is accomplished. instead, use the much simpler and more modern PDO extension. here's a list a do's and don'ts for the posted code - you need to use a prepared query when supplying any external, unknown, dynamic values to a query when it gets executed. this will require that you change any place there's an $sql input parameter to also include an array of input values, such as what would be present in a WHERE ... clause. you would also supply the offset and rows_per_page in the LIMIT term via prepared query place-holders and append the values to the array of input values. don't SELECT all the rows of data to get the total number of rows. instead, use a SELECT COUNT(*) ... query, then fetch the COUNT(*) value. the common part of the sql query, from the FROM ... term though to any HAVING ... term needs to be used for both the SELECT COUNT(*) ... and the data retrieval query. this is the value you should build in the $sql input parameter. just about every SELECT query that returns more than one row should have an ORDER BY ... term so that the rows of data are in a desired order. don't use PHP_SELF and all the related code/values. you can use relative urls in the links. when you build the pagination links, start with a copy of any existing $_GET parameters, set/modify the page parameter, then use http_build_query to build the query string part of the links. this will eliminate the need for the $append value. use exceptions for database statement errors (this is the default error mode for both the PDO and mysqli extensions in php8+) and in most cases simply let php catch and handle any database 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.) this will eliminate the need for the existing error handling and debugging logic. don't use the @ error suppressor, ever. there's no need. when learning, developing, and debugging code/query(ies), you should display all php errors. when running code on a live/public server, you should log all php errors. there's no need for the pagination code to test for a valid database connection. if the database connection fails, the page shouldn't ever get to the point of trying to use the connection for anything. 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. the code to get the logical page of data and build the pagination links should be located in item #3 in this layout. OOP is NOT about wrapping a class definition around your main code and adding $this-> in front of everything to make it work. for example, the offset value is only used in one place. there's no need for it to be a class property. just use a regular/local variable for the value.
  5. a <button> by itself doesn't do anything. a button with type='submit' must be part of a form. a button without type='submit' must have an event or javascript associated with it to do anything. you will need to put a complete post method form, with a button with type='submit', within the html table cell, for this to work.
  6. conditional statements exist in programming languages so that you can write code that makes decisions when it runs, such as only outputting markup when there's data to display. the existing code has a conditional test in it to only output the markup inside the tbody if there is data to display. wouldn't the answer to your question be to move the start of that conditional test so that it only outputs the table/thead markup when there is data to display?
  7. ^^^ this part of the code gets the selected brand id and calls the ajax code. why did you change this part of the code? the part you need to change is the code that uses the result of the ajax call.
  8. if you read the information that I posted, you will know why this doesn't work. you CANNOT repeat ids in the markup. you must use some other way of referencing the elements. i showed code to do this by finding the .closest parent container, then .finding an element with a class name within the parent container. the reason no one is posting fixed code is because you would not learn anything by just repeating what you see. programming is a writing and reading activity. you must actually understand the meaning of the words and syntax you are using. copy/pasting something that someone else writes doesn't do this.
  9. your browser's developer tools console tab may contain errors that would help you find the problem. the OP only half did the first item i listed (added a class, rather than changing the id to a class), leaving duplicate ids in the dynamically added fields. the second item i listed was done. all the rest of the information i listed was ignored.
  10. there are two reason this is not working - 1) ids must be unique. you cannot use id="Brand" more than once on a page, and 2) the dynamically added elements don't exist when you are attaching the on change event handler (even if you change the selector to be a class name.) you must attach the on change event handler to an element that exists at the time (document is the easiest, though not the most efficient), that will contain the elements you want the event handler to fire for. assuming you change id='Brand' to class='Brand', the following will attach the on change event handler to all instances of the <select class="Brand" ... within the html document - $(document).on('change', '.Brand', function(){ since ids must be unique, all the rest of the code manipulating the DOM must be changed so that they don't rely on ids. the way to do this is give the <tr a class name that you can search for, such as 'parent-container', you can then find the current parent container using - var parent = $(this).closest('.parent-container'); you can then find and reference the elements within the parent container, by a class name, such as - parent.find('.Category').html(html);
  11. your web hosting probably has a Frequently Ask Question section that lists any requirements and a code example to send emails. you would need to post any code you have tried to get any help with what could be wrong with it. code examples you find on the web as usually lacking in security, lacking a good user experience, are filled with unnecessary typing, are missing error handling and validation logic that would get them to tell you (display/log) why they aren't working, and most, incorrectly, are using the email address that was entered by the visitor as the From: email address (these emails are NOT being sent from the email address that was entered, they are being sent from the mail server at your web hosting and the Form: email address must use a domain name that is either at your web hosting or is tracible back to the ip address of the sending mail server through dns records.)
  12. if you index/pivot the data using the question as the main array index, you can directly produce the output using two nested foreach() loops, without needing extra variables and conditional logic to keep track of where each group of data changes or to output the starting/ending markup for each section, since there will be specific single points in the code for the start and end of each question section and of each option section. PDO even has a fetch mode to do this - PDO::FETCH_GROUP, which will index/pivot the data by the first column being SELECTed. to do this, change the fetchAll() line to this - $questions = $statement->fetchAll(PDO::FETCH_GROUP); you can then produce the output using this logic - foreach($questions as $question=>$group) { // start of question section echo $question; // loop over the rows in the 'group' to display the choices foreach($group as $row) { // start of choice section echo $row["choice"]; // end of choice section } // end of question section } to apply the answerA, answerB, ... classes to each choice section, you would store them in an array, e.g. $choice_styles = ['answerA', 'answerB', 'answerC', 'answerD'];, and change the second foreach loop to - foreach($group as $index=>$row) you can then get the correct style name in the choice section using - $choice_styles[$index].
  13. you must tell us what result you did get, even if the result was a blank page, and tell us what result you expected. your data is not normalized, making every query and operation on the data harder. you should have a brand table, with id (auto-increment primary index) and name columns. this will establish brand_id values. you would store the brand_id in any related table, such as the model table. would then simply query the brand table to get a list of the brand names and their ids for the select/option menu.
  14. the query and related code already is using the MySQL database. what makes you think it is not?
  15. it doesn't look like you are reading, understanding, or using the information from the previous threads. your post method form processing should - detect if the post method form was submitted (it is actually doing this.) trim the input data, one time, then use the trimmed data throughout the rest of the code. validate the trimmed data. after the end of the validation logic, if there are no errors, use the summitted data. your current test - if ($username == true && $password == true) { doesn't do this. the $username and $password will be true values, even if they contain invalid characters. one of the points of remembering if there are any errors, by storing them in variable(s), is so that you can test if there are or are not any errors in your code. in your previous thread, i recommend using an array to hold the user/validation errors. if you do this, you can simply test if the array is empty() to determine if there are no validation errors. the registration form and processing code should be very similar to the login in form and processing code. the major differences are - 1) in the login processing, after trimming the data, you don't really care what characters the values contain, all you care about is if the username/password matches an entry in the data file and 2) the actual processing of the validated data is different. why do you have a bunch of different variable name between the two pieces of code? you even have a nonexistent $name variable in the username form value='...' attribute that should be producing a php error. why are you using file() in the registration code and file_get_contents() in the login code? your logic in this code is unconditionally testing if the passwords match, without testing if the username was found. as to this ridiculous test_input() function that you found on the web. the only thing it is doing that is correct is trimming the data. the function is improperly named (it's not testing anything), it should not unconditionally apply stripslashes (when this was necessary to do, it needed to be conditionally applied, but the need to do this has been removed from php for a long time), and htmlspecialchars is an OUTPUT function, do NOT apply it to the input data in your form processing code.
  16. here's a list of suggestions for the code implementation - use a variable (or defined constant) for the file name so that if you need to change it, you can do so in a single location. if you were doing this for real, you would need to use file locking to insure that you don't corrupt/truncate the data file when there are multiple concurrent instances of your script trying to access the file. your code should test if the file exists before attempting to read it, in order to prevent php errors when the file initially doesn't exist. you should also test the result of the file() call in case the file does exist but it is empty. after you you read the data from the file, when you explode the data you should store it into an array, indexing it using the lowercase username. this will let you simply test if the lowercase submitted username exists in the data by using an isset() statement. by using the lowercase of the usernames from the data file and the lowercase of the submitted username, you will enforce uniqueness, e.g. MyUserName and myusername will be treated as the same value. the username that you store in the data file, and that you display, should retain the letter-case that was originally entered. to get a form to submit to the same page it is on, leave out the entire action='...' attribute. there's no need to use $_SERVER['PHP_SELF'] in this case and if you ever do need to use $_SERVER['PHP_SELF'], you need to apply htmlentities() to it to help prevent cross site scripting. if you put the closing </label> tag after the form field it goes with, you can leave out the for='...' and corresponding id='...' attributes (which you are currently missing most of.)
  17. your code is not testing the same value for each field throughout the code, because you are trimming it in one place, but using the original value in other places. your post method form processing code should - not try to detect if the submit button isset(). instead, test if a post method form was submitted. don't create discrete variables for each form field and don't copy variables to other variables for nothing. instead, keep the form data as a set in a php array variable, then use elements in this array variable throughout the rest of the code. don't create separate variables for each error. instead, use an array for the user/validation errors. when you add errors to the array, use the field name as the array index. this will let you perform dependent validation tests on a field only when there are no previous validation errors and it will let you output the errors adjacent to the corresponding field if you desire. trim all the input data at once, then use the trimmed data throughout the rest of the code. after you do item #2 in this list, you can trim all the data using one single line of code. after the end of the validation logic, if there are no errors (the array holding the user/validation errors will be empty), use the form data. after the end of using the form data, if there are no errors, redirect to the exact same url of the current page to cause a get request for that page. to display a one-time success message, store it in a session variable, then test, display, and clear the session variable at the appropriate location in the html document. by passing the success message through the url in the redirect, you are opening your site to phishing attacks and cross site scripting. every redirect needs an exit/die statement to stop php code execution. a header() statement doesn't stop php code execution. if there are user/validation errors at step #5 or #6, the code will continue on to display the html document, display the errors, re-display the form, populating the field values with any existing data. any data you output in a html context should have htmlentities() applied to it to help prevent cross site scripting.
  18. assuming that uploads are enabled on the server (which you can test using code) and you have a valid post method form, with at least one type='file' field, if the $_SERVER['REQUEST_METHOD'] === 'POST' and both the $_POST and $_FILES arrays are empty, it is likely that the post_max_size was exceeded. you can use this as the condition to setup and display a message that the total size of the post data was too large. the Content-Length is available in $_SERVER['CONTENT_LENGTH'] (which is in bytes.) if it is set, this is the value that the web server compares with the post_max_size value to attempt to abort requests that are too large. both the upload_max_filesize and post_max_size can be either in bytes or K/k, M/m, or G/g abbreviations, therefore you must convert these to the same units in order to compare them. your post method form processing should - detect if a post method from was submitted detect if there is $_POST/$_FILES data. test the $_FILES[...]['error'] element validate the uploaded file information i recommend that you use an array to hold user/validation errors. after the end of all the validation logic, if the array is empty, there were no errors and you can use the submitted form data. the ['type'] element comes from the request, can be set to anything, and cannot be trusted. you should determine the mime type on the server. based on the use of the back-button history for navigation, your form and form processing code are on different pages. these should be on the same page in order to simplify the code, make it easier to secure the code, and provide a better user experience.
  19. you are getting a fatal error in the browser. you need to use your browser's develop tools console tab to debug what is going on in the browser. the element you have given id="selectYear" to, is the button, not the select menu. as to the $cusid. this is already known on the server when the main page was requested. why are you passing it through the form? external data submitted to your site must always be validated before using it. if you use the already validated value that you already know on the server, you can simplify the code.
  20. sorry for how this sounds, but this code is programming nonsense. the biggest problem with it is that OOP is not about wrapping class definitions around main application code, then needing to add $var-> in front of everything to get it to work. all this is doing is creating a wall of unnecessary typing, that's changing one defining and calling syntax for another more verbose one. additional problems with this code is that it is insecure in may places, it is applying htmlspecialchars and strip_tags improperly to input data, it has no validation or error handling logic, it is using a prepared query for queries that don't have any external data being used, it is not properly using a prepared query that have external data being used, it is repetitive, and it's class methods/functions don't accept input data as call-time parameters (requiring even more $var->property statements to make it work.) the only correct programming practices it contains are - it is using dependency injection to make a single database connection available to the classes that need it and it is using a COUNT() query in a few places to get counts of matching data. don't waste any time on using this code, either as a learning resource or as a way of achieving a discussion forum.
  21. data ($_POST, $_GET, $_COOKIE, $_FILES, and some $_SERVER variables) submitted to any site can come from anywhere, not just your forms/links/cookies, can be set to anything, and cannot be trusted. you should trim all external data, mainly so that you can detect if it is all white-space characters, validate it, then use it securely in whatever context it is being use as. in a html context, such as an email body or a web page, you should apply htmlentities() to any external, unknown, dynamic values to help prevent cross site scripting. for a value that has a specific format, like an email address, after you have validated that it is not an empty string, you should validate that it is a properly formatted email address. for something like a contact form, where you don't have the ability to limit its use to only known, logged in users, yes, you should use a captcha. your post method form processing code should - if the form and form processing code are not on the same page, they should be. this will allow you to display any validation errors and repopulate the form field values upon a validation error. detect if a post method form has been submitted. this will insure that if the page is ever requested via a get request, that it won't waste time running code when there is no post data to use. keep the form data as a set in an array variable, then use elements in this array variable throughout the rest of the code, i.e. don't write out lines of code copying variables to other variables for nothing. after you do item #3 on this list, you can trim all the data at once using one single line of code. don't unconditionally loop over the $_POST data. hackers can submit 100's of post variables to your code. you should instead define an array of expected fields, then loop over this defining array when validating and using the submitted data. you should not directly accept the subject from external data. if there's a need for different subject values, define them in an array, with a numerical id indexes, then get the actual subject from the submitted numerical id value. you should have error handling logic for the mail() call. if it returns a false value, the email was not accepted by the sending email server. you would setup a general failure message for the user, and you would log everything about the attempted email so that you will both know that it failed and can perhaps see a pattern as to why it may have failed. you would only display the success content if the mail() call returned a true value. after successfully using the submitted form data, with no errors, you should perform a redirect to the exact same url of the current page to cause a get request for that page. this will prevent the browser from resubmitting the form data if the user reloads that page or navigates away from and back to that page. to display a one-time success message, store it in a session variable, then test, display, and clear that session variable at the appropriate location in the html document. don't echo static markup. there's no php code in that success message. just drop our of 'php' mode and put the markup in-line in the file.
  22. except during testing, when you enter your own email address, these emails are NOT being set From the name/email address that is entered in the form. they are being sent from the mail server at the web hosting. the domain in the From email address must either directly correspond to the sending mail server or there must be dns zone records (specifically an SPF record) at the domain in the From email address that indicates your email server can send email for that domain. short-version, use a From email address that exists at the web hosting.
  23. there's so much wrong with just this small section of code - it is using a post method form for determining which record to edit. this should use a get method form/link, so that if the user wants to return to the same point, they can bookmark the url or if there's a problem they can submit the url so that someone else can view and see the problem. the only time you should edit/update data is when correcting a mistake in a value or just changing a status value, not when performing normal data operations. for normal data operations, you should insert a new row in an appropriately defined table for every separate operation, so that you have an audit/history trail, that would let you detect if a programming mistake, accidental key press, or nefarious activity caused data to be entered and also so that you can produce a history report. the use of sprintf() provides no security against sql special characters in a value from being able to break the sql query syntax, which is how sql injection is accomplished. the simplest, fool-proof way, to provide security in an sql context, for all data types, is to use a prepared query. since the mysqli extension is overly complicated and inconsistent, especially when dealing with prepared queries, this would be a good time to switch to the much simpler and more modern PDO extension. the database should enforce uniqueness, by defining any column must not contain duplicate values as a unique index. this will produce a duplicate index error when attempting to insert/update duplicate values, that the error handling would detect and report back to the user that the value they tried to insert/update already exists. the code should not use a loop to fetch data from a query that is expected to match at most one row of data. based on the columns oil_due, inspect_date in a table named Trucks, the database is not designed correctly. There should be a table where the one-time/unique information about each vehicle is stored. this table will define a vehicle id (autoincrement primary index) that gets used when storing any related data. service records would then be stored in a separate table, one row per service, using the vehicle id to relate them back to the vehicle they belong to. if there are multiple primary defining records for any vehicle, these should be found and consolidated into a single record, the columns that must be unique need to be defined as unique indexes, and the error handling for any insert/update query need to be changed to detect a unique index error number and report to the user that the data already exists. if this Trucks table is actually the table holding the service records, there need to be a row per service operation per vehicle. the code would then query for any records that have a pending/open status value. when a service is performed, the status for that record would be updated to a value indicating it is 'complete' and what datetime it was completed. any future scheduled service would be inserted as a new row with a pending/open status value.
  24. programming is an exact science. when someone points out a problem with some code and asks what is that code, it means you need to post exactly the code that was mentioned. if someone asks what is the expected value for something, it means to post exactly what that value is.
  25. the error means that $login_level is a boolean (true or false) value, but the code expects it to be an array with a group_status element. either something has changed to cause the find_by_groupLevel() function to return an unexpected value OR that part of the code (which is using an exact comparison with a string consisting of '0') never actually worked, wasn't fully tested, and was probably always producing this error, but the error wasn't being reported/displayed. what is the code for the find_by_groupLevel() function and what is the expected value it should return for both a non-banned and a banned user?
×
×
  • 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.