PHP সার্ভার সাইডে অনেক পুনরাবৃত্তিমূলক কাজ সম্পন্ন করে। একটি মাত্র রিকোয়েস্টের জন্য হয়তো একটি ফোল্ডার ভর্তি ছবি স্ক্যান করতে হতে পারে, কোনো স্ট্যাটাস পরিবর্তন না হওয়া পর্যন্ত ডেটাবেস চেক করতে হতে পারে, অথবা ব্যবহারকারীর কাছ থেকে নির্দিষ্ট নিয়ম অনুযায়ী ইনপুট নিতে হতে পারে। একই কোড বারবার কপি-পেস্ট করে এসব কাজ করা অত্যন্ত ঝুঁকিপূর্ণ এবং রক্ষণাবেক্ষণ করা প্রায় অসম্ভব। লুপ (Loop) ঠিক এই সমস্যাটি সমাধানের জন্যই তৈরি করা হয়েছে। এগুলো লজিককে একটি পুনরায় ব্যবহারযোগ্য ব্লকে আবদ্ধ করে, ভুল কমায় এবং আপনার অ্যাপ্লিকেশনকে পরিচ্ছন্ন রাখে।

বেশিরভাগ PHP ডেভেলপার যখন জানেন যে কতবার লুপটি চালাতে হবে, তখন তারা সহজাতভাবেই for লুপ ব্যবহার করেন, অথবা যখন সংখ্যাটি কোনো ডাইনামিক কন্ডিশনের ওপর নির্ভর করে তখন while লুপ ব্যবহার করেন। তবে তৃতীয় একটি বিকল্প রয়েছে, যা যথেষ্ট ভিন্নভাবে কাজ করে এবং সঠিক সময়ে এটি ব্যবহার করলে আপনার কোড অনেক বেশি পরিষ্কার হতে পারে। do-while লুপ প্রথমে তার ব্লকের ভেতরের কোডটি কার্যকর করে, তারপর চেক করে যে এটি পুনরায় চালানো উচিত কি না।

কেন do-while লুপ আলাদা

একটি সাধারণ while লুপে, PHP ব্লকের ভেতরে ঢোকার আগেই কন্ডিশনটি যাচাই করে। যদি কন্ডিশনটি শুরুতেই মিথ্যা (false) হয়, তবে ব্লকটি পুরোপুরি বাদ দেওয়া হয়। do-while লুপ এই ক্রমটি উল্টে দেয়। এটি কোডটি একবার চালায়, শেষে পৌঁছায় এবং তারপর সিদ্ধান্ত নেয় যে লুপটি পুনরায় চলবে কি না।

এর সিনট্যাক্স (syntax) দেখতে এরকম:

do {
    // statements
} while ($condition);

while ক্লজের পরে সেমিকোলনটির দিকে লক্ষ্য করুন। এই সেমিকোলনটি আবশ্যক, এবং এটি ভুলে গেলে সিনট্যাক্স এরর (syntax error) দেখাবে। do এবং while-এর মধ্যবর্তী ব্লকটি অন্তত একবার অবশ্যই কার্যকর হবে, এমনকি কন্ডিশনটি শুরু থেকেই মিথ্যা হলেও। এই 'পোস্ট-টেস্ট' (post-test) আচরণটি কেবল একটি বৈশিষ্ট্য নয়; এটিই হলো এই লুপটির অস্তিত্বের মূল কারণ।

কখন do-while ব্যবহার করবেন

তিনটি সাধারণ ক্ষেত্রে এই স্ট্রাকচারটি দারুণ কাজ করে: মেনু প্রদর্শন করা, ইনপুট যাচাই করা এবং কোনো ব্যর্থ হওয়া কাজ পুনরায় চেষ্টা করা। প্রতিটি ক্ষেত্রেই লজিক এমনভাবে কাজ করে যে, কাজটির ফলাফল যাচাই করার আগেই কাজটি সম্পন্ন করতে হয়।

মেনু প্রদর্শন করা

একটি কমান্ড-লাইন টুল বা টেক্সট-ভিত্তিক অ্যাডমিন প্যানেলের কথা ভাবুন। আপনাকে অপশনগুলোর একটি তালিকা প্রিন্ট করতে হবে, ব্যবহারকারী একটি অপশন বেছে নেওয়া পর্যন্ত অপেক্ষা করতে হবে এবং তারপর সিদ্ধান্ত নিতে হবে যে মেনুটি আবার দেখানো হবে কি না। মেনুটি স্ক্রিনে না আসা পর্যন্ত আপনি চেক করতে পারবেন না যে ব্যবহারকারী 'exit' বেছে নিয়েছেন কি না।

$choice = 0;

do {
    echo "1. View Reports\n";
    echo "2. Export Data\n";
    echo "3. Exit\n";
    
    $choice = (int) readline("Select an option: ");
    
    if ($choice === 1) {
        // fetch and display reports
    } elseif ($choice === 2) {
        // run export logic
    }
} while ($choice !== 3);

