آموزش ارث بری در PHP با مثال و ۳ ترفند

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

در اصول برنامه نویسی شی گرا یک بحث مهم به نام ارث‌بری در کلاس‌ها داریم. به کمک ارث بری یا inheritance ویژگی‌ها و رفتارهای یک کلاس از کلاس بالاتر به ارث گرفته می‌شود.

دقیقاً همانطور که در تولیدمثل انسان‌ها، فرزندان ویژگی‌ها و رفتارهایی را از والدین خود به ارث می‌برند. البته هر فرزند می‌تواند ویژگی‌ها و رفتارهای منحصربه‌فرد خودش را نیز داشته باشد.

ارث بری در PHP

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

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

از طرفی، هر کدام ویژگی‌ها (Property) و رفتارهای (همان متد یا Method) منحصربه‌فردی نیز دارند. مثلاً استاد می‌تواند جلسات درس یا تعیین کرده یا نمره ثبت کند. دانشجوی هم می‌تواند تمرین‌هایش را ارسال کرده و صرفاً نمرات خودش را ببیند.

در این مثال، شاید بهتر باشد یک کلاس بالاتر برای دانشجو و استاد به نام «کاربر» در نظر بگیریم. کاربر یکسری اطلاعات هویتی داشته و می‌تواند وارد سایت شود. حالا برای اینکه کلاس دانشجو یا استاد را پیاده‌سازی کنیم، از کلاس user ارث‌بری کرده و متدها و ویژگی‌های اختصاصی‌اش را می‌نویسیم.

برای ارث‌بری در PHP از کلمه کلیدی extends استفاده می‌کنیم. در مثال زیر، کلاس B از کلاس A ارث‌بری کرده است. اصطلاحاً به A کلاس والد (parent) و به B کلاس فرزند (child) گفته می‌شود:

class A {
    // properties and methods
}

class B extends A {
    // properties and methods
}

مثال از ارث بری php

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

طبق تعریف کلاس در PHP یک کلاس برای حیوان به نام Animal ایجاد می‌کنم:

class Animal {
    protected $name;
    protected $eye_color;

    public function __construct($name, $eye_color) {
        $this->name = $name;
        $this->eye_color = $eye_color;
    }

    public function getName() {
        return $this->name;
    }

    public function makeSound() {
        echo 'bla bla bla!';
    }
}

این کلاس یک متد سازنده برای مقداردهی اولیه ویژگی‌ها و دو متد برای گرفتن نام و چاپ کردن صدای حیوان دارد. makeSound() فعلاً یک متن بی‌ربط را چاپ می‌کند؛ چون صدای هر حیوان متفاوت است. جلوتر یاد می‌گیریم چطور آن را مجدداً تعریف کنیم.

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

class Cat extends Animal {
    public function play() {
        echo 'this cat is playing...';
    }
}

وقتی از کلاس Animal ارث بری می‌کنیم، تمام متدها و ویژگی‌های آن به کلاس فرزند منتقل می‌شود. در نتیجه، برای ساخت یک شیء از Cat، باید دو آرگومان ورودی به آن بدهم. به نظرتان چرا؟

$milo = new Cat('milo', 'blue');

به کمک یک IDE بررسی می‌کنم که به چه متدها و ویژگی‌هایی روی این شیء دسترسی دارم:

بررسی تعریف وراثت در کلاس‌های PHP
بررسی تعریف وراثت در کلاس‌های PHP

همانطور که می‌بینید علاوه بر متد play() از کلاس Cat، به متدهای کلاس والد نیز دسترسی دارم! این یعنی ارث بری در PHP! 🙂

اینجا می‌توانیم از متدهایمان استفاده کنیم:

$milo = new Cat('milo', 'blue');
$milo->play();

