مفهوم namespace  در PHP

مفهوم namespace در PHP

یکشنبه 14 آبان 1396

یکی از ویژگی های مهمی که در 5.3 PHP اضافه شد، namespace بود. برنامه نویس های #C و جاوا با این ویژگی آشنا هستند. namespace باعث بهبود ساختار اپلیکیشن‌های PHP میشود به طوری که مشکل نام گذاری‌های یکتا حل، همچنین امکان بخش بندی کدها را به توسعه دهندگان می‌دهد، و سازماندهی و پکیج بندی کدها در آن خیلی شبیه به ساختار دایرکتوری‌ها در فایل سیستم هست. علاوه بر موارد بالا شما را قادر می سازد تا از تمام مزایای autoloaderهایی که از جدیدترین استانداردها پیروی می کنند، که شامل اتولودر کامپوزر (Composer’s autoloader) هم می‌شود بهره ببرید.

مفهوم و مقدمات namespace

از مزایای namespace گفتیم، اما بیایید کمی دقیق‌تر به موضوع نگاه کنیم. چه زمانی به استفاده از آنها نیاز پیدا می کنیم؟

فضای پیش فرض، که شما در آن به نوشتن کد PHP می‌پردازید فضای سراسری یا global space نام دارد در این فضا شما اجازه تعریف دو کلاس با نام یکسان را ندارید و اگر این کار را انجام دهید با Fatal error روبرو میشوید. این موضوع برای نام تابع‌ها و ثابت‌ها نیز صدق می‌کند.

مثال مشابه برای روشن‌تر شدن موضوع، ساختار دایرکتوری‌ها در سیستم عامل‌ها است که امکان ندارد در یک مسیر واحد دو فایل foo.txt ایجاد کرد ولی برای گروه بندی فایل‌های مرتبط می‌توان دایرکتوری دیگری تعریف کرد و یک فایل foo.txt در یک دایرکتوی و فایل foo.txt دیگر را در دایرکتوری دیگر ایجاد کرد.

وقتی پروژه گسترده می‌شود این احتمال بالاتر می‌رود که دوباره بخواهیم از نام تابع یا کلاسی که قبلا تعریف شده برای تعریف مجدد استفاده کنید. اوضاع وقتی بدتر می‌شود که بخواهید کامپوننت یا پلاگین شخص دیگری را به پروژه اضافه کنید.

این احتمال وجود دارد که در کامپوننتی که میخواهید به پروژه اضافه کنید هم، نام چند تا از کلاسها با نام کلاس‌هایی که شما انتخاب کردید یکی باشد.

بنابراین پیامدهایی که بوجود می‌آید شامل موارد زیر هستند:

  • تداخل نام بین کدهایی که شما ایجاد کردید، و کلاس ها و ثابت ها و توابع داخلی PHP و یا کلاس ها و ثابت ها و توابع یک کامپوننت دیگر.
  • برای حل مشکل اول از نام های طولانی و توصیفگر یا پیشوند گذاری قبل بعضی نام ها استفاده می‌شد. که این کار خودش به نوعی کار برنامه‌نویس را سخت‌تر می‌کند.

اما حالا براحتی می‌توانیم فضای نام یا namespace تعریف کنیم که با این کار به نوعی کلاس ها و تابع ها و ثابت‌هامون رو بخش بندی خواهیم کرد و دیگر خبری از تداخل نیست.

نکته: داخل یک namespace هم طبیعتا نمی توان کلاس‌ها یا تابع‌ها یا ثابت‌های هم نام تعریف کرد.

تعریف namespace

خط تعریف namespace باید اولین دستور در بالای کدهایتان و قبل از هر کد دیگری باشد. طبق استاندار PSR-2 یک خط خالی بعد از تعریف آن با بقیه کدها باید وجود داشته باشد.

<?php
namespace FooProject;

const CONNECT_OK = 1;
class Connection { /* ... */ }
function connect() { /* ... */  }

تعریف Sub-namespaces

مانند فایل‌ها و دایرکتوری‌ها، میتوانید یک ساختار سلسله مراتبی و تو در تو برای کدهایتان درست کنید که با کاراکتر backslash (\) از هم جدا میشوند.

<?php
namespace MyProject1;

