A while back I wrote this function in PHP, it does what it does and gives you some flexibility as well through complexity modifiers, I used a default set of 5 different 'levels' of characters and the length is also variable ofcourse.
I'm just going to chuck it in here and 'try' to explain what is going on as well as I can by comments:
function rsg($length = 10, $complexity = 2) {
        //available 'complexity' subsets of characters
        $charSubSets = array(
            'abcdefghijklmnopqrstuvwxyz',
            'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
            '0123456789',
            '!@#$%^&*()_+{}|:">?<[]\\\';,.`~',
            'µñ©æáßðøäåé®þüúíóö'
        );
        // will be filled with subsets from above $charSubsets
        $chars = '';
        //concact each subset until complexity is reached onto the $chars variable
        for ($i = 0; $i < $complexity; $i++)
            $chars .= $charSubSets[$i];
        //create array containing a single char per entry from the combined subset in the $chars variable.
        $chars = str_split($chars);
        //define length of array for mt_rand limit
        $charCount = (count($chars) - 1);
        //create string to return
        $string = '';
        //idk why I used a while but it won't really hurt you when the string is less than 100000 chars long ;)
        $i = 0;
        while ($i < $length) {
            $randomNumber = mt_rand(0, $charCount); //generate number within array index range
            $string .= $chars[$randomNumber]; //get that character out of the array
            $i++; //increment counter
        }
        return $string; //return string created from random characters
    }
This is what I currently use and it has satisfied my needs for quite some time now, if anyone reading over this has improvements I'd love to hear them as well!