You need to do the php extension check before the username routing. This, of course, will break if you happen to have a php file that is the same as a username, no way around this unless you prefix all of your usernames.
So swap your rules around and add another rule for redirecting direct accesses to php files:
# we don't want to mess with /profile.php access because there's a rule later that deals with that
RewriteCond %{REQUEST_URI} !^/profile\.php
RewriteCond %{THE_REQUEST} \ /(.*)\.php(\?|\ |$)
RewriteRule ^ /%1 [L,R=301]
# add file extensions back to URI
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
#vanity URL
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^([a-zA-Z0-9-_]*)$ /profile.php?username=$1 [L,QSA]
RewriteCond %{THE_REQUEST} \ /profile\.php\?username=([^&\ ]+)&?([^\ ]*)
RewriteRule ^ /%1?%2 [L,R=301]
What I mean by the prefix for users is something like this:
http://mysite.com/u/foobar
Here, the /u/
indicates that "foobar" is a user, and not a php page. To do this, you'll need to add a few changes to the "# vanity URL" section:
#vanity URL
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^u/([a-zA-Z0-9-_]*)$ /profile.php?username=$1 [L,QSA]
RewriteCond %{THE_REQUEST} \ /profile\.php\?username=([^&\ ]+)&?([^\ ]*)
RewriteRule ^ /u/%1?%2 [L,R=301]
essentially including /u/
as part of the match and redirect.