آموزش برنامه نویسی PHP-جلسه نوزدهم

آموزش برنامه نویسی PHP-جلسه نوزدهم

در php یک فرزند حق دارد از یک والد ارث بری کند ولی اگر نیاز داشته باشیم که از چند کلاس ارث بری داشته باشیم برای رفع این محدودیت از Traits استفاده می‌کنیم.

Table of contents [Show] [Hide]

Traits در PHP چیست؟

در برنامه‌نویسی شیءگرا در PHP، گاهی چند کلاس مختلف به مجموعه‌ای از متدها یا ویژگی‌های مشترک نیاز دارند، اما نمی‌توانیم یا نمی‌خواهیم این قابلیت‌ها را از طریق ارث‌بری بین کلاس‌ها منتقل کنیم.

در چنین شرایطی می‌توانیم از Trait استفاده کنیم.

Trait را می‌توان یک مجموعه از متدها و ویژگی‌های قابل استفاده مجدد در نظر گرفت که می‌توان آن را در چند کلاس مختلف قرار داد.

برای تعریف Trait از کلمه کلیدی trait استفاده می‌کنیم:

<?php

trait TraitName
{
    // Properties and methods
}

?>

بعد از تعریف Trait، برای استفاده از آن در یک کلاس از کلمه کلیدی use استفاده می‌کنیم:

class Welcome
{
    use TraitName;
}

در این حالت، متدها و ویژگی‌های موجود در Trait در کلاس Welcome قابل استفاده خواهند بود.


چرا از Trait در PHP استفاده می‌کنیم؟

یکی از مشکلات رایج در برنامه‌نویسی، تکرار کد است.

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

Trait به ما اجازه می‌دهد یک بار کد مورد نظر را تعریف کنیم و سپس آن را در چند کلاس مختلف استفاده کنیم.

برای مثال:

trait Message
{
    public function showMessage()
    {
        echo "Hello World!";
    }
}

حالا می‌توانیم Trait را در چند کلاس استفاده کنیم:

class Welcome
{
    use Message;
}

class User
{
    use Message;
}

در نتیجه هر دو کلاس به متد showMessage() دسترسی خواهند داشت.


ساخت Trait در PHP

یک Trait می‌تواند شامل متدهای مختلف باشد:

trait Message
{
    public function message1()
    {
        echo "Hello!";
    }

    public function message2()
    {
        echo "Welcome to PHP!";
    }
}

سپس Trait را در کلاس مورد نظر استفاده می‌کنیم:

class Welcome
{
    use Message;
}

اکنون می‌توانیم یک Object از کلاس Welcome ایجاد کنیم:

$obj = new Welcome();

$obj->message1();
$obj->message2();

خروجی:

Hello!
Welcome to PHP!

استفاده از چند Trait در یک کلاس

یکی از قابلیت‌های مهم Trait این است که یک کلاس می‌تواند چند Trait را به صورت هم‌زمان استفاده کند.

برای مثال:

trait Message1
{
    public function msg1()
    {
        echo "OOP is fun!";
    }
}

trait Message2
{
    public function msg2()
    {
        echo "OOP reduces code duplication!";
    }
}

حالا یک کلاس می‌تواند هر دو Trait را استفاده کند:

class Welcome
{
    use Message1, Message2;
}

بنابراین:

$obj = new Welcome();

$obj->msg1();
$obj->msg2();

هر دو متد در دسترس خواهند بود.


مثال کامل استفاده از چند Trait

<?php

trait Message1
{
    public function msg1()
    {
        echo "OOP is fun!";
    }
}

trait Message2
{
    public function msg2()
    {
        echo "OOP reduces code duplication!";
    }
}

class Welcome
{
    use Message1;
}

class Welcome2
{
    use Message1, Message2;
}

$obj = new Welcome();
$obj->msg1();

echo "<br>";

$obj2 = new Welcome2();
$obj2->msg1();
$obj2->msg2();

?>

در این مثال:

کلاس Welcome فقط از Message1 استفاده می‌کند.

کلاس Welcome2 از هر دو Trait یعنی Message1 و Message2 استفاده می‌کند.


Trait و ارث‌بری چه تفاوتی دارند؟

Trait با extends متفاوت است.

در ارث‌بری، یک کلاس از کلاس دیگری ویژگی‌ها و رفتارها را به ارث می‌برد:

class Child extends Parent
{
}

اما در Trait، مجموعه‌ای از قابلیت‌ها را مستقیماً داخل یک کلاس قرار می‌دهیم:

class User
{
    use Logger;
}

Trait زمانی بسیار مفید است که چند کلاس مختلف به قابلیت مشترکی نیاز داشته باشند، اما رابطه منطقی «والد و فرزند» بین آن‌ها وجود نداشته باشد.

برای مثال:

trait Logger
{
    public function log($message)
    {
        echo $message;
    }
}

می‌توانیم از این Trait در کلاس‌های مختلف استفاده کنیم:

class User
{
    use Logger;
}

class Product
{
    use Logger;
}

class Order
{
    use Logger;
}

در اینجا User، Product و Order لزوماً یک خانواده ارث‌بری نیستند، اما هر سه می‌توانند قابلیت ثبت Log را داشته باشند.


Trait می‌تواند Property داشته باشد؟

بله. Trait علاوه بر متد می‌تواند Property نیز داشته باشد.

برای مثال:

trait UserInfo
{
    public $name;

    public function setName($name)
    {
        $this->name = $name;
    }
}

سپس:

class User
{
    use UserInfo;
}

$user = new User();

$user->setName("Ali");

echo $user->name;

در اینجا Property و متد موجود در Trait در کلاس User قابل استفاده هستند.


برخورد نام متدها در Traitها

اگر دو Trait دارای متدی با نام یکسان باشند و هر دو را در یک کلاس استفاده کنیم، PHP نمی‌تواند به صورت خودکار تشخیص دهد کدام متد باید استفاده شود.

برای مثال:

trait A
{
    public function message()
    {
        echo "Message from A";
    }
}

trait B
{
    public function message()
    {
        echo "Message from B";
    }
}

اگر هر دو را در یک کلاس استفاده کنیم:

class Test
{
    use A, B;
}

با یک تعارض مواجه خواهیم شد.

برای حل این مشکل می‌توانیم از insteadof استفاده کنیم:

class Test
{
    use A, B {
        A::message insteadof B;
    }
}

در این حالت متد message() مربوط به Trait A استفاده خواهد شد.


تغییر نام یا سطح دسترسی متد Trait

Traitها قابلیت دیگری نیز دارند که به کمک آن می‌توانیم سطح دسترسی یک متد را تغییر دهیم.

برای مثال:

trait Message
{
    public function show()
    {
        echo "Hello";
    }
}

class Test
{
    use Message {
        show as protected;
    }
}

در این حالت متد show() در کلاس Test با سطح دسترسی protected در نظر گرفته می‌شود.


Static Method در PHP چیست؟

در PHP به صورت معمول برای اجرای یک متد متعلق به Object، ابتدا باید از کلاس نمونه‌سازی کنیم:

class Greeting
{
    public function welcome()
    {
        echo "Hello World!";
    }
}

$obj = new Greeting();

$obj->welcome();

اما گاهی متدی داریم که به اطلاعات یک Object خاص وابسته نیست و می‌خواهیم آن را مستقیماً از طریق خود کلاس اجرا کنیم.

در چنین شرایطی می‌توانیم متد را Static تعریف کنیم.

برای ایجاد Static Method از کلمه کلیدی static استفاده می‌کنیم:

class ClassName
{
    public static function staticMethod()
    {
        echo "Hello World!";
    }
}

نحوه فراخوانی Static Method

برای دسترسی به Static Method نیازی به ایجاد Object نداریم.

به جای -> از عملگر :: استفاده می‌کنیم:

ClassName::staticMethod();

برای مثال:

class Greeting
{
    public static function welcome()
    {
        echo "Hello World!";
    }
}

Greeting::welcome();

خروجی:

Hello World!

در این مثال بدون استفاده از new توانستیم متد welcome() را اجرا کنیم.


تفاوت متد معمولی و Static Method

یک متد معمولی به Object مربوط می‌شود:

$obj->welcome();

اما Static Method مستقیماً به کلاس مربوط است:

Greeting::welcome();

بنابراین اگر متد به وضعیت یا Propertyهای یک Object خاص وابسته نباشد، در برخی طراحی‌ها می‌توان آن را به صورت Static تعریف کرد.

البته Static بودن یک متد صرفاً برای حذف new نیست؛ انتخاب آن باید با توجه به طراحی و وابستگی‌های کلاس انجام شود.


Static Method و کلمه کلیدی self

داخل یک کلاس می‌توانیم برای دسترسی به اعضای Static همان کلاس از self استفاده کنیم.

برای مثال:

class Greeting
{
    public static function welcome()
    {
        echo "Hello World!";
    }

    public function __construct()
    {
        self::welcome();
    }
}

new Greeting();

در این مثال، Constructor هنگام ایجاد Object اجرا می‌شود و با استفاده از:

self::welcome();

متد Static مربوط به همان کلاس را فراخوانی می‌کند.

نکته مهم این است که self جایگزین کلمه static نیست؛ بلکه برای اشاره به کلاس فعلی در داخل تعریف کلاس استفاده می‌شود.


تفاوت self و static در PHP

در PHP مفاهیم self و static هر دو برای دسترسی به اعضای کلاس استفاده می‌شوند، اما دقیقاً یکسان نیستند.

self به کلاسی اشاره می‌کند که متد در آن تعریف شده است.

در حالی که static در این نوع کاربرد می‌تواند از Late Static Binding استفاده کند و به کلاس فراخوانی‌کننده نهایی اشاره کند.

برای مثال:

class ParentClass
{
    public static function show()
    {
        echo static::class;
    }
}

class ChildClass extends ParentClass
{
}

ChildClass::show();

در این حالت Late Static Binding باعث می‌شود static به کلاس ChildClass اشاره کند.

این موضوع در پروژه‌های پیشرفته‌تر PHP اهمیت زیادی پیدا می‌کند.


Static Properties در PHP چیست؟

در PHP علاوه بر متدها، می‌توان Propertyها را نیز به صورت Static تعریف کرد.

Static Property به خود کلاس تعلق دارد، نه به یک Object خاص.

برای تعریف آن:

class ClassName
{
    public static $staticProp = "PHP";
}

دسترسی به Static Property

برای دسترسی به Static Property نیز از عملگر :: استفاده می‌کنیم.

مثلاً:

echo ClassName::$staticProp;

برای مثال:

class Pi
{
    public static $value = 3.14159;
}

echo Pi::$value;

خروجی:

3.14159

در اینجا برای دسترسی به $value نیازی به ایجاد Object از کلاس Pi نداریم.


تغییر مقدار Static Property

می‌توانیم مقدار یک Static Property را نیز تغییر دهیم:

class Counter
{
    public static $count = 0;
}

Counter::$count = 10;

echo Counter::$count;

خروجی:

10

اگر Property به صورت public تعریف شده باشد، از خارج کلاس نیز می‌توان به آن دسترسی داشت.


دسترسی به Static Property داخل کلاس

برای دسترسی به Static Property در داخل همان کلاس می‌توانیم از self:: استفاده کنیم:

class Counter
{
    public static $count = 0;

    public static function increase()
    {
        self::$count++;
    }
}

Counter::increase();

echo Counter::$count;

خروجی:

1

هر بار که increase() را اجرا کنیم، مقدار Static Property افزایش پیدا می‌کند.


کاربرد Static Property در شمارنده‌ها

یکی از مثال‌های مناسب برای درک Static Property، ساخت یک Counter است.

class User
{
    public static $count = 0;

    public function __construct()
    {
        self::$count++;
    }
}

new User();
new User();
new User();

echo User::$count;

خروجی:

3

در اینجا مقدار $count بین تمام Objectهای کلاس مشترک است.

این نکته تفاوت مهم Static Property با Property معمولی است.


تفاوت Property معمولی و Static Property

در Property معمولی، هر Object مقدار مستقل خودش را دارد:

class User
{
    public $name;
}

$user1 = new User();
$user2 = new User();

$user1->name = "Ali";
$user2->name = "Sara";

در اینجا هر Object مقدار متفاوتی دارد.

اما Static Property متعلق به خود کلاس است:

class User
{
    public static $count = 0;
}

و تمام Objectها با همان Property Static مشترک کار می‌کنند.


Namespace در PHP چیست؟

در پروژه‌های PHP، به خصوص پروژه‌های بزرگ، ممکن است کلاس‌ها، توابع یا Constantهای زیادی داشته باشیم.

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

فرض کنید دو کتابخانه مختلف هر دو کلاسی به نام User داشته باشند.

اگر هر دو را بدون Namespace استفاده کنیم، احتمال ایجاد تداخل وجود دارد.

برای حل این مشکل PHP مفهومی به نام Namespace ارائه کرده است.

Namespace را می‌توان مانند یک فضای نام در نظر گرفت که به ما کمک می‌کند اجزای مختلف برنامه را دسته‌بندی و از تداخل نام‌ها جلوگیری کنیم.


