There are situations where you call a method which recalls itself multiple times, or calls other methods which recall the original method. But you want code of the original method execute only one time, and that, on the last call! What do you do?

A static variable is the solution to this situation. And it's a very simple solution too. Here is how you do it:

<?php
function recall($option) {
  static $times=0;
  //on method entry you always add to the $times variable
  $times++;
  
  //some code that re-calls this method...
  // eg. a file inclusion or another method call which re-calls this method
  // or a recall straight from within the method.
  // for any of the above scenarios $option is assumed to change on each call, and the re-calls can only happen on certain $options
  // this way we avoid falling in an endless loop...
  
  //Now make sure only the last call of this method gets to execute this code below...
  if ($times>0) {
    // put here any code you want executed only at the last call of the function.
    // it is important that you put this below the code that would eventually recall the function
  }
  
  // when done, make sure you set $times back to 0
  $times = 0;
}

So, what's basically happening here, is that each call is nested, and since PHP is a synchronous language, each call has to wait for the "child" call to finish execution, in order to proceed with its own execution.

Example: Call A($times=1) => Call B($times=2) => Call C($times=3)

Now based on the above sample code, Call A has to wait for Call B to finish, before evaluating if ($times>0). And Call B has to wait for Call C to finish, before continuing with its own evaluation.

Call C doesn't have to wait for anybody, and simply continues execution until the end of the method. It evaluates that $times>0 (3), and executes the code that should be executed only on the last call. Then it goes to the end of the method, and sets $times=0.

Execution of Call C is finished, so now Call B can resume execution. It evaluates $times>0 as false (since Call C has set $times to 0), and correctly doesn't execute the code that shouldn't be executed. Same happens to Call A.

At the end, the method is at its initial state, and waits for new calls. One time calls will be executed just fine, and nested calls will always execute restricted code only on the last call.

The problem that lead to the solution

In my years of PHP coding, I haven't ever met a situation where I needed this. Until today! I am developing a CMS from scratch, basically because I want something that can work on any HTML template, and because on many situations the available CMSes are overkill (call me Joomla/WordPress), or simply weak and ugly (no names here). My CMS is simple, fast, lightweight, and can be used on any HTML template out there. Anyway, the situation that gave me this problem (and made me think of the solution) is this:

My CMS produces SEF URLs. On page load, it has to parse these URLs back to original URLs and redirect to the correct file, based on the URL. Now, there are situations where a file may redirect to another file, based on user input, data processing, etc. This is very common in other CMS platforms as well. In such situations, we don't want the URL to change (header locations are a no-go), but we do want the page contents to change. It is also important that we do not re-load the page, because we don't want to renew anything on the PHP side. We want all properties, variables, objects, etc. to remain intact!

My CMS redirects by the redirect($uri) method, parses the $uri, and includes the appropriate file. After inclusion, it adds all its metadata, styles, and scripts to the head of the buffered document, and then outputs it to the browser.

<head> data change according to which page is loaded, and what is needed. Eg. page title, description, and Open Graph properties need to change according to the loaded page.

A second redirection made a double inclusion of everything! Metadata from both pages were added to a single <head> making a mess!

By adding the inclusion code inside the if ($times>0) block, I made sure the <head> information of only the last page was inserted to the document.

Do you know any other situations were this solution might be useful?

1 comment    (post your own)

Leave a Reply   

The Sinner In Me (Chris Vulture Mix) by Chris Vulture on Mixcloud