معرفی عملگرها - قسمت سوم (مقاله)

پرسیده شده
فعالیت 1555 روز پیش
دیده شده 1040 بار
0

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

* اولویت عملگرها:
احتمالاً اکثرتان اولویت عملگرها را بشناسید اما در این جلسه میخواهیم کمی با جزئیات آنها آشنا شویم.
ضمناً گاهی اوقات در زبان PHP آنچیزی که در مورد اولویت ها در ذهنتان داشته اید اتفاق نمی افتد ولی بخاطر گنگ نشدن موضوع آنها را فعلاً بیان نمیکنیم.

ترتیب اولویت ها مشخص میکنند که بین چندین عملگر، نحوه ی اجرا چگونه است.
نحوه ی اجرا از روی گروه بندی(بسته بندی) عبارت مشخص میشود.
برای اینکه خوب متوجه شوید منظورمان چیست، باید چند مثال بیاوریم.

برای مثال 1 + 2 * 3 را در نظر بگیرید، در نگاه اول میگویید، جواب 9 است، در حالی که جواب صحیح 7 میباشد.
اما علت مربوط به ترتیب اولویت ها میباشد.
در مقاله ی 14، عملگرهای ریاضی را معرفی کردیم و در آنجا دیدید که عملگرهای + و * بین دو عملوند اتفاق می افتند.
حالا در عبارت 1 + 2 * 3 عملوندهای هر کدام، چه اعدادی هستند؟
به کمک ترتیب اولویت عملگرها میتوان فهمید که عبارت 1 + 2 * 3 چگونه اجرا میشود.

عملگر * اولویت بالاتری نسبت به + دارد.
پس با این دانسته میدانیم که در این عبارت، ابتدا 2 * 3 انجام میگیرد.
بنابراین اگر بخواهیم با این دانسته، عبارت 1 + 2 * 3 را گروه بندی(بسته بندی) کنیم، عبارت به شکل 1 + (2 * 3) در میآید.
میبینید که با دانستن ترتیب اولویت توانستیم، عملوندها را مشخص کنیم و هر کدام که اولویت بالاتری دارد را در یک گروه قرار بدهیم.
عملوند های * اعداد 3 و 2 شدند و عملوند های + عدد 1 و نتیجه ی 2 * 3 یعنی 6 شده است.
پس ابتدا ضرب انجام میشود و سپس 1 + (6) که نتیجه ی آن برابر 7 خواهد بود.

این پرانتز گذاری ها را جهت تفهیم مطلب به کار بردیم وگرنه اگر دنبال جواب 7 باشید دیگر نیازی به پرانتز گذاری هم نیست.
اما اگر مقصود شما جواب 9 است باید حتماً عبارت را بصورت (1 + 2) * 3 عبارت را بنویسید و پرانتز گذاری کنید.
+ همانطور که ملاحظه کردید، اولویت عملگرها، گروه بندی یک عبارت را مشخص میکند.

حالا ببنیم واقعا در پشت صحنه ی PHP هم در مثال بالا، ابتدا ضرب انجام میگیرد یا جمع!!
opcode عبارت 1 + 2 * 3 به شکل زیر است:

line     #        op              return  operands
----------------------------------------------------
   2     0        MUL             ~0      2, 3
         1        ADD             ~1      1, ~0

         2        FREE                    ~1
         3        RETURN                  1

شاید در ابتدا کمی گنگ بنظر برسد ولی کافیست فقط به خط #0 و #1 نگاه بیندازید. خواهید دید که ابتدا بین اعداد 2 و 3 عمل MUL یا همان Multiply(ضرب) صورت گرفته است و در خط دوم بین عدد 1 و جواب خط قبلی (منظور عدد 6 است) عمل ADD صورت گرفته است.
این نمونه از دستورالعمل، نشان میدهد که چه عملی ابتدا صورت گرفته است.
+ ( در مقاله ی بررسی تاریخچه ی زبان PHP، کمی در مورد opcode ها هم صحیت کرده ایم.)

نکته: با پرانتز گذاری میتوانید عبارات را گروه بندی کنید تا عبارت های درون یک گروه، حتماً با یکدیگر محاسبه شوند.
+ (با این کار تأکید میکنیم که چه عباراتی با یکدیگر سنجیده شوند.)

