Beware …. of PHP substr_compare !!

Today I stumbled on something evil. Apart that PHP is evil. I discover a quirck in substr_compare from version 5.3.3 to 5.4.x. The documented behaviour of the function was not as expected.

PHP 5.3 has this code:

PHP_FUNCTION(substr_compare)
{
    ... omitted for brevity

    if (len > s1_len - offset) {
        len = s1_len - offset;
    }

    cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset)));

    if (!cs) {
        RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
    } else {
        RETURN_LONG(zend_binary_strncasecmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
    }
}

The documentation states that one can provide a length for the comparison:
http://php.net/manual/en/function.substr-compare.php

But the length specified is shortened when the length is larger than the main_str length minus the offset. An example that causes problems (string starts with):

$haystack = "/my";
$needle = "/mypath/on/fs";
$result = substr_compare($haystack, $needle, 0, strlen($needle)) === 0

This will always return true! That is because the length is truncated to the main_str. And now for the big suprise, in PHP 5.4 the behaviour has changed (to the correct way) !

PHP_FUNCTION(substr_compare)
{
    ... omitted for brevity

    cmp_len = (uint) (len ? len : MAX(s2_len, (s1_len - offset)));

    if (!cs) {
        RETURN_LONG(zend_binary_strncmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
    } else {
        RETURN_LONG(zend_binary_strncasecmp(s1 + offset, (s1_len - offset), s2, s2_len, cmp_len));
    }
}

The complete check is gone, no more truncating of the length! Huray!

I searched for an issue stating this difference or a commit point, but I could not find it!
If someone does, please leave a comment.

substr_compare is a relative fast way of determining if a string starts with another string. You can find a test here. My system gives these results (Atom quad core):

substr_startswith: 70.962190628052 ms
preg_match_startswith: 2519.1428661346 ms
substr_compare_startswith: 69.415092468262 ms
strpos_startswith: 81.983089447021 ms
strncmp_startswith: 61.192989349365 ms
strncmp_startswith2: 53.861141204834 ms

It seems strncmp is your best option (at least on my system) but substr_compare is a pretty decent second: http://php.net/manual/en/function.strncmp.php

 

SVN post commit hooks and SeLinux

On CentOS 7 I’ve spent more time than I wanted struggling with SVN and post-commit hooks in combination with SeLinux. The best documented blog post I found was http://ejohansson.se/archives/2007/11/04/selinux-subversion-and-mod_svn/.

The problem was that I used a different directory than the default. Normally the files should reside under /var/www/svn. All SeLinux contexts are already in place and everything will work like a charm.

These are the rules that already exists:

/var/www/svn(/.*)?                                 all files          system_u:object_r:httpd_sys_rw_content_t:s0
/var/www/svn/conf(/.*)?                            all files          system_u:object_r:httpd_sys_content_t:s0
/var/www/svn/hooks(/.*)?                           all files          system_u:object_r:httpd_sys_script_exec_t:s0

Hope this helps for you!