\b #the zero-width position between a character matched by \W and a character matched by \w
(?: #start non-capturing group
[A-Z]{4,20}(*SKIP)(*FAIL) #match and disqualify all-letter words
| #or
[A-Z\d]{4,20} #match between 4 and 20 digits or uppercase letters
) #end non-capturing group
\b #the zero-width position between a character matched by \W and a character matched by \w
foreach (explode(' ', $str) as $word) {
$length = strlen($word);
if ($length >= 4 // has 4 characters or more
&& $length <= 20 // has 20 characters or less
&& !isset($result[$word]) // not yet in result array
&& ctype_alnum($word) // comprised numbers and/or letters only
&& !ctype_alpha($word) // is not comprised solely of letters