آموزش شی گرایی در PHP

آموزش شی گرایی در PHP

در این قسمت از آموزش های پی اچ پی، به آموزش شی گرایی در PHP خواهیم پرداخت. شما با کلیه مفاهیم OOP و نحوه تعریف و استفاده از کلاس، متد و وراثت در PHP آشنا خواهید شد.

در این مقاله با روش استفاده از this در برنامه آشنا می گردید. همین طور روش کار access modifier ها و نحوه استفاده از ثابت ها و متدهای آماده را می آموزید. همچنین با انواع دسترسی به متغیرهای و متدهای یک کلاس و مفاهیم namespace آشنا خواهید شد. ضمن اینکه با روش کار کلاس های abstract و interface و متدهای زنجیره ای آشنا می گردید. هدف ما در این آموزش، پوشش کلیه مفاهیم شی گرایی و نحوه استفاده از مفاهیم آن در ساختار برنامه می باشد. سعی شده است برای درک کامل از مفاهیم شی گرایی، مثال های کاملی از نحوه استفاده از آن در php، ارائه گردد.

بخش اول: آشنایی با مفاهیم شی گرایی در برنامه نویسی

می توان گفت که برنامه نویسی شی گرا یا همان Object-Oriented Programming یک سبک یا الگو نوشتن کدهاست که به توسعه دهنده ها اجازه می دهد تا بخش های مشابه به هم را در مفهومی به اسم کلاس، گروه بندی نمایند. در واقع این گروه بندی به برنامه نویسان کمک خواهد کرد که به مفهوم DRY یا همان Don’t Repeat Yourself بیشتر نزدیک گردند، مضاف بر اینکه کدهای آن ها بسیار برای نگهداری و تغییر راحت تر خواهد بود. (به این مفهوم easy-to-maintain گفته می شود). یکی از مهم ترین مزیت های این سبک از کدنویسی، این می باشد که اگر قرار باشد تکه ای از داده های برنامه شما تغییر یابد، عموما لازم است فقط یک بخش در کدهای شما بروز رسانی گردد.

مهم ترین مزیت برنامه نویسی شی گرا که باعث شده است اکثر developer ها از آن استفاده کنند، شبیه سازی برنامه نویسی با مفاهیم دنیای واقعی است. در واقع شما مفاهیم بسیار پیچیده برنامه نویسی را می توانید با مفاهیم شی گرایی به مفاهیم جاری و قابل فهم در دنیا واقعی تبدیل کنید. همین طور که می دانید، اطراف ما از اشیا مختلف تشکیل یافته است. هر کدام از آن ها خواصی دارند که آن را از شی مشابه خود، متفاوت می کنید. ضمن اینکه ارتباط بین اشیا با فرستادن پیام بین همدیگر برقرار می گردد. هر شی می تواند رفتاری از خود بروز دهد که مختص همان شی می باشد. این مفاهیم باعث شد تا همه این ها در مفاهیم برنامه نویسی شی گرا استفاده شود.

در ادامه به طور مفصل در این مورد صحبت خواهد شد. در واقع برنامه نویسی با پیروی از الگوهای OOP ، یک روش برنامه نویسی است که در آن برنامه نویس، تمامی متغیرها و توابعی که بهم مرتبط می باشند را در قالب یک کلاس منفرد، سازمان دهی می کند.

مثل تمام الگوهای برنامه نویسی، این الگو نیز مزایا و معایب خود را دارد که در نوشتار نمی گنجد. به صورت کوتاه می توان گفت ممکن است که در پروژه های ساده و همین طور در سایز کوچک، استفاده از الگوی Procedural که بر مبنای استفاده از توابع می باشد، به صرفه تر باشد. اما در پروژه هایی با ابعاد بزرگ و پیچیده، مسلما باید از الگوهای شی گرا در برنامه نویسی استفاده کرد چرا که هم باعث کاهش پیچیدگی کدها در آینده خواهد شد و هم نگهداری و عیب یابی آن به مراتب ساده تر از روش های برنامه نویسی رویه ای می باشد.

مهم ترین مفاهیم الگو شی گرایی عبارتند از:

  • کلاس – Class
  • شی – Object
  • خاصیت ها و رفتار کلاس – Property and Method
  • ارث بری یا وراثت – Inheritance
  • چند ریختی – Polymorphism
  • کپسوله سازی – Encapsulation

درک مفهوم کلاس و شی

قبل از اینکه وارد مفاهیم عمیق تر در شی گرایی گردیم، ابتدا درک مفاهیم کلاس و اشیا بسیار حیاتی می باشد. دو مفهوم class و object ، مفاهیمی هستند که خیلی اوقات اشتباه توسط توسعه دهندگان به کار برده می شوند.

OOP in PHP

همانگونه که در عکس می بینید، یک کلاس می تواند یک طرح کلی و اولیه از یک خانه باشد (یا همان blueprint). در واقع کلاس شکل و اندازه های بخش های مختلف یک خانه را بیان می کند. اما در واقع این طرح کلی درواقع یک خانه واقعی نیست. در واقع این پلن اولیه، اصلا وجود خارجی ندارد، اما کاملا مشخص می کند که یک خانه چطور و چگونه باید ساخته شود. در این طرح اولیه به صورت کامل شرح داده می شود که یک خانه چه اجزایی دارد و آن ها چگونه باید در داخل خانه جایگذاری گردند.

در واقع یک شی، یک نمونه واقعی از خانه ساخته شده یا خانه ای است که از روی نقشه خانه ساخته شده است. این خانه دقیقا از روی نقشه تهیه شده و اجزا آن کاملا مانند blueprint اولیه می باشد. در واقع داده هایی که در این شی یا خانه واقعی ذخیره شده، همان چوب و سنگ و سیمان و سیم ها و سایر اجزایی است که یک خانه از آن ها تشکیل شده است. این اجزا بدون قرار گرفتن در کنار هم و اسمبل نشدن، در واقع مفهمومی به نام خانه را به وجود نخواهند آورد.

تعریف کلاس و اشیا

کلاس ها ساختارهای داده ها و عملیات مرتبط به آن ها را گروه بندی می کنند و می توان از اطلاعات یک کلاس برای ساختن اشیا استفاده کرد.

اشیا در واقع نمونه های واقعی از یک کلاس هستند که دارای خواص آن کلاس به همراه داده های واقعی و همین طور رفتار مشابه با کلاس هستند.

بخش دوم: تعریف کلاس در PHP و ساخت اشیا

اولین بخش از مفاهیم شی گرایی در PHP شامل نحوه تعریف کلاس، اشیا و نحوه دسترسی به آن ها می باشد. در این قسمت ابتدا به تعریف کلاس در پی اچ پی اشاره خواهیم کرد. سپس می آموزیم که چگونه یک شی از آن ساخته می شود. پس از آن به تعریف متد و خاصیت های یک کلاس و سطح دسترسی به آن ها خواهیم پرداخت. در ادامه به تشریح متدهای جادویی و بخش مهم آن یعنی متد construct خواهیم پرداخت.

تعریف کلاس و ساخت شی از آن درPHP

برای تعریف یک کلاس باید از کلمه کلیدی class  به صورت زیر استفاده کنیم. برای ساخت شی از آن کافی است کلمه کلیدی new استفاده کنیم:

class MyClass
{
   // Class properties and methods
}
$obj = new MyClass;

 

تعریف متد ها و خاصیت های کلاس در PHP

برای اینکه با ساختار تعریف متد و همین طور خواص در یک کلاس آشنا شوید، از یک مثال استفاده کرده ایم. در ادامه توضیحات مربوط به این مثال آمده است. در صورتی که کدهای زیر را در یک فایل با پسوند php ذخیره کنید، می توانید به راحتی آن را در localhost خود و با استفاده از WAMP یا XAMP یا MAMP به اجرا در آورید.

//create a class
class MsnCar {

    //define properties in a class
    public $name;
    private $wheel_count = 4;
    private $door_count;
    protected $company;
    public static $car_static_property = "This is for parent class";

    /**
     * MsnCar constructor.
     * @param $name
     * @param int $wheel_count
     */
    public function __construct($name=null, $wheel_count=null)
    {
        $this->name = $name;
        $this->wheel_count = $wheel_count;
    }


    //Sample of setter and getters
    /**
     * @return mixed
     */
    public function getCompany()
    {
        return $this->company;
    }

    /**
     * @param mixed $company
     */
    public function setCompany($company)
    {
        $this->company = $company;
    }


    /**
     * @return int
     */
    public function getWheelCount()
    {
        //Sample of property referencing in a class
        return $this->wheel_count;
    }