একটি সাধারণ while লুপ দিয়েও এটি করা সম্ভব, কিন্তু সেক্ষেত্রে আপনাকে লুপ শুরু হওয়ার আগে একবার মেনু আউটপুট দিতে হবে অথবা প্রাথমিক কন্ডিশন মেটানোর জন্য $choice ভেরিয়েবলটিকে একটি ভুয়া (phony) মান দিয়ে ইনিশিয়ালাইজ করতে হবে। এতে সম্পর্কিত লজিক দুটি আলাদা জায়গায় ছড়িয়ে পড়ে। do-while ভার্সনটি ডিসপ্লে, রিড এবং চেক—সবকিছুকে একটি গোছানো ব্লকের ভেতরে রাখে।

ইনপুট যাচাই করা

ইনপুট ভ্যালিডেশন বা যাচাইকরণ প্রায়ই একই প্যাটার্ন অনুসরণ করে। আপনি ব্যবহারকারীকে প্রম্পট দিতে চান, তারা কী দিয়েছে তা পরীক্ষা করতে চান এবং যদি তা আপনার নিয়ম না মানে তবে পুনরায় চেষ্টা করতে চান। যেহেতু ইনস্পেকশনের আগে প্রম্পট দেওয়া প্রয়োজন, তাই do-while লুপ এখানে খুব স্বাভাবিকভাবে মানিয়ে যায়।

do {
    $username = readline("Choose a username (min 3 chars): ");
    $isValid = strlen($username) >= 3;
    
    if (!$isValid) {
        echo "That username is too short.\n";
    }
} while (!$isValid);

একটি সাধারণ while লুপ ব্যবহার করলে আপনাকে $username ভেরিয়েবলটিকে একটি খালি স্ট্রিং দিয়ে সেট করতে হবে এবং লুপের উপরে প্রম্পট লাইনটি ডুপ্লিকেট করতে হবে যাতে কন্ডিশনটি যাচাই করার মতো কিছু থাকে। এই পুনরাবৃত্তি বাগ (bug) তৈরির ঝুঁকি বাড়ায়। আপনি যদি পরবর্তীতে প্রম্পট মেসেজ পরিবর্তন করেন, তবে হয়তো লুপের ভেতরের মেসেজটি পরিবর্তন করবেন কিন্তু লুপের উপরের কপিটি ভুলে যাবেন। do-while স্ট্রাকচারটি এই ঝুঁকি দূর করে কারণ এটি নিশ্চিত করে যে কোনো কন্ডিশন পরীক্ষা করার আগেই ব্লকের ভেতরে প্রম্পটটি চলবে।

ব্যর্থ হওয়া কাজ পুনরায় চেষ্টা করা

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

$attempts = 0;
$maxRetries = 3;
$success = false;

do {
    $attempts++;
    $response = @file_get_contents('https://api.example.com/status');
    
    if ($response !== false) {
        $success = true;
    } elseif ($attempts < $maxRetries) {
        sleep(1);
    }
} while (!$success && $attempts < $maxRetries);

if (!$success) {
    // log the permanent failure
}

এখানে গুরুত্বপূর্ণ বিষয়টি হলো, PHP "এটি কি কাজ করেছে?"—এই প্রশ্ন করার আগেই রিকোয়েস্টটি পাঠানো হয়। একটি while লুপ ব্যবহার করলে লুপে ঢোকার আগে একটি ডামি রিকোয়েস্ট করতে হবে অথবা একটি ভুয়া সাকসেস ফ্ল্যাগ সেট করতে হবে, যা আসলে কী ঘটছে তা অস্পষ্ট করে ফেলবে। do-while ভার্সনটি কাজের ধারাটিকে স্বচ্ছ রাখে: চেষ্টা করা, পরীক্ষা করা এবং সিদ্ধান্ত নেওয়া।

do-while এবং while-এর তুলনা

এই দুটি লুপের মধ্যে পার্থক্য হলো টাইমিং বা সময়ের। এমন একটি ভেরিয়েবলের কথা চিন্তা করুন যা এমন একটি অবস্থায় আছে যা যেকোনো টেস্টে ফেল করবে:

$x = 10;

while ($x < 5) {
    echo $x;        // Never runs
}

একই শুরুর মানের বিপরীতে, একটি do-while লুপ এভাবে কাজ করে:

$x = 10;

do {
    echo $x;        // Runs once, outputs 10
} while ($x < 5);

এই একটি নিশ্চিত এক্সিকিউশনই হলো মূল পার্থক্যকারী বিষয়। যদি আপনার লজিকের জন্য শূন্য বা তার বেশি ইটারেশন প্রয়োজন হয়, তবে while হলো নিরাপদ টুল। আর যদি আপনার লজিকের জন্য অন্তত এক বা তার বেশি ইটারেশন প্রয়োজন হয়, তবে do-while সাধারণত বেশি পরিচ্ছন্ন টুল।

