Whitelisting keys of an array

I needed to validate an array create from POSTed data. I wanted to discard all keys from this array that I didn't want.

Actually, I wanted to whitelist the array, and only keep keys that were on my whitelist.

Here is the litte snippet that does it :

<?php
/**
*    Keep only specified keys of the specified array. This is useful to whitelist an array of parameters.
*    \param    $array        Original array
*    \param    $whitelist        Array of keys to keep.
**/
function array_whitelist($array, $whitelist = array()) {
  return array_intersect_key($array, array_flip($whitelist));
}
?>

Hard limits using FB.ui to post Requests

I hate Facebook documentation. All pages seems out of date, displaying wrong function signature, obsolete parameters, documentation link pointing to 404 pages, etc, etc.

I've lost some hours debugging those calls, finding whatever hard limit Facebook forced on some of the arguments.

Let us write a very basic example code :

FB.ui({
  'method' : 'apprequests',
  'display': 'iframe',
  'message' : "Hey, this Request is awesome, just accept it, ok ?",
  'title' : "Awesome request incoming",
  'filters' : [
    { "name" : "Some friends", "user_ids" : [ "97841578", "548673131", "[...]"]},
    { "name" : "Some other friends", "user_ids" : [ "97841578", "56867134", "[...]"]},
  ]
});

This should open a Request popup with the custom title and message, as well as provide a list of friends that you can filter based on two criterias : "Some friends" and "Some other friends".

What will go wrong

First, you have to know that both the message and title have a character limit. If you go over it, the popup will simply display something like "An error occurred, blah blah blah".

After some fiddling, I discovered that the limit is 50 chars for the title and 255 for the message.

Also, there is no limit (as far as I know) to the limit of custom filters you can set. See update below. But there is one to the max number of users you can define in a filter. And that number is 1000.

This means that if my user_ids list for any of my custom filters contains more than 1000 users, the popup will fail. However, you can have as many filters with 999 users as you want.

Took me a little while to find, and I thought that this could be shared.

Update

I found a new hard limit : You can't set more than 5 filters at a time. If you add one more, the Request popup will fail.

New Update

Also, if you define a callback, don't forget to return true. Otherwise Webkit will refuse to close the FB popup and you'll have to click twice for it to really close itself.

Update (bis repetita)

This one was pretty hard to find but if your filter contains a facebook id of someone that is not a friend of the currently loggued user, the filter will display nothing.

In our app, we have a filter of "Neighbors" (as most social games do). But one of thoses neighbors removed the user from its friends, and we didn't update the neighbor list to reflect that, resulting in our "Neighbors" filter being empty.

Finding the perfect SSH host

I'm trying to find a company providing hosting capabilities. I have a strong set of pre-requisites.

Connected through SSH

I want the server to be accessible through SSH. I don't need http nor ftp acces on it.

I'll host git, mercurial and subversion repositories on it, and will push(/pull) from(/to) them using ssh.

So, I will need those softs installed, or at least enough power to install them (even locally).

Serving as an SSL tunnel

I also want to be able to create an SSH tunnel through this server. That way I could be securely connected from any wifi connection using SSL.

The server should have a pretty reasonable bandwidth. I'd also wish to avoid having it hosted in France. Peering agreement between providers in France are getting uglier and uglier.

Secondary accounts for holding websites

If the same company also provides more conventional hosting, that also could be an important part of my choice.

What I would like is something as flexible as allowing some websites to use Apache, and others Lighttpd. Even being able to configure it per subdomain.

Some sites would be running PHP, others Rails, even node.js. I want enough freedom to configure it and the associated inner config (lighttpd.conf, php.ini).

I also want to be able to install secondary parts like mongoDB or memcache and having a full access to their configuration.

Pricing

Of course, the cheaper the better, but I know that such an amount of freedom and features comes with its own price.

Now, let's review some of the contestants I've picked

Alwaysdata

They were the first I checked. I heard quite a good thing about their service and reliability. They really seem to know what they're doing.

