Jump to content

mac_gyver

Staff Alumni
  • Posts

    5,532
  • Joined

  • Days Won

    189

Posts posted by mac_gyver

  1. 1 hour ago, FXSniperGuy said:
    int response = WebRequest("GET", url, headers, 1000, post, result, resultHeaders);

    according to the 1st parameter, you are making a GET request. not a POST request.

    the main point of my 1st reply in this thread is that your code must validate (all) input data before using it. the account_no input is required (not an empty string), it must consist of a specific set of characters (decimal digits), and it might need to be a specific length or range of lengths. if it is not valid, it is an application error and you should setup and output a useful error message for each of these possible validation steps, and not attempt to use the input data. your first posted code and the code using the null coalescing operator are suppressing any helpful php errors (you would be getting an undefined index error) that would be pointing out that there is no expected entry in $_POST.

    so, you must change the php code to use $_GET['account_no'], and you must trim, then validate the input before using it. you would only get to the point of testing if the value is in the array of $valid_accounts if the input is valid.

  2. the reason for unusual operation is the ternary operator without a middle term, that the input is probably not what you expect, and php's type casting.

    when you leave out the middle term in the ternary operator, when the first term evaluates to true, the value used is whatever the first term is, which will be a boolean true due to the empty() statement.

    instead, your post method form processing code should -

    1. detect if a post method form was submitted before referencing any of the form data.
    2. detect if there is $_POST data (in case the post_max_size setting has been exceeded.)
    3. keep 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.
    4. trim all the input data, mainly so that you can detect if all white-space characters were entered.
    5. validate all inputs, storing user/validation errors in an array using the field name as the array index.
    6. after the end of the validation logic, if there are no user/validation errors, use the form data.
    7. after using the form data, if there are no user/validation errors, 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 trying to resubmit the form data should that page get browsed back to or reloaded. if you want to display a one-time success message, store it or a flag value in a session variable, then test for, display the success message, and clear the session variable at the appropriate location in the html document.
    8. if there are user/validation errors, the code will continue on to display the html document, where you will test for and display any errors, redisplay the form, populating fields with existing data, so that the user only needs to correct the invalid input(s) and can resubmit the form.
  3. that's because the current code is looping over the collection of class = s elements, and setting them all to the data that is returned -

    $(".s").each(function(k,v)
    	{
    		$(".s").html(data); //Populates the Standard drop-down having class .s with the data from the Standard function
    	}
    );

    you need to 'find' the correct class = s element and set it to the data that is returned. take a look at the jquery .parent() method to find a common parent container that all the related form fields are in, then use the jquery .find() method to find the element with class = s.

  4. the most immediate problem is that the onChange code for the Student select/option menu needs to get the correct sid value. by using $(".i") you are getting a collection of all the class = i elements, not the current one. using $(this) should work (all suggestions are untested unless indicated otherwise.)

    next, requiring the user to select each student from a select/option menu is a poor User Interface (UI) and provides a bad User eXperience (UX). you should simply list out each Student, the Standard select/option menu with any existing choice pre-selected, and the Attendance select/option menu with the Present choice pre-selected (the most common case.) the user should only need to make the fewest number of changes to the choices and submit the form, so, only make any Standard changes and select which students are Absent.

  5. here's a tested example showing the suggested method -

    <?php
    
    // fake a request start and end date - 1st May 2025 to 8th May 2025
    $start_date = "2025-05-01";
    $end_date = "2025-05-08";
    
    // exclude check-out date from length of stay/pricing
    $end = new DateTime($end_date);
    $end = $end->modify( '-1 day' );
    $end_date = $end->format("Y-m-d");
    
    // define default weekday and weekend prices
    $default_price = [];
    $default_price['wd'] = (object)['name'=>'Standard Weekday','price'=>'60'];
    $default_price['we'] = (object)['name'=>'Standard Weekend','price'=>'80'];
    
    // define seasonal prices
    $seasonPrices = [];
    $seasonPrices[] = (object)["SeasonName"=>"Spring half-term",	"SeasonStart"=>'2025-02-21', "SeasonEnd"=>'2025-03-03', "SeasonPrice"=>'100'];
    $seasonPrices[] = (object)["SeasonName"=>"Easter",				"SeasonStart"=>'2025-04-11', "SeasonEnd"=>'2025-04-27', "SeasonPrice"=>'100'];
    $seasonPrices[] = (object)["SeasonName"=>"May Day",				"SeasonStart"=>'2025-05-02', "SeasonEnd"=>'2025-05-05', "SeasonPrice"=>'110'];
    $seasonPrices[] = (object)["SeasonName"=>"Summer half-term",	"SeasonStart"=>'2025-05-23', "SeasonEnd"=>'2025-06-01', "SeasonPrice"=>'110'];
    $seasonPrices[] = (object)["SeasonName"=>"Summer",				"SeasonStart"=>'2025-07-18', "SeasonEnd"=>'2025-09-01', "SeasonPrice"=>'120'];
    $seasonPrices[] = (object)["SeasonName"=>"Autumn half-term",	"SeasonStart"=>'2025-10-24', "SeasonEnd"=>'2025-11-02', "SeasonPrice"=>'110'];
    $seasonPrices[] = (object)["SeasonName"=>"Christmas",			"SeasonStart"=>'2025-12-19', "SeasonEnd"=>'2026-01-04', "SeasonPrice"=>'120'];
    
    
    // start by creating an array of request dates filled with the default weekday price
    $request = array_fill_keys(Cal::bookedArray($start_date, $end_date), $default_price['wd']->price);
    
    // get the weekend prices between the request start and end dates
    $weekend_prices = get_we_prices($start_date,$end_date,$default_price);
    
    // put the weekend prices into the array
    $request = array_replace($request,$weekend_prices);
    
    // get the seasonal prices between the request start and end dates
    $season_prices = get_prices($start_date,$end_date,$seasonPrices);
    
    // put the seasonal prices into the array
    $request = array_replace($request,$season_prices);
    
    // examine the result
    echo '<pre>'; print_r($request); echo '</pre>';
    
    echo "Total: " . array_sum($request);
    
    
    // get the weekend prices between the request start and end dates
    function get_we_prices($start_date,$end_date,$default_price)
    {
    	$dates = [];
    
    	// find 1st Sat of the start date
    	$date = date('Y-m-d',strtotime("first sat of $start_date"));
    
    	// generate all the Saturdays between the start and end dates
    	$start = new DateTime($date);
    	$end = new DateTime($end_date);
    	$end = $end->modify( '+1 day' ); // include the end point
    
    	$interval = new DateInterval('P7D'); // 7 days
    	$daterange = new DatePeriod($start, $interval ,$end);
    
    	foreach($daterange as $date){
    		$dates[$date->format("Y-m-d")] = $default_price['we']->price;
    	}
    
    	// same as above but for Sundays
    	// find 1st Sun of the start date
    	$date = date('Y-m-d',strtotime("first sun of $start_date"));
    
    	// generate all the Sundays between the start and end dates
    	$start = new DateTime($date);
    	$end = new DateTime($end_date);
    	$end = $end->modify( '+1 day' ); // include the end point
    
    	$interval = new DateInterval('P7D'); // 7 days
    	$daterange = new DatePeriod($start, $interval ,$end);
    
    	foreach($daterange as $date){
    		$dates[$date->format("Y-m-d")] = $default_price['we']->price;
    	}
    	return $dates;
    }
    
    // get the seasonal prices between the request start and end dates
    function get_prices($start,$end,$seasonPrices)
    {
    	$result = [];
    	foreach($seasonPrices as $row)
    	{
    		// keep entries that match the requested start/end
    		// SeasonEnd >= start AND end >= SeasonStart
    		if($row->SeasonEnd >= $start && $end >= $row->SeasonStart)
    		{
    			// expand this entry and only keep the values between the start and end (for the case of spanning the start or end date)
    			$entry = Cal::bookedArray($row->SeasonStart, $row->SeasonEnd);
    			foreach($entry as $date)
    			{
    				// if date between start and end, keep it
    				if($start <= $date && $date <= $end)
    				{
    					$result[$date] = $row->SeasonPrice;
    				}
    			}
    		}
    	}
    	return $result;
    }
    
    class cal
    {
    	// produce a date range from start to end
        public static function bookedArray($start,$end)
        {
    		$start = new DateTime($start);
    		$end = new DateTime($end);
    		$end = $end->modify( '+1 day' ); // include the end point
    
    		$interval = new DateInterval('P1D'); // 1 day
    		$daterange = new DatePeriod($start, $interval ,$end);
    
    		$dates = array();
    		foreach($daterange as $date){
    			$dates[] = $date->format("Y-m-d");
    		}
    		return $dates;
    	}
    }

     

  6. you are constantly changing data types, names, and adding features. this makes writing code extremely wasteful. you need to define everything possible, before you write any code. you also need to define what inputs you have for any operation, what processing you are going to do based on those inputs, and what result or output you are going to produce.

    as to a simple, straightforward solution, i recommend that you read the suggestions in the previous thread, about building an array with the dates as the array index and the values being whatever you are trying to produce. in that thread, the array was for the special events for the calendar being displayed. in this thread, the array is for the requested date range and the price for each day in that range.

    the inputs to this code are the start and end dates of the request, the standard week day and week end prices, and the seasonal pricing.

    you would start by creating an array using the request range of dates as the array index. you would fill in the array with the week day and week end prices. you would then replace any price that matches the seasonal pricing data. when you are done, you will have an array with all the days of the request as the index, and the price for each day. you can then just sum the prices to get the total.

    here's a procedural function that gets the seasonal prices, given the request start and end date -

    // get seasonal prices between start and end date
    function get_prices($start,$end,$seasonPrices)
    {
    	$result = [];
    	foreach($seasonPrices as $row)
    	{
    		// keep entries that match the requested start/end
    		// SeasonEnd >= start AND end >= SeasonStart
    		if($row->SeasonEnd >= $start && $end >= $row->SeasonStart)
    		{
    			// expand this entry and only keep the values between the start and end (for the case of spanning the start or end date)
    			$entry = Cal::bookedArray($row->SeasonStart, $row->SeasonEnd);
    			foreach($entry as $date)
    			{
    				// if date between start and end, keep it
    				if($start <= $date && $date <= $end)
    				{
    					$result[$date] = $row->SeasonPrice;
    				}
    			}
    		}
    	}
    	return $result;
    }

    you can use array_replace() with the array you are building and the array returned by the above function to replace the standard prices in that array with the seasonal prices.

  7. for the posted information, the requested date range of 2025-05-06 to 2025-05-22 doesn't match any of the $seasonPrices data. It starts one day after the end of the May Day range and ends one day before the start of the Summer half-term range. it should use the base/default price for every day.

    since you are using a standard date format, you can directly perform date comparisons by order, as mentioned in the previous thread. you can directly compare a date to the $seasonPrices SeasonStart and SeasonEnd values to find if it is between a date range.

    i would write a function/class-method that accepts a date input, loops over the $seasonPrices data, returns the first price that is between the SeasonStart and SeasonEnd values, or returns zero (or some other easily detected value) if it reaches the end without finding a match.

    as a procedural function, something like - 

    function get_price($date,$seasonPrices)
    {
    	foreach($seasonPrices as $row)
    	{
    		// date between start and end
    		if($row->SeasonStart <= $date && $date <= $row->SeasonEnd)
    		{
    			return $row->SeasonPrice;
    		}
    	}
    	// no match
    	return 0;
    }

     

  8. what exactly is the single $booked data for vs the array of $bookings?

    i have the following recommendations -

    1. use a standard yyyy-mm-dd date format. if/when you store the data in a database, this will be necessary to allow queries to compare dates by order. it will also make your current code easier to understand.
    2. the bookings data should be stored in a database table.
    3. you should have a start_date and an end_date input to the code on this page, so that the code will work for any date range, from multiple years down to a single month.
    4. you are looping over the $res array for every date being displayed, currently for the whole year. i recommend that you instead produce an array using the date as the array index, and the stored value indicating what is occurring on that date, limited to be between the start_date and end_date mentioned above. as you are producing the output, you can simply test, no loop required, if there is an entry in this array, using isset(), then get the stored value if there is one, and use it to control what you output for the date.
    5. with a little conditional logic, you don't need separate logic to deal with the first (partial) week, then repeat that logic for the remainder of each month.
    6. if you use php's short-open-print tag and leave out the ; right before a closing tag, you can use simple syntax like - <?=$some_var?> to output values in the markup.
    7. use php's DateInterval and DatePeriod to expand the booking date ranges.

    here's example code for item #7 -

    $start = new DateTime($start);
    $end = new DateTime($end);
    $end = $end->modify('+1 day'); // include the end point
    
    $interval = new DateInterval('P1D'); // 1 day
    $daterange = new DatePeriod($start, $interval ,$end);
    
    $dates = array();
    foreach($daterange as $date){
        $dates[] = $date->format("Y-m-d");
    }

     

  9. this is a 'contact us' form. for the client to be able to receive and read the emails, they must be sent to an email address that the client has access to. is this To: address at their web hosting (mail_box_name@their_doman_name) or somewhere else, such as a gmail address ([email protected])?

    i didn't find any information about using unauthenticated smtp at hostgator, but if this is possible, it would require an email account to be created within cpanel for the domain name that's hosted at hostgator, then this email address would be used as the From: address. the To: address can then be anywhere, even the same as the From: address.

    everything I saw about hostgator email used smtp authentication, which needs an email account to be created within cpanel for the domain name hosted at hostgator, with the password known. if they have an email address at their domain name, but the password is not known, it needs to be reset to a known value within cpanel. for php to be able to send to (the From: and To: addresses would be the same and be the email account at hostgator) or through (the From: address would be the email account at hostgator and the To: can be anywhere) the mail server at hostgator, you would use the hostgator server settings. see the cPanel Emails, Outgoing Email Settings at this link - https://www.hostgator.com/help/article/email-connection-settings#cpanelemails

    you can, using the phpmailer/swiftmailer class, send an email to or through an external mail server, such as gmail, but this requires the username/password of the account on that external mail server.

  10. the php mail() function and the phpmailer/swiftmailer classes allow php to act like an email client, to send an email to a mail server. when you don't use smtp authentication against a mail box on that mail server, that mail server must be configured to "trust" the (ip address of the) web server where the php code is running. typically, a local mail server at web hosting is configured this way. if you are not getting any errors back, it is likely that the locally configured mail server is setup to silently accept emails, regardless of it it intends to send them, without returning an error.

    where are you running this php code at? a localhost development system or web hosting, and if on web hosting is the domain name in the From: address hosted at the sending web server or is it hosted at some other location?

  11. telling us that something doesn't work is pointless. we are not sitting next to you and didn't see what symptom or error you got that leads you to believe something didn't work. you must tell or show us what result you got and what the expected result should be.

    do you have php's error_reporting set to E_ALL and display_errors set to ON, preferably in the php.ini on your system, so that php will help you by reporting and displaying all the errors it detects?

    have you checked in the browser's developer tools, console tab for errors?

    you should use 'require' for things your code must have. require/include are not functions. the () around the path/filename do nothing and should be removed.

    $_GET['email'] is an input to your code. it may not exist. you need to use isset() to prevent errors when it doesn't exist and you must trim, then validate it before using it, when it does exist.

    the two session variables are also inputs to your code. they may not exist. you need to use isset() to prevent errors when they don't exist.

     

    • Like 1
  12. the most common reason for a password_hash()/password_verify() to fail is because the database column is not long enough to hold the hashed value.

    another common reason are programming mistakes in the form/form processing code and a lack of server-side validation that results in the hash value not actually being from the password that was submitted in the registration code, or the value being used in the login code not being what you think it is. your post method form processing code should always trim the input data, mainly so that you can detect if all white-space characters were entered, then validate all inputs before using them.

    • Like 2
  13. so, i found the problem, with the help of php's error reporting, though the problem is in javascript.

    you are echoing blocks of static html/javescript using php's heredoc syntax. when I made the test page i used, the javascript was simply in-line.

    you are using template literals with embedded expressions in the javascript, e.g. ${some_var}. however, in php's heredoc syntax, this is the syntax for a php variable. so, php is putting the last value for any of its variables with the some_var name into the echoed javascript. how i found this is that the embedded expressions in the openUpdateTotalsPopup() javascrpt, for ${width}, ... produced undefined php variable errors.

    the simplest fix would be to use php's nowdoc syntax. the correct solution would be to NOT echo blocks of static html/javascript, which I see i wrote  about in one of your previous threads.

    • Great Answer 1
  14. a test page works for me (uses the correct quoteItemId matching the clicked button, both in the javascript and in the php code) in chrome, edge, and firefox, but i don't have all the code on your page. the only changes i made to the //loop code is to add a <table> tag before it and comment out the 5 lines with number_format() calls, since i didn't want to make up fake data to loop over for these. do you have some event listener for 'buttons' that could be affecting this? this is acting like some broken markup is causing all those buttons to call the last openEditQuantityPopup(...), instead of the correct one or all the buttons are being clicked and you are seeing the end result from the last such operation. i would console.log the quoteItemId value inside the openEditQuantityPopup() faction, so that you can see how many times it gets called, and with what value as an input.

    in the end, you will need to post all the code on that page that's necessary to reproduce the problem.

  15. probably this -

    8 hours ago, mac_gyver said:

    have the session cookie parameters setup so that they match the url for both pages;

    when you browse to main.php (or is it index.php), do you include the www. on the URL or not and does the URL you use for login.php always include the www. or not?

    also, since you have shown the full login code, the redirect upon successful completion of the form processing code MUST be to the exact same URL of the login page. this is so that the browser won't attempt to resubmit the form data should that page get browsed back to or reloaded, where someone can use the browser's developer tools to look at what the form 'payload' is and see what the email and password are for the last person to submit the form.

  16. sending a session variable from one page to another involves - having a working, error free, session_start() statement on both pages; have a server properly setup with a folder to hold the session data files; have the session cookie parameters setup so that they match the url for both pages; assign a value to a session variable on one page, that you are sure isn't getting cleared after you have set it, and test for and use the session variable on the second page.

    except for very overt symptoms, there is not a 'one symptom' is always caused by 'one thing' relationship in programming. if you are expecting someone here to be able to directly tell you what the cause of this problem is, you are mistaken. there are too many possibilities.

    when something in programming doesn't work, you must find where your code and data are doing what you expect and where they are not. the problem lies between those two points. if all you have done is run your code and notice that the output doesn't exist, you haven't narrowed down the problem.

    the first step in narrowing down a programming problem is finding any errors that the language is reporting. to do this, you must setup the error related settings and verify that they are actually the values that you have set them to. in your last thread, you would have been getting a fatal run-time error to alert you to one of the problems in the code, but you didn't indicate you were getting any errors. this means that php's error related settings (error_reporting, display_errors, and log_errors) are not setup so that php will help you. once you have set the error settings as i stated, and comment out the redirect, this will narrow down the possibilities, by either producing a error pointing to a problem or if doesn't produce an error this points to where to look at next.

    • Great Answer 1
  17. 53 minutes ago, rwahdan1978 said:

    I am not getting any errors

    do you have php's error_reporting set to E_ALL and display_errors set to ON, preferably in the php.ini on your system, and you have confirmed using a phpinfo(); statement in your code that these are the values php is using?

    you can temporarily put these settings into your code, on both pages, immediately after the first opening <?php tag.

    next, if php's output_buffering is on, and you have a redirect in your code, it will discard any non-fatal errors or debugging output you have added. temporarily comment out the header() redirect so that you can see if there are any php errors up to that point.

    do you have a session_start() statement in the login.php code?

    and as is sometimes given, here's a list of programming practices for the posted code -

    1. don't use variables ending in numbers. you should completely deal with the result from one block of code before performing another operation, and there's no need to use or to keep track of numbered variables.
    2. you should list out the columns you are SELECTing in a query.
    3. you need to use prepared queries, instead of putting dynamic values directly into sql query statements. if it seems like using the mysqli extension is overly complicated and inconsistent when dealing with prepared queries, it is. this would be a good time to switch to the much simpler and better designed PDO extension.
    4. you need to use php's password_hash() and password_verify() for password handling, i.e. don't store passwords as plain text.
    5. the value you store in a session variable upon successful login should be the user's id (autoincrement primary index), and it should be named user_id or similar. you should query on each page request to get any other user data, such as the username or permissions. this is so that any changes made to this other user data take effect on the very next page request after is has been changed, without requiring the user to log out and back in again.
    6. the header() redirect needs an exit/die statement to stop php code execution. you could, for example, have some code following the posted code, that's clearing the session variable.
  18. you should do this directly in the sql query statement. MySql has a large number of datetime functions. what is the format of the 'date' column in the database table?

    if you do this in the php code, you must either compare objects with objects or compare formatted strings with formatted strings, with the same exact format, in a left-right order from most significant field (year) to least significant field (day.)

    the current php code produces a fatal error at the date_diff() method call, because the 2nd argument must be an object, not the $data["date"] string.

    $date is a datetime object, you cannot directly compare it with the $data["date"] string. you either must use the datetime ->format() method to produce a formatted string from the $date object, or you must create a datetime object from the $data["date"] string.

    $diff in a dateinterval object. to get the number of days, you would need to use the dateinterval ->format() method with the '%a' format specifier to produce the whole number of days.

    • Like 1
  19. 45 minutes ago, mac_gyver said:

    if you want the name data and a count for each last name, i would index/pivot the data using the last name as the main array index when you -

     

    12 minutes ago, phppup said:

    generated (it) from other PHP coding.

    if you post the code generating the array, someone could post an example, instead of just writing about how to do it.

  20. if all you want is a COUNT() per last name, you can do that in the query that's getting the data, with a GROUP BY last_name term.

    if you want the name data and a count for each last name, i would index/pivot the data using the last name as the main array index when you fetch the data, from wherever it is coming from.

  21. 3 hours ago, admien said:

    currently my query is as follows:

    wildcard characters % and _ are only used with a LIKE comparison in a query. if your first query produces any result, it means that you managed to store the % characters in with the data in the database table.

    3 hours ago, admien said:

    but after I change to this following:

    preg_match() is a php function. you cannot put it a query to match data in the database table. this query would be producing an sql error. are you using exceptions for errors for the sql queries (this is the default setting now in php8+)?

    MySql does have regular expressions. see this link - https://dev.mysql.com/doc/refman/8.4/en/regexp.html

    the regex pattern you came up with matches a string with exactly 3 decimal digits. your stated example is to match 2 digits. which is it and can it vary?

    what exactly is the overall top-level task you are trying to accomplish, because using regex pattern matching is slow and cannot use indexes, so is a poor choice when you have large amounts of data?

    lastly, do NOT put dynamic data values directly into sql queries, where any sql special character in a value can break the sql query syntax. use a prepared query instead. if it seems like using the mysqli extension is overly complicated and inconsistent, especially when dealing with prepared queries, it is. this would be a good time to switch to the much simpler and better designed PDO extension.

    • Great Answer 1
×
×
  • 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.