آشنایی با نحوهٔ Clone کردن یک آبجکت در زبان PHP


Object Cloning به پروسهٔ کپی کردن یک آبجکت در زبان‌های برنامه‌نویسی شیئ‌گرا گفته می‌شود و این در حالی است که در زبان پی‌اچ‌پی دو مُدل آبجکت کلونینگ داریم که عبارتند از Shallow Copy و Deep Copy که در ادامهٔ آموزش هر دو مورد را مورد بررسی قرار خواهیم داد.

پیش از هر چیز، داخل پوشهٔ oop پروژهٔ جدیدی به نام object-cloning ساخته سپس بر پایهٔ ساختاری که در این دوره تاکنون دنبال نموده‌ایم، در مسیر روت فایلی به نام index.php ساخته و آن را به صورت زیر تکمیل می‌کنیم:

<?php
$varOne = 1;
$varTwo = $varOne;
++$varTwo;
echo $varOne;
echo "\n";
echo $varTwo;

همان‌طور که می‌بینیم، varOne$ حاوی عدد ۱ است و در خط بعد متغیر دیگری ساخته‌ایم به نام varTwo$ که مقدار آن را برابر با varOne$ قرار داده‌ایم و در ادامه مقدار متغیر varTwo$ به اصطلاح Increment کرده‌ایم (یک واحد افزایش داده‌ایم.) و در نهایت هر دو متغیر را چاپ کرده‌ایم به طوری که در خروجی خواهیم داشت:

1
2

می‌بینیم که درج علائم ++ قبل از varTwo$ هیچ تأثیری روی مقدار varOne$ ندارد زیرا وقتی این متغیر را کپی کردیم،‌ اصطلاحاً‌ Pass by Value بود بدان معنا که فضای جدیدی در حافظه ایجاد شده و مقدار متغیر varTwo$ در آن ذخیره شد و از این پس این دو متغیر هیچ ربطی به یکدیگر نخواهند داشت. در ادامه، قصد داریم تا با مفهومی تحت عنوان Pass by Reference آشنا شویم به طوری که داریم:

<?php
$varOne = 1;
$varTwo = &$varOne;
++$varTwo;
echo $varOne;
echo "\n";
echo $varTwo;

در حقیقت، در خط سوم قبل از متغیر varOne$ از علامت & استفاده کرده‌ایم به طوری که می‌توان گفت از این پس متغیر varOne$ یک پوینتر یا رِفرنس به جایی از حافظه است که مقدار این متغیر در آن ذخیره شده است و اگر این فایل را اجرا کنیم،‌ در خروجی خواهیم داشت:

2
2

وقتی از علائم ++ قبل از متغیر varTwo$ استفاده کنیم، یک واحد به مقدار این متغیر افزوده می‌شود اما از آنجا که متغیر varOne$ به اصطلاح Pass by Reference است و اساساً متغیر varTwo$ یک رفرنس به جایگاهی از حافظه است که متغیر varOne$ در آن ذخیره شده است، می‌توان گفت که در حافظه فقط و فقط یک دیتا ثبت شده که هم متغیر varOne$ و هم متغیر varTwo$ به آن فضا اشاره دارند و نیاز به توضیح نیست که وقتی مقدار یکی از متغیرهای تغییر کند،‌ مقدار دیگری هم تغییر پیدا خواهد کرد چرا که هر دو به فضایی یکسان از حافظه اشاره دارند (در همین راستا و برای کسب اطلاعات بیشتر می‌توانید به مقالهٔ آشنایی با تفاوت Pass by Value و Pass By Reference در زبان PHP مراجعه نمایید.)

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

<?php
namespace SokanAcademy;