    /**
     * @param int $wheel_count
     */
    public function setWheelCount($wheel_count)
    {
        $this->wheel_count = $wheel_count;
    }

    /**
     * @return mixed
     */
    public function getDoorCount()
    {
        return $this->door_count;
    }

    /**
     * @param mixed $door_count
     */

    public function setDoorCount($door_count)
    {
        $this->door_count = $door_count;
    }


    //create a method in a class
    function moving() {
        echo "Now I'm moving So fast".'<br>';
    }

    function stopping() {
        echo "Now I'm stopping so well".'<br>';
    }

    //using properties object inside a class method
    function car_details() {
        echo $this->name.' has '.$this->wheel_count.' wheels and '.$this->door_count.' doors';
    }

    //Sample of static method to referencing parent class
    public static function car_reference()
    {
        echo self::$car_static_property.'<br>';
        //sample of late static binding
        echo static::$car_static_property.'<br>';

    }

}


//Instantiating of a class
$bmw = new MsnCar();
$benz = new MsnCar();
$myPride = new Pride();

//Invoke property of an object
echo $bmw->getWheelCount()."<br>";

//change or assign new value to object properties
$bmw->setDoorCount(2);
$benz->setWheelCount(6);
echo $bmw->getDoorCount()."<br>";
echo $benz->getWheelCount()."<br>";

تعریف خصوصیات کلاس در PHP :

Property یا خاصیت برای تعریف متغیر یا اضافه کردن داده به یک کلاس مورد استفاده قرار می گیرد. همان طور که می بینید برای تعریف Property در یک کلاس از همان تعریف متغیر در پی اچ پی استفاده می شود، تنها تفاوت آن این می باشد که هر متغیر دارای یک سطح دسترسی است که جلوتر در مورد آن توضیح داده خواهد شد.

توجه داشته باشید که برای تعریف یک ثابت، می توانید از کلمه کلیدی const استفاده کنید. همان طور که می بینید، کلاسی با نام MsnClass تعریف شده است که دارای Property ها (یا خاصیت هایی) به شکل زیر می باشد:

//define properties in a class
public $name;
private $wheel_count = 4;
private $door_count;
protected $company;
public static $car_static_property = "This is for parent class";
const site="wpwebmaster.ir";

تعریف متد های کلاس در PHP :

Method ها در واقع توابعی هستند که در یک کلاس تعریف شده و مورد استفاده قرار می گیرند. همان طور که می بینید برای تعریف متد در یک کلاس از همان تعریف تابع درPHP استفاده می شود، تنها تفاوت آن این می باشد که هر متد دارای یک سطح دسترسی است که جلوتر در مورد آن توضیح داده خواهد شد. همان طور که می بینید، کلاسی با نام MsnClass تعریف شده است که دارای متدهایی به شکل زیر می باشد:

//create a method in a class
    public function moving() {
        echo "Now I'm moving So fast".'<br>';
    }

    public function stopping() {
        echo "Now I'm stopping so well".'<br>';
    }

روش استفاده از متغیرها و متدهای یک کلاس در PHP :

برای استفاده از متغیرها و متدهای یک کلاس حتما باید یک نمونه یا شی از آن کلاس ساخته شود تا بتوان متغیر های آن کلاس را مقداردهی کرده و از متدهای آن استفاده کرد. (این مورد در مورد متغییر و متد استاتیک صادق نیست). نحوه مقدار دهی به متغیرها، همانند مقداردهی به متغیر در php می باشد، با این تفاوت که باید حتما از نام شی و سپس علامت class accessor و سپس نام متغیر استفاده نمود. برای توابع یا متد هم به همین صورت می باشد و اینکه حتما باید از () و در صورت نیاز، تعریف پارامترهای ارسالی به تابع، استفاده کرد.

//Instantiating of a class
$bmw = new MsnCar();
$benz = new MsnCar();
$myPride = new Pride();

//Invoke property of an object
echo $bmw->getWheelCount()."<br>";
echo $myPride->moving();

متغیر استاتیک و متد استاتیک در یک کلاس :

در صورتی که یک متغیر یا یک کلاس در بین همه اعضای یک کلاس مشترک باشد، آن را از نوع استاتیک تعریف می کنند. برای استفاده از یک متغیر استاتیک لازم نیست که حتما شی ای از کلاس ساخته شود. متغیر استاتیک و همین طور متدهای استاتیک با استفاده از نام کلاس و استفاده از علامت Scope Resolution یا همان :: فراخوانی می شوند.

//create a class
class MsnCar {
    //define car static property in a class
    public static $car_static_property = "This is a static property";

  
    //Sample of static method in a class
    public static function moving()
    {
        echo "Hey, I'm moving!<br>";
    }
}

echo MsnCar::car_static_property;
MsnCar::moving();

 

نکته مهم در مورد متغیرها و متدهای استاتیک

برای استفاده از متغیرها و متدهای یک کلاس، حتما می باید یک شی از آن ساخته شود اما برای استفاده از متغیر یا متد استاتیک، نیازی به ساخت یک شی نیست. در واقع برای بار اول که کلاس در حافظه ایجاد می گردد یا instantiate می گردد، شما به راحتی می توانید از نام کلاس به همراه علامت scope resolution برای استفاده از آن بهره ببرید.

از آنجایی که ساخت شی در حافظه مستلزم صرف حافظه می باشد، لذا استفاده از متغیرها و متدهای استاتیک، باعث کاهش چشمگیر حافظه مصرفی برنامه شما خواهد شد. عموما متغیرهای استاتیک ساخته شده به عنوان counter یا شمارنده مورد استفاده قرار می گیرند. همچنین متدهای استاتیک، عموما در کلاس های utility یا کمکی که جهت سرویس دهی به کلاس اصلی می باشند، ساخته می شوند.

تعریف سطح دسترسی به متدها و متغیرهای کلاس در PHP

شما می توانند برای متغیر ها و متدهای کلاس خود، تعریف کنید که چگونه قابل دسترس باشند. در واقع شما می توانید قابلیت دیده شدن آن ها در خارج و داخل کلاس را تعریف نمایید.

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

همچنین اکثر متدها برای دسترسی به رفتار یک شی، به صورت عمومی تعریف می شوند. البته این بسته به نوع طراحی کلاس، ممکن است متفاوت باشد. بر مبنای همین موارد، سه نوع دسترسی به متغیر ها و متدهای یک کلاس در پی اچ پی، تعریف شده است. شما با استفاده از کلمات کلیدی زیر، می توانید نحوه دسترسی به یک کلاس را کنترل نمایید:

سطح دسترسی به صورت public :

در این نوع سطح دسترسی، متد یا متغیری که با public مشخص می شود، قابل دسترس برای همه (چه از داخل کلاس و چه از خارج کلاس) خواهد بود.

سطح دسترسی به صورت private :

در این حالت فقط می توان به متغیر یا متد از داخل کلاس دسترسی داشت. دسترسی از خارج کلاس به متد یا متغیر از نوع private امکان پذیر نیست.

سطح دسترسی به صورت protected :

در حالت protected کلاس و همین طور سایر کلاس هایی که از کلاس جاری ارث برده اند (یا همان کلاس های فرزند) می توانند به متغیر یا متدی با این نوع دسترسی داشته باشند.

class MsnCar {
    //define properties in a class with several visibility
    public $name;
    private $wheel_count = 4;
    private $door_count;
    protected $company;
    public static $car_static_property = "This is for parent class";

    //Define access modifier for functions
    public static function moving() {
        echo "Now I'm moving So fast".'<br>';
    }
}

همانگونه که بیان شد، برای جلوگیری از دسترسی مستقیم به داده های یک کلاس یا چک کردن آن ها مثلا پیش از ذخیره در دیتابیس، عموما متغیرهای یک کلاس را از نوع private تعریف می کنند. از آنجایی که نمی توان به صورت مستقیم به این متغیر ها دسترسی داشت، باید تابع هایی به عنوان Getter و Setter تعریف نمود تا بتوان متغیرها را از داخل کلاس فراخوانی نمود و یا مقدار آن ها را تغییر داد.

چرا باید از سطوح دسترسی در شی گرایی PHP استفاده کنیم؟

در واقع با این کار، کلیه اعمال ویرایشی که از خارج کلاس بخواهد بر روی اعضای داخل کلاس (متدها و خاصیت ها) اعمال گردد را، محدود می کنیم. در واقع فقط متدهای تعریف شده یک کلاس اجازه دسترسی و تغییرات بر روی اعضای یک کلاس را دارند.

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

