Here’s an approach that I believe is 
slightly less messy than Gilles’ answer
(although I concede that to be a subjective judgment).
#!/bin/sh
this_dir=$(dirname "$0")        # Alternatively, hard-code this as this_dir=$HOME/bin
redacted_PATH=$(echo ":${PATH}:" | sed -e "s:\:$this_dir\::\::" -e "s/^://" -e 's/:$//')
if obscured_prog=$(PATH=$redacted_PATH which foo)
then
            ⋮           # Munging to do before running /usr/bin/foo
        "$(obscured_prog)" argument(s)          # may be "$@", but might not be.
            ⋮           # Munging to do after running /usr/bin/foo
else
        echo "$0: foo (real) not found in PATH."
            ⋮           # Any code that you want to do anyway.
fi
This constructs redacted_PATH to be $PATH
minus the $HOME/bin directory where this private copy of foo lives. 
echo ":${PATH}:" adds colons to the beginning and end of $PATH,
so every component of it will be preceded and followed by a colon —
even the first and last one.  The sed searches for
: $this_dir :
(with spaces added for “clarity”) and replaces it with
:
i.e., it excises $this_dir from ":${PATH}:". 
It then removes the colons from the beginning and end.
Then we temporarily set PATH to $redacted_PATH
and search for foo using which. 
If successful, we get a full path to it (e.g., /bin/foo or /usr/bin/foo),
which we use to run the real (public/shared/system) copy of foo. 
Since we changed PATH only temporarily,
/bin/foo has access to the user’s ambient $PATH,
and so, if /bin/foo runs brillig, it can find $HOME/bin/brillig
(if it exists).
This will have a problem if $HOME/bin appears in $PATH multiple times,
but that’s not too hard to remedy.
     
    
foomight in turn invoke other commands, some of which may be in the same directory as thefoowrapper./usr/bin/foois accessing "private" commands like/home/bob/bin/foo? - At best that appears to me to be a misdesign./bin/shwith the expectation that it runs “private” commands. Or run programs in/usr/binthat invoke an editor which is~/bin/EDITOR. Etc.