Monday, 28 January 2013

Ugly hack series - Ep. #1


Development for me is an art. I like the structures and the whole design. One particular difference between 'normal-people' art and tech art is the limitations. To workaround those limits you apply hacks sometimes. Well, they say it's only the matter of time. And with that I agree. But time is the one scarce resource we don't have. So we hack.


I've seen many of them through my experience. Some core hack here and there (not me, I swear), some contrit patch and the total negligence of Drupal ecosystem.

Then there are the low level hacks, such as saving temporary variables in the global scope. Or saving them in session. Which hopefully we all know is a security issue.

But today I mastered myself to a new level. (I'm not proud at all.) Sometimes you have global alters that takes a certain modification on a data. The key here is that it's global, so applied without any context. That's nice, because you can alter the system from a single point (yeah, I can hear your inner voice: 'of failure'). Oh the other hand you don't have context and when you alter you possibly cut the flexibility from other behaviors. My case is around the format_username() call. This function gets the formatted username, which is alterable by hook_username_alter(). I needed however the username in a different format - through the same format_username() call - which I didn't owned. So somehow I had to generate different output from the same function without context?

If you have enough willpower read no further and think about what would you do in this case. (Image is only for distracting you.)


Now my hack is actually using the context. How? Let me introduce: debug_backtrace(). This call gives you the call stack you're in at the moment:

$call_stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);


I knew that the different output is only needed when it's called through a special function, let's call him: 'collect_special_usernames()'. I need to filter through the call stack to see if it was the caller:

$call_stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
$caller_is_found = array_filter($call_stack, function($item){
  return $item['function'] == 'collect_special_usernames';
});


Here $caller_is_found will be empty if my function in interest wasn't there - and will contain that item if it was. To make it handy we can create a wrapper:

function is_called_through($function_name) {
  $call_stack = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);

  $caller_is_found = array_filter($call_stack, function($item){
    return $item['function'] == $function_name;
  });

  return !empty($caller_is_found);
}


At the end I managed to find a hook to fulfill my needs, so I didn't have to apply this hack. But still, has its own charm, don't you think?

---

Please, share your favorite ugly hack.

Peter

1 comment:

  1. Year ago I've used that trick to print function which called dpm() and his friends, so I didn't have to call it with _FUNCTION_, like this: dpm($var, _FUNCTION_), which I'm doing almost every time when I'm using the devel functions...

    ReplyDelete