پیش از این گفتیم که هر کلاس از دو ویژگی تحت عناوین Attribute و Behavior برخوردار است که به ترتیب نشانگر خصیصههای کلاس مذکور و کارهایی که آن کلاس از عهدهٔ آنها برمیآید هستند. در عین حال، در متودولوژی شیئگرایی این دو اصطلاح تحت عناوین دیگری نیز به کار میروند که عبارتند از Property و Method که در طول این دوره از این معادلها استفاده خواهیم کرد. به منظور درک بهتر این موضوع، ابتدا داخل پوشهٔ oop
که در آموزش گذشته ساختیم پوشهٔ جدیدی با نامی دلخواه همچون class-and-object
ساخته سپس فایلی به نام index.php
حاوی کدهای زیر داخل آن میسازیم:
<?php
class User
{
public $name = "Behzad";
public $lastName = "Moradi";
public $dob = 1362;
public function showFullName()
{
return $this->name . ' ' . $this->lastName;
}
}
در خط اول، تگ آغازین php?>
را نوشته سپس از کلیدواژه یا کیورد class
استفاده کرده که وظیفهٔ ساخت کلاس را بر عهده دارد و نامی دلخواه همچون User
برای کلاس خود انتخاب نمودهایم.
آنچه میخواهیم متعلق به کلاس مذکور باشد را میباید داخل علائم {}
بنویسیم به طوری که علامت }
به منزلهٔ نقطهٔ شروع این کلاس است و علامت {
نیز پایان این کلاس را مشخص میسازد. اساساً هر متغیری که داخل بدنهٔ کلاس یا به عبارتی مابین علائم }
و {
تعریف شده تحت عنوان Property شناخته میشود و همانطور که پیش از این متذکر شدیم، پراپرتیها به عنوان خصائص یا ویژگیهای یک کلاس تلقی میشوند. به طور مثال، در کلاس فوق سه پراپرتی تعریف کردهایم تحت عناوین name$
و lastName$
و همچنین dob$
که برای هر کدام از آنها نیز یک مقدار اولیه در نظر گرفتهایم.
همانطور که ملاحظه میشود، پیش از نام این پراپرتیها (متغیرها) از کیورد public
استفاده کردهایم که اصطلاحاً یک Access Modifier است. به عبارتی، این کیورد مشخصکنندهٔ سطح دسترسی این پراپرتیها است و همانطور که از نامش مشخص است، از هر کجای سورسکد میتوان به این پراپرتیها دسترسی پیدا کرد زیرا اساساً به شکلِ عمومی تعریف شدهاند (در آموزشهای آتی به طور مفصل پیرامون انواع سطوح دسترسی صحبت خواهیم نمود.)
در ادامه، یک فانکشن/متد/تابع تحت عنوان ()showFullName
ساختهایم که این وظیفه را دارا است تا مقدار پراپرتیهای name$
و lastName$
را بازگرداند. در زبان برنامهنویسی پیاچپی، به منظور تعریف یک متد از کیورد function
استفاده میکنیم و از آنجا که میخواهیم سطح دسترسی این متد عمومی باشد، پیش از هر چیز از کیورد public
استفاده نمودهایم.
در نامگذاری این متد، از روش اصطلاحاً camelCase استفاده نمودهایم بدین شکل که حرف اول کلمهٔ اول کوچک سپس حرف اول سایر کلمات به صورت بزرگ نوشته میشود. به عبارتی، میتوان گفت که نام متد به صورت شماتیک همچون کوهان شتر خواهد بود (توجه داشته باشیم که در نامگذاری متدها نمیتوان نام متد را با یک عدد آغاز کرد و یا از اِسپیس یا دَش برای جداسازی کلمات استفاده نمود.)
حال برسیم به محتوای داخل متد ()showFullName
که با کیورد return
آغاز میشود. در واقع، هر جایی از کد که از این کلیدواژه استفاده شود، دستوری که پس از آن قرار میگیرد بازگردانده شده و سایر دستوراتی که در خطوط بعدی قرار گرفتهاند دیگر اجرا نخواهند شد. با این توضیحات، داخل این متد دستور دادهایم که در صورت فراخوانی این متد، یک اِسپیس مابین پراپرتیهای name$
و lastName$
قرار گرفته و مجموعه کل این استرینگ ریترن گردد.
چیزی که در اینجا نیاز به توضیح بیشتر دارد، کیورد this$
است. در حقیقت، این کیورد به خودِ کلاس User
اشاره دارد که با در نظر داشتن این نکته میتوان گفت که دستور this->name$
را بدین شکل میتوان تفسیر نمود که گفتهایم «این کلاس» را مبنا قرار داده سپس به دنبال یک پراپرتی تحت عنوان name
گشته و مقدار آن را به دست آوردهایم. نکتهٔ دیگری که در ارتباط با this$
میباید به خاطر داشته باشیم آن است که به منظور هدف قرار دادن یک پراپرتی، پس از کیورد this$
از علائم <-
استفاده نموده، سپس علامت $
پراپرتی را حذف نموده و صرفاً نام آن را درج میکنیم.
تا این مرحله از کار، توانستهایم با موفقیت یک کلاس بسیار ساده که حاوی یک سری پراپرتی و یک متد است بسازیم. حال در ادامه قصد داریم ببینیم که به چه شکل میتوان این کلاس را مورد استفاده قرار داد که برای این منظور، کدهای فوق را به صورت زیر تکمیل میکنیم:
<?php
class User
{
public $name = "Behzad";
public $lastName = "Moradi";
public $dob = 1362;
public function showFullName()
{
return $this->name . ' ' . $this->lastName;
}
}
$objectMadeFromUserClass = new User();
echo $objectMadeFromUserClass->showFullName();
از روی کلاس User
، آبجکتی با نامی دلخواه همچون objectMadeFromUserClass$
که نامی بامسمی است ساخته سپس در خط پانزدهم متد ()showFullName
را روی این آبجکت فراخوانی کرده و با دستور echo
خروجی آن را چاپ کردهایم به طوری که خواهیم داشت:
Behzad Moradi
توجه داشته باشیم که به منظور ساخت یک آبجکت جدید از روی کلاسی خاص، میباید از کیورد new
استفاده نماییم. همچنین آوردن علائم ()
پس از نام کلاس در چنین شرایطی اختیاری است؛ به عبارتی، هم User
و هم ()User
هر دو صحیح هستند اما این در حالی است که اگر کانستراکتور این کلاس پارامتر ورودی بگیرید، درج علائم ()
ضروری است (در ادامهٔ آموزشها به طور مفصل با مفهوم کانستراکتور آشنا خواهیم شد.)
با توجه به این که سازوکار دستور return
برای دولوپرهایی که تازهکار هستند ممکن است کمی گیجکننده باشد، در ادامه سعی میکنیم این موضوع را بیشتر توضیح دهیم. برای این منظور، کد فوق را به صورت زیر ریفکتور میکنیم:
<?php
class User
{
public $name = "Behzad";
public $lastName = "Moradi";
public $dob = 1362;
public function showFullName()
{
echo $this->name . ' ' . $this->lastName;
}
}
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
همانطور که ملاحظه میشود، داخل متد ()showFullName
دستور return
را حذف کرده و از دستور echo
به جای آن استفاده کردهایم که این تغییر باعث میگردد به محض فراخوانی این متد، مقادیر پارامترهایی که پس از دستور echo
آمدهاند چاپ گردد. با توجه به این که خود این متد دیگر به جای ریترن کردن استرینگ مد نظر آن را چاپ میکند، لذا در خط پانزدهم دیگر لزوی به درج دستور echo
نخواهیم داشت و در صورت اجرای این اسکریپت، خروجی در کنسول یا مرورگر چاپ خواهد شد. با مد نظر قرار دادن این توضیحات، میتوان به این نتیجه رسید که دستور return
آنچه در ادامهاش قرار میگیرد را به عنوان خروجی متد ارسال میکند و تصمیم با ما است که در حین استفاده از آن متد و با استفاده از دستور echo
آن خروجی را چاپ کنیم یا نکنیم.
درآمدی بر تفاوتهای مابین فانکشن و متد
پیش از این گفتیم که ()showFullName
را هم میتوان فانکشن قلمداد کرد و هم متد اما اگر بخواهیم کمی دقیقتر به موضوع نگاه کنیم، اساساً Function و Method دو مقوله متفاوت از یکدیگر هستند که در ادامه تفاوتهای آنها را بازگو خواهیم کرد. برای روشنتر شدن موضوع، تکه کد زیر را مد نظر قرار میدهیم:
function multiply($numOne, $numTwo)
{
echo $numOne * $numTwo;
}
multiply(2, 5);
تابعی تعریف کردهایم تحت عنوان ()multiply
که دو پارامتر ورودی گرفته، آنها را در یکدیگر ضرب کرده و خروجی را چاپ میکند و در ادامه هم میبینیم که چگونه آن را مورد استفاده قرار دادهایم. در یک اسکریپت معمولی که به روش Procedural نوشته شده باشد، این بلوک از کد فانکشن نامیده میشود. حال چنانچه این بلوک از کد داخل یک کلاس قرار گیرد، تحت عنوان متد شناخته خواهد شد به طوری که داریم:
class Calculator
{
function multiply($numOne, $numTwo)
{
echo $numOne * $numTwo;
}
}
$cal = new Calculator();
$cal->multiply(2, 5);
در تفسیر اسکریپت فوق باید بگوییم که کلاسی ساختهایم تحت عنوان Calculator
که صرفاً حاوی یک فانکشن (یا بهتر بگوییم یک متد) است که در ادامه آبجکتی از روی این کلاس ساخته و متد مذکور را فراخوانی کردهایم.
در برنامهنویسی شیئگرا، معمولاً کلاسها در فایلهای جداگانه ساخته شده سپس هر جایی که نیاز داشته باشیم، آنها را ایمپورت کرده و مورد استفاده قرار میدهیم که در همین راستا، در ادامه قصد داریم ببینیم که به چه شکل میتوان کلاس User
را به شکلی مستقل ساخت.
برای این منظور، داخل پوشهٔ class-and-object
پوشهای میسازیم تحت عنوان classes
و داخل آن فایلی به نام User.php
حاوی کدهای زیر میسازیم:
<?php
class User
{
public $name = "Behzad";
public $lastName = "Moradi";
public $dob = 1362;
public function showFullName()
{
echo $this->name . ' ' . $this->lastName;
}
}
از این پس، ساختار این پروژه به صورت زیر خواهد بود:
class-and-object
├── classes
│ └── User.php
└── index.php
توجه داشته باشیم که بهتر است نام فایل با نام کلاس یکسان باشد چرا که اگر بخواهیم در آینده از مفهومی تحت عنوان Autoloading استفاده نماییم، به مشکلی برنخواهیم خورد. حال در ادامه قصد داریم تا داخل فایل index.php
از این کلاس استفاده نماییم که برای همین منظور، این فایل را به صورت زیر تکمیل میکنیم:
<?php
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
چنانچه این فایل را داخل مرورگر باز کنیم، قاعداً هیچ چیزی در خروجی مشاهده نخواهیم کرد که برای رفع این مشکل، این فایل را به صورت زیر تکمیلتر مینماییم:
<?php
ini_set('display_errors', '1');
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
همانطور که میبینیم، ابتدا از فانکشن ()ini_set
استفاده کرده و از طریق آن، مقدار display_errors
را برابر با عدد 1 قرار دادهایم. در واقع، کاری که این دستور انجام میدهد این است که تنظیمات فایل php.ini
را اُورراید نموده و این امکان را در اختیار توسعهدهنده قرار میدهد تا ارورها و اِکسپشنهای احتمالی را مشاهده کند. حال اگر این اسکریپت را مجدد داخل مرورگر ران کنیم، به عنوان خروجی خواهیم داشت:
Fatal error: Uncaught Error: Class 'User' not found in /var/www/oop/class-and-object/index.php:4
میبینیم که مفسر پیاچپی این پیام را در معرض دیدمان قرار میدهد که کلاسی تحت عنوان User
که در خط چهارم مورد استفاده قرار گرفته را نیافته است که برای رفع این مشکل، مجدد فایل فوق را به صورت زیر تکمیلتر مینماییم:
<?php
ini_set('display_errors', '1');
require_once "classes/User.php";
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
در حقیقت، با استفاده از دستور require_once
به مسیر قرارگیری کلاس User
رفته و آن را داخل این فایل ایمپورت کردهایم و از این پس چنانچه این اسکریپت را اجرا نماییم، خروجی به درستی در معرض دیدمان قرار میگیرد.
اکنون فرض کنیم که چندین و چند کلاس مختلف داریم که قصد استفاده از تمامی آنها را داخل فایل index.php
داریم که در این صورت، این فایل به شکل زیر خواهد بود:
<?php
ini_set('display_errors', '1');
require_once "classes/User.php";
require_once "classes/User2.php";
require_once "classes/User3.php";
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
$objectMadeFromUser2Class = new User2();
$objectMadeFromUser2Class->showFullName();
$objectMadeFromUser3Class = new User3();
$objectMadeFromUser3Class->showFullName();
همانطور که ملاحظه میشود، بارها و بارها استفاده از دستور require_once
کاری فرسایشی و البته باگزا خواهد بود! به منظور سادهسازی بیشتر، میتوان یک فایل اصلی با نامی بامسمی همچون bootstrap.php
و یا init.php
ساخته، کلیهٔ دستورات require_once
را داخل نوشته و صرفاً آن فایل را داخل فایلهای مختلفی که داریم ایمپورت نماییم. برای درک بهتر این موضوع، ابتدا کلاسهای User2
و User3
را ساخته سپس فایلی تحت عنوان init.php
ساخته و آن را تکمیل میکنیم. برای شروع، کلاس User2 به صورت زیر خواهد بود:
<?php
class User2
{
public function showFullName()
{
echo "This is User2";
}
}
کلاس User3 نیز به صورت زیر است:
<?php
class User3
{
public function showFullName()
{
echo "This is User3";
}
}
حال فایلی تحت عنوان init.php
ساخته و آن را به صورت زیر تکمیل میکنیم:
<?php
require_once "classes/User.php";
require_once "classes/User2.php";
require_once "classes/User3.php";
در حقیقت، هر سه کلاس موجود در این پروژه را داخل این فایل ایمپورت کردهایم سپس داخل فایل index.php
خودِ فایل init.php
را به صورت زیر ایمپورت مینماییم:
<?php
ini_set('display_errors', '1');
require_once "init.php";
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
$objectMadeFromUser2Class = new User2();
$objectMadeFromUser2Class->showFullName();
$objectMadeFromUser3Class = new User3();
$objectMadeFromUser3Class->showFullName();
این اسکریپت به درستی کار خواهد کرد اما در عین حال اصولی نیست! در واقع، هر موقع که کلاس جدیدی به پروژه اضافه میشود، به صورت دستی میباید فایل init.php
را تکمیل کنیم که همین مسئله ممکن است منجر به بروز خطاهای سهوی گردد و اینجا است که میباید با مفهومی تحت عنوان اُوتولودینگ در زبان برنامهنویسی پیاچپی آشنا شویم که در ادامه این موضوع را به طور کامل تشریح خواهیم نمود.
آشنایی با مفهوم Autoloading
این اصطلاح حاکی از آن است که وقتی یک آبجکت جدید از روی کلاسی ساخته میشود، خود مُفسر پیاچپی به صورت خودکار آن کلاس را داخل فایل مذکور ایمپورت خواهد کرد که این کار از طریق فانکشنی تحت عنوان ()spl_autoload_register
عملی میگردد. به منظور درک بهتر این موضوع، فایل init.php
را به صورت زیر ریفکتور میکنیم:
<?php
spl_autoload_register(function ($class) {
require_once "classes/{$class}.php";
});
فانکشن ()spl_autoload_register
یک پارامتر ورودی از جنس کلوژر میگیرد و میبینیم که برای این کلوژر یک پارامتر ورودی تحت عنوان class$
در نظر گرفتهایم و در ادامه نیز از دستور require_once
استفاده نموده سپس مسیر کلاسهای این پروژه را در نظر گرفتهایم (توجه داشته باشیم برای آن که مقدار متغیر class$
به درستی چاپ شود، میباید آن را داخل علائم {}
قرار داد.)
حال اگر اسکریپت فوق را اجرا نماییم، میبینیم که به صورت خودکار هر سه کلاس استفاده شده داخل فایل index.php
ایمپورت شدهاند. در واقع، قابلیت اُوتولودینگ این اطمینان را به ما میدهد که میتوانیم به صورت خودکار کلاسهای مورد استفاده را در فایلهای مد نظرمان ایمپورت نماییم. در عین حال، میتوان یک گام فراتر رفته و این پروسه را به شکلی اصولیتر در پروژههای مبتنی بر معماری OOP پیادهسازی نماییم که در ادامه این موضوع را به تفصیل تشریح خواهیم نمود.
آشنایی با سازمان PHP-FIG
PHP-FIG که بر گرفته از کلمات PHP Framework Interop Group میباشد، سازمانی عامالمنفعه است که هدف اصلیاش استانداردسازی پروژههای نوشتهشده با زبان پیاچپی از طریق ایجاد یک سری اصول و قوانین است. این گروه توسط جمعی در حدود ۵ نفر از توسعهدهندگان فریمورکهای پیاچپی در سال 2009 شکل گرفت و به مرور زمان افراد بیشتری به هستهٔ اولیهٔ گروه محلق گردید و این در حالی است که فضا برای مشارکت کلیهٔ علاقهمندان باز است.
PHP Standard Recommendation یا به اختصار PSR حاوی دهها استاندارد برای توسعهٔ نرمافزار با زبان پیاچپی است که استاندارد چهارم تحت عنوان PSR-4: Autoloader مرتبط با مقولهٔ Autoloading است که در ادامه قصد داریم آن را مورد بررسی قرار دهیم.
آشنایی با PSR-4: Autoloader
این استاندارد هر آنچه در مورد مقولهٔ اُوتولودینگ در پروژههای پیاچپی نیاز است را شامل میگردد که از آن جمله میتوان به نحوهٔ نامگذاری فایلها و ... اشاره کرد. در ادامه، ابتدا دو مثال از این استاندارد خواهیم زد سپس به توضیح شیوهٔ نامگذاری آنها میپردازیم:
FULLY QUALIFIED CLASS NAME | NAMESPACE PREFIX | BASE DIRECTORY | RESULTING FILE PATH |
Acme\Log\Writer\File_Writer\ | Acme\Log\Writer | /acme-log-writer/lib/. | acme-log-writer/lib/File_Writer.php/. |
Symfony\Core\Request\ | Symfony\Core | vendor/Symfony/Core/. | vendor/Symfony/Core/Request.php/. |
به طور کلی، قانونی که به منظور شیوهٔ نامگذاری کلاسها مورد استفاده قرار میگیرد به صورت زیر است:
\<NamespaceName>(\<SubNamespaceNames>)*\<ClassName>
Vendor Namespace که در بالا در قالب NamespaceName
نمایش داده شده است میباید به نوعی دربرگیرندهٔ سازنده، نام پروژه یا دولوپر کلاس مذکور باشد به طوری که مثلاً در Symfony\Core\Request\
نام Symfony
به سازندهٔ کلاس مذکور اشاره دارد. در ارتباط با SubNamespaceNames
باید گفت که به هر تعداد که بخواهیم میتوانیم زیرشاخه داشته باشیم که در مثال فوقالذکر Core
به عنوان زیرشاخهٔ Symfony
محسوب میگردد و در نهایت به ClassName
میرسیم که در این مثال Request
است.
بر اساس این استاندارد، کلیهٔ کلاسها میباید به پسوند php.
ختم شوند مضاف بر این که به صورت PascalCase نوشته شوند؛ به عبارتی، حرف اول نام کلاس به صورت بزرگ نوشته شده سپس چنانچه نام کلاس از چند کلمه تشکیل شده بود، حرف اول سایر کلمات نیز به شکل بزرگ نوشته شوند (به طور مثال، میتوان نام فرضی FileHandler را مد نظر قرار داد که در آن حرف اول کلمات File و Handler به صورت بزرگ نوشته شدهاند.)
حال بر اساس این اصول، در ادامه خواهیم دید که به چه شکل میتوان بر پایهٔ اصل PSR-4 اقدام به ایمپورت کردن فایلهای پروژه نمود اما قبل از آن نیاز است تا با مفهومی تحت عنوان کامپوزر آشنا شویم.
Composer چیست؟
کامپوزر (Composer) چیست؟ مقالهای است که در آن به طور مفصل پیرامون تاریخچه، ماهیت و همچنین کاربرد پَکِیج مَنِجِر کامپوزر توضیح داده شده است اما چنانچه بخواهیم به طور خلاصه به این پرسش پاسخ دهیم که «کامپوزر چیست؟» باید بگوییم کامپوزر ابزاری است که پروسهٔ Dependency Management (مدیریت وابستگی) پروژه را برای توسعهدهندگان زبان برنامهنویسی برنامهنویسی پیاچپی تسهیل میسازد. به عبارت دیگر، کامپوزر چک میکند ببیند که یک پروژه چه وابستگیهایی به سایر پروژهها دارا است، سپس آنها را بسته به نسخهٔ مد نظر به سادگی روی سیستم نصب میسازد. با توجه به این که قصد داریم تا از استاندارد PSR-4: Autoloader برای فرآیند اُوتولودینگ استفاده نماییم، نیاز به نصب کامپوزر خواهیم داشت که در ادامه نحوهٔ انجام این کار را توضیح خواهیم داد.
راهنمای نصب کامپوزر روی گنو/لینوکس
به منظور نصب کامپوزر روی سیستمعامل اوبونتو، ابتدا به ساکن باید اطمینان حاصل کنیم که ابزار curl
روی سیستم نصب است؛ سپس با استفاده از کامند زیر کَش ابزار مدیریت پکیج لینوکس را بهروزرسانی میکنیم:
$ sudo apt-get update
حال با استفاده از کامند زیر میتوانیم کامپوزر را به صورت اصطلاحاً Globally یا «سراسری» روی سیستم نصب نماییم:
$ curl -sS https://getcomposer.org/installer | sudo php -- --install-dir=/usr/local/bin --filename=composer
پس از موفقیتآمیز بودن مراحل فوق، با استفاده از کامند زیر میتوانیم از صحتِ نصب کامپوزر اطمینان حاصل نماییم:
$ composer
______
/ ____/___ ____ ___ ____ ____ ________ _____
/ / / __ \/ __ `__ \/ __ \/ __ \/ ___/ _ \/ ___/
/ /___/ /_/ / / / / / / /_/ / /_/ (__ ) __/ /
\____/\____/_/ /_/ /_/ .___/\____/____/\___/_/
/_/
Composer version 1.8.5 2019-04-09 17:46:47
Usage:
command [options] [arguments]
Options:
-h, --help Display this help message
-q, --quiet Do not output any message
-V, --version Display this application version
--ansi Force ANSI output
--no-ansi Disable ANSI output
-n, --no-interaction Do not ask any interactive question
--profile Display timing and memory usage information
--no-plugins Whether to disable plugins.
-d, --working-dir=WORKING-DIR If specified, use the given directory as working directory.
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
Available commands:
about Shows the short information about Composer.
archive Creates an archive of this composer package.
browse Opens the package's repository URL or homepage in your browser.
check-platform-reqs Check that platform requirements are satisfied.
clear-cache Clears composer's internal package cache.
clearcache Clears composer's internal package cache.
config Sets config options.
create-project Creates new project from a package into given directory.
depends Shows which packages cause the given package to be installed.
diagnose Diagnoses the system to identify common errors.
dump-autoload Dumps the autoloader.
dumpautoload Dumps the autoloader.
exec Executes a vendored binary/script.
global Allows running commands in the global composer dir ($COMPOSER_HOME).
help Displays help for a command
home Opens the package's repository URL or homepage in your browser.
i Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
info Shows information about packages.
init Creates a basic composer.json file in current directory.
install Installs the project dependencies from the composer.lock file if present, or falls back on the composer.json.
licenses Shows information about licenses of dependencies.
list Lists commands
outdated Shows a list of installed packages that have updates available, including their latest version.
prohibits Shows which packages prevent the given package from being installed.
remove Removes a package from the require or require-dev.
require Adds required packages to your composer.json and installs them.
run-script Runs the scripts defined in composer.json.
search Searches for packages.
self-update Updates composer.phar to the latest version.
selfupdate Updates composer.phar to the latest version.
show Shows information about packages.
status Shows a list of locally modified packages, for packages installed from source.
suggests Shows package suggestions.
u Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
update Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
upgrade Upgrades your dependencies to the latest version according to composer.json, and updates the composer.lock file.
validate Validates a composer.json and composer.lock.
why Shows which packages cause the given package to be installed.
why-not Shows which packages prevent the given package from being installed.
از این پس میتوانیم به سادگی با استفاده از کامپوزر اقدام به پیادهسازی اُتولودینگ در پروژهٔ خود نماییم. پروژههای پیاچپی که در آنها از پَکِیج مَنِجر کامپوزر استفاده میشود دارای فایلی تحت عنوان composer.json
در روت پروژه هستند که این فایل حاوی دیتایی در قالب فرمت جیسون است که مشخص میسازد ابزار کامپوزر از چه تنظیماتی میباید تبعیت نماید. برای همین منظور، داخل پوشهٔ class-and-object
فایلی با این نام حاوی دیتای زیر میسازیم:
{
}
همانطور که ملاحظه میشود، یک آرایهٔ خالی داخل این فایل ایجاد کردهایم. حال در مسیر روت پروژه، کامند زیر را اجرا میکنیم:
/var/www/oop/class-and-object$ composer dump-autoload -o
در صورتی که اجرای کامند فوق بدون هیچگونه مشکلی تکمیل گردد، خواهیم دید که در روت پروژه فولدر جدیدی تحت عنوان vendor
ساخته خواهد شد که حاوی محتوای زیر است:
vendor
├── autoload.php
└── composer
├── autoload_classmap.php
├── autoload_namespaces.php
├── autoload_psr4.php
├── autoload_real.php
├── autoload_static.php
├── ClassLoader.php
└── LICENSE
کاری که کامند dump-autoload
انجام میدهد آن است که بر اساس قوانینی که داخل فایل composer.json
تعریف کردهایم (که در حال حاضر هیچ قانونی داخل این فایل تعریف نشده و صرفاً یک آرایهٔ خالی قرار دادهایم.) اقدام به آپدیت فایل autoload_classmap.php
میکند و از آنجا که این اولین باری است که این کامند را اجرا میکنیم، کامپوزر ابتدا فایلها و فولدرهای پیشنیاز من جمله فولدر vendor
را ساخته سپس تَسکهای مد نظر را عملی میسازد (در دفعات بعدی که این کامند اجرا گردد دیگر صرفاً فایل autoload_classmap.php
بهروزرسانی خواهد شد. همچنین لازم به یادآوری است که آپشن o-
بر گرفته از کلمهٔ Optimized است به منظور اجرای این کامند به شکلی بهینه است.)
آنچه در این پوشه در حال حاضر برایمان حائز اهمیت میباشد فایلی به نام autoload.php
است که این وظیفه را دارد تا به صورت خودکار کلیهٔ کلاسهایی که در جایجای این وب اپلیکیشن استفاده مینماییم را ایمپورت نماید. حال فایل composer.json
را به صورت زیر تکمیل میکنیم:
{
"autoload": {
"classmap": ["classes"]
}
}
در واقع، یک کلید اصلی داریم تحت عنوان autoload
که داخل آن کلیدی درج شده به نام classmap
که مقدارش یک آرایه است و همانطور که میبینیم، نام پوشهٔ classes
را به عنوان یکی از اِلِمانهای این آرایه در نظر گرفتهایم. اکنون مجدد دستور composer dump-autoload -o
را اجرا میکنیم و چنانچه در این پروسه مشکلی رخ ندهد، محتویات فایل autoload_classmap.php
که داخل پوشهٔ vendor/composer
قرار دارد به صورت زیر آپدیت خواهد شد:
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'User' => $baseDir . '/classes/User.php',
'User2' => $baseDir . '/classes/User2.php',
'User3' => $baseDir . '/classes/User3.php',
);
میبینیم که هر سه کلاس موجود داخل پوشهٔ classes
داخل این فایل به رسمیت شناخته شدهاند. اکنون فایل index.php
را به صورت زیر تکمیل نماییم تا از این پس هر کلاسی را که ساختیم و سپس استفاده نمودیم، به صورت خودکار ایمپورت گردد:
<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$objectMadeFromUserClass = new User();
$objectMadeFromUserClass->showFullName();
$objectMadeFromUser2Class = new User2();
$objectMadeFromUser2Class->showFullName();
$objectMadeFromUser3Class = new User3();
$objectMadeFromUser3Class->showFullName();
همانطور که ملاحظه میشود، از این پس دیگر نیازی به فایل init.php
نخواهیم داشت بلکه در عوض مسیر فایل autoload.php
را درج مینماییم و اگر این اسکریپت را اجرا نماییم، بدون هیچ مشکلی هر سه متد اجرا خواهند شد. در متودولوژی شیئگرایی، باز هم میتوان یک گام فراتر رفته و پروژههای اصولیتر نوشت که این امر با استفاده از مفهومی تحت عنوان Namespace عملی میگردد که در ادامه بیشتر با این مفهوم آشنا خواهیم شد.
آشنایی با مفهوم Namespace
Namespace قابلیتی است که از نسخهٔ PHP 5.3 به بعد به این زبان اضافه شد که در پروژههای پیچیدهای که با زبان پیاچپی نوشته میشوند، بسیار مشکلگشا خواهد بود. پیش از این گفتیم که یکی از مزایایی که OOP برای دولوپرها به ارمغان میآورد، ماژولار کردن سورسکد است به طوری که میتوان از ماژولها یا کلاسهایی که پیش از این نوشته شده در سایر پروژهها استفاده نمود اما در عین حال ممکن است که در این حوزه با مشکلاتی مواجه شویم.
فرض کنیم توسعهدهندهای کلاسی به نام User
نوشته که مستقل از سایر کلاسها است و هر توسعهدهندهٔ دیگری میتواند به سادگی از آن در پروژههای خود استفاده کند. در عین حال، خودمان نیز پیش از این کلاسی تحت عنوان User
ساختهایم و این در حالی است که اگر بخواهیم از هر دو کلاس داخل یک فایل استفاده نماییم، مسلماً به کانفلیکت (تداخل) خواهیم خورد و این گرهای است که به دست نِیماِسپیس باز میگردد.
در واقع، نِیماِسپیس این امکان را در اختیار توسعهدهندگان زبان پیاچپی قرار میدهد تا کلاسها و دیگر فایلهای مد نظر خود که مرتبط با یکدیگر هستند را داخل یک فضا با نامی منحصربهفرد قرار داده و این تضمین ایجاد خواهد شد که مثلاً در مثال فوق کلاس User
که خودمان نوشتیم در یک نِیماِسپیس است و دقیقاً چنین کلاسی که متعلق به دولوپر دیگری میباشد در نِیماِسپیس متفاوتی است که همین مسئله مشکل تداخل اسمی هر دو کلاس را مرتفع میسازد.
برای درک بهتر این موضوع، پوشهای میسازیم به نام namespacing
که حاوی فولدرها و فایلهای زیر خواهد بود:
namespacing
├── index.php
├── lib1
│ └── Database.php
└── lib2
└── Database.php
برای شروع، داخل پوشهٔ lib1
فایلی تحت عنوان Database.php
حاوی کدهای زیر میسازیم:
<?php
class Database
{
public function connect()
{
echo "This is the connect() method from lib1 folder\n";
}
}
به همین منوال، پوشهٔ دیگری به نام lib2
ساخته و فایل Database.php
قرارگرفته داخل آن را به صورت زیر تکمیل میکنیم:
<?php
class Database
{
public function connect()
{
echo "This is the connect() method from lib2 folder\n";
}
}
حال فایلی به نام index.php
ساخته و آن را به صورت زیر تکمیل میکنیم:
<?php
ini_set('display_errors', '1');
require_once "lib1/Database.php";
require_once "lib2/Database.php";
$obj1 = new Database();
echo $obj1->connect();
$obj2 = new Database();
echo $obj2->connect();
همانطور که میبینیم، ابتدا هر دو فایل را ایمپورت کرده سپس آبجکتهایی از روی هر دو کلاس Database
ساخته سپس متدهای ()connect
آنها را فراخوانی نمودهایم به طوری که به عنوان خروجی خواهیم داشت:
PHP Fatal error: Cannot declare class Database, because the name is already in use in /var/www/oop/namespacing/lib2/Database.php on line 4
میبینیم که متن خطا حاکی از آن است که مفسر پیاچپی میخواهد در خط چهارم کلاسی تحت عنوان Database
را ایمپورت کند اما این در حالی است که در خط سوم این کار یک بار صورت گرفته و امکان ایمپورت مجدد آن کلاس وجود ندارد. حال با استفاده از نِیماِسپیسها قصد داریم تا این مشکل را رفع کنیم که برای این منظور کلاس Database
قرارگرفته داخل پوشهٔ lib1
را به صورت زیر ریفکتور میکنیم:
<?php
namespace lib1;
class Database
{
public function connect()
{
echo "This is the connect() method from lib1 folder\n";
}
}
همانطور که میبینیم، به منظور استفاده از نِیماِسپیسها میباید کلیدواژهٔ namespace
را پیش از هر دستور دیگری نوشته سپس نامی دلخواه برای آن انتخاب کرد. به همین منوال، کلاس Database
موجود در پوشهٔ lib2
را به صورت زیر آپدیت میکنیم:
<?php
namespace lib2;
class Database
{
public function connect()
{
echo "This is the connect() method from lib2 folder\n";
}
}
پس از انتخاب نِیماِسپیسی منحصربهفرد همچون lib2
برای این فایل، حال نوبت به آپدیت فایل index.php
به صورت زیر میرسد:
<?php
ini_set('display_errors', '1');
require_once "lib1/Database.php";
require_once "lib2/Database.php";
$obj1 = new lib1\Database();
$obj1->connect();
$obj2 = new lib2\Database();
$obj2->connect();
که با اجرای اسکریپت فوق خواهیم داشت:
/var/www/oop/namespacing$ php index.php
This is the connect() method from lib1 folder
This is the connect() method from lib2 folder
در واقع، در حین ساخت آبجکت از روی هر دو کلاس Database
، نیماسپیس آن کلاس را نوشته سپس یک علامت \
قرار داده و در نهایت نام کلاس را نوشتهایم.
یکی از قابلیتهای کاربردی نِیماِسپیسها، مفهومی تحت عنوان Alias است بدین شکل که میتوان از نامی مستعار برای کلاسهای خود استفاده نمود. به طور مثال داریم:
<?php
ini_set('display_errors', '1');
require_once "lib1/Database.php";
require_once "lib2/Database.php";
use lib1\Database as BehzadDB;
use lib2\Database as SahandDB;
$obj1 = new BehzadDB();
$obj1->connect();
$obj2 = new SahandDB();
$obj2->connect();
همانطور که میبینیم، ابتدا کیورد use
را نوشته سپس نام کامل کلاس + نِیماِسپیس را درج کرده سپس کیورد as
را نوشته و نامی دلخواه برای کلاس مذکور در نظر گرفتهایم و در حین ساخت آبجکت نیز دقیقاً از همان نام که اصطلاحاً تحت عنوان Alias شناخته میشود استفاده کردهایم.
در ادامه، به منظور درک بیشتر قابلیتهای نِیماِسپیسها، پوشهای تحت عنوان lib3
به صورت زیر به پروژه اضافه میکنیم:
namespacing
├── index.1.php
├── index.2.php
├── index.php
├── lib1
│ └── Database.php
├── lib2
│ └── Database.php
└── lib3
└── sub1
└── sub2
└── sub3
└── User.php
در واقع، داخل این پوشه سه لایه پایینتر رفته و پوشههای دیگری ساختهایم و در نهایت هم داخل پوشهٔ sub3
فایلی تحت عنوان User.php
حاوی کدهای زیر ساختهایم:
<?php
namespace lib3\sub1\sub2\sub3;
class User
{
public function showText()
{
echo "User class \n";
}
}
حال قصد داریم تا این کلاس را داخل فایل index.php
استفاده نماییم که برای این منظور خواهیم داشت:
<?php
ini_set('display_errors', '1');
require_once "lib1/Database.php";
require_once "lib2/Database.php";
require_once "lib3/sub1/sub2/sub3/User.php";
$obj1 = new lib1\Database();
$obj1->connect();
$obj2 = new lib2\Database();
$obj2->connect();
$obj3 = new lib3\sub1\sub2\sub3\User();
$obj3->showText();
به عنوان خروجی هم خواهیم داشت:
/var/www/oop/namespacing$ php index.php
This is the connect() method from lib1 folder
This is the connect() method from lib2 folder
User class
در واقع، پس از ایمپورت کردن فایل User.php
، اقدام به ساخت یک آبجکت جدید از روی کلاس User
کردهایم با مد نظر قرار دادن این نکته که میباید در حین ساخت آبجکت نیز مسیر کامل را بدهیم و چنانچه از کدی به صورت زیر استفاده نماییم:
$obj3 = new User();
با ارور زیر روبهرو خواهیم شد:
PHP Fatal error: Uncaught Error: Class 'User' not found in /var/www/oop/namespacing/index.php:11
در عین حال اگر بخواهیم که در حین ساخت آبجکت فقط از نام کلاس استفاده نماییم، میتوانیم ساختار زیر را مد نظر قرار دهیم:
<?php
ini_set('display_errors', '1');
require_once "lib1/Database.php";
require_once "lib2/Database.php";
require_once "lib3/sub1/sub2/sub3/User.php";
use lib3\sub1\sub2\sub3\User;
$obj1 = new lib1\Database();
$obj1->connect();
$obj2 = new lib2\Database();
$obj2->connect();
$obj3 = new User();
$obj3->showText();
همانطور که میبینیم، از کلیدواژهٔ use
استفاده کرده سپس آدرس کامل کلاس مذکور را نوشتهایم و در حین ساخت آبجکت در خط سیزدهم، صرفاً نام کلاس را بدون ایجاد هیچ گونه مشکلی مورد استفاده قرار دادهایم.
حال که با سازوکار نِیماِسپیس آشنا شدیم، مجدد به پروژهٔ class-and-object
باز میگردیم تا ببینیم به چه شکل میتوانیم نِیماِسپیس را با اُتولودینگ ادغام نمود. برای این منظور، پیش از هر چیز فایل composer.json
را به صورت زیر تغییر میدهیم:
{
"autoload": {
"psr-4": {
"SokanAcademy\\": "classes"
}
}
}
این بار به جای کلید classmap
، از کلیدی تحت عنوان psr-4
استفاده نمودهایم و داخل آن نیز نِیماِسپیسی دلخواه همچون SokanAcademy
در نظر گرفتهایم و مسیر منتسب به آن نیز پوشهٔ classes
است. حال نیاز است تا به پوشهٔ classes
مراجعه کرده و نِیماِسپیس انتخابی خود را برای تمامی کلاسهای موجود داخل این کلاس درج نماییم به طوری که مثلاً برای کلاس User.php
داریم:
<?php
namespace SokanAcademy;
class User
{
public $name = "Behzad";
public $lastName = "Moradi";
public $dob = 1362;
public function showFullName()
{
echo $this->name . ' ' . $this->lastName;
}
}
و کلاس User2
را به صورت زیر تغییر میدهیم:
<?php
namespace SokanAcademy;
class User2
{
public function showFullName()
{
echo "This is User2";
}
}
و همچنین کلاس User3
را نیز به صورت زیر آپدیت خواهیم کرد:
<?php
namespace SokanAcademy;
class User3
{
public function showFullName()
{
echo "This is User3";
}
}
حال از طریق کامندلاین، داخل پوشهٔ class-and-object
کامند composer dump-autoload -o
را اجرا میکنیم به طوری که اگر پس از اجرای موفقیتآمیز این کامند به فایل autoload_classmap.php
مراجعه کنیم، خواهیم داشت:
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'SokanAcademy\\User' => $baseDir . '/classes/User.php',
'SokanAcademy\\User2' => $baseDir . '/classes/User2.php',
'SokanAcademy\\User3' => $baseDir . '/classes/User3.php',
);
میبینیم که بر اساس نِیماِسپیس انتخابی، کلاسها به منظور اُتولودینگ به درستی شناسایی شدهاند. حال وارد فایل index.php
شده و تغییرات زیر را اِعمال میکنیم:
<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';
$objectMadeFromUserClass = new SokanAcademy\User();
$objectMadeFromUserClass->showFullName();
$objectMadeFromUser2Class = new SokanAcademy\User2();
$objectMadeFromUser2Class->showFullName();
$objectMadeFromUser3Class = new SokanAcademy\User3();
$objectMadeFromUser3Class->showFullName();
در واقع، تنها تغییری که اِعمال کردهایم آن است که پیش از نام کلاسها، از نِیماِسپیس SokanAcademy
استفاده نمودهایم که اجرای این اسکریپت بدون هیچ گونه مشکلی صورت خواهد گرفت (در این مرحله از کار میتوانیم فایل init.php
که پیش از ساخته بودیم را حذف نماییم چرا که اساساً بلااستفاده است.)
جمعبندی
در این آموزش با مفاهیم اصلی شیئگرایی از جمله کلاس و آبجکت آشنا شده و دیدیم که به چه شکل میتوان یک سری پراپرتی و متد برای کلاس خود در نظر گرفته و پس از ساخت آبجکت آنها را فراخوانی نمود. همچنین در ادامه به بررسی مقولهٔ اُتولودینگ پرداخته و ملاحظه شد که به چه شکل میتوان به صورت خودکار کلاسهای مورد استفاده را داخل فایلهای مد نظر خود ایمپورت نمود و در نهایت هم به بررسی مقولهٔ نِیماِسیپسها پرداختیم که پس از PHP 5.3 به این زبان افزوده شده و امروزه استفاده از آنها را در تمامی فریمورکهای مطرح این زبان شاهد هستیم.