در آموزش بررسی ساختار فریمورکی که در این دوره توسعه خواهیم داد، دیدیم که فولدر mvc
حاوی فایل و فولدرهای زیر است:
mvc
├── app
├── composer.json
├── public
└── vendor
در فصل دوم به بررسی فولدر public
پرداختیم و در این این فصل قصد داریم تا به بررسی فولدر app
بپردازیم. برای همین منظور، در مسیر روت پروژه با استفاده از کامند زیر فولدری تحت عنوان app
میسازیم:
/var/www/mvc$ mkdir app
پیش از این گفتیم که قرار است تا این پوشه حاوی فولدرها و فایلهای زیر باشد:
Core
├── App.php
├── BaseController.php
├── Interfaces
│ ├── ControllerInterface.php
│ └── UserInterface.php
└── Routing.php
در همین راستا، در این آموزش قصد داریم تا فایل Routing.php
را مورد بررسی قرار دهیم. بر اساس استاندارد PSR-4: Autoloader که در آموزش گذشته با مفهوماش آشنا شدیم، ابتدا پوشهای تحت عنوان Core
ساخته سپس با استفاده از کامند زیر این فایل را میسازیم:
/var/www/mvc/app/Core$ touch Routing.php
ابتدا کدهای زیر را داخل این فایل درج نموده سپس به بررسی آنها خواهیم پرداخت:
<?php
namespace Core;
class Routing
{
public $routes = [
[
'route' => '',
'module' => 'Base',
'controller' => 'DefaultController',
'action' => 'homepage',
],
[
'route' => 'default/homepage',
'module' => 'Base',
'controller' => 'DefaultController',
'action' => 'homepage',
],
[
'route' => 'default/about',
'module' => 'Base',
'controller' => 'DefaultController',
'action' => 'about',
],
[
'route' => 'default/users',
'module' => 'Base',
'controller' => 'DefaultController',
'action' => 'users',
]
];
public function __construct()
{
return $this->routes;
}
}
پس از درج تگ آغازین php?>
ابتدا کیورد namesapce
را نوشته سپس نام پوشهای که داخل آن قرار داریم را مینویسیم که در این مثال Core
است. در واقع، کاری که نِیماِسپیس انجام میدهد این است که مشخص میسازد فایل Routing.php
دقیقاً در چه مسیری قرار داد و به کدام نِیماِسپیس تعلق دارد؛ سپس در ادامه آموزش فایل composer.json
را تکمیل میکنیم تا نِیماِسپیسی تحت عنوان Core
در این پروژه به رسمیت شناخته شود.
همانطور که میبینیم، کلاسی تعریف کردهایم به نام Routing
که حاوی یک پراپرتی تحت عنوان routes$
از جنس public
است که این امکان را برایمان فراهم میسازد تا از هر جای پروژه به آن دسترسی داشته باشیم. نیاز به توضیح نیست که routes$
آرایهای چندبُعدی است که در آن یکسری یوآرال پیشفرض تعریف کردهایم به طوری که اگر کاربر هر لینکی به غیر از آنچه در کلیدهای route
در این آرایه تعریف شده را در آدرسبار درج نماید، با ارور 404 مواجه خواهد شد. برای مثال، آرایهٔ مربوط به هومپیج سایت را مد نظر قرار میدهیم:
[
'route' => 'default/homepage',
'module' => 'Base',
'controller' => 'DefaultController',
'action' => 'homepage',
]
در آرایهٔ فوق کلیدی داریم تحت عنوان route
که مشخصاً لینکی که به هومپیج سایت منتج میشود را تعیین میکند. سپس کلیدی داریم به نام module
که مقدار در نظر گرفته شده برای آن Base
است که به منزلهٔ ماژول اصلی این فریمورک میباشد که در فصول آتی آن را خواهیم ساخت. کلیدهای controller
و action
نیز به ترتیب مشخصکنندهٔ کنترلر و متدی در ماژول Base
هستند که پس از وارد کردن لینک default/homepage
فراخوانی خواهند شد.
لازم به یادآوری است کانستراکتور متدی میباشد که به محض ساخت یک آبجکت از روی کلاسمان فراخوانی خواهد شد. از همین روی، در کانستراکتور کلاس Routing.php
دستور دادهایم تا به محض ساخت یک آبجکت از روی این کلاس، پراپرتی routes$
ریترن گردد. اکنون به منظور تست، مجدد به فایل index.php
بازگشته و آن را به صورت زیر تغییر میدهیم:
<?php
ini_set('display_errors', '1');
require_once __DIR__ . '/../vendor/autoload.php';
$routingObj = new Core\Routing();
dd($routingObj);
function dd($input)
{
echo "<pre>";
var_dump($input);
echo "</pre>";
die;
}
با رفتن به مسیر http://mvc.local
در مرورگر، در خروجی با ارور زیر مواجه خواهیم شد:
Fatal error: Uncaught Error: Class 'Core\Routing' not found in /var/www/mvc/public/index.php:5 Stack trace: #0 {main} thrown in /var/www/mvc/public/index.php on line 5
این ارور حاکی از آن است که کلاسی با نِیماِسپیس Core\Routing
که در خط پنجم فایل index.php
استفاده شده است یافت نشد! دلیل این مسئله آن است که در فایل composer.json
چنین نِیماِسپیسی تعریف نشده است.
تعریف نِیماِسپیس Core در فایل composer.json
پیش از این گفتیم که در راستای به رسمیت شناخته شدن نِیماِسپیس Core
، میباید یکسری قوانین در فایل composer.json
تعریف نماییم. برای همین منظور، این فایل را به صورت تکمیل میکنیم:
{
"autoload": {
"classmap": [
"app/Core"
]
}
}
با رفتن به مسیر http://mvc.local
مجدد با ارور زیر مواجه خواهیم شد:
Fatal error: Uncaught Error: Class 'Core\Routing' not found in /var/www/mvc/public/index.php:5 Stack trace: #0 {main} thrown in /var/www/mvc/public/index.php on line 5
دلیل این مسئله آن است که دستور composer dump-autoload -o
را اجرا نکردهایم تا دیتای موجود در پوشهٔ vendor
آپدیت گردد. در همین راستا، ابتدا دستور زیر را اجرا کرده سپس مجدد صفحه را رِفرش میکنیم:
/var/www/mvc$ composer dump-autoload -o
Generated optimized autoload files containing 1 classes
میبینیم که پس از اجرای موفقیتآمیز کامند فوق، پیامی در معرض دیدمان قرار میگیرد با این مضمون که «یک کلاس» داریم که از این پس با استفاده از قابلیت Autoload کامپوزر قابلاستفاده است. حال اگر در مسیر vendor/composer/
فایلی تحت عنوان autoload_classmap.php
را باز کنیم، خواهیم داشت:
<?php
// autoload_classmap.php @generated by Composer
$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Core\\Routing' => $baseDir . '/app/Core/Routing.php',
);
میبینیم که نِیماِسپیس مذکور افزوده شده است. اکنون با رفتن به مسیر http://mvc.local
و رِفرش کردن صفحه خواهیم داشت:
object(Core\Routing)#3 (1) {
["routes"]=>
array(4) {
[0]=>
array(4) {
["route"]=>
string(0) ""
["module"]=>
string(4) "Base"
["controller"]=>
string(17) "DefaultController"
["action"]=>
string(8) "homepage"
}
[1]=>
array(4) {
["route"]=>
string(16) "default/homepage"
["module"]=>
string(4) "Base"
["controller"]=>
string(17) "DefaultController"
["action"]=>
string(8) "homepage"
}
[2]=>
array(4) {
["route"]=>
string(13) "default/about"
["module"]=>
string(4) "Base"
["controller"]=>
string(17) "DefaultController"
["action"]=>
string(5) "about"
}
[3]=>
array(4) {
["route"]=>
string(13) "default/users"
["module"]=>
string(4) "Base"
["controller"]=>
string(17) "DefaultController"
["action"]=>
string(5) "users"
}
}
}
میبینیم که خطوط پنجم و ششم از فایل index.php
با موفقیت اجرا شدند. در عین حال توجه داشته باشیم که فرمت فعلی در فایل composer.json
اصلاً بهینه نیست چرا که با افزودن کلاسهای جدید، به صورتی دستی میباید نِیماِسپیس آن را تعریف کنیم! در همین راستا، محتویات این فایل را به صورت زیر ریفکتور میکنیم:
{
"autoload": {
"psr-4": {
"Core\\": "app/Core"
}
}
}
همانطور که ملاحظه میشود، از کلیدی تحت عنوان autoload
استفاده کردهایم که خود حاوی کلید دیگری به نام psr-4
است که در این آرایه کلیدی تحت عنوان \\Core
تعریف کردهایم و علت آنکه پس از نِیماِسپیس Core
دو علامت \\
قرار دادهایم آن است که یکی از این دو \
به منظور اصطلاحاً Escape کردن دیگری است. در ادامه، به عنوان مقدار این کلید، مسیر app/Core
را در نظر گرفتهایم که دقیقاً همان مسیری است که پوشهٔ Core
در آن قرار دارد. در این مرحله از کار، مجدد نیاز خواهیم داشت تا دستور dump-autoload
را اجرا نماییم تا اطلاعات فایل autoload_classmap.php
آپدیت شوند:
/var/www/mvc$ composer dump-autoload -o
لازم به یادآوری است که هیچ تغییری در فایل autoload_classmap.php
مشاهده نخواهد شد اما از آنجا که شیوهٔ Autoloading در فایل composer.json
را دستخوش تغییر کردهایم، نیاز است تا مجدد این فرمان اجرا گردد و این در حالی است که اگر مجدد صفحه را رِفرش نماییم، همان خروجی قبل را مشاهده خواهیم کرد.
از این پس هر فایل جدیدی که در نِیماِسپیس Core
ایجاد کرده سپس مورد استفاده قرار دهیم به صورت خودکار به فایل مد نظرمان ایمپورت خواهد شد. برای مثال، داخل پوشهٔ Core
کلاس موقتی جدیدی به نام Tmp
میسازیم:
<?php
namespace Core;
class Tmp
{
public function __construct()
{
echo 'hi';
}
}
اکنون این کلاس را داخل فایل index.php
به صورت زیر مورد استفاده قرار میدهیم:
<?php
ini_set('display_errors', '1');
require_once __DIR__ . '/../vendor/autoload.php';
$routingObj = new Core\Routing();
$tmpObj = new Core\Tmp();
dd($tmpObj);
function dd($input)
{
echo "<pre>";
var_dump($input);
echo "</pre>";
die;
}
و به عنوان خروجی خواهیم داشت:
hi
با توجه به اینکه کلاس Tmp
صرفاً جهت تست بود، آن را از پروژه حذف میکنیم. نکتهای که در ارتباط با نحوهٔ استفاده از کلاسها وجود دارد آن است که میتوانیم به جای نوشتن نام کامل کلاس پس از کیورد new
به منظور ساخت یک آبجکت جدید، ابتدا کلاس مذکور را use
کرده سپس صرفاً نام کلاس را نوشت:
<?php
ini_set('display_errors', '1');
use Core\Routing;
require_once __DIR__ . '/../vendor/autoload.php';
$routingObj = new Routing();
dd($routingObj);
function dd($input)
{
echo "<pre>";
var_dump($input);
echo "</pre>";
die;
}
همانطور که ملاحظه میشود، ابتدا کیورد use
را نوشته سپس نِیماِسپیس کامل کلاس مد نظر خود را مینویسیم که در این مثال Core\Routing
است. حال میبینیم که در خط ششم صرفاً نام کلاس را نوشته و از روی آن یک آبجکت جدید ساختهایم.
نحوهٔ ساخت Alias برای نِیماِسپیس
در توسعهٔ نرمافزار گاهی پیش میآید که کلاسهای همنامی داشته باشیم. مثلاً فرض کنیم دو نِیماِسپیس به صورت زیر داریم:
use Core\Routing;
use SokanAcademy\Routing;
در چنین مواردی به منظور جلوگیری از هر گونه کانفلیکت (تداخل)، میتوان یا برای هر دو کلاس و یا برای یکی از آنها از یک به اصطلاح Alias یا «نام مستعار» استفاده کرد. برای مثال چنانچه به فایل index.php
بازگردیم خواهیم داشت:
<?php
ini_set('display_errors', '1');
use Core\Routing as MyRoutingClass;
require_once __DIR__ . '/../vendor/autoload.php';
$routingObj = new MyRoutingClass();
dd($routingObj);
function dd($input)
{
echo "<pre>";
var_dump($input);
echo "</pre>";
die;
}
برای این منظور، پس از نِیماِسپیس مد نظر خود ابتدا کلیدواژهٔ as
را نوشته سپس نامی دلخواه همچون MyRoutingClass
مینویسیم. همانطور که در خط ششم میبینیم، نام مستعار جدید را به منظور ساخت آبجکت در نظر گرفتهایم.
در این آموزش دیدیم که به چه شکل میتوان یک کلاس جدید ساخته و آن را با استفاده از کامپوزر به صورت خودکار ایمپورت کرد. همچنین به بررسی این موضوع پرداختیم که بر اساس استاندارد PSR-4: Autoloader به چه شکل میتوان به روشی بهینه اقدام به ایمپورت کردن کلاسها بر اساس نِیماِسپیس نمود.