سؤال: به نظرتان چرا به ویژگی‌های نام و رنگ چشم از کلاس والد دسترسی نداریم؟! پیشنهاد می‌کنم یکم به این سوال فکر کنید. جلوتر جواب آن را می‌دهم.
راهنمایی: دلیلش را در بخش انتهایی جلسه قبل (آموزش کلاس‌ها در PHP) با هم بررسی کرده‌ایم.

آموزش کار با فرم در php و ساخت فرم

آموزش کار با فرم در php و ساخت فرم

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

وقتی از ارث بری در PHP استفاده می‌کنیم، علاوه بر شیء، در بدنه کلاس فرزند نیز به متدها و ویژگی‌های والد دسترسی داریم. یعنی من می‌توانم در متد play() نام از نام گربه استفاده کنم:

class Cat extends Animal {
    public function play() {
        echo "$this->name is playing...";
    }
}

دسترسی به ویژگی‌های والد (همان متغیرهای PHP درون کلاس) دقیقاً مشابه دسترسی به ویژگی‌های درون کلاس است. به همین دلیل از کلمه کلیدی $this استفاده می‌کنیم. در مورد استفاده مستقیم متغیر در رشته در آموزش رشته string در PHP صحبت کرده‌ام.

حال برسیم به سؤالی که پرسیدم! چرا به این متغیرها در شئ دسترسی نداریم؟ چون سطح دسترسی آن‌ها (Access Modifier) روی protected هست. به همین دلیل در کلاس فرزند وجود دارند ولی از طریق شئ در دسترس نیستند.

اگر بعضی ویژگی‌ها یا متدها را private کنیم، در کلاس فرزند نیز به آن‌ها دسترسی نخواهیم داشت.

اگر بخواهیم به متدهای کلاس والد در هنگام ارث‌بری PHP دسترسی داشته باشیم، از کلمه کلیدی parent:: استفاده می‌کنیم. در قطعه کد زیر، متد getName() والد را استفاده کرده‌ام:

class Cat extends Animal {
    public function play() {
        echo "$this->name is playing...";
    }
    public function sayName(){
        $name = parent::getName();
        echo "My name is $name";
    }
}

نکات مهم

در این بخش، ۳ مفهوم و نکته مهم در ارث بری PHP را با هم مرور می‌کنیم.

قبل از آن، خوب است بدانید که در پروژه‌های بزرگ، معمولاً هر کلاس در یک فایل php قرار می‌گیرد. سپس با include کردن یا استفاده از namespaceها در کنار یکدیگر استفاده می‌شوند. من در این آموزش همه کلاس‌ها و کدها را در یک فایل نوشتم. در جلسه تمرین پروژه محور کلاس‌هایمان جداگانه تعریف می‌کنیم.

بازنویسی متد در ارث‌بری

با بازنویسی متد می‌توانیم متدهایی که از کلاس والد به ارث برده شده‌اند را تغییر دهیم. به بازنویسی در ارث بری Method Overriding گفته می‌شود.

در مثال خودمان، من می‌خواهیم متد makeSound() را برای گربه بازنویسی کنم. برای این کار کافی است این متد را دوباره در کلاس فرزند تعریف و پیاده‌سازی کنم! پس داریم:

class Cat extends Animal {
    
    public function play() {
        echo "$this->name is playing...";
    }
    public function makeSound() {  // Method Overriding
        echo 'meow!';
    }
}

$milo = new Cat('milo', 'blue');
$milo->makeSound();

// output:
// meow!

حالا وقتی روی شیء از کلاس گربه این متد را صدا بزنیم، صدای meow! می‌شنویم! 😉

به نظرتان بازنویسی متد در ارث بری PHP چه فایده‌ای دارد؟ این ویژگی کمک می‌کند بتوانیم رفتار بعضی از متدها را در کلاس فرزند تغییر دهیم. اما در هر صورت، مطمئن هستیم همه اشیاء در مجموعه Animal، متدی برای چاپ صدا دارند.

آموزش اتصال و کار با دیتابیس در PHP

آموزش اتصال و کار با دیتابیس در PHP