برای دسترسی در داخل کلاس به متغیر ها و متدهای شی ساخته از آن کلاس از کلمه کلیدی با نام this و همین طور علامت <- استفاده می شود. مفاهیم گفته شده به صورت خلاصه در زیر تشریح شده است:

  1. استفاده از کلمه کلیدی this برای دسترسی به متدها به متغیرهای یک شی در داخل کلاس:برای دسترسی به متغیرهای داخل یک کلاس توسط متدها یا سایر متغیرهای یک شی، از کلمه کلیدی this استفاده می شود.
  2. استفاده از کلمه کلیدی self برای دسترسی به متدها به متغیرهای استاتیک در داخل کلاس:از آن جایی که متغیر استاتیک متعلق به یک object می باشد، لذا در متدهای استاتیک یا سایر متدها، برای دسترسی به آن نمی توان از کلمه کلیدی this استفاده کرد. در این موارد برای دسترسی به متدها یا خواص استاتیک در متدهای معمولی، از کلمه کلیدی self به همراه علامت Scope Resolution استفاده می کنیم.
  3. استفاده از تابع getter برای دسترسی به متغیر از نوع private در کلاس :برای دسترسی به محتوا یا داده داخل یک متغیر از نوع private از توابع getter استفاده می شود. این تابع ها دارای سطح دسترسی public بوده و با ساخته شدن یک شی، از داخل کلاس به محتوای private دسترسی پیدا می کنند. برای خوانایی بهتر کدهای برنامه، این توابع عموما با نام get آغاز می شوند.
  4. استفاده از تابع setter برای تغییر مقدار متغیر از نوع private در کلاس :برای تغییر دادن محتوا یا داده داخل یک متغیر از نوع private از توابع setter استفاده می شود. این تابع ها دارای سطح دسترسی public بوده و با ساخته شدن یک شی، از داخل کلاس به محتوای داده private دسترسی پیدا می کنند. برای خوانایی بهتر کدهای برنامه، این توابع عموما با نام set آغاز می شوند.
//Sample of setter and getters
   /**
    * @return mixed
    */
   public function getCompany()
   {
       return $this->company;
   }

   /**
    * @param mixed $company
    */
   public function setCompany($company)
   {
       $this->company = $company;
   }

   /**
    * @return int
    */
   public function getWheelCount()
   {
       //Sample of property referencing in a class
       return $this->wheel_count;
   }

   /**
    * @param int $wheel_count
    */
   public function setWheelCount($wheel_count)
   {
       $this->wheel_count = $wheel_count;
   }

   /**
    * @return mixed
    */
   public function getDoorCount()
   {
       return $this->door_count;
   }

   /**
    * @param mixed $door_count
    */
   public function setDoorCount($door_count)
   {
       $this->door_count = $door_count;
   }
   
   //using properties object inside a class method
   function car_details() {
       echo $this->name.' has '.$this->wheel_count.' wheels and '.$this->door_count.' doors';
   }

متدهای زنجیره ای و روش استفاده از آن در شی گرایی در PHP

گاهی اوقات ممکن است نیاز داشته باشیم که کلاس را خلاصه تر پیاده سازی کرده و همین طور سرعت مقداردهی در آن را بالاتر ببریم. در اینگونه موارد، برای مقداردهی یک property، از چندین متد در داخل کلاس استفاده کرده و آن ها را به صورت زنجیره ای فراخوانی می کنیم. در همه توابع یک return داریم در انتها و در متد آخر، مقدار برگشتی را echo می کنیم.

class Person{
  protected $person_info;
  public function name($value){
    $this->person_info .= "Name: $value, ";
    return $this;
  }
  public function family($value){
    $this->person_info .= "Family: $value, ";
    return $this;
  }
  public function birthday($value){
    echo $this->person_info .= "Born on: $value";
  }
}
$amirhosein = new Person();
$amirhosein->name("amirhosein")->family("lashgari")->birthday("1-1-1");
/*
 * The result is:
 * Name: amirhosein, Family: lashgari, Born on: 1-1-1
 * */

متدهای زنجیره ای با استفاده از ترکیب متدهای استاتیک در PHP

شما می توانید برای پیاده سازی متدهای زنجیره ای، از ترکیب متدهای استاتیک استفاده نمایید. فقط داخل بدنه باید به جای this از کلمه کلیدی self استفاده کنیم. توجه کنید که در مثال قبل، برای مقدار بازگشتی از کلمه this استفاده می کردیم اما در اینجا از new static استفاده می کنیم.

<?php
class Person{
  static protected $person_info;

  static public function name($value){
    self::$person_info .= "Name: $value, ";
    return new static;
  }

  public function family($value){
        self::$person_info .= "Family: $value, ";
    return new static;
  }
  public function birthday($value){
    echo self::$person_info .= "Born on: $value";
  }

}

$amirhosein = new Person();
$amirhosein->name("amir")->family("hosein")->birthday("1-1-1");

/*
 * The result is:
 * Name: amir, Family: hosein, Born on: 1-1-1
 * */

متدهای جادوی و روش استفاده از آن در شی گرایی در PHP

جهت ساده سازی کار برنامه نویسان برای ساختن و استفاده کردن از object های ساخته شده از class ها، چندین متد با نام های متدهای جادویی در زبان PHP قرار داده شده است. این متدها با علامت __ (دو underline به صورت پشت سرهم) شروع می شوند. معمولا قبل از متدهای جادویی از محدود کننده های سطوح دسترسی مثل public یا private یا protected استفاده نمی شود.

متدهای جادویی در که در شی گرایی در php استفاده می شوند، به شرح زیر می باشند:

__construct
__destruct
__call
__callStatic
__get
__set
__isset
__unset
__sleep
__wakeup
__toString
__invoke
__set_state
__clone

نحوه استفاده از متد جادویی construct در PHP:

زمانی که تصمیم داشته باشیم برای ساخت یک شی از یک کلاس، عملیات خاص یا مقداردهی خاصی را انجام دهیم، از این متد استفاده می شود. به این متد سازنده یا constructor نیز گفته می شود. عموما برنامه نویسان برای مقدار دهی اولیه به متغیرها (برای جلوگیری از null بودن مقادیر آن ها) از این متد استفاده می کنند. توجه داشته باشید همین که یک object جدید از یک کلاس ساخته می شود، این متد به صورت اتوماتیک فراخوانی می شود.

در داخل متد construct نمی توان از return نیز استفاده کنید. در واقع هرگاه که بخواهیم دقیقا پس از ساخته شدن شی، کاری را روی آن انجام دهیم، از متد جادویی construct استفاده می کنیم.

//create a class
class MsnCar {
    //define properties in a class
    public $name;
    private $wheel_count = 4;
    private $door_count;
    protected $company;
    public static $car_static_property = "This is for parent class";
    /**
     * MsnCar constructor.
     * @param $name
     * @param int $wheel_count
     */
    public function __construct($name="Jeep", $wheel_count=4)
    {
        $this->name = $name;
        $this->wheel_count = $wheel_count;
    }
}

$dodge = new MsnCar("Dodge",6);

نحوه استفاده از متد جادویی destruct  در PHP:

این مفهوم دقیقا عکس مفهوم construct عمل می کند، در واقع متد destruct دقیقا قبل از اینکه شی ساخته شده از یک کلاس از بین برود، فراخوانی می شود. این متد با عنوان Destructor یا از بین برنده برای object ساخته شده از یک کلاس، شناخته می شود. این متد دقیقا پیش از زمانی که شی در حافظه destroy می شود (یا از بین می رود) به اجرا در می آید. به عنوان مثال زمانی که شما متد unset را بر روی یک شی فراخوانی می کند، این متد پیش از unset کردن شی مورد نظر، اجرا می گردد.

در نظر داشته باشید متد جادویی destruct نمی تواند پارامتر ورودی دریافت نماید

class MsnCar
{
    function __construct()
    {
        echo 'It is called after creating an object from the class';
    }

    function __destruct()
    {
        echo 'It is called before destroying an object from the class';
    }
}

نحوه استفاده از متد جادویی tostring  در PHP:

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

نحوه استفاده از متد جادویی get و set  در PHP:

این متد بر روی یک object زمانی فراخوانی می شود، که بخواهیم مقداری را به یک property بر روی شی نسبت دهیم که این خاصیت یا در کلاس مربوط به آن شی وجود ندارد یا دسترسی لازم برای تغییر آن برای ما وجود ندارد. در واقع از این متد می توان برای ساختن متغیر داینامیک برای یک کلاس بهره برد. متد جادویی set دو آرگومان ورودی می پذیرد که اولی نام خاصیت مورد نظر و دومی مقداری می باشد که قرار است به آن اختصاص دهیم.