ভুল এবং সেরা অনুশীলনসমূহ

যেহেতু do-while লুপ অন্তত একবার এক্সিকিউশন নিশ্চিত করে, তাই আপনি যদি সতর্ক না হন তবে এটি একটি ইনফিনিট লুপ (অবিরাম লুপ) তৈরি করতে পারে। যদি কন্ডিশনে চেক করা ভেরিয়েবলগুলো ব্লকের ভেতরে কখনো পরিবর্তিত না হয়, তবে PHP চিরকাল চলতে থাকবে অথবা সর্বোচ্চ এক্সিকিউশন টাইম (maximum execution time) না আসা পর্যন্ত চলতে থাকবে।

আরেকটি সাধারণ ভুল হলো শেষে সেমিকোলন (semicolon) দিতে ভুলে যাওয়া। নিচের উদাহরণটি কাজ করবে না:

do {
    // code
} while ($x < 5)    // syntax error

যখন অন্য কোনো লুপ আরও স্পষ্টভাবে কাজ বোঝাতে পারে, তখন অভ্যাসবশত do-while ব্যবহার করা এড়িয়ে চলা উচিত। আপনি যদি কোনো অ্যারে (array) নিয়ে কাজ করেন, তবে foreach প্রায় সব সময়ই বেশি কার্যকর। আর যদি আপনি একটি সাধারণ নিউমেরিক কাউন্টার (numeric counter) পরিচালনা করেন, তবে একটি for লুপের মাধ্যমে ইনিশিয়ালাইজেশন (initialization), কন্ডিশন এবং ইনক্রিমেন্ট (increment) একই লাইনে রাখা যায়, যা পড়া বা বোঝা অনেক সহজ।

পারফরম্যান্সের দিক থেকে, PHP-তে while লুপের তুলনায় do-while লুপের কোনো উল্লেখযোগ্য বাড়তি চাপ (overhead) নেই। এর সুবিধাটি সম্পূর্ণভাবে কাঠামোগত। যখন অন্য কোনো ডেভেলপার একটি do-while লুপ দেখেন, তারা সাথে সাথে বুঝতে পারেন যে কোনো কন্ডিশন যাচাই করার আগেই ব্লকটি অবশ্যই একবার চলবে। এই একটি তথ্যই তাদের স্ট্যান্ডার্ড while লুপের উপরে ডামি ভেরিয়েবল বা ডুপ্লিকেট সেটআপ কোড খোঁজার ঝামেলা থেকে বাঁচিয়ে দেয়।

এজ কেস (Edge Cases) এবং বিকল্প প্যাটার্নসমূহ

মাঝে মাঝে ইউজার ইন্টারঅ্যাকশন না থাকলেও do-while লুপ কার্যকর হতে পারে। উদাহরণস্বরূপ, যখন আপনি এমন একটি জেনারেটর (generator) বা কার্সর (cursor) ব্যবহার করছেন যা অন্তত একটি আইটেম প্রদান করে, তখন আপনি প্রথম আইটেমটি প্রসেস করতে পারেন এবং তারপর কার্সরটি পরবর্তী সঠিক অবস্থানে গিয়েছে কিনা তা পরীক্ষা করতে পারেন। আধুনিক রেজাল্ট সেটের পরিবর্তে র (raw) ডাটাবেস রিসোর্স ব্যবহার করা পুরনো কোডবেসগুলোতে (legacy codebases) আপনি এই ধরণের প্যাটার্ন দেখতে পারেন:

$row = $result->fetch();
do {
    // process $row
} while ($row = $result->fetch());

যদিও আধুনিক PHP-তে PDO-এর সাথে foreach ব্যবহার করা সুবিধাজনক, তবুও এই প্যাটার্নটি দেখায় যে কীভাবে do-while অতিরিক্ত ব্রাঞ্চিং (branching) ছাড়াই প্রথম ইটারেশনটিকে (iteration) একটি বিশেষ ক্ষেত্র হিসেবে হ্যান্ডেল করে।

মূল সারসংক্ষেপ

do-while লুপ একটি নির্দিষ্ট কাজের জন্য তৈরি টুল, কিন্তু সঠিক জায়গায় এটিই একমাত্র যৌক্তিক সমাধান। এটি আপনার সেটআপ এবং ভ্যালিডেশন লজিককে এক জায়গায় রাখে, কোনো কাজ বিচার করার আগেই সেটি করার চেষ্টা করা হয়েছে কিনা তা নিশ্চিত করে এবং কোডটি পড়া পরবর্তী ব্যক্তির কাছে আপনার উদ্দেশ্য সরাসরি পৌঁছে দেয়। যখন কোনো কাজ করার পর কন্ডিশন চেক করা প্রয়োজন, তখন do-while ব্যবহার করুন। অন্য সব ক্ষেত্রে, সহজ while, for, বা foreach ব্যবহার করাই শ্রেয়।