سرفصل‌های آموزشی
آموزش OOP در PHP
آشنایی با مفهوم وراثت در زبان PHP

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

در آموزش آشنایی با مفهوم OOP و بررسی اصول چهارگانهٔ شیئ‌گرایی، گفتیم که یکی از پایه‌های شیئ‌گرایی مبحثی است تحت عنوان Inheritance که می‌توان معادلی همچون «وراثت» برای آن در نظر گرفت. به طور خلاصه می‌توان گفت که در متودولوژی اواوپی، وراثت این امکان را در اختیار توسعه‌دهندگان می‌گذارد تا بتوانند ویژگی‌های کلاسی خاص را در دیگر کلاس‌ها وارد سازند.

به منظور درک بهتر این موضوع، داخل پوشهٔ oop پروژه‌ای با نامی دلخواه تحت عنوان inheritance ساخته و فولدر اِستراکچری که در ابتدای این دورهٔ آموزشی ایجاد کردیم را در آن می‌سازیم. سپس وارد پوشهٔ classes شده و فایل User.php را به صورت زیر تکمیل می‌کنیم:

<?php
namespace SokanAcademy;

class User
{
    public $firstname;
    public $lastname;
    public $role = 'basic';
    public $hasFullPermission = false;

    public function __construct($firstname, $lastname, $role = null, $hasFullPermission = null)
    {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
        if ($role) {
            $this->role = $role;
        }
        if ($hasFullPermission) {
            $this->hasFullPermission = $hasFullPermission;
        }
    }

    public function isAdmin()
    {
        if ($this->role == 'admin') {
            return "This user is an Administrator";
        }
    }

    public function showFullName()
    {
        return $this->firstname . ' ' . $this->lastname;
    }
}

در بدنهٔ این کلاس چهار پراپرتی تعریف کرده‌ایم که از میان آن‌ها پراپرتی‌های role$ و hasFullPermission$ دارای مقادیری پیش‌فرض هستند. سپس کانستراکتور را موظف کرده‌ایم که در پروسهٔ ساخت یک آبجکت جدید از روی کلاس User، دو پارامتر ورودی اجباری و دو پارامتر ورودی اختیاری از کاربر بگیرد؛ به عبارت بهتر، پارامترهای ورودی firstname$ و lastname$ که دارای مقدار اولیهٔ null نیستند الزامی بوده و در حین استفاده از این کلاس حتماً‌ می‌باید در نظر گرفته شوند اما پارامترهای role$ و hasFullPermission$ دارای مقدار اولیهٔ null هستند بدین معنا که اگر در حین استفاده از این کلاس آن‌ها را سِت نکنیم، مقدار null به آن‌ها اختصاص خواهد یافت و در غیر این صورت، مقادیر جدید جایگزین خواهند شد.

همچنین داخل بدنهٔ کانستراکتور دستور داده‌ایم تا مقادیر پارامترهای ورودی به پراپرتی‌های متناظرشان منتسب گردد به علاوه این که با استفاده از دو دستور شرطی چک کرده‌ایم ببینیم که آیا پراپرتی‌های role$ و hasFullPermission$ به اصطلاح سِت شده‌اند (یا به عبارتی مقداری به غیر از null دارند) یا خیر که اگر این گونه بود، مقادیر آن‌ها را نیز به پراپرتی‌های مربوطه منتسب خواهیم نمود.

در ادامه، فانکشن یا بهتر بگوییم متدی نوشته‌ایم تحت عنوان ()isAdmin و داخل آن چک کرده‌ایم ببینیم که آیا مقدار پراپرتی role$ برابر با استرینگ admin می‌باشد یا خیر که اگر این طور بود، استرینگی مبنی بر ادمین بودن کاربر را ریترن خواهیم نمود. همچنین متد دیگری به نام ()showFullName نوشته و داخل آن پراپرتی‌های firstname$ و lastname$ را با یکدیگر کانکت نموده و نتیجهٔ نهایی را ریترن کرده‌ایم. حال جهت تست، وارد فایل index.php شده و آن را به صورت زیر تکمیل می‌نماییم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

$user = new SokanAcademy\User();

تنها کاری که انجام داده‌‌ایم این است که آبجکتی تحت عنوان user$ از روی کلاس User ساخته‌ایم و چنانچه این فایل را اجرا کنیم، در خروجی خواهیم داشت:

/var/www/oop/inheritance$ php index.php 
PHP Fatal error:  Uncaught ArgumentCountError: Too few arguments to function SokanAcademy\User::__construct(), 0 passed in /var/www/oop/inheritance/index.php on line 5 and at least 2 expected in /var/www/oop/inheritance/classes/User.php:11

این ارور به درستی اشاره می‌کند که کلاس User حداقل به دو پارامتر الزامی نیاز دارد اما این در حالی است که ما در حین ساخت آبجکت هیچ گونه پارامتری را به آن پاس نداده‌ایم که برای رفع این ارور، کد فوق را به صورت زیر تصحیح می‌نماییم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

$user = new SokanAcademy\User('Behzad', 'Moradi');

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

echo $user->isAdmin();