پس طبیعتاً هر گروه دارای اولویت های درون گروهی هم خواهد بود.
برای مثال عبارت 1 + (2 + 3 * 4) را اگر ببینید، خود عبارت داخل پرانتز، بصورت مجزا دارای ترتیب اولویت میباشد: 2 + (3 * 4)
پس اگر بخواهیم به شما نحوه ی اجرا را نشان دهیم، کل عبارت به این شکل در میآید: 1 + (2 + (3 * 4)) یعنی 1 + (2 + (12)) خواهد بود.

 

سؤال: اگر عبارت به شکل 1 + 2 + 3 باشد، نحوه ی اجرا به چه شکل است؟
پاسخ: چون ترتیب اولویت ها یکسان است(و عملگرهای دارای اولویت یکسان یکی پس از دیگری آمده است)، بنابراین با توجه به لیست اولویت ها، قانون Associativity یا شرکت پذیری مشخص میکند که عملگرهای با ترتیب اولویت یکسان، چگونه گروه بندی شوند. عملگر + از سمت چپ به راست و به ترتیب در نظر گرفته میشوند.
برای مثال اگر بخواهیم 1 + 2 + 3 را پرانتز گذاری کنیم، به این شکل در میآید: (1 + 2) + 3

سؤال: اگر عبارت به شکل 1 + 4 - 3 - 2 بود، نحوه ی اجرا به چه شکل میشد؟
پاسخ: عملگرهای + و - دارای اولویت یکسانی هستند.
همچنین این دو عملگر از قانون شرکت پذیری برخوردار هستند. پس طبق لیست ترتیب اولویت ها، در این مورد هم، عبارت از سمت چپ به راست در نظر گرفته میشود.
اگر عبارت را پرانتز گذاری کنیم، به شکل روبرو در میآید: ((1 + 4) - 3) - 2

حالا لیست ترتیب اولویت های زبان PHP را ببینید:

Associativity          Operators            Additional Information
**************************************************************************
non-associative	       clone new	              clone and new
--------------------------------------------------------------------------
left to right	          [	                      array()
--------------------------------------------------------------------------
right to left	          **	                  arithmetic
--------------------------------------------------------------------------
right to left	         ++ -- ~ 
                      (int) (float) 
                    (string) (array) 
                   (object) (bool) @         types and increment/decrement
--------------------------------------------------------------------------
non-associative	     instanceof	                  types
--------------------------------------------------------------------------
right to left	          !	                      logical
--------------------------------------------------------------------------
left to right	        * / %	                  arithmetic
--------------------------------------------------------------------------
left to right	        + - .	                  arithmetic and string
--------------------------------------------------------------------------
left to right	        << >>	                  bitwise
--------------------------------------------------------------------------
non-associative	      < <= > >=	                  comparison
--------------------------------------------------------------------------
non-associative	      == != === 
                      !== <> <=>	              comparison
--------------------------------------------------------------------------
left to right	          &	                      bitwise and references
--------------------------------------------------------------------------
left to right	          ^	                      bitwise
--------------------------------------------------------------------------
left to right	          |	                      bitwise
--------------------------------------------------------------------------
left to right	          &&	                  logical
--------------------------------------------------------------------------
left to right	          ||	                  logical
--------------------------------------------------------------------------
right to left	          ??	                  comparison
--------------------------------------------------------------------------
left to right	         ? :	                  ternary
--------------------------------------------------------------------------
right to left       = += -= *= **= /= .= 
                    %= &= |= ^= <<= >>=	          assignment
--------------------------------------------------------------------------
left to right	         and	                  logical
--------------------------------------------------------------------------
left to right	         xor	                  logical
--------------------------------------------------------------------------
left to right	         or	                      logical

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

 

میخواهیم برای تثبیت مطلب، چندین مثال بزنیم:
سؤال: عبارت 1 < 2 > 1 چگونه گروه بندی و محاسبه میشود؟
پاسخ: اگر به جدول ترتیب اولویت ها نگاهی بیندازید، عملگرهای < و > دارای اولویت یکسانی هستند امـــا دارای شرکت پذیری نیستند.
بنابراین این عبارت در زبان PHP غیر مجاز میباشد.

سؤال: عبارت 1 <= 1 == 1 چطور؟
پاسخ: این عبارت مجاز است. چون عملگر == دارای اولویت پایینتری نسبت به عملگر <= است.
عبارت بصورت روبرو گروه بندی میشود: (1 <= 1) == 1

سؤال: عبارت $a = $b = $c = 0; چطور؟
پاسخ: ترتیب اولویتشان که برابر است و از طرف دیگر اگر جدول را نگاه کنید، قانون شرکت پذیری برای این عملگر، ترتیب را از راست به چپ نشان میدهد.
پس به این شکل در میآید: $a = ($b = ($c = 0));

 

سؤال: عبارت $a = 1 + 2 * 3; چطور؟
پاسخ: عملکر = دارای اولویت پایینتری نسبت به دو عملگر + و * است.
همچنین عملگر + هم دارای اولویت پایینتری نسبت به عملگر * است.
بنابراین اگر بخواهیم نشان دهیم که چگونه گروه بندی میشوند، به این شکل است: $a = (1 + (2 * 3));

سؤال: چرا خروجی دستور زیر، برابر 2 است؟

<?php
$x = 3;
echo 'Result: ' . $x + 2; // 2

پاسخ: شاید انتظار داشته باشید، خروجی دستور بالا به شکل Result: 5 باشد.
اما اگر به جدول اولویت ها نگاه کنید، میبینید که عملگرهای . و + دارای اولویت یکسانی هستند و از سمت چپ به راست ارزیابی میشوند.
پس اگر بخواهیم پرانتز بندی کنیم، نحوه ی اجرای دستور بالا به شکل echo ('Result: ' . $x) + 2; خواهد بود.
پس عبارت echo 'Result: 3' + 2; را خواهیم داشت.
حالا چیزی که میبینیم، جمع بین یک string و یک integer است. با توجه به مباحث جلسات گذشته، این string به integer تبدیل میشود یا اصطلاحاً cast میشود.
پس خواهیم داشت echo 0 + 2 و در نتیجه در خروجی، عدد 2 چاپ خواهد شد.

اما اگر مقصودتان این باشد که در خروجی Result: 5 چاپ شود، باید دستور بالا را به شکل echo 'Result: ' . ($x + 2) بنویسید.

OpCode: برای اینکه خودتان هم ترتیب opcode های مثال بالا را ببینید:

compiled vars:  !0 = $x

line     #*       op                 return    operands
----------------------------------------------------------------
   2     0        ASSIGN             !0, 3
   3     1        CONCAT             ~1        'Result%3A+', !0
         2        ADD                ~2        ~1, 2
         3        ECHO               ~2
         4        RETURN              1

اگر این قسمت را متوجه نشدید اهمیتی ندارد. فقط اگر توجه کنید میبینید اول $x = 3 انجام میشود که در opcode ها با دستور ASSIGN مشخص شده است.
در ادامه دستور CONCAT نشان میدهد که قسمت 'Result: ' . $x انجام میشود.
بعد از آن، دستور ADD نشان میدهد که 'Result: 3' + 2 شده است و ECHO شده است.
این opcode ها در موتور Zend تفسیر خواهند شد.

 

حالا به یک سؤال مهم توجه کنید:
سؤال: عبارت 1 > 2 && 3 > 1 چگونه اجرا میشود؟
پاسخ: اولویت > بالاتر از اولویت && میباشد. اگر بخواهیم عبارت را پرانتز گذاری کنیم به این شکل در میآید: (1 > 2) && (3 > 1)
اگر یادتان باشد، در جلسه ی پیش گفتیم که عملگرهای منطقی، خاصیت short-circuit دارند بنابراین در عملگر && اگر شرط اول true نباشد، دیگر شرط دوم چک نمیشود و نتیجه ی && برابر false خواهد بود. خب تا اینجا درست.
حالا شاید برای بعضی ها سؤال پیش بیاید که مگر اولویت > بالاتر از && نیست، پس چطور میشود که طرف دوم چک نشود؟ یعنی میگویند اول شرط های دو طرف && چک میشود و در آخر && محاسبه میشود.
+ گفتیم اولویت ها گروه بندی رو مشخص میکنند ولی ترتیب اجرای دستور العمل ها بستگی به مفسر PHP داره.(یکی از موارد گیج کننده شاید باشه.)

برای پاسخ به این سؤال، بهتره از روی opcode های دستور بالا بفهمیم که چطور تفسیر خواهد شد.
opcode ها به ما نشان خواهند داد که ترتیب اجرای دستور بالا، چگونه است:

line     #         op                 return      operands
-------------------------------------------------------------------------------------
   2     0        IS_SMALLER           ~0           2, 1
         1        JMPZ_EX              ~0          ~0, ->4
         2        IS_SMALLER           ~1           1, 3
         3        BOOL                 ~0          ~1
         4        FREE                 ~0
         5        RETURN                1

با توجه به شماره خط # دستورالعمل های بالا را بررسی میکنیم:
#0 : دستور IS_SMALLER چک میکند که آیا عدد 2 از عدد 1 کوچکتر است یا نه، و جوابش boolean است.
#1 : قسمتی که دنبال جواب هستیم، همین دستور JMPZ_EX است. این دستور عمل JUMP را انجام میدهد. یعنی ابتدا چک میکند که جواب 1 > 2 false است یا true(جواب در #0 بدست آمده است) و اگر جواب false(یا صفر) بود به ردیف #4 جامپ خواهد کرد. و ردیف #4 هم فضای های اختصاص داده به برنامه را آزاد میکند و در نهایت برنامه به پایان میرسد.
برای درک بهتر به شبیه سازی زیر توجه کنید:(منظور opcode های بالا مثل کد زیر است)

<?php
if(!(1 > 2)) goto end;
if(!(3 > 1)) goto end;

echo 'done';

end: return 0;

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

 

همچنین به چند مثال دیگر میتوانید نگاه بیندازید که چطور گروه بندی شده اند:

<?php
$a = $i = 1;
$b = $j = 2;
//------------------------------//

$a = $b += 3; // $a = ($b += 3); -> $a = 5, $b = 5

//------------------------------//

$a = 3 * 3 % 5; // $a = ((3 * 3) % 5); -> 4

//------------------------------//

$a = true && 'true' ? 0 : true ? 1 : 2; // $a = ((true && true ? 0 : true) ? 1 : 2); -> 2

//------------------------------//
$a = $i & $j >> 1; // $a = ($i & ($j >> 1)); -> 1

//------------------------------//

$a = $i >= 0 && $i < $j; // $a = ($i >= 0) && ($i < $j); -> true

//------------------------------//

$a = !($i and $i + $j ** 2 && $j % 2);
/*

$a = !( $i and ( ($i + ($j ** 2)) && ($j % 2) ) ) ->
$a = !(false) ->
($a = true)

*/

همانطور که در مثال ها دیدید، ترتیب اولویت ها کمک کرد تا بتوانیم عبارت ها را گروه بندی کنیم.

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

 

بحث ترتیب اولویت ها تمام است، حالا دیگر عملگرهای باقیمانده را معرفی خواهیم کرد.
* عملگر اجرا: (Execution Operator)
برای اجرای command های خود میتوانید از عملگر ` ` بهره ببرید.
دقت داشته باشید که علامت single-quote نمیباشد و نامش backtick است.
زمانی که محتوایی در بین دو backtick قرار میدهید، PHP آنرا در خط فرمان سیستم عاملتان اجرا میکند و خروجی را برای شما برمیگرداند.
این عملگر مانند تابع shell_exec() عمل میکند.

حالا اگر سیستم عامل شما windows است، میتوانید دستور زیر را تست کنید:

<?php
$output = `dir`;
echo '<pre>' . $output . '</pre>';

یا اگر سیستم عامل شما linux است:

<?php
$output = `ls`;
echo '<pre>' . $output . '</pre>';

نکته: عملگر backtick در داخل double-quote و single-quote هیچ معنی خاصی نمیدهد و بعنوان جزئی از رشته محسوب میشود.

فایل پیوست

محسن موحد
محسن موحد

3 دی 98