picture of Jesus
The problem
Just today (1999/04/29), someone asked in the PHPBuilder discussion boards if there was a way to generate random but pronounceable passwords. I guess something like the ubiquitous passwords you find in each of the AOL CD-ROMs that litter our mailboxes :)
In this article we will show how to do just that, with the constrains that we do not want to have number containing words in your output, and that we want to be able to specify the length of the words used.
The simplest way I could think to accomplish this would be to:
  1. Have a set of words (maybe in an array)
  2. Select the number of words to use in the password
  3. Pick the words and concatenate them
In the listing below, we will read a list of words from a file into an array, and then use the random functions in PHP to generate the password.
List 1: a simple pronounceable password generator

<?php

/* 
 * Pronounceable password generator
 * version 1.0beta
 * Inspired by a question made by: georgcantor_at_geocities.com
 * in the PHPBuilder discussion forum
 * (c) Jesus M. Castagnetto, 1999
 * GPL'd code, see www.fsf.org for more info
 */ 

$words =  "mywords";     /* the file w/ the words */ 
$cut_off 5;     /* minimum number of letters in each word */ 
$min_pass 2;     /* minimum number of words in password */ 
$max_pass 3;     /* maximum number of words in password */ 

/* read the external file into an array */ 
$fp fopen($words"r");

if (!
fp) {
    echo  
"[ERROR]: Could not open file $words<BR>\n";
    exit;
} else {
    
/* assuming words of up to 127 characters */ 
    
while(!feof($fp)) {
        
$tword trim(fgets($fp,128));
     
        
/* check for minimum length and for exclusion of numbers */ 
        
if ((strlen($tword) >= $cut_off) && !ereg"[0-9]",$tword)) {
            
$word[] = strtolower($tword);
        }
    }
    
fclose($fp);
}

/* generate the password */ 

$size_word count($word);
srand((double)microtime()*1000000);
$n_words rand($min_pass,$max_pass);

/* use the Mersenne Twister for a better random */ 
#mt_srand((double)microtime()*1000000); 
#$n_words = mt_rand($min_pass,$max_pass); 

for ($i=0$i $n_words$i++) {
    
$pass .= $word[rand(0,($size_word 1))] .  "_";
}

/* print the password */ 
echo substr($pass,0,-1). "<BR>\n";

?>
If you run the program listed above, the output would be (for example):

addressograph_accessible_adapt_acrid
I used the first 300 lines of the /usr/dict/words file in my Solaris box and save them into the file mywords, you can use any list of words you would like. Also, if you desire to have a better random generator, use the Mersenne Twister functions (see the manual entries for mt_rand() and mt_srand() for their description and links to more information).

Creating a function

Of course, we can make all of this into a function that you can drop in your scripts:
List 2: function ppassgen()

<?php

/*
 * Pronounceable password generator
 * version 1.0 - made into a function
 * Inspired by a question made by: georgcantor_at_geocities.com
 * in the PHPBuilder discussion forum
 * (c) Jesus M. Castagnetto, 1999
 * GPL'd code, see www.fsf.org for more info
 */


/*
 * function ppassgen()
 * parameters:
 * $words = the name of the file w/ the words (one per line)
 *      or and array of words
 * $min = the minimum number of words per password
 * $max = the maximum number of words per password
 * $cutoff = the minimum number of characters per word
 * $sep = separator for the words in the password
 */

function ppassgen($words"mywords"$min=2$max=4$cutoff=5$sep"_") {

    if(
is_array($words)) {
        
/* if we have passed and array of words, use it */
        
$word_arr =  "words";
         
/*
        while(list($k,$v) = each(${$word_arr})) {
            echo "$k $v<BR>";
        }
        */
    
} else {
        
/* read the external file into an array */
        
$fp fopen($words"r");

        if (!
fp) {
            echo  
"[ERROR}: Could not open file $words<BR>\n";
            exit;
        } else {
            
/* assuming words of up to 127 characters */
            
$word_arr =  "ext_arr";
            while(!
feof($fp)) {
                
$tword trim(fgets($fp,128));

                
/* check for minimum length and for exclusion of numbers */
                
if ((strlen($tword) >= $cut_off) && !ereg"[0-9]",$tword)) {
                    
$ext_arr[] = strtolower($tword);
                }
            }
            
fclose($fp);
        }
    }

    
/* size of array of words */
    
$size_word_arr count(${$word_arr});

    
/* generate the password */
    
srand((double)microtime()*1000000);

    
/* or use the Mersenne Twister functions */
    //mt_srand((double)microtime()*1000000);

    /* for a password of a fixed word number, min = max */
    
$n_words = ($min == $max)? $min rand($min,$max);

    
/* or use the Mersenne Twister for a better random */
    //$n_words = ($min == $max)? $min : mt_rand($min,$max);

    
for ($i=0$i<$n_words$i++) {
        
$pass .= ${$word_arr}[rand(0,($size_word_arr 1))] . $sep;
    }
    
/* return the password minus the last separator */
    
return substr($pass,0,-1*strlen($sep));
}


/* test the function */

/* generate 10 passwords using the default */
for ($j=0$j 10$j++) {
    echo 
ppassgen() .  "<BR>\n";
}

/* change some paramaters and use our own separator */
echo  "<HR>\n";
echo 
ppassgen"mywords",3,4,7"**") .  "<BR>\n";

/* give our own array to the function and generate a 2 word password */
$nwords = array( "cat""mouse""dog""house""surfboard""kirima");
echo 
ppassgen($nwords,2,2,3) . "<BR>\n";

?>
This function can accept as an input the name of the file where your list of words resides, or an array of words. At the beginning of the function you will see that we check whether an array was passed, if so we assing the variable name to the variable <?php example ('$word_arr');?>.
If no array was passed we assume that is a filename (or an URL), attempt to open the file and save the contents to an array taking into account the constrain of the minimum number of letters per word. We then assign the name of the array to the variable <?php example ('$word_arr');?>.
During the generation of the password we make use of the array of words, and the code there is independent on whether the array was passed to the function or created by reading from a file. We accomplish this by referring to the array as <?php example ('${$word_arr}');?>, which the parser will interpret as <?php example ('$words');?> or <?php example ('$ext_arr');?>, depending on the origin of the array. This is an example of the use of variable variables to make general routines.
The test samples show how to use the function to generate passwords using the default values for all parameters, how to change the separator from "_" to "**", and finally how to pass our own array of words and make the function generate password with exactly 2 words.
Running the program in Listing 2, would generate output such as:
   
   acuity_abuilding_abyssinia_actuate
   accuracy_abacus_aborning_abnormal
   ablaze_acquiescent_accomplice
   abbreviate_acme_acton
   accessible_abhorred_absent
   abreact_aaron
   academician_absolution
   abelson_academician
   abstention_adept_abuilding_acropolis
   additive_acreage_acanthus

   about**abc**acidulous

   dog_cat

Further improvements

There are many other things you could improve in this function, for example, you could have 2 or more arrays, one with verbs, another with adjectives and adverbs, and other with nouns, and then use some simple grammatical rules to construct passphrases (e.g. tomatoes fly terribly), which are more difficult to guess than regular passwords and more easy to remember for the user.
We can also improve performance by avoiding the opening of an external file, and then the parsing of it to create the array of words, by having the arrays predefined into an include file or in the same script (also the latter will make your script long).
This and other improvements are (of course) left as an excercise to the reader.
Good luck and have fun.
 
--Jesus M. Castagnetto