class Foo {
	public function Bar()
	{
		echo 'Bar method for MyProject1, Foo class. 
'; } } $objFoo1=new Foo; $objFoo1->bar(); namespace MyProject2; class Foo { public function Bar() { echo 'Bar method for MyProject2, Foo class.
'; } } $objFoo2=new Foo; $objFoo2->bar();

در مثال بالا ما دو namespace در یک فایل PHP تعریف کردیم که اینکار امکان پذیر است، اما هرگز توصیه نمی‌شود و برای هر فایل بایستی یک namespace تعریف کرد.

نکته: اگر در یک فایل بخواهید از کد namespace شده و کد namespace نشده (Global code) استفاده کنید. باید از ساختار براکت شده مثل زیر استفاده کنید:

<?php
namespace MyProject { // MyProject namespace code
}

namespace { // global code
}

فراخوانی کدهای Namespace شده

در فایل lib1.php، یک ثابت و یک تابع و یک کلاس تعریف کرده ایم و namespace آنرا App\Lib1 قرار داده ایم:

<?php
// application library 1
namespace App\Lib1;

const MYCONST = 'App\Lib1\MYCONST';

function MyFunction() {
	return __FUNCTION__;
}

class MyClass {
	static function WhoAmI() {
		return __METHOD__;
	}
}

حال برای صدا زدن (call) کدهای بالا در فایل دیگر برای مثال فایل myapp.php یک روش استفاده از کدی مشابه کد زیر است:

<?php
header('Content-type: text/plain');
require_once('lib1.php');

echo \App\Lib1\MYCONST . "\n";
echo \App\Lib1\MyFunction() . "\n";
echo \App\Lib1\MyClass::WhoAmI() . "\n";

بسیارخب، در کد myapp.php هیچ namespaceایی تعریف نشده، بنابراین در فضای global هستیم. از آنجاییکه MYCONST و MyFunction و MyClass در فضای نام App\Lib1 تعریف شده‌اند شما به طور مستقیم قادر به فراخوانی آنها نیستید و بایستی پیشوند \App\Lib1 را اضافه کنید تا یک نام fully-qualified داشته باشید. در نهایت خروجی زیر را خواهید داشت:

<?php
App\Lib1\MYCONST
App\Lib1\MyFunction
App\Lib1\MyClass::WhoAmI

اما نام های fully-qualified خیلی طولانی هستند و مزیت زیادی نسبت به مثلا نامگذاری کلاس به صورت App-Lib1-MyClass ندارند. قبل از اینکه بخواهید استفاده از namespaceها را یاد بگیرید مهم است که بدانید PHP چطور تشخیص میدهد که کدام بخش از کد namespace شده درخواست شده است. یک قیاس ساده بین namespaces در PHP و فایل سیستم می‌تواند مثال خوبی باشد.

در فایل سیستم 3 راه برای دسترسی به یک فایل داریم:

  • نام فایل به صورت نسبی (Relative) باشد مانند foo.txt. که این مسیر به currentdirectory/foo.txt تبدیل می‌شود که currentdirectory همان دایرکتوری جاری است که در آن قرار داریم.
  • آدرس دهی نسبی مثل subdirectory/foo.txt که به currentdirectory/subdirectory/foo.txt تبدیل میشود.
  • آدرس دهی مطلق مانند main/foo.txt/ که تبدیل می‌شود به main/foo.txt/ (یعنی به خودش).

همین قاعده نیز برای عناصر namespace شده PHP کاربرد دارد. مثلا نام کلاس به سه روش زیر معرفی شده:

مفهوم namespace  در PHP

1. Unqualified name یا نام کلاس بدون پیشوند مثل :

$a = new foo();

یا

foo::staticmethod();

اگر namespace جاری currentnamespace باشد، تبدیل می‌شود به currentnamespace\foo اگر هم بدون namespace باشد تبدیل می شود به foo.

نکته: وقتی علامت \ را قبل از نام تابع یا ثابت قرار بدهیم تابع یا ثابت global هدف قرار میگیرد.

<?php
namespace A\B\C;

function strlen($str)
{
	return 'ok';
}

echo strlen('hi'), "
"; // prints "ok" echo \strlen('hi'), "
"; // prints "2"

2. Qualified name یا نام کلاس همراه با پیشوند مثل :

$a = new subnamespace\foo();

یا

subnamespace\foo::staticmethod();

اگر namespace جاری currentnamespace باشد تبدیل به currentnamespace\subnamespace\foo می‌شود و اگر بدون namespace باشد، subnamespace\foo اجرا می‌شود.

3. Fully qualified name یا نام پیشوند گذاری شده با پیشوند global \ مثل :

new \currentnamespace\foo();

یا

\currentnamespace\foo::staticmethod();

همیشه تبدیل می‌شود به همان نام مشخص شده یعنی خودش

currentnamespace\foo

منابع مورد مطالعه جهت جمع آوری این مطلب:
http://aparnet.ir/3021-namespaces-in-php-part-1


نظرات