برای دسترسی به متغیرهایی که با روش بالا و استفاده از متد جادویی set تعریف شده اند، می توانید از متد جادویی get استفاده نمایید. در واقع این متد زمانی فراخوانی می شود که ما بخواهیم به یک property دسترسی داشته باشیم که یا در کلاس ما وجود ندارد و یا سطح دسترسی به آن وجود ندارد. به عنوان مثال ما می توانیم از بیرون کلاس با متد جادویی get خاصیت هایی از کلاس که به صورت private تعریف شده اند را نمایش بدهیم.

نحوه استفاده از متد جادویی isset  در PHP:

برای چک کردن اینکه property های غیر قابل دسترس در یک کلاس یا همان متغیرهای private، یا property هایی که به صورت داینامیک در کلاس تعریف شده اند، مقداردهی اولیه شده اند، از متد جادویی isset استفاده می کنیم.

نحوه استفاده از  متد جادویی unset  در PHP:

برای unset کردن property هایی که در قسمت های قبل صحبت کردیم از متد جادویی unset در php استفاده می کنیم.

نحوه استفاده از  متد جادویی clone  در PHP:

زمانی که بخواهیم از یک شی با استفاده از روش copy by value یک کپی تهیه کنیم، از متد جادویی clone استفاده می کنیم.

class MsnCar
{
    //define properties in a class
    public $name;
    protected $company;
    private $wheel_count = 4;
    private $door_count;
    /**  Location for overloaded data.  */
    private $data = array();
    public function __construct($name, $door_count, $company)
    {
        $this->name = $name;
        $this->door_count = $door_count;
        $this->company = $company;
    }
    public function __toString()
    {
        return 'The name of car is ' . $this->name . ' and from ' . $this->company . ' with ' . $this->door_count . ' doors!!!<br>';
    }
    public function __get($name)
    {
        echo "Getting '$name':";
        if (array_key_exists($name, $this->data)) {
            return $this->data[$name].'<br>';
        }
        $trace = debug_backtrace();
        trigger_error(
            'Undefined property via __get(): ' . $name .
            ' in ' . $trace[0]['file'] .
            ' on line ' . $trace[0]['line'],
            E_USER_NOTICE);
        return null;
    }
    public function __set($name, $value)
    {
        echo "Setting '$name' to '$value'<br>";
        $this->data[$name] = $value;
    }
    /**  As of PHP 5.1.0  */
    public function __isset($name)
    {
        echo " Is '$name' set? ";
        return isset($this->data[$name]).'<br>';
    }
    /**  As of PHP 5.1.0  */
    public function __unset($name)
    {
        echo "Unsetting '$name'<br>";
        unset($this->data[$name]);
    }
}
$Benz = new MsnCar('Benz', 6, 'Benz Co.');
echo $Benz;
$Benz->color = 'Blue';
echo $Benz->color;
var_dump(isset($Benz->color));
unset($Benz->color);
echo $Benz->color;
/* Return of your code is in the following: 
The name of car is Benz and from Benz Co. with 6 doors!!!
Setting 'color' to 'Blue'
Getting 'color':Blue
Is 'color' set? bool(true) Unsetting 'color'
Getting 'color':
Notice: Undefined property via __get(): color in C:\wamp64\www\PHP\00\oop\MsnCar.php on line 75 in C:\wamp64\www\PHP\00\oop\MsnCar.php on line 44
*/

نحوه استفاده از  متد جادویی call  در PHP:

زمانی که بخواهیم متدهایی به صورت داینامیک برای یک شی بسازیم که در کلاس سازنده آن وجود ندارد، از متد جادویی call استفاده می کنیم. متد جادویی call دو پارامتر ورودی دریافت می کند، اولی نام تابع داینامیکی است که می خواهیم آن را ایجاد کنیم. دومی آرایه ای از پارامترهایی است که می خواهیم به آن پاس بدهیم.

یک مثال بسیار کاربردی در این زمینه:  فرض کنید شما یک کلاس برای کار با database خود دارید. شما می خواهید در داخل کلاس متدهایی داشته باشید که بتوانید با استفاده از آن، یک فیلد با یک مقدار مشخص در دیتابیس را جستجو کنید.

یک روش این می باشد، که برای هر فیلد داخل دیتابیس، یک متد با نام مثلا findByID و الی آخر بنویسید. مسلما اگر تعداد فیلدهای جدول شما بسیار زیاد باشد، این کار عملا روش کارایی محسوب نمی گردد.

بهترین راه برای انجام این کار استفاده از متد جادویی call  می باشد که می توانید با آن متدهای داینامیک برای جستجو در داخل جدول خود بر اساس نام فیلد درست کنید. مثال زیر این امر را نمایش می دهد.

class Database
{
    function __call($method, $args)
    {
        $field = strtolower(substr($method, 6, strlen($method) - 6));
        $sql = "SELECT * FROM $this->table WHERE $field = $args[0] ";
        return $this->db->query($sql);
    }
}

$db = new Database();

$db->findByID(28);

$db->findByName('amirhosein');

نحوه استفاده از  متد جادویی callstatic  در PHP:

این متد زمانی اجرا می شود که می خواهیم به متد استاتیکی از کلاس دسترسی داشته باشیم که قابلیت دسترسی به آن وجود ندارد (یا تعریف نشده یا سطح دسترسی به آن private تعریف شده است)

class MethodTest
{
    public function __call($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling object method '$name' "
             . implode(', ', $arguments). "<br>";
    }

    public static function __callStatic($name, $arguments)
    {
        // Note: value of $name is case sensitive.
        echo "Calling static method '$name' "
             . implode(', ', $arguments). "<br>";
    }
}

$obj = new MethodTest;
$obj->runTest('in object context');

MethodTest::runTest('in static context');  

/* The reuslt is something like this in the following: 

Calling object method 'runTest' in object context
Calling static method 'runTest' in static context
*/

نحوه استفاده از  متد جادویی sleep  در PHP:

زمانی که بخواهیم روی یک شی تابع serialize را فراخوانی کنیم، متد جادویی sleep اجرا خواهد شد. هنگامی که شما یک کلاس را serialize می کنید، همه متغیر ها و اعضای آن کلاس به صورت serialize شده، ذخیره می گردند. اما گاهی نیاز نیست که همه متغیرها را نگهداری کنیم. در این گونه موارد از متد جادویی sleep استفاده خواهیم کرد. در واقع داخل متد جادویی sleep می توانیم مشخص کنیم که چه متغیر هایی از کلاس اجازه serialize شدن را دارند.

نحوه استفاده از  متد جادویی wakeup  در PHP:

متد جادویی wakeup در زمان فراخوانی تابع unserialize اجرا می شود و نحوه عملکرد آن مانند sleep برای فراخوانی تابع serialize است.

class Connection
{
    protected $link;
    private $dsn, $username, $password;
    
    public function __construct($dsn, $username, $password)
    {
        $this->dsn = $dsn;
        $this->username = $username;
        $this->password = $password;
        $this->connect();
    }
    
    private function connect()
    {
        $this->link = new PDO($this->dsn, $this->username, $this->password);
    }
    
    public function __sleep()
    {
        return array('dsn', 'username', 'password');
    }
    
    public function __wakeup()
    {
        $this->connect();
    }
}

بخش سوم: ارث بری در PHP

در این بخش می خواهیم به مفاهیم وراثت در php بپردازیم. در ابتدا نیاز داریم که با مفهوم ارث بری و اینکه چرا در شی گرایی، به ارث بری نیاز داریم بپردازیم. می توان گفت از مهم ترین مزیت هایی که باعث می شود به سمت برنامه نویسی شی گرا سوق پیدا کنیم، کاهش نوشتن خط های اضافی کد و کم شدن کدهای تکراری در برنامه ما می باشد. در واقع ما با استفاده از وراثت به مقدار بسیار زیادی از code duplication جلوگیری خواهیم کرد.

در مفهوم Inheritance از یک کلاس والد یا همان parent class استفاده می شود. این کلاس دارای تعدادی خواص و رفتارهای مربوط به خود می باشد. حال کلاسی به نام کلاس فرزند یا child class تعریف می گردد که می تواند تمامی خواص و رفتارهای کلاس والد خود را به ارث ببرد. علاوه بر این، کلاس فرزند می تواند property ها و method های جدیدی برای خودش داشته باشد که در کلاس والد وجود ندارد. همچنین کلاس فرزند می تواند رفتار و خواص متد والد خود را مورد بازنویسی قرار دهد (در ادامه به آن به صورت کامل اشاره خواهد شد).

