Playing with Lua: inheritance

2012
16
November

I've just begun learning Lua and so far it's great.
I needed a simple yet elegant solution for class inheritance and I liked what I came up with so much that I thought I'd share it with the world. :)


function class(base_class, new_class)
        function new_class:create(def)
                setmetatable(def, { __index = new_class })
                return def
        end

        if nil ~= base_class then
                setmetatable(new_class, { __index = base_class })
        end

        return new_class
end

Animal = class(nil, {
        short = "An animal",
        describe = function(self)
                print(self.short)
        end,
})

Dog = class(Animal, {
        speak = function(self)
                print("voff voff")
        end
})

obj = Animal:create({})
dog1 = Dog:create({ short = "A small brown dog" })
dog2 = Dog:create({
        short = "A large white dog with black spots",
        speak = function(self)
                print("VOFF! VOFF!")
        end
})

obj:describe()
dog1:speak()
dog2:describe()
dog2:speak()

Results in:
An animal
voff voff
A large white dog with black spots
VOFF! VOFF!

Update: So I thought about it and thought hey, someone else must have done something better than this and after a quick github search I found Middleclass a very nifty lua script offering everything you need when it comes to classes. Check it out :)

PHP syslog, not quite right

2012
18
May

When managing multiple servers (or even just the one) logging to syslog instead of /var/log/php_error.log becomes very handy. Setting up syslog logging couldn't be easier either, all you have to do is changing your php.ini 'error_log' config setting to 'syslog'. Granted you can't define the facility or ident manually but that is a minor issue.

The Syslog C lib provides 3 important functions to facilitate logging to syslog. These are openlog, syslog and closelog.

void openlog(char *ident, int option, int facility);
void syslog(int facility_priority, char *format, ...);
void closelog(void);

A call to openlog opens a connection to syslog and sets the parameters to use when logging. It is also the only way to set the ident to use in the logged messages. closelog simply closes the connection again. You don't have to call openlog before you call syslog, because syslog will call openlog implicitly the first time it is used. But if you don't call openlog explicitly then syslog will use the default ident and options, which can be important to know. The default ident for example will usually be the process name. Note that there's no connection reference in either of the functions, this is because the syslog library only keeps one global connection within the process. This means that any options set directly or indirectly through openlog will be used until they are changed by another call to openlog.

As we can see in the excerpt below PHPs internal error logger doesn't explicitly call openlog so the facility will default to LOG_USER and the ident to the process name*.

main/main.c
 616 #ifdef HAVE_SYSLOG_H                                                             
 617       if (!strcmp(PG(error_log), "syslog")) {
 618           php_syslog(LOG_NOTICE, "%s", log_message);
 619           PG(in_error_log) = 0;
 620           return;                                   
 621       }                                                       
 622 #endif

php_syslog is defined as syslog in main/php_syslog.h.
Obviously openlog could have been called from another location in the code but that isn't the case.

Now in itself this isn't a problem, unless we've changed the defaults in syslog.conf (or rsyslog.conf) our PHP errors should end up in /var/log/syslog, /var/log/messages or /var/log/user.log depending on the OS/dist. But PHP also exposes the 3 functions openlog, syslog and closelog as PHP functions so that our scripts can log directly to syslog, and this is where things get interesting. Because the connection to syslog is global within the process, as mentioned above, any calls to openlog in a PHP script will alter the syslog settings for the entire process potentially causing all kinds of trouble.

To demonstrate this we'll set up syslog to send messages to the facility LOG_LOCAL6 to the file /var/log/php-junk.log. In syslog.conf add:

local6.*	/var/log/php-junk.log

If your PHP errors aren't showing up in /var/log/user.log, /var/log/syslog or /var/log/messages then add the following as well

user.*	/var/log/user.log

Restart syslog and then run this simple script:

<?php
    trigger_error("Some php error…");
    openlog("myscript", LOG_LOCAL6);
    syslog(LOG_NOTICE, "Kilroy was here");
    trigger_error("Some other php error…");
?>

The first line should correctly end up in user.log while the following two should be sent to php-junk.log.
Now if we had called closelog(); directly after our syslog call the last message would have been sent to user.log since openlog gets called implicitly as the result of trigger_error().

A quick fix would be to add a call to openlog/closelog before/after the call to php_syslog in main/main.c but that would break any script that expected openlog to be called only once. A much better solution was suggested by Jérôme Loyet in the rfc 'Allow multiple simultaneous syslog connections' but it seems it didn't gather enough interest so the solution has since been abandoned.

