Comment

PHP লগিন সিস্টেম (Login System)

PHP নিবন্ধন ফর্ম (Registration)

Estimated reading: 22 minutes 29 views Contributors

সারাংশ: সারাংশ: এই টিউটোরিয়ালে, আপনি শিখবেন কিভাবে স্ক্র্যাচ থেকে একটি PHP রেজিস্ট্রেশন ফর্ম তৈরি করতে হয়।

PHP নিবন্ধন ফর্ম পরিচিতি

এই টিউটোরিয়ালে, আপনি নিম্নলিখিত ইনপুট ক্ষেত্রগুলি নিয়ে গঠিত একটি ব্যবহারকারী নিবন্ধন ফর্ম তৈরি করবেন:

  • ব্যবহারকারীর নাম
  • ইমেইল
  • পাসওয়ার্ড
  • পাসওয়ার্ড নিশ্চিতকরণ
  • চুক্তি চেকবক্স
  • রেজিস্টার বাটন

যখন একজন ব্যবহারকারী ফর্মটি পূরণ করেন এবং নিবন্ধন বাটন ক্লিক করেন, তখন আপনাকে এটি করতে হবে:

  • স্যানিটাইজ করুন এবং ব্যবহারকারীর ইনপুট যাচাই করুন।
  • ফর্ম ডেটা বৈধ না হলে, ব্যবহারকারীর ইনপুট এবং ত্রুটি বার্তা সহ ফর্মটি দেখান ৷
  • ফর্ম ডেটা বৈধ হলে, ব্যবহারকারীদের ডাটাবেস টেবিলে নতুন ব্যবহারকারীকে সন্নিবেশ করুন, একটি ফ্ল্যাশ বার্তা সেট করুন, ব্যবহারকারীকে login.php পৃষ্ঠায় পুনঃনির্দেশ করুন এবং ফ্ল্যাশ বার্তাটি প্রদর্শন করুন ৷

নোট করুন যে, আপনি পরবর্তী টিউটোরিয়ালে কীভাবে একটি লগইন ফর্ম তৈরি করবেন তা শিখবেন।

সেটআপ প্রকল্প কাঠামো

প্রথমে, একটি প্রকল্প রুট ফোল্ডার তৈরি করুন, যেমন, auth।

দ্বিতীয়ত, প্রকল্প রুট ফোল্ডারের অধীনে নিম্নলিখিত ফোল্ডারগুলি তৈরি করুন যেমন

├── config
├── public
└── src
   ├── inc
   └── libs

নিম্নলিখিত প্রতিটি ফোল্ডারের উদ্দেশ্য বর্ণনা করে:

FolderPurpose
configকনফিগারেশন ফাইল সংরক্ষণ করুন যেমন ডাটাবেস কনফিগারেশন
publicব্যবহারকারীদের দ্বারা সরাসরি অ্যাক্সেস করা পাবলিক ফাইল সংরক্ষণ করুন
srcসোর্স ফাইলগুলি সংরক্ষণ করুন যা জনসাধারণের কাছে প্রকাশ করা উচিত নয়
src/incএকটি পৃষ্ঠার শিরোনাম এবং ফুটার হিসাবে সাধারণত অন্তর্ভুক্ত ফাইল সংরক্ষণ করুন
src/libsলাইব্রেরি ফাইল সংরক্ষণ করুন, যেমন, বৈধতা, স্যানিটাইজেশন, ইত্যাদি।

URL থেকে পাবলিক সরান

প্রথমে, পাবলিক ফোল্ডারে register.php তৈরি করুন। register.php পৃষ্ঠা অ্যাক্সেস করতে, আপনাকে নিম্নলিখিত URL ব্যবহার করতে হবে:

http://localhost/auth/public/register.php

To remove the public from the above URL, you can use the URL Rewrite module of the Apache Web Server. To do it, you need to use a .htaccess file.

দ্বিতীয়ত, প্রজেক্ট রুট ফোল্ডারে (auth) .htaccess ফাইল তৈরি করুন এবং নিম্নলিখিত কোডটি ব্যবহার করুন:

<IfModule mod_rewrite.c>
    RewriteEngine on
    RewriteRule ^$ public/ [L]
    RewriteRule (.*) public/$1 [L]
</IfModule>

উপরের নির্দেশাবলী Apache কে ইউআরএল থেকে জনসাধারণকে সরানোর নির্দেশ দেয়। আপনি URL http://localhost/auth/register.php খুললে, আপনি একটি ত্রুটি দেখতে পাবেন।

ত্রুটিটি ঠিক করতে, আপনাকে সর্বজনীন ফোল্ডারে আরেকটি .htaccess ফাইলের প্রয়োজন হবে৷ তৃতীয়ত, পাবলিক ফোল্ডারে আরেকটি .htaccess তৈরি করুন এবং নিম্নলিখিত নির্দেশাবলী ব্যবহার করুন:

<IfModule mod_rewrite.c>
    Options -Multiviews
    RewriteEngine On
    RewriteBase /auth/public
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME} !-f
</IfModule>

এখন, আপনি এইভাবে URL-এ /public/ ব্যবহার না করে register.php পৃষ্ঠা অ্যাক্সেস করতে পারেন:

http://localhost/auth/register.php

রেজিস্ট্রেশন ফর্ম তৈরি করুন

প্রথমে, register.php ফাইলে একটি নিবন্ধন ফর্ম তৈরি করুন:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
    <title>Register</title>
</head>
<body>
<main>
    <form action="register.php" method="post">
        <h1>Sign Up</h1>
        <div>
            <label for="username">Username:</label>
            <input type="text" name="username" id="username">
        </div>
        <div>
            <label for="email">Email:</label>
            <input type="email" name="email" id="email">
        </div>
        <div>
            <label for="password">Password:</label>
            <input type="password" name="password" id="password">
        </div>
        <div>
            <label for="password2">Password Again:</label>
            <input type="password" name="password2" id="password2">
        </div>
        <div>
            <label for="agree">
                <input type="checkbox" name="agree" id="agree" value="yes"/> I agree
                with the
                <a href="#" title="term of services">term of services</a>
            </label>
        </div>
        <button type="submit">Register</button>
        <footer>Already a member? <a href="login.php">Login here</a></footer>
    </form>
</main>
</body>
</html>