در واقع مفهوم وراثت این امکان را به ما می دهد که کدهای خود را تنها یک بار در کلاس پدر نوشته و به کرات آن را در کلاس های فرزندی که از کلاس پدر ارث برده اند، استفاده نماییم. در مورد وراثت و نحوه ارث بری در PHP نکاتی وجود دارد که در زیر به آن ها اشاره شده است:

نحوه ارث بری یک کلاس از کلاس دیگر در PHP:

برای اینکه بتوانید از یک کلاس ارث ببرید، کافی است از کلمه extends به صورت زیر استفاده نمایید. در نظر داشته باشید، با ارث بردن از کلاس والد، کلیه خاصیت ها و متدهای کلاس parent برای کلاس child هم به همان شکل تعریف می گردند.

class MsnCar
{
    //define properties in a class
    public $name;
    protected $company;
    private $wheel_count = 4;
    private $door_count;
    public function __construct($name, $door_count, $company)
    {
        $this->name = $name;
        $this->door_count = $door_count;
        $this->company = $company;
    }
    public function moving()
    {
        echo "Now I'm moving So fast" . '<br>';
    }
    public function stopping()
    {
        echo "Now I'm stopping so well" . '<br>';
    }
    public function __toString()
    {
        return 'The name of car is ' . $this->name . ' and from ' . $this->company . ' with ' . $this->door_count . ' doors!!!<br>';
    }
}

//Inheritance in PHP
class benz extends MsnCar
{
    public $wings_number;
    public function __construct($name, $door_count, $company)
    {
        parent::__construct($name, $door_count, $company); // Call the parent class's constructor
        echo "A new constructor in " . __CLASS__ . ".<br />";
    }
    public function flying()
    {
        echo "You can fly with " . __CLASS__ . ".<br />";
    }
    public function __toString()
    {
        $temp = parent::__toString();
        return $temp.' This benz has '.$this->wings_number.' wings';
    }
}

// Create a new object
$newobj = new benz('benz', 6, 'benz Co.');

// Define and use new method in child class
echo $newobj->flying();

// Use a method from the parent class
$newobj->moving();

// Use a method from the parent classg
$newobj->stopping();

// Define and use new property in child class
$newobj->wings_number = 2;

// Use a method from the parent class and ovverriding parent method in child method
echo $newobj;


/* The result is: 
A new constructor in benz.
You can fly with benz.
Now I'm moving So fast
Now I'm stopping so well
The name of car is benz and from benz Co. with 6 doors!!!
This benz has 2 wings
*/

سطوح دسترسی در استفاده از وراثت در PHP

همانند آنچه در قبل تر در مورد سطح دسترسی ها در PHP بیان شد، کلاس فرزند می تواند تنها به متد ها و property هایی با سطح دسترسی public و protected در کلاس پدر خود دسترسی داشته باشد. کلاس child دسترسی به متدها یا خاصیت های private در کلاس parent را نخواهد داشت. در نظر داشته باشید که در صورتی که متد یا خاصیتی به صورت protected تعریف گردد، امکان دسترسی به آن هم در داخل کلاس والد و هم در کلاس های ارث برده شده از آن، فراهم می باشد.

تعریف property ها و method های اختصاصی در کلاس child

یک کلاس child یا فرزند، علاوه بر اینکه به متدها و خواص کلاس parent خود دسترسی دارد، می تواند دارای متدها و همین طور خاصیت های مربوط به خود باشد. در مثال بالا برای کلاس فرزند، این امر با متد flying نمایش داده شده است.

مفهوم overriding  در PHP

در صورتی که برای متد (یا خواص) موجود در کلاس والد، پیاده سازی جدیدی در کلاس فرزند آن کلاس داشته باشیم، در این صورت می گوییم آن متد یا خاصیت override شده است. در صورتی که متد یا خاصیتی override شده باشد، برای دسترسی به متدهای والد، باید از کلمه کلیدی parent استفاده کنید. همانگونه که در مثال بالا، متد construct در کلاس child دوباره نوشته شده و متد construct کلاس parent در داخل آن با کلمه کلیدی parent و استفاده از علامت scope resolution یا :: فراخوانی شده است.

مفهوم overloading: تفاوت overloading با overriding چیست؟

در بسیاری از مواقع این دو مفهوم باهم اشتباه گرفته شده و اسامی آن ها به جای همدیگر به کار برده می شود. در مفهوم overriding که در بالا در مورد آن توضیح داده شد، دو متد دقیقا با یک نام و یک تعداد آرگومان ورودی اما با پیاده سازی متفاوت، در کلاس پدر و در کلاس فرزند موجود می باشند. اما در overloading، چند متد در یک کلاس با یک نام اما با تعداد آرگومان های ورودی متفاوت، تعریف و پیاده سازی می شوند (این امر بسیار در مورد constructor ها استفاده می شود)

کلمه کلیدی Final برای متد ها و کلاس در PHP

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

در صورتی که بخواهید اجازه ارث بری از یک کلاس را بگیرید و نگذارید تا آن کلاس extend گردد، از کلمه کلیدی final در پیش از کلمه class در تعریف کلاس، استفاده می کنید. شما نمی تواند از خاصیت inheritance بر روی کلاس های final استفاده کنید و نمی توانید از آن ها ارث بری کنید.

مفهوم late static binding در PHP

مفهوم late static binding از مفاهیم بسیار مهم و کاربردی در php می باشد. در پیش تر گفته شد که برای دسترسی به یک متد یا خاصیت از نوع استاتیک در متدهای یا خاصیت معمولی، می باید به جای استفاده از کلمه کلیدی this از کلمه کلیدی self استفاده کنیم.

حال گاهی اوقات ممکن است در کلاس parent بخواهیم از متد یا property ای که به صورت static در کلاس فرزند تعریف شده، استفاده نماییم. به این کار اصطلاحا late static binding گفته می شود. برای این کار، یعنی استفاده از متد یا خاصیت static کلاس فرزند در کلاس پدر، ار کلمه کلیدی static به جای self استفاده می کنیم.

آشنایی با مفهوم auto loading در PHP

مفهوم autoloading یکی از موارد بسیار مهم و کاربردی در PHP می باشد. همواره برای اینکه بتوانیم از یک فایل یا یک کلاس PHP در فایل ها یا کلاس های دیگر استفاده نماییم، می باید محل آن فایل را با دستور include یا require به بالای فایل خود اضافه کنیم. وقتی برنامه کوچک است و تعداد بخش ها و فایل های آن زیاد نیست، استفاده از include یا require مهم نمی باشد. اما هنگامی که برنامه کمی بزرگ می شود و تعداد بخش های آن افزایش می یابد، هر بار می باید تعداد زیادی فایل را قبل از استفاده، include نماییم.

زمانی که تعداد فایل ها افزایش می یابد، مدیریت این کار فوق العاده سخت و پیچیده خواهد شد. در واقع ما باید تمام فایل ها لود کنیم در حالی که فقط به بخشی از آن ها نیاز داریم. در ابتدا برای مدیریت این کار از متد جادویی autoload برای کلاس ها استفاده می شد. در PHP7 و در SPL یا همان Standard PHP Library تابعی با نام spl_autoload_register معرفی شد. در واقع PHP در صورتی که فایل یا کلاسی را در برنامه ببیند که در ابتدای برنامه تعریف نشده، آن را به این تابع ارجاع می دهد تا این تابع به صورت خودکار، فایل یا کلاس مورد نظر را پیدا کرده و به برنامه اضافه کند. به این عمل اصطلاحا autoloading گفته می شود. مزیت استفاده از این روش این می باشد که هم استفاده از فضای حافظه کمتر خواهد شد و هم باعث افزایش سرعت پردازش برنامه می گردد.

برای انجام auto loading و استفاده از تابع spl_autoload_register ، شما می باید یک ساختار مشخص برای فایل ها یا کلاس های خود مشخص کنید، تا هنگام ارجاع شدن یک فایل یا کلاس ناشناخته، کلاس autoloading شما بتواند ساختار آن را درست کرده و به برنامه شما اضافه کند. به عنوان مثال، شما در کلیه کلاس های خود را در یک دایرکتوری می گذارید. همچنین اول اسم کلیه کلاس ها را با -class نام گذاری می کنید.

