nine / solution 9.2
<?php
function addError(string $e)
{
global $errors;
!isset($errors) && ($errors = []);
$e == "noData" && ($e = "no data was submitted.");
$e == "nameMissing" && ($e = "you must have a name.");
$e == "emailMissing" && ($e = "you must have an email.");
$e == "passwordMissing" && ($e = "you must have a password.");
$e == "passwordVerifyMissing" && ($e = "you must verify your password.");
$e == "agreeMissing" && ($e = "you must agree to the terms and conditions.");
$e == "nameFailed" && ($e = "your name must be at least your first and last names.");
$e == "emailFailed" && ($e = "your email must be a valid email.");
$e == "passwordFailed" && ($e = "your password must match and contain at least one letter, one number and one symbol and be a minimum of eight characters long.");
!in_array($e, $errors) && ($errors[] = $e);
}
function showErrors()
{
global $errors;
if (!isset($errors) || !count($errors)) {
return;
}
print_r("<ul>");
foreach ($errors as $e) {
print_r("<li>" . $e . "</li>");
}
print_r("</ul>");
}
function doSignup()
{
global $errors;
if (!isset($_POST) || !count($_POST)) {
addError("noData");
return false;
}
$f = [];
foreach (["name", "email", "password", "passwordVerify"] as $k) {
!(($f[$k] = getPost($k)) && strlen($f[$k])) && addError($k . "Missing");
}
!($f["name"] = verifyName($f["name"])) && addError("nameFailed");
!($f["email"] = verifyEmail($f["email"])) && addError("emailFailed");
!($f["hash"] = verifyPassword($f["password"], $f["passwordVerify"])) && addError("passwordFailed");
// always good to remove passwords from arrays you'll be passing around
unset($f["password"], $f["passwordVerify"]);
!getPost("agree") == "yes" && addError("agreeMissing");
return isset($errors) && count($errors) ? false : verifyUser($f);
}
function verifyName(string $i)
{
$i = preg_replace('/[^\p{L}\s\'\-]/u', "", $i);
$i = trim(preg_replace("/\s+/", " ", $i));
$i = mb_convert_case($i, MB_CASE_TITLE);
return strlen($i) < 3 || count(explode(" ", $i)) < 2 ? false : $i;
}
function verifyEmail(string $e)
{
// check for basic pattern ([email protected])
if (!preg_match("/.+@.+\..+/", $e)) {
return false;
}
// convert to lowercase
$e = mb_convert_case($e, MB_CASE_LOWER);
// eliminate whitespace
$e = preg_replace("/\s+/", "", $e);
// simplify consecutive periods
$e = preg_replace("/\.+/", ".", $e);
// split email into name and domain because their constraints are different
$name = explode("@", $e)[0];
$domain = explode("@", $e)[1];
// check for both elements
if (!strlen($name) || !strlen($domain)) {
return false;
}
// remove leading/trailing periods
$name = trim($name, ".");
$domain = trim($domain, ".");
// remove invalid characters from both sections
$name = preg_replace("/[^a-z0-9!#%\$&\'\*\+\-\/\=\?\^_`\{|\}~\.]/", "", $name);
$domain = preg_replace("/[^a-z0-9\-\.]/", "", $domain);
// crop name to 64 characters
$name = substr($name, 0, 64);
// crop each domain segment to 63 characters
$domain = explode(".", $domain);
foreach ($domain as &$i) {
$i = substr($i, 0, 63);
}
$domain = implode(".", $domain);
// check for both elements again (in case all characters were invalid)
if (!strlen($name) || !strlen($domain)) {
return false;
}
// rebuild email
$e = $name . "@" . $domain;
return $e;
}
function verifyPassword(string $p, string $q)
{
return $p !== $q || !preg_match("/^(?=.*[A-Za-z])(?=.*\d)(?=.*[\W_]).{8,}$/", $p) ? false : password_hash($p, PASSWORD_DEFAULT);
}
function verifyUser(array $f)
{
if (!count($f) || !isset($f["name"]) || !isset($f["email"]) || !isset($f["hash"])) {
return false;
}
// you can hardcode the server url if you know it
$protocol = (!empty($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] !== "off") || $_SERVER["SERVER_PORT"] == 443 ? "https://" : "http://";
$url = $protocol . $_SERVER["HTTP_HOST"];
$f["id"] = uniqid("", true);
$f["verification"] = randomString(64);
$f["verificationLink"] = $url . "/verifyUser.php?" . $f["verification"];
print_r("<p>welcome, " . explode(" ", $f["name"])[0] . "! please check your email for a verification link.</p>");
return $f;
}
function randomString(int $length = 32, string $c = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", $s = "")
{
for ($i = 0; $i < $length; $i++) {
$s .= $c[random_int(0, strlen($c) - 1)];
}
return $s;
}
function getPost(string $k)
{
return !isset($_POST) || !isset($_POST[$k]) || !strlen($_POST[$k]) ? "" : $_POST[$k];
}
!($user = doSignup()) && showErrors();