আপনি যদি URL http://localhost/auth/register.php অ্যাক্সেস করেন, আপনি একটি ব্যবহারকারী নিবন্ধন ফর্ম দেখতে পাবেন।

রেজিস্ট্রেশন ফর্ম আরো সুসংগঠিত করুন

প্রথমে, src/inc ফোল্ডারে header.php ফাইলটি তৈরি করুন এবং register.php ফাইলের হেডার বিভাগটিকে header.php ফাইলে কপি করুন

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
    <title>Register</title>
</head>
<body>
<main>

দ্বিতীয়ত, src/inc ফোল্ডারে footer.php ফাইলটি তৈরি করুন এবং register.php ফাইলের ফুটার অংশটিকে footer.php ফাইলে অনুলিপি করুন: file:

</main>
</body>
</html>

hird, register.php ফাইলে inc ফোল্ডার থেকে header.php এবং footer.php ফাইলগুলি অন্তর্ভুক্ত করুন। সাধারণত, আপনি প্রয়োজন ব্যবহার করুন বা নির্মাণ অন্তর্ভুক্ত করুন।

যাইহোক, আপনি অন্য ফাইলগুলিতে header.php এবং footer.php ফাইলগুলি অন্তর্ভুক্ত করতে চাইতে পারেন, যেমন, login.php৷ অতএব, পৃষ্ঠার শিরোনাম এইভাবে স্থির করা উচিত নয়:

<title>Register</title>

<title> ট্যাগকে গতিশীল করতে, আপনি src/libs ফোল্ডারে একটি helpers.php ফাইল তৈরি করতে পারেন এবং view() ফাংশনটি সংজ্ঞায়িত করতে পারেন যা একটি PHP ফাইল থেকে কোড লোড করে এবং এতে ডেটা পাঠায়:

function view(string $filename, array $data = []): void
{
    // create variables from the associative array
    foreach ($data as $key => $value) {
        $$key = $value;
    }
    require_once __DIR__ . '/../inc/' . $filename . '.php';
}

এই view() ফাংশনটি .php ফাইল এক্সটেনশন উল্লেখ না করেই একটি ফাইল থেকে কোড লোড করে।

view() ফাংশন আপনাকে অ্যাসোসিয়েটিভ অ্যারে হিসাবে। অন্তর্ভুক্ত ফাইলে ডেটা প্রেরণ করতে দেয় অন্তর্ভুক্ত ফাইলে, আপনি পরিবর্তনশীল নাম হিসাবে উপাদানগুলির কী এবং পরিবর্তনশীল মান হিসাবে মানগুলি ব্যবহার করতে পারেন। আরো বিস্তারিত জানার জন্য পরিবর্তনশীল ভেরিয়েবল চেক আউট.

উদাহরণস্বরূপ, নিম্নলিখিতগুলি header.php ফাইল থেকে কোড লোড করতে view() ফাংশন ব্যবহার করে এবং শিরোনামটিকে header.php ফাইলে একটি পরিবর্তনশীল হিসাবে তৈরি করে:

<?php view('header', ['title' => 'Register']) ?>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>

এই নতুন header.php ফাইলে, শিরোনাম ভেরিয়েবল সেট না থাকলে হোমে ডিফল্ট হবে।

register.php-এ view() ফাংশন ব্যবহার করতে, register.php-এ helpers.php অন্তর্ভুক্ত করতে হবে।

ফাইলগুলিকে কেন্দ্রীয়ভাবে অন্তর্ভুক্ত করতে, আপনি src ফোল্ডারে একটি bootstrap.php ফাইল তৈরি করতে পারেন। bootstrap.php সমস্ত প্রয়োজনীয় ফাইল অন্তর্ভুক্ত করবে। এবং আপনি register.php ফাইলে bootstrap.php ফাইলটি অন্তর্ভুক্ত করুন।

boostrap.php ফাইলটি এরকম হবে:

<?php

require_once __DIR__ . '/libs/helpers.php';

এবং register.php ফাইলটি নিচের মত দেখাবে:

<?php

require __DIR__ . '/../src/bootstrap.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
...
</form>

<?php view('footer') ?>

নিবন্ধন ফর্ম জমা প্রক্রিয়া

রেজিস্ট্রেশন ফর্ম HTTP POST পদ্ধতি ব্যবহার করে register.php এ জমা দেয়। ফর্ম ডেটা প্রক্রিয়া করার জন্য, আপনি HTTP অনুরোধটি register.php ফাইলের শুরুতে POST কিনা এইভাবে পরীক্ষা করতে পারেন:

if(strtoupper($_SERVER['REQUEST_METHOD']) === 'POST') {
    // process the form
}

যেহেতু উপরের কোডটি অন্যান্য পৃষ্ঠাগুলিতে ব্যবহার করা যেতে পারে, আপনি এটিকে এনক্যাপসুলেট করতে helpers.php ফাইলে is_post_request() ফাংশনটি সংজ্ঞায়িত করতে পারেন:

function is_post_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'POST';
}

একইভাবে, আপনি is_get_request() ফাংশনটিও সংজ্ঞায়িত করতে পারেন যা বর্তমান HTTP অনুরোধ GET হলে সত্য ফেরত দেয়:

function is_get_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'GET';
}

এবং register.php ফাইলের শুরুতে, আপনি is_post_request() ফাংশনটি নিম্নরূপ ব্যবহার করতে পারেন:

<?php

require __DIR__ . '/../src/bootstrap.php';


if (is_post_request()) {
    //...
}

স্যানিটাইজ করুন এবং ব্যবহারকারীর ইনপুট যাচাই করুন

ব্যবহারকারীর ইনপুটগুলিকে স্যানিটাইজ ও যাচাই করতে, আপনি স্যানিটাইজিং এবং ভ্যালিডেটিং ইনপুট টিউটোরিয়ালে তৈরি sanitize() এবং valdiate() ফাংশনগুলি ব্যবহার করতে পারেন অথবা আপনি filter() ফাংশনটি ব্যবহার করতে পারেন ৷ এই ফাংশনগুলি ব্যবহার করার জন্য, আপনাকে এটি করতে হবে:

  • প্রথমে src/libs ফোল্ডারে sanitization.php, validation.php এবং filter.php ফাইল তৈরি করুন।
  • দ্বিতীয়ত, এই ফাইলগুলিতে কোড যোগ করুন (দয়া করে এই টিউটোরিয়ালের শেষে কোডটি দেখুন)।
  • তৃতীয়, bootstrap.php ফাইলে এই ফাইলগুলি অন্তর্ভুক্ত করুন।

