تاریخ های تغییر پذیر می توانند منبع بروز سردرگمی و باگ های غیر منتظره در کد شما باشند. هدف من این نیست که به شما بگویم DateTime به خاطر تغییر پذیر بودنش بد است، بلکه می خواهم خوبی ها و فواید استفاده از آبجکت های DateTime تغییر پذیر و تغییر ناپذیر را در پروژه های خود ببینید. این که یک مجموعه آزمایش خوب را طی کنید، و بدانید که متود های اصلاح کننده چگونه آبجکت های تاریخ شما را تحت تاثیر قرار می دهند.
تا همین چند وقت پیش، اصلا نمی دانستم که PHP یک کلاس همتای DateTime به نام DateTimeImmutable را نیز به همراه دارد. کلاس DateTimeImmutable دقیقا به مانند کلاس DateTime کار می کند، به جز این که هیچ وقت خودش خودش را تغییر نمی دهد، بلکه در عوض یک آبجکت جدید را باز می گرداند. پس اگر می دانید که چگونه با DateTime کار کنید، سریعا کار با DateTimeImmutable را نیز یاد خواهید گرفت.
کار با DateTime های تغییر پذیر در PHP
خوشبختانه برای ما، می توانیم تمام این کلاس ها را به طور چکیده در کتابخانه هایی مثل Carbon داشته باشیم. در بخش های بعدی، شما را با یک کتابخانه مشابه تغییر ناپذیر بر پایه DateTimeImmutable آشنا خواهم کرد.
Carbon تنوع تغییر پذیری DateTime را گسترش می دهد، یعنی این که می توانید هر موقع که نیاز است، یک نمونه Carbon را سریعا به کار بگیرد، و ببینید که نتیجه تغییر می کند:
$person = Person::find(1);
echo $person->born // 1998-05-29 02:35:53
$person->born->addDay(1);
echo $person->born // 1998-05-30 02:35:53
$person->save();
در زمینه Model Eloquent، این کاملا منطقی به نظر می رسد. در غیر این صورت، هر زمان که می خواهیم یک تاریخ را با استفاده از روش تغییر پذیر به روز رسانی کنیم، مجبوریم این کار غیر منطقی را انجام دهیم:
// This code doesn't work, it's what it would look like if Carbon instances were immutable
$newBirthday = $person->born->addDay(1);
$person->born = $newBirthday;
// Or...
$person->born = $person->born->addDay(1);
اگر می خواهید سنجش هایی انجام دهید که نیاز به اصلاح دارد، مجبورید یک کپی از این تاریخ را داشته باشید تا از به هم ریزی صفت اصلی جلوگیری کنید. برای مثال، این کد صفت Model را به هم میریزد:
function birthdayIsTomorrow($birthday) {
return $birthday->addDay(1)->isTomorrow();
}
// Mutation takes place...
if (birthdayIsTomorrow($person->born)) {
}
// No mutation takes place...
if (birthdayIsTomorrow($person->born->copy())) {
}
// Via clone, no mutation takes place...
if (birthdayIsTomorrow(clone $person->born)) {
}
نکته جالب دیگر در اینجا، این است که کد شما تاریخ را به هم میریزد، اما شما Model را save() نمی کنید، و به این صورت شاید به اختلالی بر بخورید که حل کردنش آسان نیست. هدف اصلی در اینجا این است که بدانید این کد چگونه تاریخ شما را به هم می ریزد، و یک کپی از تاریخ، به جای یک کپی از کل Model تا متودی که می خواهد تاریخ را اصلاح کند، بگیرید.
نمی خواهم بگویم که استفاده از از کتابخانه های تغییر پذیری مثل Carbon اشتباه است؛ بلکه می خواهم نحوه رفتار این نوع آبجکت، و تکنیک های دفاعی ای که باید برای جلوگیری از اختلال استفاده کنید را نشان دهم.
کار با DateTime های تغییر ناپذیر در PHP
اگر کار با Carbon را دوست دارید، و می خواهید چنین تجربه ای را با یک نسخه تغییر ناپذیر که API مشابهی دارد را تجربه کنید، شاید از کتابخانه Chronos library، ساخته موسسه CakePHP خوشتان بیاید. Chronos در ابتدا بر پایه Carbon بود، و حال یک کتابخانه مستقل، بدون هیچگونه وابستگی خارج از PHP نسخه 5.5.9|7 است.
به علاوه، Chronos شامل انواع تاریخ و ساعت های تغییر پذیر است، که در 5 کلاس خلاصه می شوند:
- Cake\Chronos\Chronos که یک آبجکت تغییر ناپذیر تاریخ و زمان است.
- Cake\Chronos\Date که یک آبجکت تغییر ناپذیر تاریخ است.
- Cake\Chronos\MutableDateTime که یک آبجکت تغییر پذیر تاریخ و زمان است.
- Cake\Chronos\MutableDate که یک آبجکت تغییر پذیر تاریخ است.
- Cake\Chronos\ChronosInterval که یک امتدادی برای آبجکت DateInterval است.
هنگام استفاده از Chronos در مثال قبلی، آبجکت تاریخ هیچ چیز را به هم نمی ریزد، زیرا هر تغییری یک آبجکت جدید را باز می گرداند:
if (birthdayIsTomorrow($person->born)) {
// Immutable, $person->born would equal `today` still
}
هر زمان که شما یک آبجکت را اصلاح می کنید، یک کپی جدید باز گردانده می شود، که کد شما را از مشکلات وابستگی مبتنی بر ترتیب خلاص می کند. این مثال را در نظر بگیرید:
// i.e., 2018-05-29 04:23:01.342143
$meeting = \Cake\Chronos\Chronos::tomorrow();
$meeting->addDay(1); // Returns a new object
// No mutation, it returns the original 2018-05-29...
return $meeting;
برای کار با تاریخ های تغییر ناپذیر، باید در هنگام کار با Modifier ها، متغیر ها را جایگزین کنید:
$meeting = \Cake\Chronos\Chronos::tomorrow();
$meeting = $meeting->addDay(1);
// Returns 2018-05-30...
return $meeting
حتی اگر می خواهید تاریخ را به شروع روز تنظیم کنید، باید همین کار را انجام دهید، یه آن را به عملیات اصلی متصل کنید:
// Assignment
$meeting = \Cake\Chronos\Chronos::tomorrow();
$meeting = $meeting->addDay(1);
$meeting = $meeting->startOfDay();
// Chain the original
// i.e., 2018-05-31 00:00:00.000000
$meeting = \Cake\Chronos\Chronos::tomorrow()
->addDay(1)
->startOfDay();
تغییر ناپذیر بودن، شما را مجبور می کند تا با به صراحت تغییر دادن وظیفه یک آبجکت تاریخ، تغییرات عمدی را اعمال کنید، و در نتیحه ترسی از کار با آبجکت اصلی نداشته باشید.
بیشتر بدانید
گرچه این تفاوت لطیف به نظر می آید، استفاده از آبجکت های تاریخ تغییر ناپذیر می تواند به شما اطمینان خاطر بدهد که که کار با یک تاریخ هیچ اختلالی در آبجکت اصلی ایجاد نمی کند، مگر این که به طور خارجی آن را مجبود کنید.
نمی خواهم بگویم که یک روش از دیگری بهتر است، می خواهم بگویم که باور دارم یاد گرفتن DateTimeImmutable در PHP می تواند به شما در واضح سازی تکنیک های دفاعی که تاریخ های تغییر ناپذیر به کد شما می دهند، کمک کند.
با دانستن این و استفاده از Carbon، باید راحب هر تاریخی که می خواهید دستکاری کنید، تا متود های دیگر سنجش و منطق های دیگر، اطلاعاتی داشته باشید. پیشنهاد می کنم که همیشه تاریخ را کپی کنید، تا از به هم ریزی جلوگیری کنید، و مراقب نقاطی که ممکن است باعث به هم ریزی های غیر منتظره شوند، باشید.
اگر می خواهید آزمایشات بیشتری درباره DateTime های تغییر ناپذیر با یک API خوب مثل Carbon انجام دهید، نگاهی به اسناد Chronos داشته باشید.
دیدگاه و پرسش
در حال دریافت نظرات از سرور، لطفا منتظر بمانید
در حال دریافت نظرات از سرور، لطفا منتظر بمانید