حال فقط لازم است کلاس autoloader را خود را در هدر صفحات یا تابعی به نام init.php اضافه کنید. پس از آن دیگر نیاز به اضافه کردن فایل ها و کلاس های خود برای شناخته به برنامه ندارید و کلاس autoloader شما به راحتی به صورت خودکار این کار را برای شما انجام خواهد داد. کد زیر، یک نمونه از کلاس autoloader می باشد. البته در نظر بگیرید که پس از تعریف این کلاس، کلیه کلاس های شما باید با نام -class آغاز گردد تا کلاس autoloader شما بتواند آن ها را به صورت اتوماتیک به برنامه در حال اجرای شما اضافه نماید.

/***** Autoloder class *****/
class Autoloader
{
    public function __construct()
    {
        spl_autoload_register(array($this, 'autoload'));
    }
    public function autoload($class_name)
    {
        $file = $this->convert_class_to_file($class_name);
        if (is_file($file) && file_exists($file) && is_readable($file) && !class_exists($class_name)) {
            //var_dump($file);
            include $file;
        }else {
            die("This file name: {$file} was not found...!");
        }
    }
    public function convert_class_to_file($class_name)
    {
        $class = strtolower($class_name);
        $class = 'class-'.$class;
        $filename = "includes/{$class}.php";
        return $filename;
    }
}
new Autoloader();

بخش چهارم: مفهوم Abstract شی گرایی در PHP

در مفاهیم شی گرایی، زمانی که بخواهیم سایر توسعه دهندگان را الزام کنیم که در هنگام ارث بری از کلاسی، حتما متد خاصی در آن را پیاده سازی کنند، از کلاس ها و متدهای abstract استفاده می کنیم. در واقع در یک کلاس abstract یا انتزاعی می خواهیم که برنامه نویس حتما متدی که در کلاس والد با نام abstract مشخص شده است را، پیاده سازی نماید. اگر یک متد abstract در یک کلاس موجود باشد، حتما آن کلاس باید به صورت abstract تعریف گردد. کلاس abstract قابل نمونه گیری یا instantiation نبوده اما قابل ارث بری می باشند.

متدهای انتزاعی در داخل کلاس abstract، دارای فقط اسم و آرگومان های ورودی هستند و بدنه آن ها در کلاس abstract یا والد خالی است. کلاس انتزاعی والد، فقط دارای Method’s Signature می باشد. بنابراین بدنه متدهای انتزاعی و عملیاتی که قرار است در آن ها انجام شود، در کلاس فرزند که از آن ارث برده است، می باید تعریف گردد. پس implementation متد در این بخش باید انجام پذیرد.

در نظر داشته باشید که Visibility یا همان سطح دسترسی متد فرزند، باید مساوی یا بیشتر از سطح دستری متد abstract باشد. یعنی اگر visibility متد انتزاعی در کلاس parent برابر protected باشد، در کلاسی که از آن مشتق شده است، سطح دسترسی نمی تواند private بوده و باید protected یا public باشید. کلاس های abstract می توانند دارای property ها یا متد هایی باشند که پیاده سازی شده باشند. یعنی یک کلاس انتزاعی می تواند شامل خاصیت ها و همین طور متدهای غیر انتزاعی نیز باشد.

برای اینکه یک کلاس انتزاعی تعریف کنید، کافی است یک یا چند متد abstract در یک کلاس که با کلید واژه abstract آغاز شده است را به صورت زیر تعریف نمایید:

تعریف property و method های غیر انتزاعی در PHP:

همانند یک کلاس معمولی، شما می توانید در داخل یک کلاس abstract ، خواص و متدهای غیر abstract تعریف نمایید.

abstract class AbstractMsnCar
{
    /*Abstract classes can have property*/
    protected $tankCapacity;

    /*Abstract classes can have non abstract method*/
    public function setTankCapacity($tankCapacity)
    {
        $this->tankCapacity = $tankCapacity;
    }

    // Abstract method
    abstract public function distanceOnFullTank();
}

نحوه ارث بری یک کلاس از کلاس abstract در PHP:

ارث بری از یک کلاس انتزاعی، همانند ارث بری در سایر بخش هاست. فقط حتما باید متدهایی که در کلاس parent تعریف شده اند، در کلاس child بازنویسی شده یا implement گردند. متد abstract در کلاس والد، هیچ بدنه ای ندارد و در کلاس فرزند، این متد باید override گردد.

class BMW extends AbstractMsnCar
{
    public function distanceOnFullTank()
    {
        // TODO: Implement distanceOnFullTank() method.
        $kilometers = $this->tankCapacity*100;
        return $kilometers;
    }

}

بخش پنجم: مفهوم Interface شی گرایی در PHP

یکی دیگر از مفاهیم شی گرایی، مفهوم interface می باشد. از مفهوم interface در OOP برای موارد متعدد از جمله پیاده سازی چند ریختی استفاده می شود. مفهوم interface بسیار شبیه کلاس انتزاعی بوده و به نوعی یک کلاس abstract محسوب می شود. تفاوت آن ها این می باشد که در داخل interface ها هیچ متدی که دارای بدنه باشد، نمی تواند تعریف شود. هر کلاسی که از یک interface مشتق شده باشد باید کلیه متدهای کلاس پدر خود را بازنویسی نماید. درون interface نمی توان property تعریف کرد. ضمن اینکه کلیه متدهای داخل یک interface باید دارای سطح دسترسی public باشند. با توجه به مفهوم interface در شی گرایی، یک interface نمی تواند دارای متد غیر انتزاعی باشد. در واقع کلیه متدهای داخل یک interface به صورت abstract هستند.

 

اساسی ترین کاربرد interface در زبان PHP

مهم ترین کاربرد interface در زبان PHP پیاده سازی مفهوم Multi Inheritance یا وراثت چند گانه می باشد. یک کلاس تنها می تواند از یک کلاس دیگر ارث بری کند اما می تواند چندین interface را پیاده سازی یا implement نماید.

برای تعریف interface به جای استفاده از کلمه کلیدی class باید از کلمه کلیدی interface استفاده نمایید. در نظر داشته باشید که interface فقط می تواند شامل متد های abstract و ثابت ها یا همان constants باشد. هیچ متغیر یا property ای را نمی توان در interface تعریف نمود.

نحوه ارث بری از interface در PHP:

برای اینکه یک کلاس فرزند بتواند از یک interface مشتق گردد، می باید در تعریف خود به جای کلمه کلیدی extends از کلمه کلیدی implements استفاده کند. کلیه متدهایی که در interface تعریف شده اند، باید به صورت کامل در کلاس child پیاده سازی یا implement گردند.

interface BMW
{
    public function setModel($name);

    public function getModel();
}

class BMW Mseries implements BMW
{
    private $model;

    public function getModel()
    {
        return $this->model;
    }

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

ارث بری interface ها یا extendable interface در PHP:

هر interface می تواند از یک یا چندین interface دیگر مشتق گردد اما یک کلاس تنها می تواند از یک کلاس دیگر مشتق گردد.

interface a
{
    public function ooo();
}

interface b extends a
{
    public function baz(Baz $baz);
}

// This will work
class c implements b
{
    public function ooo()
    {
    }

    public function baz(Baz $baz)
    {
    }
}

// This will not work and result in a fatal error
class d implements b
{
    public function ooo()
    {
    }

    public function baz(ooo $ooo)
    {
    }
}

نحوه پیاده سازی ارث بری چندگانه یا Multiple interface inheritance  در PHP:

یکی از قابلیت های بسیار کاربردی استفاده از interface پیاده سازی ارث بری چندگانه یا همان Multiple Inheritance می باشد. در شما می توانید چندین interface را در یک کلاس implement یا پیاده سازی کنید. در واقع یک کلاس می تواند بیش از یک interface را implement کند. در مثال بالا فرض کنید بخواهیم کلاس BMW Mseries از دو کلاس BMW و Vehicle ارث برده و آن ها را پیاده سازی نماید. پیاده سازی آن به صورت زیر خواهد بود:

interface Vehicle
{
    public function setHasWheels($bool);

    public function getHasWheels();
}

class BMW Mseries implements BMW, Vehicle
{
    private $model;
    private $hasWheels;

    public function getModel()
    {
        return $this->model;
    }

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

    public function getHasWheels()
    {
        return ($this->hasWheels) ? "has wheels" : "no wheels";
    }