bootstrap.php ফাইলটি নিচের মত দেখাবে

<?php

session_start();
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';

ব্যবহারকারীর ইনপুটগুলিকে স্যানিটাইজ এবং যাচাই করতে filter() ফাংশনটি কীভাবে ব্যবহার করবেন তা নিম্নলিখিতটি দেখায়:

$fields = [
    'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
    'email' => 'email | required | email | unique: users, email',
    'password' => 'string | required | secure',
    'password2' => 'string | required | same: password',
    'agree' => 'string | required'
];

// custom messages
$messages = [
    'password2' => [
        'required' => 'Please enter the password again',
        'same' => 'The password does not match'
    ],
    'agree' => [
        'required' => 'You need to agree to the term of services to register'
    ]
];

[$inputs, $errors] = filter($_POST, $fields, $messages);

filter() ফাংশনে, আপনি তিনটি আর্গুমেন্ট পাস করেন:

  • $_POST অ্যারে ব্যবহারকারীর ইনপুট সংরক্ষণ করে।
  • $fields অ্যাসোসিয়েটিভ অ্যারে সমস্ত ক্ষেত্রের নিয়ম সংরক্ষণ করে।
  • $messages হল একটি বহুমাত্রিক অ্যারে যা পাসওয়ার্ড2 এবং সম্মত ক্ষেত্রগুলির প্রয়োজনীয় এবং একই নিয়মগুলির জন্য কাস্টম বার্তাগুলিকে নির্দিষ্ট করে ৷ এই যুক্তি ঐচ্ছিক. যদি আপনি এটি এড়িয়ে যান, ফিল্টার() ফাংশন ডিফল্ট বৈধতা বার্তা ব্যবহার করবে।

যদি ফর্মটি অবৈধ হয়, তাহলে আপনাকে পোস্ট-রিডাইরেক্ট-গেট (PRG) কৌশল ব্যবহার করে ব্যবহারকারীদের register.php পৃষ্ঠাতে পুনঃনির্দেশ করতে হবে। এছাড়াও, আপনাকে $_SESSION ভেরিয়েবলে $inputs এবং $errors অ্যারে যোগ করতে হবে যাতে আপনি পুনঃনির্দেশের পরে GET অনুরোধে সেগুলি অ্যাক্সেস করতে পারেন।

ফর্মটি বৈধ হলে, আপনাকে একটি ব্যবহারকারীর অ্যাকাউন্ট তৈরি করতে হবে এবং PRG কৌশল ব্যবহার করে ব্যবহারকারীদের login.php পৃষ্ঠায় পুনঃনির্দেশ করতে হবে। পৃষ্ঠাগুলিতে একবার একটি বার্তা দেখানোর জন্য, আপনি সেশন-ভিত্তিক ফ্ল্যাশ বার্তাগুলি ব্যবহার করতে পারেন।

ফ্ল্যাশ বার্তা পরিচালনা করুন

ফ্ল্যাশ বার্তা পরিচালনা করতে, আপনি ফ্ল্যাশ বার্তা টিউটোরিয়ালে সংজ্ঞায়িত ফ্ল্যাশ() ফাংশন ব্যবহার করুন:

  • প্রথমে, src/libs ফোল্ডারে একটি নতুন ফাইল flash.php তৈরি করুন।
  • দ্বিতীয়ত, flash.php ফাইলে কোড যোগ করুন।
  • তৃতীয়ত, bootstrap.php-এ flash.php ফাইলটি অন্তর্ভুক্ত করুন।

register.php ফাইলে একটি ফ্ল্যাশ বার্তা তৈরি করতে, আপনি flash() ফাংশনটি কল করুন:

flash(
    'user_register_success',
    'Your account has been created successfully. Please login here.',
    'success'
);

সমস্ত ফ্ল্যাশ বার্তা দেখানোর জন্য, আপনি কোনও আর্গুমেন্ট পাস না করেই flash() ফাংশনটি কল করুন:

flash()

উদাহরণস্বরূপ, আপনি header.php ফাইলে ফ্ল্যাশ বার্তাগুলি দেখাতে পারেন:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>
<?php flash() ?>

ত্রুটি CSS ক্লাস সেট করুন

যখন একটি ফর্ম ফিল্ডে অবৈধ ডেটা থাকে, যেমন, ভুল ইমেল ঠিকানা বিন্যাস, আপনাকে একটি ত্রুটি CSS ক্লাস যোগ করে এটি হাইলাইট করতে হবে ৷

নিম্নলিখিতটি src/libs/helpers.php ফাইলে error_class() ফাংশনকে সংজ্ঞায়িত করে, যা $errors অ্যারেতে একটি ক্ষেত্রের সাথে সম্পর্কিত ত্রুটি থাকলে ‘error’ শ্রেণী প্রদান করে:

function error_class(array $errors, string $field): string
{
    return isset($errors[$field]) ? 'error' : '';
}

পুনঃনির্দেশ

ব্যবহারকারীরা অ্যাকাউন্টের জন্য সফলভাবে নিবন্ধন করার পরে, আপনাকে তাদের লগইন পৃষ্ঠায় পুনঃনির্দেশ করতে হবে। এটি করতে, আপনি প্রস্থান নির্মাণের সাথে header() ফাংশন ব্যবহার করতে পারেন:

header ('Location: login.php');
exit;

নিম্নলিখিত কোডটি মোড়ানোর জন্য helpers.php ফাইলে redirect_to() ফাংশন নামক একটি ফাংশন সংজ্ঞায়িত করে:

function redirect_to(string $url): void
{
    header('Location:' . $url);
    exit;
}

যদি ফর্ম ডেটা অবৈধ হয়, আপনি ব্যবহারকারীদের register.php পৃষ্ঠায় পুনঃনির্দেশ করতে পারেন। এটি করার আগে, আপনাকে $_SESSION ভেরিয়েবলে $inputs এবং $errors ভেরিয়েবল যোগ করতে হবে যাতে আপনি পরবর্তী অনুরোধে সেগুলি অ্যাক্সেস করতে পারেন।

