Complex if statements in authentication method I had asked questions about my entire class which I'll flag for closing soon. It appeared to be a little too broad for 1 post so I chopped it up and will be asking a little more specific questions separately.



In my Application Layer I have a class AuthService containing the following method with a code structure that has gotten quite complex with if statements that I'm unable to re-factor a better way.

I'd appreciate anybody who could review this piece of code and give me suggestions on  improving / optimizing the design of it. Any other suggestions are welcome as well.

public function findLoggedInUser()
{
    if (($userData = $this-&gt;httpSession-&gt;getParameter('user')) &amp;&amp; isset($userData['id'])) {
        // User found in current http session
        return $this-&gt;userMapper-&gt;findById($userData['id']);
    } elseif ($httpSessionData = $this-&gt;httpCookie-&gt;getParameter('usess')) {
        if (isset($httpSessionData['uid'], $httpSessionData['sno'], $httpSessionData['token'])) {
            if ($user = $this-&gt;userMapper-&gt;findById($httpSessionData['uid'])) {
                if ($httpSession = $this-&gt;httpSessionMapper-&gt;findByUserAndSeriesNo($user, $httpSessionData['sno'])) {
                    if (password_verify($httpSessionData['token'], (string) $httpSession-&gt;getTokenHash()) {
                        // Generate new http session entity tokens
                        $token['raw']  = new Token(CryptoCharGen::numeric(60));
                        $token['hash'] = new TokenHash(password_hash((string) $token['raw'], PASSWORD_BCRYPT, ['cost' =&gt; 4]));

                        // Update and persist http session entity with new token
                        $httpSession-&gt;setTokenHash($token['hash']);

                        $this-&gt;httpSessionMapper-&gt;update($httpSession);

                        // Issue new http cookie usess with updated http session recovery data
                        $this-&gt;httpCookie-&gt;setParameter('usess', [
                            'uid'   =&gt; (string) $httpSession-&gt;getUser()-&gt;getId(),
                            'sno'   =&gt; (string) $httpSession-&gt;getSeriesNo(),
                            'token' =&gt; (string) $token['raw']
                        ], 60 * 60 * 24 * 90);

                        // User has been found
                        return $httpSession-&gt;getUser();
                    } else {
                        // Token invalid, sabotage assumed
                        // Invalidate all persisted http sessions of this user
                        $this-&gt;httpSessionMapper-&gt;deleteAllByUser($user);

                        $this-&gt;httpCookie-&gt;destroy('usess');
                    }
                } else {
                    // Destroy invalid cookie
                    $this-&gt;httpCookie-&gt;destroy('usess');
                }
            } else {
                // Destroy invalid cookie
                $this-&gt;httpCookie-&gt;destroy('usess');
            }
        } else {
            // Destroy invalid cookie
            $this-&gt;httpCookie-&gt;destroy('usess');
        }
    }
}
Two suggestions on how to keep this code maintainable:


use early exits
keep conditions atomic (one if checks for one thing) 


Example:

function findLoggedInUser()
{
    if (($userData = $this-&gt;httpSession-&gt;getParameter('user')) &amp;&amp; isset($userData['id'])) {
        // User found in current http session
        return $this-&gt;userMapper-&gt;findById($userData['id']);
    }

    $httpSessionData = $this-&gt;httpCookie-&gt;getParameter('usess');
    if(!$httpSessionData) {
        $this-&gt;httpCookie-&gt;destroy('usess');
        return null;
    }

    if(!isset($httpSessionData['uid'], $httpSessionData['sno'], $httpSessionData['token'])) {
        $this-&gt;httpCookie-&gt;destroy('usess');
        return null;
    }

    $user = $this-&gt;userMapper-&gt;findById($httpSessionData['uid']);
    if(!$user) {
        $this-&gt;httpCookie-&gt;destroy('usess');
        return null;
    }

    $httpSession = $this-&gt;httpSessionMapper-&gt;findByUserAndSeriesNo($user, $httpSessionData['sno']));
    if(!$httpSessionData) {
        $this-&gt;httpCookie-&gt;destroy('usess');
        return null;
    }

    if (!password_verify($httpSessionData['token'], (string) $httpSession-&gt;getTokenHash()) {
        $this-&gt;httpSessionMapper-&gt;deleteAllByUser($user);
        $this-&gt;httpCookie-&gt;destroy('usess');
        return null;
    }

    // EVERYTHING'S FINE, DO THE JOB

    // Generate new http session entity tokens
    $token['raw']  = new Token(CryptoCharGen::numeric(60));
    $token['hash'] = new TokenHash(password_hash((string) $token['raw'], PASSWORD_BCRYPT, ['cost' =&gt; 4]));

    // Update and persist http session entity with new token
    $httpSession-&gt;setTokenHash($token['hash']);

    $this-&gt;httpSessionMapper-&gt;update($httpSession);

    // Issue new http cookie usess with updated http session recovery data
    $this-&gt;httpCookie-&gt;setParameter('usess', [
        'uid'   =&gt; (string) $httpSession-&gt;getUser()-&gt;getId(),
    'sno'   =&gt; (string) $httpSession-&gt;getSeriesNo(),
    'token' =&gt; (string) $token['raw']
        ], 60 * 60 * 24 * 90);

    // User has been found
    return $httpSession-&gt;getUser();
}


This way, the code remains readable and flexible for future modifications.