-
Posts
5,457 -
Joined
-
Days Won
175
Community Answers
-
mac_gyver's post in Normal User and Admin User was marked as the answer
learning involves forming a theory about how something works, performing an experiment to prove or disprove the theory, observing the result of the experiment and concluding if the theory was correct or not. you will learn much better and quicker if you make an attempt at designing, writing, and testing code to see if it does what you expect.
if(loggedin()) { // code for all logged in users if($user_level == 1) { // code for a logged in user with level == 1 } else { // code for a logged in user with level != 1 } } else { // code for a non-logged in user } i recommend two things - 1) use defined constants, such as define('ADMIN_USER',1);, instead of literal numbers, so that anyone reading the code can tell what the numerical values mean, 2) when conditional 'failure' code is shorter then the 'success' code, invert/complement the condition being tested and put the shorter code first. this will make your code easier to read and understand.
-
mac_gyver's post in Coming from MySQL 5 was marked as the answer
PDO examples -
a non-prepared query. assuming the literal 4 (which is not a string, it's a number) is hard-coded in the query -
$query = "SELECT * FROM ember_features WHERE id = 4"; $stmt = $pdo->query($query); $row = $stmt->fetch(); a prepared query. assuming the value being shown in this query is a dynamic value -
$query = "SELECT * FROM ember_features WHERE id = ?"; // a simple ? place-holder where the dynamic value is used at in the query $stmt = $pdo->prepare($query); // prepare the query $stmt->execute([ 4 ]); // execute the query, suppling dynamic value(s) as an array to the execute() call $row = $stmt->fetch(); where did you find this connection code? here's everything that's wrong with it -
it's not setting the character set to match your database tables, so character conversion can occur, breaking multiple-byte characters it's not using exceptions for errors, which is the default setting now, which simplifies all the error handling it's not using true prepared queries. by using emulated prepared queries (the default) and not setting the character set, your queries are open to sql special characters in a value being able to break the sql query syntax, which is how sql injection is accomplished it's not setting the default fetch mode to assoc, so you will need to specify the fetch mode in each fetch statement there's no point in catching and handling a connection error in your application since a user to your site can do nothing about it and doesn't need to know it is the reason a page isn't working. catching and outputting the raw error information also helps hackers when they intentionally trigger connection errors (by flooding your site with requests that consume all the database connections.) if you have ever looked at a connection error, it lists the database server host/ip address, the database username, if the connection is using a password, and web server path information. do you really want to give someone this much information about your site? you should only catch and handle user recoverable database errors in your application, such as when inserting/updating duplicate user submitted data. for all other query errors and all other type of queries, simply do nothing in your code and let php catch hand handle database errors. -
mac_gyver's post in New dB not functioning was marked as the answer
in the phpinfo output, there's a value near the top named - Loaded Configuration File. is this the same php.ini that you checked?
next, search for the error_reporting and display_errors output. what are both the local and master values for these?
here's typical PDO connection code -
$DB_HOST = ''; // database host name or ip address $DB_USER = ''; // database username $DB_PASS = ''; // database password $DB_NAME = ''; // database name $DB_ENCODING = 'utf8mb4'; // db character encoding. set to match your database table's character set. note: utf8 is an alias of utf8mb3/utf8mb4 $options = [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // set the error mode to exceptions. this is the default setting now in php8+ PDO::ATTR_EMULATE_PREPARES => false, // run real prepared queries PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // set default fetch mode to assoc ]; $pdo = new pdo("mysql:host=$DB_HOST;dbname=$DB_NAME;charset=$DB_ENCODING",$DB_USER,$DB_PASS,$options); in your previous thread, i have instructions on converting a query, that has php variables being put directly into it, into a prepared query.
also from that thread - if you want to post examples of your existing code, i/we can show what it would look like using current practices.
a lot of the coding suggestions given in that thread actually eliminate code, so that you can just remove things, rather than spending time updating them.
-
mac_gyver's post in how to display one heading using for loop was marked as the answer
assuming you are using the PDO extension, it has a fetch mode that will index/pivot the data for you -
// build and execute the query here... // index/pivot the fetched data using the first column in the SELECT list $data = $stmt->fetchAll(PDO::FETCH_GROUP); // at the point of producing the output if(!$data) { echo "<p>There is no data to display</p>"; } else { foreach($data as $index=>$items) { // output the section heading echo "<li class='current'><a href='#'>$index</a></li>\n"; // loop over the data in the section foreach($items as $row) { // output each row of data echo "<li>{$row['Sub']}</li>\n"; } // if needed, close the section here... } }
-
mac_gyver's post in unset() not working within foreach loop was marked as the answer
the query is SELECTing the column_name, which is why you are echoing column_name to produce the output -
$row['column_name'] will be 'well_no' or 'geom'.
you would create an array with the two values you want to exclude, then because you cannot directly loop over the result set from a query a second time, fetch all the rows of data into a php variable (see the fetchAll() method), then as you are looping over this array of data, use a conditional test if( !in_array($row['column_name'],['well_no','geom']) ) { produce the output here... } to exclude those two column_name values.
and as was written in one of your previous threads, there no good reason to catch and handle an exception from a SELECT query in your code. just let php catch and handle it. remove the try/catch logic from this query, simplifying the code. also, where this catch logic is at, you won't see the output from it unless you look in the 'view source' in the browser.
-
mac_gyver's post in preg_split(): No ending delimiter '_' found in was marked as the answer
all the split() statements that your search/replace change to preg_split() can simply use explode()
there's was an existing preg_split() in the code that your search/replace changed to preg_preg_split() that needs to be changed back to just preg_split()
-
mac_gyver's post in mysqli_connect suddenly not working was marked as the answer
one of the great points of using exceptions for errors is that your main code will only 'see' error free execution. no discrete error checking logic will ever get executed upon an error and should be removed, simplifying the code.
php's error_reporting should always be set to E_ALL. temporarily set display_errors to ON so that php will display all the reported errors, which will now include any uncaught database exceptions.
-
mac_gyver's post in Display problem in mpdf php with database was marked as the answer
here's a note about AddPage(), which does the same as the <pagebreak> tag -
apparently WriteHtml() adds the first page at the beginning of a new document, so the specific <pagebreak> adds a second one.
-
mac_gyver's post in PHP help needed. Webpage php script works for all teams except team "0" was marked as the answer
this checks the datatype of the variable, not what's in the variable. get, post, cookies are by definition strings, regardless of what value they hold. change this to is_numeric().
-
mac_gyver's post in Design of a database for a price list was marked as the answer
i/we don't understand abbreviations like tpu, pu, n, n 2, r, r 2 that would give meaning to the posted information. can you provide some context, in English, for the two examples you have posted? are these different categories of items?
ultimately, your task boils down to database normalization. you can search on the web to find out what this means.
to start with, you need an item table that holds the unique information about the items, one row per item. at a minimum, this table would have columns for - id (autoincrement primary index), and name. this will establish the item ids that will get used when storing data related to the items.
if these various wookbooks are for different categories of items, you also need a category table that holds the unique information about the categories, one row per category. at a minimum, this table would have columns for - id (autoincrement primary index), and name. this will establish the category ids that will get used when storing data related to the categories. if this is the case, the above item table would also have a category_id column that will hold the corresponding category id for each item.
since i/we don't know if pricing is per category, per item, or per list of items. i.e. what the extent and scope of the data is, i cannot offer any specific suggestions beyond the above.
-
mac_gyver's post in PHP calculation was marked as the answer
here's some coding pointers -
don't copy variables to other variables for nothing. just use the original variable(s) that data is in. don't write out code for every possible value. use a data-driven design and let the computer operate on each value for you. here's what I came up with. in your initialization section -
// define the month number and names (columns) $months = [1=>"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Decm"]; // note: the reason you needed to use decm is because dec is a reserved word. you can use dec if you surround it in back-tacks in any sql statement inside the while(){...} loop -
// array to hold the data for the current row $data = []; // loop over the month number and month names foreach($months as $mo=>$month) { // produce the year-month value $ym = $row['Year'] .'-'. str_pad($mo,2,'0',STR_PAD_LEFT); // test if the year-month is between the start and end dates if($ym >= $row['start'] && $ym <= $row['end']) // use whatever you have named the start and end date columns { // put the value into the data array // use zero for any empty value $data[] = $row[$month] == '' ? 0 : $row[$month]; } } // at this point, $data contains the entries to use for the current row of fetched data // examine the result echo '<pre>'; print_r($data); echo '</pre>'; // calculate the average $TotalMark = round((array_sum($data)) / count($data), 2); // examine the result echo $TotalMark; note: you should use a value like 9999-12 for the end date for a person who is still employed
-
mac_gyver's post in Experiencing problems with CODE tags was marked as the answer
the correct way of including code in the forum is to use the menu's <> button.
-
mac_gyver's post in num_rows with object mysqli and statements was marked as the answer
i also recommend the much simpler and more modern PDO extension. you can directly fetch data from a prepared query, the same as how you would fetch it for a non-prepared query.
if you are going to use the mysqli extension, forget about mysqli_int/mysqli_stmt_prepare. just use mysqli_prepare(). also, forget about the num_rows property. just fetch the data and test if there was any fetched data.
if you are querying to find a row of data matching an (active) user, you would not include the password comparison in the WHERE ... term. also, you should be using php's password_hash() and password_verify() for password hashing.
php finally realized the it should use exceptions for errors for things like database statement errors. as of php8, the mysqli (and PDO) connection, query, exec, prepare, and execute calls throw exceptions for errors. this means that any conditional error handling logic you have for these statements can be removed since they won't ever get executed upon an error, simplifying the code.
there's also generally no need to close prepared query handles, free up result sets, or close database connections in your code, since php destroys all resources when your script ends, simplifying the code.
pdo version -
$sql="SELECT * FROM users WHERE username=? AND active=1"; $stmt = $pdo->prepare($sql); $stmt->execute([ $uname ]); if($user_data = $stmt->fetch()) { // a row was found // you can reference elements in $user_data, such as $user_data['id'], $user_data['added'], ... } else { // no row was found }
-
mac_gyver's post in Query Failed : SQLSTATE[HY093]: Invalid parameter number: parameter was not defined was marked as the answer
when using named prepared query place-holders, the name must match between what you use in the sql query and the binding. in your code, they don't. e.g. :LogID is not the same as :LOGIN_ID. ...
i recommend that you use simple positional ? place-holders. there's less to type and keep track of. regardless of the place-holder type, you can simply supply an array of the input values to the ->execute([...]) call, saving more typing.
you should not manage the id value yourself in code. this is not concurrent safe. instead, use an autoincrement primary index. the database engine will perform the necessary table locking to insure that concurrent queries will generate unique values.
your database must enforce uniqueness, it is the last step in the process. the username column must be defined as a unique index. you would then attempt to insert a row of data, and test in the exception handling catch block for that query if an duplicate index error (number) occurred. if it did, setup a message for the user letting them know that the username is already in use. for all other error numbers, rethrow the exception and let php handle it.
there's also nothing to fetch from an insert query. why are you doing that?
-
mac_gyver's post in Self submitting AJAX was marked as the answer
provided the code for the page is 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 at the end of item #2, if the request was an AJAX request, you would build and output the response to the ajax request, then exit/die, so that the rest of the code on the page isn't executed.
here's a snippet of code to detect an AJAX request -
define('IS_AJAX_REQUEST', isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'); you can then conditionally run AJAX request only code using -
if(IS_AJAX_REQUEST) { // build and output the response to the AJAX request // typeically json_encode([some meaningful error or success data]); // stop php code execution die; }
-
mac_gyver's post in Row count based on the two condition was marked as the answer
OR you can use the complement -
select count(*) as CustomerActual, Month from sbms.customerdata WHERE EmpID='83201858' AND (VisitType = 'No Due' OR VisitDate !='') group by Month
-
mac_gyver's post in HTTP response code was marked as the answer
to get php to cause a http 500 status for fatal syntax/runtime errors, php's display_errors setting needs to be set to OFF. you would then want the log_errors setting to be set to ON, so that you have a record of what errors are occurring. also, php's error_reporting needs to always be set to E_ALL or a -1.
-
mac_gyver's post in Php random math test with form entry was marked as the answer
are you doing this as a learning exercise? what is your goal?
some pointers -
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 post method form processing should not attempt to detect if the submit button is set, there are cases where it won't be. instead, detect if a post method form was submitted before referencing any of the form data. keep all the form data as a set, in a php array variable, then operate on elements in this array variable throughout the rest of the code. trim all the input data before validating it, mainly so that you can detect if all white-space characters were entered. validate all the trimmed input data at once, storing user/validation errors in an array, using the field name as the main array index. after the end of the validation logic, if there are no errors (the array holding the user/validation errors is empty), use the input data. since all you are doing is comparing an input value with the correct answer, you can do this as part of the validation logic. if you were storing data in a database, authenticating a user, sending an email, ... you would put the code needed to perform these actions here. after using the input data, if there are no errors, perform a redirect to the exact same URL of the current page to cause a get request for the page. this will prevent the browser from trying to resubmit the form data should the page get reloaded or browsed back to. 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. if there are user/validation errors, the code will continue on to display the html document, display any errors, redisplay the form, populating the field values with any existing data, so that the user doesn't need to keep reentering values 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. there's a programming issue with the division operator and computers. this operation can result in a fractional part that cannot be represented exactly in a computer and then cannot be easily compared. you may want to test the answer produced is this case and limit the question/answer to those which only have whole integer answers, i.e. keep something like 9/3, but not 7/6, and also don't allow division by 0.
if you do all of that, except for handling the division cases, you would end up with code that looks like this -
<?php // initialization // the error related settings should be in the php.ini on your system error_reporting(E_ALL); ini_set('display_errors', '1'); session_start(); $post = []; // array to hold a trimmed working copy of the form data $errors = []; // array to hold user/validation errors // post method form processing if($_SERVER['REQUEST_METHOD'] === 'POST') { // inputs: number_entered, $_SESSION['answer'] // trim all the post data at once $post = array_map('trim',$_POST); // if any input is a array, use a recursive trim call-back function here instead of php's trim // validate inputs if($post['number_entered'] === '') { $errors['number_entered'] = 'You must enter a number'; } // note: this assumes that only integer answers are permitted (in the case of the division operator) else if((int)$post['number_entered'] !== (int)$_SESSION['answer']) { $errors['number_entered'] = "Incorrect guess<br>The correct<br>number was <b>{$_SESSION['answer']}</b> <br><img src='sorry-tryagain.png'><br>"; // since you are displaying the correct answer, you would want to generated a new question in this case? //unset($_SESSION['question']); } // if no errors, success if(empty($errors)) { $_SESSION['success_message'] = "<img src='you-win.png'><br><b>{$_SESSION['answer']}</b> IS THE<br>CORRECT GUESS!</b><br>"; // to continue, you would generated a new question //unset($_SESSION['question']); // redirect to the exact same url of the current page to cause a get request - PRG Post, Redirect, Get. die(header("Refresh:0")); } } // get method business logic - get/produce data needed to display the page // if there's no question/answer, generate one if(!isset($_SESSION['question'])) { $rand1 = rand(0, 9); $rand2 = rand(0, 9); $operator = array('*', '/', '+', '-'); $randoperator = $operator[rand(0, 3)]; switch ($randoperator) { case "+": $finaalvalue = $rand1 + $rand2; break; case "-": $finaalvalue = $rand1 - $rand2; break; case "*": $finaalvalue = $rand1 * $rand2; break; case "/": // note: this can produce a fractional number, which you must take care with when performing comparisons. // also division by zero. $finaalvalue = $rand1 / $rand2; break; } $_SESSION['question'] = "$rand1 $randoperator $rand2 = "; $_SESSION['answer'] = $finaalvalue; } // html document - this is an incomplete document. it only shows the necessary parts for the demonstration. ?> <?php // display any success message if(isset($_SESSION['success_message'])) { echo $_SESSION['success_message']; unset($_SESSION['success_message']); } ?> <?php // display any errors if(!empty($errors)) { echo "<p>".implode('<br>',$errors)."</p>"; } ?> <?php // display the form if(!empty($_SESSION['question'])) { ?> <form method="POST"> <br><b>Level 1<br>Do The Math</b><br><br> <?=$_SESSION['question']?> <input type="text" name="number_entered" value="<?=htmlentities($post['number_entered']??'',ENT_QUOTES)?>" autocomplete="off"><br><br> <input class="button" type="submit" value="Enter Guess"><br><br> </form> <?php }
-
mac_gyver's post in Error fetching booked seats: SyntaxError: Unexpected token '<', "<br /> was marked as the answer
lol.
all the javascript posted for this problem is unnecessary. upon the DOM being loaded/rendered, it's getting data that's known at the time of the request for the map2.php page (the value being sent to getBookedSeats.php is coming from a js variable that's being set to a php value echoed on the page.) this is just a roundabout wall of code and data churn.
here's a list of why this is not working -
1. you are making a POST request to getBookedSeats.php. the value won't be in any $_GET variable and adding an isset() won't make it work. all that did is hide the problem and caused the php code to be skipped over.
2. you are sending JSON encoded data to getBookedSeats.php. you would need to use the following to read and decode the data -
$json = file_get_contents('php://input'); $data = json_decode($json,true); 3. the value will then be in $data['screeningId'], because that's the name of the javascript variable holding the value that you are sending in the ajax fetch request, which is a value that is coming from php in the first place.
-
mac_gyver's post in Pass variables from page to page was marked as the answer
each page must enforce what the current user can do and see on that page, for a non-logged in user, for a logged in user, and for a logged in administrator.
if the current user is not logged in, they can only do and see what you have coded on that page for a non-logged in user to do and see. if they are a logged in user and the user ID in the URL is the same as the currently logged in user ID, they can perform actions like editing their own data and seeing all of their own data. if the currently logged in user is a administrator, he/she would be able to pick any user and be able to perform actions like editing that user's normal data and seeing all that user's normal data, and be able to edit/see additional data, such as permissions, edit history, site activity, ip history, ...
if you aren't already doing so, your login code should store the user id (autoincrement primary index) in a session variable to indicate who the currently logged in user is. if there is a logged in user, you would query on each page request to get any other user data, such as - username, email, permissions, ...
-
mac_gyver's post in I can't dynamically paginate with this php script was marked as the answer
you would need to use JSON.stringify({page:2}) for there to be a 'page' element in the decoded data.
-
mac_gyver's post in trying to create new connections after each login using PDO was marked as the answer
the user registration/login system on your web site is for the purpose of determining what a user can see and do on the web pages. this has nothing to do with database connections. it is your application code that is performing the database operations and the database connection credentials are making a connection to allow your application code to do its work. it is up to your application code to determine what database operations can be performed for any user to your site.
this is not how web applications are implemented. post or describe an example of something you think requires this, and a forum member will provide information on how to correctly implement it.
-
mac_gyver's post in problem - spaces in an associative array values from $_POST[] variable was marked as the answer
a space in an attribute value is a stop character, indicating the end of the value. to make this work, you need to enclose the attribute value in quotes (single or double.) you should actually always enclose attribute values in quotes, for consistency, even in those cases where they are not required.
however, the value attributes should be the organization ids, not the organization names, so that when you store the submitted data, you are storing the organization id. this will result in the least amount of data storage, the fastest queries, and allow you to edit the organization name or even display the organization name in a different language, without affecting the stored data.
next, for the 'required' attribute to work, the first option choice needs to have an empty value attribute and serve as a prompt, e.g. <option value=''>Select an Organization</option>.
lastly, there's no good reason to catch and handle an exception from a SELECT query. any error from this type of query, is either due to a programming mistake or the database server is not running, is not recoverable by the user, and the user doesn't need to know anything about its occurrence. simply do nothing in this case and let php catch and handle any database exception from a SELECT query.
-
mac_gyver's post in Multi-dimensional array issue. was marked as the answer
the reason to use the name as the array index is this allows you to directly test for and access the entry in the array, without needing to search through the entire array. this is the same reason that you index data in a database. this greatly simplifies and speeds up the code/query.
the reason your code doesn't work when the data starts at the zeroth index in the array is because array_search() returns the index if the value is found, but the zero index is a false value in a loose-comparison. array_search() returns an exact false value when the search fails to match a value. to test for a not-found condition, you must test if the returned values is exactly false, e.g. ===false. to test for a found condition, you must if the returned value is not exactly false, e.g. !==false.
-
mac_gyver's post in Data not showing properly in correct header was marked as the answer
the code is doing exactly what it was written to do, build a <tr><td>num</td></tr> for each number. if you want to produce a single <tr></tr>, you would need to add the opening <tr> before the start of the looping and add the closing </tr> after the end of the looping.