নিম্নলিখিতটি redirect_with() ফাংশনটিকে সংজ্ঞায়িত করে যা $items অ্যারের উপাদানগুলিকে $_SESSION ভেরিয়েবলে যোগ করে এবং একটি URL এ পুনঃনির্দেশ করে:

function redirect_with(string $url, array $items): void
{
    foreach ($items as $key => $value) {
        $_SESSION[$key] = $value;
    }

    redirect_to($url);
}

লক্ষ্য করুন যে redirect_with() ফাংশনটি redirect_to() ফাংশনটিকে একটি URL-এ ব্যবহারকারীদের পুনঃনির্দেশিত করতে কল করে। নিম্নলিখিতটি দেখায় কিভাবে redirect_with() ফাংশন ব্যবহার করতে হয় যা $inputs এবং $errors অ্যারে যোগ করে এবং register.php পৃষ্ঠায় পুনঃনির্দেশ করে:

redirect_with('register.php', [
   'inputs' => $inputs,
   'errors' => $errors
]);

আপনি যদি একটি ফ্ল্যাশ বার্তা সেট করতে চান এবং অন্য পৃষ্ঠায় পুনঃনির্দেশ করতে চান, তাহলে আপনি একটি নতুন সহায়ক ফাংশন redirect_with_message() এইভাবে সংজ্ঞায়িত করতে পারেন:

function redirect_with_message(string $url, string $message, string $type=FLASH_SUCCESS)
{
    flash('flash_' . uniqid(), $message, $type);
    redirect_to($url);

}

মনে রাখবেন যে ফ্ল্যাশ বার্তাটির নাম flash_ দিয়ে শুরু হয় এবং একটি অনন্য আইডি দ্বারা অনুসরণ করা হয় যা unicode() ফাংশন দ্বারা প্রত্যাবর্তিত হয়। এটি এই মত দেখাবে:

flash_615481fce49e8

আমরা ফ্ল্যাশ বার্তার জন্য একটি উৎপন্ন নাম ব্যবহার করি কারণ এটি এই ক্ষেত্রে গুরুত্বপূর্ণ নয়। এবং আমরা যাইহোক সমস্ত ফ্ল্যাশ বার্তা প্রদর্শন করব।

উদাহরণস্বরূপ, আপনি এই মত একটি সফল বার্তা সহ ব্যবহারকারীদের লগইন পৃষ্ঠায় পুনঃনির্দেশ করতে পারেন:

redirect_with_message(
    'login.php',
    'Your account has been created successfully. Please login here.'
);

ফ্ল্যাশ সেশন ডেটা

$_SESSION থেকে ডেটা পেতে এবং অবিলম্বে এটি সরাতে, আপনি একটি সহায়ক ফাংশন session_flash():

function session_flash(...$keys): array
{
    $data = [];
    foreach ($keys as $key) {
        if (isset($_SESSION[$key])) {
            $data[] = $_SESSION[$key];
            unset($_SESSION[$key]);
        } else {
            $data[] = [];
        }
    }
    return $data;
}

session_flash() ফাংশন পরিবর্তনশীল সংখ্যক কী গ্রহণ করে। এটি একটি বৈচিত্র্যময় ফাংশন হিসাবেও পরিচিত।

যদি $_SESSION ভেরিয়েবলে একটি কী বিদ্যমান থাকে, তাহলে ফাংশনটি সেই কীটির মান রিটার্ন অ্যারেতে যোগ করে এবং মানটিকে আনসেট করে। অন্যথায়, ফাংশনটি সেই কীটির জন্য একটি খালি অ্যারে ফিরিয়ে দেবে।

নিম্নলিখিতগুলি $_SESSION ভেরিয়েবল থেকে $ইনপুট এবং $ররগুলি পেতে এবং session_flash() ফাংশন ব্যবহার করে সেগুলি আনসেট করতে অ্যারে ডিস্ট্রাকচারিং ব্যবহার করে:

[$errors, $inputs] = session_flash('errors', 'inputs');

boostrap.php ফাংশন আপডেট করা হয়েছে to session_start() ফাংশন এবং flash.php ফাইলে কল অন্তর্ভুক্ত করার জন্য:

<?php

session_start();
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/flash.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';

মনে রাখবেন যে আপনাকে একটি নতুন সেশন শুরু করতে session_start() ফাংশনটি কল করতে হবে বা সেশন ডেটা পরিচালনা করতে একটি বিদ্যমান একটি পুনরায় শুরু করতে হবে।

সম্পূর্ণ register.php ফাইল

নিম্নলিখিত সম্পূর্ণ register.php ফাইল দেখায়:

<?php

require __DIR__ . '/../src/bootstrap.php';

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}

?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>
    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>

লজিক এবং দেখার অংশগুলি আলাদা করতে, আপনি src ফোল্ডারে একটি নতুন ফাইল register.php তৈরি করতে পারেন এবং এতে public/register.php ফাইলের প্রথম অংশ যোগ করতে পারেন:

<?php

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}

এবং নিম্নলিখিতগুলি public/register.php ফাইলটি দেখায়:

<?php

require __DIR__ . '/../src/bootstrap.php';
require __DIR__ . '/../src/register.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>

    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>

যেহেতু register_user() ফাংশনটি বিদ্যমান নেই, তাই আপনাকে এটি সংজ্ঞায়িত করতে হবে। এটি করার আগে, আপনাকে একটি নতুন ডাটাবেস এবং ব্যবহারকারীর টেবিল তৈরি করতে হবে।

একটি নতুন ডাটাবেস এবং ব্যবহারকারীদের টেবিল তৈরি করুন

আমরা ব্যবহারকারীর তথ্য সংরক্ষণ করতে MySQL ব্যবহার করব। MySQL এর সাথে ইন্টারঅ্যাক্ট করতে, আপনি যেকোন MySQL ক্লায়েন্ট টুল যেমন phpmyadmin বা mysql ব্যবহার করতে পারেন। প্রথমে, MySQL ডাটাবেস সার্ভারে auth নামে একটি নতুন ডাটাবেস তৈরি করুন:

CREATE DATABASE auth;

