পিএইচপি ফাংশনাল প্রোগ্রামিং এবং Scope পরিচিতি
গত ৩টি ক্লাসে আমরা PHP এর গঠন-প্রণালি, ব্যবহার পদ্ধতি, তার একেকটি শক্তিশালী বৈশিষ্ট্যসহ বিভিন্ন ধরণের সুবিধা-অসুবিধা ইত্যাদি দেখেছি। মোটকথা, একটি প্রোগ্রামিং ল্যাংগুয়েজ হিসেবে PHP তার অভ্যন্তরে যত ধরণের সৌন্দর্য বা বৈচিত্র ধারণ করে আমরা তার একটি ঝলক দেখেছি।
এখন প্রশ্ন হল, যতটুকু আমরা দেখেছি তাতে কি এটা ভাবা যায় যে PHP প্রোগ্রামিং ল্যাংগুয়েজটির শক্তিমত্তা আমাদের জানা হয়ে গেছে?
উত্তর নির্দ্বিধায় হবে, না। আমরা আসলে PHP এর আসল শক্তিমত্তা সম্পর্কে কিছুটা ধারণা পেয়েছি মাত্র। আমরা যা শিখেছি তা দিয়ে টুকটাক কাজ চালিয়ে নিতে পারব ঠিকই, কিন্তু দেখা যাবে একটা সহজ কাজ করতে আমাদের কি-বোর্ডে ঝড় তুলতে হবে।
কেন? ম্যাথমেটিকাল অপারেশন চালানোর সময় (২য় ক্লাসে) আমরা কিছু ফাংশনের ব্যবহার দেখেছিলাম। যেগুলোকে আমরা PHP এর বিল্ট-ইন ফাংশন বলেছিলাম। সেখানে আমরা pi() নামে একটা বিল্ট-ইন ফাংশন দেখেছিলাম, যা আমাদেরকে ইউনিভার্সাল পাই-এর মান বের করে দিত। এখন ভাবুন তো, যদি ঐ ফাংশনটি না থাকত তাহলে আমাদেরকে পাই-এর মান বের করার জন্য কি কি করতে হত?
আপনি যেকোন প্রোগ্রামিং ল্যাংগুয়েজ কেন শিখবেন? যাতে তার সাহায্যে আপনি কম্পিউটারের সাথে কথা বলতে পারেন, ফলে কম্পিউটার আপনার একঘেয়ে বিরক্তিকর, দীর্ঘ এবং ভারী কাজগুলো খুব সহজেই দ্রুততার সাথে সম্পন্ন করতে পারে। তাই নয় কি?
এখন শুধু একটা পাই-এর মান বের করার জন্য আপনাকে যদি কি-বোর্ডে ঝড় তুলতে হয়, লাইনের পর লাইন কোড লিখতে হয়; তাহলে প্রোগ্রামিং ল্যাংগুয়েজ বা কম্পিউটার আপনার জীবন সহজ করল কোথায়? এটাতো একটি উদাহরণ মাত্র। আরও অসংখ্য কাজ যে বাকি আছে সেগুলোও বা কিভাবে করবেন?
এখানেই আসে ফাংশনের কথা।
ফাংশন পরিচিতি
উপরের কথাগুলো যদি বুঝে থাকেন তাহলে আমরা এই সমীকরণে পৌঁছতে পারি যে ফাংশন আমাদের জীবন সহজ করে। কিন্তু কিভাবে? ফাংশন জিনিসটাই বা কি? কিভাবে কাজ করে? কিভাবে তৈরি হয়? ব্যবহার কিভাবে হয়? ফাংশন দেখলে বুঝব কিভাবে কিংবা ফাংশনের প্রকারভেদ কত ও কি কি?
এই ক্লাসে চলুন আমরা সবগুলোর উত্তর জানার চেষ্টা করি।
ফাংশন কাকে বলে?
ফাংশনকে আপনি একটি ব্লক অব কোড, পিস অব কোড বা সেট অব কোড বলতে পারেন। অর্থাৎ ফাংশন এক বা একাধিক কোড ইন্সট্রাকশনের নাম, যে কোডগুলো আপনি নির্দিষ্ট এক বা একাধিক কাজ সময়মতো আঞ্জাম দেয়ার জন্য একবার লিখে থাকেন এবং প্রয়োজন অনুসারে বারবার ব্যবহার করার প্রত্যাশা করেন।
ফাংশনের পরিচয় দেখে আশা করি বুঝতে পারছেন, এটা আপনার জীবন কিভাবে সহজ করবে। ফাংশনের ব্যবহার একবার শিখে গেলে আপনি তারপর কি করবেন? একই প্যাটার্নের একাধিক কাজ (ধরেন উদাহরণস্বরূপ: ১০টা) প্রোগ্রামের বিভিন্ন জায়গায় করার প্রয়োজন পড়লে প্রত্যেকবারই নতুন নতুন কোড লিখবেন নাকি একবার একটা ফাংশন লিখে সেটা ব্যবহার করবেন। যদি বলেন ফাংশন লিখব, তাহলে জীবন তো সহজ হয়ে গেল তাইনা।
ঠিক তাই। বাস্তব জীবনে প্রোগ্রামিং করতে গিয়ে আপনি ১০ জায়গায় একই সমস্যার সমাধানকল্পে ১০টি কোড লিখে সময় নষ্ট করতে চাইবেন না। বরং কোনভাবে একবার সমস্যার সমাধান করতে পারলে সেই কোডটাই অল্প এফোর্ট দিয়ে ১০ জায়গায় চালিয়ে দিতে চাইবেন। এটাই ইফেক্টিভ প্রোগ্রামিং।
PHP তে ফাংশন ঠিক এই দায়িত্বটাই পালন করে। আপনি কোন একটা সমস্যার সমাধান করতে একবার ফাংশন লিখবেন এবং যখন যেখানে যেভাবে প্রয়োজন ব্যবহার করবেন। প্রয়োজন না হলে করবেন না। আপনার হাতে যখন এমন শক্তি থাকবে তখন আর কিসের প্রয়োজন বলুন!
পিএইচপির ফাংশনাল পাওয়ার
ফাংশন প্রতিটি প্রোগ্রামিং ল্যাংগুয়েজেই থাকে। নাহলে একসময় ল্যাংগুয়েজটি বিশেষত্ব হারায়। তবে ফাংশনাল প্রোগ্রামিংয়ের ময়দানে পিএইচপির শক্তিমত্তা ঈষৎ ঈর্ষণীয় বটে। কিভাবে?
পিএইচপি-তে 1000 টিরও বেশি বিল্ট-ইন ফাংশন রয়েছে। ফলে এমন হতে পারে যে আপনি কোন সমস্যা বা কাজ নিয়ে মাথা ঘামাচ্ছেন আর ভাবছেন যে ”এর জন্য ফাংশনটা কিভাবে লিখব?” পিএইচপির ফাংশনগুলো একটু ঘাঁটাঘাটি করলে হয়তো দেখতে পাবেন যে ঐ রিলেটেড ফাংশন আগে থেকেই করা আছে। কতটা চমৎকার বিষয় তাইনা?
আবার, এর পাশাপাশি আপনি আপনার ইউনিক সমস্যা সমাধানের জন্য স্পেসিফিক ফাংশনও লিখতে পারেন এবং মনমতো ব্যবহার করতে পারেন। কতটা সহজ এবং সুবিধাজনক বৈশিষ্ট্য, তাইনা বলুন।
তাহলে সংক্ষেপে বলা যায় যে, পিএইচপিতে ফাংশন একটি সহজ এবং সুবিধাজনক পদ্ধতি। যার অধীনে এক বা একাধিক কোড কোন সমস্যার সমাধানকল্পে প্রয়োজন অনুসারে লেখা হয় এবং যখন যেখানে প্রয়োজন পড়ে সুবিধামত তা ব্যবহার করা হয়। পাশাপাশি পিএইচপিতে অনেকগুলো বিল্ট-ইন ফাংশন থাকায় প্রোগ্রামারের জন্য এর ব্যবহার আরো সহজ এবং সুলভ হয়।
এখন জানব ফাংশন কিভাবে তৈরি করতে হয়।
ফাংশন গঠন-প্রক্রিয়া এবং ব্যবহার পদ্ধতি
ফাংশন তৈরি করার জন্য প্রথমে function
এই কিওয়ার্ডটি ব্যবহার করতে হয়।
অতঃপর যে নামে ফাংশনটি তৈরি করতে চাইছেন তার নাম দিতে হয়।
অতঃপর Parentheses ()
লিখতে হয়। Parentheses ()
বা ১ম বন্ধনী মূলত প্যারামিটার লিখতে ব্যবহৃত হয়। প্যারামিটার কি জিনিস জানতে পড়ুন: ফাংশন প্যারামিটার ও আর্গুমেন্টস কি। যদি ফাংশনের কোন প্যারামিটার না থাকে, তবুও জেনারেল ফাংশন তৈরিতে এই Parentheses ()
লিখতে হয়।
অতঃপর Curly braces {}
দিতে হয়। এই {}
বা ২য় বন্ধনীর ভেতরে ফাংশনটি দিয়ে আমরা যা কিছু করতে চাই তা লিখতে হয়। একে আমরা ফাংশন বডিও বলতে পারি। ছোট থেকে ছোট, বড় থেকে বড় যেকোন ধরণের এবং সাইজের কোড আপনি এর ভেতর লিখতে পারেন। এর ভেতর সাধারণ লেখা ছাড়াও বিভিন্ন রকম লজিক বিল্ড করে যে কোন ধরণের প্রবলেম সলভিং কাজে একে ব্যবহার করতে পারেন।
তবে সাধারণত একটি বড় সমস্যাকে ব্রেক ডাউন করে প্রথমে ছোট ছোট সমস্যায় বা টাস্কে বিভক্ত করা হয়। অতঃপর প্রতিটি সমস্যা বা টাস্কের জন্য আলাদা ছোট ছোট ফাংশন লিখা হয়। বাস্তব জীবনে বড় কোন সমস্যার প্রয়োজনে এভাবেই ফাংশনের ব্যবহার দেখা যায়।
ফাংশন তৈরির সিনট্যাক্স:—
<?php
function functionName() {
// code to be executed
}
<?php
function functionName() {
// code to be executed
}
ফাংশন ব্যবহার পদ্ধতি
ফাংশন তৈরির পর ফাংশনটিকে ব্যবহার করতে হলে তাকে call বা invoke করতে হয়। সেক্ষেত্রে প্রথমে ফাংশনের নামটি দিতে হয়। অতঃপর Parentheses ()
দিতে হয়। ফাংশনে যদি প্যারামিটার সেট করা থাকে, তাহলে ফাংশন কল করার সময় – প্রত্যাশিত পরিমাণ অনুযায়ী – আর্গুমেন্ট এই Parentheses ()
এর ভেতর পাস করতে (চালাতে) হয়। যেমন:-
<?php
functionName(); // call the function
<?php
functionName(); // call the function
প্যারামিটার এবং আর্গুমেন্ট নিয়ে জানুন।
আসুন একটি বাস্তব উদাহরণ দেখি
<?php
function hello() {
echo "Hello World!";
}
hello(); // call the function
<?php
function hello() {
echo "Hello World!";
}
hello(); // call the function
আউটপুট:
Hello World!
Hello World!
ফাংশন তৈরি এবং ব্যবহার সংক্রান্ত গুরুত্বপূর্ণ কিছু তথ্য
ফাংশনের নাম পিএইচপির অন্যান্য লেবেলের (উদাহরণস্বরুপ: variable) মতই হওয়া উচিত। তাই ভ্যারিয়েবল তৈরিতে আমরা যে রুলগুলো ফলো করেছি তাই এখানে ফলো করা উচিত। যেমন:-
- ফাংশন নাম letter বা underscore
_
দিয়ে শুরু করা যেতে পারে, - ফাংশন নামের ভেতর any number of letters, numbers অথবা underscores থাকতে পারে,
- ফাংশন নাম numbers দিয়ে শুরু করা যাবে না, নামের ভেতর কোন ধরণের সিম্বল বা সাইন ব্যবহার করা যাবে না।
- কোডিং স্টান্ডার্ড অনুযায়ী ২ শব্দের ফাংশন নাম camelCase এ হওয়া উচিত। খেয়াল রাখবেন আপনার ফাংশন নেম যদি ২ শব্দবিশিষ্ট হয় তাহলে উভয় শব্দের মাঝে কোন স্পেস দেয়া যাবে না।
- ফাংশন নাম case-sensitive নয়।
- ফাংশনের নাম অর্থবোধক বা declarative হওয়া উচিত।
অর্থাৎ ফাংশনটি কি কাজ বা সমাধান প্রদান করে নামকরণের সময় এই বিষয়টি খেয়াল রাখা উচিত এবং সে অনুযায়ী নামকরণ করা উচিত। তাই আমাদের পূর্ববর্তী উদাহরণে ফাংশনের কাজ অনুযায়ী তার নামকরণ সাধারণভাবে
hello()
না দিয়ে অর্থবোধক কিছু দিতে পারি। যেহেতু ফাংশনটি সম্ভাষণ বা অভিবাদনের কাজ করে, তাই আমরা তার নামwriteMsg()
অথবাgreet()
দিতে পারি।
ফাংশনকে কল বা রেফার করার পূর্বে ডিফাইন বা তৈরি করে নিতে হবে। অর্থাৎ আপনি একটা ফাংশন কল করলেন, যেমন ধরুন:- hello()
অথচ কোডের কোথাও এই ফাংশনটি তৈরিই করেননি; এমন কিছু করা যাবেনা।
হ্যাঁ যদি ফাংশনটি conditionally কোথাও ডিফাইন করেন তখনকার কথা আলাদা। সেক্ষেত্রেও ঐ ফাংশনটি কল করার পূর্বে তার ডিফাইন হয়ে যাওয়া জরুরী, যাতে কন্ডিশন সত্যি হলেই তাকে এক্সেস এবং কল করার সুযোগ তৈরি হয়। বুঝে না থাকলে নিন্মে উদাহরণ ২টি দেখুন:-
Conditional functions এর উদাহরণ:–
<?php
$makefoo = true;
/* We can't call foo() from here
since it doesn't exist yet,
but we can call bar() */
bar();
if ($makefoo) {
function foo()
{
echo "I don't exist until program execution reaches me.\n";
}
}
/* Now we can safely call foo()
since $makefoo evaluated to true */
if ($makefoo) foo();
function bar()
{
echo "I exist immediately upon program start.\n";
}
<?php
$makefoo = true;
/* We can't call foo() from here
since it doesn't exist yet,
but we can call bar() */
bar();
if ($makefoo) {
function foo()
{
echo "I don't exist until program execution reaches me.\n";
}
}
/* Now we can safely call foo()
since $makefoo evaluated to true */
if ($makefoo) foo();
function bar()
{
echo "I exist immediately upon program start.\n";
}
Functions within functions এর উদাহরণ:–
<?php
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* We can't call bar() yet
since it doesn't exist. */
foo();
/* Now we can call bar(),
foo()'s processing has
made it accessible. */
bar();
<?php
function foo()
{
function bar()
{
echo "I don't exist until foo() is called.\n";
}
}
/* We can't call bar() yet
since it doesn't exist. */
foo();
/* Now we can call bar(),
foo()'s processing has
made it accessible. */
bar();
ফাংশন গ্লোবাল স্কোপে ডিফাইন করা হোক অথবা অন্য কোন ফাংশনের অভ্যন্তরে ডিফাইন করা হোক PHP তে সকল ফাংশনই গ্লোবাল স্কোপে থাকে। অর্থাৎ ফাংশন যেখানেই ডিফাইন করা হোক আপনি তাকে সবজায়গা থেকেই কল করতে পারবেন। ভ্যারিয়েবলের মত ফাংশনের বেলায় ২টি স্কোপ নয়, একটিমাত্র স্কোপ এবং সেটি গ্লোবাল। স্কোপ নিয়ে এই ক্লাসেই আমরা জানব।
পাইথন বা জাভা ল্যাংগুয়েজের মত PHP ফাংশন overloading সাপোর্ট করেনা। ফাংশন overloading এর মাধ্যমে মূলত একটি ফাংশনকে redefine বা redeclare করা যায়। পাশাপাশি একই নামের ফাংশনকে একবার এক ধরণের প্যারামিটার সেট করে ব্যবহার করা যায়, অন্যবার অন্য ধরণের প্যারামিটার সেট করে ব্যবহার করা যায়। PHP তে একবার ফাংশন ডিফাইন করলে তাকে পুরো script execution এ আর undefine, redefine অথবা overload করা যায়না।
ফাংশন নাম যেহেতু case-sensitive নয়, তাই ফাংশন কল করার সময় যেকোন ভাবেই কল করা সম্ভব। তবে উত্তম এবং উচিত হল, যে ধারায় ফাংশন ডিফাইন হয় সে ধারায়ই তাকে কল করা।
ফাংশন প্যারামিটার ও আর্গুমেন্টস কি?
ফাংশনের মধ্যে আপনি প্যারামিটার সেট করতে পারেন সেটা ইতিমধ্যেই জেনেছেন। কিন্তু প্যারামিটার আসলে কি?
চলুন সাধারণভাবে আমরা প্যারামিটার বোঝার চেষ্টা করি। আপনি ইতিমধ্যেই ভ্যারিয়েবল সম্পর্কে জেনেছেন। ভ্যারিয়েবল ইনিশিয়ালাইজ করা যেমন:-
<?php
$var; // initialize variable without value
<?php
$var; // initialize variable without value
ভ্যারিয়েবলের ভ্যালু ডিফাইন করা যেমন:-
<?php
$var = 'Value defined';
<?php
$var = 'Value defined';
এগুলো প্রথম ক্লাসেই জেনেছেন।
ঠিক একই ভাবে আপনি ফাংশন প্যারামিটারকে শুধুমাত্র ফাংশনের মধ্যে ব্যবহারযোগ্য ভ্যারিয়েবল হিসেবে বিবেচনা করতে পারেন। তাই আপনি যখন ফাংশনে কোন প্যারামিটার সেট করেন, তখন মূলত আপনি ঐ ফাংশনটিতে ব্যবহার করতে চান এমন এক বা একাধিক ভ্যালুর জন্য ভ্যারিয়েবল ইনিশিয়ালাইজ করেন। অতঃপর ফাংশন কল করার সময় আপনি ভ্যালুগুলো ডিফাইন করে দেন বা বলতে পারি pass করে দেন।
তাহলে আমরা বলতে পারি, প্যারামিটার হচ্ছে ফাংশনের মধ্যে ডাটা পাঠানোর একটি মাধ্যম বা বাহক। আপনি প্রথমে প্যারামিটার রূপে ডাটা স্টোর করার এক বা একাধিক ভ্যারিয়েবল সেট করেছেন। ডাটাগুলো পেলে ফাংশনটি কিভাবে বিহেভ করবে তা সেট করেছেন এবং সর্বশেষে ফাংশন কল করে কাঙ্খিত ডাটা পাঠিয়ে দিয়েছেন।
এখন প্রশ্ন হল, প্যারামিটার কোথায় এবং কিভাবে সেট করতে হয়?
আপনি যখন ফাংশন ডিফাইন করবেন তখন Parentheses () বা ১ম বন্ধনীর ভেতর প্যারামিটার সেট করতে হয়। ভ্যারিয়েবল বা ফাংশনের নামকরণের সময় যে বিষয়গুলো খেয়াল রাখতে হয় প্যারামিটারের নামকরণেও সেই একই বিষয় খেয়াল রাখতে হয়।
একটি ফাংশনে আপনি কতগুলো প্যারামিটার পারেন?
অসংখ্য অগনিত প্যারামিটার সেট করতে পারেন। খেয়াল করার বিষয় হল, একাধিক প্যারামিটার সেট করলে প্রতিটি প্যারামিটারকে কমা ,
দিয়ে আলাদা করতে হবে।
ফাংশনের প্যারামিটার লিখার সিনট্যাক্স হল:-
<?php
function functionName($parameter1, $parameter2, $parameter3) {
// code to be executed
}
<?php
function functionName($parameter1, $parameter2, $parameter3) {
// code to be executed
}
খেয়াল করার বিষয় হল, আপনি ফাংশনে প্যারামিটার সেট করলে এবং তার কোন ডিফল্ট ভ্যালু এসাইন না করলে ফাংশন কল করার সময় আর্গুমেন্ট আকারে প্যারামিটারের ভ্যালু অবশ্যই প্রদান করতে হবে। কারণ তখন প্যারামিটারগুলো ফাংশনের জন্য required arguments হিসেবে বিবেচিত হয়। আর্গুমেন্ট আকারে ভ্যালু প্রদান না করলে এরর পাবেন।
এবার আসুন জানার চেষ্টা করি ফাংশন আর্গুমেন্ট কি।
ফাংশন প্যারামিটারের কনসেপ্ট টা যদি বুঝে থাকেন তাহলে আর্গুমেন্ট আপনি হয়তো ইতিমধ্যেই বুঝে গেছেন। তবুও আরো পরিষ্কারভাবে বুঝতে চাইলে বলব, ফাংশনে আমরা তো প্যারামিটার সেট করেছি। যে আমাদের পাঠানো ভ্যালুগুলো ভ্যারিয়েবলের মত হোল্ড করবে। এখন ভ্যালুগুলো পাঠাবেন কি করে? উত্তর হল, আর্গুমেন্টের সাহায্যে বা বলতে পারি ফাংশনে আপনি যেকোন ভ্যালু শুধুমাত্র আর্গুমেন্টের মাধ্যমেই পাঠাতে পারবেন।
তাই বলতে পারি ফাংশন কল করার সময় যে ডাটা বা ভ্যালু আমরা পাস করি তাকে আর্গুমেন্ট বলে। মনে রাখবেন, আর্গুমেন্ট এবং প্যারামিটার শুধুমাত্র ২টি টার্ম। তাই আপনি চাইলে প্যারামিটারকে আর্গুমেন্ট এবং আর্গুমেন্টকে প্যারামিটার এভাবে interchange করে বলতে পারেন। প্রোগ্রামিং জীবনে অনেককেই এমনটা করতে দেখবেন। তাতে বিভ্রান্ত হবেননা।
তাহলে আর্গুমেন্ট কিভাবে পাস করব? উদাহরণ দেখি:-
<?php
functionName($argument1, $argument2, $argument3);
<?php
functionName($argument1, $argument2, $argument3);
এবার আসুন ২ টার সম্মিলিত উদাহরণ দেখি:-
<?php
function sum($num1, $num2) { // two parameters in function definition
$total = $num1 + $num2;
echo "Sum of the two numbers $num1 and $num2 is : $total";
}
sum(10, 20); // argument values passed in function call as needed
<?php
function sum($num1, $num2) { // two parameters in function definition
$total = $num1 + $num2;
echo "Sum of the two numbers $num1 and $num2 is : $total";
}
sum(10, 20); // argument values passed in function call as needed
আউটপুট:
Sum of the two numbers 10 and 20 is : 30
Sum of the two numbers 10 and 20 is : 30
এবার আরো একটি উদাহরণ দেখি:-
<?php
function familyName($fname) {
echo "$fname Refsnes.<br>";
} // familyName function has one argument named ($fname)
/*
When the familyName() function is called,
we also pass along a name (e.g. Jani, etc.),
the name is used inside the function,
which outputs several different first names, but an equal last name:
*/
familyName("Jani");
familyName("Hege");
familyName("Stale");
familyName("Kai Jim");
familyName("Borge");
<?php
function familyName($fname) {
echo "$fname Refsnes.<br>";
} // familyName function has one argument named ($fname)
/*
When the familyName() function is called,
we also pass along a name (e.g. Jani, etc.),
the name is used inside the function,
which outputs several different first names, but an equal last name:
*/
familyName("Jani");
familyName("Hege");
familyName("Stale");
familyName("Kai Jim");
familyName("Borge");
আউটপুট:
Jani Refsnes.
Hege Refsnes.
Stale Refsnes.
Kai Jim Refsnes.
Borge Refsnes.
Jani Refsnes.
Hege Refsnes.
Stale Refsnes.
Kai Jim Refsnes.
Borge Refsnes.
আরেকটি উদাহরণ দেখিঃ
<?php
function familyName($fname, $year) {
echo "$fname Refsnes. Born in $year <br>";
}
familyName("Hege", "1975");
familyName("Stale", "1978");
familyName("Kai Jim", "1983");
<?php
function familyName($fname, $year) {
echo "$fname Refsnes. Born in $year <br>";
}
familyName("Hege", "1975");
familyName("Stale", "1978");
familyName("Kai Jim", "1983");
আউটপুট:
Hege Refsnes. Born in 1975
Stale Refsnes. Born in 1978
Kai Jim Refsnes. Born in 1983
Hege Refsnes. Born in 1975
Stale Refsnes. Born in 1978
Kai Jim Refsnes. Born in 1983
প্যারামিটার ও আর্গুমেন্ট নিয়ে তো জানলাম। এখন কি?
চলুন পিএইচপির ফাংশনাল পাওয়ার সম্পর্কে আরো কিছু জানি।
পিএইচপির ডাটা টাইপ সম্পর্কে আমরা প্রথম ক্লাসেই জেনেছি। প্যারামিটার ও আর্গুমেন্ট নিয়ে যে উদাহরণগুলো উপরে দেখেছেন সেখানে একটা বিষয় কি আপনি খেয়াল করেছেন, আমরা কোন ফাংশনের আর্গুমেন্টে নাম্বার টাইপ ডাটা (পিএইচপির ভাষায় বললে ইন্টিজার) পাস করেছি, কোন ফাংশনে আবার স্ট্রিং টাইপ ডাটা পাস করেছি।
প্রশ্ন হল, আমাদের ফাংশন বুঝতে পারছে কিভাবে কোনটা কি টাইপের ডাটা এবং সে অনুযায়ী কোনটার সাথে কি করা উচিত? কোনো সিস্টেম কি পিএইচপি ব্যবহার করছে যাতে ফাংশনগুলো কোনভাবে বুঝতে পারে কি ধরণের ডাটা তারা পাচ্ছে? থাকলে তা ব্যবহারের উপায় কি? নাকি ফাংশনের বেলায় এমন কিছু বিবেচনায় রাখা হয়না?
চলুন নেক্সট টপিকে উত্তরগুলো মেলানোর চেষ্টা করি। তার আগে পিএইচপির একটি সুন্দর বৈশিষ্ট্য নিয়ে একটু জানি চলুন।
PHP কে Loosely Typed কেন বলা হয়?
পিএইচপিকে বলা হয় Loosely Typed Language। না, ভুল বুইঝেন না। কোন লুজ মেন্টালিটির মানুষ এইটা লিখেছে তা নয়। পিএইচপিকে তৈরি করা হয়েছে এমনভাবে যাতে আপনি যেকোন টাইপের ডাটা তাকে প্রদান করলে সে সেটা থেকে অর্থবোধক কিছু আপনাকে তৈরি করে দিতে পারে। অন্য অনেক ল্যাংগুয়েজের মত সে কোন ধরণের স্ট্রিক্ট বা কঠিন নিয়মের বেড়াজালে আপনাকে আটকে রাখেনি।
তাই আপনি যদি কোন ডাটা তৈরি বা পাস করার সময় তার প্রকৃতি পিএইচপিকে না বলেন, তবুও সে আপনার হয়ে ঐ ডাটার গতি-প্রকৃতি দেখে রানটাইমে তার জন্য যোগ্য ডাটা টাইপ নিজ থেকেই সেট করে দিবে। যেমন আপনি যেখানে পূর্ণ নাম্বার পাস করেছেন পিএইচপি সেই ডাটার টাইপ ইন্টিজার সেট করে দিবে, আবার যেখানে স্ট্রিং পাস করেছেন সেখানে ডাটা টাইপও স্ট্রিং সেট করে দিবে। এই কাজ পিএইচপি ইন্টার্নালি স্ক্রিপ্ট রান করার সময় করে থাকে।
কিন্তু আপনি যদি ভুলবশত যেখানে ইন্টিজার নাম্বার প্রত্যাশিত সেখানে সরাসরি নাম্বার না দিয়ে স্ট্রিংয়ের ভেতর নাম্বারটা দেন তখন কি হবে?
যেমন ধরুন আপনি ৫ এর সাথে ৫ গুণ করতে চাইছেন। এখন এটি কোডে রূপান্তর করে আপনি কিভাবে লিখবেন? 5 * 5
এভাবে লিখলে তো হবে, কিন্তু আপনি যদি 5 * "5"
এভাবে লিখেন তখন কি হবে? আপনি কি সঠিক উত্তর পাবেন?
হ্যাঁ আপনি সঠিক উত্তরটিই পাবেন। কিন্তু কাজটা কেমন গোলমেলে হয়ে গেল না? ১টি নাম্বারের সাথে আরেকটি নাম্বারের গুণ হওয়া উচিত না? তো যেখানে নাম্বার প্রত্যাশিত ছিল সেখানে ভিন্ন একটি ডাটা টাইপ দিয়েও আপনি সঠিক উত্তরটি কিভাবে পাচ্ছেন?
কারণ পিএইচপি আপনার উদ্দেশ্য বুঝতে পেরে স্ট্রিং ৫ সংখ্যাকে ইন্টিজারে কনভার্ট করে অতঃপর গুণের কাজটি সেরেছে। তাই আপনি সঠিক উত্তর পেয়েছেন। এই যে, একটি Fatal Error খাওয়ার কাজ করেও আপনি সঠিক উত্তরটি পেলেন, পিএইচপি আপনাকে এরর দেয়া তো দূরের কথা বরং আপনার হয়ে ভুল ইনপুটকে সঠিক করে তারপর সঠিক আউটপুট দিল, এই সমস্ত কারণেই পিএইচপিকে Loosely Typed Language বলা হয়। যেখানে আপনি কোড স্টান্ডার্ড মেনে না চললেও পার পেয়ে যাবেন।
এটার উপর নির্ভর করে রিয়াল ওয়ার্ল্ড প্রোগ্রামিংয়েও কি আপনি এমনটা করবেন?
উত্তর হল, না করবেন না। এমনটা করলে আপনার প্রোগ্রাম বা এপ্লিকেশনে প্রচুর বাগ (BUG) সৃষ্টি হবে। ফলে একটা ইফেক্টিভ প্রোডাক্ট তৈরি হবে না এবং প্রতিযোগিতামূলক মার্কেটে আপনার প্রোডাক্ট টিকবে না। পাশাপাশি ইউজারদের যাচ্ছেতাই ইনপুট ঠিকমত স্যানিটাইজ না করলে এই দুর্বলতাকে কাজে লাগিয়ে হ্যাকাররা আপনার প্রোগ্রামে পেনেট্রেট করবে।
এর প্রতিরোধ কি তাহলে?
এর প্রতিরোধে পিএইচপি আপনাকে সুযোগ দিয়েছে প্রত্যাশিত ভ্যালুর ডাটা টাইপ নির্ধারণ করার। ভ্যারিয়েবল ডিক্লারেশন থেকে শুরু করে ফাংশনের প্যারামিটার সেট করাসহ সবজায়গায় আপনি ভ্যালুর ডাটা টাইপ সেট করে দিতে পারবেন।
ভ্যারিয়েবল ডিক্লারেশনে ডাটা টাইপ সেট করার প্রয়োজনীয়তা অনুভূত হয়না। আপনি যদি ইন্টিজার ভ্যালুই সেট করতে চান কোন ভ্যারিয়েবলে, তখন কেন অযথাই ফ্লোট ভ্যালু তাতে সেট করবেন এবং ডাটা টাইপ সেট করে আবার সেটাকে ইন্টিজারে কনভার্ট করবেন। সম্পূর্ণ অপ্রয়োজনীয় একটি কাজ। হ্যাঁ, যদি ভ্যারিয়েবলটির ভ্যালু ডাইনামিকালি ইউজারের ইনপুটের মাধ্যমে সেট হয়, সেক্ষেত্রে ডাটা টাইপ সেট করে ভ্যালুটা স্যানিটাইজ করা যেতে পারে এবং ইউজারকে স্পেসিফিক টাইপের ভ্যালু প্রদানে বাধ্য করা যেতে পারে।
ফাংশন প্যারামিটারের ব্যাপারটা প্রায় এরকমই। ফাংশন কল করার সময় আর্গুমেন্টে স্পেসিফিক টাইপের ভ্যালুর বদলে কেউ অপ্রত্যাশিত টাইপের ভ্যালু পাঠাতে পারে। এজন্য ফাংশন প্যারামিটার নির্ধারণ করার সময় ডাটা টাইপ সেট করার প্রয়োজনীয়তা অত্যাধিক। যাতে আর্গুমেন্ট পাস করতে গিয়ে কেউ ভুলভাল ইনপুট না দেয়।
এবার চলুন আগের টপিকের উত্তরগুলো খুঁজে বের করি।
প্যারামিটার/আর্গুমেন্ট ডাটা টাইপ নির্ধারণ
আমরা জেনেছি ফাংশনে ডাটা টাইপ নির্ধারণের প্রয়োজনীয়তা। কিভাবে? এবার সেটা জানি।
প্যারামিটার সেট করার সময় তার ডাটা টাইপ নির্ধারণ করতে প্যারামিটার নামের আগে ডাটার টাইপটা বলে দিতে হবে। একে আমরা type declarations-ও বলি। প্রথম ক্লাসে আমরা এটা নিয়ে বিস্তারিত জেনেছি। ডাটা টাইপ Parentheses ()
এর ভেতর লেখা যায়। আবার Parentheses ছাড়াও লেখা যায়। উদাহরণ দেখি:-
<?php
function addNumbers(int $a, int $b) {
return $a + $b;
}
// since strict is NOT enabled "5 days" is changed to int(5), and it will return 10
echo addNumbers(5, "5 days");
<?php
function addNumbers(int $a, int $b) {
return $a + $b;
}
// since strict is NOT enabled "5 days" is changed to int(5), and it will return 10
echo addNumbers(5, "5 days");
আউটপুট:-
10
10
ডাটা টাইপ সেট তো করলেন কিন্তু ফাংশন কল করার সময় ইনপুটও তো ভুল দিলেন, আউটপুটে তো Error আসার কথা তাইনা? অথচ সঠিক আউটপুট পাচ্ছেন। কিভাবে?
টাইপ ডিক্লারেশন করার পরও PHP এর ডিফল্ট বিহেভিয়ারে কোন পরিবর্তন বা ইফেক্ট হয়না। PHP তার ন্যাচারাল বিহেভিয়াল বজায় রাখে। এর কারণ, টাইপ ডিক্লারেশন করার পর কোডের ভেতর যে Strict Maintainability দরকার পড়ে শুধুমাত্র ডাটা টাইপ ডিক্লেয়ার করার মাধ্যমে PHP সেভাবে বিহেভ করা শুরু করেনা। তার জন্য PHP 7 থেকে ইন্ট্রোডিউস হওয়া একটা ফিচার আপনাকে ইম্প্লিমেন্ট করতে হয়। আর সেটা হল, স্ট্রিক্ট টাইপ ডিক্লারেশন বা মোর প্রিসাইজলি declare(strict_types=1)
।
পিএইচপি ওপেনিং ট্যাগের পর অন্য সকল কোডের আগে এই ডিক্লারেশন প্রোভাইড করতে হয়। এরপর থেকে পিএইচপি আপনার আমার প্রত্যাশা অনুযায়ী স্ট্রিক্টলি মেইনটেইন করা শুরু করে। তাই এই ডিক্লারেশনের পর আপনি ভুল ইনপুট দিলে প্রত্যাশা অনুযায়ী Error পাবেন। উদাহরণ:-
<?php
declare(strict_types=1); // strict requirement
function addNumbers(int $a, int $b) {
return $a + $b;
}
// since strict is enabled and "5 days" is not an integer, an error will be thrown
echo addNumbers(5, "5 days"); // PHP Fatal error: Uncaught TypeError
<?php
declare(strict_types=1); // strict requirement
function addNumbers(int $a, int $b) {
return $a + $b;
}
// since strict is enabled and "5 days" is not an integer, an error will be thrown
echo addNumbers(5, "5 days"); // PHP Fatal error: Uncaught TypeError
আউটপুট:-
PHP Fatal error: Uncaught TypeError: addNumbers(): Argument #2 ($b) must be of type int, string given
PHP Fatal error: Uncaught TypeError: addNumbers(): Argument #2 ($b) must be of type int, string given
তাহলে স্ট্রিক্ট টাইপ ডিক্লারেশন ছাড়া শুধুমাত্র ডাটা টাইপ সেট করার উপকারিতা কি?
শুধুমাত্র হিন্ট দেয়া ছাড়া সেক্ষেত্রে আর তেমন উপকারিতা নেই। অর্থাৎ তখন আপনি নিজেকে এবং অন্য ডেভলপারদের এক ধরণের হিন্ট দিচ্ছেন যে কি ধরণের ডাটা আসলে ফাংশনে তাদের পাস করা উচিত। এইজন্য একে টাইপ হিন্টিংও বলা হয়। এর বেশি তেমন কোন উপকারিতা নেই।
আচ্ছা, উপরের উদাহরণগুলোতে দেখতে পেয়েছেন যে আর্গুমেন্টের মাধ্যমে আমরা ফাংশনে আমাদের ডিরেক্ট ডাটা বা ভ্যালুগুলো পাস করি। হতে পারে সেটা হার্ডকোডেড বা ভ্যারিয়েবলে ভেতর থাকা কোন ভ্যালু। এই ডাটা বা ভ্যালুগুলো ফাংশনের ভেতর by value আকারে পাস হয়। ফলে ফাংশনের ভেতরে ভ্যালুর চেঞ্জ হলে পরিবর্তনটা ফাংশনের ভেতরেই দেখা যায়। ফাংশনের বাইরে ঐ ভ্যালুর কোন পরিবর্তন দেখা যায়না। এটা ফাংশনে ভ্যালু পাস করার ডিফল্ট পদ্ধতি। এছাড়াও passing by reference, default argument values, Variable-length argument lists বা আনলিমিটেড আর্গুমেন্ট পাস করা এবং Named Arguments ইত্যাদির মাধ্যমেও ফাংশনে ভ্যালু পাস করা যায়। পরবর্তীতে আমরা একে একে সব জানব।
Passing Arguments by Reference
আমরা চাইলে ফাংশনে রেফারেন্স ভ্যালুও পাস করতে পারি। সেক্ষেত্রে আর্গুমেন্টে আমাদের ভ্যালু হোল্ড করা কোন ভ্যারিয়েবল পাস করতে হয়। ফলে যখন ফাংশনের ভেতর ভ্যালু চেঞ্জ হয়, তখন ঐ রেফার করা ভ্যারিয়েবল - ফাংশনের ভেতরে বা বাহিরে তথা স্ক্রিপ্টের যেখানেই থাকুক - তার ভ্যালুও চেঞ্জ হয়ে যায়। এটি ইম্প্লিমেন্ট করতে হলে ফাংশন প্যারামিটার সেট করার সময় প্যারামিটার নামের আগে একটি &
বা Ampersand Sign বসাতে হয়।
এই কনসেপ্টটা বুঝতে হলে প্রথম ক্লাসের ভ্যারিয়েবল assigned by reference টা আপনাকে বুঝতে হবে। কারণ, ঐ একই প্রক্রিয়া এখানেও ফলো করা হয়। উদাহরণ দেখুন:-
<?php
function add_five(&$value) {
$value += 5;
}
$num = 2;
add_five($num);
echo $num;
<?php
function add_five(&$value) {
$value += 5;
}
$num = 2;
add_five($num);
echo $num;
আউটপুট:
7
7
Default Argument Value
ফাংশনে ভ্যালু পাবার আরো একটি পদ্ধতি হল প্যারামিটারের ডিফল্ট ভ্যালু সেট করে রাখা।
প্যারামিটারের পরিচয়ে আমরা বলেছি প্যারামিটার ভ্যারিয়েবলের মত। তাই এর বিহেভিয়ারও ভ্যারিয়েবলের মত। যেমনিভাবে uninitialized variable এর কনসেপ্ট আমরা দেখেছি ঠিক তেমনি যখন কোন ভ্যালু এসাইন না করে আমরা ফাংশন প্যারামিটার সেট করি তখন সেটা তো uninitialized variable এর মতই তাইনা। পরবর্তীতে ফাংশন কল করতে আর্গুমেন্ট হিসেবে আমরা যে ভ্যালু পাস করি যা মূলত প্যারামিটারের ভ্যালু হিসেবে সেট হয়, সেটা তো পরের বিষয়।
এখন যেহেতু বলেছি ভ্যারিয়েবলের মত বিহেভ করবে এইজন্য আপনি চাইলে value initialized variable এর মত ফাংশন তৈরির সময় প্যারামিটারেরও value initialize করতে পারেন। এইটা করতে যেভাবে কোন ভ্যারিয়েবলের ভ্যালু এসাইন করেন ঠিক সেভাবেই প্যারামিটার নামের পর =
(Assignment Operator) দিয়ে তারপর ভ্যালু ইনিশিয়ালাইজ করতে পারেন। একে ফাংশনের ভাষায় প্যারামিটার/আর্গুমেন্টের ডিফল্ট ভ্যালু সেট করা বলে।
এর সুবিধা হল, আপনি যদি ফাংশন কল করার সময় এক বা একাধিক আর্গুমেন্টে কোন ভ্যালু প্রদান না করেন তখন ঐ পার্টিকুলার আর্গুমেন্টের জন্য যে ডিফল্ট ভ্যালু প্যারামিটারে সেট করে রাখা আছে ফাংশনের ভেতর সেটা এভেলেবল হবে। আর যদি ভ্যালু আপনি পাস করে দেন তখন ডিফল্ট ভ্যালু চেঞ্জ হয়ে আপনার দেয়া ভ্যালুটা প্যারামিটার ভ্যালু হিসেবে সেট হবে এবং সেই ভ্যালুটা ফাংশনের ভেতর এভেলেবল হবে।
মনে আছে, ভ্যারিয়েবলের ভ্যালু ভ্যারি করে বা পরিবর্তন হয়। প্যারামিটারেও তাই ঘটে। ডিফল্ট ভ্যালু সেট করা থাকলে প্যারামিটারের ভ্যালু হিসেবে তাকে গণ্য করা হয় যখন আর্গুমেন্টে ভ্যালু পাস না করা হয়। আর্গুমেন্টে ভ্যালু পাস করা হলে প্যারামিটারের ভ্যালু চেঞ্জ হয়ে পাস করা ভ্যালুটা বসে যায়। উদাহরণ দেখি:-
<?php
function makecoffee($type = "cappuccino")
{
return "Making a cup of $type.\n";
}
echo makecoffee(); // argument value not passed
echo makecoffee(null); // it means $type = null
echo makecoffee("espresso"); // it means $type = "espresso"
<?php
function makecoffee($type = "cappuccino")
{
return "Making a cup of $type.\n";
}
echo makecoffee(); // argument value not passed
echo makecoffee(null); // it means $type = null
echo makecoffee("espresso"); // it means $type = "espresso"
আউটপুট:
Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.
Making a cup of cappuccino.
Making a cup of .
Making a cup of espresso.
এখন প্যারামিটারে ডিফল্ট ভ্যালু হিসেবে —scalar values, arrays, the special type null
এবং as of PHP 8.1.0, objects using the new ClassName()
syntax— এগুলোও সেট করতে পারবেন। scalar values সেট করার উদাহরণ আগেই দেখেছি। এবার arrays সেট করার উদাহরণ দেখি:-
<?php
function makecoffee($types = array("cappuccino"), $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(array("cappuccino", "lavazza"), "teapot");
<?php
function makecoffee($types = array("cappuccino"), $coffeeMaker = NULL)
{
$device = is_null($coffeeMaker) ? "hands" : $coffeeMaker;
return "Making a cup of ".join(", ", $types)." with $device.\n";
}
echo makecoffee();
echo makecoffee(array("cappuccino", "lavazza"), "teapot");
আউটপুট:
Making a cup of cappuccino with hands.
Making a cup of cappuccino, lavazza with teapot.
Making a cup of cappuccino with hands.
Making a cup of cappuccino, lavazza with teapot.
array সম্পর্কে আমরা জানতে পঞ্চম ক্লাস দেখুন। আপাতত কোড বুঝতে অসুবিধা হলে স্কিপ করে যান।
উপরের উদাহরণে join()
ফাংশনের ব্যবহার জানতে ম্যানুয়াল পড়ুন।
লক্ষ্যণীয় বিষয়, ডিফল্ট ভ্যালু অবশ্যই constant expression হতে হবে। ভ্যারিয়েবল, অন্য কোন ফাংশন কল অথবা অন্য কোন class member হতে পারবেনা।
required arguments এর কথা মনে আছে? যখন প্যারামিটারের ডিফল্ট ভ্যালু সেট না করা হয় তখন সেটা required arguments হিসেবে বিবেচিত হয়। অর্থাৎ আপনি ফাংশন কল করবেন কিন্তু আর্গুমেন্ট পাস করবেন না এমনটা হতে পারবেনা। এরর পাবেন। কিন্তু উল্টো হল আপনি যখন ডিফল্ট ভ্যালু সেট করেন তখন ফাংশন কল করার সময় আর্গুমেন্ট পাস করা অপশনাল হয়ে যায়। একে optional arguments ও বলতে পারেন। এখন ভ্যালু পাস করলে ভালো, না পাস করলেও সমস্যা নাই। ফাংশন আগে থেকে সেট করা ডিফল্ট ভ্যালু ব্যবহার করবে।
required arguments এবং optional arguments কেন্দ্রিক সমস্যা
তবে সমস্যা হল, আপনার ফাংশনে যদি required arguments এবং optional arguments দুটোই সেট করা হয় তখন কি হবে?
সেক্ষেত্রে ফাংশন তৈরির সময় optional arguments গুলো required arguments এর পরে সেট করতে হবে। যদি উল্টোটা করেন এবং ভাবেন optional arguments এর ভ্যালুগুলো তো সেট করা আছে তাহলে ফাংশন কল করার সময় শুধু required arguments এর ভ্যালুগুলো দিব। এমনটা ভাবলে বড় ধরণের হোঁচট খাবেন।
কেন? কারণ আপনি এমনটা ভাবলেও পিএইচপি এমনটা ভাবতে পারেনি। সে তার স্বভাব অনুযায়ী required arguments এর ভ্যালু তার নির্দিষ্ট সিরিয়ালে খুঁজতে থাকবে। আগে পরে কি আছে সেটা সে দেখবেনা। তাই নির্দিষ্ট সিরিয়ালে সে যদি কোন ভ্যালু না পায় তাহলে ধরে নেবে আপনি তার ভ্যালু প্রোভাইড করেননি এবং প্যারামিটার ফাংশন এক্সিকিউট হওয়ার পরও আন-ইনিশিয়ালাইজড অবস্থায় থাকতে পারেনা তাই আপনাকে এরর দেবে।
উদাহরণ দেখুন:-
<?php
function makeyogurt($container = "bowl", $flavour)
{
return "Making a $container of $flavour yogurt.\n";
}
// "raspberry" is $container, not $flavour. $flavour which is required
// but value was not given
echo makeyogurt("raspberry");
<?php
function makeyogurt($container = "bowl", $flavour)
{
return "Making a $container of $flavour yogurt.\n";
}
// "raspberry" is $container, not $flavour. $flavour which is required
// but value was not given
echo makeyogurt("raspberry");
আউটপুট:-
Fatal error: Uncaught ArgumentCountError: Too few arguments
to function makeyogurt(), 1 passed in example.php on line 42
Fatal error: Uncaught ArgumentCountError: Too few arguments
to function makeyogurt(), 1 passed in example.php on line 42
এবার, নিচের উদাহরণের সাথে compare করুন:-
<?php
function makeyogurt($flavour, $container = "bowl")
{
return "Making a $container of $flavour yogurt.\n";
}
// "raspberry" is $flavour, which is required argument and the value was set
echo makeyogurt("raspberry");
<?php
function makeyogurt($flavour, $container = "bowl")
{
return "Making a $container of $flavour yogurt.\n";
}
// "raspberry" is $flavour, which is required argument and the value was set
echo makeyogurt("raspberry");
আউটপুট:-
Making a bowl of raspberry yogurt.
Making a bowl of raspberry yogurt.
As of PHP 8.0.0, আপনি named arguments ব্যবহার করে multiple optional parameters কে স্কিপ করতে পারেন। উদাহরণ:-
<?php
function makeyogurt($container = "bowl", $flavour = "raspberry", $style = "Greek")
{
return "Making a $container of $flavour $style yogurt.\n";
}
echo makeyogurt(style: "natural");
<?php
function makeyogurt($container = "bowl", $flavour = "raspberry", $style = "Greek")
{
return "Making a $container of $flavour $style yogurt.\n";
}
echo makeyogurt(style: "natural");
আউটপুট:-
Making a bowl of raspberry natural yogurt.
Making a bowl of raspberry natural yogurt.
named arguments নিয়ে আমরা সামনে জানব।
তবে As of PHP 8.0.0, required arguments কে optional arguments এর পর ডিক্লেয়ার করাটা deprecated হয়ে গেছে। এর ফলে আপনি optional arguments গুলোতে যে ডিফল্ট ভ্যালু সেট করে রেখেছিলেন তা আর ভ্যালিড থাকবেনা। তখন এমন হবে যেন আপনি ডিফল্ট ভ্যালু সেটই করেন নাই। সবগুলোই তখন required arguments হয়ে যাবে। কারণ আপনার সেট করে দেয়া ডিফল্ট ভ্যালু তো কখনোই ব্যবহারের সুযোগ আসবেনা, তাইনা।
এই নিয়মের ব্যতিক্রম হল, যদি আপনি ডিফল্ট ভ্যালু হিসেবে null সেট করেন, যেমন:- $param = null
। এক্ষেত্রে প্যারামিটারটাই তখন implicitly nullable টাইপ হয়ে যায়। এই কারণে কিছুটা শিথিলতা তৈরি হয় বিধায় তখন deprecation notice দেয়া হয়না। কিন্তু প্যারামিটারটা তখনও required arguments হিসেবে বিবেচিত হয়। তবে উত্তম হল explicit nullable type ব্যবহার করা। উদাহরণ দেখুন:-
<?php
function foo($a = [], $b) {} // Default not used; deprecated as of PHP 8.0.0
function foo($a, $b) {} // Functionally equivalent, no deprecation notice
function bar(A $a = null, $b) {} // Still allowed; $a is required but nullable
function bar(?A $a, $b) {} // Recommended
<?php
function foo($a = [], $b) {} // Default not used; deprecated as of PHP 8.0.0
function foo($a, $b) {} // Functionally equivalent, no deprecation notice
function bar(A $a = null, $b) {} // Still allowed; $a is required but nullable
function bar(?A $a, $b) {} // Recommended
আমরা তো ফাংশনে ডাটা বা ভ্যালুর রেফারেন্সও পাঠাতে পারি তাইনা? এমন প্যারামিটারেও ডিফল্ট ভ্যালু সেট করা যাবে। যেমন:- &$value = "value"
।
আনলিমিটেড আর্গুমেন্ট পাস করা
ফাংশনে ভ্যালু পাস করার আরো একটি পন্থা হল, Variable-length argument lists। এর সাহায্যে আপনি যেমনিভাবে ফাংশনে আনলিমিটেড প্যারামিটার সেট করতে পারেন তেমনি আনলিমিটেড আর্গুমেন্ট পাস করতে পারেন।
এইজন্য ফাংশন তৈরির সময় প্যারামিটার নামের পূর্বে ...
বা তিনটে ডট সাইন টোকেন ব্যবহার করতে হয়। একে spread operator ও বলে। এর ফলে বোঝানো হয় যে ফাংশনটি variable number of arguments গ্রহণ করার উপযোগী। এখন এই variable number এর যেহেতু কোন সীমা নাই তাই আপনি ফাংশনটিকে আনলিমিটেড আর্গুমেন্ট গ্রহণ করার উপযোগীও বলতে পারেন।
মনে রাখবেন, এই spread operator এর সুবিধা শুধুমাত্র arrays এবং পিএইচপির Traversable সিস্টেমকে ইম্প্লিমেন্ট করা objects গুলোই নিতে পারে। তাই ফাংশনে যে ভ্যালু পাস হবে তা আসলে array টাইপের ভ্যালু হবে নিঃসন্দেহে।
variable number of arguments সেট করার উদাহরণ দেখুন:-
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
echo sum(1, 2, 3, 4, 5); // 15
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
echo sum(1, 2, 3, 4, 5); // 15
আউটপুট:-
15
15
আবার এভাবেও বলা যায়, arrays এবং পিএইচপির Traversable সিস্টেমকে ইম্প্লিমেন্ট করা objects গুলো ফাংশনে ভ্যালু আকারে পাসও করা যেতে পারে। variable number of arguments পাস করার উদাহরণ দেখুন:-
<?php
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
<?php
function add($a, $b) {
return $a + $b;
}
echo add(...[1, 2])."\n";
$a = [1, 2];
echo add(...$a);
আউটপুট:-
3
3
3
3
আবার, আপনি কিন্তু variable number of arguments সেট করার আগে স্বাভাবিক positional arguments ও সেট করতে পারেন। কোন বাধা নেই। তবে এক্ষেত্রে ফাংশন কলে পাঠানো আর্গুমেন্টগুলোতে প্রথমে positional arguments এর সিরিয়াল বা পজিশন মেইনটেইন করা হবে। অতঃপর বাকি এক/একাধিক যত আর্গুমেন্ট থাকে সেগুলো variable number of arguments এর ভ্যালু হিসেবে বিবেচিত হবে।
এমনিভাবে আপনি variable number of arguments এর টাইপ ডিক্লেয়ার করতে পারেন। সেক্ষেত্রে ...
টোকেনের আগে আপনি টাইপটা ডিক্লেয়ার করতে হবে এবং ডিক্লেয়ার করা হলে যতগুলো আর্গুমেন্ট এই ধারায় পড়বে সবগুলোকে অবশ্যই টাইপ ম্যাচ করতে হবে। অন্যথায় এরর হবে। উদাহরণ দেখুন:-
<?php
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// This will fail, since null isn't a DateInterval object.
echo total_intervals('d', null);
<?php
function total_intervals($unit, DateInterval ...$intervals) {
$time = 0;
foreach ($intervals as $interval) {
$time += $interval->$unit;
}
return $time;
}
$a = new DateInterval('P1D');
$b = new DateInterval('P2D');
echo total_intervals('d', $a, $b).' days';
// This will fail, since null isn't a DateInterval object.
echo total_intervals('d', null);
আউটপুট:-
3 days
Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2
3 days
Catchable fatal error: Argument 2 passed to total_intervals() must be an instance of DateInterval, null given, called in - on line 14 and defined in - on line 2
এই উদাহরণটি পুরোপুরি বুঝতে হলে আপনাকে পিএইচপির DateInterval class টা বুঝতে হবে।
সবশেষে variable arguments এর এই সুবিধা passed by reference প্যারামিটারেও সেট করা যাবে। সেক্ষেত্রে ampersand (&) সাইনের আগে ...
এই টোকেনটি বসাতে হবে।
প্যারামিটার/আর্গুমেন্টের নামকরণ
পিএইচপিতে ফাংশনের প্যারামিটার সেট করার সময় তার নামকরণও করা যায়। এই অতিরিক্ত ফিচারটি পিএইচপি 8.0.0 ইন্ট্রোডিউস করেছে। এই ফিচারটি ইন্ট্রোডিউস হওয়ায় আর্গুমেন্ট পাস করার যে সীমাবদ্ধতা আমরা ইতিপূর্বে আলোচনা করেছি তা দূর হয়ে গেছে।
ফলস্বরূপ positional parameters এর যে কনসেপ্টটা আমরা দেখেছি, যার ফলে প্রতিটি পজিশন ধরে ধরে বা সিরিয়াল মেইনটেইন করে আর্গুমেন্ট পাস করার যে প্রয়োজনীয়তা দেখা দেয়, সেটার আর দরকার হয়না। এই ফিচারটি ইম্প্লিমেন্ট করে আপনি আর্গুমেন্টগুলোকে যেমনি self-documenting করে ফেলতে পারেন তেমনি যেখানে খুশি আর্গুমেন্ট পাস করার মত order-independent ও করে দিতে পারেন। তখন ইচ্ছামত আপনি যেকোন জায়গায় ডিফল্ট প্যারামিটার সেট করতে পারেন, তেমনি আর্গুমেন্টও পজিশন বা সিরিয়াল বজায় না রেখে পাস করতে পারেন।
বিস্তারিত জানতে পিএইচপির Named-Arguments নিয়ে আর্টিকেলটা পড়ুন।
Named argument syntax
উদাহরণ দেখি:-
<?php
myFunction(paramName: $value);
array_foobar(array: $value);
// NOT supported.
function_name($variableStoringParamName: $value);
<?php
myFunction(paramName: $value);
array_foobar(array: $value);
// NOT supported.
function_name($variableStoringParamName: $value);
ফাংশনে ভ্যালু রিটার্ন করা কি এবং কেন?
ফাংশন তৈরি হয় মূলত কোন একটি টাস্ক, অপারেশন, ক্যালকুলেশন অথবা প্রবলেম সলভিং ইত্যাদি বিষয় সম্পন্ন করতে। এখন যে কাজটিই ফাংশন সম্পাদন করুক না কেন তার রেজাল্ট বা আউটপুট তো আপনাকে পেতে হবে তাইনা। ফাংশন এক্সিকিউশনের পর সেই রেজাল্টটি পেতে হলে আপনার জন্য ফাংশনের ভেতর থেকে এক্সিকিউটেড ভ্যালু রিটার্ন করা জরুরি হয়ে পড়ে।
সেটি করতেই ফাংশনের ভেতর আমাদেরকে return
statement টি ব্যবহার করতে হয়। এর ব্যবহার optional বা ঐচ্ছিক। ফলে ফাংশন তৈরি করতে গেলে ভ্যালু রিটার্ন করতেই হবে এমন কোন বাধ্যবাধকতা নাই।
ফাংশন যেকোন টাইপের ভ্যালু রিটার্ন করতে পারে। এমনকি array বা object ও রিটার্ন করতে পারে। ফাংশনের যেই লাইনে বা অংশে return
statement ব্যবহার হয় ফাংশন এক্সিকিউশন সেখানেই থেমে যায়। অর্থাৎ এর পরেও যদি কোন কোড ফাংশনের ভেতর থাকে তবে তা এক্সিকিউট হয়না, বরং কোড রানার ঐ অংশে পৌঁছতেই পারেনা এবং সেটি unreachable code হয়ে যায়।
return
statement ব্যবহারের ফলে ফাংশন তার অপারেশন শেষ করে ভ্যালু রিটার্ন করে এবং কোড রানার কোডের পরবর্তী অংশগুলো এক্সিকিউট করার সুযোগ পায়।
আপনি যদি return
statement ব্যবহার না করেন তখন ফাংশন ভ্যালু হিসেবে null রিটার্ন করে। যদিও বাস্তবে এর কারণে কোন সমস্যা দেখা দেয়না।
উদাহরণ দেখি:-
<?php
function sum(int $x, int $y) {
$z = $x + $y;
return $z;
}
echo "5 + 10 = " . sum(5, 10) . "<br>";
echo "7 + 13 = " . sum(7, 13) . "<br>";
echo "2 + 4 = " . sum(2, 4);
<?php
function sum(int $x, int $y) {
$z = $x + $y;
return $z;
}
echo "5 + 10 = " . sum(5, 10) . "<br>";
echo "7 + 13 = " . sum(7, 13) . "<br>";
echo "2 + 4 = " . sum(2, 4);
আউটপুট:
5 + 10 = 15
7 + 13 = 20
2 + 4 = 6
5 + 10 = 15
7 + 13 = 20
2 + 4 = 6
ফাংশনে return
statement ব্যবহার করে একের অধিক ভ্যালু রিটার্ন করা যায়না। একাধিক ভ্যালুর একান্তই দরকার হলে ভ্যালুগুলো array এর ভেতর স্টোর করে তারপর রিটার্ন করা যেতে পারে।
<?php
function small_numbers()
{
return [0, 1, 2];
}
// Array destructuring will collect each member of the array individually
[$zero, $one, $two] = small_numbers();
// Prior to 7.1.0, the only equivalent alternative is using list() construct
list($zero, $one, $two) = small_numbers();
<?php
function small_numbers()
{
return [0, 1, 2];
}
// Array destructuring will collect each member of the array individually
[$zero, $one, $two] = small_numbers();
// Prior to 7.1.0, the only equivalent alternative is using list() construct
list($zero, $one, $two) = small_numbers();
array নিয়ে জানতে পরের ক্লাসটি দেখুন।
ফাংশনে return
statement ব্যবহার করে রেফারেন্স ভ্যালুও রিটার্ন করা যায়। সেজন্য ফাংশন তৈরির সময় নামের আগে &
(Ampersand Sign) বা reference operator ব্যবহার করতে হয়। এমনিভাবে ফাংশন কল করে তার ভ্যালু যখন কোন ভ্যারিয়েবলে এসাইন করা হয় তখনও ঐ একই reference operator নামের পূর্বে ব্যবহার করতে হয়।
<?php
function &returns_reference()
{
return $someref;
}
$newref =& returns_reference();
<?php
function &returns_reference()
{
return $someref;
}
$newref =& returns_reference();
references নিয়ে আরো বিস্তারিত জানতে References Explained পড়ে দেখুন।
ফাংশনের রিটার্ন টাইপ নির্ধারণ
প্যারামিটার/আর্গুমেন্ট টাইপ ডিক্লারেশনের কথা মনে আছে। ঠিক তেমনি ফাংশনের রিটার্ন করা ভ্যালুরও ডাটা টাইপ ডিক্লেয়ার করার সুযোগ আছে। এক্ষেত্রেুও স্ট্রিক্ট ডিক্লারেশন করার আগ পর্যন্ত শুধুমাত্র ডাটা টাইপ ডিক্লারেশন হিন্ট দেয়ার কাজ করে, যাকে আমরা type hinting বলেছি। এর বেশি কিছু নয়। তবে স্ট্রিক্ট ডিক্লারেশন করার পর ডাটা টাইপে মিসম্যাচ হলে যে Fatal Error হবে তাতো আমরা আগেও জেনেছি।
কিভাবে ডিক্লেয়ার করবেন রিটার্ন ভ্যালুর ডাটা টাইপ?
এজন্য প্যারামিটারের ()
Parenthesis এর পর একটি :
বা কোলন সাইন দিতে হবে, অতঃপর Opening Curly Braces এর পূর্বে ডাটা টাইপটা ডিক্লেয়ার করতে হবে। উদাহরণ:-
<?php
declare(strict_types=1); // strict requirement
function addNumbers(float $a, float $b) : float {
return $a + $b;
}
echo addNumbers(1.2, 5.2);
<?php
declare(strict_types=1); // strict requirement
function addNumbers(float $a, float $b) : float {
return $a + $b;
}
echo addNumbers(1.2, 5.2);
আউটপুট:
6.4
6.4
ফাংশনের আর্গুমেন্ট টাইপ এবং রিটার্ন করা ভ্যালুর টাইপ এক হতে হবে এমন কোন বাধ্যবাধকতা নাই। ভিন্ন টাইপেরও ডাটা হতে পারে। শুধু খেয়াল রাখতে হবে ডিক্লেয়ার করা ডাটার সাথে রিটার্ন করা ডাটার টাইপে যেন মিল থাকে, মিসম্যাচ না হয়। উদাহরণ দেখি:-
<?php
declare(strict_types=1); // strict requirement
function addNumbers(float $a, float $b) : int {
return (int)($a + $b);
}
echo addNumbers(1.2, 5.2);
<?php
declare(strict_types=1); // strict requirement
function addNumbers(float $a, float $b) : int {
return (int)($a + $b);
}
echo addNumbers(1.2, 5.2);
আউটপুট:
6
6
রিয়াল ওয়ার্ল্ড প্রোগ্রামিংয়ে ফাংশনের ব্যবহার
ফাংশনের পরিচিতিতে আমরা বলেছি রিয়াল ওয়ার্ল্ড প্রোগ্রামিংয়ে সাধারণত এর ব্যবহার আসলে কেমন হয়ে থাকে। এখানে আমরা বিষয়টিকে গুরুত্বসহকারে বোঝানোর জন্য আরো কিছু বিষয় উপস্থাপন করছি।
রিয়াল ওয়ার্ল্ডের প্রোগ্রামাররা ফাংশনকে একটি সিংগল টাস্ক বা জবের জন্য রেস্পন্সিবল মনে করে। তাদের বিবেচনা অনুযায়ী একটি ফাংশন শুধুমাত্র একটি সিংগল টাস্ক, জব বা প্রবলেম - যাই বলি না কেন - তা নিয়ে কাজ করবে। এর অতিরিক্ত কোন কিছু নিয়ে একটি ফাংশন রিসোর্স অপচয় করবেনা।
তাই সাধারণত তাদের ফাংশনের সাইজ খুব বেশি বড় হয়না। কারণ তারা একই ফাংশনে একাধিক লজিক নিয়ে কাজ করতে আগ্রহী নয়। তাদের মতে একটি ফাংশন শুধুমাত্র একটি লজিক এবং এ রিলেটেড বিষয়গুলো ইম্প্লিমেন্ট করবে।
ফাংশন তৈরিতে এই একই বিষয় মাথায় রেখে আপনারও উচিত একটা ফাংশনে সব লজিক ইম্প্লিমেন্ট না করে লজিকগুলোকে পার্ট বাই পার্ট বিভাজন করা। অতঃপর প্রতিটি পার্টের জন্য ছোট ছোট ফাংশন লিখে কালেক্টিভলি পুরো সমস্যার সমাধান করা। এতে যেমনিভাবে আপনার কোড অন্য ডেভলপারের পড়ে বুঝতে সুবিধা হবে, তেমনি আপনি নিজেও লম্বা বিরতির পর কখনো নিজের কোড দেখার প্রয়োজন হলে সহজে বুঝতে পারবেন কোন ফাংশনটি কি কাজ করছে।
এটা সাধারণত বেস্ট প্রাকটিস হিসেবে গণ্য হয়। তবে সবাই একই প্যাটার্ন বা চিন্তাধারা অনুসরণ করে বিষয়টা তা নয়। তাই এর ব্যতিক্রমও আছে। তবে উত্তম এটাই। উদাহরণ দেখি:-
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
function avg(...$numbers) {
$total = sum(...$numbers);
return $total / count($numbers);
}
echo avg(1, 2, 3, 4, 5); // 3
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
function avg(...$numbers) {
$total = sum(...$numbers);
return $total / count($numbers);
}
echo avg(1, 2, 3, 4, 5); // 3
ফাংশনের মধ্যে ফাংশন ব্যবহার করা
আপনি চাইলে ফাংশনের মধ্যে ফাংশন ব্যবহার করতে পারেন।
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
function avg(...$numbers) {
$total = sum(...$numbers);
return $total / count($numbers);
}
function avg_sum(...$numbers) {
$average = avg(...$numbers);
$total = sum(...$numbers);
return "Average: $average, Sum: $total";
}
echo avg_sum(1, 2, 3, 4, 5); // Average: 3, Sum: 15
<?php
function sum(...$numbers) {
$total = 0;
foreach ($numbers as $n) {
$total += $n;
}
return $total;
}
function avg(...$numbers) {
$total = sum(...$numbers);
return $total / count($numbers);
}
function avg_sum(...$numbers) {
$average = avg(...$numbers);
$total = sum(...$numbers);
return "Average: $average, Sum: $total";
}
echo avg_sum(1, 2, 3, 4, 5); // Average: 3, Sum: 15
রিকার্সিভ ফাংশন কি?
যে ফাংশন নিজেই নিজেকে কল করে তাকে রিকার্সিভ ফাংশন বলে।
এই ফাংশন ইম্প্লিমেন্ট করতে চাইলে ২টি বিষয় আপনাকে অবশ্যই খেয়াল করতে হবে।
প্রথমেই একটি বেইস কেইস (Base Case) ডিফাইন করা। যার মাধ্যমে ফাংশনটি কখন টার্মিনেট বা শেষ হবে তা নির্ধারণ করে দিতে হয়। এই বেইস কেইস নির্ধারণ না করলে ফাংশনটি infinite recursion এ পরিণত হয়। ফলে আপনার প্রোগ্রাম, সফটওয়্যার এমনকি কম্পিউটার হ্যাং হয়ে যেতে পারে। তখন অনেকটা ইনফিনিট লুপের মত হয়ে যায়। এইজন্য প্রোপার ভ্যালিডেশন মেইনটেইন করতে হবে যাতে ভুলবশতঃ ফাংশন ইনফিনিট চক্করে পড়ে না যায়।
রিকার্সিভ কেইস নির্ধারণ করা। যার উপর নির্ভর করে ফাংশনটি যাতে নিজেই নিজেকে কল করতে পারে। ফাংশনকে রিকার্সিভ করতে হলে এই স্টেপটা অবশ্যই প্রোভাইড করতে হবে।
Base Case ডিফাইন না করে একটি উদাহরণ দেখি। সতর্কতা! আপনার প্রোগ্রাম হ্যাং হতে পারে। তাই নিজ রিস্কে ব্যবহার করুন। যেমন:-
<?php
function number($n)
{
echo $n ."\n";
$n++;
number($n);
}
number(1);
<?php
function number($n)
{
echo $n ."\n";
$n++;
number($n);
}
number(1);
Base Case ডিফাইন করে প্রোপার ভ্যালিডেশন বজায় রেখে তৈরি উদাহরণ দেখি:-
<?php
function factorial($n) {
if($n <= 1) {
return 1;
}
else {
return $n * factorial($n - 1);
}
}
echo factorial(5); // 120
<?php
function factorial($n) {
if($n <= 1) {
return 1;
}
else {
return $n * factorial($n - 1);
}
}
echo factorial(5); // 120
উদাহরণটিতে দেখতে পাচ্ছেন আমরা এর মাধ্যমে একটি নাম্বারের factorial বের করার চেষ্টা করেছি। রিকার্সিভ ফাংশনের ব্যবহার সাধারণত এই সমস্ত ক্ষেত্রগুলোতেই হয়। রিকার্সিভ ফাংশনকে আপনি লুপ (কন্ট্রোল স্ট্রাকচারের) বিকল্পও বলতে পারেন।
প্রোগ্রামিংয়ে separate of concern কি?
এতক্ষণ যাবত যতগুলো উদাহরণ বা আলোচনা আপনি দেখতে পেয়েছেন সেগুলো আমরা একটি সিংগল পিএইচপি ইনডেক্স ফাইলে নথিবদ্ধ করেছি। আমরা সহজে উদাহরণ সেট করার সুবিধার্থে এমনটা করেছি এবং আমাদের এই পার্টিকুলার ফাইলটি খুব জটিল বা সফিসটিকেটেড কোন দায়িত্ব পালন করছেনা।
কিন্তু রিয়াল ওয়ার্ল্ড প্রোগ্রামিংয়ে আপনি তো শুধু এমন ছোটখাট বিষয় ডিল করবেননা। সেখানে প্রোগ্রাম needs হবে বড়, requirements থাকবে অনেক, logics হবে অনেক জটিল এবং sophisticated, বিধায় আপনার জন্য লাইনের পর লাইন কোড লেখা, ফাংশনের পর ফাংশন তৈরি করার প্রয়োজনীয়তা দেখা দেবে। পাশাপাশি শুধুমাত্র পিএইচপি ফাইল তো থাকবেনা। এইচটিএমএল, সিএসএস, জাভাস্ক্রিপ্ট সহ আরো কত ফাইল এবং লজিক নিয়ে আপনাকে মাথা ঘামাতে হবে।
তখন কি করবেন? উপরের উদাহরণের মত একটা ফাইলেই সব কিছু করার প্রয়াস চালাবেন? রিয়াল ওয়ার্ল্ড প্রোগ্রামাররা কি তাই করে?
চলুন জানার চেষ্টা করি।
আপনি যদি সকল needs, requirements আর logics এক ফাইলের ভেতরে কাভার করার চিন্তা করেন তাহলে তা ইফেক্টিভ থিংকিং নয়। কারণ রিয়াল ওয়ার্ল্ড প্রোগ্রামিংয়ে আপনাকে অনেক অনেক বিষয় বিবেচনা করে তারপর একটি প্রজেক্ট সেটআপ করতে হয়। প্রজেক্টটি স্মুথভাবে ইফেক্টিভ ওয়ে বা প্রোডাক্টিভ ওয়েতে রান করতে হলে আপনার প্রসেসটাও তো ইফেক্টিভ হতে হবে তাইনা?
রিয়াল ওয়ার্ল্ড প্রোগ্রামাররা সেই প্রসেসটাকে সাধারণত separate of concern নামে অভিহিত করে। যার মূলমন্ত্র হল, যেমনিভাবে একটি ফাংশন একটিমাত্র কাজের রেস্পন্সিবিলিটি গ্রহণ করবে, ইতিপূর্বে আমরা এ বিষয়ে আলোচনা করেছি, তেমনিভাবে একটি প্রজেক্টের ফাইল বা ফোল্ডারগুলো তাদের রেস্পন্সিবিলিটি বা টাস্কের ভিত্তিতে বিভিন্ন সেকশনে সেপারেটেড থাকবে এবং ইন্ডিপেন্ডেন্ডলি কাজ করবে। এক সেকশন বা লজিকের ফাইল অন্য সেকশন বা লজিকের ফাইলে রেফার হবে এবং সেই ভিত্তিতে ব্যবহৃত হবে, কিন্তু একটি নির্দিষ্ট সেকশনে থাকার যোগ্য কোড বা লজিক অন্য সেকশনে রাখা হবেনা। এভাবে কোডের রিপিটেশন যেমন বন্ধ হবে তেমনি অবস্থান বা এনভায়রনমেন্ট অনুযায়ী কোড মেইনটেইন করার ফ্লেক্সিবিলিটি তৈরি হবে। একইসাথে কোডবেসটা ইন্ডিপেন্ডেন্ডলি রি-ইউজেবল হওয়ার সুযোগও তৈরি হবে।
এই প্রসেসটা ফাংশন তৈরি বা ফাংশনাল প্রোগ্রামিংয়ের সময় অবশ্যই ব্যবহার করা উচিত। রিয়াল ওয়ার্ল্ড প্রোগ্রামারদের দ্বারা রেকমেন্ডেডও বটে।
ফলে আপনি ফাংশনগুলো সাধারণত যেটা আপনার প্রোগ্রামের লজিক্যাল পার্ট হয়ে থাকে তাকে আলাদা একটা সেকশনে সেপারেটেড রাখতে পারবেন। সময় এবং প্রয়োজন অনুযায়ী রি-ইউজ করতে পারবেন।
উদাহরণ দেখি:-
functions.php
<?php
// sum
function sum($a, $b) {
return $a + $b;
}
<?php
// sum
function sum($a, $b) {
return $a + $b;
}
index.php
<?php
require_once 'functions.php';
echo sum(1, 2); // 3
<?php
require_once 'functions.php';
echo sum(1, 2); // 3
উপরের উদাহরণে আমরা আমাদের ফাংশনাল লজিকগুলো functions.php
নামক একটা ফাইলে সেট করেছি। পরবর্তীতে index.php
ফাইলে সেটা ইমপোর্ট করে ব্যবহার করছি।
এই টপিকে যা আলোচনা হয়েছে তা মূলত ডিজাইন প্যাটার্ন নামক একটি প্রসেসের সূচনামাত্র। অনেকেই তাকে MVC Pattern বলে। সেসব নিয়ে আমরা ভবিষ্যতে আরো বিস্তারিত জানব।
Scope, Local and Global Variables
প্রোগ্রামিংয়ে Scope ভ্যারিয়েবল কেন্দ্রিক একটি context বা ক্ষেত্রের নাম যেখানে সাধারণত ভ্যারিয়েবলগুলো ডিফাইন করা হয় এবং যার মাধ্যমে তাদের অবস্থান নির্ধারিত হয়। আবার এভাবেও বলা যায়, প্রোগ্রামিংয়ে Scope এর ধারণা থাকায় আমরা বুঝতে পারি একটা ভ্যারিয়েবলকে আমরা কখন কোথায় এক্সেস করতে পারি। এর মাধ্যমে একটি ভ্যারিয়েবলের ভিজিবিলিটি কতটুকু তা আমরা পরিমাপ করতে পারি।
পিএইচপিতে ফাংশন ব্যবহার করার আগ পর্যন্ত একটিমাত্র Scope বা ক্ষেত্র থাকে। তাকে আমরা global scope বলে থাকি। বেশিরভাগ ক্ষেত্রে ভ্যারিয়েবলগুলো এই স্কোপেই ডিফাইন করা হয়। এই স্কোপে ডিফাইন করা ভ্যারিয়েবলগুলোর পরিধি include
এবং require
দিয়ে ইমপোর্ট করা ফাইলগুলো পর্যন্ত বিস্তৃত হয়। এ জাতীয় ভ্যারিয়েবলকে গ্লোবাল ভ্যারিয়েবল বলে। যেমন ধরুন:-
<?php
$a = 1;
include 'b.inc';
<?php
$a = 1;
include 'b.inc';
এই $a
ভ্যারিয়েবলটি আমরা এখন 'b.inc' ফাইলের ভেতর থেকে এক্সেস করতে পারব।
কিন্তু যখন আমরা ফাংশনের ব্যবহার শুরু করি তখন আরো একটি Scope এর ধারণা তৈরি হয়। তাকে আমরা local function scope বা মোর স্পেসিফিক্যালি বললে local scope বলে অভিহিত করি। এই স্কোপের মধ্যে ডিক্লেয়ার করা ভ্যারিয়েবলগুলো কখনোই ফাংশনের বাইরে থেকে এক্সেস বা ব্যবহার করা যায় না। এই ভ্যারিয়েবলগুলোকে লোকাল ভ্যারিয়েবল বলে। এদের ভিজিবিলিটি ফাংশনের মধ্যেই সীমাবদ্ধ। উদাহরণ দেখি:-
<?php
function myTest() {
$x = 5; // local scope
echo "<p>Variable x inside function is: $x</p>";
}
myTest();
// using x outside the function will generate an error
echo "<p>Variable x outside function is: $x</p>";
<?php
function myTest() {
$x = 5; // local scope
echo "<p>Variable x inside function is: $x</p>";
}
myTest();
// using x outside the function will generate an error
echo "<p>Variable x outside function is: $x</p>";
এখন প্রশ্ন হল, আমরা কি গ্লোবাল ভ্যারিয়েবলকে ফাংশনের ভেতর এক্সেস করতে পারি? উত্তর হল, না। যেমনিভাবে লোকাল ভ্যারিয়েবল তার নির্ধারিত স্কোপের বাইরে এক্সেসিবল নয়, তেমনি গ্লোবাল ভ্যারিয়েবলও তার সীমার বাইরে এক্সেসযোগ্য নয়। উদাহরণ দেখি:-
<?php
$x = 5; // global scope
function myTest() {
// using x inside this function will generate an error
// because echo statement refers to a local version of the $x variable
// and it has not been assigned a value within this scope
echo "<p>Variable x inside function is: $x</p>";
}
myTest();
echo "<p>Variable x outside function is: $x</p>";
<?php
$x = 5; // global scope
function myTest() {
// using x inside this function will generate an error
// because echo statement refers to a local version of the $x variable
// and it has not been assigned a value within this scope
echo "<p>Variable x inside function is: $x</p>";
}
myTest();
echo "<p>Variable x outside function is: $x</p>";
আউটপুট:
Variable x inside function is:
Variable x outside function is: 5
Variable x inside function is:
Variable x outside function is: 5
তাহলে কি আমরা ফাংশনের ভেতর কখনোই গ্লোবাল ভ্যারিয়েবল এক্সেস করতে পারবনা? পারব, তবে সেজন্য পিএইচপির একটি বিল্ট-ইন কিওয়ার্ড global
অথবা বিল্ট-ইন associative array টাইপ সুপারগ্লোবালের সাহায্য নিতে হবে। চলুন সামনে বিস্তারিত জানার চেষ্টা করি।
global
কিওয়ার্ডের ব্যবহার
ফাংশন বা লোকাল স্কোপের ভেতর গ্লোবাল ভ্যারিয়েবল এক্সেস করার প্রথম উপায় হল, global
কিওয়ার্ডের ব্যবহার। এজন্য ফাংশনের ভেতর গ্লোবাল যে ভ্যারিয়েবলগুলো এক্সেস বা ব্যবহার করার প্রয়োজন দেখা দেয় তার পূর্বে কিওয়ার্ডটি ব্যবহার করতে হবে। ফলে তখন ভ্যারিয়েবলগুলো গ্লোবাল স্কোপে রেফার হবে এবং সেখানে থাকা ভ্যালুগুলো এক্সেস করে ব্যবহার করবে। যেমন:-
<?php
$x = 5;
$y = 10;
function myTest() {
global $x, $y;
$y = $x + $y;
}
myTest();
echo $y; // outputs 15
<?php
$x = 5;
$y = 10;
function myTest() {
global $x, $y;
$y = $x + $y;
}
myTest();
echo $y; // outputs 15
তবে যদি একই নামের ভ্যারিয়েবল লোকাল স্কোপেও থাকে তখন লোকাল ভ্যারিয়েবল প্রাধান্য পাবে। যেমন:-
<?php
$x = 5;
$y = 10;
function myTest()
{
//$x = 15; // don't use it here
global $x, $y; // as $x is overriden here
$x = 15;
$y = $x + $y;
}
myTest();
echo $y; // outputs 25
<?php
$x = 5;
$y = 10;
function myTest()
{
//$x = 15; // don't use it here
global $x, $y; // as $x is overriden here
$x = 15;
$y = $x + $y;
}
myTest();
echo $y; // outputs 25
তবে এভাবে গ্লোবাল ভ্যারিয়েবলকে এক্সেস করার ব্যবহার পরিহার করা উচিত। কারণ এতে এক্সিডেন্টালি কোডবেসের কোথাও ভ্যারিয়েবলের ভ্যালু পরিবর্তন করা হয়ে যেতে পারে। পরবর্তীতে যা ডিবাগ করে বের করা অসাধ্য সাধনের মত। তাই প্রাথমিকভাবেই সেফ থাকা উচিত।
একটি ফাংশনে একাধিক ভ্যারিয়েবলকে গ্লোবাল ভ্যারিয়েবল বানানো যেতে পারে। কোন সীমা নাই।
$GLOBALS
array এর ব্যবহার
ফাংশনের ভেতর গ্লোবাল ভ্যারিয়েবল এক্সেস করার আরো একটি পদ্ধতি হল $GLOBALS
associative array এর ব্যবহার। একে superglobal ও বলে। এটি পিএইচপির একটি বিল্ট-ইন associative array।
তাই গ্লোবাল ভ্যারিয়েবলগুলো এক্সেস করতে associative array এক্সেস করার যে পদ্ধতি সেটা ব্যবহার করতে হবে। যেখানে গ্লোবাল ভ্যারিয়েবলের নামটা হচ্ছে array key এবং প্রতিটি array element এর ভ্যালু হচ্ছে ভ্যারিয়েবলগুলোর একচুয়াল ভ্যালু। উদাহরণ দেখি:-
<?php
$a = 1;
$b = 2;
function Sum()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b; // outputs 3
<?php
$a = 1;
$b = 2;
function Sum()
{
$GLOBALS['b'] = $GLOBALS['a'] + $GLOBALS['b'];
}
Sum();
echo $b; // outputs 3
আবারও, যেহেতু বেটার কোড স্ট্রাকচার মেইনটেইন করার গুরুত্ব অপরিসীম। তাই এজাতীয় সুবিধা পারতপক্ষে ব্যবহার না করাই ভালো।
The static
Keyword
উপরোক্ত ২টি স্কোপ ছাড়াও ভ্যারিয়েবলের আরো একটি গুরুত্বপূর্ণ স্কোপ রয়েছে। তাকে বলে static scope। যার ভ্যারিয়েবলের নাম static variables। এটি শুধুমাত্র ফাংশন বা লোকাল স্কোপে এভেলেবল। এটি যতটা না স্কোপ তারচেয়ে বেশি স্কোপ রিলেটেড একটি গুরুত্বপূর্ণ ফিচার।
কেন দরকার এই ফিচার?
সাধারণত যখন আমরা ফাংশনের লোকাল স্কোপে কোন ভ্যারিয়েবল ডিক্লেয়ার করে তাকে নিয়ে কাজ করি তখন তার ভ্যালিডিটি বা শেল্ফ লাইফ ঐ ফাংশন এক্সিকিউশন হওয়া পর্যন্ত বহাল থাকে। যেই মুহুর্তে ফাংশন এক্সিকিউট হয়ে যায় তার পর থেকে ঐ ভ্যারিয়েবলগুলো আর এক্সেস করা যায়না এবং তার ভ্যালুসহ সে প্রোগ্রাম থেকে অটোমেটিক ডিলেট হয়ে যায়। উদাহরণে বোঝার চেষ্টা করুন:-
<?php
function test()
{
$a = 0;
echo $a;
$a++;
}
<?php
function test()
{
$a = 0;
echo $a;
$a++;
}
উপরের ফাংশনে যে ক্যালকুলেশন আমরা করেছি তার আসলে কোন ইফেক্টিভ প্রয়োগ নাই। কারণ যতবারই আমরা ফাংশনটি কল করব $a
ভ্যারিয়েবলের ভ্যালু 0 প্রিন্ট হবে। post increment ($a++
) করে ভ্যারিয়েবলের ভ্যালু ১ করে বাড়ালেও বাস্তবে তো কোন পরিবর্তন আমরা পাচ্ছিনা। ফাংশন এক্সিকিউশন হওয়ার সাথে $a
ভ্যারিয়েবলও ভ্যানিশ হয়ে যাওয়ায় বাড়ানো ভ্যালুটার নাগাল কোনভাবে আর পাচ্ছিনা। অথচ সামনের কোন কাজে ঐ বৃদ্ধি করা ভ্যালুটা দরকার। তাহলে কি করার?
আমাদের এমন একটা উপায় দরকার যার সাহায্যে ঐ বৃদ্ধি করা ভ্যালুটার নাগাল আমরা পেতে পারি এবং ফাংশনটিকেও ইফেক্টিভ করতে পারি। সেই সুবিধা আমাদেরকে প্রদান করতেই static variables এর কনসেপ্টটা এসেছে।
এজন্য আপনাকে নির্ধারিত ভ্যারিয়েবলের আগে static
এই কিওয়ার্ডটি ব্যবহার করতে হবে। একই ধরণের ২টি উদাহরণের মাধ্যমে আমরা বিষয়টি বোঝার চেষ্টা করি। যেমন:-
<?php
function myTest() {
static $x = 0;
echo $x;
$x++;
}
myTest();
myTest();
myTest();
<?php
function myTest() {
static $x = 0;
echo $x;
$x++;
}
myTest();
myTest();
myTest();
আউটপুট:
0
1
2
0
1
2
এভাবে যতবার myTest()
ফাংশনটি কল হবে ভ্যালু ১ করে বাড়তেই থাকবে। আমাদের উপরে দেয়া উদাহরণটাতে এই সুবিধা ব্যবহার করার চেষ্টা করি। যেমন:-
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
<?php
function test()
{
static $a = 0;
echo $a;
$a++;
}
এবার, $a
ভ্যারিয়েবলের ভ্যালু প্রথমবার test()
ফাংশনটি কল করার পর ইনিশিয়ালাইজ হয়ে সেট হয়ে যাবে। আর ডিলেট হবেনা। এরপর থেকে যতবার test()
ফাংশনটি কল করব ভ্যালু ১ করে বাড়তেই থাকবে।
এভাবেই আমরা লোকাল স্কোপে থাকা একটি ভ্যারিয়েবলে গ্লোবাল স্কোপের সুবিধা ব্যবহার করতে পারি।
রিকার্সিভ ফাংশনেও সাধারণত static variables এর ব্যবহার দেখা যায়। যেমন:-
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
<?php
function test()
{
static $count = 0;
$count++;
echo $count;
if ($count < 10) {
test();
}
$count--;
}
স্টাটিক ভ্যারিয়েবলে constant expressions ইভালুয়েট করে পাওয়া ভ্যালু এসাইন করা যায়, তবে dynamic expressions যেমন function calls ইত্যাদির ভ্যালু এসাইন করলে parse error আসবে। যেমন:-
<?php
function foo(){
static $int = 0; // correct
static $int = 1+2; // correct
static $int = sqrt(121); // wrong (as it is a function)
$int++;
echo $int;
}
<?php
function foo(){
static $int = 0; // correct
static $int = 1+2; // correct
static $int = sqrt(121); // wrong (as it is a function)
$int++;
echo $int;
}
আলহামদুলিল্লাহ! এর মাধ্যমে ফাংশন এবং স্কোপ সংক্রান্ত মোটামুটি একটা ধারণা দিতে আমরা চেষ্টা করেছি। চতুর্থ ক্লাস এখানেই শেষ।
আমরা পঞ্চম ক্লাস থেকে array নিয়ে বিস্তারিত জানার চেষ্টা করব ইনশাআল্লাহ।