    public function setHasWheels($bool)
    {
        $this->hasWheels = $bool;
    }
}

بخش ششم: مفهوم Polymorphism شی گرایی و پیاده سازی آن در PHP

یکی از موارد بسیار کاربردی در شی گرایی، Polymorphism می باشد که در فارسی از آن به عنوان چند ریختی یاد می شود. در واقع، چند ریختی بودن وقتی اتفاق می افتد که کلیه کلاس های ارث برده از یک کلاس دارای یک متد از کلاس والد هستند. این متد بین همه آن ها مشترک است اما رفتار این متد در هرکدام از کلاس ها متفاوت می باشد. متد کلاس فرزند بسته به نوع کلاس، پیاده سازی متفاوتی دارد.

مثالی برای polymorphism

از معروف ترین مثال ها در زمینه چند ریختی در شی گرایی، عکسی است که در بالا نمایش داده شده است. همان گونه که می بینید، کلاس Shape یا همان شکل، دارای متد draw یا کشیدن می باشد. کلیه اشکال مثل خط، دایره، مثلث و مستطیل، از کلاس شکل و متد کشیدن آن ارث می برند. اما نحوه کشیدن در هر شکل، بسته به نوع آن فرق دارد. به این حالت چند ریختی گفته می شود.

مثال زیر نیز به خوبی این مورد را نمایش می دهد. در این مثال متد محاسبه مساحت یک شکل در اشکال مختلف باهم فرق می کند اما همه آن ها یک نام داشته و از یک پدر یعنی کلاس shape ارث برده اند.

interface Shape
{
    public function calcArea();
}

class Circle implements Shape
{
    private $radius;

    public function __construct($radius)
    {
        $this->radius = $radius;
    }

    // calcArea calculates the area of circles
    public function calcArea()
    {
        return $this->radius * $this->radius * pi();
    }
}

class Rectangle implements Shape
{
    private $width;
    private $height;

    public function __construct($width, $height)
    {
        $this->width = $width;
        $this->height = $height;
    }

    // calcArea calculates the area of rectangles
    public function calcArea()
    {
        return $this->width * $this->height;
    }
}

$circ = new Circle(3);
$rect = new Rectangle(3, 4);
echo $circ->calcArea();
echo $rect->calcArea();

/*
 * The result is:
   28.274333882308
   12
 * */

بخش هفتم: مفهوم Encapsulation شی گرایی و استفاده از namespace در PHP

یکی دیگر از مفاهیم شی گرایی در PHP، مفهوم Encapsulation و نحوه پیاده سازی آن می باشد. در واقع namespace ها راهی برای کپسوله سازی داده ها و آیتم ها در PHP می باشد. در دنیای پی اچ پی namespace ها برای حل دو مشکل اصلی طراحی شده اند که عموما کتابخانه ها و برنامه های توسعه دهندگان، با آن ها هنگام برنامه نویسی مواجه می شدند. در واقع شما، وقتی بخش هایی از کد که برای استفاده مجدد می نویسید (مانند کلاس ها و تابع ها)، عموما با دو مشکل زیر مواجه می گردید:

  1. تداخل نام ها بین کدهایی که شما ساخته اید با کلاس ها، توابع، ثابت های PHP یا با کلاس ها، توابع یا ثابت های یک برنامه third-party
  2. ایجاد اشکال در هنگام کوتاه کردن (استفاده از alias) نام های بلند که باعث افزایش خوانایی یا readability کد های شما خواهد شد

برای جلوگیری از تداخل نام برای class ها، function ها و constant های هم نام، از مفهوم namespace در PHP استفاده می شود. در واقع namespace ها راهی را برای گروه بندی کلاس ها، interface ها، تابع ها و ثابت های مرتبط را به ما می دهند. در صورتی که دو کلاس یا متد (یا کلاس و متد) داشته باشیم که دارای نام های یکسان باشند، برای جلوگیری از تداخل آن ها باید از namespace جداگانه در قبل تعریف کلاس، استفاده کنیم.

برای تعریف namespace کافی است پیش از عبارت namespace و سپس نام آن استفاده کنیم. در صورتی که namespace از چند بخش (یعنی از دایرکتوری های تو در تو) تشکیل شده باشد، برای جداسازی آن از علامت \ استفاده می کنیم. برای صدا کردن متد ها یا ساخت اشیا از کلاس دارای namespace، می باید نام namespace به همراه علامت بک اسلش فراخوانی شده و بعد از آن، نام متد آورده شود.

class MsnClass {}
function msnfunction() {}
const MSNCONST = 1;

$a = new MsnClass;
$c = new \my\msn\MsnClass;
// see "Global Space" section

$a = strlen('hi'); 
// see "Using namespaces: fallback to global function/constant" section

$d = namespace\MSNCONST;
// see "namespace operator and __NAMESPACE__ constant" section
$d = __NAMESPACE__ . '\MSNCONST';
echo constant($d); // see "Namespaces and dynamic language features" section

استفاده از best practice ها برای namespace در PHP:

همان طور که می دانید، best practice ها با نام PHP Standard Recommendation یا به اختصار PSR ها شناخته می شوند. برای دیدن لیست PSR ها در php، می توانید اینجا کلیک کنید. در حال حاضر 5 بخش کلی برای PSR ها با نام های PSR-0 و PSR-1 و PSR-2 و PSR-3 و PSR-4 وجود دارد که PSR-0 و PSR-4 به مباحث حل مشکلات تداخل نام ها و استفاده از namespace برای FQCN می پردازد.

FQCN یا Fully Qualified Class name می باشد. در واقع این اصطلاح به مجموع full namespace + class name گفته می شود. این امر باعث بهبود در زمینه autoloading می باشد. استاندارد هایی که در PSR-0 و PSR-4 برای namespacing تعریف شده، به شما اجازه می دهند تا path خود را به یک FQCN معتبر یا valid تبدیل کنید. فرض کنید شما یک ساختار به فرم زیر دارید و می خواهید مفهوم autoloading را با در نظر گرفتن namespace های آن، پیاده سازی کنید:

Structure :
{path}/autoloader.php
{path}/index.php
{path}/src/Msn/Tools/MsnTool.php

فایل ها را به صورت جداگانه در زیر قرار داده ایم. فایل اول، index.php می باشد که فایل autoloader.php خود را در آن include می کنیم. پس از آن به راحتی می توانیم از کلاس MsnTool استفاده کنیم.

// {path}/index.php
include 'autoloader.php';
$tool = new Msn/Tools/MsnTool();
// {path}/src/Msn/Tools/MsnTool.php
namespace Msn\Tools;
class MsnTool {}
// {path}/autoloader.php
function loadClass($className)
{
    $fileName = '';
    $namespace = '';

    // Sets the include path as the "src" directory
    $includePath = dirname(__FILE__) . DIRECTORY_SEPARATOR . 'src';

    if (false !== ($lastNsPos = strripos($className, '\\'))) {
        $namespace = substr($className, 0, $lastNsPos);
        $className = substr($className, $lastNsPos + 1);
        $fileName = str_replace('\\', DIRECTORY_SEPARATOR, $namespace) . DIRECTORY_SEPARATOR;
    }
    $fileName .= str_replace('_', DIRECTORY_SEPARATOR, $className) . '.php';
    $fullFileName = $includePath . DIRECTORY_SEPARATOR . $fileName;

    if (file_exists($fullFileName)) {
        require $fullFileName;
    } else {
        echo 'Class "' . $className . '" does not exist.';
    }
}

spl_autoload_register('loadClass'); // Registers the autoloader

استفاده از namespace alias در PHP:

گاهی اوقات مسیرهای ما طولانی شده و این باعث طولانی شدن namespace های ما می باشد. استفاده از alias ها باعث می گردد که یک namespace طولانی را به یک نام خلاصه شده تبدیل کنیم. با استفاده از alias به جای استفاده از namespace ، از نام خلاصه آن با استفاده از کلمات کلیدی use و as به صورت زیر استفاده می کنیم.

use Msn\Library01 as L;
use Msn\Library02\MsnClass as Obj;

header('Content-type: text/plain');
require_once('library01.php');
require_once('library02.php');

echo L\MsnCONST . "\n";
echo L\MsnFunction() . "\n";
echo L\MsnClass::msnmethod() . "\n";
echo Obj::msnmethod() . "\n";

بخش هشتم: مفهوم Trait  در PHP

زبان هایی چون ++C و پایتون، زبان هایی هستند که از وراثت چندگانه یا همان Multi Inheritance پشتیبانی می کنند. اما زبان PHP از وراثت چندگانه پشتیبانی نمی کند. برای رفع این مشکل و پیاده سازی مکانیزمی برای استفاده از مجدد از کدهای تکراری در زبان single inheritance از مفهوم trait استفاده می شود. درواقع trait محدودیت های موجود در وراثت یگانه را برداشته و توسعه دهنده می تواند آزادانه از چندین کلاس مختلف که در ساختار سلسه مراتبی کلاسی مختلف قرار دارند، در کلاس های خود استفاده کند. مفهوم trait باعث کم شدن پیچیدپی های و جلوگیری از مشکلات رایج مربوط با وراثت چندگانه و Mixin ها می گردد.

trait ها بسیار شبیه کلاس هستند، با این تفاوت که از آن ها نمی تواند ارث برد اما می توان از آن ها در کلاس ها به راحتی استفاده کرد. در واقع به نوعی می توان در جاهایی که نیاز داریم به جای مفهوم is-a در شی گرایی از مفهوم has-a یا همان composition استفاده کنیم، از trait استفاده می کنیم. برای استفاده از trait کافی به جای کلمه کلیدی class از کلمه کلیدی trait استفاده کنیم. همچنین برای استفاده trait، کافی است از کلمه کلیدی use در داخل کلاس استفاده نماییم.

استفاده از trait به صورت ساده در PHP

فرض کنید شما یک کد برای لاگ کردن در بخش های مختلف سیستم دارید. حال شما می خواهید هم آن را در کلاس حساب بانکی استفاده کنید و هم در کلاس user. برای اینکار به صورت زیر عمل می کنید:

trait Logger
{
    function log($msg)
    {
        echo '<pre>';
        echo date('Y-m-d h:i:s') . ':' . '(' . __CLASS__ . ') ' . $msg . '<br/>';
        echo '</pre>';
    }
}


class BankAccount
{
    use Logger;