اما در خروجی هیچ چیزی ملاحظه نخواهد شد چرا که داخل این متد دستور داده‌ایم که چنانچه پراپرتی role$ برابر با استرینگ admin بود چیزی را ریترن کند و در غیر این صورت هیچ کار خاصی انجام ندهد.

در این مرحله از آموزش، نیاز داریم تا به مبحث وراثت ورود پیدا کنیم که برای این منظور، فایلی با نامی دلخواه همچون Administrator.php داخل پوشهٔ classes می‌سازیم و کاری می‌کنیم تا کلیهٔ خصوصیاتش را از کلاس User به ارث ببرد.

در زبان برنامه‌نویسی پی‌اچ‌پی پروسهٔ ارث‌بری با استفاده از کیورد extends آغاز می‌گردد. به طور مثال، اگر بخواهیم کاری کنیم که کلاسی همچون Administrator از User ارث‌بری کند، وارد فایل Administrator.php شده و کدهای زیر را داخل آن درج می‌کنیم:

<?php
namespace SokanAcademy;

class Administrator extends User
{

}

آنچه در ارتباط با کلاس فوق نیاز به تفسیر دارد این است که پس از نام کلاس، از کیورد extends که در ظاهر نامی بامسمی است استفاده کرده سپس نام کلاسی را درج می‌کنیم که قصد داریم ویژگی‌هایش را به ارث ببریم.

نکته
با توجه به این که هم کلاس Administrator و هم کلاس User در یک نِیم‌اِسپیس قرار دارند، فلذا در حین نوشتن نام کلاس User نیازی به درج نِیم‌اِسپیس نیست.

در این مرحله کار خاصی با کلاس Administrator نخواهیم داشت بلکه نیاز است تا وارد فایل index.php شده تا ببینیم که به چه شکل می‌توانیم اقدام با ساخت یک آبجکت از روی این کلاس کنیم به طوری که داریم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

// $user = new SokanAcademy\User('Behzad', 'Moradi');
// echo $user->isAdmin();
$admin = new SokanAcademy\Administrator('Sahand', 'Ahmadi', 'admin', true);
echo $admin->isAdmin();

می‌بینیم که از روی کلاس Administrator آبجکتی تحت عنوان admin$ ساخته و هر چهار پارامتر ورودی این کلاس را نیز سِت کرده‌ سپس اقدام به فراخوانی متد ()isAdmin کرده‌ایم. در واقع، می‌بینیم که به عنوان پارامتر سوم، از استرینگ admin استفاده نموده‌ایم و چنانچه مجدد به کلاس User باز گردیم که به عنوان کلاس Parent (والد) شناخته می‌شود، می‌بینیم که داخل کانستراکتور این کلاس دستور داده‌ایم که اگر پارامتر سوم این کلاس مقداری به غیر از null داشت، وارد دستور شرطی اول شده و مقدار آن را به پراپرتی role$ که در بدنهٔ این کلاس قرار دارد منتسب نماید. همچنین مقدار true را به عنوان پارامتر چهارم در نظر گرفته‌ایم که بر اساس توضیحات فوق، داخل کانستراکتور کلاس User دستور شرطی دوم اجرا شده و این مقدار به پراپرتی hasFullPermission$ اختصاص داده خواهد شد. حال اگر این فایل را اجرا کنیم، در خروجی خواهیم داشت:

This user is an Administrator

در واقع می‌بینیم گرچه کلاس Administrator که اصطلاحاً تحت عنوان کلاس Child (فرزند) شناخته می‌شود حاوی هیچ گونه کدی نیست، اما توانسته کلیهٔ خصوصیاتش را از کلاس والدش به ارث ببرد.

همچنین با توجه به سِت کردن پارامتر چهارم با مقدار true، از این پس پراپرتی hasFullPermission$ این آبجکت مقداردهی شده است به طوری که جهت تست آن، می‌توان کدهای زیر را مورد توجه قرار داد:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

// $user = new SokanAcademy\User('Behzad', 'Moradi');
// echo $user->isAdmin();
$admin = new SokanAcademy\Administrator('Sahand', 'Ahmadi', 'admin', true);
echo $admin->isAdmin();
if ($admin->hasFullPermission) {
    echo "\n";
    echo $admin->showFullName();
}

در تفسیر کدهای فوق باید گفت که اول با استفاده از یک دستور شرطی چک کرده‌ایم ببینیم که آیا پراپرتی hasFullPermission$ دارای مقدار true است یا خیر که اگر این گونه بود، ابتدا با چاپ دستور n\ وارد یک خط جدید شده سپس متد ()showFullName را فراخوانی کرده‌ایم به طوری که از این پس در خروجی خواهیم داشت:

This user is an Administrator
Sahand Ahmadi

چنانچه بر اساس ارتباطات دنیای واقعی بخواهیم ارتباط مابین کلاس‌های User و Administrator را شبیه‌سازی کنیم، می‌توان گفت که کلاس User همچون پدر است و Administrator فرزند آن! حال قصد داریم تا برای کلاس پدر یک نوه نیز بسازیم که برای این منظور، داخل پوشهٔ classes فایل جدیدی تحت عنوان SubAdmin.php ساخته و کدهای زیر را داخل آن درج می‌کنیم:

<?php
namespace SokanAcademy;

class SubAdmin extends Administrator
{

}

همان‌طور که می‌بینیم، این کلاس ویژگی‌هایش را از کلاس Administrator به ارث برده است و از آنجا که خودِ کلاس Administrator نیز خصوصیاتش را از کلاس User ارث‌بری کرده است، پس می‌توان گفت که کلیهٔ ویژگی‌های کلاس والدِ User داخل کلاس نوهٔ SubAdmin موجود هستند. در ادامه، مجدد به فایل index.php بازگشته و آن را به صورت زیر آپدیت می‌کنیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

// $user = new SokanAcademy\User('Behzad', 'Moradi');
// echo $user->isAdmin();
// $admin = new SokanAcademy\Administrator('Sahand', 'Ahmadi', 'admin', true);
// echo $admin->isAdmin();
// if ($admin->hasFullPermission) {
//     echo "\n";
//     echo $admin->showFullName();
// }
$grandChild = new SokanAcademy\SubAdmin('Mitra', 'Gholami');
echo $grandChild->showFullName();

می‌بینیم که آبجکتی تحت عنوان grandChild$ از روی کلاس SubAdmin ساخته و پارامترهای الزامی را نیز سِت کرده‌ایم و در خط بعد هم خروجی متد ()showFullName را چاپ کرده‌ایم به طوری که در خروجی خواهیم داشت:

Mitra Gholami

حال قصد داریم تا با مفهومی تحت عنوان Overriding در شیئ‌گرایی آشنا شویم که در همین راستا، کلاس SubAdmin را به صورت زیر آپدیت می‌کنیم:

<?php
namespace SokanAcademy;

class SubAdmin extends Administrator
{
    public function showFullName()
    {
        return $this->firstname . ' ' . $this->lastname . ' from the SubAdmin class';
    }
}

همان‌طور که می‌بینیم، متد ()showFullName که در کلاسِ والدِ User قرار دارد را مجدد نوشته و کدهای داخل آن را تغییر داده‌ایم که به این کار اصطلاحاً Method Overriding گفته می‌شود؛ به عبارت بهتر، با این کار الگوریتم اصلی متدی که داخل کلاس والد بود را بازنویسی کرده‌ایم به طوری که اگر مجدد فایل index.php را اجرا کنیم، در خروجی خواهیم داشت:

Mitra Gholami from the SubAdmin class

می‌بینیم که از این پس دیگر متد اصلی ()showFullName که داخل کلاس User قرار داشت اجرا نشده بلکه متدی با همین نام که داخل کلاس SubAdmin نوشتیم به کار گرفته شده است.

موضوعی که در شیئ‌گرایی حائز اهمیت می‌باشد این است که ما می‌توانیم علاوه بر ارث‌بری از کلاس والد، یک سری خصوصیات نیز برای خود کلاس فرزند نیز در نظر بگیریم که در کنار خصوصیات به ارث برده از کلاس والد حضور خواهند داشت. به طور مثال،‌ کلاس SubAdmin را به صورت زیر تکمیل می‌کنیم:

<?php
namespace SokanAcademy;

class SubAdmin extends Administrator
{
    public function showFullName()
    {
        return $this->firstname . ' ' . $this->lastname . ' from the SubAdmin class';
    }

    public function doSomething()
    {
        echo "This method is specific to SubAdmin class";
    }
}

همان‌طور که می‌بینیم متدی تحت عنوان ()doSomething نوشته‌ایم که فقط مختص به این کلاس است به طوری که در فایل index.php می‌توانیم آن را به صورت زیر فراخوانی کنیم:

<?php
ini_set('display_errors', '1');
require_once 'vendor/autoload.php';

// $user = new SokanAcademy\User('Behzad', 'Moradi');
// echo $user->isAdmin();
// $admin = new SokanAcademy\Administrator('Sahand', 'Ahmadi', 'admin', true);
// echo $admin->isAdmin();
// if ($admin->hasFullPermission) {
//     echo "\n";
//     echo $admin->showFullName();
// }
$grandChild = new SokanAcademy\SubAdmin('Mitra', 'Gholami');
// echo $grandChild->showFullName();
echo $grandChild->doSomething();

با فراخوانی این متد جدید، در خروجی خواهیم داشت:

This method is specific to SubAdmin class

می‌بینیم که خروجی این متد به درستی در معرض دیدمان قرار گرفته است.

جمع‌بندی
در این آموزش به بررسی یکی از پایه‌های کلیدی OOP تحت عنوان Inheritance پرداختیم که این امکان را در اختیار توسعه‌دهندگان زبان برنامه‌نویسی پی‌اچ‌پی می‌گذارد تا از دوباره‌کاری و نوشتن کدهای تکراری جلوگیری کنند و این فیچر چیزی است که در تمامی فریمورک‌ها و لایبرری‌های مطرح این زبان به کار گرفته می‌شود.

online-support-icon