দ্বিতীয়ত, ব্যবহারকারীর তথ্য সংরক্ষণ করতে ব্যবহারকারীদের নামে একটি নতুন টেবিল তৈরি করুন:

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(25) NOT NULL UNIQUE,
    email VARCHAR(320) NOT NULL UNIQUE,
    password VARCHAR(256) NOT NULL,
    is_admin TINYINT(1) not null default 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

ব্যবহারকারীর টেবিলে নিম্নলিখিত কলাম রয়েছে:

  • id হল প্রাথমিক কী। যেহেতু আইডি একটি স্বয়ংক্রিয়-বৃদ্ধি কলাম, তাই প্রতিটি নতুন সারির জন্য MySQL এর মান এক করে বৃদ্ধি করবে। অন্য কথায়, টেবিলে একটি নতুন সারি ঢোকানোর সময় আপনাকে আইডি প্রদান করতে হবে না।
  • ব্যবহারকারীর নামটি শূন্য নয় এবং অনন্য সীমাবদ্ধতা সহ একটি ভিন্ন অক্ষরের কলাম। এর অর্থ হল একই ব্যবহারকারীর নামের সাথে কোন দুটি সারি থাকবে না।
  • ইমেল কলামটি ব্যবহারকারীর নাম কলামের মতো, যা শূন্য এবং অনন্য নয়।
  • পাসওয়ার্ড কলাম একটি পরিবর্তিত কলাম এবং নাল নয়।
  • is_admin একটি ক্ষুদ্র পূর্ণসংখ্যা কলাম। এর ডিফল্ট মান শূন্য। যদি is_admin শূন্য হয়, ব্যবহারকারী প্রশাসক নয়। যদি is_admin 1 হয়, ব্যবহারকারী একজন প্রশাসক যার নিয়মিত ব্যবহারকারীদের চেয়ে বেশি সুবিধা রয়েছে।
  • create_at হল একটি টাইমস্ট্যাম্প কলাম যেটি MySQL বর্তমান টাইমস্ট্যাম্পে আপডেট করবে যখন আপনি টেবিলে একটি নতুন সারি ঢোকাবেন
  • update_at হল একটি datetime কলাম যে MySQL এটিকে বর্তমান টাইমস্ট্যাম্পে স্বয়ংক্রিয়ভাবে আপডেট করবে যখন আপনি একটি বিদ্যমান সারি আপডেট করবেন।

MySQL ডাটাবেসের সাথে সংযোগ করুন

MySQL ডাটাবেসের সাথে সংযোগ করতে, আপনি PHP ডেটা অবজেক্ট (বা PDO) লাইব্রেরি ব্যবহার করবেন।

প্রথমে, কনফিগার ফোল্ডারে একটি নতুন ফাইল database.php ফাইল তৈরি করুন এবং ফাইলটিতে নিম্নলিখিত ডেটাবেস প্যারামিটার যোগ করুন:

<?php

const DB_HOST = 'localhost';
const DB_NAME = 'auth';
const DB_USER = 'root';
const DB_PASSWORD = '';

দ্বিতীয়ত, src/libs ফোল্ডারে connection.php ফাইলটি তৈরি করুন।

তৃতীয়ত, db() ফাংশনটি সংজ্ঞায়িত করুন যা একবার ডাটাবেসকে সংযুক্ত করে এবং একটি নতুন PDO অবজেক্ট প্রদান করে। db() ফাংশনটি config/database.php ফাইলে সংজ্ঞায়িত ডাটাবেস কনফিগারেশন ব্যবহার করে।

