Is it possible to hook to variable_set() event?

by Andy Truong   Last Updated January 07, 2018 22:07 PM

I would like to track the system changes event, to make them revertable. When checking variable_set(), I see there is no hook provided for that event. Is there any way for me to do this?

I can alter to hook to settings forms, but there's a lot of settings form to track, if I can hook to variable_set() directly, code becomes much simpler.

I can also track the variable changes with features + strongarm modules, but it's better if Drupal admin can browse the variable history without code touching.

Tags : 7 hooks

Answers 3

It seems that it's impossible using only Drupal, which means:

variable_set() itself doesn't invoke any hooks, but it uses db_merge(). That function is using the MergeQuery class. Now, it would be nice to hook with hook_query_alter(), but it only works for query classes that implements the QueryAlterableInterface interface. Sadly, this interface is now implemented only by the SelectQuery and the SelectQueryExtender classes, not by the MergeQuery class.

Note that even if you will find a way to create a child class of MergeQuery, that will implement QueryAlterableInterface, and make Drupal use it. hook_query_alter() only works on queries that have tags, and variable_set() doesn't tag its query, so the hook would not be used anyway, unless you are willing to hack core. But if you are, you don't need all that, you could simply hack in a hook call.

If you feel hardcore you can use a more indirect PHP approach: $conf is a global array of configuration variables; you can write a module that will replace it with object acting like an array, as described on Stack Overflow. To make it a good substitute you need to implement ArrayAccess. Pull all values from original $conf into your object. Then, in ArrayAccess::offsetSet() implement your logging logic.

September 30, 2013 09:04 AM

As you can see in the source code, variable_set() makes no request for hooks or alterations, e.g no module_invoke_all() or drupal_alter() calls there.

function variable_set($name, $value) {
  global $conf;

  db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute();

  cache_clear_all('variables', 'cache_bootstrap');

  $conf[$name] = $value;

However, you might be able to listen to the db_merge() query with a specially placed hook_query_alter() and do some additional processing there but, as pointed out by Molot, hook_query_alter() looks unlikely to be able to target the db_merge() query.

Alternatively, you could perhaps cron snapshot the variable table to diff it against previous revisions of that table, or else implement some other form of variable revision storage to compare against.

David Thomas
David Thomas
September 30, 2013 09:05 AM

You could use a database trigger, which would be faster than code.

Here is the MySQL doc.

  1. create a table to store old values

    CREATE TABLE variable_backup
        name varchar(128) not null,
        value longblob,
        updated datetime not null,
        primary key (name, updated)
  2. create your triggers, one for insert and one for update:

    CREATE TRIGGER backup_variable_update BEFORE UPDATE ON variable
        FOR EACH ROW
            INSERT INTO variable_backup (name, value, type, updated) VALUES (, OLD.value, "update", NOW());
    CREATE TRIGGER backup_variable_insert BEFORE INSERT ON variable
        FOR EACH ROW
            INSERT INTO variable_backup (name, value, type, updated) VALUES (, NEW.value, "insert", NOW());

Now all of your updates and inserts will record old values in variable_backup.

Scott Joudry
Scott Joudry
September 30, 2013 14:39 PM

Related Questions

How to find hooks being called by a Drupal page?

Updated April 03, 2015 21:20 PM

Modify text of error message for required field

Updated April 22, 2015 20:03 PM

Where are hooks stored/cached?

Updated April 13, 2015 20:03 PM