{"id":2787,"date":"2021-09-30T06:41:09","date_gmt":"2021-09-30T06:41:09","guid":{"rendered":"https:\/\/phptutorial.net\/?page_id=2787"},"modified":"2025-04-09T07:58:14","modified_gmt":"2025-04-09T07:58:14","slug":"php-email-verification","status":"publish","type":"page","link":"https:\/\/www.phptutorial.net\/php-tutorial\/php-email-verification\/","title":{"rendered":"PHP Email Verification"},"content":{"rendered":"\r\n<p><strong>Summary<\/strong>: In this tutorial, you&#8217;ll learn how to securely verify the new account&#8217;s email address using an activation link.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='introduction-to-the-php-email-verification-for-new-accounts'>Introduction to the PHP email verification for new accounts <a href=\"#introduction-to-the-php-email-verification-for-new-accounts\" class=\"anchor\" id=\"introduction-to-the-php-email-verification-for-new-accounts\" title=\"Anchor for Introduction to the PHP email verification for new accounts\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>In previous tutorials, you learned how to <a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-registration-form\/\">create a registration form<\/a> that allows users to register for accounts. You also learned how to <a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-login\/\">build a login form<\/a> to enable users to sign in using the username and password.<\/p>\r\n\r\n\r\n\r\n<p>When users register for new accounts, they enter their email addresses. However, users can enter any email address because the system does not verify email.<\/p>\r\n\r\n\r\n\r\n<p>To verify users&#8217; email addresses, you can send a verification email to these email addresses and request users to open their emails and click an activation link.<\/p>\r\n\r\n\r\n\r\n<p>To do it, you follow the following steps when users register accounts:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li>Generate a unique activation code and set an expiration time, e.g., one day.<\/li>\r\n\r\n\r\n\r\n<li>Save the user record into the database and mark the user&#8217;s status as inactive. Also, save the hash of the activation code &amp; expiration time.<\/li>\r\n\r\n\r\n\r\n<li><a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-mail\/\">Send an email<\/a> with the activation link to the user&#8217;s email address. The activation link will contain the email address and activation code, e.g., <code>https:\/\/app.com\/activate.php?email=email&amp;activation_code=abcd<\/code><\/li>\r\n\r\n\r\n\r\n<li>Inform the user to activate the account via email.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<p>Hashing the activation code ensures that only the user who owns the email address can activate the account, not anyone else, even the admin, who can access the database.<\/p>\r\n\r\n\r\n\r\n<p>If users have not activated their accounts, they cannot log in.<\/p>\r\n\r\n\r\n\r\n<p>When users click the activation link in the email, you need to perform the following steps:<\/p>\r\n\r\n\r\n\r\n<ul class=\"wp-block-list\">\r\n<li><a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-sanitize-input\/\">Sanitize<\/a> and <a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-validation\/\">validate<\/a> the email and activation code.<\/li>\r\n\r\n\r\n\r\n<li>Find the inactive user with the email address. If no user record exists, redirect to the registration form.<\/li>\r\n\r\n\r\n\r\n<li>If a user record exists and the activation code has expired, delete it from the database and redirect to the registration form.<\/li>\r\n\r\n\r\n\r\n<li>Otherwise, match the activation code with the hash of the activation code stored in the database. If they match, mark the user record as active and redirect to the login page.<\/li>\r\n<\/ul>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='recreate-the-users-table'>Recreate the users table <a href=\"#recreate-the-users-table\" class=\"anchor\" id=\"recreate-the-users-table\" title=\"Anchor for Recreate the users table\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>First, drop <a href=\"https:\/\/www.mysqltutorial.org\/mysql-basics\/mysql-drop-table\/\" target=\"_blank\" rel=\"noreferrer noopener\">table<\/a> <code>user<\/code> from the <code>auth<\/code> database:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql\"><span class=\"hljs-keyword\">DROP<\/span> <span class=\"hljs-keyword\">TABLE<\/span> <span class=\"hljs-keyword\">users<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Second, <a href=\"https:\/\/www.mysqltutorial.org\/mysql-basics\/mysql-create-table\/\">create a table<\/a> <code>users<\/code> with the new columns <code>active<\/code>, <code>activation_code<\/code>, <code>activation_at<\/code>, <code>activation_expiry<\/code>:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"SQL (Structured Query Language)\" data-shcb-language-slug=\"sql\"><span><code class=\"hljs language-sql\"><span class=\"hljs-keyword\">CREATE<\/span> <span class=\"hljs-keyword\">TABLE<\/span> <span class=\"hljs-keyword\">users<\/span>\r\n(\r\n    <span class=\"hljs-keyword\">id<\/span>                <span class=\"hljs-built_in\">int<\/span> auto_increment PRIMARY <span class=\"hljs-keyword\">KEY<\/span>,\r\n    username          <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">25<\/span>)  <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    email             <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    <span class=\"hljs-keyword\">password<\/span>          <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    is_admin          <span class=\"hljs-built_in\">tinyint<\/span>(<span class=\"hljs-number\">1<\/span>)   <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span> <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-number\">0<\/span>,\r\n    active            <span class=\"hljs-built_in\">tinyint<\/span>(<span class=\"hljs-number\">1<\/span>)            <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-number\">0<\/span>,\r\n    activation_code   <span class=\"hljs-built_in\">varchar<\/span>(<span class=\"hljs-number\">255<\/span>) <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    activation_expiry datetime     <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    activated_at      datetime              <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-literal\">NULL<\/span>,\r\n    created_at        <span class=\"hljs-built_in\">timestamp<\/span>    <span class=\"hljs-keyword\">NOT<\/span> <span class=\"hljs-literal\">NULL<\/span> <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-keyword\">current_timestamp<\/span>(),\r\n    updated_at        datetime              <span class=\"hljs-keyword\">DEFAULT<\/span> <span class=\"hljs-keyword\">current_timestamp<\/span>() <span class=\"hljs-keyword\">ON<\/span> <span class=\"hljs-keyword\">UPDATE<\/span> <span class=\"hljs-keyword\">current_timestamp<\/span>()\r\n\r\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">SQL (Structured Query Language)<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">sql<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>The following explains the meaning of the new columns.<\/p>\r\n\r\n\r\n\r\n<p>The value of the <code>active<\/code> column defaults to 0. Users registering for accounts without verifying their email addresses will be inactive by default.<\/p>\r\n\r\n\r\n\r\n<p>The <code>activation_code<\/code> column will store the hash of the activation code. Its length should be sufficient to store the string returned by the <code>password_hash()<\/code> function.<\/p>\r\n\r\n\r\n\r\n<p>It&#8217;s important to notice that the hash will be truncated if the <code>activation_code<\/code> column isn&#8217;t long enough. It&#8217;ll cause the <code>password_verify()<\/code> function to fail to match the activation code with the hash.<\/p>\r\n\r\n\r\n\r\n<p>The <code>activation_expiry<\/code> column stores the expiration time to use the activation code before expiry. The expiration time ensures that the activation code cannot be used if the email address is compromised after the expiration time.<\/p>\r\n\r\n\r\n\r\n<p>The <code>activated_at<\/code> column stores the date and time when users activate their accounts.<\/p>\r\n\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='project-structure'>Project structure <a href=\"#project-structure\" class=\"anchor\" id=\"project-structure\" title=\"Anchor for Project structure\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>Let&#8217;s review the current project structure before adding the email verification functions:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">\u251c\u2500\u2500 config\r\n|  \u251c\u2500\u2500 app.php\r\n|  \u2514\u2500\u2500 database.php\r\n\u251c\u2500\u2500 <span class=\"hljs-keyword\">public<\/span>\r\n|  \u251c\u2500\u2500 index.php\r\n|  \u251c\u2500\u2500 login.php\r\n|  \u251c\u2500\u2500 logout.php\r\n|  \u2514\u2500\u2500 register.php\r\n\u2514\u2500\u2500 src\r\n    \u251c\u2500\u2500 auth.php\r\n    \u251c\u2500\u2500 bootstrap.php\r\n    \u251c\u2500\u2500 inc\r\n    |  \u251c\u2500\u2500 footer.php\r\n    |  \u2514\u2500\u2500 header.php\r\n    \u251c\u2500\u2500 libs\r\n    |  \u251c\u2500\u2500 connection.php\r\n    |  \u251c\u2500\u2500 filter.php\r\n    |  \u251c\u2500\u2500 flash.php\r\n    |  \u251c\u2500\u2500 helpers.php\r\n    |  \u251c\u2500\u2500 sanitization.php\r\n    |  \u2514\u2500\u2500 validation.php\r\n    \u251c\u2500\u2500 login.php\r\n    \u2514\u2500\u2500 register.php<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='modify-the-functions-in-auth-php-file'>Modify the functions in auth.php file <a href=\"#modify-the-functions-in-auth-php-file\" class=\"anchor\" id=\"modify-the-functions-in-auth-php-file\" title=\"Anchor for Modify the functions in auth.php file\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>The following adds the activation code and expiry parameter to the <code>register_user()<\/code> function. By default, the expiration time is one day ( <code>1 * 24 * 60 * 60<\/code>).<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">register_user<\/span><span class=\"hljs-params\">(string $email, string $username, string $password, string $activation_code, int $expiry = <span class=\"hljs-number\">1<\/span> * <span class=\"hljs-number\">24<\/span>  * <span class=\"hljs-number\">60<\/span> * <span class=\"hljs-number\">60<\/span>, bool $is_admin = false)<\/span>: <span class=\"hljs-title\">bool<\/span>\r\n<\/span>{\r\n    $sql = <span class=\"hljs-string\">'INSERT INTO users(username, email, password, is_admin, activation_code, activation_expiry)\r\n            VALUES(:username, :email, :password, :is_admin, :activation_code,:activation_expiry)'<\/span>;\r\n\r\n    $statement = db()-&gt;prepare($sql);\r\n\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':username'<\/span>, $username);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':email'<\/span>, $email);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':password'<\/span>, password_hash($password, PASSWORD_BCRYPT));\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':is_admin'<\/span>, (int)$is_admin, PDO::PARAM_INT);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':activation_code'<\/span>, password_hash($activation_code, PASSWORD_DEFAULT));\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':activation_expiry'<\/span>, date(<span class=\"hljs-string\">'Y-m-d H:i:s'<\/span>,  time() + $expiry));\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> $statement-&gt;execute();\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>The <code>register_user()<\/code> function uses the <code><a href=\"https:\/\/phptutorial.net\/php-tutorial\/php-password_hash\/\">password_hash()<\/a><\/code> function to hash the activation code.<\/p>\r\n\r\n\r\n\r\n<p>The <code>find_user_by_username()<\/code> function includes the <code>active<\/code> column in the result:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">find_user_by_username<\/span><span class=\"hljs-params\">(string $username)<\/span>\r\n<\/span>{\r\n    $sql = <span class=\"hljs-string\">'SELECT username, password, active, email\r\n            FROM users\r\n            WHERE username=:username'<\/span>;\r\n\r\n    $statement = db()-&gt;prepare($sql);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':username'<\/span>, $username);\r\n    $statement-&gt;execute();\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> $statement-&gt;fetch(PDO::FETCH_ASSOC);\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>The following defines a new function <code>is_user_active()<\/code> that returns true if a user is active:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">is_user_active<\/span><span class=\"hljs-params\">($user)<\/span>\r\n<\/span>{\r\n    <span class=\"hljs-keyword\">return<\/span> (int)$user&#91;<span class=\"hljs-string\">'active'<\/span>] === <span class=\"hljs-number\">1<\/span>;\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>The <code>login()<\/code> function should allow only active users to sign in:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">login<\/span><span class=\"hljs-params\">(string $username, string $password)<\/span>: <span class=\"hljs-title\">bool<\/span>\r\n<\/span>{\r\n    $user = find_user_by_username($username);\r\n\r\n    <span class=\"hljs-keyword\">if<\/span> ($user &amp;&amp; is_user_active($user) &amp;&amp; password_verify($password, $user&#91;<span class=\"hljs-string\">'password'<\/span>])) {\r\n        <span class=\"hljs-comment\">\/\/ prevent session fixation attack<\/span>\r\n        session_regenerate_id();\r\n\r\n        <span class=\"hljs-comment\">\/\/ set username in the session<\/span>\r\n        $_SESSION&#91;<span class=\"hljs-string\">'user_id'<\/span>] = $user&#91;<span class=\"hljs-string\">'id'<\/span>];\r\n        $_SESSION&#91;<span class=\"hljs-string\">'username'<\/span>] = $user&#91;<span class=\"hljs-string\">'username'<\/span>];\r\n\r\n        <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">true<\/span>;\r\n    }\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">false<\/span>;\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='define-functions-that-deal-with-email-verification'>Define functions that deal with email verification <a href=\"#define-functions-that-deal-with-email-verification\" class=\"anchor\" id=\"define-functions-that-deal-with-email-verification\" title=\"Anchor for Define functions that deal with email verification\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>We&#8217;ll add the functions that deal with email verification to the <code>auth.php<\/code> file.<\/p>\r\n\r\n\r\n\r\n<p>First, create a new file <code>app.php<\/code> in the <code>config<\/code> folder and define the following constants:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\r\n\r\n<span class=\"hljs-keyword\">const<\/span> APP_URL = <span class=\"hljs-string\">'http:\/\/localhost\/auth'<\/span>;\r\n<span class=\"hljs-keyword\">const<\/span> SENDER_EMAIL_ADDRESS = <span class=\"hljs-string\">'no-reply@email.com'<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>We&#8217;ll use these constants for sending activation emails to users. To use these constants, you need to include the <code>app.php<\/code> file in the <code>bootstrap.php<\/code> file:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\r\n\r\nsession_start();\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/..\/config\/app.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/..\/config\/database.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/helpers.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/flash.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/sanitization.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/validation.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/filter.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/libs\/connection.php'<\/span>;\r\n<span class=\"hljs-keyword\">require_once<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/auth.php'<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Second, define a function that generates a uniquely random activation code:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">generate_activation_code<\/span><span class=\"hljs-params\">()<\/span>: <span class=\"hljs-title\">string<\/span>\r\n<\/span>{\r\n    <span class=\"hljs-keyword\">return<\/span> bin2hex(random_bytes(<span class=\"hljs-number\">16<\/span>));\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Third, define a function that sends an email verification with an activation link.<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">send_activation_email<\/span><span class=\"hljs-params\">(string $email, string $activation_code)<\/span>: <span class=\"hljs-title\">void<\/span>\r\n<\/span>{\r\n    <span class=\"hljs-comment\">\/\/ create the activation link<\/span>\r\n    $activation_link = APP_URL . <span class=\"hljs-string\">\"\/activate.php?email=$email&amp;activation_code=$activation_code\"<\/span>;\r\n\r\n    <span class=\"hljs-comment\">\/\/ set email subject &amp; body<\/span>\r\n    $subject = <span class=\"hljs-string\">'Please activate your account'<\/span>;\r\n    $message = <span class=\"hljs-string\">&lt;&lt;&lt;MESSAGE\r\n            Hi,\r\n            Please click the following link to activate your account:\r\n            <span class=\"hljs-subst\">$activation_link<\/span>\r\n            MESSAGE;\r\n    \/\/ email header\r\n    <span class=\"hljs-subst\">$header<\/span> = \"From:\" . SENDER_EMAIL_ADDRESS;\r\n\r\n    \/\/ send the email\r\n    mail(<span class=\"hljs-subst\">$email<\/span>, <span class=\"hljs-subst\">$subject<\/span>, nl2br(<span class=\"hljs-subst\">$message<\/span>), <span class=\"hljs-subst\">$header<\/span>);\r\n\r\n}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Suppose the app&#8217;s URL is <code>http:\/\/localhost\/auth<\/code>, the activation URL will look like this:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">http:<span class=\"hljs-comment\">\/\/localhost\/auth\/activate.php?email=john@phptutorial.net&amp;activation_code=e01e5c9a028d58d888ff2555b971c882<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>The <code>send_activation_email()<\/code> function uses the built-in <code>mail()<\/code> function for sending emails.<\/p>\r\n\r\n\r\n\r\n<p>Fourth, define a function that deletes a user by id and status. By default, it deletes an inactive user by id.<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">delete_user_by_id<\/span><span class=\"hljs-params\">(int $id, int $active = <span class=\"hljs-number\">0<\/span>)<\/span>\r\n<\/span>{\r\n    $sql = <span class=\"hljs-string\">'DELETE FROM users\r\n            WHERE id =:id and active=:active'<\/span>;\r\n\r\n    $statement = db()-&gt;prepare($sql);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':id'<\/span>, $id, PDO::PARAM_INT);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':active'<\/span>, $active, PDO::PARAM_INT);\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> $statement-&gt;execute();\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Fifth, define a function that finds an unverified user by an email and activation code. If the activation code is expired, the function also deletes the user record by calling the <code>delete_user_by_id()<\/code> function.<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">find_unverified_user<\/span><span class=\"hljs-params\">(string $activation_code, string $email)<\/span>\r\n<\/span>{\r\n\r\n    $sql = <span class=\"hljs-string\">'SELECT id, activation_code, activation_expiry &lt; now() as expired\r\n            FROM users\r\n            WHERE active = 0 AND email=:email'<\/span>;\r\n\r\n    $statement = db()-&gt;prepare($sql);\r\n\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':email'<\/span>, $email);\r\n    $statement-&gt;execute();\r\n\r\n    $user = $statement-&gt;fetch(PDO::FETCH_ASSOC);\r\n\r\n    <span class=\"hljs-keyword\">if<\/span> ($user) {\r\n        <span class=\"hljs-comment\">\/\/ already expired, delete the in active user with expired activation code<\/span>\r\n        <span class=\"hljs-keyword\">if<\/span> ((int)$user&#91;<span class=\"hljs-string\">'expired'<\/span>] === <span class=\"hljs-number\">1<\/span>) {\r\n            delete_user_by_id($user&#91;<span class=\"hljs-string\">'id'<\/span>]);\r\n            <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">null<\/span>;\r\n        }\r\n        <span class=\"hljs-comment\">\/\/ verify the password<\/span>\r\n        <span class=\"hljs-keyword\">if<\/span> (password_verify($activation_code, $user&#91;<span class=\"hljs-string\">'activation_code'<\/span>])) {\r\n            <span class=\"hljs-keyword\">return<\/span> $user;\r\n        }\r\n    }\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">null<\/span>;\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Sixth, define a new <code>activate_user()<\/code> function that activates a user by an id:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">activate_user<\/span><span class=\"hljs-params\">(int $user_id)<\/span>: <span class=\"hljs-title\">bool<\/span>\r\n<\/span>{\r\n    $sql = <span class=\"hljs-string\">'UPDATE users\r\n            SET active = 1,\r\n                activated_at = CURRENT_TIMESTAMP\r\n            WHERE id=:id'<\/span>;\r\n\r\n    $statement = db()-&gt;prepare($sql);\r\n    $statement-&gt;bindValue(<span class=\"hljs-string\">':id'<\/span>, $user_id, PDO::PARAM_INT);\r\n\r\n    <span class=\"hljs-keyword\">return<\/span> $statement-&gt;execute();\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='modify-the-register-php-page'>Modify the register.php page <a href=\"#modify-the-register-php-page\" class=\"anchor\" id=\"modify-the-register-php-page\" title=\"Anchor for Modify the register.php page\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>The <code>src\/register.php<\/code> needs to incorporate the logic to handle the email verification logic.<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\r\n\r\n<span class=\"hljs-keyword\">if<\/span> (is_user_logged_in()) {\r\n    redirect_to(<span class=\"hljs-string\">'index.php'<\/span>);\r\n}\r\n\r\n$errors = &#91;];\r\n$inputs = &#91;];\r\n\r\n<span class=\"hljs-keyword\">if<\/span> (is_post_request()) {\r\n    $fields = &#91;\r\n        <span class=\"hljs-string\">'username'<\/span> =&gt; <span class=\"hljs-string\">'string | required | alphanumeric | between: 3, 25 | unique: users, username'<\/span>,\r\n        <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'email | required | email | unique: users, email'<\/span>,\r\n        <span class=\"hljs-string\">'password'<\/span> =&gt; <span class=\"hljs-string\">'string | required | secure'<\/span>,\r\n        <span class=\"hljs-string\">'password2'<\/span> =&gt; <span class=\"hljs-string\">'string | required | same: password'<\/span>,\r\n        <span class=\"hljs-string\">'agree'<\/span> =&gt; <span class=\"hljs-string\">'string | required'<\/span>\r\n    ];\r\n\r\n    <span class=\"hljs-comment\">\/\/ custom messages<\/span>\r\n    $messages = &#91;\r\n        <span class=\"hljs-string\">'password2'<\/span> =&gt; &#91;\r\n            <span class=\"hljs-string\">'required'<\/span> =&gt; <span class=\"hljs-string\">'Please enter the password again'<\/span>,\r\n            <span class=\"hljs-string\">'same'<\/span> =&gt; <span class=\"hljs-string\">'The password does not match'<\/span>\r\n        ],\r\n        <span class=\"hljs-string\">'agree'<\/span> =&gt; &#91;\r\n            <span class=\"hljs-string\">'required'<\/span> =&gt; <span class=\"hljs-string\">'You need to agree to the term of services to register'<\/span>\r\n        ]\r\n    ];\r\n\r\n    &#91;$inputs, $errors] = filter($_POST, $fields, $messages);\r\n\r\n    <span class=\"hljs-keyword\">if<\/span> ($errors) {\r\n        redirect_with(<span class=\"hljs-string\">'register.php'<\/span>, &#91;\r\n            <span class=\"hljs-string\">'inputs'<\/span> =&gt; $inputs,\r\n            <span class=\"hljs-string\">'errors'<\/span> =&gt; $errors\r\n        ]);\r\n    }\r\n\r\n    $activation_code = generate_activation_code();\r\n\r\n    <span class=\"hljs-keyword\">if<\/span> (register_user($inputs&#91;<span class=\"hljs-string\">'email'<\/span>], $inputs&#91;<span class=\"hljs-string\">'username'<\/span>], $inputs&#91;<span class=\"hljs-string\">'password'<\/span>], $activation_code)) {\r\n\r\n        <span class=\"hljs-comment\">\/\/ send the activation email<\/span>\r\n        send_activation_email($inputs&#91;<span class=\"hljs-string\">'email'<\/span>], $activation_code);\r\n\r\n        redirect_with_message(\r\n            <span class=\"hljs-string\">'login.php'<\/span>,\r\n            <span class=\"hljs-string\">'Please check your email to activate your account before signing in'<\/span>\r\n        );\r\n    }\r\n\r\n} <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> (is_get_request()) {\r\n    &#91;$errors, $inputs] = session_flash(<span class=\"hljs-string\">'errors'<\/span>, <span class=\"hljs-string\">'inputs'<\/span>);\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>How it works.<\/p>\r\n\r\n\r\n\r\n<p>First, generate an activation code:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$activation_code = generate_activation_code();<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Second, register the user with the activation code:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">register_user($inputs&#91;<span class=\"hljs-string\">'email'<\/span>], $inputs&#91;<span class=\"hljs-string\">'username'<\/span>], $inputs&#91;<span class=\"hljs-string\">'password'<\/span>], $activation_code)<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Third, send an email to the user&#8217;s email address by calling the <code>send_activation_email()<\/code> function:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">send_activation_email($inputs&#91;<span class=\"hljs-string\">'email'<\/span>], $activation_code);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Finally, redirect the user to the login page and show a flash message that requests the user to activate the account via email:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">redirect_with_message(\r\n    <span class=\"hljs-string\">'login.php'<\/span>,\r\n    <span class=\"hljs-string\">'Please check your email to activate your account before signing in'<\/span>\r\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<h2 class=\"wp-block-heading\" id='create-the-activate-php-page'>Create the activate.php page <a href=\"#create-the-activate-php-page\" class=\"anchor\" id=\"create-the-activate-php-page\" title=\"Anchor for Create the activate.php page\">#<\/a><\/h2>\r\n\r\n\r\n\r\n<p>To allow users to activate their accounts after registration, you can create a new <code>activate.php<\/code> page in the <code>public<\/code> folder and use the following page:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-meta\">&lt;?php<\/span>\r\n\r\n<span class=\"hljs-keyword\">require<\/span> <span class=\"hljs-keyword\">__DIR__<\/span> . <span class=\"hljs-string\">'\/..\/src\/bootstrap.php'<\/span>;\r\n\r\n<span class=\"hljs-keyword\">if<\/span> (is_get_request()) {\r\n\r\n    <span class=\"hljs-comment\">\/\/ sanitize the email &amp; activation code<\/span>\r\n    &#91;$inputs, $errors] = filter($_GET, &#91;\r\n        <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'string | required | email'<\/span>,\r\n        <span class=\"hljs-string\">'activation_code'<\/span> =&gt; <span class=\"hljs-string\">'string | required'<\/span>\r\n    ]);\r\n\r\n    <span class=\"hljs-keyword\">if<\/span> (!$errors) {\r\n\r\n        $user = find_unverified_user($inputs&#91;<span class=\"hljs-string\">'activation_code'<\/span>], $inputs&#91;<span class=\"hljs-string\">'email'<\/span>]);\r\n\r\n        <span class=\"hljs-comment\">\/\/ if user exists and activate the user successfully<\/span>\r\n        <span class=\"hljs-keyword\">if<\/span> ($user &amp;&amp; activate_user($user&#91;<span class=\"hljs-string\">'id'<\/span>])) {\r\n            redirect_with_message(\r\n                <span class=\"hljs-string\">'login.php'<\/span>,\r\n                <span class=\"hljs-string\">'You account has been activated successfully. Please login here.'<\/span>\r\n            );\r\n        }\r\n    }\r\n}\r\n\r\n<span class=\"hljs-comment\">\/\/ redirect to the register page in other cases<\/span>\r\nredirect_with_message(\r\n    <span class=\"hljs-string\">'register.php'<\/span>,\r\n    <span class=\"hljs-string\">'The activation link is not valid, please register again.'<\/span>,\r\n    FLASH_ERROR\r\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>How the activate.php works.<\/p>\r\n\r\n\r\n\r\n<p>First, sanitize and validate the email and activation code:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">&#91;$inputs, $errors] = filter($_GET, &#91;\r\n    <span class=\"hljs-string\">'email'<\/span> =&gt; <span class=\"hljs-string\">'string | required | email'<\/span>,\r\n    <span class=\"hljs-string\">'activation_code'<\/span> =&gt; <span class=\"hljs-string\">'string | required'<\/span>\r\n]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Second, find the unverified user based on the email and verification code if there are no validation errors. The <code>find_unverified_user()<\/code> will also delete the unverified user if the expiration time has expired.<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">$user = find_unverified_user($inputs&#91;<span class=\"hljs-string\">'activation_code'<\/span>], $inputs&#91;<span class=\"hljs-string\">'email'<\/span>]);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Third, activate the user and redirect to the login.php page:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-keyword\">if<\/span> ($user &amp;&amp; activate_user($user&#91;<span class=\"hljs-string\">'id'<\/span>])) {\r\n    redirect_with_message(\r\n        <span class=\"hljs-string\">'login.php'<\/span>,\r\n        <span class=\"hljs-string\">'You account has been activated successfully. Please login here.'<\/span>\r\n    );\r\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p>Finally, redirect to the <code>registration.php<\/code> if there&#8217;s an error:<\/p>\r\n\r\n\r\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\">redirect_with_message(\r\n    <span class=\"hljs-string\">'register.php'<\/span>,\r\n    <span class=\"hljs-string\">'The activation link is not valid, please register again.'<\/span>,\r\n    FLASH_ERROR\r\n);<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\r\n\r\n\r\n<p><a href=\"https:\/\/phptutorial.net\/wp-content\/uploads\/2025\/04\/auth_email_verification.zip\" target=\"_blank\" rel=\"noreferrer noopener\">Download the login with the email verification source code<\/a><\/p>\r\n\r\n\r\n\r\n<p>In this tutorial, you&#8217;ve learned how to implement email verification for user accounts in PHP.<\/p>\r\n<div class=\"helpful-block-content\" data-title=\"\">\n\t<header>\n\t\t<div class=\"wth-question\">Did you find this tutorial useful?<\/div>\n\t\t<div class=\"wth-thumbs\">\n\t\t\t<button\n\t\t\t\tdata-post=\"2787\"\n\t\t\t\tdata-post-url=\"https:\/\/www.phptutorial.net\/php-tutorial\/php-email-verification\/\"\n\t\t\t\tdata-post-title=\"PHP Email Verification\"\n\t\t\t\tdata-response=\"1\"\n\t\t\t\tclass=\"wth-btn-rounded wth-yes-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t\tclass=\"feather feather-thumbs-up block w-full h-full\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M14 9V5a3 3 0 0 0-3-3l-4 9v11h11.28a2 2 0 0 0 2-1.7l1.38-9a2 2 0 0 0-2-2.3zM7 22H4a2 2 0 0 1-2-2v-7a2 2 0 0 1 2-2h3\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> Yes <\/span>\n\t\t\t<\/button>\n\n\t\t\t<button\n\t\t\t\tdata-response=\"0\"\n\t\t\t\tdata-post=\"2787\"\n\t\t\t\tdata-post-url=\"https:\/\/www.phptutorial.net\/php-tutorial\/php-email-verification\/\"\n\t\t\t\tdata-post-title=\"PHP Email Verification\"\n\t\t\t\tclass=\"wth-btn-rounded wth-no-btn\"\n\t\t\t>\n\t\t\t\t<svg\n\t\t\t\t\txmlns=\"http:\/\/www.w3.org\/2000\/svg\"\n\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\tfill=\"none\"\n\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\tstroke-width=\"2\"\n\t\t\t\t\tstroke-linecap=\"round\"\n\t\t\t\t\tstroke-linejoin=\"round\"\n\t\t\t\t>\n\t\t\t\t\t<path\n\t\t\t\t\t\td=\"M10 15v4a3 3 0 0 0 3 3l4-9V2H5.72a2 2 0 0 0-2 1.7l-1.38 9a2 2 0 0 0 2 2.3zm7-13h2.67A2.31 2.31 0 0 1 22 4v7a2.31 2.31 0 0 1-2.33 2H17\"\n\t\t\t\t\t><\/path>\n\t\t\t\t<\/svg>\n\t\t\t\t<span class=\"sr-only\"> No <\/span>\n\t\t\t<\/button>\n\t\t<\/div>\n\t<\/header>\n\n\t<div class=\"wth-form hidden\">\n\t\t<div class=\"wth-form-wrapper\">\n\t\t\t<div class=\"wth-title\"><\/div>\n\t\t\t\n\t\t\t<textarea class=\"wth-message\"><\/textarea>\n\n\t\t\t<button class=\"btn btn-primary wth-btn-submit\">Send<\/button>\n\t\t\t<button class=\"btn wth-btn-cancel\">Cancel<\/button>\n\t\t\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this tutorial, you&#8217;ll learn how to verify the new account&#8217;s email address securely via an activation link.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":15,"menu_order":104,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-2787","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/pages\/2787","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/comments?post=2787"}],"version-history":[{"count":5,"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/pages\/2787\/revisions"}],"predecessor-version":[{"id":3345,"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/pages\/2787\/revisions\/3345"}],"up":[{"embeddable":true,"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/pages\/15"}],"wp:attachment":[{"href":"https:\/\/www.phptutorial.net\/wp-json\/wp\/v2\/media?parent=2787"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}