function db(): PDO
{
    static $pdo;

    if (!$pdo) {
        $pdo = new PDO(
            sprintf("mysql:host=%s;dbname=%s;charset=UTF8", DB_HOST, DB_NAME),
            DB_USER,
            DB_PASSWORD,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    return $pdo;
}

db() ফাংশনে, $pdo হল একটি স্ট্যাটিক ভেরিয়েবল। যখন আপনি প্রথমবার db() ফাংশন কল করেন, $pdo ভেরিয়েবলটি আরম্ভ করা হয় না।অতএব, if স্টেটমেন্টের ভিতরের কোড ব্লকটি কার্যকর করে যা ডাটাবেসের সাথে সংযোগ করে এবং একটি নতুন PDO বস্তু প্রদান করে।

যেহেতু $pdo একটি স্ট্যাটিক ভেরিয়েবল, ফাংশন db() ফাংশন সম্পূর্ণ হওয়ার পরেও এটি জীবিত থাকে। অতএব, আপনি যখন db() ফাংশনটিকে আবার কল করেন, তখন এটি PDO অবজেক্ট রিটার্ন করে। অন্য কথায়, ফাংশনটি আবার ডাটাবেসের সাথে সংযোগ করে না।

যেহেতু ডাটাবেসের সাথে একটি সংযোগ তৈরি করা সময় এবং সম্পদের পরিপ্রেক্ষিতে ব্যয়বহুল, তাই আপনার অনুরোধ প্রতি একবার ডাটাবেসের সাথে সংযোগ করা উচিত।

register_user() ফাংশন সংজ্ঞায়িত করুন

প্রথমে src ফোল্ডারে auth.php নামে একটি নতুন ফাইল তৈরি করুন। দ্বিতীয়ত, register_user() ফাংশনটি সংজ্ঞায়িত করুন যা ব্যবহারকারীদের টেবিলে একটি নতুন ব্যবহারকারী সন্নিবেশ করে:

function register_user(string $email, string $username, string $password, bool $is_admin = false): bool
{
    $sql = 'INSERT INTO users(username, email, password, is_admin)
            VALUES(:username, :email, :password, :is_admin)';

    $statement = db()->prepare($sql);

    $statement->bindValue(':username', $username, PDO::PARAM_STR);
    $statement->bindValue(':email', $email, PDO::PARAM_STR);
    $statement->bindValue(':password', password_hash($password, PASSWORD_BCRYPT), PDO::PARAM_STR);
    $statement->bindValue(':is_admin', (int)$is_admin, PDO::PARAM_INT);

    return $statement->execute();
}

ডাটাবেসে একটি পাসওয়ার্ড সংরক্ষণ করার সময়, আপনি নিরাপত্তার কারণে এটিকে প্লেইন টেক্সটে সংরক্ষণ করবেন না। পরিবর্তে, আপনি সবসময় এটি সংরক্ষণ করার আগে পাসওয়ার্ড হ্যাশ করা উচিত.

একটি পাসওয়ার্ড হ্যাশ করতে, আপনি অন্তর্নির্মিত password_hash() ফাংশন ব্যবহার করুন:

password_hash($password, PASSWORD_BCRYPT)

উদাহরণস্বরূপ, পাসওয়ার্ডটি পাসওয়ার্ড1 হলে, password_hash() ফাংশনটি নিম্নলিখিত হ্যাশ প্রদান করে:

<?php

echo password_hash('Password1', PASSWORD_BCRYPT);

আউটপুট:

$2y$10$QlUdCEXY68bswdVsKlE.5OjHa7X8fvtCmlYLnIkfvbcGd..mqDfwq

PASSWORD_BCRYPT আর্গুমেন্ট পাসওয়ার্ড_হ্যাশ() ফাংশনকে CRYPT_BLOWFISH অ্যালগরিদম ব্যবহার করার নির্দেশ দেয় যা খুবই নিরাপদ।

যেহেতু password_hash() একটি একমুখী ফাংশন, তাই হ্যাকাররা এটিকে মূল প্লেইন টেক্সটে (Password1) “ডিক্রিপ্ট” করতে পারে না। এটি একটি ডাটাবেস ফাঁস হওয়ার সময় আপস করা পাসওয়ার্ডগুলির বিরুদ্ধে একটি প্রতিরক্ষা প্রদান করে৷

সব একসাথে রাখুন

প্রকল্প ফোল্ডার এবং ফাইল গঠন নিম্নলিখিত মত হবে:

├── config
|  └── database.php
├── public
|  └── register.php
└── src
   ├── auth.php
   ├── bootstrap.php
   ├── inc
   |  ├── footer.php
   |  └── header.php
   ├── libs
   |  ├── connection.php
   |  ├── flash.php
   |  ├── helpers.php
   |  ├── sanitization.php
   |  ├── validation.php
   |  └── filter.php
   └── register.php

config/database.php ফাইল

<?php

const DB_HOST = 'localhost';
const DB_NAME = 'auth';
const DB_USER = 'root';
const DB_PASSWORD = '';

inc/auth.php ফাইল

/**
* Register a user
*
* @param string $email
* @param string $username
* @param string $password
* @param bool $is_admin
* @return bool
*/
function register_user(string $email, string $username, string $password, bool $is_admin = false): bool
{
    $sql = 'INSERT INTO users(username, email, password, is_admin)
            VALUES(:username, :email, :password, :is_admin)';

    $statement = db()->prepare($sql);

    $statement->bindValue(':username', $username, PDO::PARAM_STR);
    $statement->bindValue(':email', $email, PDO::PARAM_STR);
    $statement->bindValue(':password', password_hash($password, PASSWORD_BCRYPT), PDO::PARAM_STR);
    $statement->bindValue(':is_admin', (int)$is_admin, PDO::PARAM_INT);


    return $statement->execute();
}

src/libs/connection.php

<?php

/**
 * Connect to the database and returns an instance of PDO class
 * or false if the connection fails
 *
 * @return PDO
 */
function db(): PDO
{
    static $pdo;

    if (!$pdo) {
        $pdo = new PDO(
            sprintf("mysql:host=%s;dbname=%s;charset=UTF8", DB_HOST, DB_NAME),
            DB_USER,
            DB_PASSWORD,
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]
        );
    }
    
    return $pdo;
}

src/libs/flash.php ফাইল

<?php

const FLASH = 'FLASH_MESSAGES';

const FLASH_ERROR = 'error';
const FLASH_WARNING = 'warning';
const FLASH_INFO = 'info';
const FLASH_SUCCESS = 'success';

/**
* Create a flash message
*
* @param string $name
* @param string $message
* @param string $type
* @return void
*/
function create_flash_message(string $name, string $message, string $type): void
{
    // remove existing message with the name
    if (isset($_SESSION[FLASH][$name])) {
        unset($_SESSION[FLASH][$name]);
    }
    // add the message to the session
    $_SESSION[FLASH][$name] = ['message' => $message, 'type' => $type];
}


/**
* Format a flash message
*
* @param array $flash_message
* @return string
*/
function format_flash_message(array $flash_message): string
{
    return sprintf('<div class="alert alert-%s">%s</div>',
        $flash_message['type'],
        $flash_message['message']
    );
}

/**
* Display a flash message
*
* @param string $name
* @return void
*/
function display_flash_message(string $name): void
{
    if (!isset($_SESSION[FLASH][$name])) {
        return;
    }

    // get message from the session
    $flash_message = $_SESSION[FLASH][$name];

    // delete the flash message
    unset($_SESSION[FLASH][$name]);

    // display the flash message
    echo format_flash_message($flash_message);
}

/**
* Display all flash messages
*
* @return void
*/
function display_all_flash_messages(): void
{
    if (!isset($_SESSION[FLASH])) {
        return;
    }

    // get flash messages
    $flash_messages = $_SESSION[FLASH];

    // remove all the flash messages
    unset($_SESSION[FLASH]);

    // show all flash messages
    foreach ($flash_messages as $flash_message) {
        echo format_flash_message($flash_message);
    }
}

/**
* Flash a message
*
* @param string $name
* @param string $message
* @param string $type (error, warning, info, success)
* @return void
*/
function flash(string $name = '', string $message = '', string $type = ''): void
{
    if ($name !== '' && $message !== '' && $type !== '') {
        // create a flash message
        create_flash_message($name, $message, $type);
    } elseif ($name !== '' && $message === '' && $type === '') {
        // display a flash message
        display_flash_message($name);
    } elseif ($name === '' && $message === '' && $type === '') {
        // display all flash message
        display_all_flash_messages();
    }
}

src/inc/header.php

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="stylesheet" href="https://www.phptutorial.net/app/css/style.css">
    <title><?= $title ?? 'Home' ?></title>
</head>
<body>
<main>
<?php flash() ?>

src/inc/footer.php ফাইল

</main>
</body>
</html>

src/libs/helpers.php

<?php

/**
 * Display a view
 *
 * @param string $filename
 * @param array $data
 * @return void
 */
function view(string $filename, array $data = []): void
{
    // create variables from the associative array
    foreach ($data as $key => $value) {
        $$key = $value;
    }
    require_once __DIR__ . '/../inc/' . $filename . '.php';
}


/**
 * Return the error class if error is found in the array $errors
 *
 * @param array $errors
 * @param string $field
 * @return string
 */
function error_class(array $errors, string $field): string
{
    return isset($errors[$field]) ? 'error' : '';
}

/**
 * Return true if the request method is POST
 *
 * @return boolean
 */
function is_post_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'POST';
}