    private $accountNumber;

    function __construct($accountNumber)
    {
        $this->accountNumber = $accountNumber;
        $this->log("A new $accountNumber bank account created");
    }
}

class User
{
    use Logger;

    function __construct()
    {
        $this->log("A new user created");
    }
}

$account = new BankAccount('1234567674');
$user = new User();

استفاده از Multiple trait یا trait چندگانه در PHP

در مثال زیر، شما یک کلاس IDE دارید و از چندین trait در داخل آن استفاده می کنید

trait Preprocessor
{
    function preprocess()
    {
        echo 'Preprocess...done' . '<br/>';
    }
}

trait Compiler
{
    function compile()
    {
        echo 'Compile code... done' . '<br/>';
    }
}

trait Assembler
{
    function createObjCode()
    {
        echo 'Create the object code files... done.' . '<br/>';
    }
}

trait Linker
{
    function createExec()
    {
        echo 'Create the executable file...done' . '<br/>';
    }
}

class IDE
{
    use Preprocessor, Compiler, Assembler, Linker;

    function run()
    {
        $this->preprocess();
        $this->compile();
        $this->createObjCode();
        $this->createExec();

        echo 'Execute the program...done' . '<br/>';
    }
}

$ide = new IDE();
$ide->run();

Composition یا ترکیب کردن trait ها با هم در Multiple traits

از دیگر موارد جالب، ترکیب کردن چندین trait و استفاده از آن ها در یک trait دیگر و سپس استفاده آن در یک کلاس می باشد. مثال زیر این مورد را نشان می دهد

trait Reader
{
    public function read($source)
    {
        echo sprintf("Read from %s <br/>", $source);
    }
}

trait Writer
{
    public function write($destination)
    {
        echo sprintf("Write to %s <br/>", $destination);
    }
}

trait Copier
{
    use Reader, Writer;

    public function copy($source, $destination)
    {
        $this->read($source);
        $this->write($destination);
    }
}

class FileUtil
{
    use Copier;

    public function copyFile($source, $destination)
    {
        $this->copy($source, $destination);
    }
}

بخش نهم: کامنت گذاری با استفاده از DocBlocks  در PHP

برای کامنت گذاری کلاس ها در PHP می توانیم از استایل DocBlock استفاده کنیم. بسیاری از IDE های پر استفاده مانند PhpStorm یا Eclipse  از این روش برای تولید خودکار کامنت در کلاس ها، استفاده می کنند. قدرت واقعی DocBlock ها در توانایی استفاده از tag ها می باشد که با علامت @ شروع می شوند. به این وسیله برنامه نویس می تواند اطلاعات اضافی در مورد بخش های مختلف کلاس را در کد خود، نشانه گذاری و تشریح نمایید.

پر استفاده ترین این تگ ها عبارتند از:

  • author@
  • copyright@
  • license@
  • var@
  • param@
  • return@

مثال زیر این مورد را به خوبی نمایش می دهد

 

/**
 * A simple class
 *
 * This is the long description for this class,
 * which can span as many lines as needed.
 *
 * It can also span multiple paragraphs if the
 * description merits that much verbiage.
 *
 * @author Jason Lengstorf <jason.lengstorf@ennuidesign.com>
 * @copyright 2010 Ennui Design
 * @license http://www.php.net/license/3_01.txt PHP License 3.01
 */
class SimpleClass
{
    /**
     * A public variable
     *
     * @var string stores data for the class
     */
    public $abc;

    /**
     * Sets $abc to a new value upon class instantiation
     *
     * @param string $val a value required for the class
     * @return void
     */
    public function __construct($val)
    {
        $this->abc = $val;
    }

    /**
     * Multiplies two integers
     *
     * Accepts a pair of integers and returns the
     * product of the two.
     *
     * @param int $az a number to be multiplied
     * @param int $ax a number to be multiplied
     * @return int the product of the two parameters
     */
    public function bar($az, $ax)
    {
        return $az * $ax;
    }
}

بخش دهم:مقایسه اشیا در PHP

در PHP شما می توانید اشیا را باهم مقایسه کنید که به آن PHP Compare Object گفته می شود.

مقایسه اشیا با استفاده از عامل (==)  یا comparison operator در PHP

زمانی که شما از عامل == برای مقایسه دو شی در PHP استفاده می کنید، دو object باهم برابر خواهند بود در صورتی که هر دو نمونه از یک کلاس instantiate شده باشند و همچنین دارای property ها و مقدار یکسان باشند.

class Point
{
    private $x;
    private $y;

    public function __construct($x, $y)
    {
        $this->x = $x;
        $this->y = $y;
    }

    /**
     * Compare two points
     * @param Point $p1
     * @param Point $p2
     * @return boolean return true if two points are equal, otherwise returns false
     */
    public static function compare($p1, $p2)
    {
        return $p1 == $p2;
    }

    public function getX()
    {
        return $this->x;
    }

    public function setX($x)
    {
        $this->x = $x;
    }

    public function getY()
    {
        return $this->y;
    }

    public function setY($y)
    {
        $this->y = $y;
    }

}

$p1 = new Point(10, 20);
$p2 = new Point(10, 20);

if (Point::compare($p1, $p2)) {
    echo 'p1 and p2 are equal <br/>';
} else {
    echo 'p1 and p2 are not equal <br/>';
}

مقایسه اشیا با استفاده از عامل (===)  یا identity operator در PHP

زمانی که شما از عامل === برای مقایسه دو شی در PHP استفاده می کنید، دو object باهم برابر خواهند بود در صورتی که هر دو نمونه به یک شی از آن کلاس اشاره کنند.

بخش یازدهم: نتیجه گیری برای مقاله آموزش شی گرایی در PHP

این مقاله به عنوان یک مرجع در آموزش شی گرایی می تواند مورد استفاده قرار گیرد. دراین مقاله به طور کلی هم با مفاهیم شی گرایی و هم با نحوه پیاده سازی این مفاهیم شی گرایی در PHP صحبت کردیم.

در ابتدا با مفاهیم شی و کلاس و خاصیت ها و متدهای آن آشنا شدیم. سپس به بررسی پیاده سازی آن ها در شی گرایی پرداختیم و سطح دسترسی به کلاس ها و متدهای آن را بررسی کردیم. در ادامه به بررسی متدهای زنجیره ای و متدهای جادویی در php پرداختیم.

مفاهیم ارث بری و وراثت در PHP و نحوه پیاده سازی آن آشنا شدیم. با مفاهیم abstract و interface را آشنا شدیم و در ادامه نشان دادیم که چگونه می توان پولی مورفیسم را در PHP پیاده سازی کرد. سپس در مورد کپسوله سازی یا همان encapsulation توسط name space ها صحبت کردیم. در انتها نیز به مفاهیم triat و نحوه کامنت گذاری استاندارد در کلاس و مقایسه اشیا باهم پرداختیم.

ممکن است شما موارد بیشتری را بدانید که ما فراموش کرده ایم تا در این مقاله قرار دهیم. اگر موارد دیگری به نظرتان می آید که جایشان در این مقاله خالیست و می تواند به آموزش مفهوم شی گرایی کمک کند، خوشحال میشیم ما را از طریق ثبت تیکت پشتیبانی از آن آگاه کنید.

 

 

 

مطالب زیر را حتما بخوانید

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *