More Reusable PHP Functions

Several years ago I wrote an article about creating functions that can be easily reused in multiple projects (read that article here, if you're so inclined) and I thought it would be a good idea to include a few of our more recently developed favorites.

// Upload file with unique name
// $filename = filename to be uploaded; $tmpname = PHP created temporary name for the uploaded file; $path = full path of the file on the file system
function unique_upload($filename, $tmpname, $path) {
 
	// Upload the file
	$newfilename = preg_replace('`[^a-z0-9-_.]`i','', $filename);
	// Ensure directory exists -- if not, create it
	if (!is_dir(UPLOAD_PATH . $path)) {
		mkdir(UPLOAD_PATH . $path, 0755, true);
	}
	$pathandfile = UPLOAD_PATH . $path . '/' . $newfilename;
	$counter = 1;
	while (file_exists($pathandfile)) { // Check for unique name
		($counter == 1) ? $newfilename = $counter . '_' . $newfilename : $newfilename = $counter . stristr($newfilename, '_');
		$pathandfile = UPLOAD_PATH . $path . '/' . $newfilename;
		$counter++;
	}
	// Move the file to the proper folder
	move_uploaded_file($tmpname, $pathandfile);
	// Return values necessary to calling function
	$uploadvalues = array('filename' => $newfilename, 'doctype' => $doctype, 'pathandfile' => $pathandfile);
	return $uploadvalues;
}

When uploading files you'll most likely want to make sure that the filenames are unique -- else you run the risk of overwriting an existing file. This function accepts a filename, temp filename (created in the $_FILES collection as I'll illustrate below) and a path to upload the file. It then cycles through the file system in the active path and looks for a matching name. If it finds one, the function prepends a number and an underscore to the filename to make sure that it's a unique name, saves the file, and then returns the new, unique name. Call the function like so:

$uploadedfile = unique_upload($_FILES['uploaded_file_from_form']['name'], $_FILES['uploaded_file_from_form']['tmp_name'], '/public_html/files');

The $uploadedfile variable will then contain the new unique file name.

Ever have to reset your password on a website? If so, you're probably familiar with the email that you receive which contains a link with a long reset code that you have to click which effectively confirms that you have access to your email address on file, thereby allowing you to reset your password. Here's how to create those reset codes:

// Helper function to create secure random numbers
// $min = minimun length; $max = maximum length
function crypto_rand_secure($min, $max) {
	$range = $max - $min;
	if ($range < 0) return $min; // not so random...
	$log = log($range, 2);
	$bytes = (int) ($log / 8) + 1; // length in bytes
	$bits = (int) $log + 1; // length in bits
	$filter = (int) (1 << $bits) - 1; // set all lower bits to 1
	do {
		$rnd = hexdec(bin2hex(openssl_random_pseudo_bytes($bytes)));
		$rnd = $rnd & $filter; // discard irrelevant bits
	} while ($rnd >= $range);
	return $min + $rnd;
}
 
// Creates the random password reset token which is then saved and emailed to the user
// $length = token length in bytes (always 64)
function get_reset_token($length = 64){
	$token = "";
	$codeAlphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	$codeAlphabet.= "abcdefghijklmnopqrstuvwxyz";
	$codeAlphabet.= "0123456789";
	for ($i=0; $i < $length; $i++) {
		$token .= $codeAlphabet[crypto_rand_secure(0, strlen($codeAlphabet))];
	}
	return $token;
}

The first function (crypto_rand_secure) randomly selects a number from, in this case, 0 to the sum of all of the uppercase letters in the alphabet, the lowercase letters of the alphabet, and the numbers 0-9 (a total of 62 possible characters.) The second function (get_reset_token) is the one that does the generation as it calls the first function 64 times, picking a character from those alphabet sets each time, and then saves the combined random characters as the $token variable which is in turned sent back to the calling script. With that, it's a simple matter of sending a link via email that would look something like this: <a href="http://yoursite.com/reset?code=<?php print get_reset_token(); ?>">Click to reset your password</a>

In several of our projects we've needed to send alerts to various users when tasks are overdue, or when documents are expiring, or we've created reports that check to see when upcoming events occur, and we needed the ability to exclude weekends from those date calculations. As an example, if a client wants to know how many projects they'll have on their dashboard in the coming week they'd need a report that would calculate the items that would be due in the next business days, but not including weekends. Here are the functions we use for that calculation:

// Helper function to check two dates for weekend comparison
// $compare1 = earlier date; $compare2 = later date
function check_same_day($compare1, $compare2) {
  if (date('Ymd', $compare1) == date('Ymd', $compare2)) {
    return true;
  }
  else {
    return false;
  }
}
 
// Helper function to return the time difference
function timeIntersect($time1_from, $time1_to, $time2_from, $time2_to){
  $overlap = min($time1_to, $time2_to) - max($time1_from, $time2_from);
  if ($overlap < 0) {
    return false;
  }
  else {
    return $overlap;
  }
}
 
// Determines difference in time between two timestamps, excluding weekends
// $timestamp1 = earlier time; $timestamp2 = later time
function get_time_without_weekends($timestamp1, $timestamp2){
 
  // Double check $timestamp2 is after and not before $timestamp1
  if ($timestamp2 < $timestamp1) {
    return false;
  }
  // All-time difference
  $difference = $timestamp2 - $timestamp1;
  // This will hold the number of weekend-seconds that needs to be subtracted
  $weekendSubtract = 0;
  $keepLoop = true;
  $currentDayStart = $timestamp1;
 
  while ($keepLoop) {
    if (check_same_day($currentDayStart, $timestamp2)){ 
      $keepLoop = false; // exit at the last day
      $currentDayEnd = $timestamp2;
    }
    else {
      $currentDayEnd = strtotime('tomorrow 00:00', $currentDayStart);
    }
 
    switch (date('w', $currentDayStart)){ // 0 - Sunday, 1 - Monday, 5 - Friday, 6 - Saturday
      case 5: // Friday
        $weekendSubtract += timeIntersect($currentDayStart, $currentDayEnd, strtotime('this Friday 20:00', $currentDayStart), strtotime('this Saturday 00:00', $currentDayStart));
        break;
      case 6: // full weekend days, 0 - Sunday, 6 - Saturday
      case 0:     
        $weekendSubtract += $currentDayEnd - $currentDayStart;
        break;
      case 1: // Monday
        $weekendSubtract += timeIntersect($currentDayStart, $currentDayEnd, strtotime('this Monday 00:00', $currentDayStart), strtotime('this Monday 05:00', $currentDayStart));    
        break;
      default:
        break;
    }
 
    // -- lastly, set the new day start
    $currentDayStart = strtotime('tomorrow 00:00', $currentDayStart);
 
  } // end while $keepLoop
 
  $difference -= $weekendSubtract;
 
  return $difference;     
}

The first two functions (check_same_day() and timeIntersect) are both helper functions for the final one which does most of the work (get_time_without_weekends()). That function accepts two UNIX timestamps in the variables labeled $timestamp1 and $timestamp2 and returns the difference in time between the two, ignoring the weekend days that may occur between them.

Add new comment

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

Questions?

Do you have a question about something you saw here in our blog? Are you interested in a custom website for your business? If so, please contact us here.