Jump to content

mac_gyver

Staff Alumni
  • Content Count

    4,271
  • Joined

  • Last visited

  • Days Won

    109

Everything posted by mac_gyver

  1. the following is a single page password reset example, showing the recommendations listed above - <?php // define some 'helper' functions - these would typically be defined in an external .php file and 'required' when needed // apply html htmlentities to a value function _ent($val) { return htmlentities($val); // this uses the current/default character encoding setting } // return an element from an array - used to reference array elements that might not be set function _element($arr,$index) { return isset($arr[$index]) ? $arr[$index] : ''; } // recursive function to trim data. function _trim($val) { if(is_array($val)) { return array_map('_trim',$val); } else { return trim($val); } } // define an array of the expected/required form fields $fields = []; $fields['user_uid'] = ['label'=>'Username']; // i suspect the user_uid is actually the username $fields['temporary_password'] = ['label'=>'Existing Password']; // suspect temporary pwd is a generated pwd, in any case it is the existing password $fields['password'] = ['label'=>'New Password']; $fields['confirm_password'] = ['label'=>'Confirm New Password']; $errors = []; // define an array to hold validation errors $post = []; // define an array to hold a working copy of the submitted form data // form processing code if($_SERVER['REQUEST_METHOD'] == 'POST') // this is a general purpose way of detecting if a post method form has been submitted. { // get a trimmed copy of the submitted form data $post = array_map('_trim',$_POST); // validate the submitted data // check that the required fields are not empty foreach($fields as $field=>$arr) { if($post[$field] == '') { $errors[$field] = "{$arr['label']} is empty."; } } // check if the password/confirm_password match if(empty($errors['password']) && empty($errors['confirm_password']) && $post['password'] != $post['confirm_password']) { $errors['password'] = "The {$fields['password']['label']} and {$fields['confirm_password']['label']} are not the same."; } // if the new password must meet any length or 'strength' requirements, validate those here... // at this point, if there are no errors, use the submitted form data if(empty($errors)) { // use the data in $post here... require 'dbh.php'; // get the current password for the user $sql = "SELECT user_password FROM users where user_uid = ?"; $stmt = $pdo->prepare($sql); $stmt->execute([$post['user_uid']]); if(!$row = $stmt->fetch()) { // the user doesn't exist $errors['user'] = "The username/password is incorrect."; // a generic message to help prevent finding valid usernames } else { // the user does exist, check the existing password if(!password_verify($post['temporary_password'],$row['user_password'])) { // password doesn't match $errors['user'] = "The username/password is incorrect."; // a generic message to help prevent finding valid usernames } else { // password does match, update the password $sql = "UPDATE users SET user_password = ? WHERE user_uid = ? "; $stmt = $pdo->prepare($sql); $stmt->execute([password_hash($post['password'], PASSWORD_DEFAULT),$post['user_uid']]); } } } // at this point, if there are no errors, the form processing code was successful if(empty($errors)) { // do whatever you want when the password reset was successful } } // output the html document starting here... ?> <!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Reset password</title> </head> <body> <?php // display any errors if(!empty($errors)) { echo implode('<br>',$errors); } ?> <form method="POST"> <input type="text" name="user_uid" placeholder="Username" value='<?php echo _ent(_element($post,'user_uid')); ?>'> <br> <input type="text" name="temporary_password" placeholder="Existing Password" value='<?php echo _ent(_element($post,'temporary_password')); ?>'> <br> <input type="text" name="password" placeholder="New Password" value='<?php echo _ent(_element($post,'password')); ?>' > <br> <input type="text" name="confirm_password" placeholder="Confirm New Password" value='<?php echo _ent(_element($post,'confirm_password')); ?>'> <br><br> <button type="submit" name="submit" class="button">Reset Password</button> </form> </body> </html> you don't have to understand OOP notation in order to use it. calling an OOP class method is really no different than calling a procedural function. compare the PDO statements on ~ lines 70-72 and 88-89 with the massive number of mysqli statements you have in your code, and decide which you would rather be using. you will also note that the sql syntax for ? prepared query place-holders is the same between mysqli and PDO, so you don't have to change any of the sql syntax.
  2. as to the error, an else {} statement requires a corresponding opening if(){} statement. i can only guess that you are trying to add an else {} because you have some code (the success redirect) in the wrong place and you are not actually reading the code and seeing where the opening { and closing } are at now. before going any further, you need to add missing features and simplify the existing code. at a minimum do the following - 1) detect that a post method form has been submitted before referencing any of the $_POST data. 2) validate all input data before using it. use an array to hold validation error messages. this will let you validate all the data at once and then display all the errors when you re-display the form. 3) the form processing code and the form need to be on the same page. this will eliminate all the header() redirects and it will let you re-populate the form with the submitted data when there is a validation error, so that the visitor doesn't need to keep reentering the same data over an over. 4) don't copy variables to other variables without a good reason. one good reason when processing user submitted values would be to trim() the data, so that you can detect all white-space characters. you can do this with a single line of code that operates on the data as a set. 5) password_hash() is used on the password you are going to insert/update in the database table. password_verify() is used to compare the stored hashed password with a submitted password. all other uses of these that you have in your code now are not doing anything useful and in the case of trying to compare the stored hashed password with the submitted password, you need to use password_verify() after you have fetched the stored hashed password. 6) don't use mysqli_stmt_init() and mysqli_stmt_prepare(). just use mysqli_prepare(). it does the same thing with one statement. 7) use exceptions to handle database statement (connection, query, prepare, execute) errors. this will simplify the code (you can remove the conditional logic you have now) and give you error handling for all the statements (the execute call can fail too, but you don't have any error handling for it.) to enable exceptions for the mysqli extension, add the following line before the point where you are making the database connection - mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT); 8 ) don't use mysqli_stmt_get_result() it results in non-portable code. this statement may work on your development system and current web host, but it may not on a different web host. if you move to a different host, you may have to rewrite the code. it is better to avoid using statements that may not exist. in fact, you need to switch to use the php PDO extension (Barand posted an example in one of your threads.) the PDO extension doesn't have any statements that may not exist and it is overall simpler and more consistent to use than the mysqli extension. 9) the query in the code will at most match one row. don't use a loop to fetch the data. this is just cluttering up your code with unnecessary logic and syntax. Keep It Simple - KISS.
  3. i'm not sure if (programming pun intended) or what the question is? if you are asking what a php if/else statement does, please see the control structure section of the php.net documentation - http://php.net/manual/en/language.control-structures.php if you are asking when you should use an if(){} or if(){}else{} statement or what code you should put in each part of the if/else statement, that depends on what you have defined you want the code to do. conditional statements control which code gets executed, based on the Boolean result of the expression being evaluated. the if(){...} block of code gets executed if the expression evaluates to a true value. the else {...} block of code gets executed if the expression evaluates to a false value. if you want to redirect the user to a different page if they are not logged in, you would use an if(){...} statement. the expression being evaluated would be a value that indicates the not logged in state. the code that gets executed would perform the redirect. btw - in the 1st piece of code, the first else {} is not need and should be removed, because the if(){} code exits and halts program execution when the expression is true. just put the remainder of the code after the closing } of the if(){} statement.
  4. your page is redirecting to itself, over and over, until the browser has had enough. your program logic is not correct. you need to decide what the code on your page is going to do if the user is logged in and if the user isn't logged in, starting with what is the purpose of this page? is it the login form processing and login form (based on the loginsystem in the path), or is it the site's main index.php page? if you are trying to make a single page that has form processing code and the form on it, the only redirect you should have is at the end of successfully completing the post method form processing code, in which case you should redirect to the exact same URL of the page to cause a get request for the page so that the browser won't try to re-submit the post data if you refresh the page or navigate away from and back to it.
  5. mac_gyver

    Need help Rename the image while uploading.

    if you are storing uploaded files associated with each user, wouldn't using the user id as a leading part of the file name, followed by a separator character, then the file category, make sense? you can then find all the files associated with a user by doing a wild-card file match starting with the user's id and separator character.
  6. mac_gyver

    ipn simulator not working on 000webhost...

    this is likely due to a + near the end of the payment date for the time zone, and when you use http_build_query() on the data, this gets treated as is (a space), rather than a literal + , which should end up being a %2B urlencoded value. you should log the received $_POST data so that you can see what it is. if the payment date looks like - Wed+Jul+4+2018+10%3A15%3A11+GMT+0000, you will need to handle the last + differently in the code. next, either use or examine the 'official' php paypal listener code, to see how the + near the end of the payment date gets treated - https://github.com/paypal/ipn-code-samples/tree/master/php
  7. mac_gyver

    Displaying records from database using enum

    you will want to do something that was posted about an hour ago on one of the other help forums, and use the same where clause in both queries.
  8. i recommend reading the php.net documentation for glob(), which Barand posted a link to in his post. it contains an example - Example #1 Convenient way how glob() can replace opendir() and friends, which is exactly what you are doing.
  9. mac_gyver

    How can I format a select query

    if the NORM is to use the formatted version, you should format the data when it is inserted. if you need the value without the : it is much simpler to remove it when you don't need it.
  10. mac_gyver

    insert is dropping off digits

    the preg_split isn't the problem and you could echo the $mac value inside the loop to know for sure. the mac database column is most likely defined as an integer and the values are being truncated at the first non-numerical character. another possibility is that the column is a character type, but it is not long enough to hold the data.
  11. the biggest out of date problem is the use (and misuse) of addslashes(). for values that are being supplied to an sql query statement, you need to instead use a prepared query and supply the values when the query is executed. the php PDO extension is much simpler to use, over the mysqli extension, especially when using prepared queries. for the $taal value that is being used in the sql queries as part of a column name, you must validate that it contains only and exactly an expected value, since you cannot protect against sql injection in 'identifiers' by escaping the value and you cannot supply 'identifiers' using a prepared query place-holder. for values that are being used to build URLs, addslashes() shouldn't even be used. these cases should use urlencode() or even better yet, use http_build_query(), which applies urlencode() for you, when building the query string part of URLs. along with using prepared queries when supplying values to an sql query, you need to use exceptions to handle database statement (connection, query, prepare, execute) errors, and in most cases let php catch the exception, where it will use its error_reporting, display_errors, and log_errors settings to control what happens with the actual error information. this will give you consistent error handling through out the code, and let you eliminate any error handling logic you may have now. enabling exceptions for either the mysqli or PDO extension takes a single line of code, but is different between the two extensions, so you need to pin down which one you are going to use firstly. as to not seeing any php error messages. you should have your development system set up with error_reporting set to E_ALL, display_errors set to ON, and output_buffering set to OFF, in the php.ini, and you should remove any lines of code setting these. lastly, this code is filled with repetitive blocks that only differ in what value they use and with inefficient coding., which requires that you make changes and corrections in multiple places. going through and cleaning up the code before you try to update it, will reduce the overall amount of work. two immediate things that can reduce the amount of code when building links are - 1) the code is using some/all of the existing get parameters, and adding/modifying one of them when building links. to do this, just get a copy of the current $_GET variable, assign or unset elements in this copy, then use http_build_query to produce the query string part of the URLs. 2) the code is building a comma separated list of posted groep values in the URL &groep= parameter. you can just implode the posted data. there's no need for all the code using a flag, a loop, and conditional logic, repeated in multiple places. i would also recommend that you rearrange the code so that any post method form processing comes before the start of the html document and that the php 'business logic', that knows how to get/produce data needed to display the page, comes after the post method form processing code and before the start of the html document. the result from these two sections of code should be stored in php variables, with simple php code inside the html document using this data.
  12. mac_gyver

    PHP Array

    and after you switch to use the php PDO extension, your goal should be to execute one single JOIN query that gets the related data that you want in the order that you want it. you should never execute SELECT queries inside of loops. and while it will probably go away when you convert to use the PDO extension, a fetch() statement doesn't cause a database error, so using or die() logic on the end of a fetch() statement doesn't do anything useful. when you switch to use the PDO extension, you should use exceptions to handle the database errors, and in most cases, let php catch the exception, where it will use it's error_reporting, display_errors, and log_errors settings to control what happens with the actual error information.
  13. mac_gyver

    Help with sorting an array

    if you cast each string as an integer, any leading digits will be converted to their integer value. function cmp($a, $b) { $a = (int)$a; $b = (int)$b; if ($a == $b) { return 0; } return ($a < $b) ? -1 : 1; }
  14. mac_gyver

    improve my database class

    @AdRock and anyone else, please don't follow the tutorial at that link. It is doing just about everything that a (database) class shouldn't do. it has broken scope by using defined constants for the connection credentials, so that there can only be one connection to one type of database server in an application. the connection credentials should be supplied to the constructor at call time. it needs to set the character set for the connection, turn off emulated prepared queries, and it should set the default fetch mode to assoc. it should extend the base class, so that all of the properties and methods of the base class are directly available, without needing to write one line methods for each one. it should not use php to check the type of variables and explicitly bind input data, as this limits the values to php's maximums, which for applications now a days can be less than your databases's definition and usage. while you should use exceptions to handle errors, this class should not catch any connection exception, just store the error message, and continue running like nothing bad just happened. the application code should catch any exception it is responsible for handling and let php catch and handle the rest, where php will use its' error_reporting, display_errors, and log_errors settings to control what happens with the actual error information or where php will use any user defined exception handler.
  15. mac_gyver

    improve my database class

    using the PDO extension will eliminate about half of the code (the code related to dynamically binding inputs), which will significantly speed up the code's execution, and will eliminate the need for the get_result() method, making the code portable across php version, build, and configuration differences. the mysqli stmt ->get_result() method is php version specific and requires that php be built to use the mysqlnd driver and that driver must be present. i've even seen at least one thread where these conditions where met but a more direct socket connection was being used, rather than a tcp/ip connection, and the get_result() method was not available, breaking the code.
  16. mac_gyver

    Android file uploads

    what is the size of the files that work and what are the sizes of the files that don't work?
  17. i would store all the earnings in one table, with a member/guest 'type' column (or perhaps you are already storing a value that distinguishes between members and guests?) if you only want the total per campaign, the query structure you already have would produce the correct result. if you also want to retrieve and display separate earning values for members and guests, you would just add conditional logic in the SELECT term to SUM() the two 'type' values separately. if at any point you just want to query for member earnings or for guest earnings, rather than to query against a separate table for each, you would just add the appropriate condition to the WHERE term.
  18. ^^^ and which is required if you leave out a column value.
  19. there would be a related and helpful php error, if php's error_reporting/display_errors were set properly, that would help identify that the problem is most likely due to the wrong format for the 1st bind_param() parameter. this should be a string of just the format specifiers, without commas between them. next, whatever your sanitize() function code is doing, it is either one or all of - ineffective, insecure, or redundant and is probably not needed. could you post the code for the sanitize() function. the current code will allow empty value(s) to be used for all the fields (an empty username won't match any existing user and an empty password will be equal to an empty confirmation password.) you should validate all the inputs before using them and validate all the inputs at once. you are only validating some of the data and you are stopping at the first validation error, so it will take multiple form submissions to validate all of the data, but since you are redirecting back to the form, the user must keep filling in all the form values after each error, which will increase the likely-hood of introducing more errors. you should put the form processing code and the form on the same page so that you can display all the validation errors when you re-display the form and you can populate the form fields with the existing values so that the user doesn't need to keep re-tying in all the values when there is a validation error. edit, you should also list the column names in the INSERT query so that your code will be self-documenting and will still work if the columns get rearranged.
  20. i'm wondering why you are doing this comparison? you should not have any data with a zero or negative user_id stored in any table, so the above is causing unnecessary work examining the values and will produce the same result if that comparison is not in the query.
  21. mac_gyver

    syntax error detection

    if you were seeing fatal parse errors before, it was because of the configuration settings, not anything you were doing in the code (unless the errors were in a required/included file and the settings were either in the main file or a file included/required before the one where the error is at.) if you are not seeing them now, it's because the configuration settings are different. use a .php script with a phpinfo() statement in it to find what the master/local error_reporting, display_errors, and log_errors settings are.
  22. mac_gyver

    Help with php stock level updating

    that function, as i already stated, isn't responsible for storing the data. all it does is produce the current balance and store (update) it in a different table. the problem isn't this redundant balance. the problem is storing the correct source data. to correctly store the data, so that you have a record of what items are actually in each location, you must insert row(s) in the transactions table for each addition or subtraction to the quantity for any location. transferring something from one location to another involves subtracting it from the location where it was at and adding it to the location where it has been moved to. once you store the correct transaction row(s), the code you have posted that gets the current balance will work correctly as is. since transferring things from one location to another is probably not used much, what is the problem with submitting the form twice, once to subtract the quantity from where the item is at, and a second time to add the quantity to where the item will be moved to?
  23. mac_gyver

    Help with php stock level updating

    what is the 'Transfer In' choice used for? to do this, there must be two records inserted into the transaction table. one record must account for the removal of the quantity from the 1st warehouse location and the second record must account for the addition of the quantity to the 2nd warehouse location. you can currently do this using two form submissions, one with a 'Transfer Out' choice for the 1st warehouse location and one with a 'Transfer In' choice for the 2nd warehouse location. if you want to use a single form submission, it will need to have a selection/option menu to pick the 2nd warehouse location and you will need to write program logic to detect the 'Transfer' choice and to cause the two records to be inserted into the transactions table. are your programming skills sufficient to do this?
  24. mac_gyver

    Help with php stock level updating

    the update_balances() code ONLY sums the transaction table quantity and updates the items table balance column. this code is both unnecessary (you should just sum the transaction quantity when you need it) and isn't where the problem is at, assuming you are using 'Transfer In' for the transaction type for the transfer to a warehouse. if you are executing code that inserts a 'Transfer Out' transaction for warehouse A and inserts a 'Transfer In' transaction for warehouse B and the items balance does not end up being the same as the starting value, then there's something wrong with the insert transactions, such as a spelling mistake in the transaction type or both transactions being 'Transfer Out' (not 'Transfer In'.) i see that one of your transaction types contains at least one character with a special character encoding. you should have a separate table with the transaction type names, that assigns a transaction type id (auto-increment integer column) to each one. the transaction type id should be stored in the transaction table, not the transaction type name. this will reduce the data storage requirements, speed up queries, make it easier to validate input, and eliminate the possibility of different character encoding causing values to match or not match.
  25. mac_gyver

    Is there a proper way to do this query?

    the following is the SELECT query syntax definition, with the relevant parts in red - the FROM table_references part is where any JOIN tables and join conditions go. JOINs (there are a handful of types, which you can research on the web or in the mysql database documentation to find out about) are used to get data from related tables or even within the same table. the two tables you have are related through the campaign_id columns. if you only want results that have entries in both tables (you can join as many tables as needed), you would use just a JOIN. if you want results from the 1st table, regardless of any entries in the 2nd table, you would use a LEFT JOIN (this would correspond to having a campaign without any earnings row(s)). i recommend that you copy the relevant parts from the syntax definition above and try to fill in the information based on your tables. one thing that will help simplify the query is to use alias names for the tables. you can use alias names of 'c' for the campaigns table, and 'e' for the earnings table. the alias names are defined when you list the tables names in the FROM table_references part of the query. any place you reference a column in the query, use the table_alias_name.column (even when the table part is not necessary.) this will serve to help document what the query is trying to accomplish and force you to reference the columns from the correct table (important if you have same name columns in different tables with different meanings.) to get you started, here is the FROM table_references part for your tables using a LEFT JOIN - FROM campaigns AS c LEFT JOIN earnings AS e ON c.campaign_id = e.campaign_id note: the AS keyword is optional (once you understand aliases, you can leave the AS out). when you have the same name columns in both tables and you are only matching column values, with no other conditions, instead of the ON keyword syntax shown, you can use USING(campaign_id)
×

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.