The [L] flag stops processing for the current pass through the mod_rewrite code only. In the .htaccess context, mod_rewrite is recursive, though; It restarts as soon as an [L] flag or the end of the file is reached, whichever comes first.
Only after a complete pass through all mod_rewrite code is made without any rule being invoked is mod_rewrite processing terminated. So, use of the [L] flag is an optimization, a "stop here for now" kind of thing.
Also, if a redirect is done, it's important to realize that the current HTTP transaction is ended, the client will start a new one, and the server will have no memory of the previous transaction.
The following is a correct solution for your problem. Note that all external redirects should be done first, followed by all internal rewrites.
# Externally redirect only direct client requests for .php URLs to extensionless URLs
RewriteCond %{THE_REQUEST} ^[A-Z]+\ /([^/]+/)*[^.]+\.php([?#][^\ ]*)?\ HTTP/
RewriteRule ^(([^/]+/)*[^.]+)\.php http://www.example.com/$1 [R=301,L]
#
# Internally rewrite requests fore extensionless URLs to .php filepaths
RewriteRule ^(([^/]+/)*[^./]+)$ /$1.php [L]
Note the leading slash added before "$1" in the second rule. This is important to prevent giving the client complete control of the filepath to be accessed on your server.
Note also that you can use %{REDIRECT_STATUS} instead of %{THE_REQUEST} if you prefer. %{REDIRECT_STATUS} will be blank if not previous rewrites have been invoked. However, rules added in server config files or higher-level .htaccess files can break this latter method...
Jim