class User
{
    public $projectName;

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

داخل بدنهٔ این کلاس یک پراپرتی داریم به نام projectName$ که به محض ساخت یک آبجکت از روی این کلاس، می‌باید پارامتری تحت عنوان project$ به کانستراکتور این کلاس پاس دهیم تا در نهایت به این پراپرتی منتسب گردد. حال وارد فایل index.php شده و آن را به صورت زیر تکمیل می‌کنیم:

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

$user = new SokanAcademy\User('Sokan Academy');
echo $user->projectName;

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

Sokan Academy

حال این فایل را به صورت زیر آپدیت می‌کنیم:

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

$user = new SokanAcademy\User('Sokan Academy');
echo $user->projectName;
echo "\n";
$newUser = $user;
$newUser->projectName = 'New Project';
echo $newUser->projectName;
echo "\n";
echo $user->projectName;

در خط هشتم یک آبجکت جدید ساخته‌ایم تحت عنوان newUser$ اما به جای آن که با استفاده از کلیدواژهٔ new نمونهٔ جدیدی از روی کلاس User ساخته و به آن منتسب کنیم، از آبجکت قبلی user$ استفاده کرده‌ایم. در ادامه، مقدار جدیدی برای پراپرتی projectName$ منتسب به این آبجکت جدید در نظر گرفته و در خط بعد آن را چاپ کرد‌ه‌ایم و در نهایت مجدد پراپرتی projectName$ منتسب به آبجکت اصلی یا همان user$ را چاپ کرده‌ایم به طوری که در خروجی خواهیم داشت:

Sokan Academy
New Project
New Project

در تفسیر خروجی فوق باید گفت که چاپ استرینگ «Sokan Academy» مربوط به چاپ پراپرتی projectName$ در خط ششم است و استرینگ «New Project» اول مربوط به اجرای خط دهم است و استرینگ «New Project» دوم مربوط به اجرای خط دوازدهم می‌باشد. همان‌طور که می‌بینیم، پس از ساخت یک آبجکت جدید و تغییر مقدار تنها پراپرتی آن، مقدار پراپرتی موجود در آبجکت اول که نقش آبجکت اصلی را بازی می‌کند نیز تغییر یافته است!

این مسئله از آنجا ناشی می‌شود که ما اساساً آبجکتی را کپی نکرده‌ایم بلکه متغیری ساخته‌ایم تحت عنوان newUser$ که به منزلهٔ یک پوینتر (اشاره‌گر) به دقیقاً همان بخش از حافظه می‌باشد که متغیر user$ در آن ذخیره شده است و بالتبع وقتی هر کدام از این پوینترها منجر به تغییر دیتا شوند، داده‌های هر دو متغیر آپدیت خواهند شد. برای درک بهتر این موضوع، فایل index.php را به صورت زیر مجدد تغییر می‌دهیم:

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

$user = new SokanAcademy\User('Sokan Academy');
echo $user->projectName;
echo "\n";
$newUser = $user;
$newUser->projectName = 'New Project';
echo $newUser->projectName;
echo "\n";
echo $user->projectName;
echo "\n";
$user->projectName = 'Back to Sokan Academy';
echo $user->projectName;
echo "\n";
echo $newUser->projectName;

همان‌طور که ملاحظه می‌شود، در خط چهاردهم مجدد مقدار جدیدی برای پراپرتی منتسب به آبجکت user$ در نظر گرفته‌ایم و در خط بعد آن را چاپ نموده‌ایم و در خط آخر مجدد پراپرتی منتسب به آبجکت جدید را چاپ کرده‌ایم به طوری که داریم:

Sokan Academy
New Project
New Project
Back to Sokan Academy
Back to Sokan Academy

می‌بینیم که مجدد استرینگ جدید «Back to Sokan Academy» که به پراپرتی آبجکت اصلی اختصاص یافته است برای پراپرتی آبجکت کپی نیز در نظر گرفته شده است. حال برای این که از بُعد دیگری یکسان بودن هر دو آبجکت را چک کنیم، کدهای فوق را به صورت زیر تکمیل می‌کنیم:

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

$user = new SokanAcademy\User('Sokan Academy');
echo $user->projectName;
echo "\n";
$newUser = $user;
$newUser->projectName = 'New Project';
echo $newUser->projectName;
echo "\n";
echo $user->projectName;
echo "\n";
$user->projectName = 'Back to Sokan Academy';
echo $user->projectName;
echo "\n";
echo $newUser->projectName;
echo "\n";
var_dump(spl_object_hash($user));
var_dump(spl_object_hash($newUser));

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

Sokan Academy
New Project
New Project
Back to Sokan Academy
Back to Sokan Academy
string(32) "00000000096d82ac000000006e7fdb05"
string(32) "00000000096d82ac000000006e7fdb05"

در واقع،‌ فانکشن ()spl_object_hash شناسهٔ به اصطلاح Hash ID هر آبجکت را باز می‌گرداند و همان‌طور که می‌بینیم، مقدار هَش هر دو آبجکت دقیقاً یکسان است چرا که هر دو آبجکت به جایگاهی یکسان از حافظه ارجاع می‌دهند. در حقیقت،‌ می‌بینیم که در زبان پی‌اچ‌پی وقتی با آبجکت‌ها سروکار داریم، گویی کپی کردن بدین شکل Pass By Reference است. با این تفاسیر، ایدهٔ این طور کلون کردن آبجکت‌ها کارساز نیست و می‌باید به دنبال راه‌کار دیگری باشیم که پروسهٔ Object Cloning به شکل اصولی‌تری صورت گیرد که در ادامهٔ این آموزش به بررسی روش‌های دیگر انجام این کار خواهیم پرداخت.

این بخش از محتوا مخصوص کاربرانی است که ثبت‌نام کرده‌اند.
جهت مشاهدهٔ این بخش از محتوا لاگین نمایید.

جمع‌بندی
در این آموزش به بررسی مقولهٔ Object Cloning یا به عبارتی کپی کردن آبجکت‌ها در زبان پی‌اچ‌پی پرداختیم و دیدیم که به چه شکل با استفاده از دستور clone و مَجید متد ()clone__ می‌توان مفاهیم Shallow Copy و Deep Copy را در این زبان پیاده‌سازی نمود. در پایان لازم به یادآوری است که به منظور پیاده‌سازی Deep Copy در ارتباط با آبجکت‌ها، می‌توان از لایبرری اپن‌سورس DeepCopy نیز استفاده نمود.

دانلود فایل‌های تمرین

لیست نظرات
کاربر میهمان
دیدگاه شما چیست؟
کاربر میهمان