-
Posts
5,545 -
Joined
-
Days Won
194
Everything posted by mac_gyver
-
how are you invoking this? is the code looping over data, and if so what is the code with the looping? approximately how long was the execution time for each of the segments of sent emails? you are using exceptions for errors. when using exceptions, none of the discrete error checking logic will ever get executed upon an error and should be removed. when using exceptions for errors, unless you are doing something special with the error information, you should not catch and handle exceptions in your code. instead let php catch and handle any exception, where php will use its error related settings (error_reporting, display_errors, and log_errors) to control what happens with the actual error information, via an uncaught exception error. php's error_reporting should always be set to E_ALL. if you are invoking this via a browser, you should set display_errors to ON, so that any php detected errors (which will include any uncaught exceptions) will be displayed. if you are invoking this via a cron job/scheduled task, you should set log_errors to ON, so that any php detected errors will be logged.
-
are you getting an error from the browser about redirecting (it would be a http xxx error number) or is this your - ' Illegal redirection from ...' message? note: require is not a function. the () around the path/filename do nothing and should be removed.
-
Uncaught Error: Value of type null is not callable
mac_gyver replied to ppowell777's topic in PHP Coding Help
this is unnecessary and is hiding simple typo mistakes. except for unchecked checkbox/radio fields, all other fields will be set/will exist after a form has been submitted. after you have detected that a post method form has been submitted, these 'always set' fields will be set, regardless of what they contain. you only need check if a field is set for checkbox/radio fields. your post method form processing code should - detect if a post method form was submitted - if($_SERVER['REQUEST_METHOD'] === 'POST'). detect if there is $_POST (or $_FILES) data. there may not be if the total size of the submitted form data is greater than the post_max_size setting. 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. trim all the input data, mainly so that you can detect if all white-space characters were entered. validate all inputs separately, storing user/validation errors in an array using the field name as the 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 submitted form data. after 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. 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 dynamically validate and process form data, and dynamically produce the corresponding form, create an array with the expected form fields, using the field name as the array index, with an array for each field with a label element, field data type, validation rules, and processing rules. you can then loop over this defining array and call general-purpose code to handle each field. -
Unable to pass variable into function inside array_map()
mac_gyver replied to ppowell777's topic in PHP Coding Help
the anonymous function has local variable scope, like any php function. you can add use ($total) to the definition to make the variable available inside the function - $resultsCalcArray = array_map(function($votes) use ($total) { as to the posted code - the input call-time parameter should only be the $id and you should only call this function after you have validated the $id. this function should also have an existing pdo connection as an input call-time parameter. it is the wrong responsibility for this function to make a database connection. there's no point is defining and initializing $conn, $stmt, and $rs. don't use the global keyword to get data into or out of a function. this is not general purpose and results in spaghetti code that is hard to debug problems in. all input data to a function should be supplied as call-time parameters and the function should return the result it produces to the calling code. if you set the default fetch mode to assoc when you make the database connection, you won't need to specify it in each fetch statement. fetchAll() won't ever return a null, so the !is_null() test will never fail and should be removed. since you are only operating on the 'kount' column, that is the only thing the query should SELECT. if you have some reason to select other columns, you can build (or add to) the $resultsCalcArray from just the 'kount' column by using array_column(). you can directly get the total of the $resultsCalcArray 'kount' values by using array_sum(). no in-line code after the throw $e; statement will ever get executed and should be removed. there's generally no point in freeing up result sets, closing prepared query handles, or closing database connections in your code, at all, and there's certainly no point in doing this inside a function, since all those things get destroyed when the function call ends. -
ongoing problem with missing session variables
mac_gyver replied to Paul-D's topic in PHP Coding Help
this is apparently the previous related thread - https://forums.phpfreaks.com/topic/321226-pdo-error-on-function-since-a-migration-to-pso at that time, the "Statement.php Lines 14-17" were in a file being included before the session_start() and would never have found the session variable set. i'm going to guess that the "fetch() on null error" was occurring on every page request? you have since moved these lines to statement.php. after reviewing the code you posted previously, if this problem is only occurring occasionally, it is likely due to some auto logout logic, a page auto reload occurring exactly at the point of being logged out, and a timing/race condition in the logic, due to all these session variables. at the risk or repeating myself. the only piece of user related data you should store in a session variable upon successful login is the user id (autoincrement primary index.) you should query on each page request to get any other user data, so that any changes made to this other user data take effect on the very next page request. if you cannot determine the cause of this problem, you will need to post all the current code for statemnt.php, everything being included (you should use require for things your code must have) by statement.php, everything those files are including, and index.php (and everything it includes and everything those files include) since it is involved in the redirects and could be redirecting back to statement.php, less any database credentials or sensitive site information (which should be in a configuration .php file), for anyone here to be able to help. -
ongoing problem with missing session variables
mac_gyver replied to Paul-D's topic in PHP Coding Help
obviously it is not. you must determine what the non-printing value actually is and find where in your code it's being set to that value. you either have an assignment, instead of a comparison, like i already wrote, or you have some code that's running, such as after a redirect, where you didn't stop php code execution, and it's assigning a value that when echoed is an empty value. -
ongoing problem with missing session variables
mac_gyver replied to Paul-D's topic in PHP Coding Help
$_SESSION['CounterValue'] may be set, but doesn't contain what you think. either use var_dump() on $view to display what it is or use var_export(), with the 2nd parameter set to true, when you supply it to the StoreData() call to cause it's value (empty string '', null, false, or true) to be used. best guess is you have some code assigning a value to it, using one =, instead of testing the value in it using two == or three ===. -
Strange problem with main.js file and index.php .
mac_gyver replied to Ali_Hegazy's topic in Javascript Help
browses cache content by default. on your development system, you must tell the browser to not cache the content of file types that you will be changing often during the development process. your web server likely/should already have a setting that outputs headers for .php files that tell the browser to not cache the content for the request. you need to add the .js and possibly .css/.html file extensions to this. for an apache web server, there would be an entry in the .htaccess file that looks like - <IfModule mod_headers.c> <FilesMatch "\.(php|js|css|html)$"> Header set Cache-Control "no-store, no-cache, must-revalidate, max-age=0" Header set Pragma "no-cache" </FilesMatch> </IfModule> -
automatically logout deleted user with ajax no refresh
mac_gyver replied to ssscriptties's topic in PHP Coding Help
the code for every page (http request) must enforce what the current user can do or see on that page. if you do what i wrote in one of your recent threads - the code performing the admin actions will find that the current user is either not logged in, doesn't exist, or no longer has a role that allows access to the code on that page and the user will be prevented from performing any action. -
remember me login system and how to make it work with cookies
mac_gyver replied to ssscriptties's topic in PHP Coding Help
here are some implementation practices - the form processing code and form should be on the same page. by putting them on separate pages, you are creating a lot of extra code. by only validating one input at a time and not having the form fields 'sticky', you are providing a poor User eXperience (UX). by storing the 'login_attempts' and 'lockout_time' in session variables, a nefarious user/bot can get unlimited new login attempts by simply not propagating the session id cookie between requests. you must store this data persistently on the server in a database table. the only user related value you should store in a session variable upon successful login is the user id (autoincrement primary index.) you should query on each page request to get any other user data, so that any changes made to the user data will take effect on the very next page request, without requiring the user to log out and back in again. the way a 'remember me' operation should be implemented is that if the remember me checkbox is checked, at the point of successfully verifying the user's credentials, generate a unique token, store that in a cookie and in a database 'remember me' table that also includes the user id, and the current datatime, for a determining token expiration. on any page request, if the remember me token cookie is set, query to find a matching row in the remember me table. if there is a row and the token is not timed out, use the user id from that row to set the session variable that identifies who the logged in user is. the rest of the code then uses this value in the session variable, just like it was set in the login form processing code. the registration process, unless being performed by an administrator, which your code is not doing, should not include the role. the role should not be something that the user can decide when they register. modern php (8+) uses exceptions for database statement errors by default - connection, query, prepare, and execute. any discrete logic you currently have testing the result of these statements should be removed since it will never get executed upon an error. both the username and email must be unique or you should only use the email and forget about a separate username. the correct way of determining if a unique value already exists in a database table is to define the column(s) as a unique index, just attempt to insert the data, and detect in the exception catch logic for the insert query if a duplicate index error (number) occurred. any form processing code should keep for the form data as a set, in an array variable, then operate on elements in this array variable throughout the rest of the code. i.e. don't write out a line of code copying every $_POST variable to a discrete variable. you need to trim ALL the user supplied inputs, mainly so that you can detect if all white-space characters were entered, before validating the data. you need to use an array to hold user/validation errors, and validate all the inputs at once, storing the errors in the array using the field name as the array index. after the end of the validation logic, if there are no errors (the array will be empty), use the submitted form data. in the login validation logic, all you really care about is that the required inputs are are not empty strings, after being trimmed. by providing additional feedback to a nefarious user/bot, you are helping narrow down the values they need to try.- 1 reply
-
- 1
-
-
- cookies
- remember me
-
(and 1 more)
Tagged with:
-
the above line is missing any { }, so the only line of code that gets executed for an is_dir() is the - echo '<strong>'.$directory .'</strong> <br>'; all the rest of the lines get executed regardless of what $directory is. i recommend that you always format your code so that you can see when it is actually doing.
-
if you use a cookie or the session to hold this data, it can be bypassed by simply deleting the cookie or not propagating the cookie or session id cookie between requests. you must store this data persistently on the server, in a database table. next, you are not trying to lock the account, you are preventing login attempts for an account, from a device (client type) and its location (ip). if you actually lock the account, it will allow someone to log out and lock out a legitimate user, by just making a bunch of bad login attempts for an account. once you have stored the data in a database table, on each login attempt, you would query to find if, how many, and how long ago the bad login attempts were for the account, for the device (client type) and its location (ip). If the current time is greater than the time limit you have chosen from the last bad attempt, you would process the login attempt.
-
have you examined what the $_GET data is? echo '<pre>'; print_r($_GET); echo '</pre>';
-
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.
-
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 - detect if a post method form was submitted before referencing any of the form data. detect if there is $_POST data (in case the post_max_size setting has been exceeded.) 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. trim all the input data, mainly so that you can detect if all white-space characters were entered. validate all inputs, storing user/validation errors in an array using the field name as the array index. after the end of the validation logic, if there are no user/validation errors, use the form data. 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. 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.
-
i recommend that you validate the resulting web page at validator.w3.org
-
i don't know what this will look like incorporated into the yii syntax, but this is what i used with just jquery/javascript - // the element that was clicked on var e = $(this); $.post("index.php?r=attendancereport/standard&aid="+$("#academicyearid").val()+ "&ascid="+$("#ascid").val()+"&sid="+e.val(),function(data){ e.closest('tr').find('.s').html(data); });
-
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.
-
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.
-
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; } }
-
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.
-
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; }
-
what exactly is the single $booked data for vs the array of $bookings? i have the following recommendations - 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. the bookings data should be stored in a database table. 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. 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. 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. 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. 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"); }
-
send email with phpmailer not using smtp issue
mac_gyver replied to ianhaney10's topic in PHP Coding Help
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.