:(

* Another quirk whose origin I haven't tracked down yet is that when the process name is used as ident it will sometimes be cut. I noticed that when running php-fpm with the default pool name 'www', logged messages would get the ident 'ool www'.

Uninstalling a source installed PHP environment

2012
11
May

Since the PHP devs doesn't see it fit to include an option to uninstall (at least not in v5.3.6) in their source distribution I thought I'd share a quick tip with you.

In order to uninstall what we installed with "make install" we first need to find out what was installed.
"make -n install" will dump the install commands used during installation, at least as long as you haven't rebuilt the config or other wise changed the install files since installation.

You will have to extract out the resulting files on your own since the output of "make -n install" includes commands to create directories.

It should result in a list similar to this one:


/usr/local/bin/php-cgi
/usr/local/bin/php
/usr/local/bin/phpize
/usr/local/bin/phar
/usr/local/bin/phar.phar
/usr/local/bin/php-config
/usr/local/man/man1/php.1
/usr/local/man/man1/phpize.1
/usr/local/man/man1/php-config.1
/usr/local/lib/php/
/usr/local/include/php/

Just remove those files and directories and you are done.

Installing transmission-daemon from source in debian Squeeze.

2012
15
March

I had problems with transmission-daemon not downloading properly, especially when fed magnet links.
Since the version installed through apt was an older version (2.03?) I decided to install from source.
It's not at all difficult but I did run in to a small snag so I thought I'd write about it here.

Transmission 2.50 requires libevent 2.10, my system had a much older version so I downloaded the latest source release from github and then installed it into /usr/local/lib (default location). I later realized that Transmission expected it to be in /usr/lib so I symlinked it in. You can specify the path during configure or install.


# apt-get install build-essential automake autoconf libtool pkg-config intltool libcurl4-openssl-dev libglib2.0-dev
# wget https://github.com/downloads/libevent/libevent/libevent-2.0.17-stable.tar.gz
# tar -zxf libevent-2.0.17-stable.tar.gz
# cd libevent-2.0.17-stable/
# ./configure
# make
# make install
# ln -s /usr/local/lib/libevent-2.0.so.5 /usr/lib/libevent-2.0.so.5

We then compile transmission and install it in to /usr/bin.


# wget http://download.transmissionbt.com/files/transmission-2.50.tar.bz2
# bzip2 -d transmission-2.50.tar.bz2
# tar -xf transmission-2.50.tar
# cd transmission-2.50
# ./configure --disable-gtk --disable-cli --disable-mac
# make
# cd daemon
# cp transmission-daemon /usr/bin/transmission-daemon
# cp transmission-remote /usr/bin/transmission-remote

Done :)

Checking if an option exists in an MySQL enum column

2012
23
January

Although using MySQL enums is often discouraged I sometimes use them for their simplicity. Today however I stumbled upon one of the problems with enums. There is no simple method to determine all possible values of an enum column.

While it was easy enough to extract with a little creative PHP the solution is rather ugly. I'd much prefer to be able to write "SELECT options(enum_column) FROM table_name" which would then result in one row per option.

Until we come to our senses and stop using enums in MySQL I guess we are stuck with hacks like the following:



// First get the result of $query into variable $column using your prefered db abstraction.
// In my environment I use $db->fetch()
// You can user mysqli or pdo or whatever. Ex:
// $result = mysql_query(, $con);
// $column = mysql_fetch_row($result)

$query = "SHOW columns FROM table WHERE Field = 'enum_column'";
$column = $db->fetch($query);

// $column['Type'] will contain the definition for the enum and it will look something like:
// enum('value1', 'value2', 'value3')
// What we want to do is convert this in to an array. We could do this by replacing "enum" with array
// and then run the string through eval(). But since eval is a "dangerous" function we'll do it using json.
// So first, we do a little string replacing so that we end up with a string looking like:
// ["value1", "value2", "value3"]
//  We then pass that string to json_decode so that we get an array.
$options = json_decode(str_replace(
    array("enum(", ")", "'"),
    array("[", "]", '"'),
    $column['Type']
));

// Now that we have all the options in an array it's simple to test if a certain option is present.
if (in_array("foobar", $options))
{
    doSomething();
}

That's all folks!