Basic login script for educational purposes I have created a login script to demonstrate the usage of sessions in PHP. The focus is not on security, databases or encryption. The idea is to exemplify how you can use sessions to protect pages from user access.

Key points to focus on:


Logical structure (return early, missing checks, error handling).
Compliance with PSR-1 and PSR-2.
Possible errors in the code.




&lt;?php
  // Step 1. Start session management.
  session_start();

  // Step 2. Check if user is already logged in.
  if (isset($_SESSION['log']) &amp;&amp; !empty($_SESSION['log'])) {
    // If true, redirect user to protected content
    header('Location: index.php');
  }

  // Step 3. Check if form was submitted through POST.
  if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Create array to store possible error messages.
    $errors = array();

    // Step 4. Check if username or password has been left empty by user.
    if (empty($_POST['user']) || empty($_POST['pass'])) {
      // If true, create an error message.
      $errors[] = 'Please check that you have filled in username and password.';
    }

    // Declare username and password.
    // These are only used to exemplify usage of sessions and should as standard be
    // stored in an encrypted form in a database (salt/hash).
    $user = 'admin';
    $pass = 'test';

    // Step 5. Check if username and password from form match with those declared.
    if ($_POST['user'] == $user &amp;&amp; $_POST['pass'] == $pass) {
      // If true, create a session variable with assigned value true.
      $_SESSION['log'] = true;

      // Redirect user to protected content.
      header('Location: index.php');
    }
    else {
      // If username or password are incorrect, create an error message.
      $errors[] = 'Incorrect username or password.';
    }
  }
  else {
    // Error message if form has not been submitted through POST.
    $errors[] = 'Form has not been submitted through POST.';
  }
?&gt;
&lt;!DOCTYPE html&gt;
&lt;html&gt;
  &lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;title&gt;login.php&lt;/title&gt;
    &lt;link rel="stylesheet" href="style.css"&gt;
  &lt;/head&gt;
  &lt;body&gt;
    &lt;?php
      // Prints out error messages raised by login script
      if (count($errors) &gt; 0) {
        foreach($errors as $e) {
          echo $e . '&lt;br&gt;';
        }
      }
    ?&gt;
    &lt;form method="post" action="login.php"&gt;
      &lt;fieldset&gt;
        &lt;legend&gt;Enter user information&lt;/legend&gt;
        &lt;ol&gt;
          &lt;li&gt;
            &lt;label for="user"&gt;Username&lt;/label&gt;
            &lt;input name="user" type="text"&gt;
          &lt;/li&gt;
          &lt;li&gt;
            &lt;label for="pass"&gt;Password&lt;/label&gt;
            &lt;input name="pass" type="password"&gt;
          &lt;/li&gt;
        &lt;/ol&gt;
      &lt;/fieldset&gt;
      &lt;fieldset&gt;
        &lt;button type="submit" name="submit"&gt;Login&lt;/button&gt;
      &lt;/fieldset&gt;
    &lt;/form&gt;
  &lt;/body&gt;
&lt;/html&gt;
First, all of your indentation is half indentation: you two-spaces instead of four.

Most IDEs have proper spacing if you press TAB.



Naming:

You have variables need re-naming:


$_SESSION['log']: a log is a regular or systematic record of incidents or observations, not whether you've logged in, which has entirely different meaning, also the same applies to the following variables, log is a completely different thing than the state it represents: loggedIn or signedIn would be much better.
$pass, $_SESSION['pass'], $user and $_SESSION['user']: it's really not worth it to sacrifice four extra characters here and there, for the sake of simplification. add word and name to the end of those.




Security:

You SHOULD not be saving the password to a session variable, there are ways to break into sessions, and therefore, is an insecure way to save them longer than needed; use unset($_SESSION['password']) to remove them after processing.



Failed Globals:

if ($_SERVER['REQUEST_METHOD'] == 'POST') {
        $errors = array();


You use the $errors variable outside of the if statement, which is a no-no. Declare the variable up there with session_start().



You shouldn't be manually providing the name of the PHP form, but, rather using $_SERVER['PHP_SELF'] instead:


&lt;form method="post"&gt;



into:

&lt;form action=&lt;?php echo htmlspecialchars($_SERVER['PHP_SELF']); ?&gt; method="post"&gt;





  if (count($errors) &gt; 0) {
    foreach($errors as $e) {
      echo $e . '&lt;br&gt;';
    }
  }




count($errors) &gt; 0 can be simplified to: count($errors)
count($errors) can simplified to: $errors
$errors can simplified to: '' (Nothing)


Explicitly checking the length of $errors is redundant, when you have a for or foreach loop.

If there's \$0\$ elements, the foreach won't trigger.

foreach($errors as $e) {
    echo $e . '&lt;br&gt;';
}


In fact, in this instance, foreach is redundant in itself.

Using a neat function called implode, you can join an array together, without needing to iterate over its elements explicitly.

echo implode('&lt;br&gt;', $errors);


Actually, &lt;br&gt; isn't really the best way to display multiple errors.

Consider using &lt;ul&gt; instead.

&lt;ul&gt;
    &lt;li&gt;
    &lt;?php echo implode ('&lt;/li&gt;&lt;li&gt;', $errors); ?&gt;
    &lt;/li&gt;
&lt;/ul&gt;


That certainly slimmed down, didn't it? When displaying content built from variables or arrays, make sure you consider how you choose to return them to the client.




    &lt;ol&gt;
      &lt;li&gt;
        &lt;label for="user"&gt;Username&lt;/label&gt;
        &lt;input name="user" type="text"&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;label for="pass"&gt;Password&lt;/label&gt;
        &lt;input name="pass" type="password"&gt;
      &lt;/li&gt;
    &lt;/ol&gt;



A few points to make about this block:


Why are you using &lt;ol&gt;? It seems silly, and pointless. Even using &lt;ul&gt; would be pointless.
In the &lt;input&gt; you can use the attribute placeholder, which as the name suggests, lets you set a placeholder to show an example password, or the name of the field. Personally, I'd ditch the labels, and put username and password as placeholders. As @KevinBrown pointed out in the comments: screen readers need lables, and without them, it can cause issues, however, I'd still set placeholders.

&lt;ol&gt;
  &lt;li&gt;
    &lt;label for="user"&gt;Username&lt;/label&gt;
    &lt;input name="user" type="text"&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;label for="pass"&gt;Password&lt;/label&gt;
    &lt;input name="pass" type="password"&gt;
  &lt;/li&gt;
&lt;/ol&gt;



Into:

&lt;input name="user" placeholder="username" type="text"&gt;
&lt;input name="pass" placeholder="password" type="password"&gt;


You can individually space them with CSS (Recommended method), &lt;ul&gt; + inline, or &lt;br&gt;.




if (isset($_SESSION['log']) &amp;&amp; !empty($_SESSION['log'])) {



$_SESSION['log'] is stored as a boolean, not a variable. Meaning, you can simply test it, without the need for functions like isset and !empty.

if ($_SESSION['loggedIn']){




Finally, the usage of $errors is a little superfluous.

$errors is used as an array, implying multiple errors will be added. However, only one $error should be added to the array, meaning a variable would be better.

Well, that would be the case, if you added a break, or return in the empty() check, but instead, you check if it's empty, add an error.

Then proceed to run through the rest of the code before comparing them. You already know if they're empty, they won't match the username and password given by the server, so why continue the script?

First, initialise $error (Note the lack of s) as a variable.
Then, break the function if they're empty.
Finally, return $error as a variable.

(I do realise this nullifies my previous point about displaying the errors, but, nonetheless, if you were to display errors, that's the way to do it.)