تعریف Namespace در PHP

برای تعریف Namespace از کلمه کلیدی namespace استفاده می‌کنیم:

<?php

namespace Html;

class Table
{
}

در این مثال کلاس Table در Namespace به نام Html قرار گرفته است.

اکنون نام کامل این کلاس:

Html\Table

خواهد بود.


Namespace تو در تو

می‌توان Namespaceها را به صورت ساختاریافته‌تر نیز تعریف کرد.

برای مثال:

namespace Code\Html;

در اینجا:

  • Code بخش اصلی Namespace است.
  • Html بخش زیرمجموعه آن است.

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

Code\Html\Table

این ساختار در پروژه‌های بزرگ برای دسته‌بندی بخش‌های مختلف برنامه بسیار مفید است.


چرا Namespace اهمیت دارد؟

فرض کنید دو کلاس با نام یکسان داریم:

namespace App\Models;

class User
{
}

و:

namespace Admin\Models;

class User
{
}

هر دو کلاس User نام دارند، اما Namespace آن‌ها متفاوت است.

بنابراین می‌توانیم هر دو را در یک پروژه داشته باشیم بدون اینکه صرفاً نام User باعث تداخل شود.

نام کامل کلاس اول:

App\Models\User

نام کامل کلاس دوم:

Admin\Models\User

استفاده از Namespace با نام کامل کلاس

اگر بخواهیم مستقیماً از یک کلاس دارای Namespace استفاده کنیم، می‌توانیم مسیر کامل آن را بنویسیم:

$table = new Html\Table();

یا:

$table = new Code\Html\Table();

در این حالت PHP از Namespace برای پیدا کردن کلاس مورد نظر استفاده می‌کند.


استفاده از use برای Namespace

در پروژه‌های بزرگ نوشتن نام کامل کلاس در تمام قسمت‌های برنامه می‌تواند طولانی باشد.

برای ساده‌تر کردن کد می‌توانیم از use استفاده کنیم.

فرض کنید کلاس زیر را داریم:

namespace App\Models;

class User
{
}

در فایل دیگری می‌توانیم بنویسیم:

use App\Models\User;

$user = new User();

در این حالت User به کلاس App\Models\User اشاره می‌کند.

این قابلیت در پروژه‌های PHP مدرن و مخصوصاً پروژه‌هایی که از Composer و کتابخانه‌های مختلف استفاده می‌کنند، بسیار رایج است.


Namespace و کتابخانه‌ها

Namespace به تنهایی «کتابخانه» نیست.

این نکته مهم است، زیرا Namespace صرفاً یک سیستم سازمان‌دهی و نام‌گذاری در PHP است.

کتابخانه می‌تواند از Namespace استفاده کند تا کلاس‌ها و اجزای مختلف خودش را سازمان‌دهی کند.

برای مثال یک کتابخانه می‌تواند ساختاری شبیه این داشته باشد:

Library
├── Http
│   ├── Request
│   └── Response
├── Database
│   ├── Connection
│   └── Query
└── Models
    └── User

و برای هر بخش Namespace مناسبی تعریف کند:

Library\Http
Library\Database
Library\Models

به همین دلیل هنگام کار با پروژه‌های حرفه‌ای PHP، Namespaceها را بسیار زیاد مشاهده خواهید کرد.


Namespace و Composer

در پروژه‌های مدرن PHP، Namespace معمولاً در کنار Composer و Autoloading استفاده می‌شود.

Composer یک ابزار مدیریت وابستگی برای PHP است و می‌تواند کلاس‌های پروژه و کتابخانه‌ها را به صورت خودکار بارگذاری کند.

برای مثال در بسیاری از پروژه‌ها از PSR-4 برای ارتباط Namespace با ساختار پوشه‌ها استفاده می‌شود.

به همین دلیل ممکن است در پروژه‌های حرفه‌ای با ساختاری مانند زیر روبه‌رو شوید:

src/
    Models/
        User.php
    Controllers/
        UserController.php

و Namespaceهای متناظر:

namespace App\Models;

و:

namespace App\Controllers;

این ساختار باعث می‌شود پروژه منظم‌تر و قابل توسعه‌تر باشد.


تفاوت namespace و use در PHP

این دو مفهوم را نباید با یکدیگر اشتباه گرفت.

namespace مشخص می‌کند کد فعلی در چه فضای نامی قرار دارد:

namespace App\Models;