I've tried their free 10Mo plan and am quite satisfied with it. Mercurial, git, subversion and quite a batch of others softs are already installed. They also have a pretty 10Mo/s awesome bandwith.

I haven't tried their hosting plans, but they also seems nice. All default (shared) hosting provides php, perl, ruby and python.

Their custom (managed dedicated server) provides almost anything you could want from lightty to memcache, but the price goes high with it to. 350€/month that is way too much I can afford for what I have in mind.

On the other hand, they not only provide hosting but the whole support package along with hotline and custom installation.

Dreamhost

I'm fond of Dreamhost. I've used them for years. They do provide a very nice shared hosting, with a lot of options to configure. They even allow cronjob for their admin panel.

And they are cheap, with a customer support very professional, I highly recommend them for your simple hosting needs.

I haven't tested their dedicated server version, so can't really talk about it.

However, the SSH capabilities of the shared hosting are quite small. They have git, svn and mercurial installed, but the version are quite outdated. Even the python version running mercurial was out of date.

You can however download and install them yourself on your account (which I did), but it is not as full featured as alwaysdata.

Their bandwith is also capped to 1Mo/s.

OVH

Haven't tested yet, but their VPS offer seems nice for as low as 60€/year.

I have tried the shared hosting offer of OVH before and am mitigated. On one hand I like some of their features like the automatic backup of your ftp files, on the other hand their administration panel is a total mess and accessing the server through SSH is not that simple.

I've heard they have a 10Mo/s bandwith. I'll try to get more information on this.

Gandi

Gandi is an amazing registrar (both technically and ethically). I've never had any issues with their service and can only praise their technical support.

Some years ago, they started doing hosting too. I tested it back then and was quite disapointed. This was horribly slow and crashed.

I never tried it again since then. I think they improved their service, and I just asked a test account. Their VPS offer is interesting, the server is pretty decent from what I can read and the price is still in my range (12€/month).

I'll continue this review later, with more information. If you, reader, have any suggestion on a good host filling my need, feel free to post it in the comments, I'll review it.

Another PHP casting weirdness

Second weird PHP behavior behavior of the day :

$foo = false;
echo $foo['bar']; // Ouptuts nothing, but doesn't throw an error either

Strange. I try to read an undefined index (falseis not an array), but PHP doesn't complain. That's weird.

$foo = true;
echo $foo['bar']; // Throws an error

This time, PHP tells me that the index is not defined and throws an error. Wait, what ?

Apparently, this is the intended behavior, but it does seem a bit strange...

json_decode casts strings to floats

The PHP function json_decode takes a JSON string as argument and return a decoded array/object .

However, when passing an argument that has nothing to do with a JSON string, the function was supposed to return null. But, in practice, this didn't go so well.

Passing a string

Here's an example :

$foo = 'foo';
$bar = json_decode($foo);
// $bar is nothing,
echo (is_null($bar)) ? "This is null" : "This is not null";

Passing a float

Here, we try to decode a string. The function rejects it and returns null.

$foo = 0.4;
$bar = json_decode($foo);
// $bar is a float
echo (is_float($bar)) ? "This is a float" : "This is not a float";

Here, we pass a floating number. And the function returns... a floating number. Wait, what ?

This is weird, I expected it to return null, once again. But maybe it's correct and the JSON specs says so. I didn't check, actually.

Passing a string thats looks like a float

But the next example is even better :

$foo = '0.4';
$bar = json_decode($foo);
// $bar is also a float, even if $foo was a string
echo (is_float($bar)) ? "This is a float" : "This is not a float";

This time I pass a string as a parameter (like example 1) but got a float as a result (like example 2).

Well, this time I'm sure that can't be the correct behavior.

How to deal with it

I finally wrote a little wrapper for json_decode to handle those strange cases :

function my_json_decode($var) {
  $decodedValue = json_decode($var, true);
  return is_array($decodedValue) ? $decodedValue : $var;
}

This will check that the result of the JSON decoding is an array, and if not (meaning the original string was not a JSON string), it will return the original string.