/**
 * Return true if the request method is GET
 *
 * @return boolean
 */
function is_get_request(): bool
{
    return strtoupper($_SERVER['REQUEST_METHOD']) === 'GET';
}

/**
 * Redirect to another URL
 *
 * @param string $url
 * @return void
 */
function redirect_to(string $url): void
{
    header('Location:' . $url);
    exit;
}

/**
 * Redirect to a URL with data stored in the items array
 * @param string $url
 * @param array $items
 */
function redirect_with(string $url, array $items): void
{
    foreach ($items as $key => $value) {
        $_SESSION[$key] = $value;
    }

    redirect_to($url);
}

/**
 * Redirect to a URL with a flash message
 * @param string $url
 * @param string $message
 * @param string $type
 */
function redirect_with_message(string $url, string $message, string $type = FLASH_SUCCESS)
{
    flash('flash_' . uniqid(), $message, $type);
    redirect_to($url);
}

/**
 * Flash data specified by $keys from the $_SESSION
 * @param ...$keys
 * @return array
 */
function session_flash(...$keys): array
{
    $data = [];
    foreach ($keys as $key) {
        if (isset($_SESSION[$key])) {
            $data[] = $_SESSION[$key];
            unset($_SESSION[$key]);
        } else {
            $data[] = [];
        }
    }
    return $data;
}

inc/sanitization.php

<?php
const FILTERS = [
    'string' => FILTER_SANITIZE_STRING,
    'string[]' => [
        'filter' => FILTER_SANITIZE_STRING,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'email' => FILTER_SANITIZE_EMAIL,
    'int' => [
        'filter' => FILTER_SANITIZE_NUMBER_INT,
        'flags' => FILTER_REQUIRE_SCALAR
    ],
    'int[]' => [
        'filter' => FILTER_SANITIZE_NUMBER_INT,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'float' => [
        'filter' => FILTER_SANITIZE_NUMBER_FLOAT,
        'flags' => FILTER_FLAG_ALLOW_FRACTION
    ],
    'float[]' => [
        'filter' => FILTER_SANITIZE_NUMBER_FLOAT,
        'flags' => FILTER_REQUIRE_ARRAY
    ],
    'url' => FILTER_SANITIZE_URL,
];

/**
* Recursively trim strings in an array
* @param array $items
* @return array
*/
function array_trim(array $items): array
{
    return array_map(function ($item) {
        if (is_string($item)) {
            return trim($item);
        } elseif (is_array($item)) {
            return array_trim($item);
        } else
            return $item;
    }, $items);
}

/**
* Sanitize the inputs based on the rules an optionally trim the string
* @param array $inputs
* @param array $fields
* @param int $default_filter FILTER_SANITIZE_STRING
* @param array $filters FILTERS
* @param bool $trim
* @return array
*/
function sanitize(array $inputs, array $fields = [], int $default_filter = FILTER_SANITIZE_STRING, array $filters = FILTERS, bool $trim = true): array
{
    if ($fields) {
        $options = array_map(fn($field) => $filters[$field], $fields);
        $data = filter_var_array($inputs, $options);
    } else {
        $data = filter_var_array($inputs, $default_filter);
    }

    return $trim ? array_trim($data) : $data;
}

src/libs/validation.php

<?php


const DEFAULT_VALIDATION_ERRORS = [
    'required' => 'The %s is required',
    'email' => 'The %s is not a valid email address',
    'min' => 'The %s must have at least %s characters',
    'max' => 'The %s must have at most %s characters',
    'between' => 'The %s must have between %d and %d characters',
    'same' => 'The %s must match with %s',
    'alphanumeric' => 'The %s should have only letters and numbers',
    'secure' => 'The %s must have between 8 and 64 characters and contain at least one number, one upper case letter, one lower case letter and one special character',
    'unique' => 'The %s already exists',
];


/**
* Validate
* @param array $data
* @param array $fields
* @param array $messages
* @return array
*/
function validate(array $data, array $fields, array $messages = []): array
{
    // Split the array by a separator, trim each element
    // and return the array
    $split = fn($str, $separator) => array_map('trim', explode($separator, $str));

    // get the message rules
    $rule_messages = array_filter($messages, fn($message) => is_string($message));
    // overwrite the default message
    $validation_errors = array_merge(DEFAULT_VALIDATION_ERRORS, $rule_messages);

    $errors = [];

    foreach ($fields as $field => $option) {

        $rules = $split($option, '|');

        foreach ($rules as $rule) {
            // get rule name params
            $params = [];
            // if the rule has parameters e.g., min: 1
            if (strpos($rule, ':')) {
                [$rule_name, $param_str] = $split($rule, ':');
                $params = $split($param_str, ',');
            } else {
                $rule_name = trim($rule);
            }
            // by convention, the callback should be is_<rule> e.g.,is_required
            $fn = 'is_' . $rule_name;

            if (is_callable($fn)) {
                $pass = $fn($data, $field, ...$params);
                if (!$pass) {
                    // get the error message for a specific field and rule if exists
                    // otherwise get the error message from the $validation_errors
                    $errors[$field] = sprintf(
                        $messages[$field][$rule_name] ?? $validation_errors[$rule_name],
                        $field,
                        ...$params
                    );
                }
            }
        }
    }

    return $errors;
}

/**
* Return true if a string is not empty
* @param array $data
* @param string $field
* @return bool
*/
function is_required(array $data, string $field): bool
{
    return isset($data[$field]) && trim($data[$field]) !== '';
}

/**
* Return true if the value is a valid email
* @param array $data
* @param string $field
* @return bool
*/
function is_email(array $data, string $field): bool
{
    if (empty($data[$field])) {
        return true;
    }

    return filter_var($data[$field], FILTER_VALIDATE_EMAIL);
}

/**
* Return true if a string has at least min length
* @param array $data
* @param string $field
* @param int $min
* @return bool
*/
function is_min(array $data, string $field, int $min): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) >= $min;
}