اما use به ما اجازه می‌دهد یک کلاس، Interface یا Trait را با نام کوتاه‌تر یا Alias وارد محدوده فعلی کنیم:

use App\Models\User;

پس به صورت ساده:

namespace → فضای نام فعلی را مشخص می‌کند

use → کلاس یا Namespace دیگری را برای استفاده وارد می‌کند

استفاده از Alias در Namespace

می‌توانیم هنگام استفاده از یک کلاس، برای آن نام مستعار یا Alias تعیین کنیم.

برای مثال:

use App\Models\User as UserModel;

سپس:

$user = new UserModel();

این قابلیت زمانی مفید است که دو کلاس نام یکسانی داشته باشند:

use App\Models\User as AppUser;
use Admin\Models\User as AdminUser;

حالا می‌توانیم هر دو را به شکل واضح استفاده کنیم:

$appUser = new AppUser();
$adminUser = new AdminUser();

ارتباط Traits و Namespace

Traitها نیز می‌توانند داخل Namespace قرار بگیرند.

برای مثال:

namespace App\Traits;

trait Logger
{
    public function log($message)
    {
        echo $message;
    }
}

سپس در کلاس دیگری:

namespace App\Models;

use App\Traits\Logger;

class User
{
    use Logger;
}

این روش در پروژه‌های بزرگ باعث می‌شود Traitها نیز مانند سایر اجزای برنامه ساختار منظم و مشخصی داشته باشند.


جمع‌بندی

در این بخش با چهار مفهوم مهم در PHP آشنا شدیم که در پروژه‌های واقعی و حرفه‌ای کاربرد زیادی دارند.

Trait

Trait برای استفاده مجدد از کد بین چند کلاس استفاده می‌شود و به کاهش تکرار کد کمک می‌کند.

trait Logger
{
    public function log()
    {
        echo "Log";
    }
}

و سپس:

class User
{
    use Logger;
}

Static Method

Static Method متعلق به کلاس است و برای اجرای آن نیازی به ایجاد Object نداریم:

ClassName::method();

Static Property

Static Property نیز به کلاس تعلق دارد و بین Objectهای آن کلاس مشترک است:

ClassName::$property;

Namespace

Namespace برای سازمان‌دهی کد و جلوگیری از تداخل نام‌ها استفاده می‌شود:

namespace App\Models;

و می‌توان با use از کلاس‌های موجود در Namespaceهای دیگر استفاده کرد:

use App\Models\User;

درک صحیح این چهار مفهوم به شما کمک می‌کند ساختار پروژه‌های PHP را بهتر درک کنید. در پروژه‌های بزرگ، این مفاهیم در کنار OOP، Composer، Autoloading، PSR-4 و معماری مناسب نرم‌افزار نقش مهمی در ایجاد کدهای تمیز، قابل نگهداری و قابل توسعه دارند.

پیشنهاد ویژه:آموزش رایگان PHP

اشتراک گذاری :
مریم گوهرزاد
نویسنده

مریم گوهرزاد

مدرس و بنیانگذار هلدینگ آرتا رسانه. برنامه نویس و محقق حوزه بلاکچین و توسعه‌دهنده بلاکچین | متخصص Solidity | توسعه‌دهنده وب Full-Stackمن یک توسعه‌دهنده علاقه‌مند به فناوری‌های بلاکچین و وب ۳ هستم و در زمینه Solidity، توسعه قراردادهای هوشمند و ساخت برنامه‌های غیرمتمرکز (DApps) فعالیت می‌کنم. تمرکز من بر طراحی و پیاده‌سازی راهکارهایی است که در کنار بهره‌گیری از فناوری‌های نوین، عملکرد مناسب، امنیت و تجربه کاربری مطلوبی ارائه دهند.در کنار تخصص در حوزه بلاکچین، به عنوان یک توسعه‌دهنده وب Full-Stack نیز در طراحی و توسعه وب‌سایت‌ها و برنامه‌های تحت وب فعالیت می‌کنم. تجربه کار در بخش‌های Front-End و Back-End به من این امکان را داده است که پروژه‌ها را از مرحله ایده و طراحی تا توسعه و اجرا، با دیدی جامع دنبال کنم. در فرآیند توسعه، توجه ویژه‌ای به کدنویسی تمیز، معماری مناسب، طراحی واکنش‌گرا، عملکرد مطلوب و تجربه کاربری روان دارم.

TEST_VALUE_123
https://t.me/artarasaneh
tel:09193426251
https://wa.me/+989193426251
https://instagram.com/artarasaneh_com