Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Forms make the web

Kevin Dees
April 20, 2017

Forms make the web

Web design and PHP OOP best practices for web forms.

Kevin Dees

April 20, 2017
Tweet

More Decks by Kevin Dees

Other Decks in Design

Transcript

  1. 1. Use UTF-8 2. CSRF 3. POST as array data

    4. REST Hacking 5. Data Injection Order 6. Feedback "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  2. <meta charset="UTF-8" /> <form action="user.php" accept-charset="utf-8"> <?php header('Content-Type: text/html; charset=utf-8');

    CREATE TABLE `users` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `first` varchar(60) DEFAULT '', `last` varchar(60) DEFAULT '', `email` varchar(60) DEFAULT '', PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8; <?php new PDO('mysql:host=%;dbname=db;charset=utf8', 'user', 'pass'); "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  3. <?php session_start(); // check for submit if( !empty($_POST['_csrf']) && !empty($_SESSION['_csrf'])

    ) { if( $_POST['_csrf'] != $_SESSION['_csrf']){ die('CSRF Block!'); } } // new key $form_access_key = uniqid('_csrf_'); $_SESSION['_csrf'] = $form_access_key; ?> <form action="user.php" method="POST" accept-charset="utf-8"> <input type="hidden" name="_csrf" value="<?php echo $form_access_key; ?>"> </form> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  4. <form action="user.php" method="POST" accept-charset="utf-8"> <input type="hidden" name="_csrf" value="<?php echo $form_access_key;

    ?>"> <label>First Name <input type="text" name="user[first]"></label> <label>Last Name <input type="text" name="user[last]"></label> <label>Email <input type="text" name="user[email]"></label> <p><input type="submit" value="Do Action" /></p> </form> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  5. <form action="user.php" accept-charset="utf-8"> <input type="hidden" name="_csrf" value="<?php echo $form_access_key; ?>">

    <input type="hidden" name="_method" value="PUT"> .... "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  6. function rest_method($actual = false) { if($actual) { return $_SERVER['REQUEST_METHOD']; }

    return ! empty($_POST['_method']) ? strtoupper($_POST['_method']) : $_SERVER['REQUEST_METHOD']; } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  7. $user_controller = new \App\UserController; $rest_method = rest_method(); if( method_exists($user_controller, $rest_method)

    ) { $user_controller->{$rest_method}(); } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  8. 1. Old Data If Errors 2. Model Data If Present

    "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  9. <label> Email <input type="text" name="user[email]" value="<?php echo load_value('email', $model); ?>"

    /> </label> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  10. function load_value( $name, $model = false ) { if( !empty($_SESSION['old'][$name])

    ) { return $_SESSION['old'][$name]; } if( $model instanceof Model) { return $model->{$name}; } return ''; } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  11. if( $errors = has_form_errors() ) { $_SESSION['old'] = $_POST['user']; }

    "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  12. Your email address and password are wrong. or Your login

    is not working out. "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  13. Placement 1. Inline with field 2. Flash on top 3.

    Alert by overlay "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  14. function has_form_errors() { $errors = []; if( is_not_email( $_POST['user']['email'] )

    ) { $errors['user']['email'] = 'Opps! Check that email for us.' } if( empty($errors) ) { return false; } return $errors; } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  15. <label> Email <input type="text" name="user[email]" value="<?php echo load_value('email', $model); ?>"

    /> <span> <?php echo $errors['user']['email'] ?? 'This field is required'; ?> </span> </label> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  16. <label> Email <input type="text" name="user[email]" value="<?php echo load_value('email', $model); ?>"

    /> <span class="<?php echo !empty($errors['user']['email']) ? 'animated fadeInDown' : ''; ?>" style="display: inline-block;" > <?php echo $errors['user']['email'] ?? 'This field is required'; ?> </span> </label> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  17. <?php if( !empty($errors['user']) ) : ?> <ul class="animated zoomInUp"> <?php

    foreach( $errors['user'] as $message ) : ?> <li><?php echo $message; ?></li> <?php endforeach; ?> </ul> <?php endif; ?> "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  18. <?php namespace App; class Form { public $model; public $method;

    public $action; public $group; public $errors; function __construct( $model, $method, $action, $group, $errors ) { $this->model = $model; $this->method = $method; $this->action = $action; $this->group = $group; $this->errors = $errors; } } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  19. class Form { .... function open() { $csrf = $_SESSION['_csrf'];

    $str = "<form action=\"{$this->action}\" method=\"POST\" accept-charset=\"utf-8\">"; $str .= "<input type=\"hidden\" name=\"_csrf\" value=\"{$csrf}\">"; $str .= "<input type=\"hidden\" name=\"_method\" value=\"$this->method\">"; return $str; } function close() { return '<p><input type="submit" value="Do Action" /></p></form>'; } function text($name) { return new \App\Field($name, $this); } } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  20. <?php namespace App; class Field { public $form; public $name;

    public $value; public $label; function __construct($name, $form) { $this->name = $name; $this->form = $form; $this->value = load_value($name, $this->form->model); } } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  21. class Field { .... function label($label) { $this->label = $label;

    return $this; } function __toString() { $group = $this->form->group; $name = $this->name; $errors = $this->form->errors; ob_start(); // echo HTML return ob_get_clean(); } } "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  22. // echo HTML ?> <label> <?php echo $this->label; ?> <input

    type="text" name="<?php echo "{$group}[{$name}]"; ?>" value="<?php echo htmlspecialchars($this->value); ?>" /> <span class="<?php echo !empty($errors[$group][$name]) ? 'animated fadeInDown' : ''; ?>" style="display: inline-block;" > <?php echo $errors[$group][$name] ?? 'This field is required'; ?> </span> </label> <?php "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  23. $form = new \App\Form($model, 'POST', 'user.php', 'user', $errors); echo $form->open();

    echo $form->text('email')->label('Email Address'); echo $form->close(); "Forms: Best Practices" by Kevin Dees // TypeRocket // Robojuice
  24. ! Make YOUR forms amazing ! "Forms: Best Practices" by

    Kevin Dees // TypeRocket // Robojuice