<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های محسن موحد (آموزش برنامه نویسی)</title>
        <link>https://virgool.io/feed/@bidak</link>
        <description>برنامه نویس متخصص PHP، Mysql، Javascript، HTML، CSS، Node.js، Android، Laravel، Yii2 - مدیر پشتیبانی فنی و سرپرست منتورها در شرکت 7learn.com</description>
        <language>fa</language>
        <pubDate>2026-07-06 13:40:11</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/115326/avatar/qFEPU3.jpeg?height=120&amp;width=120</url>
            <title>محسن موحد (آموزش برنامه نویسی)</title>
            <link>https://virgool.io/@bidak</link>
        </image>

                    <item>
                <title>اسکریپت Chat با COMET (+ دمو)</title>
                <link>https://virgool.io/@bidak/%D8%A7%D8%B3%DA%A9%D8%B1%DB%8C%D9%BE%D8%AA-chat-%D8%A8%D8%A7-comet-%D8%AF%D9%85%D9%88-avutazm7gn8f</link>
                <description>سلام.چند سال پیش این اسکریپت رو با استفاده از تکنولوژی COMET نوشتم حالا گفتم با ویرگولی ها هم به اشتراکش بذارم. در نوع خودش سبک جالبیه.قبل از توضیحات اسکریپت میتونید دمو رو ببینید. ازینجا هم اسکریپتو دانلود کنید.ببینید به طور کلی سه روش وجود داره برای اینکه client و server بصورت real-time باهم تعامل داشته باشن:Long PollingWebsocketsServer-Sent Events (SSE)من دراین اسکریپت اومدم از مکانیسم HTTP Long Polling استفاده کردم. یعنی درواقع تکنولوژی Comet رو میتونیم به دو طریق پیاده کنیم: 1. Streaming و 2. Long Pollingبرای پیاده سازی این سیستم میتونیم از ajax استفاده کنیم و هر یک یا چند ثانیه با توابعی مثل setinerval یا settimeout عمل آجاکس رو تکرار کنیم و سمت سرور بریم و چک کنیم اگر پیغام جدیدی اومده بود برای کاربر ارسال بشه. ولی تعداد درخواست های زیادی که از سمت کلاینت فرستاده میشه بار زیادی رو سرور میزاره که باعث اتلاف منابع سرور و عدم کارایی سیستم میشه. یکی از راه حل های این موضوع تکنولوژی Comet هست.در کامت وقتی درخواستی با توابع ajax به سمت سرور از سمت کلاینت فرستاده میشه, در سمت سرور فورا پاسخی داده نمیشه و ارتباط باز میمونه و تا زمانی که نتیجه ای رو دریافت کنه این ارتباطو نگه میداریم.این کارو با یک حلقه ی بینهایت میتونیم اجرا کنیم.البته در هاست های اشتراکی بعد از 30 ثانیه timeout میشه و ما ارتباطو در حد چند ثانیه باز نگه میداریم. کلیت قضیه این بود.حالا یکی یکی با کد میرم جلو.واسه سند یک پیام نیازی به comet نیست و از ajax استفاده میکنیم.چون قراره هر وقت دکمه ی اینتر و یا دکمه ی سند کلیک شد یک پیام در دیتابیس ثبت شود که این اتفاق زمان کلیک کردن کاربر روی دکمه بوجود میاد بنابراین از ajax استفاده میکنیم.ارسال پیام:  (فایل send.js)تابع sendMessage با فشردن دکمه ی enter اجرا میشه:function sendMessage() 
{ 
   var qt = &#039;&#039;; 
   var txt = text = $(&#039;#txtedit&#039;).val(); 
   to = 0; 

   if(typeof quote != &#039;undefined&#039; &amp;&amp; quote.trim() != &#039;&#039;) 
   { 
      if(text.match(/\[:::\]_.+_\[:::\]/g)) 
      { 
         var rx = /\[:::\]_(.+)_\[:::\]/g; 
           var arr = rx.exec&#40;text&#41;[1]; 
           arr = arr.split(&#039;-&#039;); 
           to = arr[1]; 
           messageID = arr[2]; 

         txt = text = text.replace(/\[:::\]_.+_\[:::\]/g, &#039;&#039;); 
      } 
       
      qt = quote.split(&#039;:&#039;); 
      if(qt[1].trim() != &#039;&#039;) 
      { 
         text += &#039;[_::_]&#039; + quote; 
         qt = &#039;&lt;div class=&amp;quotfrom&amp;quot &#039;+ colorQuote +&#039;&amp;quot&gt;&#039;+
 htmlEntities(qt[0]) +&#039;:&lt;/div&gt;&#039;+ htmlEntities(qt[1]) +&#039;&lt;/div&gt;&#039;; 
      } 
      else 
      { 
         qt = &#039;&#039;; 
      } 

      quote = undefined; 
   } 

   $(&#039;#messageBox&#039;).text(&#039;&#039;); 
   $(&#039;#txtedit&#039;).val(&#039;&#039;); 
   if($(&#039;.context&#039;).scrollTop() + $(&#039;.context&#039;).innerHeight() &gt;= $(&#039;.context&#039;)[0].scrollHeight) 
   { 
      $(&amp;quot.context&amp;quot).animate({ scrollTop: $(&#039;.context&#039;)[0].scrollHeight}, 1000); 
   } 
   if(txt.trim() != &#039;&#039;) 
   { 
      color = $(&#039;.me&#039;).attr(&#039;color&#039;); 
      txt = htmlEntities(txt); 
      txt = convertToImg(txt); 
      $(&#039;.context&#039;).append(&#039;&lt;div class=&amp;quotinner quote lfloat&amp;quot from=&amp;quotno&amp;quot&gt;&#039;+ qt.trim() 
+&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: &#039;+ color +&#039; !important;&amp;quot&gt;Me:&lt;/div&gt;&#039;+ txt +&#039;&lt;/div&gt;&#039;); 
      $(&#039;#send&#039;).attr(&#039;disabled&#039;, &#039;disabled&#039;); 
      $.ajax({ 
         url: &#039;http://localhost/chat/display&#039;, 
         async: true, 
         type: &amp;quotPOST&amp;quot, 
         data: &#039;text=&#039; + text.trim() + &#039;&amp;to=&#039; + to, 
         cache: false, 
         //dataType: &#039;json&#039;, 
         success: function (msg) { 
            if (msg == 1) 
            { 
               $(&#039;#send&#039;).removeAttr(&#039;disabled&#039;); 
            } 
         }, 
         error: function (msg) { 
            alert&#40;&#039;app has an error!&#039;&#41;; 
         } 
      }); 
   } 
}این تابع شرو میکنه به دریافت مقادیر مورد نیاز از textbox و چک کردن وجود نقل قول و ... تا به ارسال پیام میرسه.قبل از ارسال پیام من پیغام کاربرو در صفحه ی خودش چاپ کردم:$(&#039;.context&#039;).append(&#039;&lt;div class=&amp;quotinner quote lfloat&amp;quot from=&amp;quotno&amp;quot&gt;&#039;+ 
qt.trim() +&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: &#039;+ color +&#039; !important;&amp;quot&gt;Me:&lt;/div&gt;&#039;+ txt +&#039;&lt;/div&gt;&#039;);بعد هم بخش اصلی تابع که عمل سند پیام رو انجام میده این قسمت هست:$.ajax({ 
         url: &#039;http://localhost/chat/display&#039;, 
         async: true, 
         type: &amp;quotPOST&amp;quot, 
         data: &#039;text=&#039; + text.trim() + &#039;&amp;to=&#039; + to, 
         cache: false, 
         //dataType: &#039;json&#039;, 
         success: function (msg) { 
            if (msg == 1) 
            { 
               $(&#039;#send&#039;).removeAttr(&#039;disabled&#039;); 
            } 
         }, 
         error: function (msg) { 
            alert&#40;&#039;app has an error!&#039;&#41;; 
         } 
      });text حاوی پیام کاربره و to مشخص میکنه به چه کاربری داره ارسال میشه یعنی اگر صفر نباشد آیدی کاربری که پاسخش رو دادیم ارسال میکنه.نمایش پیام کاربرو قبل از آجاکس انجام دادم و append کردم.اولش این عمل نمایش پیامو داخل رویداد success گذاشته بوده ولی چون ارتباط comet باز بود و درخواست کاربر pending میموند تا برگرده و این عمل سند انجام بشه که به همین علت مدت زمانی طول میکشید و نمایش پیامی که سند کرده به تاخیر می افتاد, بخاطر همین قبل از ajax قرار دادم که به محض دکمه ی enter روی صفحه ی کاربر پیامش نمایش داده بشه.در ادامه , تابع ajax درخواستی رو برای آدرس display در صفحه ی index.php ارسال میکنه و در صفحه ی ایندکس این کد اومده:if (isset($parts[0])) 
      { 
         switch ($parts[0]) 
         { 
            case &#039;save&#039;: 
               $obj = new Save(); 
               echo $obj-&gt;save(); 
               break; 

            case &#039;display&#039;: 
               $obj = new Message(true); 
               echo $obj-&gt;display(); 
               break; 

            case &#039;logout&#039;: 
               session_destroy(); 
               Base::redirect($config-&gt;baseUrl . &#039;/login&#039;); 
               break; 

            case &#039;404&#039;: 
               exit(&#039;Pooof...&#039;); 
               break; 

            default: 
               Base::redirect($config-&gt;baseUrl . &#039;/404&#039;); 
               break; 
         }وقتی درخواست ارسال میشه به این خط کد مرسه و آبجکتی از کلاس message ساخته میشه و متد display صدا زده میشه:case &#039;display&#039;: 
               $obj = new Message(true); 
               echo $obj-&gt;display(); 
               break;نکته: شرط (case) اول , که save هست اضافیه. یادم رفته پاکش کنم.(چون اول دریافت و ارسال در دو کلاس مختلف انجام میشد و به دلیل اینکه درخواست دریافت پیام ها به مدت چند ثانیه pending میموند و عملیات ارسال هم باید منتظر میشد , تصمیم گرفتم عمل دریافت و ارسال رو در یک کلاس و در یک متد به نام display پیاده کنم.بنظرم خوبه اینجا یه stop به درخواست pending بدیم تا درخواست ارسال پیام اجرا بشه Surprised )  - (فایل دانلود اصلاح شد.)در ادامه وارد متد display میشه :public function display() 
   { 
      if(isset($_POST[&#039;text&#039;])) 
      { 
         $text = DB::Escape($_POST[&#039;text&#039;]); 
         $time = microtime(true); 
         $to_message_id = (isset($_POST[&#039;to&#039;]) &amp;&amp; $_POST[&#039;to&#039;] &gt; 0 ? intval($_POST[&#039;to&#039;]) : 0); 
         DB::Query(&amp;quotINSERT INTO `messages` 
(`user_id`, `to_message_id`, `text`, `timestamp`) 
VALUES (&#039;{$_SESSION[&#039;id&#039;]}&#039;, &#039;{$to_message_id}&#039;, &#039;{$text}&#039;, &#039;{$time}&#039;)&amp;quot); 
         if(DB::AffectedRows() &gt; 0) 
         { 
            return 1; 
         } 
         else 
         { 
            return DB::LastError(); 
         } 
      } 

      if(isset($_POST[&#039;time&#039;])) 
      { 
         $time = floatval($_POST[&#039;time&#039;]); 
         $current = time(); 
         $list = &#039;&#039;; 
         while (time() - $current &lt; 100) 
         { 
            $result = self::findAll(&amp;quotWHERE (`timestamp` &gt; &#039;{$time}&#039; AND `user_id` != &#039;{$_SESSION[&#039;id&#039;]}&#039;)&amp;quot); 
            if(count($result) &gt; 0) 
            { 
               $result = array_reverse($result); 
               $html = &#039;&#039;; 
               $to = &#039;&#039;; 
               foreach ($result as $obj) 
               { 
                  $qt = &#039;&#039;; 
                  $text = &#039;&#039;; 
                  if(strpos($obj-&gt;text, &#039;[_::_]&#039;)) 
                  { 
                     $array = explode(&#039;[_::_]&#039;, $obj-&gt;text); 
                     $text = $array[0]; 
                     $q = explode(&amp;quot:&amp;quot, $array[1]); 
                     $qt = &#039;
&lt;div class=&amp;quotfrom&amp;quot  &#039;. ($_SESSION[&#039;username&#039;] == trim($q[0]) ? $_SESSION[&#039;userColor&#039;] : &#039;#000&#039;) 
.&#039; !important;&amp;quot&gt;&#039;
. ($_SESSION[&#039;username&#039;] == trim($q[0]) ? &#039;Me&#039; : Base::HtmlEscape($q[0])) .&#039;:&lt;/div&gt;&#039;
. Base::HtmlEscape($q[1]) .&#039;&lt;/div&gt;&#039;; 
                  } 
                  else 
                  { 
                     $text = $obj-&gt;text; 
                  } 
                  $html .= &#039;&lt;div class=&amp;quotinner quote&amp;quot from=&amp;quot&#039;
. ($_SESSION[&#039;id&#039;] == $obj-&gt;user_id ? &amp;quotno&amp;quot : $obj-&gt;user_id . &#039;-&#039; 
. $obj-&gt;message_id) .&#039;&amp;quot&gt;&#039;. $qt .&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: &#039;. $obj-&gt;color .&#039; !important;&amp;quot&gt;&#039;
. Base::HtmlEscape($obj-&gt;username) .&#039;:&lt;/div&gt;&#039;. Base::convertToImg(Base::HtmlEscape($text)); 
                  $html .= &#039;&lt;div style=&amp;quotheight: 20px;&amp;quot&gt;&lt;/div&gt;&#039;; 
                  $html .= &#039;&lt;div class=&amp;quottime&amp;quot&gt;&#039;.
 Jdf::jdate(&#039;H:i:s | l , j F Y&#039;, (explode(&#039;.&#039;, $obj-&gt;timestamp)[0])). &#039;&lt;/div&gt;&#039;; 
                  $html .= &#039;&lt;/div&gt;&#039;; 
                  if ($_SESSION[&#039;id&#039;] == $obj-&gt;to_message_id) 
                  { 
                     $to .= &#039;&lt;div class=&amp;quotinnerMyMessages quote&amp;quot from=&amp;quot&#039;
. $obj-&gt;user_id . &#039;-&#039; . $obj-&gt;message_id .&#039;&amp;quot&gt;&#039;. $qt .&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: 
&#039;. $obj-&gt;color .&#039; !important;&amp;quot&gt;&#039;. Base::HtmlEscape($obj-&gt;username) 
.&#039;:&lt;/div&gt;&#039;. Base::convertToImg(Base::HtmlEscape($text)) .&#039;&lt;/div&gt;&#039;; 
                  } 
               } 
               return json_encode(array(&#039;message&#039; =&gt; $html, &#039;to&#039; =&gt; $to , 
&#039;time&#039; =&gt; $obj-&gt;timestamp, &#039;onlineUsers&#039; =&gt; substr($list, 0, -1))); 
            } 
            else 
            { 
               sleep(2); 
            } 
            $sess = Loader::load(&#039;Session&#039;); 
            $onlineUsers = $sess-&gt;onlineUsers(); 
            foreach ($onlineUsers as $user) 
            { 
               $list .= $user . &#039;.&#039;; 
            } 
         } 
         return json_encode(array(&#039;onlineUsers&#039; =&gt; substr($list, 0, -1))); 
      } 
   }دو تا شرط وجود داره.یکی وجود متغیر text و شرط دیگه وجود متغیر time. اگر متغیر تکست وجود داشت یعنی کاربر پیامی ثبت کرده.در قسمت دیتای تابع ajax , ما text رو ارسال کردیم.پس وارد این قسمت از متد میشه:if(isset($_POST[&#039;text&#039;])) 
      { 
         $text = DB::Escape($_POST[&#039;text&#039;]); 
         $time = microtime(true); 
         $to_message_id = (isset($_POST[&#039;to&#039;]) &amp;&amp; $_POST[&#039;to&#039;] &gt; 0 ? intval($_POST[&#039;to&#039;]) : 0); 
         DB::Query(&amp;quotINSERT INTO `messages` 
(`user_id`, `to_message_id`, `text`, `timestamp`) 
VALUES (&#039;{$_SESSION[&#039;id&#039;]}&#039;, &#039;{$to_message_id}&#039;, &#039;{$text}&#039;, &#039;{$time}&#039;)&amp;quot); 
         if(DB::AffectedRows() &gt; 0) 
         { 
            return 1; 
         } 
         else 
         { 
            return DB::LastError(); 
         } 
      }و عملیات ثبت پیام در دیتابیس انجام میشه.بهتر بود بعد از عملیات insert , بجای return 1 عمل سلکت هم انجام میدادم و نتایج جدید return میکردم. کل ماجرای سند پیام جدید اینه.بقیه ی کدهایی که در فایل send.js اومده , تجزیه و بررسی متن وارد شده ی کاربره.دریافت پیام کاربران در حین چت: (فایل display.js)بعد از ورد به صفحه ی چت و لود صفحه تابع displayMessage اجرا میشه:$(document).ready(function() { 
   $(window).load(displayMessage(lastTimestamp)); 
});ین تابع یک درخواست به سمت سرور ارسال میکنه و ما در سمت سرور تا زمانی که رکورد جدیدی از دیتابیس پیدا نکنیم حلقه ای رو سمت سرور تکرار میکنیم و پاسخی به سمت کلاینت نمیفرستیم.به محض دریافت رکورد جدید , پاسخ به سمت کاربر ارسال میشه و پاسخ دریافت شده در رویداد success بعد از چند بررسی به کاربر نمایش داده میشود و بعد از نمایش پیام های جدید یک وقفه ی چند ثانیه با تابع settimeout ایجاد میکنیم و دوباره تابع displayMessage رو صدا میزنیم تا همین مرحله ها از ابتدا تکرار شوند و درخواستی سمت سرور ارسال شود.یعنی با کامت request ها و response ها رو به حداقل رسوندیم.تابع displayMessage:function displayMessage(time) 
{ 
   $.ajax({ 
      url: &#039;http://localhost/chat/display&#039;, 
      async: true, 
      type: &amp;quotPOST&amp;quot, 
      data: &#039;time=&#039; + time, 
      cache: false, 
      dataType: &#039;json&#039;, 
      success: function (msg) { 
         //alert&#40;msg[&#039;message&#039;]&#41;;return; 
         if(msg[&#039;message&#039;]) 
         { 
            if($(&#039;.context&#039;).scrollTop() + $(&#039;.context&#039;).innerHeight() &gt;= $(&#039;.context&#039;)[0].scrollHeight) 
            { 
               $(&amp;quot.context&amp;quot).animate({ scrollTop: $(&#039;.context&#039;)[0].scrollHeight}, 1000); 
            } 
            $(&#039;.context&#039;).append(msg[&#039;message&#039;]); 
            if(msg[&#039;to&#039;] != &#039;&#039;) 
            { 
               $(&#039;.my-messages&#039;).append(msg[&#039;to&#039;]); 
            } 
            time = msg[&#039;time&#039;]; 
            //alert&#40;time&#41; 
         } 
         if(msg[&#039;onlineUsers&#039;]) 
         { 
            var users = msg[&#039;onlineUsers&#039;].split(&#039;.&#039;); // point 
            var dvUsers = &#039;&#039;; 
            users = array_unique(users); 
             
            $(&#039;.users .innerUsers&#039;).each(function() { 
               dvUsers += $(this).text().trim() + &#039;,&#039;; 
            }); 

            dvUsers = dvUsers.split(&#039;,&#039;); 
            dvUsers = dvUsers.filter(function(a){return  a != &#039;&#039;;}); 

            for (var i = 0; i &lt; users.length; i++) 
            { 
               if($(&#039;.users&#039;).html().indexOf(users[i]) == -1) 
               { 
                  $(&#039;&lt;div class=&amp;quotinnerUsers &#039;+ users[i] +&#039;&amp;quot&gt;&#039;+ users[i] +&#039;&lt;/div&gt;&#039;).appendTo(&#039;.users&#039;).fadeIn(1000); 
               } 

               if(dvUsers.indexOf(users[i]) != -1) 
               { 
                  delete dvUsers[dvUsers.indexOf(users[i])]; 
                  dvUsers = dvUsers.filter(function(a){return typeof a !== &#039;undefined&#039;;}); 
               } 
            }; 

            if(dvUsers.length &gt; 0) 
            { 
               $.each(dvUsers, function(index, value) { 
                  $(&#039;.&#039; + value).remove(); 
               }); 
            } 
         } 
         setTimeout&#40;displayMessage, 2000, time&#41;; 
      }, 
      error: function (msg) { 
         displayMessage(time); 
      } 
   }); 
}این تابع یک time میگیرد.در واقع مقدار time برابر time آخرین رکوردیست که از دیتابیس فراخوانی شده و نمایش داده شده. زمانیکه پیج چت را باز میکنیم , در صفحه ی main.php گفته شده 100 پیام آخر داخل دیتابیس , در صفحه نمایش داده شود.و در ادامه ی همین کدهای main.php این کدو گذاشتم: 
         var lastTimestamp = &lt;?php echo (isset($obj) ? $obj-&gt;timestamp : 0); ?&gt;; 
متغیری در جاواسکریپت تعریف کردم و مقدارشو برابر timestamp آخرین رکورد fetch شده قرار دادم.این timestamp بعنوان اولین مقدار time و در اولین اجرای تابع displayMessage قرار میگیره.در فراخوانی های بعدی تابع این مقدار جایگزین میشه. بعد از اجرای displayMessage آجاکس فراخوانی میشه و در خواستی به همان آدرس قبلی یعنی display به صفحه ی index.php ارسال میشه که حاوی دیتای time است.در صفحه ی index.php و شرط switch بازهم شرط display اجرا میشود. متد display فراخوانی میشود و از بین دو شرطی که در پست قبل گفتیم , کد شرط وجود time اجرا میشود:if(isset($_POST[&#039;time&#039;])) 
      { 
         $time = floatval($_POST[&#039;time&#039;]); 
         $current = time(); 
         $list = &#039;&#039;; 
         while (time() - $current &lt; 100) 
         { 
            $result = self::findAll(&amp;quotWHERE (`timestamp` &gt; &#039;{$time}&#039; AND `user_id` != &#039;{$_SESSION[&#039;id&#039;]}&#039;)&amp;quot); 
            if(count($result) &gt; 0) 
            { 
               $result = array_reverse($result); 
               $html = &#039;&#039;; 
               $to = &#039;&#039;; 
               foreach ($result as $obj) 
               { 
                  $qt = &#039;&#039;; 
                  $text = &#039;&#039;; 
                  if(strpos($obj-&gt;text, &#039;[_::_]&#039;)) 
                  { 
                     $array = explode(&#039;[_::_]&#039;, $obj-&gt;text); 
                     $text = $array[0]; 
                     $q = explode(&amp;quot:&amp;quot, $array[1]); 
                     $qt = &#039;&lt;div class=&amp;quotfrom&amp;quot color: &#039;
. ($_SESSION[&#039;username&#039;] == trim($q[0]) ? $_SESSION[&#039;userColor&#039;] : &#039;#000&#039;) 
.&#039; !important;&amp;quot&gt;&#039;. ($_SESSION[&#039;username&#039;] == trim($q[0]) ? &#039;Me&#039; : Base::HtmlEscape($q[0])) 
.&#039;:&lt;/div&gt;&#039;. Base::HtmlEscape($q[1]) .&#039;&lt;/div&gt;&#039;; 
                  } 
                  else 
                  { 
                     $text = $obj-&gt;text; 
                  } 
                  $html .= &#039;&lt;div class=&amp;quotinner quote&amp;quot from=&amp;quot&#039;
. ($_SESSION[&#039;id&#039;] == $obj-&gt;user_id ? &amp;quotno&amp;quot : $obj-&gt;user_id . &#039;-&#039; 
. $obj-&gt;message_id) .&#039;&amp;quot&gt;&#039;. $qt .&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: &#039;. $obj-&gt;color .&#039; !important;&amp;quot&gt;&#039;
. Base::HtmlEscape($obj-&gt;username) .&#039;:&lt;/div&gt;&#039;
. Base::convertToImg(Base::HtmlEscape($text)); 
                  $html .= &#039;&lt;div style=&amp;quotheight: 20px;&amp;quot&gt;&lt;/div&gt;&#039;; 
                  $html .= &#039;&lt;div class=&amp;quottime&amp;quot&gt;&#039;
. Jdf::jdate(&#039;H:i:s | l , j F Y&#039;, (explode(&#039;.&#039;, $obj-&gt;timestamp)[0])). &#039;&lt;/div&gt;&#039;; 
                  $html .= &#039;&lt;/div&gt;&#039;; 
                  if ($_SESSION[&#039;id&#039;] == $obj-&gt;to_message_id) 
                  { 
                     $to .= &#039;&lt;div class=&amp;quotinnerMyMessages quote&amp;quot from=&amp;quot&#039;
. $obj-&gt;user_id . &#039;-&#039; . $obj-&gt;message_id .&#039;&amp;quot&gt;&#039;. $qt 
.&#039;&lt;div class=&amp;quotfrom&amp;quot style=&amp;quotcolor: &#039;. $obj-&gt;color .&#039; !important;&amp;quot&gt;&#039;
. Base::HtmlEscape($obj-&gt;username) .&#039;:&lt;/div&gt;&#039;
. Base::convertToImg(Base::HtmlEscape($text)) .&#039;&lt;/div&gt;&#039;; 
                  } 
               } 
               return json_encode(array(&#039;message&#039; =&gt; $html, &#039;to&#039; =&gt; $to ,
 &#039;time&#039; =&gt; $obj-&gt;timestamp, &#039;onlineUsers&#039; =&gt; substr($list, 0, -1))); 
            } 
            else 
            { 
               sleep(2); 
            } 
            $sess = Loader::load(&#039;Session&#039;); 
            $onlineUsers = $sess-&gt;onlineUsers(); 
            foreach ($onlineUsers as $user) 
            { 
               $list .= $user . &#039;.&#039;; 
            } 
         } 
         return json_encode(array(&#039;onlineUsers&#039; =&gt; substr($list, 0, -1))); 
      }کل کدها به کنار قسمت اصلی کد , حلقه ی while با تکرار به مدت 100 ثانیه هست:(این مقدار بسته به منابع هاست میتونه کم یا زیاد بشه!)while (time() - $current &lt; 100)هر بار که این حلقه اجرا میشود , یک کوئری اجرا میشود:$result = self::findAll(&amp;quotWHERE (`timestamp` &gt; &#039;{$time}&#039; AND `user_id` != &#039;{$_SESSION[&#039;id&#039;]}&#039;)&amp;quot);این کوئری تمام رکوردهایی که timestamp اشون بزرگتر از time ارسالی توسط آجاکس(یعنی time آخرین رکورد نمایش داده شده در صفحه ی کاربر) است رو واکشی میکنه.البته این شرط هم وجود داره که پیام هایی واکشی بشن که فرستندشون خود کاربر نباشن.(چون پیام های خود کاربر به محض سند شدن رو صفحه ی خودش یکبار نمایش داده شده.اینجا فرضو براین گرفتم که یک یوزر با یک مرورگر قراره باز بشه و بصورت همزمان کسی نباید باز کنه.)میشد شرطو جابجا کرد و اول user_id چک بشه و بعد AND بشه با شرط timestamp. چون اگه شرط user_id برقرار نبود دیگه شرط timestamp اجرا نشه و باعث سرعت بیشتری در کوئری بشه.خلاصه بعد از عمل fetch کردن اگر رکوردی پیدا شد if اجرا میشه و تمام پیام های جدید واکشی شده بسمت کلاینت return میشن و در success دریافت و نمایش داده میشن و بعد از چند ثانیه دوباره یک درخواست جدید بهمراه آخرین timestamp بسمت سرور فرستاده میشه.اما اگر رکوردی یافت نشد یعنی پیغامی ثبت نشده , بنابراین else اجرا میشود. در قسمت else :else 
{ 
      sleep(2); 
}به مدت دوثانیه وقفه ایجاد کردیم تا به سرور فشار زیادی وارد نشه. و بعد از else دوباره این حلقه اجرا میشه.این حلقه در مدت 100 ثانیه ای که تکرار میشه همین روالو ادامه میده.اگر رکوردی پیدا شد ارتباط قطع میشه قطع میشه و پاسخ به سمت کلاینت فرستاده میشود. اما اگر در مدت 100 ثانیه رکوردی پیدا نشد این اسکریپت به پایان میرسه و تنها تعداد کاربران آنلاینو return میکنه.یک نکته در مورد time: در اول کار , زمان رو برحسب ثانیه وارد دیتابیس میکردم.ولی در تست هایی که انجام دادم , وقتی سریع و پشت سرهم مثلا عدد 1 تا 9 ارسال میکردم , در نمایش پیام ها قاطی میکرد.بعضی هارو هم جا مینداخت. یعنی در دیتابیس درست درج شده بود ولی در نمایش به مشکل میخورد. واسه همین فیلد timestamp رو double گذاشتم و مقدار تایم هر پیام رو با microtime ذخیره کردم.توجه: این اسکریپت 6-7 سال پیش نوشته شده و اصلاً مبنای گرافیکی و طراحی نداشته و فقط بر پایه ی پیاده سازی سیستمی با تکنولوژی های متفاوت بوده است.(که هنوز هم ازین تکنولوژی میتوان استفاده کرد.)برنامه نویس: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 13:10:20 +0330</pubDate>
            </item>
                    <item>
                <title>توابع کاربردی آرایه ها</title>
                <link>https://virgool.io/@bidak/%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%DB%8C-%D8%A2%D8%B1%D8%A7%DB%8C%D9%87-%D9%87%D8%A7-srm6pagidwwq</link>
                <description>سلام.همونطور که از عنوان مطلب مشخصه توابعی که بیشتر کاربرد دارنو اینجا معرفی میکنم.تابع array_keysاین تابع 3 تا آرگومان داره:اولین پارامتری که میگیره  , یک آرایه اس.دومین پارامتری که میگیره , search value هست یعنی کلمه ای که میخواین دنبالش بگردین.(اختیاری)و سومین نوع operator مقایسه ای هست واسه شرط تساوی با search value. (اختیاری , مقدار پیش فرض = false)توضیحات:این تابع ایندکس های یک آرایه رو بر میگردونه.$array = array(0 =&gt; 100, &amp;quotcolor&amp;quot =&gt; &amp;quotred&amp;quot);
print_r(array_keys($array));خروجی:Array
(
    [0] =&gt; 0
    [1] =&gt; color
)* سایت php وقتی آرگومانیرو داخل کروشه ( ] ) گذاشت یعنی وجودش اختیاریه.توو مثال قبلی فقط یک پارامتر داده و پارامتر دوم چیزی واسه سرچ ندادیم , بنابر این سرچی انجام نمیشه و تمام ایندکس هارو بر میگردونه.مثال با پارامتر دوم:$array = array(&amp;quotblue&amp;quot, &amp;quotred&amp;quot, &amp;quotgreen&amp;quot, &amp;quotblue&amp;quot, &amp;quotblue&amp;quot);
print_r(array_keys($array, &amp;quotblue&amp;quot));خروجی:Array
(
    [0] =&gt; 0
    [1] =&gt; 3
    [2] =&gt; 4
)اینجا چون پارامتر دوم داده پس تمام ایندکس هارو بر نمیگردونه و دنبال اون چیزی که سرچ شده میگرده که ایجا دنبال blue بوده و خانه ی صفرم و سوم و چهارم این عبارتو داره که برگردونده.پارامتر سوم اگر true بدید زمان سرچ از operator سه تا مساوی (===) برای مقایسه استفاده میکنه.تفاوت سه تا مساوی با دو تا مساوی در شرط اینه که سه تا مساوی علاوه بر برابر بودن مقادیر باید type اشونم یکی باشه یعنی هر دو عدد باشن یا هر دو رشته باشن بعبارت دیگه سه تا مساوی برابر این شرطه:if ($a==$b &amp;&amp; gettype($a) == gettype($b))* gettype نوع متغیر بر میگردونه.تابع array_mergeاین تابع آرایه هارو ادغام میکنه.هر تعدادی که پارامتر بدید.$array1 = array(&amp;quotcolor&amp;quot =&gt; &amp;quotred&amp;quot, 2, 4);
$array2 = array(&amp;quota&amp;quot, &amp;quotb&amp;quot, &amp;quotcolor&amp;quot =&gt; &amp;quotgreen&amp;quot, &amp;quotshape&amp;quot =&gt; &amp;quottrapezoid&amp;quot, 4);
$result = array_merge($array1, $array2);
print_r($result);خروجی:Array
(
    [color] =&gt; green
    [0] =&gt; 2
    [1] =&gt; 4
    [2] =&gt; a
    [3] =&gt; b
    [shape] =&gt; trapezoid
    [4] =&gt; 4
)* green جایگزین red شده , چون ایندکس هاشون یکسانه.تابع array_reverseخوب اینم مشخصه دیگه.آرایه رو معکوس میکنه.$input  = array(&amp;quotphp&amp;quot, 4.0, array(&amp;quotgreen&amp;quot, &amp;quotred&amp;quot));
$reversed = array_reverse($input);

print_r($input);
print_r($reversed);خروجی:// input
Array
(
    [0] =&gt; php
    [1] =&gt; 4
    [2] =&gt; Array
        (
            [0] =&gt; green
            [1] =&gt; red
        )

)

###################################

// reversed
Array
(
    [0] =&gt; Array
        (
            [0] =&gt; green
            [1] =&gt; red
        )

    [1] =&gt; 4
    [2] =&gt; php
)* این تابع پارامتر دومش اختیاریه و پیش فرض false. اگر مقدار true بگیره ایندکس های عددی ایندکسشون عوض نمیشه و فقط جاش تغییر میکنه و ایندکس های غیر عددی خودشونن.مقدار ایندکس هارو ببینید.ادامه ی مثال بالا:$preserved = array_reverse($input, true);
print_r($preserved);خروجی:Array
(
    [2] =&gt; Array
        (
            [0] =&gt; green
            [1] =&gt; red
        )

    [1] =&gt; 4
    [0] =&gt; php
)تابع array_valuesیک آرگومان داره و مقادیر یک آرایه رو برمیگردونه.$array = array(&amp;quotsize&amp;quot =&gt; &amp;quotXL&amp;quot, &amp;quotcolor&amp;quot =&gt; &amp;quotgold&amp;quot);
print_r(array_values($array));خروجی:Array
(
    [0] =&gt; XL
    [1] =&gt; gold
)تابع array_change_key_caseحروف ایندکس آرایه رو به حروف کوچک یا بزرگ تبدیل میکنه.دو تا آرگومان داره:آرایه میگیرهحالتو میگیره : CASE_UPPER یا CASE_LOWER  , اختیاریه و پیش فرض = CASE_LOWER$input_array = array(&amp;quotFirSt&amp;quot =&gt; 1, &amp;quotSecOnd&amp;quot =&gt; 4);
print_r(array_change_key_case($input_array, CASE_UPPER));خروجی:Array
(
    [FIRST] =&gt; 1
    [SECOND] =&gt; 4
)تابع rengeیک آرایه شامل طیفی از عناصر میسازه.سه تا آرگومان داره:مقداری برای گام شروعمقداری برای گام پایانگام پیمایش مراحل , عددی مثبت میگیره و (اختیاریه) و پیش فرض = 1$a = range(0, 12);      // array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12)
$b = range(0, 100, 10); // array(0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100)
$c = range(&#039;a&#039;, &#039;i&#039;);   // array(&#039;a&#039;, &#039;b&#039;, &#039;c&#039;, &#039;d&#039;, &#039;e&#039;, &#039;f&#039;, &#039;g&#039;, &#039;h&#039;, &#039;i&#039;)
$d = range(&#039;c&#039;, &#039;a&#039;);   // array(&#039;c&#039;, &#039;b&#039;, &#039;a&#039;)تابع listلیستی از متغیر ها میگیره و این متغیر هارو بترتیب مقدار دهی میکنه.$info = array(&#039;coffee&#039;, &#039;brown&#039;, &#039;caffeine&#039;); // an array
list($drink, $color, $power) = $info;
echo $drink; // coffee
echo $color; // brown
echo $power; // caffeineتابع countتعداد عناصرو شمارش میکنه.2 تا آرگومان:یک آرایه یا یک آبجکت قابل شمارش میگیرهmode میگیره که با COUNT_RECURSIVE یا 1 مقدار دهی میشه و (اختیاریه)بصورت بازگشتی شمارش میکنه و تمام عناصر آرایه های چند بعدی رو شمارش میکنه.$a[0] = 1;
$a[1] = 3;
$a[2] = 5;
$result = count($a);
// $result == 3

$b[0]  = 7;
$b[5]  = 9;
$b[10] = 11;
$result = count($b);
// $result == 3

$result = count(null);
// $result == 0

$result = count(false);
// $result == 1مثال بهمراه recursive count:$food = array(&#039;fruits&#039; =&gt; array(&#039;orange&#039;, &#039;banana&#039;, &#039;apple&#039;),
              &#039;veggie&#039; =&gt; array(&#039;carrot&#039;, &#039;collard&#039;, &#039;pea&#039;));

// recursive count
echo count($food, COUNT_RECURSIVE); // output 8

// normal count
echo count($food); // output 2تابع is_arrayیک متغیر  میگیره و چک میکنه که متغیری که دادیم , یک آرایه هست یا نه.$yes = array(&#039;this&#039;, &#039;is&#039;, &#039;an array&#039;);

echo is_array($yes) ? &#039;Array&#039; : &#039;not an Array&#039;;
// output =&gt; Array

#####################################

$no = &#039;this is a string&#039;;

echo is_array($no) ? &#039;Array&#039; : &#039;not an Array&#039;;
// output =&gt; not an Array* جمله های شرطی سه گانه : اولین قسمت جمله ی شرطی هست و بعد از علامت سؤال اگر شرط برقرار باشد اجرا میشه و در غیر اینصورت بعد از : اجرا میشه.تابع array_count_valuesاین تابع یک آرایه میگیره و یک آرایه برمیگردونه که مشخص میکنه هر value چند بار تکرار شده.آرایه ای که برمیگردونه , ایندکس هاش value آرایه ی اوله و مقادیرش تعداد تکرار هر value:$array = array(1, &amp;quothello&amp;quot, 1, &amp;quotworld&amp;quot, &amp;quothello&amp;quot);
print_r(array_count_values($array));خروجی:Array
(
    [1] =&gt; 2
    [hello] =&gt; 2
    [world] =&gt; 1
)تابع array_flipیک آرایه میگیره و آرایه ای بر میگردونه که جای index  و value عوض شده.یعنی در آرایه جدید ,  ایندکس آرایه ی ورودی میشه مقدار آرایه جدید و مقدار آرایه ورودی میشه ایندکس آرایه جدید.$input = array(&amp;quotoranges&amp;quot, &amp;quotapples&amp;quot, &amp;quotpears&amp;quot);
$flipped = array_flip($input);

print_r($flipped);خروجی:Array
(
    [oranges] =&gt; 0
    [apples] =&gt; 1
    [pears] =&gt; 2
)

############################

$input = array(&amp;quota&amp;quot =&gt; 1, &amp;quotb&amp;quot =&gt; 1, &amp;quotc&amp;quot =&gt; 2);
$flipped = array_flip($input);

print_r($flipped);خروجی:Array
(
    [1] =&gt; b
    [2] =&gt; c
)تابع array_sumآرایه ای از مقادیر میگیره و جمع مقادیرو بصورت عددی یا اعشاری بر میگردونه.$a = array(2, 4, 6, 8);
echo &amp;quotsum(a) = &amp;quot . array_sum($a) . &amp;quot\n&quot;
// sum(a) = 20

##########################

$b = array(&amp;quota&amp;quot =&gt; 1.2, &amp;quotb&amp;quot =&gt; 2.3, &amp;quotc&amp;quot =&gt; 3.4);
echo &amp;quotsum(b) = &amp;quot . array_sum($b) . &amp;quot\n&quot;
// sum(b) = 6.9تابع shuffleیک آرایه میگیره (بصورت ارجاعی) و مقادیر اون آرایه بصورت رندم مخلوط میشن و جاهاشون عوض میشه.$numbers = range(1, 10); // array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
shuffle($numbers);
print_r($numbers);

##########################

// numbers
Array
(
    [0] =&gt; 7
    [1] =&gt; 6
    [2] =&gt; 5
    [3] =&gt; 1
    [4] =&gt; 9
    [5] =&gt; 4
    [6] =&gt; 2
    [7] =&gt; 3
    [8] =&gt; 10
    [9] =&gt; 8
)* چون shuffle بصورت ارجاعی آرایه رو میگیره , تغییرات روی خودش صورت میگیره.تابع array_pushرفتارش مثل یک پشته (stack) است و مقادیری که میخوایم اضافه کنیم به آخر آرایه اضافه میشن.کاری که array_push انجام میده , مثل مثال زیره:$array[] = $var;تابع array_push  پارامتر اولی که میگیره , یک آرایه بصورت ارجاعی است و پارامترهای بعدی بسته به تعداد عناصری که میخوایم به انتهای یک آرایه اضافه کنیم میان.$stack = array(&amp;quotorange&amp;quot, &amp;quotbanana&amp;quot);
array_push($stack, &amp;quotapple&amp;quot, &amp;quotraspberry&amp;quot);
print_r($stack);

##########################

Array
(
    [0] =&gt; orange
    [1] =&gt; banana
    [2] =&gt; apple
    [3] =&gt; raspberry
)تابع array_ushiftبر خلاف تابع  (array_push) , که در مثال قبل معرفی کردیم , تابع array_ushift مانند یک صف عمل میکند و مقادیری که به تابع میدیم به ابتدای آرایه ی مد نظر , اضافه میکنه.مثل مثال قبل , این تابع هم یک آرایه بصورت ارجاعی میگیره و عناصر بعدی که به عنوان پارامتر میان , به ابتدای آرایه اضافه میشن.$queue = array(&amp;quotorange&amp;quot, &amp;quotbanana&amp;quot);
array_unshift($queue, &amp;quotapple&amp;quot, &amp;quotraspberry&amp;quot);
print_r($queue);

##########################

Array
(
    [0] =&gt; apple
    [1] =&gt; raspberry
    [2] =&gt; orange
    [3] =&gt; banana
)تابع array_popاین تابع یک آرایه بصورت ارجاعی میگیره و آخرین مقدار از آرایه ی ارجاعی رو حذف میکنه و بعنوان نتیجه میتونیم در یک متغیر دیگه بریزیمش یا اینکه چاپش کنیم.$stack = array(&amp;quotorange&amp;quot, &amp;quotbanana&amp;quot, &amp;quotapple&amp;quot, &amp;quotraspberry&amp;quot);
$fruit = array_pop($stack);
echo $fruit; // output =&gt; raspberry

print_r($stack);

##################

// stack
Array
(
    [0] =&gt; orange
    [1] =&gt; banana
    [2] =&gt; apple
)تابع array_shiftبرخلاف مثال قبلی که زدیم (array_pop) عمل میکند.array_shift یک آرایه بصورت ارجاعی میگیره و اولین خانه آرایرو رد میده و حذف میکنه و این خانه از آرایه رو میتونیم بعنوان نتیجه برگردونیم.$stack = array(&amp;quotorange&amp;quot, &amp;quotbanana&amp;quot, &amp;quotapple&amp;quot, &amp;quotraspberry&amp;quot);
$fruit = array_shift($stack);
echo $fruit; // output =&gt; orange

print_r($stack);

#######################

// stack
Array
(
    [0] =&gt; banana
    [1] =&gt; apple
    [2] =&gt; raspberry
)تابع array_key_exists&lt;br/&gt;چک میکنه , ایندکسی که دنبالش هستیم در آرایه مد نظر وجود داره یا نه , اگر وجود داشت , جواب true است در غیر اینصورت false.دو تا پارامتر میگیره:ایندکسی که به دنبالش هستیمآرایه ی مد نظر$search_array = array(&#039;first&#039; =&gt; 1, &#039;second&#039; =&gt; 4);
if (array_key_exists(&#039;first&#039;, $search_array)) // true
{
    echo &amp;quotThe &#039;first&#039; element is in the array&quot;
}مثال قبلو اگر برای ایندکس first با تابع isset چک کنیم نتیجه ی false برمیگردونه.دلیلش این هست که مقدار first برابره null هست. ولی اگر بدنبال این هستیم که همچین ایندکسی در این آرایه اصلا وجود داره یا نه باید از array_key_exists استفاده کنیم:$search_array = array(&#039;first&#039; =&gt; null, &#039;second&#039; =&gt; 4);

// returns false
echo isset($search_array[&#039;first&#039;]);

// returns true
echo array_key_exists(&#039;first&#039;, $search_array);تابع array_sliceاستخراج قسمتی از آرایه.4 آرگومان دارد:یک آرایه میگیرهنقطه ی شروع* اگر مقدار منفی باشد از انتهای آرایه محاسبه میشه.طول (اختیاری)* اگر طول بزرگتر از طول آرایه باشد , تنها مقادیر قابل دسترس و موجود برمیگردد.* اگر طول منفی باشد , از پایان آرایه به اندازه طول داده شده از جواب حذف میشود.اگر مقدار true بدیم نام index های (عددی) باقی میمونه در غیر اینصورت by default ایندکس ها از 0 مقدار دهی میشن. (اختیاری)$input = array(&amp;quota&amp;quot, &amp;quotb&amp;quot, &amp;quotc&amp;quot, &amp;quotd&amp;quot, &amp;quote&amp;quot);

$output = array_slice($input, 2);      // returns &amp;quotc&amp;quot, &amp;quotd&amp;quot, and &amp;quote&amp;quot
$output = array_slice($input, -2, 1);  // returns &amp;quotd&amp;quot
$output = array_slice($input, 0, 3);   // returns &amp;quota&amp;quot, &amp;quotb&amp;quot, and &amp;quotc&amp;quotمثال دیگه:$input = array(&amp;quota&amp;quot, &amp;quotb&amp;quot, &amp;quotname&amp;quot=&gt;&amp;quotc&amp;quot, 2=&gt;&amp;quotd&amp;quot, &amp;quote&amp;quot);

$output = array_slice($input, 2, -1);
print_r($output);

*************

// result
Array
(
    [name] =&gt; c
    [0] =&gt; d
)

########################################

$input = array(&amp;quota&amp;quot, &amp;quotb&amp;quot, &amp;quotname&amp;quot=&gt;&amp;quotc&amp;quot, 2=&gt;&amp;quotd&amp;quot, &amp;quote&amp;quot, &amp;quotf&amp;quot);

$output = array_slice($input, 2, -1, true);
print_r($output);

**************
// result
Array
(
    [name] =&gt; c
    [2] =&gt; d
    [3] =&gt; e
)تابع array_searchmixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )3 آرگومان:مقداری که به دنبالش هستیم.آرایه ی مد نظرآیا type مقادیر , در نظر گرفته شود یا خیر. (اختیاری - پیش فرض : نوع مقادیر لحاظ نمی شود.)همونطور که از اسم تابع مشخصه , مقداری به تابع میدیم و در آرایه , این مقدار جستجو میشه.اگر عبارت پیدا شد , کلید مقدار برگردانده میشود در غیر اینصورت False.مثال:$array = array(0 =&gt; &#039;blue&#039;, 1 =&gt; &#039;red&#039;, 2 =&gt; &#039;green&#039;, 3 =&gt; &#039;red&#039;);

$key = array_search(&#039;green&#039;, $array, true);   // $key = 2;
$key = array_search(&#039;red&#039;, $array);           // $key = 1;اما اگر پارامتر سومو true بذاریم , در مقایسه از (===) استفاده میشود یعنی type هم در نظر گرفته میشود.تفاوت true و false رو تو مثال زیر دیده میشه: (تفاوت رشته و مقدار عددی)$array = array(
			0 =&gt; &#039;1&#039;, 
			1 =&gt; &#039;2&#039;, 
			2 =&gt;  2 , 
			3 =&gt; &#039;3&#039;
		);

$key = array_search(2, $array, FALSE); // $key = 1;
$key = array_search(2, $array, TRUE);   // $key = 2;نویسنده: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 12:47:20 +0330</pubDate>
            </item>
                    <item>
                <title>توابع کاربردی رشته ها</title>
                <link>https://virgool.io/@bidak/%D8%AA%D9%88%D8%A7%D8%A8%D8%B9-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%DB%8C-%D8%B1%D8%B4%D8%AA%D9%87-%D9%87%D8%A7-er15qbzkjzjf</link>
                <description>تابع strlenیک آرگومان:یک رشته میگیره.طول رشته را برمیگرداند.$str = &#039;abcdef&#039;;
echo strlen($str); // 6

$str = &#039; ab cd &#039;;
echo strlen($str); // 7* نکته: حالا strlen رو برای رشته ی فارسی اگه استفاده کنیم , هر کاراکترو دو بایت بحساب میاره :(علت: چون strlen تعداد بایت هارو خروجی میده و براساس مجموعه ASCII که یک بایتی هست محاسبه میشود نه Unicode های چند بایتی)$str = &#039;محسن&#039;;
echo strlen($str); // 8برای حل این موضوع , باید از تابع mb_strlen استفاده کنیم.تابع mb_strlenدو آرگومان داره:رشته میگیرهانکدینگ میگیره (اختیاری - که ما از utf-8 استفاده میکنیم.)$str = &#039;محسن&#039;;
echo mb_strlen($str, &#039;utf-8&#039;); // 4* توابع کار با رشته های کدگذاری شده(چند بایتی) با (mb_(funcName شروع میشن.تابع str_word_countسه آرگومان:یک رشته میگیرد.یک عدد بین 0 تا 2 میگیرد (اختیاری - پیش فرض = 0)* 0 =&gt; تعداد کلمات یک رشته رو بر میگردونه.* 1 =&gt; آرایه ای از کلمات رشته برمیگردونه و هر کلمه رو در یک خانه ی آرایه قرار میده.* 2 =&gt; مانند مورد قبلی آرایه ای از کلمات رشته بر میگردونه ولی ایندکس هاشون برابر موقعیتی هست که در جمله دارن.         ++ اولین کاراکتر در رشته موقعیت صفر داره و بعدی 1 و بعدی 2 و ...         ++ اولین کاراکتر کلمه در رشته , بیانگر موقعیت کلمه هست.لیستی از کاراکتر هایی که میتونیم مشخص کنیم تا بعنوان جزئی از یک کلمه محسوب بشن. (مثلا نقطه یا عدد)  (اختیاری)$str = &amp;quotHello fri3nd, you&#039;re
       looking          good today!&amp;quot


// Example 1
print_r(str_word_count($str, 1));
// Example 2
print_r(str_word_count($str, 2));
// Example 3
print_r(str_word_count($str, 1, &#039;àáãç3&#039;));

// Example 4
echo str_word_count($str);خروجی ها:// Example 1
Array
(
    [0] =&gt; Hello
    [1] =&gt; fri
    [2] =&gt; nd
    [3] =&gt; you&#039;re
    [4] =&gt; looking
    [5] =&gt; good
    [6] =&gt; today
)

// Example 2
Array
(
    [0] =&gt; Hello
    [6] =&gt; fri
    [10] =&gt; nd
    [14] =&gt; you&#039;re
    [29] =&gt; looking
    [46] =&gt; good
    [51] =&gt; today
)

// Example 3
Array
(
    [0] =&gt; Hello
    [1] =&gt; fri3nd
    [2] =&gt; you&#039;re
    [3] =&gt; looking
    [4] =&gt; good
    [5] =&gt; today
)

// Example 4
7تابع strtolowerیک آرگومان:رشته میگیرهکاراکتر ها رو تبدیل به کاراکتر های کوچک میکند.$str = &amp;quotMary Had A Little Lamb and She LOVED It So&amp;quot
$str = strtolower($str);
echo $str; // Prints: mary had a little lamb and she loved it soتابع strtoupperبرعکس تابع قبلی , رشته تبدیل به حروف بزرگ میشن.$str = &amp;quotMary Had A Little Lamb and She LOVED It So&amp;quot
$str = strtoupper($str);
echo $str; // Prints: MARY HAD A LITTLE LAMB AND SHE LOVED IT SOتابع ucfirstیک آرگومان:رشته میگیرهاولین کاراکتر رشته رو به کاراکتر بزرگ تبدیل میکنه (first upper case).$foo = &#039;hello world!&#039;;
$foo = ucfirst($foo);             // Hello world!

$bar = &#039;HELLO WORLD!&#039;;
$bar = ucfirst($bar);             // HELLO WORLD!
$bar = ucfirst(strtolower($bar)); // Hello world!تابع lcfirstبرعکس تابع قبلی , اولین کاراکتر رشته رو به حرف کوچک تبدیل میکنه.$foo = &#039;HelloWorld&#039;;
$foo = lcfirst($foo);             // helloWorld

$bar = &#039;HELLO WORLD!&#039;;
$bar = lcfirst($bar);             // hELLO WORLD!
$bar = lcfirst(strtoupper($bar)); // hELLO WORLD!تابع ucwordsیک آرگومان:یک رشته میگیره.اولین کاراکتر هر کلمه در رشته رو تبدیل به حرف بزرگ میکنه.$foo = &#039;hello world!&#039;;
$foo = ucwords($foo);             // Hello World!

$bar = &#039;HELLO WORLD!&#039;;
$bar = ucwords($bar);             // HELLO WORLD!
$bar = ucwords(strtolower($bar)); // Hello World!تابع explodeسه آرگومان:یک مرزی برای جدا کردن کلمات از یک دیگر(delimiter)یک رشتهیک عدد , برای مشخص کردن محدوده ی انتخابی.(اختیاری)* اگر عدد منفی باشه , از آخر به اندازه ی عدد , از محدوده ی انتخابی حذف میشن.این تابع رشته ای میگیره و بر اساس مرز جدا کننده ای که مشخص میکنیم , کلمات رو در خونه های یک آرایه میریزه.رشته تبدیل میشه به یک آرایه.// Example 1
$pizza  = &amp;quotpiece1 piece2 piece3 piece4 piece5 piece6&amp;quot
$pieces = explode(&amp;quot &amp;quot, $pizza);
echo $pieces[0]; // piece1
echo $pieces[1]; // piece2

#################################

// Example 2
$data = &amp;quotfoo:*:1023:1000::/home/foo:/bin/sh&amp;quot
list($user, $pass, $uid, $gid, $gecos, $home, $shell) = explode(&amp;quot:&amp;quot, $data);
echo $user; // foo
echo $pass; // *
echo $shell; // /bin/sh

#################################

// Example 2
$pizza  = &amp;quotpiece1, piece2, piece3, piece4, piece5, piece6&amp;quot
$pieces = explode(&amp;quot,&amp;quot, $pizza, -2);
print_r($pieces); // Array ( [0] =&gt; piece1 [1] =&gt; piece2 [2] =&gt; piece3 [3] =&gt; piece4 )تابع implodeدو آرگومان:کاراکتر یا عبارتی برای چسباندن کلمات بهم.یک آرایه.این تابع برخلاف تابع قبلی عمل میکنه و یک آرایه میگیره و بر اساس کاراکتر چسباننده , خانه های آرایه رو بهم می چسبونه و یک رشته به عمل میاد.(یعنی بر عکس تابع explode)$array = array(&#039;lastname&#039;, &#039;email&#039;, &#039;phone&#039;);
$comma_separated = implode(&amp;quot,&amp;quot, $array);

echo $comma_separated; // lastname,email,phoneتابع strrevیک آرگومان:یک رشتهخوب مشخصه دیگه. یک رشته میگیره و رشته رو برعکس میکنه.echo strrev(&amp;quotHello world!&amp;quot); // outputs &amp;quot!dlrow olleH&amp;quotتابع str_shuffleیک آرگومان:یک رشته میگیرد.کاراکتر های رشته به صورت رندوم جاهاشون عوض میشه و مخلوط میشن.مثل تابع shuffle در آرایه.$str = &#039;abcdef&#039;;
$shuffled = str_shuffle($str);

// This will echo something like: bfdaec
echo $shuffled;تابع str_repeatدو آرگومان:عبارتی که میخواهیم تکرار بشه.تعداد دفعات تکرار عبارت (گام تکرار) که این عدد باید بزرگتر یا مساوی , صفر باشد.* اگر برابر صفر باشد , یک رشته ی خالی برمیگردونه.این تابع , عبارتی که بهش میدیمو با تعداد دفعاتی که مشخص کردیم, تکرار میکنه.echo str_repeat(&amp;quot-=&amp;quot, 10);

// output
-=-=-=-=-=-=-=-=-=-=تابع str_replaceاز اسمش معلومه که کارش عوض کردن کلمه ها و مقادیره.4 آرگومان:کلمه ای یا لیستی از کلماتی که در جمله به دنبالش هستیم و می خواهیم تغییرش دهیم.کلمه ای یا لیستی از کلمات جدیدی که میخواهیم در جمله جایگزین کنیم.رشته ی مورد نظری که قراره عملیات جایگزینی روش انجام بشه. (subject)متغیری میدیم تا تابع , تعداد کلمات replace شده رو داخل این متغیر بریزه. (اختیاری)// Example 1
$bodytag = str_replace(&amp;quot%body%&amp;quot, &amp;quotblack&amp;quot, &amp;quot&lt;body text=&#039;%body%&#039;&gt;&amp;quot);
echo $bodytag; // provides:  &lt;body text=&#039;black&#039;&gt;


// Example 2
$vowels = array(&amp;quota&amp;quot, &amp;quote&amp;quot, &amp;quoti&amp;quot, &amp;quoto&amp;quot, &amp;quotu&amp;quot, &amp;quotA&amp;quot, &amp;quotE&amp;quot, &amp;quotI&amp;quot, &amp;quotO&amp;quot, &amp;quotU&amp;quot);
$onlyconsonants = str_replace($vowels, &amp;quot&amp;quot, &amp;quotHello World of PHP&amp;quot);
echo $onlyconsonants; // output:  Hll Wrld f PHP


// Example 3
$phrase  = &amp;quotYou should eat fruits, vegetables, and fiber every day.&amp;quot
$healthy = array(&amp;quotfruits&amp;quot, &amp;quotvegetables&amp;quot, &amp;quotfiber&amp;quot);
$yummy   = array(&amp;quotpizza&amp;quot, &amp;quotbeer&amp;quot, &amp;quotice cream&amp;quot);

$newphrase = str_replace($healthy, $yummy, $phrase);
echo $newphrase; // output:  You should eat pizza, beer, and ice cream every day


// Example 4
$str = str_replace(&amp;quotll&amp;quot, &amp;quot&amp;quot, &amp;quotgood golly miss molly!&amp;quot, $count);
echo $count; // output:  2تابع htmlentities : (جلوگیری از حملات XSS)string htmlentities ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 
[, string $encoding = ini_get(&amp;quotdefault_charset&amp;quot) [, bool $double_encode = true ]]] )4 آرگومان:یک رشته میگیرهتعیین کردن چگونگی رفتار نسبت به کوتیشن ها و مقادیر نامعتبر (اختیاری - پیش فرض = ENT_COMPAT)یه سری ثابت ها هستن که میتونیم در این فلگ استفاده کنیم:        + ENT_COMPAT : این ثابت , دابل کوتیشن (&quot;) های رشته رو کدگذاری میکنه.(تک کوتیشنو شامل نمیشه)        + ENT_QUOTES : این ثابت , هم دابل کوتیشن(&quot;) و هم تک کوتیشنو(&#x27;) شامل میشه.        + ENT_NOQUOTES : این ثابت , هیچکدوم از کوتیشن هارو شامل نمیشه.        + ENT_IGNORE : قسمت نامعتبر رشته رو حذف میکنه.(ignores invalid encoding) - اضافه شده در php 5.3.0انکدینگ - (اختیاری - UTF-8 بزارید.)کدگذاری مجدد روی مقادیر کدگذاری شده (اختیاری - پیش فرض = true)* این تابع کاراکتر های معنی دار و قابل اجرا رو در برنامه مثل (تگ ها) با کدگذاری اثرشونو خنثی میکنه و در صفحه دیده میشن.بعنوان مثال , چنتا از کاراکتر ها و معادل کد گذاری شده ی اونها:&lt;    =&gt;    &lt;
&gt;    =&gt;    &gt;;
&amp;    =&gt;    &amp;
&amp;quot    =&gt;    &amp;quot
&#039;    =&gt;    &#039;خوب با توجه به کاراکتر هایی که نمونه اوردم چنتا مثال میزنم.$str = &#039;&#039;;
echo htmlentities($str , ENT_COMPAT, &#039;UTF-8&#039;);چیزی که در صفحه چاپ میشه:اما چیزی که در page source چاپ میشه.(البته var_dump هم میتونید بگیرید.)alert&#40;&amp;quotXSS&amp;quot&#41;یک نکته: ENT_COMPAT گفتیم دابل کوت هارو کد میکنه.(تک کوت هارو شامل نمیشه!)مثال:$str = &amp;quotA &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;&amp;quot

// Page Source: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;
echo htmlentities($str); // A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;   =&gt; by default , the flag is ENT_COMPAT
echo htmlentities($str, ENT_COMPAT); // Display: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;

***********************************

$str = &amp;quotA &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;&amp;quot

// Page Source: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;
// Encodes double and single quotes
echo htmlentities($str, ENT_QUOTES); // Display: A &#039;quote&#039; is &lt;b&gt;bold&lt;/b&gt;حالا یک مثال واسه فلگ ENT_IGNORE:$str = &amp;quot\x8F!!!&amp;quot

// Outputs an empty string
echo htmlentities($str, ENT_QUOTES, &amp;quotUTF-8&amp;quot);

// Outputs &amp;quot!!!&amp;quot
echo htmlentities($str, ENT_QUOTES | ENT_IGNORE, &amp;quotUTF-8&amp;quot);با فلگ ignore , قسمت نامعتبرو نادیده میگیره و بقیه رشته رو چاپ میکنه ولی فلگ های (quotes , compat , noquotes) اگر مقداری نامعتبر در رشته وجود داشته باشه , یک رشته ی خالی بر میگردونن که در مثال بالا دیدیم.اما مثالی برای آرگومان چهارم با مقدار false:$str = &#039;Hot &amp; Cold and On &amp; Off&#039;;

// Display: Hot &amp; Cold and On &amp; Off
echo htmlentities($str, ENT_COMPAT, &#039;UTF-8&#039;, TRUE); // Source: Hot &amp; Cold and On &amp; Off



// Display: Hot &amp; Cold and On &amp; Off
echo htmlentities($str, ENT_COMPAT, &#039;UTF-8&#039;, FALSE); // Source: Hot &amp; Cold and On &amp; Offهمونطور که مشاهده کردید , زمانی که در حالت true باشه , عبارت (;amp&amp;) و قسمت (&amp;) عبارت یک مرتبه ی دیگه , encode شد و تبدیل شد به:&amp;خوب  , فک کنم مثال ها کافی بودن واسه آشنایی با این تابع.از این تابع برای چاپ مقادیری که از کاربر میگیرین استفاده کنین.این تابع از حملات XSS جلوگیری میکنه.تابع htmlspecialchars : (جلوگیری از حملات XSS)string htmlspecialchars ( string $string [, int $flags = ENT_COMPAT | ENT_HTML401 
[, string $encoding = ini_get(&amp;quotdefault_charset&amp;quot) [, bool $double_encode = true ]]] )* مشخصات و کاربرد مثل تابع قبلی (htmlentities)* این تابع , فقط همین چند کاراکتر مشخص شده رو encode میکنه:&lt;&gt;&#x27;&quot;&amp;به نوعی این تابع زیر مجموعه ای از htmlentities می باشد.تابع nl2brstring nl2br ( string $string [, bool $is_xhtml = true ] )2 آرگومان:عبارت مورد نظربا Xhtml سازگار است یا خیر. (اختیاری - پیش فرض: true)بجای new lines تگ &lt;/ br&gt; و یا &lt;br&gt; را اضافه میکند.New Lines:\r\n
\n\r
\n 
\rمثال ها:(1)

echo nl2br(&amp;quotfoo isn&#039;t\n bar&amp;quot);

// Output
	foo isn&#039;t&lt;br /&gt;
	 bar

/*********************************/
(2)

// is_xhtml = false
echo nl2br(&amp;quotWelcome\r\nThis is my HTML document&amp;quot, false);

// Output
	Welcome&lt;br&gt;
	This is my HTML document

/*********************************/
(3)

$string = &amp;quotThis\r\nis\n\ra\nstring\r&amp;quot
echo nl2br($string);

// Output
	This&lt;br /&gt;
	is&lt;br /&gt;
	a&lt;br /&gt;
	string&lt;br /&gt;* اگر از جایی متنی کپی میکنید و در ادیتوری ویرایش نشده از این تابع استفاده کنید.نویسنده: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 12:30:57 +0330</pubDate>
            </item>
                    <item>
                <title>آشنایی با Singleton Design Pattern</title>
                <link>https://virgool.io/@bidak/%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-singleton-design-pattern-vror3qt5ai1e</link>
                <description>سلام.در قسمت های مختلف پروژه ما از یک کلاس , آبجکتی میسازیم و ممکنه چندین بار در پروژه عمل new کردن و ساختن یک آبجکت از کلاس رو انجام داده باشیم , اما گاهی اوقات سناریو به این شکل است که  ما در هر بار اجرای اسکریپت نیاز به ساخت یک آبجکت از کلاس داریم و نمیخوایم چندین بار در پروژه از اون کلاس ,  آبجکت ساخته بشه.برای حل این مشکل از معماری Singleton استفاده میکنیم.با استفاده از این معماری , دیگه نیاز نیست به فکر چند بار آبجکت ساختن , باشیم و با خیال راحت در همه ی جای پروژه , آبجکتی از کلاس میسازیم و تنها در صورت وجود نداشتن شی ء , آبجکت جدید ساخته میشه.در کلاسی که میخوایم از معماری Singleton استفاده کنیم , کافیه یک متد static بسازیم.یک نمونه از معماری در کلاس دیتابیسمون:class Database
{
    private static $instance;
 
    public function __construct()
    {
    	// connection
    }
 
 	// Singleton Design Pattern
    public static function getInstance()
    {
        if(self::$instance === null)
        {
            self::$instance = new Database();
        }
        return self::$instance;
    }
}* متد های static , مانند function های خارج از کلاس هستن و برای فراخوانی آبجکتی نمیسازیم.حالا برای صدا زدن کلاس , در پروژه متد getInstance رو صدا بزنیم.به این صورت:// this is an object of db class
$obj = Database::getInstance();* متد های static برای صدا زدن , اول نام کلاس مربوطه میاد و بعد از این علامت(::) , نام متد.اگر چندین بار پشت سر هم صدا بزنید باز هم یک آبجکت ساخته میشه.بخاطر وجود این شرط:if(self::$instance === null)روش دیگری برای پیاده سازی این معماری:class Singleton
{
    private static $instance;
    public function __construct()
    {
        if(!self::$instance)
        {
            self::$instance = $this;
            echo &#039;a new object is created...&#039;;
        }
        return self::$instance;
    }
}

$obj1 = new Singleton();
$obj2 = new Singleton();
$obj3 = new Singleton();
$obj4 = new Singleton();
$obj5 = new Singleton();اینجا چون کدهای مربطه رو در constructor نوشتیم , در هر بار new اولین کدهایی که اجرا میشن , کدهای سازنده (constructor) هستن , یعنی با هربار new کردن کلاس , متد constructor صدا زده میشه.* در تمام new کردن و Instance جدیدی ساختن ازین کلاس , تنها یکبار  آبجکت ساخته میشه.نویسنده: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 12:01:07 +0330</pubDate>
            </item>
                    <item>
                <title>متد های جادویی (Magic Methods)</title>
                <link>https://virgool.io/@bidak/%D9%85%D8%AA%D8%AF-%D9%87%D8%A7%DB%8C-%D8%AC%D8%A7%D8%AF%D9%88%DB%8C%DB%8C-magic-methods-e5au8fwieax8</link>
                <description>Constructor:یکی از متد های جادویی construct__ هست.در زمان ساخت آبجکت از یک کلاس , این متد اجرا میشه.class BaseClass
{
   public function __construct()
   {
       echo &amp;quotIn BaseClass constructor&amp;quot
   }
}

// In BaseClass constructor
$obj = new BaseClass();  // __construct() will be runزمان new عبارت &quot;In BaseClass constructor&quot; چاپ میشه.حالا کلاسی دیگه میسازیم که از BaseClass مشتق شده باشه و متد construct رو در خودش داره:class SubClass extends BaseClass
{
   function __construct()
   {
       parent::__construct();
       echo &amp;quotIn SubClass constructor&amp;quot
   }
}

// In BaseClass constructor
// In SubClass constructor
$obj = new SubClass();* با دستور parent میتونیم متد های کلاس والد رو فراخوانی کنیم:parent::MethodNameدر حالت بعدی کلاسی مشتق شده از BaseClass داریم ولی این کلاس متد construct نداره:class OtherSubClass extends BaseClass
{
    // inherits BaseClass&#039;s constructor
}

// In BaseClass constructor
$obj = new OtherSubClass();این کلاس , چون متد construct نداره , از والد خودش (BaseClass) این متدو به ارث میبره.نوع دیگری برای پیاده سازی constructor , ساخت یک متد همنام با کلاس:class Bar
{
    public function Bar()
    {
        echo &amp;quotIn Bar constructor&amp;quot
    }
}

// In Bar constructor
$obj = new Bar();Destructor :متد جادویی دیگری که در php تعریف شده destruct__ هست.این متد در زمان از بین رفتن آبجکت یا به پایان رسیدن اسکریپت , اجرا میشود.class Example
{
   public function __construct()
   {
       print &amp;quotIn constructor&lt;br&gt;&amp;quot
       $this-&gt;name = &amp;quotExample&amp;quot
   }

   public function __destruct()
   {
       echo &amp;quotDestroying &amp;quot . $this-&gt;name;
   }
}

$obj = new Example();نتیجه ای که میبینیم:In constructor
Destroying Exampleشاید بنظرتون برسه دو متد , با ساخت آبجکت , همزمان اجرا شدن ولی دلیل اجرای destructor به پایان رسیدن اسکریپت هست.واسه نشون دادن این مسئله , یک مثال دیگه میزنم:class Example
{
   public function __construct()
   {
       print &amp;quotIn constructor&lt;br&gt;&amp;quot
       $this-&gt;name = &amp;quotExample&amp;quot
   }

   public function __destruct()
   {
       echo &amp;quotDestroying &amp;quot . $this-&gt;name;
   }
}

$obj = new Example();

echo &#039;Numbers: &#039;;
for($i = 1; $i &lt;= 10; $i++)
{
	echo $i . &#039; &#039;;
}
echo &#039;&lt;br&gt;&#039;;نتیجه ی حاصل از اسکریپت بالا:In constructor
Numbers: 1 2 3 4 5 6 7 8 9 10 
Destroying Exampleمورد دیگه ای که در اول متن گفتم , اجرای destructor در زمان از بین رفتن آبجکت:class Example
{
   public function __construct()
   {
       print &amp;quotIn constructor&lt;br&gt;&amp;quot
       $this-&gt;name = &amp;quotExample&amp;quot
   }

   function __destruct()
   {
       echo &amp;quotDestroying &amp;quot . $this-&gt;name;
   }
}

$obj = new Example();

echo &#039;Numbers:&lt;br&gt;&#039;;

unset($obj); // __destruct() will be run

echo &#039;&lt;br&gt;&#039;;
for($i = 1; $i &lt;= 10; $i++)
{
	echo $i . &#039; &#039;;
}
echo &#039;&lt;br&gt;&#039;;نتیجه ی اسکریپت:In constructor
Numbers:
Destroying Example
1 2 3 4 5 6 7 8 9 10همونطور که میبینید , با استفاده از تابع unset , آبجکت کلاس رو حذف کردیم که باعث فراخوانی متد destructor شد.Getter :متد get__ در زمان خواندن یک فیلد , اجرا میشود.class Example
{
	private $name;
	private $family;

	public function __construct()
	{
		$this-&gt;name = &#039;Mohsen&#039;;
		$this-&gt;family = &#039;Movahed&#039;;
	}
}

$obj = new Example();
echo $obj-&gt;name . &#039; &#039; . $obj-&gt;family; // errorنتیجه با اررور مواجه میشه.متن اررور:Error: Cannot access private property Example::$nameعلت: فیلد های private , بیرون از کلاس قابل دسترسی نیستن.حالا با توجه به تعریفی که از getter داشتیم , زمان صدا زدن فیلد (echo $obj-&gt;name) , متد get__ (در صورتی که تعریف شده باشه) , اجرا میشه:class Example
{
	private $name;     // field 1
	private $family;   // field 2

	public function __construct()
	{
		$this-&gt;name = &#039;Mohsen&#039;;
		$this-&gt;family = &#039;Movahed&#039;;
	}

	public function __get($field)
	{
		return $this-&gt;$field;
	}
}

$obj = new Example();
echo $obj-&gt;name . &#039; &#039; . $obj-&gt;family; // Mohsen Movahed

//****************OR******************//

$obj = new Example();
$name = $obj-&gt;name;         // __get() will be run
$family = $obj-&gt;family;     // __get() will be run

echo $name . &#039; &#039; . $family; // Mohsen Movahedحالا با وجود متد get , فیلدها از داخل خود کلاس مورد دستیابی قرار میگیرن و در چاپ نام و فامیل ,  این متد دو بار اجرا میشود.***************************************************Setter :متد set__ در زمان مقدار دهی فیلدها اجرا خواهد شد.دو آرگومان داره:نام فیلدمقدارclass Example
{
	private $name;     // field 1
	private $family;   // field 2

	public function __get($field)
	{
		return $this-&gt;$field;
	}

	public function __set($field, $value)
	{
		$this-&gt;$field = $value;
	}
}

$obj = new Example();

$obj-&gt;name = &#039;Mohsen&#039;;        // __set() will be run
$obj-&gt;family = &#039;Movahed&#039;;     // __set() will be run

echo $obj-&gt;name . &#039; &#039; . $obj-&gt;family; // Mohsen Movahedپیاده سازی getter و setter به روشی دیگر: (برای مثال)class Example
{
	private $fields;

	public function __construct()
	{
		$this-&gt;fields = array(
			&#039;name&#039; =&gt; null,
			&#039;family&#039; =&gt; null,
			&#039;age&#039; =&gt; null,
		);
	}

	public function __get($key)
	{
		if(array_key_exists($key, $this-&gt;fields))
		{
			return $this-&gt;fields[$key];
		}
		return false;
	}

	public function __set($key, $value)
	{
		if(array_key_exists($key, $this-&gt;fields))
		{
			$this-&gt;fields[$key] = $value;
		}
	}
}

$obj = new Example();

$obj-&gt;name = &#039;Mohsen&#039;;
$obj-&gt;family = &#039;Movahed&#039;;
$obj-&gt;age = 23;
$obj-&gt;email = &#039;l3iidak@gmail.com&#039;;

echo $obj-&gt;name . &#039; &#039; . $obj-&gt;family . &#039; &#039; . $obj-&gt;age . &#039; &#039; . $obj-&gt;email; // Mohsen Movahed 23email جزو ایندکس ها نیست , بنابراین مقدار دهی نمیشود.* به شکل های مختلف و اعتبار سنجی ها متفاوت میتونید این دو متدو پیاده سازی کنید.Call :وقتی توسط یک آبجکت , متدی رو فراخوانی کنیم که در کلاس مورد نظر وجود نداشته باشد , متد call__ اجرا میشود.دو آرگومان دارد:نام متدآرگومان های ارسالیclass MethodTest
{
    public function __call($method, $arguments)
    {
        // Note: value of $mthod is case sensitive.
        echo &amp;quotCalling object method &lt;strong&gt;&#039;$method&#039;&lt;/strong&gt; with arguments:&lt;br&gt;&amp;quot
        echo &#039;&lt;pre&gt;&#039;. print_r($arguments, true) .&#039;&lt;/pre&gt;&#039;;
    }

    public function display()
    {
    	echo &#039;Display Method&#039; . &#039;&lt;br&gt;&#039;;
    }
}

$obj = new MethodTest();
$obj-&gt;display(); // Display Method
$obj-&gt;runTest(1, 2, 3, 4);متدهایی که در کلاس وجود دارند , اگر فراخوانی شوند خود متد ها اجرا می شوند , بنابراین متد display اجرا میشود.اما چون متد runTest در کلاس وجود ندارد , متد Call__ اجرا میشود. (نام متد: runTest - آرگومان ها : 1 , 2 , 3 , 4)نتیجه ی کد بالا:Display Method
Calling object method &#039;runTest&#039; with arguments:
Array
(
    [0] =&gt; 1
    [1] =&gt; 2
    [2] =&gt; 3
    [3] =&gt; 4
)callStatic :متد callStatic__ هم مانند متد call__ عمل میکند با این تفاوت که برای متد های استاتیک هست.class MethodTest
{
    public static function __callStatic($method, $arguments)
   {
       // Note: value of $method is case sensitive.
       echo &amp;quotCalling static method &lt;strong&gt;&#039;$method&#039;&lt;/strong&gt; with arguments: &amp;quot 
                                                        . implode(&#039;, &#039;, $arguments) . PHP_EOL;
   }
}

MethodTest::runTest(&#039;in static context&#039;);نتیجه:Calling static method &#039;runTest&#039; with arguments: in static contextادامه دارد...نویسنده: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 11:02:44 +0330</pubDate>
            </item>
                    <item>
                <title>خطای Cannot modify header information - headers already sent by</title>
                <link>https://virgool.io/@bidak/%D8%AE%D8%B7%D8%A7%DB%8C-cannot-modify-header-information-headers-already-sent-by-bbf2zbi6pqsy</link>
                <description>ریدایرکت در سه وضعیت 301 - 302 - 303یک سری توابع هستن که هدرهای مرورگرو (Http-Header) تغییر میدن و مرورگر طبق این هدر ها عمل میکنه.چنتا ازین توابع:()header()    -     header_remove()session_start()     -     session_regenerate_id()setcookie()      -      setrawcookieهدرها تا زمانی قادرن تغییر کنن که هیچ خروجی (حتی یک space) در صفحه ی مرورگر چاپ نشده باشه.یعنی هیچ داده ای از سمت سرور به مرورگر کاربر نرسیده باشه.مثلا کد زیر غلطه:&lt;html&gt;
&lt;?php
	session_start();
?&gt;علتش بخاطر وجود تگ html هست.چون قبل ازینکه سشن استارت بشه , تگ html در صفحه ی مرورگر چاپ شده و باعث بروز این مشکل میشه.اگر بجای تگ html یک فاصله(space) هم بود بازم همین مشکل وجود داشت.راه حل:قبل از این توابع چیزی چاپ نشه.از قبیل echo , print , html tag ,...فایل با انکد utf-8 without byte order mark  ذخیره بشه.(utf-8 without BOM)چون utf-8 سه بایت(0xEF, 0xBB, 0xBF) به ابتدای فایل اضافه میکنه که با توجه به توضیحات بالا مشکل ساز میشه.استفاده از بافر خروجی (ob_start) - حتما نیاز نیست ازین راه استفاده بشه.* زمانی که بافر فعال بشه , تمام دیتاهایی که قراره در مرورگر کاربر چاپ بشه , ابتدا در بافر روی حافظه رم سرور قرار میگیره و تا زمانی که دستور خالی کردن بافرو ندادیم (ob_end_flush) , چیزی در مرورگر چاپ نمیشه.زمان استفاده از header برای عمل redirect صفحه , ممکنه زیاد با این خطا مواجه بشیم.تابعی که استفاده میکنیم برای redirect:header(&#039;location: index.php&#039;);اما راه حل جایگزین برای redirect :با استفاده از جاوااسکریپت:&lt;?php
       echo &amp;quot = &#039;index.php&#039;&quot;
?&gt;0 =&gt; یعنی بعد از 0 ثانیه این عمل انجام شود.با استفاده از تگ متا:&lt;?php
       echo &amp;quot&lt;meta http-equiv=&#039;Refresh&#039; content=&#039;0; url=index.php&#039;&gt;&quot;
?&gt;استفاده از تابع زیر: (بهترین راه)function redirect($url)
{
    @header(&#039;location: &#039; . $url);
    exit(&#039;&lt;meta http-equiv=&amp;quotRefresh&amp;quot content=&amp;quot0;url=&#039;. $url .&#039;&amp;quot&gt;&#039;);
}اما برای مدیریت ریدایرکت های دائمی یا موقتی یا وضعیت 303:function redirect($url, $statusCode = 303)
{
   header(&#039;Location: &#039; . $url, true, $statusCode);
   die();
}این لینک لیستی از هدر های درخواست رو نشون میده. بطور مثال (content-type , location , refresh , ....)منبع: محسن موحد</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 10:38:11 +0330</pubDate>
            </item>
                    <item>
                <title>جلوگیری از خواندن آدرس ایمیل توسط ربات های Email Crawler</title>
                <link>https://virgool.io/@bidak/%D8%AC%D9%84%D9%88%DA%AF%DB%8C%D8%B1%DB%8C-%D8%A7%D8%B2-%D8%AE%D9%88%D8%A7%D9%86%D8%AF%D9%86-%D8%A2%D8%AF%D8%B1%D8%B3-%D8%A7%DB%8C%D9%85%DB%8C%D9%84-%D8%AA%D9%88%D8%B3%D8%B7-%D8%B1%D8%A8%D8%A7%D8%AA-%D9%87%D8%A7%DB%8C-email-crawler-aimmhgzigxnw</link>
                <description>سلام.راه های متفاوتی وجود داره برای اینکار مثلا یه نمونش تبدیل ایمیل به تصویر، ولی هر کدوم از راه ها یه سری معایب هم داره که بیشترشون توسط ربات های الان خونده میشن.اما راه جالبی که داخل این لینک دیدم و با css پیاده شده بودو گفتم به شما کدشو نشون بدم:.e-mail:before {
content: &amp;quot06c03306906906406106b04006706d06106906c02e06306f06d&amp;quot
}ایمیل من داخل پراپرتی content به هگزادسیمال encode شده.نتیجه ی اجرای کد بالا رو ببینید: jsfiddle</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 10:04:41 +0330</pubDate>
            </item>
                    <item>
                <title>Lock شدن فایل Session و معرفی تابع session_write_close</title>
                <link>https://virgool.io/@bidak/lock-%D8%B4%D8%AF%D9%86-%D9%81%D8%A7%DB%8C%D9%84-session-%D9%88-%D9%85%D8%B9%D8%B1%D9%81%DB%8C-%D8%AA%D8%A7%D8%A8%D8%B9-sessionwriteclose-rmz6prvusdpo</link>
                <description>سلام.قبلا کدی نوشته بودم که درخواست های Ajax رو بصورت موازی و همزمان به دو صفحه ی PHP ارسال میکرد.( بطور عادی وقتی چند صفحه ی PHP رو همزمان باز میکنیم، انتظار داریم، همزمان اسکریپت ها اجرا بشن و هر صفحه بطور مجزا تفسیر بشه و صفحه ای منتظر پایان اجرای صفحه ی دیگه نباشه.)ولی در پروژه ی من، وقتی درخواست ajax به صفحه ی اول میرفت، چون صفحه ی اول، چند ثانیه طول میکشید تا اجراش به پایان برسه، صفحه ی دوم، منتظر میماند تا صفحه ی اول اجراش به پایان برسه. البته این چند ثانیه ای که صفحه ی اول طول میکشید، چون زمانش مقداری زیاد بود(مثلا ۲۰ ثانیه)، من متوجه این مسئله شده بودم وگرنه به چشم نمیومد.با سرچ متوجه شدم که علتش، استفاده از سشن بوده و session_start رو در این فایل ها صدا زده بودم.یک مثال برای تست میارم تا متوجه مسئله بشید.صفحه ی page۱.php رو بسازید و کدهای پایینو داخلش بذارید:&lt;?php
session_start();

$_SESSION[&#039;fullname&#039;] = &#039;Mohsen Movahed&#039;;


sleep(15);


echo &#039;Page 1&#039;;
echo &#039;&#039;;
echo $_SESSION[&#039;fullname&#039;];یک sleep به مدت ۱۵ ثانیه ایجاد کردم تا متوجه این مسئله بشیم.صفحه ی page۲.php رو ایجاد کنید و کدهای پایینو داخلش بذارید:&lt;?php
session_start();

echo &#039;Page 2&#039;;حالا در مرورگر دو تا تب بازکنید و در تب اول page۱.php رو بزنید و در تب دوم page۲.php و همزمان اجرا بگیرید.در حالت عادی page۲.php باید سریع اجرا بشه ولی وقتی اول page۱.php رو میزنید، page۲.php منتظر میمونه تا اجرای page۱.php به اتمام برسید.علت این ماجرا اینه که PHP بصورت پیشفرض، سشن ها رو روی فایل ها مدیریت میکنه.وقتی session_start اجرا میشه، فایل سشنی که مربوط به کاربر مورد نظر هست در حالت Lock قرار میگیرد تا زمانیکه اسکریپت به پایان برسد. چون ممکنه در حین اجرای اسکریپت تغییراتی در فایل سشن ایجاد شود.این مورد هم برای فایل ها طبیعییست که همزمان روی یک فایل چیزی رو ننویسن تا تداخلی ایجاد نشود.در PHP بطور معمول دیتای مربوط به سشن، بعد از اتمام اسکریپت ذخیره میشود.یعنی اینکه اسکریپت که به پایان رسید، بطور اتومات، محتوای سشن ذخیره میشود و فایل سشن مربوط به کاربر unlock میشود.حالا ممکنه در یک اسکریپت(مثل page۱.php) ما نخوایم تا  پایان اجرای اسکریپت صبر کنیم.در توابع سشن، تابعی وجود دارد با نام session_write_close که سشن جاری رو close میکنه و محتوای سشن رو داخلش ذخیره میکنه. حالا اگر در جایی نیاز پیدا کردیم میتونیم ازین تابع استفاده کنیم. ضمن اینکه اگر بخواهیم سشن رو روی دیتابیس مدیریت کنیم، اونجاهم برای پیاده سازیش نیاز به استفاده ازین تابع داریم.برای حل مشکل اسکریپت، داخل page۱.php(چون طول میکشه و فایل سشن، زمان زیادی رو توو حالت lock میمونه) بعد از اینکه مقادیرو ست کردیم میتونیم با استفاده از تابع session_write_close بگیم سشن جاری close بشه:&lt;?php
session_start();

$_SESSION[&#039;fullname&#039;] = &#039;Mohsen Movahed&#039;;
session_write_close(); // Ends the current session and store session data



sleep(15);


echo &#039;Page 1&#039;;
echo &#039;&#039;;
echo $_SESSION[&#039;fullname&#039;];حالا میتونید یکبار دیگه page۱.php رو اجرا کنید و بعد page۲.php و میبینید مشکل حل شده!با دونستن کاربردش، حالا جایی که نیاز داشتین میتونین ازش بهره ببرین.یک نکته هم برای استارت کردن سشن:ممکنه در پروژه هاتون اگر ساختار درستی رو رعایت نکرده باشید یا حجم پروژه بالا باشه و یا هر دلیل دیگه در صفحات مختلفی، سشن رو استارت کنید و اگر در حین اجرای اسکریپت، سشن از قبل در صفحه ای استارت شده باشه و مجدد فراخوانی بشه یک Notice اتفاق میوفته که میگه سشن قبلا استارت شده بنابراین میتونید سشن هاتونو به این شکل استارت کنید:if(session_status() !== PHP_SESSION_ACTIVE) {
	session_start();
}خروجی های session_status رو ببینید.* چون در مورد سشن مطلب اوردم بد نیست این مطلبم اضافه کنم، جدای ازینکه Session ID روی Cookie مرورگر LifeTime داره، خود Session هم روی سرور یک LifeTime داره میتونید در موردش داخل داک PHP تحقیق کنید.</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 09:51:46 +0330</pubDate>
            </item>
                    <item>
                <title>نحوه ایندکس‌ گذاری روی دیتابیس Mysql و افزایش Performance</title>
                <link>https://virgool.io/@bidak/%D9%86%DA%A9%D8%A7%D8%AA-%D9%85%D9%87%D9%85-%D8%A7%DB%8C%D9%86%D8%AF%DA%A9%D8%B3-%DA%AF%D8%B0%D8%A7%D8%B1%DB%8C-%D8%B1%D9%88%DB%8C-%D8%AC%D8%AF%D8%A7%D9%88%D9%84-%D9%88-%D8%A7%D9%81%D8%B2%D8%A7%DB%8C%D8%B4-performance-nt8i1dvwrwk1</link>
                <description>سلام.یک سری مطالعه و تست ها در مورد ایندکس ها روی دیتابیس انجام دادم و میخوام در موردشون توضیح بدم.چرا روی فیلدها ایندکس میذاریم؟برای اینکه پرفورمنس و سرعت response time کوئری ها، زمان خواندن اطلاعات رو بالا ببریم.ایندکس ها باعث میشن که دیتابیس شما، درختی مرتب شده بر اساس اون فیلد مورد نظرو تولید کنه و زمانی که شما عملیات سلکت رو روی اون فیلد انجام میدهید، عملیات سلکت روی درخت مرتب شده صورت میگیرد و نتیجه با سرعت بالایی برمیگردد.برای مثال، فرض کنیم جدولی داریم با یک میلیون رکورد و هیچ ایندکسی روی این جدول وجود نداره.حالا کوئری میزنم به جدول که یوزر bidak رو سلکت کنه:SELECT * FROM `users` WHERE `username` = &#039;bidak&#039;نتیجه کوئری بالا در زمان اندکی واسم نمایش داده میشه.اما میخوام با دستور EXPLAIN ببینم که دقیقا چه تعداد رکوردی قراره پیمایش بشه:EXPLAIN SELECT * FROM `users` WHERE `username` = &#039;bidak&#039;نتیجه:mysql&gt; EXPLAIN SELECT * FROM `users` WHERE `username` = &#039;bidak&#039;G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1000000
     filtered: 10.00
        Extra: Using where
1 row in set, 1 warning (0.00 sec)همونور که میبینید possible_keys و key مقدار NULL دارند.possible_keys ایندکس های موجود در کوئری رو نشون میده که کوئری میتونه ازشون استفاده کنه.key ایندکسی رو نشون میده که کوئری ازش استفاده کرده.(یعنی برای جستجو از درختی استفاده کرده که برای اون ایندکس، مرتب و ساخته شده.)اما قسمت rows که برابر ۱,۰۰۰,۰۰۰ میباشد. یعنی Mysql تخمین زده که یک میلیون ردیف(ستون username) در این جستجو باید بررسی بشه.اما قسمت filtered درصدی رو نشون میده که جدول فیلتر میشه.(براساس شروط داخل کوئری)بالاترین عدد ۱۰۰ درصد است که در این قسمت ۱۰% هست و همه چیز نشان از پرفورمنس بسیار پایین این جدول دارد.حالا میام و یک ایندکس روی username میگذارم.(میتونید ایندکس بذارید یا میتونید unique بذارید):mysql&gt; EXPLAIN SELECT * FROM `users` WHERE `username` = &#039;bidak&#039;G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ref
possible_keys: username
          key: username
      key_len: 258
          ref: const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)قسمت key برابر username هست و rows نشون میده برابر ۱ است و مقدار filtered هم برابر ۱۰۰%شرایط نشون میده پرفورمنس بهتری جدول گرفته. ولی قطعا همه چیز این اعداد ارقام نیستن و دیتابیس شرایط مختلفی رو بررسی میکنه.با این مثال ها فهمیدیم که اهمیت ایندکس گذاری چیه و فهمیدیم اصلا ایندکس گذاری چه کاربردی دارد.بنابراین اگر در پروژه، عملیات خواندن(select) زیادی روی یک فیلد داشتید، اون فیلد رو ایندکس گذاری کنید.ایندکس ها عمدتا روی WHERE و ORDER BY در کوئری هاتون اتفاق میوفته.نکته: بد نیست همیشه روی کوئریتون مثل الان از EXPLAIN استفاده کنید تا متوجه بشید که از یک ایندکس استفاده میشه یا نه. این مثالو ببینید:mysql&gt; EXPLAIN SELECT * FROM `users` ORDER BY usernameG;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ALL
possible_keys: NULL
          key: NULL
      key_len: NULL
          ref: NULL
         rows: 1000000
     filtered: 100.00
        Extra: Using filesort
1 row in set, 1 warning (0.00 sec)میبینید که از ORDER BY استفاده کردم ولی از هیچ ایندکسی استفاده نشده، در صورتی که username ایندکس شده.حالا به کوئری LIMIT رو اضافه میکنم:mysql&gt; EXPLAIN SELECT * FROM `users` ORDER BY username LIMIT 1;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: index
possible_keys: NULL
          key: username
      key_len: 258
          ref: NULL
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)* اگر LIMIT رو بدون ORDER BY username که ایندکس شده بیارید، مقدار rows برابر ۱ میلیون میشه.نکات:** ایندکس ها، سرعت اینسرت و آپدیت رو کند میکنن . سربار اضافی ایجاد میشه، بنابراین اگر وجود ایندکس ضروری نبود، ایجاد نکنید.تست:جدولی دارم با یک میلیون رکورد. در تستی که پایین اوردم، تمام داده های یک ستون رو آپدیت کردم.(روی ۱ میلیون رکورد)روی جدول ۱ ایندکس PK وجود داشت: ۳۰ ثانیه عمل آپدیت طول کشید.روی جدول ۲ ایندکس دیگه اضافه کردم: ایندفعه ۹۰ ثانیه طول کشید.روی جدول ۷ ایندکس وجود داشت: نزدیک ۴۰۰ ثانیه طول کشید.البته این تایم ها روی سیستم من انجام شده و روی PHPMYAdmin و در خود کنسول MySql ثانیه ها پایینتر بود. مثلا با ۷ ایندکس چیزی حدود ۷۰ ثانیه طول کشید و همینطور روی سرور پایینتر خواهد امد. اما چیزی که ازین تست میشد فهمید اینه که بیهوده ایندکس ایجاد نکنیم چون روی پرفورمنس دیتابیس، تأثیر مستقیم میذاره.** ازونجایی که برای هر ایندکسی که ایجاد میکنید یک درخت تشکیل میشه(Type=BTREE)، بنابراین اگر روی یک فیلد خاص، عملیات سلکت رو زیاد استفاده نمیکنید، اون فیلد رو ایندکس نکنید. چون این کار باعث میشه فضای بیشتری از دیسک اشغال بشه. برای مثال فرضاً جدول شما ۱۰۰ مگ اطلاعات داره ولی شما ۱۰ تا ایندکس ایجاد کردید و دیتابیس شما شده ۱ گیگ. یا مثلا هر خونه از فیلد username شما ۸ بایت فضا رو اشغال میکنه و شما ۱ میلیارد رکورد دارید. فیلد username رو ایندکس میکنید و در نتیجه ۱,۰۰۰,۰۰۰,۰۰۰ * ۸ بایت یعنی برای این ایندکس به میزان ۸ گیگابایت فضا روی هارد دیسک نیاز دارید.** یوقتی هست یه سناریو مثل این دارید که در جدول firstname و lastname دارید و چون روی جفتشون سلکت انجام میدید، میاید firstname رو ایندکس میکنید و lastname رو هم ایندکس میکنید. این دوتا فیلدو هر کدومو جدا ایندکس کردید. نتیجه ی سلکت رو ببینید:mysql&gt; EXPLAIN SELECT * FROM `users` WHERE firstname = &#039;Mohsen&#039; AND lastname = &#039;Movahed&#039;G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ref
possible_keys: firstname,lastname
          key: firstname
      key_len: 53
          ref: const
         rows: 1
     filtered: 5.00
        Extra: Using where
1 row in set, 1 warning (0.10 sec)Mysql در هر کوئری تنها از یک ایندکس استفاده میکند. وقتی در کوئری چندین ایندکس بیاد، Mysql خودش یک ایندکس رو بر اساس اینکه شرایط کدوم مناسبتره انتخاب میکنه. خود همین تحقیق Mysql، سربار اضافی ایجاد میکنه.اما من میام از Composite Index استفاده میکنم و یک ایندکس روی دو فیلد قرار میدم:ADD INDEX `composite_index` (`firstname`, `lastname`) USING BTREE;نتیجه:mysql&gt; EXPLAIN SELECT * FROM `users` WHERE first_name = &#039;Mohsen&#039; and last_name = &#039;Movahed&#039;G;
*************************** 1. row ***************************
           id: 1
  select_type: SIMPLE
        table: users
   partitions: NULL
         type: ref
possible_keys: composite_index
          key: composite_index
      key_len: 106
          ref: const,const
         rows: 1
     filtered: 100.00
        Extra: NULL
1 row in set, 1 warning (0.00 sec)** اگر ستون Varchar ای رو خواستید ایندکس کنید، در نظر داشته باشید که محدودیتی روی طول آن وجود دارد و فقط ۷۶۷ بایت ایندکس میشه.ازونجایی Mysql برای هر کاراکتر UTF-۸ سه بایت رو در نظر میگیره، بنابراین:Varchar(255)انتخاب مناسبیه. چون ستون مورد نظر، حداکثر ۷۶۵ بایت فضا رو اشغال خواهد کرد.(۲۵۵ * ۳ = ۷۶۵)البته کاراکتر ست ای هم داریم که ۴ بایت را ساپورت میکند: utf۸mb۴** مورد آخر هم اینو بیارم که Constraint Key ها علاوه بر اینکه محدودیت هایی روی فیلد ایجاد میکنن، ایندکس هم هستن. برای مثال Unique Key = محدودیت جلوگیری از درج مقادیر تکراری + ایندکس، در نتیجه چه بگیم Unique Key و چه بگیم Unique Index تفاوتی نمیکند.</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 09:39:34 +0330</pubDate>
            </item>
                    <item>
                <title>تشریح PSR-4 برای پیاده سازی autoload و ساختار NameSpace</title>
                <link>https://virgool.io/@bidak/%D8%AA%D8%B4%D8%B1%DB%8C%D8%AD-psr-4-%D8%A8%D8%B1%D8%A7%DB%8C-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-autoload-%D9%88-%D8%B3%D8%A7%D8%AE%D8%AA%D8%A7%D8%B1-namespace-mdey3cx4hxax</link>
                <description>جامع ترین مقاله برای NameSpace هاسلام.در این مقاله میخوام قوانین ساخت یک autoloader استاندارد رو بر اساس PSR-4 توضیح بدم.این مقاله کاملترین و جامعترین آموزشی است برای یادگیری تمام نکات NameSpace ها...سعی شده، مطالب بصورت ساده و روان بهمراه نکات و مثال هایی روی دیگر پروژه های استاندارد گفته بشه.در مورد namespace ها هم نکاتی آورده شده تا درک مناسب و بهتری در این موضوع پیدا کنید.1. قوانین این لیست برای class ها ، interface ها ، trait ها و دیگر ساختارهای مشابه قابل پیاده سازی است و هر جا کلمه ی class یا کلاس اومد اشاره به همین ها دارد.2. یک namespace کامل به شکل زیر ذکر میشود:\&lt;NamespaceName&gt;(\&lt;SubNamespaceNames&gt;)*\&lt;ClassName&gt;++ بک اسلش (\) ابتدایی، به Global Space اشاره میکند. اگر خواستید کلاسی رو که در یک space دیگر قرار دارد داخل یک namespace بیاورید میبایست ابتدای آن یک بک اسلش(\) بگذاریم:&lt;?php
namespace A\B\C;

$dt = new DateTime();
echo $dt-&gt;getTimestamp() . PHP_EOL;در کد بالا یک Fatal Error رخ میدهد:Fatal error: Uncaught Error: Class &#039;A\B\C\DateTime&#039; not foundعلتش اینه که ما در فضای A\B\C هستیم و در این فضا دنبال کلاس DateTime میگرده. در صورتی که این تابع جزو توابع خود PHP هست و در فضای global قرار دارد. پس یک \ اضافه میکنیم تا بگیم از global space دنبال کلاس DateTime بگرده:&lt;?php
namespace A\B\C;

$dt = new \DateTime();
echo $dt-&gt;getTimestamp() . PHP_EOL;
// output: 1591927525در غیر اینصورت نیازی به آوردنش نیست. اگر با استفاده از use کلاس را import میکنید هم فرقی نمیکند بیاورید یا نه.با توجه به توضیحات، این مثال رو میتونم به دو شکل بنویسم و صحیح است:&lt;?php
namespace A\B\C;

$a = (new \classes\base\Hello)-&gt;sayHelloWorld();یا بوسیله ی use کلاس رو import کنم:namespace A\B\C;

use classes\base\Hello;

$a = (new Hello)-&gt;sayHelloWorld();تذکر: به یک نکته توجه کنید، سینتکس تعریف namespace میبایست بعنوان اولین دستور در فایل PHP نوشته شود و قبل از تگ php هم هیچ کاراکتر و whitespace ای نباید در خروجی قرار بگیره وگرنه خطای زیر تولید میشه:[whitespace]
&lt;?php
namespace A\B\C;

Fatal error: Namespace declaration statement has to be the very first statement or after any declare call in the script in ...بنابراین به این مورد هم دقت داشته باشید که encoding فایلتون روی UTF-8 with BOM نباشه وگرنه باز هم این خطا نمایش داده میشه.(بخاطر وجود byte order mark در ابتدای فایل)++ namespace ها میتونن چندین level تو در تو داشته باشن ولی اگر هیچ sub-level ای در نظر نگرفتید، حداقل باید یک top-level داشته باشند تا یک namespace در فضای global قرار نگیرد. برای مثال classes\Hello.php در اینجا classes یک top-level محسوب میشود.++  پس با توجه به جمله ی قبلی، SubNamespace ها میتونه یک یا چنتا بیاد. classes\base\App.php++  ساختار namespace با اسم کلاس خاتمه پیدا میکنه. App.php++ در این ساختار Underscore ها(_) هیچ معنی خاصی ندارن.++ حروف الفبا میتونه ترکیبی از حروف کوچک و بزرگ باشد.++ نام کلاس باید به حروف کوچک و بزرگ حساس باشد. یعنی در ساختار تابع autoload نام کلاس رو همونطوری که هست بگیرید و در تابع strtolower نگذارید.3. بعد از رعایت قوانین گفته شده در ادامه، نکات مهمی از PSR-4 رو میخوام توضیح بدم تا انتها بهمراه مثال هاش بخونید که متوجه بشید، موضوع چیه:++ برای جلوگیری از دایرکتوری های تو در تو (اصطلاحاً nested directories) ترجیحاً میتونید بخشی از namespace رو بعنوان یک namespace prefix در نظر بگیرید که این prefix به مسیری اشاره میکند که فایل های پروژه داخلش قرار گرفتن(base directory)پروژه میتونه prefix های مختلفی بهمراه مسیرهای متفاوتی باشه.برای اینکه این مطلب بهتر جابیوفته، نکته ی بعدی رو هم بیارم بعد مثال میزنم.++ اسامی هر sub-namespace که بعد از prefix اومده مطابق با یک sub directory درون base directory خواهد بود که این sub directory باید همنام(از نظر کوچک و بزرگی هم مطابقت داشته باشد) با اسامی sub-namespace ها باشد. ضمناً جداکننده namespace (یعنی \ بک اسلش ها) در مسیردهی آدرس فایل روی سیستم با ثابت DIRECTORY_SEPARATOR باید replace شوند. (مسیر دایرکتوری ها در لینوکس با / از هم جدا میشوند.)++ نام کلاسی که در انتهای namespace آمده است باید با یک فایل با پسوند .php هم نام باشد.(از نظر کوچک و بزرگی هم مطابقت داشته باشد)چنتا مثال از خود داک و جاهای دیگه میارم.داک PHPFig:FULLY QUALIFIED CLASS NAME      	   NAMESPACE PREFIX      	   BASE DIRECTORY      	   RESULTING FILE PATH
\Acme\Log\Writer\File_Writer	                Acme\Log\Writer	           ./acme-log-writer/lib/	   ./acme-log-writer/lib/File_Writer.php
\Aura\Web\Response\Status	                    Aura\Web        	           /path/to/aura-web/src/     /path/to/aura-web/src/Response/Status.php
\Symfony\Core\Request	                        Symfony\Core	       ./vendor/Symfony/Core/      ./vendor/Symfony/Core/Request.php
\Zend\Acl	                                            Zend	             /usr/includes/Zend/	   /usr/includes/Zend/Acl.phpفریمورک Yii:برای مثال در این فریمورک namespace ای با عنوان yii\base\Application یا yii\web\Application یا yii\BaseYii و ... وجود دارد. این فریمورک طبق این مواردی که گفتیم، path رو در یک آرایه به اسم aliases نگه میداره. در این namespace هایی که گذاشتم top-level یعنی yii رو در آرایه ای با نام aliases مسیردهی میکنه به این شکل که ایندکسش رو @yii قرار داده و مقدارش آدرسی مطابق با مسیر دایرکتوری yii2 در نظر گرفته میشه path/to/project_directory/vendor/yiisoft/yii2 ست میشه.بنابراین اگر بنویسیم:* use yii\base\Application منظورمان فایل Application.php مثلا در همچین مسیری است:/var/www/html/yiiblog/vendor/yiisoft/yii2/base/Application.php* use yii\web\Application:/var/www/html/yiiblog/vendor/yiisoft/yii2/web/Application.php* use yii\BaseYii:/var/www/html/yiiblog/vendor/yiisoft/yii2/BaseYii.phpالبته در فریمورک Yii، مسیر کلاس های داخل فریمورک در آرایه ی classMap تعریف شده. بخشی ازین آرایه رو ببینید:return [
  &#039;yii\base\Action&#039; =&gt; YII2_PATH . &#039;/base/Action.php&#039;,
  &#039;yii\base\ActionEvent&#039; =&gt; YII2_PATH . &#039;/base/ActionEvent.php&#039;,
  &#039;yii\base\ActionFilter&#039; =&gt; YII2_PATH . &#039;/base/ActionFilter.php&#039;,
  &#039;yii\base\Application&#039; =&gt; YII2_PATH . &#039;/base/Application.php&#039;,
  // ....
  // ....
  // etc....
];ثابت YII2_PATH و yii alias یعنی @yii مسیر مشترکی دارند و به مسیر دایرکتوری yii2 اشاره میکنند.اگر namespace ای در classMap نبود Autoload میره از prefix namespace هایی که مسیرشون در آرایه ی aliases ست شده، کمک میگیره برای پیدا کردن فایل ها و ...Composer:پکیج هایی که در vendor قرار دارند رو اگر باز کنید، هر کدوم فایل composer.json دارند که داخل این فایل و در قسمت autoload psr-4 مسیر prefix namespace ها مشخص شده. یعنی مشخص شده فایل های مرتبط با prefix در چه مسیری قرار گرفته.برای مثال اگر پکیج doctrine رو باز کنید: path/to/project_name/vendor/doctrine/instantiatorدر فایل composer.json آمده:&amp;quotautoload&amp;quot: {
    &amp;quotpsr-4&amp;quot: {
        &amp;quotDoctrine\\Instantiator\\&amp;quot: &amp;quotsrc/Doctrine/Instantiator/&amp;quot
    }
},* در \\ بک اسلش اول برای escape کردن بک اسلش دوم (در داخل رشته) اومده.با توجه به مسیر هر پکیج خود composer هم یک map داره که مسیردهی پکیج هارو در فایل path/to/project_name/vendor/composer/autoload_psr4.php مپ کرده. بخشی ازین فایل:&lt;?php

// autoload_psr4.php @generated by Composer

$vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);

return array(
    &#039;yii\\composer\\&#039; =&gt; array($vendorDir . &#039;/yiisoft/yii2-composer&#039;),
    &#039;yii\\bootstrap\\&#039; =&gt; array($vendorDir . &#039;/yiisoft/yii2-bootstrap/src&#039;),
    &#039;yii\\&#039; =&gt; array($vendorDir . &#039;/yiisoft/yii2&#039;),
    &#039;Doctrine\\Instantiator\\&#039; =&gt; array($vendorDir . &#039;/doctrine/instantiator/src/Doctrine/Instantiator&#039;),
    // ....
    // ....
    // etc....
);خب بند آخر PHPFig رو هم بیارم و تمام.4.autoloader شما نباید هیچ exception یا خطایی تولید کنه و توصیه میشه که هیچ مقداری هم return نشه.(البته من دیدم توو پروژه ها exception تولید میکنن)این مثال رو برای پیاده سازی یک autoload ببینید.منبع: بیت هاب</description>
                <category>محسن موحد (آموزش برنامه نویسی)</category>
                <author>محسن موحد (آموزش برنامه نویسی)</author>
                <pubDate>Sat, 20 Nov 2021 09:26:26 +0330</pubDate>
            </item>
            </channel>
</rss>