/**
* Return true if a string cannot exceed max length
* @param array $data
* @param string $field
* @param int $max
* @return bool
*/
function is_max(array $data, string $field, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return mb_strlen($data[$field]) <= $max;
}

/**
* @param array $data
* @param string $field
* @param int $min
* @param int $max
* @return bool
*/
function is_between(array $data, string $field, int $min, int $max): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $len = mb_strlen($data[$field]);
    return $len >= $min && $len <= $max;
}

/**
* Return true if a string equals the other
* @param array $data
* @param string $field
* @param string $other
* @return bool
*/
function is_same(array $data, string $field, string $other): bool
{
    if (isset($data[$field], $data[$other])) {
        return $data[$field] === $data[$other];
    }

    if (!isset($data[$field]) && !isset($data[$other])) {
        return true;
    }

    return false;
}

/**
* Return true if a string is alphanumeric
* @param array $data
* @param string $field
* @return bool
*/
function is_alphanumeric(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    return ctype_alnum($data[$field]);
}

/**
* Return true if a password is secure
* @param array $data
* @param string $field
* @return bool
*/
function is_secure(array $data, string $field): bool
{
    if (!isset($data[$field])) {
        return false;
    }

    $pattern = "#.*^(?=.{8,64})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*\W).*$#";
    return preg_match($pattern, $data[$field]);
}

/**
* Return true if the $value is unique in the column of a table
* @param array $data
* @param string $field
* @param string $table
* @param string $column
* @return bool
*/
function is_unique(array $data, string $field, string $table, string $column): bool
{
    if (!isset($data[$field])) {
        return true;
    }

    $sql = "SELECT $column FROM $table WHERE $column = :value";

    $stmt = db()->prepare($sql);
    $stmt->bindValue(":value", $data[$field]);

    $stmt->execute();

    return $stmt->fetchColumn() === false;
}

src/libs/filter.php

<?php

/**
 * Sanitize and validate data
 * @param array $data
 * @param array $fields
 * @param array $messages
 * @return array
 */
function filter(array $data, array $fields, array $messages = []): array
{
    $sanitization = [];
    $validation = [];

    // extract sanitization & validation rules
    foreach ($fields as $field => $rules) {
        if (strpos($rules, '|')) {
            [$sanitization[$field], $validation[$field]] = explode('|', $rules, 2);
        } else {
            $sanitization[$field] = $rules;
        }
    }

    $inputs = sanitize($data, $sanitization);
    $errors = validate($inputs, $validation, $messages);

    return [$inputs, $errors];
}

src/bootstrap.php

<?php

<?php

session_start();
require_once __DIR__ . '/../config/database.php';
require_once __DIR__ . '/libs/helpers.php';
require_once __DIR__ . '/libs/flash.php';
require_once __DIR__ . '/libs/sanitization.php';
require_once __DIR__ . '/libs/validation.php';
require_once __DIR__ . '/libs/filter.php';
require_once __DIR__ . '/libs/connection.php';
require_once __DIR__ . '/auth.php';

public/register.php

<?php
require __DIR__ . '/../src/bootstrap.php';
require __DIR__ . '/../src/register.php';
?>

<?php view('header', ['title' => 'Register']) ?>

<form action="register.php" method="post">
    <h1>Sign Up</h1>

    <div>
        <label for="username">Username:</label>
        <input type="text" name="username" id="username" value="<?= $inputs['username'] ?? '' ?>"
               class="<?= error_class($errors, 'username') ?>">
        <small><?= $errors['username'] ?? '' ?></small>
    </div>

    <div>
        <label for="email">Email:</label>
        <input type="email" name="email" id="email" value="<?= $inputs['email'] ?? '' ?>"
               class="<?= error_class($errors, 'email') ?>">
        <small><?= $errors['email'] ?? '' ?></small>
    </div>

    <div>
        <label for="password">Password:</label>
        <input type="password" name="password" id="password" value="<?= $inputs['password'] ?? '' ?>"
               class="<?= error_class($errors, 'password') ?>">
        <small><?= $errors['password'] ?? '' ?></small>
    </div>

    <div>
        <label for="password2">Password Again:</label>
        <input type="password" name="password2" id="password2" value="<?= $inputs['password2'] ?? '' ?>"
               class="<?= error_class($errors, 'password2') ?>">
        <small><?= $errors['password2'] ?? '' ?></small>
    </div>

    <div>
        <label for="agree">
            <input type="checkbox" name="agree" id="agree" value="checked" <?= $inputs['agree'] ?? '' ?> /> I
            agree
            with the
            <a href="#" title="term of services">term of services</a>
        </label>
        <small><?= $errors['agree'] ?? '' ?></small>
    </div>

    <button type="submit">Register</button>

    <footer>Already a member? <a href="login.php">Login here</a></footer>

</form>

<?php view('footer') ?>

src/register.php

<?php

$errors = [];
$inputs = [];

if (is_post_request()) {

    $fields = [
        'username' => 'string | required | alphanumeric | between: 3, 25 | unique: users, username',
        'email' => 'email | required | email | unique: users, email',
        'password' => 'string | required | secure',
        'password2' => 'string | required | same: password',
        'agree' => 'string | required'
    ];

    // custom messages
    $messages = [
        'password2' => [
            'required' => 'Please enter the password again',
            'same' => 'The password does not match'
        ],
        'agree' => [
            'required' => 'You need to agree to the term of services to register'
        ]
    ];

    [$inputs, $errors] = filter($_POST, $fields, $messages);

    if ($errors) {
        redirect_with('register.php', [
            'inputs' => $inputs,
            'errors' => $errors
        ]);
    }

    if (register_user($inputs['email'], $inputs['username'], $inputs['password'])) {
        redirect_with_message(
            'login.php',
            'Your account has been created successfully. Please login here.'
        );

    }

} else if (is_get_request()) {
    [$inputs, $errors] = session_flash('inputs', 'errors');
}

public/login.php

<?php

require __DIR__ . '/../src/bootstrap.php';

Leave a Comment

Share this Doc

PHP নিবন্ধন ফর্ম (Registration)

Or copy link

CONTENTS

Subscribe

×
Cancel