کلمه final در PHP

کلمه final به معنی «نهایی» است. وقتی یک کلاس یا متد از نظر ما در کامل‌ترین حالت ممکن است، می‌توانیم آن را final کنیم.

از یک کلاس final در PHP نمی‌توان ارث‌بری کرد. همچنین یک متد final را نمی‌توانیم بازنویسی کنیم.

اینکه چه زمان از این کلمه باید استفاده کنیم، نیاز به کمی تجربه دارد. معمولاً زمانی که به عنوان برنامه‌نویسی نمی‌خواهید از کلاستان ارث‌بری شود یا یک متد override شود، از آن استفاده می‌شود. این مسئله می‌تواند به خاطر محدودیت‌ها، نیازمندی‌ها یا مسائل امنیتی باشد.

در باکس کد و تصویر زیر، یک متد و یک کلاس به‌صورت final تعریف شده‌اند:

class TestFinal {
    public final function do_something(){
        echo "This is a Test from SabzDanesh.com";
    }
}
مثال تعریف Final Class در PHP
مثال تعریف Final Class در PHP
آموزش هش در PHP با ۴ تابع رمزنگاری یک طرفه

آموزش هش در PHP با ۴ تابع رمزنگاری یک طرفه

ارث بری سلسله مراتبی PHP

در مثال آموزشی‌مان، می‌توانیم بسته به نیاز و وسعت پروژه، سلسله مراتب ارث‌بری در PHP را افزایش دهیم. یعنی بگوییم Cat زیرمجموعه‌ای از «چهارپا» است که چهارپا خودش در مجموعه Animal قرار می‌گیرد.

اینکه چقدر جزئیات را افزایش دهیم به نیازمندی‌های پروژه ما برمی‌گردد. مثلاً اگر قرار بود حیوان دیگری که چهارپا نیست را در سیستم داشته باشیم، شاید این سلسله مراتب خوب باشد. چون پرندگان و چهارپایان ویژگی‌ها و متدهای مختلف دارند اما همگی در «کلاس حیوان» قرار می‌گیرند. پس خصوصیات مشترکی هم دارند.

در تصویر زیر، نمونه‌ای از یک ارث بری سلسله مراتبی در php را می‌بینید:

مثال ارث‌بری سلسله مراتبی در پی‌اچ‌پی
مثال ارث‌بری سلسله مراتبی در پی‌اچ‌پی

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

مرور ارث‌بری در PHP

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

این کار با استفاده از کلمه کلیدی extends در بخش تعریف نام کلاس انجام می‌شود. در اینصورت، می‌توانیم ویژگی‌ها یا متدهای بیشتری را در کلاس فرزند پیاده‌سازی کنیم. همچنین می‌توان به ویژگی‌های والد دسترسی داشته و با parent:: از متدهای آن نیز استفاده کنیم.

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

  • کلاس والد (Parent) : کلاس super یا کلاس پایه (base class)
  • کلاس فرزند (Child) : زیر کلاس (sub-class) یا کلاسِ مشتق‌شده (derived class)

در بخش نکات، با بازنویسی متد آشنا شدیم که پیاده‌سازی متدهای کلاس والد را تغییر می‌دهد. با کلمه final می‌توان توسعه یک کلاس یا بازنویسی method را ممنوع کرد.

پیشنهاد می‌کنم همین الآن سعی کنید ۲ یا ۳ کلاس (غیر از مثال‌های آموزش) برای خودتان پیاده‌سازی کنید تا به مفاهیم و نحوه کارکردشان مسلط‌تر شوید.

مثال‌های متنوعی در صفحه مستندات php وجود دارد که می‌توانید نگاهی هم به آن‌ها بندازید. اگر سؤال، نکته یا تجربه‌ای در مورد ارث بری یا inheritance در PHP دارید، در بخش دیدگاه‌ها با ما و دوستانتان به اشتراک بگذارید. 🙂

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