<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>یادداشتهای بیت نیمسوز &#187; PHP</title>
	<atom:link href="http://cyberrabbits.net/tag/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://cyberrabbits.net</link>
	<description>یک وبلاگ دیگر از یک برنامه نویس دیگر</description>
	<lastBuildDate>Thu, 17 May 2012 06:58:08 +0000</lastBuildDate>
	<language>fa</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.2</generator>
		<item>
		<title>از یک دانلود ساده تا یک خود درگیری بزرگ!</title>
		<link>http://cyberrabbits.net/1068/challenge-accepted/</link>
		<comments>http://cyberrabbits.net/1068/challenge-accepted/#comments</comments>
		<pubDate>Thu, 05 Apr 2012 08:17:57 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[Bash]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[لینوکس]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[mini2440]]></category>
		<category><![CDATA[pacman]]></category>
		<category><![CDATA[sftp]]></category>
		<category><![CDATA[ssh]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=1068</guid>
		<description><![CDATA[یکی از مشکلاتی که لینوکسی همیشه میبینم دربارش بحث میکنن یه دانلودر خوب برای دانلود شبانه‌است. به عبارتی دانلودری که زمان‌بندی هم داشته باشه. البته هستن یه چند تای، ولی هیچکدوم همه شرایط لازم رو همزمان ندارن. ولی خوب، میشه گفت این مشکل خیلی ساده حل میشه. دانلودرهای خط‌فرمانی خوبی توی لینوکس وجود داره و&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/264/download-with-resume-support-in-php/' rel='bookmark' title='دانلود فایل با قابلیت Resume در PHP'>دانلود فایل با قابلیت Resume در PHP</a> <small>کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/360/bash-gui-wget/' rel='bookmark' title='Bash GUI for wget'>Bash GUI for wget</a> <small>اینبار هم میخوام بازم یه اسکریپت رو که تازه نوشتم...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از مشکلاتی که لینوکسی همیشه میبینم دربارش بحث میکنن یه دانلودر خوب برای دانلود شبانه‌است. به عبارتی دانلودری که زمان‌بندی هم داشته باشه. البته هستن یه چند تای، ولی هیچکدوم همه شرایط لازم رو همزمان ندارن. ولی خوب، میشه گفت این مشکل خیلی ساده حل میشه.<br />
دانلودرهای خط‌فرمانی خوبی توی لینوکس وجود داره و من،‌به شخصه، شدیدا طرفدار aria2 هستم. بعضیا ممکنه axel رو دوست داشته باشن. ولی خوب برای زمان بندی اینها چه کاری میشه کرد؟ سادست :) cron اینجا به دادتون میرسه.<br />
من برای دانلود از یه <a href="http://www.friendlyarm.net/products/mini2440">mini2440</a> استفاده میکنم. علتشم اینه که مصرفش خیلی کمه، و از همه مهمتر سر و صدا نداره و مجبور نیستم یه کامپیوتر یا لپ تاب رو بیخود روشن بذارم. روش خیلی ساده aria2 رو کامپایل کردم و الان مدتهاست مثل ساعت داره کار میکنه :)) خوب حالا چطور این کار رو انجام دادم؟<br />
<a href="http://cyberrabbits.net/wp-content/uploads/2012/04/mini2440_1.jpg"><img src="http://cyberrabbits.net/wp-content/uploads/2012/04/mini2440_1-300x273.jpg" alt="مینی ۲۴۴۰ یکی از تفریحات من :)" title="mini2440" width="300" height="273" class="aligncenter size-medium wp-image-1072" /></a></p>
<p>توی یه پوشه به خصوص، جایی که میخواید دانلودها اونجا ریخته بشن، یه فایل متنی ساده درست کنید. من اسمشو گذاشتم dl.lst و توی پوشه /mnt/sd این فایل رو قرار دادم. توی هر خط این فایل یه لینک میذارم که قراره دانلود بشه و یه اسکریپت خیلی ساده هم به این صورت مینویسم : </p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/bash

cd /mnt/sd/
aria2c -i /mnt/sd/dl.lst -m 10 -j 2
</pre>
<p>اینکه آرگومانها چه کاری میکنن، با خودتون :) فقط آرگومان -i رو حواستون بهش باشه که آدرس فایل لیستی که بالاتر درست کردید باید بعدش بیاد.<br />
 و نهایتا میرسه نوبت به cron .<br />
اگه ویرایشگر خاصی مدنظرتونه (مثلا nano یا vim یا هرچی ) اینطوری عمل کنید : </p>
<pre class="brush: bash; title: ; notranslate">
EDITOR=nano crontab -e
</pre>
<p>اینجوری ویرایشگر مورد علاقتون باز میشه و اگه قبلا cron داشته باشید این فایل که باز شده خالی نیست. ولی ممکنه خالی باشه :)‌اونوقت باید یه چیزی شبیه این توش نوشته بشه : </p>
<pre class="brush: plain; title: ; notranslate">
0  2  *  *  *  /bin/aria.sh
0  8  *  *  *  /usr/bin/killall aria2c
</pre>
<p>ساعت ۲ و ۸ به ترتیب شروع و پایان دانلود رایگان منه. اگه از قبل چیزی در اون فایل بود، فقط اینو به انتهاش اضافه کنید.<br />
فرض بر اینه که اسم اسکریپتی که بالاتر نوشتم، aria.sh باشه توی پوشه bin باشه (شما جای دیگه بذارید من توی mini یه کم محدودیت داشتم :)) )و اجرایی هم شده باشه به این صورت : </p>
<pre class="brush: bash; title: ; notranslate">
chmod a+x /bin/aria.sh
</pre>
<p>دستور killall هم معمولا تو همین آدرسه ولی اگه خواستید مطمئن بشید بزنید : </p>
<pre class="brush: bash; title: ; notranslate">
which killall
</pre>
<p>همین :)) فقط کافیه لیست فایلهایی که میخواید دانلود کنید رو بذارید تو فایل dl.lst که اول ساختید. </p>
<p><strong>خود درگیری به سبک یک گیک!!!<br />
</strong><br />
<span id="more-1068"></span><br />
خوب خیلیها تا همین‌جا براشون کافیه، برای من نبود.من میخوام دو تا سیستم رو هم به روز نگه دارم. یکی سیستم خودم که آرچه و یکی دیگه هم سیستم بیتا، که مینته. در حقیقت میخوام آپدیتها توی دانلود شبانه گنجونده بشه، و در نهایت بگیرمشون و روی سیستم خودم داشته باشمشون. و اینکار خیلی ساده انجام بشه. یعنی هی نخوام فایلها رو از رو اون سیستم بیارم اینور کپی کنم و فلان. خوب اینجا دیگه حقیقتا حس bash نوشتن نداشتم این شد که رفتم سراغ PHP .<br />
خوب برای اینکار sftp بهترین گزینست. روی دو سیستم اصلی openssh نصبه و روی مینی هم یه نسخه ساده کامپایل کردم از قبل. خوب برای انتقال فایل از sftp میشه این دستورات رو استفاده کرد : </p>
<pre class="brush: bash; title: ; notranslate">
cd /target/dir/on/local
sftp user@server &lt;&lt;EOF
cd /target/dir/on/remote
put /file/address/in/local
get /file/address/in/remote
quit
EOF
</pre>
<p>خوب زیاد هم سخت نیست. فرض کنید بخواید یه فایل از اون سرور انتقال بدید این طرف، یه فایل از این طرف بفرستید اون طرف :) اول تو سیستم خودتون وارد پوشه مقصد میشید. دستور sftp رو اجرا میکنید بعد توی sftp با کمک دستور cd (که اونجا هم دقیقا مثل اصلش کار میکنه) وارد پوشه مقصد میشید. با دستور put که بعدش باید آدرس فایل روی ماشین خودتون باشه، فایل فرستاده میشه تو پوشه‌ای که بهش cd کردید تو ماشین دوم (سرور) و با دستور get یه فایل رو میتونید از اونجا بگیرید و توی پوشه‌ای که توی ماشین خودتون قبل از دستور sftp داخلش شدید قرار میگیره. و بعد هم quit که خارج میشه .<br />
اون EOF هم یه چیزی مثل Heredoc ها توی PHP رو برای bash پیاده سازی میکنه و میگه از اینجا تا زمانی که به EOF برسی، یه دستوره و چند خط پیاپی و بی ربط نیست.<br />
تنها دستور دیگه که لازمه rm ـه که توی sftp هم دقیقا مثل rm اصلی عمل میکنه (sftp هم چیزی جدای از ssh نیست :) )<br />
خوب قدم به قدم.<br />
اول نیاز دارم بدونم که چه فایلهایی باید برای آپدیت دانلود شن: </p>
<pre class="brush: php; title: ; notranslate">
#!/usr/bin/php
&lt;?php
echo &quot;Get update list from pacman...\n&quot;;

$updlist = `sudo pacman -Syup | egrep -o -e &quot;(ht|f)tp://[^\']+&quot;`;
$updlist = explode(&quot;\n&quot;, $updlist );

$updPkg = `sudo pacman -Sup --print-format %n`;
$updPkg = explode(&quot;\n&quot;, $updPkg );

while (substr($updPkg[0] , 0 , 2) == '::')
    array_shift($updPkg);

$neededPkg = array ();
foreach ($updlist as $indx =&gt; $pkg)
{
    if (!empty($pkg))
    {
        $neededPkg[$updPkg[$indx]] = $pkg;
	}
}
echo &quot;List updates : &quot;;
print_r($neededPkg);
</pre>
<p>اول pacman رو اجرا میکنیم با سوییچ p و لیست دانلودها رو میگیریم. منتها من بدم نمیومد که اسم پکیجها رو هم داشته باشم. اجرای دوباره pacman برای گرفتن اسم پکیجهاست. بعد این آدرسها رو توی آرایه neededPkg گذاشتم.<br />
حالا وقت اینه که بررسی کنم ببینم آیا قبلا، هیچکدوم از این فایلها دانلود شدن یا نه؟ اونم به این صورت انجام دادم : </p>
<pre class="brush: php; title: ; notranslate">
define ('server','root@192.168.1.24' );
echo &quot;Get downloaded list... \n&quot;;

$cmnd  = &quot;sftp &quot; . server . &quot; &lt;&lt; EOF
ls /mnt/sd
quit
EOF&quot;;

$files =explode (&quot;\n&quot;,`$cmnd`);

$results = array( );

foreach ( $files as $fi )
{
	$f = trim($fi);
    if ( substr($f , 0 , strlen( &quot;/mnt/sd/&quot; ) ) == '/mnt/sd/')
        $results[ basename($f)] = $f;
}

echo &quot;This is the list of files available on sd card : \n&quot;;
print_r($results);
</pre>
<p>خیلی ساده :) sftp رو اجرا میکنم، دستور ls رو اجرا میکنم، بعد خروجی رو میگیرم. نتیجه توی آرایه results ریخته شده.<br />
گام سوم ببینم آیا فایلی از فایلهایی که میخوام تو لیست هست یا نه. aria2 وقتی دانلود هنوز تموم نشده باشه یه فایل کنار فایل اصلی میسازه به اسم همون فایل فقط آخرش یه .aria2 میچسبونه. وجود این فایل میتونه نشون بده که فایل کامل دانلود نشده : </p>
<pre class="brush: php; title: ; notranslate">

$downloaded = array();
$newList = array();
foreach ($neededPkg as $pkg =&gt; $addreess)
{
    $x = basename($addreess);
    if (isset($results[$x] ) &amp;&amp; !isset( $results[$x . &quot;.aria2&quot;]))
    {
       $downloaded[$pkg] = $x;
       echo $x . &quot; is done. \n&quot;;
    }
    else
    {
        $newList[] = $addreess;
        echo $x . &quot; is NOT done. \n&quot;;
    }
}

$newList = implode(&quot;\n&quot;, $newList );
file_put_contents(&quot;/tmp/upd.lst&quot; , $newList);
</pre>
<p>اگه دقت کنید یه فایل لیست ایجاد شده (/tmp/upd.lst ) حاوی فایلهایی که لازمن ولی هنوز دانلودشون تموم نشده. آرایه downloaded هم لیست فایلهاییه که دانلود شدن. خوب حالا قدم نهایی گرفتن این فایلهاست از سرور و آوردنشون روی سیستم محلیه.<br />
من میخوام اونها رو بریزم توی پوشه‌ای به نام .upd توی home خودم. برای همین لازمه که اول وارد این پوشه بشم : </p>
<pre class="brush: php; title: ; notranslate">

chdir (&quot;/home/f0rud/.upd&quot;);

$newCmnd = 'sftp ' . server . &quot; &lt;&lt;EOF
cd /mnt/sd/
rm /mnt/sd/upd.lst
put /tmp/upd.lst
&quot;;

$pacman = 'sudo pacman -S --noconfirm ';
$updateIsAvailable = false;
foreach ($downloaded as $p =&gt; $x)
{
    $newCmnd .= &quot;\nget \&quot;/mnt/sd/$x\&quot;&quot;;
    $newCmnd .= &quot;\nrm \&quot;/mnt/sd/$x\&quot;&quot;;
    $pacman .= &quot; $p &quot;;
    $updateIsAvailable = true;
}

$newCmnd .= &quot;\nquit
EOF
&quot;;

echo $newCmnd;

`$newCmnd`;
</pre>
<p>یه متغیر newCmnd درست میشه که توی این دستور، فایل upd.lst که دفعه قبل ساخته شد، فرستاده میشه به طرف سرور، فایلهایی که در مرحله قبل فهمیدیم دانلودشون تموم شدن، یکی یکی از سرور گرفته میشن، بعد از روی سرور حذف میشن، و همزمان یه دستوری هم ساخته میشه که باید در مرحله آخر برای آپدیت فایلهای دانلود شده اون دستور اجرا بشه. (منظورم متغیر pacman توی کده. ) خوب، وقتشه newCmnd اجرا میشه (یادم رفت بگم که اپراتور بک تیک ` توی PHP ، دستور رو داخل شل اجرا میکنه :)) ) محض احتیاط، دستور ایجاد شده رو هم چاپ میکنیم که ببینیم چه خبره :)<br />
و حالا گام آخر، وقتشه که پکمن اجرا بشه. </p>
<pre class="brush: php; title: ; notranslate">

if ($updateIsAvailable)
{
	$str = &quot;sudo mv /home/f0rud/.upd/* /var/cache/pacman/pkg/&quot;;
	`$str`;

	echo $pacman;

	`$pacman`;
}else
	echo &quot;No update for now.&quot;;
</pre>
<p>اگه آپدیتی باشه، فایلها به پوشه کش پکمن منتقل میشن، و بعد پکمن اجرا میشه.<br />
خوب حالا یه مشکلی هست، این فایل upd.lst که الان اون طرف هست، چطور باید به لیست دانلود اضافه بشه؟ خیلی ساده! اسکریپت بالایی رو که هنوز یادتون هست؟ همونی که aria2 رو اجرا میکرد و اسمش aria.sh بود . تغییرش دادم به این : </p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/bash

cd /mnt/sd/
cat /mnt/sd/upd.lst &gt; /mnt/sd/dl
echo &quot; &quot; &gt;&gt; /mnt/sd/dl
cat /mnt/sd/dl.lst &gt;&gt; /mnt/sd/dl

aria2c -i /mnt/sd/dl -m 10 -j 2
</pre>
<p>همین :)) حالا کافیه که فایل php رو اجرایی کنم، و بعد هم خیلی ساده اونو اجراش کنم و تمام! خودش همه کارها رو انجام میده. فقط یه مشکل :) این sftp رمز میخواد. sudo هم به همچنین :) حالا sudo قابل تحمله،‌ چون یه بار که میپرسه تا یه مدتی بیخیال میشه :) ولی sftp چی؟ خیلی سادست :)) </p>
<p>توجه کنید لطفا! اگر قبلا برای مثلا github یا هر جای دیگه یا هر دلیل دیگه کلید خودتون رو درست کردید، گام اول رو انجام ندید وگرنه کلید قبلی از بین میره!!!! </p>
<pre class="brush: plain; title: ; notranslate">
~  ᐅ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/f0rud/.ssh/id_rsa):
Created directory '/home/f0rud/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/f0rud/.ssh/id_rsa.
Your public key has been saved in /home/f0rud/.ssh/id_rsa.pub.
The key fingerprint is:
29:90:f2:bc:3b:61:4a:d1:32:85:03:f0:70:4f:d3:a3 f0rud@elbit
The key's randomart image is:
+--[ RSA 2048]----+
|=...o.           |
| +oo.oo          |
|  o++. .         |
|  ++E.   .       |
|   +o . S        |
|  . o. .         |
| . o..           |
|  . ..           |
|    ..           |
+-----------------+
</pre>
<p>با دستور ssh-keygen یه کلید جدید درست کنید. هر چی پرسید فقط Enter بزنید. یعد در گام دوم، با دستور ssh-copy-id این کلید رو منتقل کنید به ماشین مقصد : </p>
<pre class="brush: bash; title: ; notranslate">
ssh-copy-id root@192.168.1.24
</pre>
<p>اینجا رمز ماشین رو میپرسه، بعد کلید رو منتقل میکنه. حالا سعی کنید به ماشین مقصد ssh کنید. اگه همه چی درست باشه دیگه رمز ازتون نمیپرسه. خوب همه چی حله!!!!<br />
اینم کل اسکریپت بالایی به صورت یک جا : </p>
<pre class="brush: php; title: ; notranslate">
#!/usr/bin/php
&lt;?php
define ('server','root@192.168.1.24' );

echo &quot;Get update list from pacman...\n&quot;;

$updlist = `sudo pacman -Syup | egrep -o -e &quot;(ht|f)tp://[^\']+&quot;`;
$updlist = explode(&quot;\n&quot;, $updlist );

$updPkg = `sudo pacman -Sup --print-format %n`;
$updPkg = explode(&quot;\n&quot;, $updPkg );

while (substr($updPkg[0] , 0 , 2) == '::')
    array_shift($updPkg);

$neededPkg = array ();
foreach ($updlist as $indx =&gt; $pkg)
{
    if (!empty($pkg))
    {
        $neededPkg[$updPkg[$indx]] = $pkg;
	}
}
echo &quot;List updates : &quot;;
print_r($neededPkg);

echo &quot;Get downloaded list... \n&quot;;

$cmnd  = &quot;sftp &quot; . server . &quot; &lt;&lt; EOF
ls /mnt/sd
quit
EOF&quot;;

$files =explode (&quot;\n&quot;,`$cmnd`);

$results = array( );

foreach ( $files as $fi )
{
	$f = trim($fi);
    if ( substr($f , 0 , strlen( &quot;/mnt/sd/&quot; ) ) == '/mnt/sd/')
        $results[ basename($f)] = $f;
}

echo &quot;This is the list of files available on sd card : \n&quot;;
print_r($results);

$downloaded = array();
$newList = array();
foreach ($neededPkg as $pkg =&gt; $addreess)
{
    $x = basename($addreess);
    if (isset($results[$x] ) &amp;&amp; !isset( $results[$x . &quot;.aria2&quot;]))
    {
       $downloaded[$pkg] = $x;
       echo $x . &quot; is done. \n&quot;;
    }
    else
    {
        $newList[] = $addreess;
        echo $x . &quot; is NOT done. \n&quot;;
    }
}

$newList = implode(&quot;\n&quot;, $newList );
file_put_contents(&quot;/tmp/upd.lst&quot; , $newList);

chdir (&quot;/home/f0rud/.upd&quot;);

$newCmnd = 'sftp ' . server . &quot; &lt;&lt;EOF
cd /mnt/sd/
rm /mnt/sd/upd.lst
put /tmp/upd.lst
&quot;;

$pacman = 'sudo pacman -S --noconfirm ';
$updateIsAvailable = false;
foreach ($downloaded as $p =&gt; $x)
{
    $newCmnd .= &quot;\nget \&quot;/mnt/sd/$x\&quot;&quot;;
    $newCmnd .= &quot;\nrm \&quot;/mnt/sd/$x\&quot;&quot;;
    $pacman .= &quot; $p &quot;;
    $updateIsAvailable = true;
}

$newCmnd .= &quot;\nquit
EOF
&quot;;

echo $newCmnd;

`$newCmnd`;

if ($updateIsAvailable)
{
	$str = &quot;sudo mv /home/f0rud/.upd/* /var/cache/pacman/pkg/&quot;;
	`$str`;

	echo $pacman;

	`$pacman`;
}else
	echo &quot;No update for now.&quot;;
</pre>
<p>چقدر نوشتم!!!!!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/1068/challenge-accepted/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/264/download-with-resume-support-in-php/' rel='bookmark' title='دانلود فایل با قابلیت Resume در PHP'>دانلود فایل با قابلیت Resume در PHP</a> <small>کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/360/bash-gui-wget/' rel='bookmark' title='Bash GUI for wget'>Bash GUI for wget</a> <small>اینبار هم میخوام بازم یه اسکریپت رو که تازه نوشتم...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/1068/challenge-accepted/feed/</wfw:commentRss>
		<slash:comments>15</slash:comments>
		</item>
		<item>
		<title>PHP 5.4 Trait</title>
		<link>http://cyberrabbits.net/1056/php-5-4-trait/</link>
		<comments>http://cyberrabbits.net/1056/php-5-4-trait/#comments</comments>
		<pubDate>Mon, 05 Mar 2012 10:32:33 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Class]]></category>
		<category><![CDATA[interface]]></category>
		<category><![CDATA[php 5.4]]></category>
		<category><![CDATA[singleton]]></category>
		<category><![CDATA[trait]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=1056</guid>
		<description><![CDATA[یکی از قابلیتهای جدیدی که به PHP 5.4 (که بالاخره چند روز پیش نسخه نهاییش منتشر شد) اضافه شده، Trait ها هستن. خیلی زبانها اجازه ارث بری چندگانه رو میدن ولی PHP از اونها نیست. البته این قابلیت-ارث بری چندگانه-، به نظر شخصی من یکی از اون قابلیتهاییه که توی ایجاد کردن دردسر و ابهام&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1002/phpunit/' rel='bookmark' title='PHPUnit'>PHPUnit</a> <small>یکی از کارهایی که به عنوان برنامه نویس باید انجام...</small></li>
<li><a href='http://cyberrabbits.net/733/regular-expression-part1/' rel='bookmark' title='عبارات با قاعده در PHP – بخش اول'>عبارات با قاعده در PHP – بخش اول</a> <small>عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از قابلیتهای جدیدی که به PHP 5.4 (که بالاخره چند روز پیش<a href="http://www.php.net/archive/2012.php#id2012-03-01-1"> نسخه نهاییش منتشر شد</a>) اضافه شده، <a href="https://wiki.php.net/rfc/horizontalreuse">Trait</a> ها هستن. خیلی زبانها اجازه ارث بری چندگانه رو میدن ولی PHP از اونها نیست. البته این قابلیت-ارث بری چندگانه-، به نظر شخصی من یکی از اون قابلیتهاییه که توی ایجاد کردن دردسر و ابهام میتونه خیلی نقش داشته باشه.<br />
PHP توی نسخه جدید،‌برای رفع این نقیصه یه موجودیت جدید رو معرفی کرده که البته دقیقا همون ارث بری چندگانه نیست، ولی به اندازه کافی مفید هست.. </p>
<p>خیلی وقتها کلاسهایی که مینویسیم، شباهتهایی با همدیگه دارن. مثلا برای سینگلتون من همیشه اینطور عمل میکنم : </p>
<pre class="brush: php; title: ; notranslate">
class Singleton
{
	private static $instance;

	public static function getInstance(){
		if (!self::$instance)
			self::$instance = new self();
		return self::$instance;
	}

}
</pre>
<p>حالا اگه من تعداد زیادی کلاس داشته باشم که بخوام همه اونها سینگلتون باشن، بای همه رو از این کلاس به ارث ببرم، که عملا ممکن نیست، مثلا من نمیتون کلاس مربوط به دیتابیس و کلاس مربوط به Translation رو از یه کلاس مشترک شروع کنم. </p>
<p>قبلا <a href="http://ir2.php.net/manual/en/language.oop5.interfaces.php">interface</a> ها معرفی شده بودن، ولی اونها فقط یه اعلام تابع هستن و بدنه تابع، برای هر کلاس باید دوباره توی خود کلاس نوشته بشه.<br />
توی زبانهایی مثل C++ میشه اینو خیلی ساده حل کرد، هر کلاس میتونه بیشتر از یه پدر داشته باشه،‌و تمام. (مثلا کلاس دیتابیس میتونه همزمان از کلاس دیتابیس پایه ارث ببره و هم از اینکلاس Singleton ) ولی PHP اون روش رو انتخاب نکرد، و من خوشحالم که اینکار رو نکرد، چون قبلا صابون ارث بری چندگانه به تنم خورده بود :)) </p>
<p>trait ها، یه موجودیت کامل نیستن. امکان نداره بشه یه شی از یه trait ساخت. اونها کلاس نیستن، بلکه قراره که بخشی از یه کلاس باشن.<br />
مثلا برای مثال سینگلتون : </p>
<pre class="brush: php; title: ; notranslate">
trait Singleton
{
	private static $instance;

	public static function getInstance(){
		if (!self::$instance)
			self::$instance = new self();
		return self::$instance;
	}
}

class MySingletonClass
{
	use Singleton;
}

$singleton = MySingletonClass::getInstance();
</pre>
<p>&#8211; یه نکته خیلی جالب، تو سایت php.net نوشته که </p>
<blockquote><p>
Static variables can be referred to in trait methods, but cannot be defined by the trait.
</p></blockquote>
<p>یعنی من نمیتونم یه متغیر استاتیک رو داخل یک trait تعریف کنم، ولی کد بالا بدون مشکل کار میکنه و این عجیبه! (همین کد بالا اجرا میشه،‌اگه به جای private عمومی هم باشه اجرا میشه و کلا مشکلی نیست.)</p>
<p>این خیلی ساده، باعث میشه که شما از نوشتن کد زاید بی نیاز بشید. مثالهای خیلی بهتر و جامعتری میشه ارایه داد، برای من که در عمل خیلی پیش اومده که یه قسمت از یه کلاس بارها و بارها در کلاسهای دیگه استفاده بشه. توی کامنتهای خود سایت php.net یه مثال جالب بود، و اونهم اینکه trait در حقیقت مثل Copy/Paste عمل میکنه و من تا حدی باهاش موافقم</p>
<p>مثالهای خود <a href="http://php.net/manual/en/language.oop5.traits.php">php.net</a> هم جالبن :<br />
یه کلاس میتونه از یه کلاس دیگه ارث ببره و هر چند تا trait هم داشته باشه مثلا : </p>
<pre class="brush: php; title: ; notranslate">

class Base {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait SayWorld {
    public function sayHello() {
        parent::sayHello();
        echo 'World!';
    }
}

class MyHelloWorld extends Base {
    use SayWorld;
}

$o = new MyHelloWorld();
$o-&gt;sayHello();  //Result is Hello World!
</pre>
<p>از طرفی تا یه حدی مشکل تداخل هم هست. در بعضی موارد، PHP اونها رو رفع میکنه مثلا در این مورد :</p>
<pre class="brush: php; title: ; notranslate">
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o-&gt;sayHello();  //Result is Hello Universe!
</pre>
<p>متد خود کلاس بعد از use اومده و اونه که اولویت داره. </p>
<p>یه نکته جالب دیگه اینه که میشه هر چند تا trait رو توی یه کلاس استفاده کرد :‌</p>
<pre class="brush: php; title: ; notranslate">
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo ' World';
    }
}

class MyHelloWorld {
    use Hello, World;
    public function sayExclamationMark() {
        echo '!';
    }
}

$o = new MyHelloWorld();
$o-&gt;sayHello();
$o-&gt;sayWorld();
$o-&gt;sayExclamationMark();
</pre>
<p>اگه دو trait تو یه کلاس استفاده بشن، و بعد هر دو یه متد رو داشته باشن، با خطای fatal متوقف میشید. برای اینکه بتونید این مشکل رو حل کنید و ابهام به وجود نیاد، PHP کلمه کلیدی جدید معرفی کرده به اسم insteadof (به شباهتش با instanceof توجه کنید، فکر کنم عمدا اینطوری انتخاب شده که شبیه باشن) </p>
<p>علاوه بر او کلیدواژه as هم میشه برای تغییر اسم دادن یک متد از یک trait توی یک کلاس استفاده میشه مثلا : </p>
<pre class="brush: php; title: ; notranslate">
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
</pre>
<p>البته as یه کاربرد دیگه هم داره و اون تغییر دسترسی یک متده . مثلا اگه یه متد توی trait عمومی باشه (public ) و شما بخواید تو کلاستون اونو محافظت شده داشته باشید : </p>
<pre class="brush: php; title: ; notranslate">
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

// Change visibility of sayHello
class MyClass1 {
    use HelloWorld { sayHello as protected; }
}

// Alias method with changed visibility
// sayHello visibility not changed
class MyClass2 {
    use HelloWorld { sayHello as private myPrivateHello; }
}
</pre>
<p>یک trait خودش میتونه از trait های دیگه استفاده کنه : </p>
<pre class="brush: php; title: ; notranslate">
trait Hello {
    public function sayHello() {
        echo 'Hello ';
    }
}

trait World {
    public function sayWorld() {
        echo 'World!';
    }
}

trait HelloWorld {
    use Hello, World;
}

class MyHelloWorld {
    use HelloWorld;
}

$o = new MyHelloWorld();
$o-&gt;sayHello();
$o-&gt;sayWorld();
</pre>
<p>توی trait ها میتونیم متد غایب داشته باشیم (abstract) ولی وقتی یه trait توی یک کلاس استفاده میشه اون کلاس یا باید خودش abstract باشه و اگه نیست باید حتما اون متد رو پیاده سازی کنه : </p>
<pre class="brush: php; title: ; notranslate">
trait Hello {
    public function sayHelloWorld() {
        echo 'Hello'.$this-&gt;getWorld();
    }
    abstract public function getWorld();
}

class MyHelloWorld {
    private $world;
    use Hello;
    public function getWorld() {
        return $this-&gt;world;
    }
    public function setWorld($val) {
        $this-&gt;world = $val;
    }
}
</pre>
<p>البته trait میتونه property هم تعریف کنه،‌ولی در این صورت کلاسی که ازش استفاده میکنه نمیتونه همون property رو داشته باشه تو ساختارش، چون با هم تداخل پیدا میکنن، و این تداخل، قابل اصلاح نیست (بر خلاف تداخل متدها). البته اگه مقدار اولیه دو خصیصه یکی باشه، خطایی گرفته نمیشه. نکته قابل توجه اینه که این مشکل درباره static property ها نیست، و در اون صورت خطایی اعلام نمیشه، ولی منطقا غلطه و این خطرناکتره به نظر من : </p>
<pre class="brush: php; title: ; notranslate">

trait PropertiesTrait {
    public $same = true;
    public $different = false;
}

class PropertiesExample {
    use PropertiesTrait;
    public $same = true; // Strict Standards.
    public $different = true; // Fatal error
}
</pre>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/1056/php-5-4-trait/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1002/phpunit/' rel='bookmark' title='PHPUnit'>PHPUnit</a> <small>یکی از کارهایی که به عنوان برنامه نویس باید انجام...</small></li>
<li><a href='http://cyberrabbits.net/733/regular-expression-part1/' rel='bookmark' title='عبارات با قاعده در PHP – بخش اول'>عبارات با قاعده در PHP – بخش اول</a> <small>عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/1056/php-5-4-trait/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>PHPUnit</title>
		<link>http://cyberrabbits.net/1002/phpunit/</link>
		<comments>http://cyberrabbits.net/1002/phpunit/#comments</comments>
		<pubDate>Tue, 14 Feb 2012 16:00:50 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[pear]]></category>
		<category><![CDATA[PHPUnit]]></category>
		<category><![CDATA[test]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=1002</guid>
		<description><![CDATA[یکی از کارهایی که به عنوان برنامه نویس باید انجام بدهم، ولی تنبلی مانع از انجامش میشود، نوشتن Test برای کد‌ها و ماژولهاست. فرض کنید که یک مدل نوشته شده برای اینکه یک کاربر را شبیه سازی کند. مدلی مثلا به شکل زیر : کد چندان پیچیده نیست، اما در عمل به همین سادگی نخواهد&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1056/php-5-4-trait/' rel='bookmark' title='PHP 5.4 Trait'>PHP 5.4 Trait</a> <small>یکی از قابلیتهای جدیدی که به PHP 5.4 (که بالاخره...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از کارهایی که به عنوان برنامه نویس باید انجام بدهم، ولی تنبلی مانع از انجامش میشود، نوشتن Test برای کد‌ها و ماژولهاست. فرض کنید که یک مدل نوشته شده برای اینکه یک کاربر را شبیه سازی کند. مدلی مثلا به شکل زیر : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	class UserModel {

		private $userName;

		private $isAuthenticated;

		public function __construct(){

			$this-&gt;userName = null;
			$this-&gt;isAuthenticated = false;
		}

		public function getUserName(){
			return $this-&gt;userName;
		}

		public function getIsAuthenticated(){
			return $this-&gt;isAuthenticated;
		}

		public function login($userName , $password){
			//For this example, I do it simple, but in real world we do it diffrent
			if ( $userName == 'admin' &amp;&amp; $password == 'bita')
			{
				$this-&gt;isAuthenticated = true;
				$this-&gt;userName = $userName;
				return true;
			}

			return false;

		}
	}
</pre>
<p>کد چندان پیچیده نیست، اما در عمل به همین سادگی نخواهد بود. معمولا برای تعیین اعتبار از دیتابیس استفاده میشود و جلسات (Session) هم  در آن نقش دارند. برای سادگی من همین حالت را در نظر گرفته‌ام.<br />
خوب برای اینکه این کلاس را تست کنیم و عملکردش را بررسی کنیم،‌بایستی یک نسخه از آن بسازیم و تک تک متدها را هم امتحان کنیم. چیزی مثل این : </p>
<pre class="brush: php; title: ; notranslate">
$user = new UserModel();

if ($user-&gt;getIsAuthenticated())
	echo &quot;Not OK&quot;;
else
	echo &quot;OK&quot;;
</pre>
<p>البته، با توجه به سادگی کد نوشته شده، چنین تستی ممکن است بی معنی به نظر برسد، اما خیلی وقتها همه این تست های ساده هم مهم خواهند بود.<br />
این روش، پاسخگو هست، ولی چندان خوشایند نیست. نوشتن if برای هر تست و چاپ خروجی و متن مناسب و .. همه اینها این روش را دشوار میکند. روش اصلی، استفاده از Unit Testing است.<br />
برای PHP یک کتابخانه کامل و جامع نوشته شده که میتوانید آنرا از طریق PEAR نصب کنید : </p>
<pre class="brush: bash; title: ; notranslate">
pear config-set auto_discover 1
pear install pear.phpunit.de/PHPUnit
</pre>
<p>و همچنین میتوانید راهنمای کامل آن و برگه تقلب را هم دانلود کنید. برای یک تست ساده، برای کدی که نوشتم،‌ یک کلاس دیگر، که عمل تست را انجام میدهد ولی جزئی از پروژه اصلی نیست لازم داریم :<br />
<span id="more-1002"></span></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
//This is our model class file
require 'UserModel.class.php';

class UserModelTest extends PHPUnit_Framework_TestCase
{
	public function testInitial()
	{
		$user = new UserModel();
		$this-&gt;assertEquals(false, $user-&gt;getIsAuthenticated());
		$this-&gt;assertEquals(null, $user-&gt;getUserName());
	}
}
</pre>
<p>کلاس جدید، از کلاس  PHPUnit_Framework_TestCase مشتق شده است، و یک متد دارد که اسم آن با test شروع شده، و داخل آن از یک سری متد استفاده شده است (در اینجا assertEquals ) متدهایی که با assert شروع میشوند، یعنی که در صورتی که شرط آنها برقرار باشد (مثلا equal بودن دو آرگومان) آنها موفق خواهند بود،‌در غیراینصورت ناموفقند. لزومی به require کردن فایلی به جز فایل اصلی که قرار است تست شود نیست، خود phpunit فایلهای مورد نیاز را در وقت اجرا اضافه خواهد کرد.<br />
خود PHPUnit کل کلاسهایی که از این کلاس استفاده کرده اند را پیدا کرده و به صورت اتوماتیک یکی یکی متدهایی که با اسمشان با test شروع شده را اجرا میکند. </p>
<p>حالا وقت اجرای تست است. بر فرض که فایل تست به نام test.php ذخیره شده باشد، دستور زیر :</p>
<pre class="brush: bash; title: ; notranslate">
phpunit  ./test.php
</pre>
<p>خروجی‌ای به این صورت خواهد داشت : </p>
<pre class="brush: plain; title: ; notranslate">
PHPUnit 3.6.9 by Sebastian Bergmann.

.

Time: 0 seconds, Memory: 5.50Mb

OK (1 test, 2 assertions)
</pre>
<p>خوب این یعنی تست با موفقیت انجام شده است و نتیجه مثبت بوده. تعداد نقطه‌های خط سوم، تعداد کل test هاست که انجام شده و در نهایت مقدار حافظه مصرف شده و در نهایت تعداد تست ها و assert های انجام شده را نشان میدهد.<br />
برای بهتر دیده شدن نتیجه، سوییچ &#8211;colors را به دستور اضافه کنید خروجی رنگی خواهد بود. حالا یک متد دیگر به تست اضافه میکنیم : </p>
<pre class="brush: php; title: ; notranslate">
	public function testThisMustFail()
	{
		$user = new UserModel();
		$this-&gt;assertTrue($user-&gt;login(&quot;me&quot;, &quot;mypassword&quot;));
	}
</pre>
<p>با توجه به کدی که در ابتدا نوشتم، این تابع بازگشتی false خواهد داشت و من از assertTrue استفاده کردم، به این معنی که باید آرگومان true باشد، که نیست. نتیجه تست اینطور خواهد بود : </p>
<pre class="brush: plain; title: ; notranslate">
PHPUnit 3.6.9 by Sebastian Bergmann.

.F

Time: 0 seconds, Memory: 5.50Mb

There was 1 failure:

1) UserModelTest::testThisMustFail
Failed asserting that false is true.

/home/f0rud/test.php:17

FAILURES!
Tests: 2, Assertions: 3, Failures: 1.
</pre>
<p>در حقیقت، تستها باید طوری نوشته شوند که assert ها فقط در حالاتی اشتباه شوند که واقعا مشکلی وجوددارد،‌در صورتی که در تست دوم، واقعا مشکلی وجود ندارد و کد درست عمل میکند، بنابراین طریقه نوشتن تست اشتباه است نه کد. در حقیقت باید از متد assertFalse استفاده کرد که در این صورت نتیجه درست خواهد بود.<br />
چند نکته را در نظر بگیرید :<br />
۱- اسم تست ها، هر چه واضح تر باشند بهتر است. طول اسم متد مهم نیست، الگویی پیدا کنید و بر اساس الگویی که تعیین کردید اسم گذاری کنید، مثلا testNamespaceClassMethod<br />
۲- وقتی کلاس درست عمل کند تست باید موفقیت آمیز باشد. شاید در بعضی شرایط، کد باید یک خطا ایجاد کند، در این موارد حتما توجه کنید که تست را طوری بنویسید که هر وقت رفتار مورد انتظار نشان داده شد، assert موفقیت آمیز باشد، هیچ تفاوت یا ارجحیتی مثلا بین assertTrue و assertFalse نیست.<br />
۳- خیلی از framework ها، برای تست کد، نیاز به مقدماتی دارند که معمولا در خود framework پیش بینی آن میشود. مثلا در Zend از قبل کلاسهایی برای UnitTest وجود دارد، همچنین در Symfony یا Agavi. برای نوشتن کد تست برای این framework ها بهتر است که راهنمای خود آنها را ببینید و از روش خودشان استفاده کنید.</p>
<p>برای کد بالا، این تست تقریبا تست مناسبی است :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

require 'UserModel.class.php';

class UserModelTest extends PHPUnit_Framework_TestCase
{
	public function testUserModelInitialize()
	{
		$user = new UserModel();
		$this-&gt;assertEquals(false, $user-&gt;getIsAuthenticated());
		$this-&gt;assertEquals(null, $user-&gt;getUserName());
	}

	public function testUserModelLogin()
	{
		$user = new UserModel();
		$this-&gt;assertFalse($user-&gt;login(&quot;me&quot;, &quot;mypassword&quot;));
		$this-&gt;assertEquals(false, $user-&gt;getIsAuthenticated());
		$this-&gt;assertEquals(null, $user-&gt;getUserName());
		$this-&gt;assertTrue($user-&gt;login(&quot;admin&quot; , &quot;bita&quot;));
		$this-&gt;assertEquals(true, $user-&gt;getIsAuthenticated());
		$this-&gt;assertEquals('admin', $user-&gt;getUserName());
	}
}
</pre>
<p>در آینده هم اگر بتوانم باز درباره UnitTest خواهم نوشت.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/1002/phpunit/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1056/php-5-4-trait/' rel='bookmark' title='PHP 5.4 Trait'>PHP 5.4 Trait</a> <small>یکی از قابلیتهای جدیدی که به PHP 5.4 (که بالاخره...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/1002/phpunit/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>ربات مافیا &#8211; PHP</title>
		<link>http://cyberrabbits.net/962/mafia-irc-php/</link>
		<comments>http://cyberrabbits.net/962/mafia-irc-php/#comments</comments>
		<pubDate>Wed, 21 Sep 2011 16:36:27 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[متفرقه]]></category>
		<category><![CDATA[Bot]]></category>
		<category><![CDATA[Eli]]></category>
		<category><![CDATA[IRC]]></category>
		<category><![CDATA[mafia]]></category>
		<category><![CDATA[zconf]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=962</guid>
		<description><![CDATA[بازی مافیا رو اولین بار وقتی دانشجو بودیم انجام دادم و متاسفانه چون توی فصل امتحانات بود و منم فارغ التحصیل زیاد قاطی قضیه نشدم :)‌ چند وقت پیش، دوست خوبم ایمان، سعی کرد بچه ها رو جمع کنه برای بازی تو Gtalk و همون موقع تصمیم گرفتم که این بازی رو به صورت یه&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/321/about-past/' rel='bookmark' title='یه کم درباره گذشته&#8230;.'>یه کم درباره گذشته&#8230;.</a> <small>چند وقتی بود که درگیر بودم. یه سر رفتم نمایشگاه....</small></li>
<li><a href='http://cyberrabbits.net/159/linux-and-the-lost-delight/' rel='bookmark' title='لینوکس و شوقی که گم کرده بودم'>لینوکس و شوقی که گم کرده بودم</a> <small>خیلی سال پیش،‌ وقتی برای اولین بار یک کامپیوتر دیدم،‌بلافاصله...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>بازی <a href="http://fa.wikipedia.org/wiki/%D9%85%D8%A7%D9%81%DB%8C%D8%A7_(%D8%A8%D8%A7%D8%B2%DB%8C_%DA%AF%D8%B1%D9%88%D9%87%DB%8C)">مافیا</a> رو اولین بار وقتی دانشجو بودیم انجام دادم و متاسفانه چون توی فصل امتحانات بود و منم فارغ التحصیل زیاد قاطی قضیه نشدم :)‌<br />
چند وقت پیش، دوست خوبم ایمان، سعی کرد بچه ها رو جمع کنه برای بازی تو Gtalk و همون موقع تصمیم گرفتم که این بازی رو به صورت یه ربات برای IRC پیاده سازی کنم. به عبارتی این ربات نقش خدا رو توی بازی انجام میده.</p>
<p>تو این بین، تجربه های جالبی هم داشتیم، تو همایش <a href="http://2011.zconf.ir/">ZConf</a> بازی رو به صورت حضوری انجام دادیم و بعد هم گویا ترکشهای این بازی به <a href="http://yeenghelabi.wordpress.com/2011/09/18/%D9%85%D8%A7%D9%81%DB%8C%D8%A7-%D9%88-%D8%A7%D9%88%D9%84%DB%8C%D9%86-%D8%AA%D8%AC%D8%B1%D8%A8%D9%87%E2%80%8C%DB%8C-%D9%85%D9%86/">جشن اصفهان</a> هم رسید و الی آخر!</p>
<p>اما ربات. تصمیم گرفتم که منتشرش کنم. قبلا هم <a href="http://cyberrabbits.net/non/mafia/">راهنماشو</a> نوشتم و حالا کد.<br />
فقط یه لطفی کنید و اگه میخواید ازش استفاده کنید :‌<br />
۱- خودتون دو تا Channel برای مافیا درست کنید اون اصلی ها رو که من ثبت کردم (##PMG و ##PMGMafais ) رو شما باید تغییر بدید. -module/mafia/MafiaGame.php-<br />
۲-برای ربات هم یه اسم دیگه انتخاب کنید fzBot رو من ثبتش کردم :)<br />
۳- همین دیگه خود دانید :) یادتون باشه این ربات فقط برای تفریح نوشته شده و من اصلا قصد انتشارشو یا حتی رسوندنش به این مرحله رو نداشتم، پس زیاد انتظار یه کد ایده آل نداشته باشید.</p>
<p>فعلا همین :) تا بعد که نسخه بعدیشو در بیارم، شاید یه تیپ دیگه و &#8230;</p>
<p>پ.ن : امروز تولد الیه. تولدت مبارک خانمی :)<br />
<ins datetime="2011-09-26T14:08:04+00:00">و اینم Github : https://github.com/fzerorubigd/pmg</ins></p>
<p>برای گرفتن کد از GIT استفاده کنید :))</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/962/mafia-irc-php/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/321/about-past/' rel='bookmark' title='یه کم درباره گذشته&#8230;.'>یه کم درباره گذشته&#8230;.</a> <small>چند وقتی بود که درگیر بودم. یه سر رفتم نمایشگاه....</small></li>
<li><a href='http://cyberrabbits.net/159/linux-and-the-lost-delight/' rel='bookmark' title='لینوکس و شوقی که گم کرده بودم'>لینوکس و شوقی که گم کرده بودم</a> <small>خیلی سال پیش،‌ وقتی برای اولین بار یک کامپیوتر دیدم،‌بلافاصله...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/962/mafia-irc-php/feed/</wfw:commentRss>
		<slash:comments>13</slash:comments>
		</item>
		<item>
		<title>HipHop</title>
		<link>http://cyberrabbits.net/909/hiphop/</link>
		<comments>http://cyberrabbits.net/909/hiphop/#comments</comments>
		<pubDate>Sat, 02 Jul 2011 06:27:30 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[لینوکس]]></category>
		<category><![CDATA[C++]]></category>
		<category><![CDATA[CMake]]></category>
		<category><![CDATA[HipHop]]></category>
		<category><![CDATA[webserver]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=909</guid>
		<description><![CDATA[&#8211; این فقط یه معرفی ساده یه پروژه است نه راهنمای کاملش. یکی از معروفترین سایتهای فعلی، فیسبوکه و شکی در این نیست (نمیدونم با وجود گوگل پلاس وضع همینطوری میمونه یا نه؟). ولی این سایت همینجوری که تبدیل نشده به اینی که الان هست! یک تیم برنامه نویسی قدرتمند هم پشت این پروژه هست&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/811/zend-translate-and-gnugettext/' rel='bookmark' title='Zend_Translate و GnuGetText'>Zend_Translate و GnuGetText</a> <small>یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم،...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><em>&#8211; این فقط یه معرفی ساده یه پروژه است نه راهنمای کاملش. </em><br />
یکی از معروفترین سایتهای فعلی، فیسبوکه و شکی در این نیست (نمیدونم با وجود گوگل پلاس وضع همینطوری میمونه یا نه؟). ولی این سایت همینجوری که تبدیل نشده به اینی که الان هست! یک تیم برنامه نویسی قدرتمند هم پشت این پروژه هست و چیزی که امروز میخوام معرفی کنم، دلیلی بر این مدعاست.</p>
<p>پروژه <a href="https://github.com/facebook/hiphop-php/wiki/">HipHop for PHP</a> پروژه ای است که بر اساس نیازهای آنها -فیس بوکی ها- ساخته شده. پروژه ای که یه کار &#8211; به ظاهر &#8211; ساده انجام میده. خیلی ساده، کدهای PHP رو تبدیل میکنه به C++ و بعد اون رو کامپایل میکنه، در نهایت کل پروژه تبدیل میشه به یه فایل اجرایی تکی، که خودش یک وب سرور کامل هم هست.<br />
اینجوری، سرعت اجرای کد به طرز کاملا محسوسی بالاتر میره و فشار اجرای مفسری PHP به شدت کم میشه . البته هنوز فایلهای استاتیک به صورت مجزا از این فایل وجود دارند ولی دیگه PHP وجود نداره، کد باینری واقعی هست. یه چیزی مثل <a href="https://live.gnome.org/Vala">vala</a> که تبدیل میشه به C . مثل پروژه هایی که حتما دیدید و فایل PHP رو توی یک فایل اجرایی میچپونن هم نیست -<a href="http://www.bambalam.se/bamcompile/">مثلا این پروژه</a>-.<br />
 این پروژه یه بازنویسی از کل کد PHP هستش نه یه پروژه مشتق شده از PHP. یه سری توابع از دست میرن (مثلا تابع eval) ولی در نهایت توابعی هم که از دست میرن جایگزین مناسبی دارن :) مثلا همین eval یه جورایی از دید من یه تابع خیلی خطرناک محسوب میشه و استفاده ازش احتیاط خیلی زیادی رو میطلبه!<br />
این برنامه در کل ممکنه برای ما هم خیلی کاربردی نباشه چون به درد همه جا نمیخوره. کلا  بعید میدونم به همین راحتی رو ویندوز کامپایل بشه! ولی گاهی وقتها میتونه کارتون رو راه بندازه،مثلا ما الان نیاز به یه وب سرویس داریم تو یه پروژه که روی سرورهای داخلی یه شرکت قراره نصب بشه و فقط یک ارتباط ساده رو با دیتابیس برقرار کنه و از طرفی نصب آپاچی و PHP هم یه جورایی از طرف مدیر شبکه منع شده -یه جور سرور radius هستش و میخوان سبک بمونه &#8211; خوب بهتر از HipHop چی میتونه به ما کمک کنه؟ </p>
<p>حالا میرسیم به چگونگی استفاده از این به اصطلاح HipHop!<br />
<span id="more-909"></span><br />
<strong>نصب !</strong><br />
برای آرچ لینوکس توی <a href="http://aur.archlinux.org/packages.php?ID=35023">AUR</a> هستش. و نصبش هم نیاز به خیلی چیزها داره که شاید بد نباشه از اینجا بخونیدش : <a href='https://github.com/facebook/hiphop-php/wiki/Building-and-installing' title='Building and installing - GitHub'>Building and installing &#8211; GitHub</a><br />
به هر حال سورسش اینجاست :</p>
<pre class="brush: bash; title: ; notranslate">
git clone git://github.com/facebook/hiphop-php.git
</pre>
<p>نمیرم تو بحث نصب به صورت کامل، توصیه میکنم بگردید دنبال پکیج یا راهنمای مناسب برای دیستروی خودتون، چون دردسر داره و کلی هم طول میکشه. فقط اگه رفتید برای نصب از سورس چند تا نکته رو در نظر داشته باشید :<br />
۱- یه چیزی که برای من عجیبه اینه که دولوپرهاش فراموش کردن فایلهای زاید رو پاک کنن. فایل CMakeCache.txt رو از توی پوشه اصلی پروژه پاک کنید که بودنش دردسر ساز میشه و اصلا اجازه کامپایل به شما نمیده مگه اینکه مستقیما توی opt کامپایل بشه که بازم نمیشه خیلی وقتا!<br />
۲- بعد از اینکه رفتید برای cmake دوباره یه فایل CMakeCahce.txt براتون میسازه. کامپایل رو متوقف کنید، توی اون فایل رو بگردید و هر چی -O3 (منهای O او بزرگ بعدش بلافاصله یه ۳ ) هست رو عوض کنید با -O2 (همون فقط ۲ به جای ۳ ) یه مشکل عجیب وقت لینک کردن دامنگیر من و یه عده دیگه -البته نه همه- شده بود که با این روش حل شد.<br />
۳- توی آرچ لینوکس libmemcached 0.50-1 با این کار نمیکنه (تا تاریخ نوشتن این پست) و مجبورید برگردید به نسخه قبلی، libmemcached-0.49-1 بدون مشکل کار میکنه.</p>
<p><strong>طریقه استفاده </strong><br />
برای استفاده از این برنامه،‌ به لیست فایلهای PHP که قراره تبدیل بشن احتیاج دارید. یادتون باشه هر فایلی که توش PHP هست باید توی این لیست باشه.<br />
برای اجرای هیپ هاپ باید برید توی پوشه خود پروژه و چند تا متغیر رو تنظیم بکنید برای استفاده در شل:</p>
<pre class="brush: bash; title: ; notranslate">
cd .. # into the root of the hphp checkout
export HPHP_HOME=`pwd`
export HPHP_LIB=`pwd`/bin
# if you followed the Ubuntu 9.10 instructions, you also need
export CMAKE_PREFIX_PATH=`/bin/pwd`/../
</pre>
<p>این دستورات باید یک بار قبل از اجرای هیپ هاپ استفاده بشن و گرنه درست کار نمیکنه. شاید یه اسکریپت مثل این :‌</p>
<pre class="brush: bash; title: ; notranslate">
#!/bin/sh
export CMAKE_PREFIX_PATH=/path/to/hiphop/top/folder #Parent folder of hip hop not root folder
export HPHP_HOME=$CMAKE_PREFIX_PATH/hiphop-php
export HPHP_LIB=$HPHP_HOME/bin
$HPHP_HOME/src/hphp/hphp $*
</pre>
<p>-این اسکریپت توی پکیج AUR آرچ لینوکس هستش و من از اونجا کپیش کردم :) -<br />
من یه کد خیلی ساده PHP نوشتم در این حد ساده : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
echo &quot;Hello world!&quot;;
</pre>
<p>و با این دستور تبدیلش کردم : </p>
<pre class="brush: bash; title: ; notranslate">
/opt/hiphop/hphp.sh test.php --keep-tempdir=1 --log=3
</pre>
<p>یه فایل اجرایی بهم تحویل داد به نام program تو یه پوشه داخل tmp به حجم ۲۶ مگا بایت! که با strip کردن شد ۲۱.۳ مگابایت :) و بدون نیاز به هیچگونه فایل اضافی و کاملا مستقل، که هم توی ترمینال درست عمل میکرد (فراخوانی مستقیم بدون هیچگونه سوییچ اضافه) و هم به صورت سرور :</p>
<pre class="brush: bash; title: ; notranslate">
/tmp/hphp_p6vSsP/program -m server -p 8080
</pre>
<p>- نکته نسبتا انحرافی! اینه که روت وب سرور شما میشه جایی که شما هیپ هاپ رو اجرا میکنید. مثلا من اول به جای test.php زدم ~/test.php و بعد برای حالت سرور باید کل مسیر فایل رو هم تو بروزر میزدم.<br />
یه سوییچ -f هم داره که انتظار میره فایل PHP بهش بدی و مستقیم بدون کامپایل &#8211; مثل خود PHP &#8211; اجراش کنه که هر کاری کردم خطا گرفتم، و در کل خود PHP که هست چه مرضیه آخه!<br />
برای اجرای یه پروژه کامل اول یه لیستی از فایلهای PHP اون پروژه ایجاد کنید : </p>
<pre class="brush: bash; title: ; notranslate">
cd /project/root
find . -name &quot;*.php&quot; &gt; files.list
</pre>
<p>و بعد یه دستوری تو این مایه ها : </p>
<pre class="brush: bash; title: ; notranslate">
$HPHP_HOME/src/hphp/hphp --input-list=files.list -k 1 --log=3 \
  --force=1 --cluster-count=50
</pre>
<p>برای کامپایل، البته انتخابهای بیشتری هست خیلی بیشتر. بعد اگه همه چی خوب پیش بره پروژه شما توی tmp ساخته میشه و شما میتونید اونو اجرا کنید : </p>
<pre class="brush: bash; title: ; notranslate">
cd /project/root/
sudo /full/path/to/program -m server -v &quot;Server.SourceRoot=`pwd`&quot; \
  -v &quot;Server.DefaultDocument=index.php&quot; -c $HPHP_HOME/bin/mime.hdf
</pre>
<p>این دستور باید از توی جایی اجرا بشه که فایلهای استاتیک مثل عکس و CSS و &#8230; قرار دارن.<br />
البته انتخابهای بشتری هم هست، ولی اینجا جای توضیحشون نیست،‌در حقیقت این پروژه در حال پیشرفته و ممکنه همه اینها تغییر کنه، بنابراین ویکی خود پروژه انتخاب مناسبتریه برای راهنما :) </p>
<p>من با هیپ هاپ یه چند تایی برنامه رو به صورت کامل کامپایل کردم بدک نبود، ولی برای من فقط تنها استفاده ای که میتونه داشته باشه همون پروژه ای هستش که بالاتر گفتم ولی این چیزی از ارزش این پروژه کم نمیکنه. به هر حال من اصلا از فیسبوک خوشم نمیاد،‌ولی بابت این پروژه ازشون ممنونم!!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/909/hiphop/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/811/zend-translate-and-gnugettext/' rel='bookmark' title='Zend_Translate و GnuGetText'>Zend_Translate و GnuGetText</a> <small>یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم،...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/909/hiphop/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>رشته ها و آرایه ها PHP</title>
		<link>http://cyberrabbits.net/885/string-array-php/</link>
		<comments>http://cyberrabbits.net/885/string-array-php/#comments</comments>
		<pubDate>Fri, 03 Jun 2011 07:40:48 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[array]]></category>
		<category><![CDATA[bug]]></category>
		<category><![CDATA[strings]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=885</guid>
		<description><![CDATA[امروز درگیر یک کد بودم شبیه این : به نظر شما خروجیش چیه؟؟؟ یعنی اصلا چیزی باید چاپ بشه یا نه؟ من معتقدم که نباید چیزی چاپ بشه. یعنی شرط if اصلا درست نیست که بخواد برسه به echo ولی مساله مسخره اینه که isset میگه این ایندکس وجود داره و -برداشت شخصی من اینه&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>امروز درگیر یک کد بودم شبیه این : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	$string = &quot;This is a string&quot;;

	if (isset($string['default']))
		echo $string['default'];
</pre>
<p>به نظر شما خروجیش چیه؟؟؟ یعنی اصلا چیزی باید چاپ بشه یا نه؟ من معتقدم که نباید چیزی چاپ بشه. یعنی شرط if اصلا درست نیست که بخواد برسه به echo ولی مساله مسخره اینه که <a href="http://ir.php.net/manual/en/function.isset.php">isset</a> میگه این ایندکس  وجود داره و  -<strong>برداشت شخصی من اینه که</strong>- PHP وقتی میخواد یه رشته رو باهاش به صورت آرایه ای برخورد کنه، اول ایندکس آرایه رو به صورت عدد درمیاره با اون قواعدی که خودش داره، ( قواعد استاندارد خودش ) بعد اون کاراکتر رو چاپ میکنه.<br />
نتیجه خیلی غیر قابل تصور بود برای من!!!!!<br />
حالا اگه بگم تا ۱ ساعت درگیر این مساله مسخره بودم و این کد : </p>
<pre class="brush: php; title: ; notranslate">
		foreach ($opts as $opt =&gt; $value)
		{
			if (isset($value['default']) )
			{
				$result[$opt] = $value['default'];
			}
			elseif (isset($value['type']))
			{
				$result[$opt] = '';
			}
			else
			{
				$result[$opt] = $value;
			}
		}
</pre>
<p>چقدر منو عذاب داد مطمئن باشید راست میگم و اصلا اغراق نیست!!!!!!</p>
<p><strong>نظر شما چیه؟ این باگه؟ یا همینه که هست؟ باید گزارش بشه یا نه؟؟؟؟ کلا درسته که تبدیل روی ایندکس هم انجام بشه؟</strong><br />
&#8211; خیلی وقت بود ننوشته بودم :)‌چقدر دلم تنگ شده بود!!<br />
&#8211; پی نوشت :‌نمیدونم چطوری اشتباهی :) نظرها رو بسته بودم. الان بازه دیگه &#8230;.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/885/string-array-php/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/885/string-array-php/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Zend_Translate و GnuGetText</title>
		<link>http://cyberrabbits.net/811/zend-translate-and-gnugettext/</link>
		<comments>http://cyberrabbits.net/811/zend-translate-and-gnugettext/#comments</comments>
		<pubDate>Thu, 03 Feb 2011 09:07:37 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[Gnu Get Text]]></category>
		<category><![CDATA[poedit]]></category>
		<category><![CDATA[Zend_Cache]]></category>
		<category><![CDATA[Zend_Translate]]></category>
		<category><![CDATA[Zend_Translate_Adapter_Gettext]]></category>
		<category><![CDATA[zf]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=811</guid>
		<description><![CDATA[یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم، استفاده نکردن هرچه بیشتر!!! از زبان فارسیست. یعنی سعی میکنم تحت هیچ شرایطی توی کد حتی یک کلمه به زبانی غیر از انگلیسی، خصوصا فارسی ننویسم. علت سادست، فارسی میتونه باعث مشکلات زیادی در زمان کد نویسی بشه خصوصا مشکلاتی که معمولا ویرایشگرهایی که من&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/618/new-theme/' rel='bookmark' title='قالب جدید'>قالب جدید</a> <small>همونطور که معلومه، قالب رو عوض کردم. هنوز مشکلاتی داره،...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم، استفاده نکردن هرچه بیشتر!!! از زبان فارسیست. یعنی سعی میکنم تحت هیچ شرایطی توی کد حتی یک کلمه به زبانی غیر از انگلیسی، خصوصا فارسی ننویسم.</p>
<p>علت سادست، فارسی میتونه باعث مشکلات زیادی در زمان کد نویسی بشه خصوصا مشکلاتی که معمولا ویرایشگرهایی که من استفاده میکنم با این زبان دارن.<br />
راه حل سادست، استفاده از سیستمهای مترجم، که من این وسط <a href="http://www.gnu.org/software/gettext/">GnuGetText</a> رو ترجیح میدم. اونم تازه اینکه مربوطه به گنو و منم تا گنو هست هیچ مشابهی رو استفاده نمیکنم، مهم نیست که چه امکاناتی ارایه میکنه!<br />
 توی این پست و پست بعدی قصد دارم در مورد این سیستم ترجمه و چگونگی اتصال اون با Zend Framework بنویسم. اول درمورد اینکه چطور این سیستم رو فعال کنیم و دوم اینکه چطور میشه قابلیتهاشو بیشتر کرد و یه سیستم ترجمه پویا هم ایجاد کرد خواهم نوشت.<br />
اول از همه (معمولا تو bootstrap) باید یه شی ایجاد کنیم :</p>
<pre class="brush: php; title: ; notranslate">
  define('LANGUAGE','fa_IR');
  $trans=new Zend_Translate(&quot;Zend_Translate_Adapter_Gettext&quot;,
                            &quot;/path/to/language/&quot;.LANGUAGE.&quot;.mo&quot;,
                            LANGUAGE);
</pre>
<p>البته بستگی داره چه زبانی مد نظرتون باشه. به ازای هر زبان باید یه فایل با پسوند mo وجود داشته باشه (که مسیرش کاملش رو به عنوان آرگومان دوم به این سازنده فرستادیم.). حالا دو مساله پیش میاد. یکی اینکه چطور این فایل mo رو بسازیم. اینو میذارم برای یه کم پایینتر، مساله فعلا اینه که چطور از این شی که ساخته شده برای ترجمه استفاده کرد. <span id="more-811"></span><br />
این شی trans (این یه عادت شخصیه من اسمشو میذارم trans تو همه پروژه هام اینه که اینجا هم همینکارو انجام میدم) رو بهتره توی Zend_Registry ثبتش کنیم.<br />
مثلا اینطوری : </p>
<pre class="brush: php; title: ; notranslate">
  Zend_Registry::set('trans',$trans);
</pre>
<p>البته مزایایی هست که این متغیر به اسم Zend_Translate ثبت بشه. در صورت ثبت به این اسم،  بعضی از کلاسها مثل Zend_Form به صورت اتوماتیک استفاده میکنن ازش برای ترجمه فیلدها. ولی من ترجیح میدم به جای اینکه به Zend_Form اجازه بدم خودش به صورت اتوماتیک فیلدها رو ترجمه کنه خودم اونها رو ترجمه کنم و به Zend_Form بفرستم. (علتشو پایینتر میگم)<br />
حالا هرجا بخوایم چیزی رو بفرستیم به خروجی (یا اصولا هر بار خواستیم جمله ای ترجمه داشته باشه) اینطوری عمل میکنیم : </p>
<pre class="brush: php; title: ; notranslate">
  $trans=Zend_Registry('trans');

  echo $trans-&gt;_(&quot;This is untranslated text.&quot;);
</pre>
<p>یعنی به راحتی به جای اینکه متن رو بفرستیم به خروجی، اون رو از این فیلتر رد کنیم.<br />
تابع translate هم وجود داره که تابع زیرخط در حقیقت اسم دیگه ای برای این تابع اصلی هست ولی استفاده از تابع زیرخط چون پارسر GetText اون رو به صورت Out of the box میشناسه، به نظر من بهتره.</p>
<p> سعی کنید تا ممکنه اینجوری اینکار رو انجام ندید : </p>
<pre class="brush: php; title: ; notranslate">
  $trans=Zend_Registry('trans');
  $msg=  &quot;This is untranslated text.&quot;;
  echo $trans-&gt;_($msg);
</pre>
<p>البته هیچ مشکلی وجود نداره از لحاظ برنامه نویسی، مشکل بیشتر مربوط میشه به پارسر GetText .<br />
پارسر GetText معمولا میگرده دنبال تابع _ (زیرخط) و متنهای داخل اون رو برای ترجمه بیرون میکشه. به این ترتیب لزومی نداره که خودتون دستی متون رو به فایل ترجمه اضافه کنید.<br />
دقیقا به همین دلیله که من دوست ندارم Zend_Form (یا هر کلاس دیگری) به صورت اتوماتیک از این شی استفاده کنه. چون در اون صورت مزایای پارسر GetText رو از دست میدم. حالا این پارسر GetText چی هست؟</p>
<p>برای این کار، برنامه <a href="http://www.poedit.net/">PoEdit</a> رو لازم دارید که نصب کنید. تقریبا در تمام دیستروهای لینوکس، این برنامه توی مخازن اصلی هست و به راحتی قابل نصبه. برای ویندوز هم به راحتی از سایت اصلیش قابل <a href="http://www.poedit.net/download.php">دانلود</a> هست.<br />
متاسفانه به خاطر اینکه Zend_Framework از پسوندهای مختلفی استفاده میکنه، باید یه کم تغییر توی تنظیمات PoEdit داده بشه.  تنظیمات PoEdit رو از طریق منوی Edit->Prefrence انتخب کنید. توی تب Parsers  از لیست زبانهای برنامه نویسی، PHP رو انتخاب کنید و دکمه Edit رو فشار بدید و تنظیمات رو مثل شکل زیر تغییر بدید:<br />
<div id="attachment_812" class="wp-caption aligncenter" style="width: 585px"><a href="http://cyberrabbits.net/wp-content/uploads/2011/02/snapshot3.png"><img src="http://cyberrabbits.net/wp-content/uploads/2011/02/snapshot3.png" alt="تنظیمات PoEdit برای شناختن پسوندهای مختلف Zend" title="snapshot3" width="575" height="726" class="size-full wp-image-812" /></a><p class="wp-caption-text">تنظیمات PoEdit برای شناختن پسوندهای مختلف Zend</p></div><br />
یک کاتالوگ جدید ایجاد کنید.(تنظیماتش مشخصه، اسم و ایمیل و زبان و کشور رو انتخاب کنید کافیه. برای هر زبان یک کاتالوگ باید ایجاد بشه) بعد از ایجاد کاتالوگ اونو توی یک پوشه مناسب، همون جایی که باید فایل mo قرار بگیره و بالاتر توی ایجاد Zend_Translate به عنوان آرگومان دوم نوشتید ذخیره کنید. این فایل پسوندش po هست.</p>
<p>گام بعدی انتخاب فایلهای پروژه برای پارسر هست. وقتی کاتالوگ باز هست، از منوی کاتالوگ گزینه Settings رو انتخاب کنید و توی دیالوگی که مثل این هست، هم میتونید اسم مترجم و چیزهای دیگه رو بنویسید و هم اینکه توی تب Paths میتونید مسیرهایی که فایلهای پروژه شما توش قرار داره انتخاب کنید.میتونید از مسیرهای مطلق استفاده کنید یا اینکه به صورت نسبی مسیر بدید که اون انتخاب شماست مثلا :<br />
<div id="attachment_813" class="wp-caption aligncenter" style="width: 606px"><a href="http://cyberrabbits.net/wp-content/uploads/2011/02/snapshot4.png"><img src="http://cyberrabbits.net/wp-content/uploads/2011/02/snapshot4.png" alt="تنظیمات کاتالوگ" title="snapshot4" width="596" height="486" class="size-full wp-image-813" /></a><p class="wp-caption-text">تنظیمات کاتالوگ</p></div><br />
همین. (اگه پروژه ای دارید که قبلا نوشتید و یا اینکه به هر دلیلی نمیخواید از تابع زیرخط استفاده کنید، از تب Keywords میتونید تابعی رو که به عنوان مترجم استفاده کردید رو اضافه کنید تا GetText اونها رو هم به رسمیت بشناسه.)<br />
اگه قبلا پروژه رو ساختید و کارهایی که بالاتر گفتم انجام داده باشید، با انتخاب گزینه Update from sources توی منوی Catalogs یا با دکمه معادلش توی Toolbar خود برنامه اون مسیرهایی رو که انتخاب کردید رو میگرده و تمام جملات رو استخراج میکنه. جملات رو توی PoEdit ترجمه کنید، با فشردن دکمه Save فایل mo هم ساخته میشه. هر از چندگاهی با تغییر توی پروژه میتونید دوباره دکمه Update رو بزنید. خودش جملاتی که حذف شدن رو حذف میکنه، جملاتی رو که اضافه شدن اضافه میکنه.<br />
البته حذف شده ها همچنان توی کاتالوگ po هستن، ولی داخل فایل mo نمیان. برای آپلود فایل، دیگه فایل po لازم نیست و فایل mo کفایت میکنه.<br />
این روش مزایایی داره نسبت به روشهای دیگه که مهمترینشون همین پارسر اتوماتیکه که دیگه لازم نیست خودتون برای اضافه کردن هر جمله کاری رو انجام بدید. جمله اصلی که به زبان انگلیسی هست در صورتی که جمله ترجمه وجود ندارشته باشه نمایش داده میشه. کد کاملا دست نخورده میمونه و سرعتشم خوبه.<br />
البته چند تا نکته کوچیک رو بد نیست رعایت کنید یکی اینکه ترجمه ها رو Cache کنید که اینکار هم به راحتی توسط Zend_Translate پشتیبانی میشه. برای اینکار : </p>
<pre class="brush: php; title: ; notranslate">
  $cache = Zend_Cache::factory('Core',
	                             'File',
		                           $frontendOptions,
		                           $backendOptions);
  Zend_Translate::setCache($cache);
</pre>
<p>البته ایجاد Cache یکی از اون مواردیه که گاهی فکر میکنم دربارش بنویسم بد نیست :)  خط اول کد بالا همین جوری کار نمیکنه :) باید خودتون $cache رو ایجاد کنید.(شک نکنید! Cache توی Zend یکی از واجباته!)<br />
دفعه بعد درباره ایجاد یک آداپتور ساده صحبت میکنم که یه کم قابلیتهای این آداپتور اصلی رو بهتر میکنه.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/811/zend-translate-and-gnugettext/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/618/new-theme/' rel='bookmark' title='قالب جدید'>قالب جدید</a> <small>همونطور که معلومه، قالب رو عوض کردم. هنوز مشکلاتی داره،...</small></li>
<li><a href='http://cyberrabbits.net/372/mail-for-zend-framework/' rel='bookmark' title='ارسال mail از طریق Zend Framework'>ارسال mail از طریق Zend Framework</a> <small>لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/811/zend-translate-and-gnugettext/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>بازم PHP و نکات کوچیکش!</title>
		<link>http://cyberrabbits.net/750/php-and-simple-notes/</link>
		<comments>http://cyberrabbits.net/750/php-and-simple-notes/#comments</comments>
		<pubDate>Thu, 23 Dec 2010 19:32:19 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=750</guid>
		<description><![CDATA[یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح کرده بودم‌ (بنا به خواست یه کارفرما). چند تا سوال در زمینه PHP هم پرسیده بودم، یکیش همون قضیه اولویت عملگرها بود و دقیقا کدی که اینجا هست رو پرسیده بودم خروجیش چی میشه، که هیچ کس درست جواب نداده بود. (برام زیاد دور از&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/420/another-personal-note/' rel='bookmark' title='بازم یادداشت شخصی'>بازم یادداشت شخصی</a> <small>از اونجایی که بارها گفتم این یه وبلاگ شخصیه که...</small></li>
<li><a href='http://cyberrabbits.net/885/string-array-php/' rel='bookmark' title='رشته ها و آرایه ها PHP'>رشته ها و آرایه ها PHP</a> <small>امروز درگیر یک کد بودم شبیه این : به نظر...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح کرده بودم‌ (بنا به خواست یه کارفرما). چند تا سوال در زمینه PHP هم پرسیده بودم، یکیش همون قضیه اولویت عملگرها بود و <a href="http://cyberrabbits.net/564/a-small-tip-in-php/">دقیقا کدی که اینجا هست</a> رو پرسیده بودم خروجیش چی میشه، که هیچ کس درست جواب نداده بود. (برام زیاد دور از انتظار نبود.) یکی هم این <a href="http://cyberrabbits.net/585/php-simple-note/">قضیه اپراتورهای Identical بود</a> که چند نفر درست جواب دادن ولی خیلی کم.<br />
سوال دیگه ای هم که هیچ کس جواب نداده بود‌ ( و یه کم هم عجیب بود برام! این دیگه سخت نیست که!! )‌ این بود که خروجی کد زیر چیه :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
  $x=0123;
  if ($x==123)
    echo &quot;Equal&quot;;
  else
    echo &quot;Not equal&quot;;
</pre>
<p>جالبه که حتی یه نفر از اینها (که چند تاییشون در حدی مدعی بودن که من با خودم گفتم دیگه باید برم غاز بچرونم!) این سوال رو جواب ندادن!<br />
<span id="more-750"></span><br />
&#8211; خوب بابت تکمیل پست این دو تا با هم برابر نیستن! تو PHP ، عدد صحیحی که یه صفر در آغاز داره میشه مبنای هشت. یعنی عدد 0123 میشه 83<br />
&#8211; یه نکته دیگه اینکه اگر مثلا بنویسیم 012394 چون 9 در اعداد مبنای 8 وجود نداره، PHP خیلی ساده از اون به بعدو حذفش میکنه و میشه همون 0123 .</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/750/php-and-simple-notes/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/420/another-personal-note/' rel='bookmark' title='بازم یادداشت شخصی'>بازم یادداشت شخصی</a> <small>از اونجایی که بارها گفتم این یه وبلاگ شخصیه که...</small></li>
<li><a href='http://cyberrabbits.net/885/string-array-php/' rel='bookmark' title='رشته ها و آرایه ها PHP'>رشته ها و آرایه ها PHP</a> <small>امروز درگیر یک کد بودم شبیه این : به نظر...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/750/php-and-simple-notes/feed/</wfw:commentRss>
		<slash:comments>12</slash:comments>
		</item>
		<item>
		<title>عبارات با قاعده در PHP – بخش دوم</title>
		<link>http://cyberrabbits.net/740/regular-expression-part%db%b2/</link>
		<comments>http://cyberrabbits.net/740/regular-expression-part%db%b2/#comments</comments>
		<pubDate>Mon, 20 Dec 2010 21:01:56 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[عبارات با قاعده]]></category>
		<category><![CDATA[Regular Expression]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=740</guid>
		<description><![CDATA[توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا میخوام ادامش بدم. کاراکتر نقطه &#8220;.&#8221; فرض کنید میخواید تمام کلماتی که در ابتداشون h داره و در انتهاشون llo رو پیدا کنید. مثلا hello ، یا hallo یا hollo یا هر ترکیب دیگری. اینطوری میتونید از کاراکتر نقطه استفاده کنید. به عبارتی کاراکتر نقطه&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/733/regular-expression-part1/' rel='bookmark' title='عبارات با قاعده در PHP – بخش اول'>عبارات با قاعده در PHP – بخش اول</a> <small>عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر...</small></li>
<li><a href='http://cyberrabbits.net/236/php-tricks-1/' rel='bookmark' title='فوت کوزه گری!'>فوت کوزه گری!</a> <small>گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>توی <a href="http://cyberrabbits.net/733/regular-expression-part1/">نوشته قبلی</a>، درباره عبارات با قاعده نوشتم، و حالا میخوام ادامش بدم. </p>
<p><strong>کاراکتر نقطه &#8220;.&#8221;</strong><br />
فرض کنید میخواید تمام کلماتی که در ابتداشون h داره و در انتهاشون llo رو پیدا کنید. مثلا hello ، یا hallo یا hollo یا هر ترکیب دیگری. اینطوری میتونید از کاراکتر نقطه استفاده کنید.<br />
به عبارتی کاراکتر نقطه میگه هر کاراکتری باشه مهم نیست. مثلا برای این مثالی که گفتم :</p>
<pre class="brush: plain; title: ; notranslate">
/h.llo/
</pre>
<p> بعد از h هر کاراکتری باشه، پذیرفته میشه.<br />
<strong>آکولاد باز و بسته &#8220;{}&#8221; </strong><br />
درباره * گفتم برای اینکه صفر یا چند کاراکتر رو شامل بشه استفاده میشه (صفر یا تعداد نامحدود) برای + هم گفتم برای انتخاب ۱ یا هر چند تا که باشه مفیده، علامت سوال هم برای یک یا هیچی. منتها گاهی لازم میشه مثلا یه الگو دست کم سه بار تکرار بشه، حداکثر ۷ بار. خوب این موقع چی؟<br />
اگه بخواید یه الگو دقیقا یه تعداد مشخصی تکرار بشه میتونید به این صورت بنویسید : </p>
<pre class="brush: plain; title: ; notranslate">
/hel{2}o/
</pre>
<p>یعنی یک he بعدش بلافاصله دو تا l (دقیقا دو تا ال انگلیسی) و بعد یه o. یعنی همون hello.<br />
اینجوری هم میشه نوشت : </p>
<pre class="brush: plain; title: ; notranslate">
/wel{1,2}come/
</pre>
<p>این یه مشکل بزرگ رو حل میکنه. اینجوری اول یه we اگه باشه، بعد دست کم یکی یا حداکثر دو تا l (حرف ال انگلیسی) و بعد come این الگو درست در میاد. خوب خیلی خوبه :)‌ هر روایتی از کلمه خوش آمدید رو میشه بررسی کرد!<br />
اگه بخوایم مینیمم رو مشخص کنیم ولی ماکزیمم مشخص نشه، میتونیم بنویسیم : </p>
<pre class="brush: plain; title: ; notranslate">
/wel{2,}come/
</pre>
<p>یعنی l باید دو بار دست کم تکرار شده باشه حالا اگه هزار بار هم تکرار شده باشه مشکلی نیست.</p>
<p>حالا برای نمونه :‌<br />
<span id="more-740"></span></p>
<pre class="brush: plain; title: ; notranslate">
/^.{5}$/
</pre>
<p>یعنی کلمه ای دقیقا پنج حرفی تشکیل شده از هر کاراکتری. یا </p>
<pre class="brush: plain; title: ; notranslate">
/^.{5,7}$/
</pre>
<p>یعنی یک رشته دست کم ۵ حرفی و دست بالا، ۷ حرفی مثلا hello یا rabbits یا rabbit</p>
<p><strong>گروه کاراکتری -براکت باز و بسته &#8220;[]&#8220;</strong><br />
خوب اینا همه به کنار. من اگه بخوام یه سری کاراکتر خاص انتخاب کنم، چه کاری باید انجام بدم. مثلا میخوام اعداد مبنای ۱۶ به سبک PHP رو شناسایی کنم. یعنی یه الگو بنویسم که این اعداد رو شناسایی کنه.<br />
این اعداد با صفر شروع میشن،‌بعد یه x و بعد کاراکترهای صفر تا نه، و A تا F . اینجوریه  که نیاز به یک گروه کاراکتری احساس میشه. یه چیزی که بتونه یه کاراکتر رو از یه لیست انتخاب کنه.<br />
خوب ببینید :</p>
<pre class="brush: plain; title: ; notranslate">
/0x[0-9A-Fa-f]+/
</pre>
<p>که یعنی یه صفر بعدش یه ایکس و بعدش اونچیزی که تو براکت قرار داره بین ۱ تا هر چند بار(علامت + در آخر رو دقت کنید.).<br />
خوب اونی که توی براکت هست یعنی چی؟ یعنی صفر تا نُه، A تا F و a تا f . (حروف کوچک و بزرگ در عبارات با قاعده متفاوت هستن مگر در شرایطی که از کاراکتر تغییر دهنده i استفاده بشه که آخرای این پست یه چیزهایی مینویسم دربارش.)یعنی، این براکت برای انتخاب یه کاراکتر (دقیقا یکی) از لیستی که ارایه شده استفاده میشه.<br />
یک لیست رو مثلا میشه اینطوری نوشت : </p>
<pre class="brush: plain; title: ; notranslate">
/[0123456789]/
</pre>
<p>یعنی اعداد صفر تا نه. به عبارتی میشه تک تک کاراکترهای مورد نظر رو ذکر کرد، که یه کمی طولانی میشه و فقط در یه سری موارد که این کاراکترهانظم ندارن مفیده. همین مثال نظم داره، به جای نوشتن صفر تا نُه، میشه نوشت :</p>
<pre class="brush: plain; title: ; notranslate">
/[0-9]/
</pre>
<p>پس در عمل کاراکتر &#8211; (hyphen ، dash ، منها) به عنوان رنج استفاده میشه.البته میشه مثل همون مثال بالاتر که نوشتم برای اعداد مبنای شانزده چند تا رنج رو پشت سر هم بنویسید، که بازم درسته.<br />
اینجا میشه معنی رو برعکس کرد. برفرض مثال شما میخواید یه جا هر کاراکتری باشه، غیر از صفر تا نُه. علامت ^ رو تو نوشته قبلی توضیح دادم. این علامت اگه داخل گروه کاراکتری بیاد، معنی ای که قبلا داشت رو نمیده. اینجوری اگه در ابتدای یک گروه کاراکتری بیاد، یعنی بعد از علامت براکت باز ] اونوقت به صورت نقیض عمل میکنه :</p>
<pre class="brush: plain; title: ; notranslate">
/[^0-9]/
</pre>
<p>یعنی هر کاراکتری باشه درسته فقط صفر تا نُه نباشه. دقیقا برعکس بالایی.<br />
نکته مهم اینه که براکت به معنی یه حرف هست. نه چند تا. برای اینکه چند تا بشه از ستاره، علامت سوال، بعلاوه و &#8230; استفاده میشه که بالاتر توضیح دادم. </p>
<p>تا همینجا اون الگویی رو که توی پست قبلی برای ایمیل گفتم میتونیم بررسی کنیم. </p>
<pre class="brush: plain; title: ; notranslate">
^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$
</pre>
<p>خوب این الگو رو حالا میشه معنی کرد.<br />
<div id="attachment_742" class="wp-caption aligncenter" style="width: 607px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/12/regex.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/12/regex.png" alt="یک الگوی نسبتا ساده برای بررسی آدرس ایمیل" title="یک الگوی نسبتا ساده برای بررسی آدرس ایمیل" width="597" height="109" class="size-full wp-image-742" /></a><p class="wp-caption-text">یک الگوی نسبتا ساده برای بررسی آدرس ایمیل</p></div></p>
<p><strong>۱-</strong> این یعنی آغاز خط و بلافاصله بعد از اون الگوی ۲ قرار گرفته باشه.<br />
<strong>۲-</strong> در الگوی ۲ یک گروه کاراکتری متشکل از a تا z کوچک و بزرگ، صفر تا نُه، نقطه، زیرخط و علامت منها قرار گرفته. بلافاصله بعد از این گروه، یک علامت بعلاوه هست که یعنی از این گروه کاراکتری دست کم یکی و دست بالا! هر چند تا که باشه درسته. به عبارتی این میشه آدرس ایمیل قبل از @ مثلا در someone@example.com میشه someone .<br />
<strong>۳-</strong> علامت @ معنی خاصی نداره و میشه خودش. تا به اینجا شد، ابتدای خط، بلافاصله بعدش یک تا هر چند تا کاراکتر (الگوی ۲) و بعد بلافاصله یک علامت @.<br />
<strong>۴-</strong> بعد یه گروه کاراکتری دیگه.اینبار یه کم تفاوت داره با الگوی اسم. چون این مربوط میشه به اسم دومین. توی someone@example.com این میشه قسمت example یعنی بعد از @ و قبل از نقطه. اینبار دیگه نقطه و زیرخط نیستن چون این دو تا کاراکتر توی اسم مجازن، اما تو اسم دومین نه. بلافاصله بعدش هم یه علامت + (بعلاوه) اومده که یعنی الگوی کاراکتری یک یا هر چند بار میتونه تکرار بشه.<br />
<strong>۵-</strong> اما نقطه، چون نقطه توی عبارت باقاعده معنی داره،‌ولی ما اینجا منظورمون دقیقا خود نقطه هست نه معنیش، بالاجبار باید قبلش یه بک اسلش \ بگذاریم که نقطه تعبیر نشه به هر کاراکتری، و معنی فقط نقطه اعمال بشه.<br />
<strong>۶-</strong>بعد از نقطه پسوند میاد مثلا  کام (com) یا آی آر (ir) یا گاهی کو دات آی آر (co.ir) کوتاهترین پسوند دو حرفیه مثل ir و بلندترین پنج حرفی مثل co.ir ، به همین دلیل نوشته شده {2,5} .تشکیل هم شده از حروف انگلیسی و نقطه. که اگه دقت کنید نقطه ای که داخل گروه کاراکتری قرار گرفته بک اسلش نداره، چون اونجا دیگه معنی همه کاراکترها رو نمیده و معنی خودش رو میده.(بعضی کاراکترها داخل گروه کاراکتری معنیشون فرق میکنه مثل همین نقطه یا ^ )<br />
<strong>۷-</strong> بعدشم که آخر خط. یعنی بعد از این الگوها باید برسیم به آخر خط و ادامه نداشته باشه.</p>
<p><strong>تغییر دهنده های الگو</strong><br />
بالاتر گفتم که (به طور مثال) کاراکترها در عبارات با قاعده به حروف کوچک و بزرگ حساس هستند. اما روش PCRE ، امکان یک سری تغییرات در روش رو میده. با اضافه کردن یه کاراکتر (یا چند تا) در انتهای الگوی مورد جستجو میشه یه سری تغییرات به وجود آورد.<br />
مثلا کاراکتر i، باعث میشه که الگو دیگه به حروف کوچک و بزرگ حساس نباشه : </p>
<pre class="brush: plain; title: ; notranslate">
/[a-z0-9]*/i
</pre>
<p>تو این عبارت، اون کاراکتر i بعد از اسلش دومی منظور منه. این کاراکترها که بعد از اسلش دوم قرار میگیرن جزئی از الگو نیستن، فقط طریقه تعبیر الگو رو تغییر میدن. مثل همین کاراکتر i که اینجا هست.<br />
البته این سری قصد توضیح کامل اینها رو ندارم، و میمونه برای پست های بعدتر! البته امیدوارم که وقتشو پیدا کنم!<br />
دفعه دیگه یه کم بیشتر درباره الگوها تو PHP صحبت میکنم و کاربردهای مختلف رو بررسی میکنم. </p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/740/regular-expression-part%db%b2/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/733/regular-expression-part1/' rel='bookmark' title='عبارات با قاعده در PHP – بخش اول'>عبارات با قاعده در PHP – بخش اول</a> <small>عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر...</small></li>
<li><a href='http://cyberrabbits.net/236/php-tricks-1/' rel='bookmark' title='فوت کوزه گری!'>فوت کوزه گری!</a> <small>گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/740/regular-expression-part%db%b2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>عبارات با قاعده در PHP – بخش اول</title>
		<link>http://cyberrabbits.net/733/regular-expression-part1/</link>
		<comments>http://cyberrabbits.net/733/regular-expression-part1/#comments</comments>
		<pubDate>Thu, 16 Dec 2010 08:21:23 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[عبارات با قاعده]]></category>
		<category><![CDATA[Regular Expression]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=733</guid>
		<description><![CDATA[عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر خودم هم بود. تا اینکه یه کتاب خوندم دربارش و تازه پی بردم به قدرتش. (این کتاب مورد نظره : Mastering Regular Expressions نوشته Jeffrey E. F. Friedl ، منتها من نسخه ای رو خوندم که غیر قانونی دانلود شده بود و به اشتراک گذاشتن این&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
<li><a href='http://cyberrabbits.net/236/php-tricks-1/' rel='bookmark' title='فوت کوزه گری!'>فوت کوزه گری!</a> <small>گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>عبارات با قاعده، به نظر خیلی ها ترسناکه. به نظر خودم هم بود. تا اینکه یه کتاب خوندم دربارش و تازه پی بردم به قدرتش. (این کتاب مورد نظره : Mastering Regular Expressions نوشته Jeffrey E. F. Friedl ، منتها من نسخه ای رو خوندم که غیر قانونی دانلود شده بود و به اشتراک گذاشتن این نسخه خیلی خیلی بدتره از خوندنش!! خودتون میتونید تو وب پیداش کنید)<br />
حالا این سری من میخوام چند تا از مهمترینهاشون رو توضیح بدم، تا هرجا که اعصابم اجازه بده!!(یا به عبارتی من قصد ندارم اینجا راهنمای کامل بنویسم. فقط یه راهنمایی مختصر!!)</p>
<p>توی PHP ما دو جور عبارت با قاعده داریم. یکی عبارات با قاعده <a href="http://www.php.net/manual/en/ref.regex.php">POSIX Extended</a> ، ودومی عبارات با قاعده <a href="http://www.php.net/manual/en/ref.pcre.php">Perl Compatible</a>.<br />
برای اولی تمام توابع با eregi و ereg شروع میشه. مثلا <a href="http://php.net/manual/en/function.eregi-replace.php">eregi_replace</a> ولی برای دومی (Perl) این توابع با preg شروع میشن، مثل <a href="http://php.net/manual/en/function.preg-replace.php">preg_replace </a>. البته این دو تا یه سری تفاوتها دارن، من همیشه از دومی استفاده میکنم چون سریعتر و کاملتر هستن. یه تفاوت مهم اینه که توی دومی، عبارات با قاعده داخل دو تا / (علامت اسلش) قرار میگیرن. </p>
<p>خوب یه مثال : </p>
<pre class="brush: plain; title: ; notranslate">
^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$
</pre>
<p>خوب این معمولا الگویی هستش که برای شناسایی آدرس ایمیل استفاده میشه. خوب این یعنی چی؟ این مثال هم طریقه استفاده ازش با کمک php : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
   $pattern = &quot;/^[a-zA-Z0-9._-]+@[a-zA-Z0-9-]+\.[a-zA-Z.]{2,5}$/&quot;;
   $email = &quot;mine@example.com&quot;;

   if (preg_match($pattern,$email))
      echo &quot;Match&quot;;
   else
      echo &quot;Not match&quot;;
</pre>
<p>خوب قضیه سادست. من از توابع مربوط به PCRE (یا همون Perl Compatible) استفاده کردم، پس ببینید که دو طرفش دو تا اسلش گذاشتم. البته همین دو تا اسلش رو بردارید و از تابع eregi استفاده کنید به جای این تابع preg_match باز هم جواب میده. خوب حالا توضیح مختصری درباره این عبارات داخل یه عبارت با قاعده.<br />
خوب اول از همه، وقتی شما دنبال یه کلمه خاص میگردید، مثلا کلمه hello داخل یه جمله، الگوی با قاعده شما میشه همون کلمه. اینو میشه با خیلی توابع رشته ای هم انجام داد. مثلا :<br />
<span id="more-733"></span></p>
<pre class="brush: php; title: ; notranslate">
  //We search for this (just hello not /hello/ two / is required for the function!):
  $pattern=&quot;/hello/&quot;;
  //In this string :
  $subject=&quot;hello world!&quot;;
  if (preg_match($pattern,$subject))
     echo &quot;Match&quot;; //&lt;==Answer
  else
     echo &quot;Not match&quot;;
</pre>
<p>دقت کنید من همیشه دو طرف الگو (که همیشه اسمشو میذارم pattern ) یه جفت اسلش گذاشتم، این به خاطر روش پرل هست نه جزیی از رشته.</p>
<p><strong> کاراکتر بک اسلش &#8220;\&#8221;</strong><br />
قبل از هر کاراکتر دیگه ای، این کاراکتر باید معرفی بشه. این کاراکتر برای اینه که کلیه کاراکترهای دیگه رو بی معنی کنه. مثلا کاراکتر ^ رو پایینتر معرفی میکنم. اگه توی الگو بیاد، معنی خاصی داره. حالا اگه ما بخوایم خود کاراکتر رو بنویسیم نه معنی خاصش رو چی؟ کافیه قبل از کاراکتر یه بک اسلش بذاریم.<br />
<strong>کاراکتر &#8220;^&#8221; </strong><br />
این کاراکتر، یعنی آغاز خط. مثلا شما میخواید چک کنید و ببینید که یه جمله با  hello شروع شده یا نه؟مثلا :</p>
<pre class="brush: php; title: ; notranslate">
  //We search for this :
  $pattern=&quot;/^hello/&quot;;
  //In this string :
  $subject=&quot;hello world!&quot;;
  if (preg_match($pattern,$subject))
     echo &quot;Match&quot;; //&lt;==Answer
  else
     echo &quot;Not match&quot;;
</pre>
<p>به عبارتی، همون الگوی قبلی هستش، فقط و فقط اینکه ما همون الگو رو میخوایم منتها باید در ابتدای جمله باشه و نه هر جایی از جمله. مثلا اگه subject باشه world hello دیگه این الگو باهاش مچ نمیشه.<br />
<strong>کاراکتر &#8220;$&#8221;</strong><br />
این دقیقا برعکس قبلیه هستش. یعنی پایان خط. به طور مثال :  </p>
<pre class="brush: php; title: ; notranslate">
  //We search for this :
  $pattern=&quot;/hello$/&quot;;
  //In this string :
  $subject=&quot;hello world!&quot;;
  if (preg_match($pattern,$subject))
     echo &quot;Match&quot;;
  else
     echo &quot;Not match&quot;; //&lt;==Answer
</pre>
<p>یعنی این جایی مچ میشه که اول، کلمه hello بیاد و بعد بلافاصله جمله تموم بشه.مثلا اگه subject باشه world hello مثال بالایی درست میشه.<br />
حالا اگه ما جمله ای رو بخوایم که فقط و فقط یه کلمه hello توش باشه الگوش میشه این : </p>
<pre class="brush: plain; title: ; notranslate">
/^hello$/
</pre>
<p><strong>کاراکتر نقطه &#8220;.&#8221;</strong><br />
این کاراکتر به معنی همه کاراکترها میشه.البته به جز کاراکتر خط جدید. مثلا میخواید الگویی رو پیدا کنید که مثلا با he شروع شده باشه و با lo تموم. میشه اینطوری الگو رو نوشت : </p>
<pre class="brush: plain; title: ; notranslate">
/he.lo/
</pre>
<p>خوب این الگو، کلماتی رو پیدا میکنه مثل hello &#8211; he78686hhjulo یا حتی he lo ولی نه helo .<br />
<strong>کاراکتر پایپ &#8220;|&#8221;</strong><br />
این کاراکتر، یه جورایی به معنی یا میشه. مثلا اگه شما دنبال hello میگردید یا hallo میتونید بنویسید : </p>
<pre class="brush: plain; title: ; notranslate">
/hello|hallo/
</pre>
<p><strong>پرانتز باز و پرانتز بسته &#8220;()&#8221;</strong><br />
مثال بالایی رو ببینید. همون hello و hallo ما میتونیم الگو رو یه کم تغییر بدیم. ما کلماتی رو میخوایم که با h شروع شدن، حرف دومشون e هست یا a و بعد در انتها llo . خوب ما میتونیم از زیر الگو استفاده کنیم. یعنی الگویی که داخل الگوی دیگه قرار میگیره. دو طرف این زیر الگو پرانتز قرار میگیره : </p>
<pre class="brush: plain; title: ; notranslate">
/h(e|a)llo
</pre>
<p>اینطور میتونید چند تا الگو رو با هم ترکیب کنید. زیر الگوها تو تابع preg_match_all خیلی مهمتر میشن :)<br />
<strong>کاراکتر ستاره &#8220;*&#8221;</strong><br />
گاهی من مثلا مینویسم hellllllo (خیلی خوشحال که باشم از دیدن کسی ممکنه همچین چیزی هم بنویسم!) و میخوام بگردم دنبال کلیه hello ها، منتها اینکه نمیدونم چند تا l ممکنه باشه. یکی ، دو تا ، ۱۰ تا یا اصلا هیچی (لابد هیچی l یعنی اینکه اصلا از دیدنت خوشحال نشدم !!) میتونم از * استفاده کنم. برای این که گفتم :</p>
<pre class="brush: plain; title: ; notranslate">
/hel*o/
</pre>
<p>نکته مهم اینه که میشه از این * بعد از پرانتز بسته قرار بگیره. اینجوری از اون زیر الگو هر چند تا که داشته باشیم پشت سر هم قبوله به طور مثال : </p>
<pre class="brush: plain; title: ; notranslate">
/h(e|a)*llo
</pre>
<p>میشه یه h بعد از اون هر چند تا a یا e که باشه مهم نیست (حتی اگه هیچی باشه) و بعد از اونها llo . خوب برای مثال : heeaeaeallo درسته. hllo هم درسته.<br />
<strong>کاراکتر بعلاوه &#8220;+&#8221;</strong><br />
خوب این دقیقا شبیه * عمل میکنه، منتها دیگه هیچی رو قبول نمیکنه. یعنی تکرار بیشتر از یکی. برای مثال : </p>
<pre class="brush: plain; title: ; notranslate">
/h(e|a)+llo
</pre>
<p> در این حالت hllo قبول نیست ولی در حالتی که از ستاره استفاده کرده بودم، قبول بود.<br />
<strong>کاراکتر علامت سوال &#8220;?&#8221;</strong><br />
این کاراکتر یعنی از کاراکتر یا زیر الگوی قبلی صفر و یا یک بار تکرار شده باشه. مثلا </p>
<pre class="brush: plain; title: ; notranslate">
/h(e|a)?llo
</pre>
<p>یعنی اول یه h بعد دقیقا هیچی یا فقط یکی a یا e و بعدش llo . مثلا hello یا hllo یا hallo ولی نه heello یا heallo  .</p>
<p>باقیش بمونه برای پست بعدی فعلا حوصله ندارم!!!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/733/regular-expression-part1/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
<li><a href='http://cyberrabbits.net/236/php-tricks-1/' rel='bookmark' title='فوت کوزه گری!'>فوت کوزه گری!</a> <small>گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/733/regular-expression-part1/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>echo vs. print</title>
		<link>http://cyberrabbits.net/708/echo-vs-print/</link>
		<comments>http://cyberrabbits.net/708/echo-vs-print/#comments</comments>
		<pubDate>Wed, 08 Dec 2010 09:31:27 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[echo]]></category>
		<category><![CDATA[print]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=708</guid>
		<description><![CDATA[حتما دو تا تابع echo و print رو میشناسید. این دو تا عملا یه کار رو انجام میدن. پس چرا شدن دو تا تابع؟ تفاوتشون چیه؟؟ خوب من سعی میکنم که توضیح بدم این قضیه رو. شباهتها هر دو تابع نیستن :) جزئی از ساختار زبانی هستن. یعنی میشه بدون پرانتز فراخوانیشون کرد ولی استفاده&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>حتما دو تا تابع  <a href="http://ir.php.net/manual/en/function.echo.php">echo</a> و <a href="http://ir.php.net/manual/en/function.print.php">print</a> رو میشناسید. این دو تا عملا یه کار رو انجام میدن. پس چرا شدن دو تا تابع؟ تفاوتشون چیه؟؟<br />
خوب من سعی میکنم که توضیح بدم این قضیه رو.<br />
<strong>شباهتها </strong><br />
هر دو تابع نیستن :) جزئی از ساختار زبانی هستن. یعنی میشه بدون پرانتز فراخوانیشون کرد ولی استفاده از توابعی مثل <a href="http://php.net/manual/en/function.call-user-func.php">call_user_func</a> و <a href="http://ir.php.net/manual/en/function.call-user-func-array.php">call_user_func_array</a> براشون جواب نمیده (البته به صورت مستقیم، وگرنه اگه تو یه تابع باشن و اون یه تابع فراخوانی بشه بصورت غیر مستقیم میشه.).<br />
پس :</p>
<pre class="brush: php; title: ; notranslate">
//Call them like functions:
print (&quot;This is a test&lt;br /&gt;&quot;);
echo (&quot;This is a test&lt;br /&gt;&quot;);

// No parenthesis:
print &quot;This is a test&lt;br /&gt;&quot;;
echo &quot;This is a test&lt;br /&gt;&quot;;
</pre>
<p>هر دو درستن. بدون پرانتز و با پرانتز.<br />
<strong>تفاوتها</strong><br />
<span id="more-708"></span><br />
تابع print بازگشتی داره و همیشه هم بازگشتیش یکه.اما echo بازگشتی نداره!!خوب این یعنی چی؟ مثلا : </p>
<pre class="brush: php; title: ; notranslate">
($x)? print &quot;True&quot; : print &quot;False&quot;;
</pre>
<p>کاملا درسته، ولی </p>
<pre class="brush: php; title: ; notranslate">
($x)? echo &quot;True&quot; : echo &quot;False&quot;;
</pre>
<p>کاملا غلط و اجرا نمیشه.<br />
اپراتور @ روی echo کار نمیکنه. یعنی در: </p>
<pre class="brush: php; title: ; notranslate">
@print 10/0 ;
</pre>
<p>اپراتور درست عمل میکنه، خطای پیش آمده (که تقسیم بر صفر باشه) رو خفه میکنه. منتها :</p>
<pre class="brush: php; title: ; notranslate">
@echo 10/0 ;
</pre>
<p>اصلا اجرا نمیشه. تا به حال دو بر هیچ به نفع print !!<br />
خوب تفاوت بعدی به نفع echo هستش.<br />
print فقط و فقط یه آرگومان میگیره، ولی echo هر چند آرگومان رو میتونه قبول کنه :</p>
<pre class="brush: php; title: ; notranslate">
echo '$x = ', $x ,'$y=',$y;
</pre>
<p>البته این یه جورایی خیلی کنده :) همین خروجی با print تولید نمیشه مگه با کمک اپراتور نقطه (که با echo هم میشه ) :</p>
<pre class="brush: php; title: ; notranslate">
print '$x = '. $x .'$y='.$y;
</pre>
<p>تو PHP میشه از رشته که با &#8221; نوشته شده استفاده کرد و متغیر رو داخلش نوشت، ولی کندتره، به عبارتی معادل این دو تا خط بالا میشه نوشت :</p>
<pre class="brush: php; title: ; notranslate">
echo &quot;\$x = $x \$y=$y&quot;;
</pre>
<p>منظور نوع رشته هست، با print و echo هیچ فرقی نداره.<br />
در کل میگن که echo سریعتره. من از یه چیزی مشابه این کد استفاده کردم و فهمیدم که راست میگن، منتها تفاوت خیلی نیست. همیشه، متوسط حالتی که از echo استفاده کردم از حالتی که به جاش print گذاشتم کمتر بود : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
   $mtime = microtime();
   $mtime = explode(&quot; &quot;,$mtime);
   $mtime = $mtime[1] + $mtime[0];
   $starttime = $mtime;

	for ($i=0;$i&lt;1000000;$i++)
		print $i;

   $mtime = microtime();
   $mtime = explode(&quot; &quot;,$mtime);
   $mtime = $mtime[1] + $mtime[0];
   $endtime = $mtime;
   $totaltime = ($endtime - $starttime);
   echo &quot;This page was created in &quot;.$totaltime.&quot; seconds&quot;;
</pre>
<p>برای این تست از cli استفاده کردم نه وب، عددای بزرگ خیلی طول میکشن و علت هم حجم اطلاعات منتقل شدست، ربطی به تابع نداره.  (من این تست پایینی رو انجام دادم در حقیقت، نه بالایی رو،  این تو ویندوز جواب نمیده مگه اینکه تو msys یا یه چیزی مشابهش انجام بشه، بالایی میشه تو یه فایل نوشتش و انجامش داد ) : </p>
<pre class="brush: bash; title: ; notranslate">
php -d implicit_flush=off -r '$s=microtime(true); for($i=0;$i&lt;1000000;$i++) echo &quot;this is a test\n&quot;; echo microtime(true)-$s,&quot;\n&quot;;' | tail -n 1
</pre>
<p>و برای print : </p>
<pre class="brush: bash; title: ; notranslate">
php -d implicit_flush=off -r '$s=microtime(true); for($i=0;$i&lt;1000000;$i++) print &quot;this is a test\n&quot;; echo microtime(true)-$s,&quot;\n&quot;;' | tail -n 1
</pre>
<p>فقط چندین بار انجام بدید در شرایط یکسان و میانگین بگیرید.<br />
تا به اینجا میشه ۲ به ۲ :) </p>
<p>مهمترین تفاوت این دو تا به نظر من یه چیز دیگست که باعث میشه من ترجیح بدم از echo استفاده کنم نه print!! سادست، echo چهار حرفه ولی print پنج حرفه!! فقط مشکل اون کاراکتر پنجمه، یه کاراکتر اضافه، یه t :) </p>
<p>پ.ن.۱ : این دلیل آخری به صورت غیر مستقیم! میتونه توضیح بده چرا من دیر به دیر پست میزنم اینجا :D<br />
پ.ن.۲ : امروز هم تولد وبلاگمه!! پست قبلی تولد خودم بود،‌ این یکی تولد وبلاگم. البته این وبلاگ اولین وبلاگ منه که بیشتر از یه سال توش نوشتم!!!!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/708/echo-vs-print/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/708/echo-vs-print/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP و فایلها&#8230;</title>
		<link>http://cyberrabbits.net/646/php-and-files/</link>
		<comments>http://cyberrabbits.net/646/php-and-files/#comments</comments>
		<pubDate>Tue, 09 Nov 2010 14:23:49 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=646</guid>
		<description><![CDATA[توی ویندوز کوچک و بزرگ بودن حروف در اسم فایلها تاثیری نداره. ولی توی لینوکس این یه کم متفاوته، مثلا تو ویندوز TEST.php و test.php یکی هستن اگه به عنوان اسم فایل استفاده بشن. خودمونی تر! تو یه فولدر نمیشه دو تا فایل رو همزمان به این دو اسم ایجاد کرد، ولی تو لینوکس میشه.&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/78/date-convertor/' rel='bookmark' title='تبدیل تاریخ'>تبدیل تاریخ</a> <small>بارها در این باره، با خیلی ها بحث کردم. حالا...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>توی ویندوز کوچک و بزرگ بودن حروف در اسم فایلها تاثیری نداره. ولی توی لینوکس این یه کم متفاوته، مثلا تو ویندوز TEST.php و test.php یکی هستن اگه به عنوان اسم فایل استفاده بشن. خودمونی تر! تو یه فولدر نمیشه دو تا فایل رو همزمان به این دو اسم ایجاد کرد، ولی تو لینوکس میشه.<br />
خوب، بنابراین، زمانی که مثلا تو PHP میخوایم یه فایل رو include کنیم، حتما رعایت کنیم این مساله رو و در نظر داشته باشیم که ممکنه کد ما روی لینوکس قرار بگیره،‌هر چند الان رو ویندوز نوشته میشه، و برعکس، یعنی به کوچکی و بزرگی اسم فایلها در ویندوز توجه کنیم و هم اینکه از این قابلیت لینوکس که فایلهای همنام ولی متفاوت از لحاظ کوچک و بزرگ بودن حروف رو میتونه کنار هم قرار بده، استفاده نکنیم، چون رو ویندوز یکی از فایلها توسط اون یکی فایل رونویس میشه. </p>
<p>اما مشکل اینجا یه کم بدتر هم میشه. <a href="http://php.net/manual/en/function.require-once.php">require_once</a> و <a href="http://www.php.net/manual/en/function.include-once.php">include_once</a> هر دو، قراره یه فایل رو هر چند بار که درخواست شد، فقط و فقط یه بار استفاده کنن. حالا فرض کنید رو ویندوز،‌کدی دارید به این صورت : </p>
<pre class="brush: php; title: ; notranslate">
	require_once(&quot;some_file.php&quot;);
	require_once(&quot;some_file.PHP&quot;);
</pre>
<p>روی لینوکس، کلا این میگرده دنبال دو تا فایل مختلف و اگه دو فایل با این اسامی نباشه از شما خطا میگیره. اما ویندوز، اون موقع قضیه یه کم بامزه تره. فایل some_file.php دو بار (نه یک بار) require میشه، یعنی در مورد این دو تابع، بدون توجه به پلاتفرم، PHP شبیه لینوکس عمل میکنه، نه ویندوز&#8230;. حالا فرض کنید مثلا Some_file.php و SOME_file.php و&#8230;</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/646/php-and-files/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/78/date-convertor/' rel='bookmark' title='تبدیل تاریخ'>تبدیل تاریخ</a> <small>بارها در این باره، با خیلی ها بحث کردم. حالا...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/646/php-and-files/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>کلمات مشابه</title>
		<link>http://cyberrabbits.net/625/similar-words/</link>
		<comments>http://cyberrabbits.net/625/similar-words/#comments</comments>
		<pubDate>Wed, 27 Oct 2010 17:46:58 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[levenshtein]]></category>
		<category><![CDATA[metaphone]]></category>
		<category><![CDATA[similar_text]]></category>
		<category><![CDATA[soundex]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=625</guid>
		<description><![CDATA[گاهی لازم میشه که شما میخواید از توی یه لیست از کلمات یه سری کلمه رو که شبیه تر هستن به کلمات دیگه، جدا کنید. توی PHP یه سری تابع برای اینکار معرفی شده (و البته MySQL هم تابعی داره براش) ولی متاسفانه جز یکی از این توابع باقی برای زبان فارسی کار نمیکنن. من&#8230;
بدون پست مرتبط.]]></description>
			<content:encoded><![CDATA[<p>گاهی لازم میشه که شما میخواید از توی یه لیست از کلمات یه سری کلمه رو که شبیه تر هستن به کلمات دیگه، جدا کنید. توی PHP یه سری تابع برای اینکار معرفی شده (و البته MySQL هم تابعی داره براش) ولی متاسفانه جز یکی از این توابع باقی برای زبان فارسی کار نمیکنن.<br />
من امروز قصد دارم همون ها رو معرفی کنم با یه مثال ساده، در حال حاضر هم (اگه اینهمه مشغله الکی بذاره) میخوام نسخه فارسی یکی از این توابع رو بنویسم.<br />
اولین تابع <a href="http://php.net/manual/en/function.levenshtein.php">levenshtein</a> هستش که فاصله دو کلمه رو بر اساس نحوه جایگذاری حروف پیدا میکنه. اطلاعات بیشتر رو توی<a href="http://en.wikipedia.org/wiki/Levenshtein_distance"> wiki مربوط به همین الگوریتم</a> ببینید. این تابع برای فارسی هم کم و بیش کار میکنه، هر وقت دو رشته کاملا برابر باشن، بازگشتی صفر میشه و در غیر اینصورت بازگشتی تغییر میکنه هر چی اختلاف بیشتره عدد هم بزرگتره. اینهم مثالی که توی خود راهنمای تابع نوشته شده :<br />
<span id="more-625"></span></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
// input misspelled word
$input = 'carrrot';

// array of words to check against
$words  = array('apple','pineapple','banana','orange',
                'radish','carrot','pea','bean','potato');

// no shortest distance found, yet
$shortest = -1;

// loop through words to find the closest
foreach ($words as $word) {

    // calculate the distance between the input word,
    // and the current word
    $lev = levenshtein($input, $word);

    // check for an exact match
    if ($lev == 0) {

        // closest word is this one (exact match)
        $closest = $word;
        $shortest = 0;

        // break out of the loop; we've found an exact match
        break;
    }

    // if this distance is less than the next found shortest
    // distance, OR if a next shortest word has not yet been found
    if ($lev &lt;= $shortest || $shortest &lt; 0) {
        // set the closest match, and shortest distance
        $closest  = $word;
        $shortest = $lev;
    }
}

echo &quot;Input word: $input\n&quot;;
if ($shortest == 0) {
    echo &quot;Exact match found: $closest\n&quot;;
} else {
    echo &quot;Did you mean: $closest?\n&quot;;
}

?&gt;
</pre>
<p>خروجی هم میشه : </p>
<pre class="brush: plain; title: ; notranslate">
Input word: carrrot
Did you mean: carrot?
</pre>
<p>این تابع وقتی بحث مقایسه پیش میاد انتخاب خوبیه. اما فرض کنید که میخوایم از توی دیتابیس اینکار رو انجام بدیم، نیاز به یه Stored Procedure احساس میشه :)<br />
من یکی<a href="http://codejanitor.com/wp/2007/02/10/levenshtein-distance-as-a-mysql-stored-function/"> تو این آدرس</a> پیدا کردم که برای من جواب داد (کم و بیش) : </p>
<pre class="brush: sql; title: ; notranslate">
delimiter //

CREATE FUNCTION LEVENSHTEIN (s1 VARCHAR(255), s2 VARCHAR(255))
  RETURNS INT
    DETERMINISTIC
      BEGIN
        DECLARE s1_len, s2_len, i, j, c, c_temp, cost INT;
        DECLARE s1_char CHAR;
        DECLARE cv0, cv1 VARBINARY(256);
        SET s1_len = CHAR_LENGTH(s1), s2_len = CHAR_LENGTH(s2), cv1 = 0x00, j = 1, i = 1, c = 0;
        IF s1 = s2 THEN
          RETURN 0;
        ELSEIF s1_len = 0 THEN
          RETURN s2_len;
        ELSEIF s2_len = 0 THEN
          RETURN s1_len;
        ELSE
          WHILE j &lt;= s2_len DO
            SET cv1 = CONCAT(cv1, UNHEX(HEX(j))), j = j + 1;
          END WHILE;
          WHILE i &lt;= s1_len DO
            SET s1_char = SUBSTRING(s1, i, 1), c = i, cv0 = UNHEX(HEX(i)), j = 1;
            WHILE j &lt;= s2_len DO
                SET c = c + 1;
                IF s1_char = SUBSTRING(s2, j, 1) THEN SET cost = 0; ELSE SET cost = 1; END IF;
                SET c_temp = CONV(HEX(SUBSTRING(cv1, j, 1)), 16, 10) + cost;
                IF c &gt; c_temp THEN SET c = c_temp; END IF;
                SET c_temp = CONV(HEX(SUBSTRING(cv1, j+1, 1)), 16, 10) + 1;
                IF c &gt; c_temp THEN SET c = c_temp; END IF;
                SET cv0 = CONCAT(cv0, UNHEX(HEX(c))), j = j + 1;
            END WHILE;
            SET cv1 = cv0, i = i + 1;
          END WHILE;
        END IF;
        RETURN c;
      END//
</pre>
<p>&#8211; اون که اول delimiter رو تغییر دادم لازمه تا کد درست کار کنه.<br />
با اضافه کردن این تابع به پایگاه داده (که توی MySQL 5 به بالا هم کار میکنه فقط) اونوقت شما میتونید از این استفاده کنید برای پیدا کردن اسامی مشابه توی پایگاه داده.<br />
مثلا : </p>
<pre class="brush: sql; title: ; notranslate">
SELECT user_name FROM mytable WHERE LEVENSHTEIN('admin',user_name)&lt;3
</pre>
<p>اینجوری تمام نامهای توی فیلد که حداکثر سه تفاوت با نام admin دارن پیدا میشن.<br />
خوب این روش رو من بیشتر پسندیدم چون با فارسی خوب کار کرد. اما مشکل اینه که یه کم کنده.<br />
برای انگلیسی الگوریتم <a href="http://en.wikipedia.org/wiki/Soundex">soundex</a> وجود داره، که PHP هم تابع <a href="http://www.php.net/manual/en/function.soundex.php">soundex</a> و MySQL هم تابع <a href="http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_soundex">SOUNDEX</a> رو براش معرفی میکنن.</p>
<p>منتها مشکل بزرگ این تابع، الگوریتمشه که بر اساس حروف انگلیسی کار میکنه، نه فارسی و من دارم سعی میکنم که با آزمایش و خطا نسخه فارسی این تابع رو هم ایجاد کنم.<br />
این تابع یه رشته برای هر کلمه ایجاد میکنه که طولش ۴ حرفه و میشه از اون برای مقایسه دو تا رشته استفاده کرد، و نشون میده که این دو تا رشته تا چه حد به هم نزدیک هستن. </p>
<p>اما تابع بعدی تابع<a href="http://www.php.net/manual/en/function.similar-text.php"> similar_text</a> هستش، که برعکس تابع levenshtein هر چی شباهت بیشتر باشه عدد بزرگتری رو به عنوان بازگشتی برمیگردونه، این تابع هم با فارسی کار میکنه، بد هم نیست.<br />
آخرین تابع این سری تابع <a href="http://www.php.net/manual/en/function.metaphone.php">metaphone</a> هستش که باز هم برای انگلیسی کار میکنه و شبیه soundex عمل میکنه. منتها بر خلاف soundex از قواعد فونوتیک هم اطلاعاتی داره، و نتیجه استفاده از این تابع خیلی به واقعیت نزدیکتره تا soundex.</p>
<p>&#8211; اما علت نوشتن این متن، من قصد دارم روی یه تابع مشابه کار کنم برای فارسی، اگه کسی ایده ای داره، یا اینکه میدونه جایی کد GPL یا چیزی تو این مایه ها (نه کد غیر آزاد) هست برای فارسی یا حتی عربی، اینجا بگه. </p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/625/similar-words/"></g:plusone></div><p>بدون پست مرتبط.</p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/625/similar-words/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>فنی حرفه ای و رویای پیشرفت!</title>
		<link>http://cyberrabbits.net/604/technical-vocational/</link>
		<comments>http://cyberrabbits.net/604/technical-vocational/#comments</comments>
		<pubDate>Tue, 12 Oct 2010 17:51:50 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[متفرقه]]></category>
		<category><![CDATA[Iran]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Technical & Vocational]]></category>
		<category><![CDATA[tvto]]></category>
		<category><![CDATA[فنی و حرفه ای]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=604</guid>
		<description><![CDATA[قبلا گفتم، رشته تحصیلی من برق بوده. ولی بنا به دلایلی با PHP میونه خیلی خوبی دارم و از چند سال پیش به خواست یکی از دوستانم که اون موقع مدیر گروه کامپیوتر بود، PHP رو هم درس میدم. امسال از یه دانشگاه پیام نور، که کلاسهای فنی و حرفه ای رو هم به صورت&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
<li><a href='http://cyberrabbits.net/436/change-link-structure/' rel='bookmark' title='تغییر ساختار لینک'>تغییر ساختار لینک</a> <small>متاسفانه،‌ به خاطر مشکلی که پیش اومده بود، مجبور شدم...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>قبلا گفتم، رشته تحصیلی من برق بوده. ولی بنا به دلایلی با PHP میونه خیلی خوبی دارم و از چند سال پیش به خواست یکی از دوستانم که اون موقع مدیر گروه کامپیوتر بود، PHP رو هم درس میدم.<br />
امسال از یه دانشگاه پیام نور، که کلاسهای فنی و حرفه ای رو هم به صورت رایگان برای دانشجوهاشون میگذارن باهام تماس گرفتن (و هنوز هم نمیدونم منو کی معرفی کرده!!) برای دوره توسعه دهنده وب با PHP از من خواستن که درس بدم.<br />
رفتم و سرفصلها رو از سایتش گرفتم، یکی دیگه هم بود (<a href="http://research.irantvto.ir/uploads/83_147_moha%20barna%20PHP%2088.pdf">این لینک</a> و <a href="http://research.irantvto.ir/uploads/83_26_toseadahande%20veb%20ba%20php%201.pdf">این لینک</a>) بگذریم از کل قضیه مسخره بودن سرفصل، رسیدم به منابع :</p>
<p><a href="http://cyberrabbits.net/wp-content/uploads/2010/10/Screenshot-83_26_toseadahande-veb-ba-php-1.pdf.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/10/Screenshot-83_26_toseadahande-veb-ba-php-1.pdf-300x232.png" alt="منابع استاندارد توسعه دهنده وب با PHP فنی و حرفه ای" title="منابع استاندارد توسعه دهنده وب با PHP فنی و حرفه ای" width="300" height="232" class="alignnone size-medium wp-image-605" /></a><br />
موارد هشت و نه.<br />
اول اینکه سایت PHP.com هیچ ربطی به PHP نداره جز اینکه با PHP نوشته شده!! اون سایت Parents Helping Parents هستش نه PHP : Hypertext Preprocessor !<br />
دوم اینکه مگه MySQL محصول RedHat هستش که سایت RedHat رو معرفی میکنید براش؟؟!! خودش یتیمه مگه!! خصوصا الان که صد تا صاحب پیدا کرده!</p>
<p>حالا مساله اینجاست، کسی که این استاندارد رو نوشته، اصلا به این سایتها سر زده؟ اصلا اطلاعاتی درباره اینها داره؟ و آیا این شخص یا گروه صلاحیت نوشتن این استاندارد رو دارن؟  مطالب هم ثابت میکنه اینرو -خودتون بخونید- ، وگرنه میشه گفت اینها غلط چاپیه :) که من مطمئنم نیست!<br />
باقیش رو نمیدونم ولی واقعا این استانداردیه که شما دارید به خورد ملت میدید؟؟ اونوقت این میخواد مایه پیشرفت بشه؟</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/604/technical-vocational/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
<li><a href='http://cyberrabbits.net/436/change-link-structure/' rel='bookmark' title='تغییر ساختار لینک'>تغییر ساختار لینک</a> <small>متاسفانه،‌ به خاطر مشکلی که پیش اومده بود، مجبور شدم...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/604/technical-vocational/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>بازم یک نکته کوچک تو PHP</title>
		<link>http://cyberrabbits.net/585/php-simple-note/</link>
		<comments>http://cyberrabbits.net/585/php-simple-note/#comments</comments>
		<pubDate>Fri, 08 Oct 2010 19:16:16 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=585</guid>
		<description><![CDATA[یکی از مشکلاتی که برنامه نویسهای تازه کار با اون خیلی برخورد میکنن، طرز برخورد PHP با اپراتورهای منطقیه. برای نمونه == که برای بررسی اینه که دو متغیر با هم برابرن یا نه. کد ساده ای رو در نظر بگیرید مثل : و خیلی ساده اینکه اینها برابرند و شرط درسته. در حقیقت PHP&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون خیلی برخورد میکنن، طرز برخورد PHP با اپراتورهای منطقیه. برای نمونه == که برای بررسی اینه که دو متغیر با هم برابرن یا نه.<br />
کد ساده ای رو در نظر بگیرید مثل : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
if ('string'==true)
	echo &quot;It's equal!&quot;
</pre>
<p>و خیلی ساده اینکه اینها برابرند و شرط درسته. در حقیقت PHP همه رشته ها رو به true تعبیر میکنه، مگه دو رشته به خصوص رو. اولی رشته ای که خالی باشه،‌ و دومی رشته ای که حاوی 0 (کاراکتر صفر) باشه. این دو تا برابر false هستن. یعنی دقیقا یک صفر داخل رشته.(اینم از اون استثناهای PHP هستش که نمیتونم درکش کنم، ولی اینجوریه دیگه! گفتن که رشته خالی با رشته حاوی یک عدد صفر کله گنده، و NULL و FALSE هر چهار تا برابرند! علت رو میتونم حدس بزنم ولی نمیتونم درکش کنم، به راحتی میتونست اینجوری نباشه، خصوصا اون رشته حاوی صفر منظورمه.)<br />
برای اینکه رفتار شبیه C رو داشته باشید از اپراتور === (سه مساوی) استفاده کنید، این اپراتور علاوه بر مقدار نوع رو هم بررسی میکنه. یعنی این کد : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
if ('string'===true)
	echo &quot;It's equal!&quot;
</pre>
<p>دیگه مثل قبلی نیست. این حالت دیگه if درست نیست.<br />
شاید بگید این خیلی سادست، چیزی که همیشه میشنوم! ولی وقت عمل که میشه، میبینم همین نکته کوچک چقدر افراد رو دچار دردسر میکنه! تا حدی که امروز یکی اومده و میگه که یه باگ تو PHP کشف کردم! (همون کد اولی رو) و باقیش هم که قابل حدسه! چون بار اول بود با اون شخص روبرو میشدم، نمیشد یهو جوابشو بدم،  از یه طرف هم طرف کلی ادعا داشت، که مونده بودم اصلا چی میتونم بگم! </p>
<p>&#8211; یه خواهش از دوستان همشهری و حضوری! من یه برنامه نویسم، حرفه ای هم کار میکنم، ولی به خدا با هیچ کس کل کل ندارم!!!! عاجزانه خواهشمندم منو تو هیچ زمینه ای علم! نکنید. گر چه قد و قواره من فقط به درد علم میخوره و بس!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/585/php-simple-note/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/585/php-simple-note/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>آدرس دهی درست</title>
		<link>http://cyberrabbits.net/580/seo-friendly-url/</link>
		<comments>http://cyberrabbits.net/580/seo-friendly-url/#comments</comments>
		<pubDate>Wed, 29 Sep 2010 21:24:01 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[آپاچی]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[htaccess]]></category>
		<category><![CDATA[SEO]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=580</guid>
		<description><![CDATA[یکی از دغدغه های فعلی طراحان وب، ایجاد لینک های &#8220;موتور جستجو پسند!&#8221; شده. پر بیراه هم نیست، که این لینکها هم به راحتی به خاطر سپرده میشوند و هم اینکه خواناتر هستند هم برای آدمیزاد هم ربات. ابن لینکها، برای یک سایت SEO Friendly یکی از مهمترین بخشهاست. اکثر CMS ها یا Framework ها&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
<li><a href='http://cyberrabbits.net/43/vhosts/' rel='bookmark' title='هاست مجازی بر روی آپاچی'>هاست مجازی بر روی آپاچی</a> <small>خیلی پیش می آید که ما نیاز به چند آدرس...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یکی از دغدغه های فعلی طراحان وب، ایجاد لینک های &#8220;موتور جستجو پسند!&#8221; شده. پر بیراه هم نیست، که این لینکها هم به راحتی به خاطر سپرده میشوند و هم اینکه خواناتر هستند هم برای آدمیزاد هم ربات. ابن لینکها، برای یک سایت SEO Friendly یکی از مهمترین بخشهاست.<br />
اکثر CMS ها یا Framework ها امکاناتی برای ایجاد این لینکها معرفی میکنند. منتها اگر لازم باشه که خودتون بنویسیدش، شاید یه کم دردسر ساز بشه. تو این پست میخوام توضیح بدم با‌ آپاچی و PHP چطوری میشه این کار رو انجام داد.<br />
معمولا، آدرسهای اینترنتی به این صورت هستن :</p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/users.php?id=10&#038;action=delete&#038;uid=somedata
</pre>
<p>که ما ترجیح میدیم (مثلا) اینطوری باشن (که من هدفم همینه): </p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/users/id:10/action:delete/uid:somedata
</pre>
<p>یا مثلا این :</p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/users/id/10/action/delete/uid/somedata
</pre>
<p>دو تا حالت فرقی نمیکنه، شاید شما بخواید حتی جور دیگه ای ایجادش کنید که یه کم بحث سلیقه و انتخاب شخصی خودتون  و نیازها مطرح میشه. من حالت اول رو توضیح میدم، دومی هم آنچنان فرقی نمیکنه.</p>
<p><strong>۱- ملزومات</strong><br />
اولین چیز <a href="http://apache.org/">آپاچی</a> هستش با <a href="http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html">mod_rewrite</a> که روش نصب و فعال باشه. برای باقی وب سرور ها هم ممکنه، مثلا <a href="http://www.lighttpd.net/">lighttpd</a> هم این قابلیت رو داره با کمی تفاوت، و IIS 7 هم . منتها برای IIS های قدیمیتر، بدون یک سری اضافات ممکن نیست. البته میشه از error document استفاده کرد، که در آینده اگه حسش بود و وقتش بود توضیح میدم دربارش (همین الان من از این استفاده میکنم، توی IIS 6 و همین وبلاگ)<br />
بعد از نصب آپاچی مطمئن بشید که mod_rewrite فعاله. توی تنظیمات بگردید دنبالش، یه خطی به این صورت :</p>
<pre class="brush: plain; title: ; notranslate">
LoadModule rewrite_module modules/mod_rewrite.so
</pre>
<p>البته نکته جالب برای خودم اینه که الان اصلا و ابدا من این فایل mod_rewrite.so رو ندارم، توی تنظیمات آپاچی هم نیست همچین خطی ولی rewrite فعاله!! (من از <a href="http://www.zend.com/en/products/server-ce/">Zend Server CE</a> استفاده میکنم) حالا راه ساده تر (اگه به تنظیمات دسترسی ندارید یا مثل من نسخه تغییر داده شده آپاچی رو نصب دارید) اینه که اینطوری تستش کنید:<br />
یه فایل بسازیدد توی پوشه ریشه آپاچی به اسم .htaccess (یک نقطه، قبل از اسم فایل هست، یعنی در حقیقت فایلی بدون اسم با پسوند htacces برای ویندوزیها و برای لینوکسی ها یک فایل مخفی.)<br />
تا جایی که یادمه توی ویندوز نمیشد از طریف explorer چنین فایلی ساخت، به جاش یه فایل بسازید با notepad بعد با اون که ذخیرش کنید با این اسم.داخلش این کد رو بنویسید : </p>
<pre class="brush: plain; title: ; notranslate">
RewriteEngine On
</pre>
<p>بعد سایت رو از طریق بروزر باز کنید. اگه صفحه 500, Internal Server Error گرفتید یعنی نصب نیست. در غیر اینصورت مشکلی نیست.</p>
<p><ins datetime="2011-01-05T20:19:20+00:00">یه نکته که فراموش کرده بودم این بود که توی تنظیمات اصلی آپاچی، اونجایی که Virtual Host رو تعریف میکنید عبارت<br />
AllowOverride All<br />
باشه، یعنی دست کم None نباشه به جای All.</ins><br />
باقیش هم که فقط مربوط میشه به PHP و تمام.<br />
<span id="more-580"></span><br />
<strong>۲- mod_rewrite </strong><br />
این ماژول آپاچی خودش کلی ریزه کاریها داره. منتها من فقط با یه سری تنظیمات ساده کار دارم و همونها رو توضیح میدم. توی فایل htaccess اینو بنویسید : </p>
<pre class="brush: plain; title: ; notranslate">
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
</pre>
<p>این فایل، و این تنظیمات، باعث میشه که تمام درخواستهای که به وب سرور میرسن ولی وجود ندارن (یعنی تو ریشه پوشه ای به اون نام وجود نداره و فایلی هم به اون اسم و آدرس نیست، فرستاده بشن به فایل index.php ، و این همون چیزی هستش که ما لازم داریم.<br />
در حقیقت تو این تنظیمات، ما سه تا شرط داریم که با RewriteCond شروع شدن و اگر هم دقت کنید متوجه میشید که با هم ترکیب شدن (با OR ) و در نهایت هم RewriteRule که مشخص میکنه وقتی شرطها برقرار شدن آپاچی چیکار کنه. </p>
<p>خوب دیگه تا اینجا کار ما با آپاچی تمومه. از این به بعد میریم سراغ PHP</p>
<p><strong>۳- PHP </strong><br />
گام سوم و آخر PHP هستش. چطوری بفهمیم درخواستی که اومده چه پارامترهایی داره و اونو پردازش کنیم؟<br />
مطمئن بشید فایل index.php رو ساختید. این فایل رو بگذارید کنار فایل .htaccess .توی این فایل این کد رو بنویسید : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	echo $_SERVER['REQUEST_URI'];
</pre>
<p>با هر آدرسی دوست دارید (البته قسمت سرورش که ثابته، منظورم از سرور به بعده) اون رو فراخوانی کنید، میبینید که آدرس نمایش داده میشه و در همه موارد این فایل اجرا میشه.<br />
سعی کنید یه فایل موجود رو آدرس دهی کنید، مثلا یه عکس و یا یه فایل جاوااسکریپت، میبینید مشکلی پیش نمیاد و اون فایل درست برگردونده میشه.</p>
<p>حالا میرسیم به بحث اول خودمون.<br />
من میخوام اینچنین ساختار لینکی ایجاد کنم : </p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/somefile/parameter1:value1/parameter2:value2
</pre>
<p>با هر چند پارامتر که لازمه. البته تو مدلهای MVC معمولا به اینصورته :</p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/module/controller/action/param1:value1/param2:value2
</pre>
<p>که به جای : از / هم استفاده میشه در بعضی پیاده سازیها مثل Zend ولی من حالت ساده تر رو پیش میگیرم.<br />
من چند تا فایل دارم، که قسمت اول بعد از آدرس سایت اسم اون فایله. مثلا فایل users.php و فایل products.php . فایل index.php هم که ورودی سایت هستش. وی index.php این کد رو قرار بدید :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php

	$address=$_SERVER['REQUEST_URI'];
	$addressStack=explode('/',$address);
	//First one is always empty string, so remove it.
	array_shift($addressStack);

	//Next step is to get file and parameter
	if ($addressStack[0]==''){
		//This is default call. just show the message:
?&gt;
	This is index..&lt;br /&gt;
	&lt;a href=&quot;/users/name:ali&quot;&gt;Ali's page&lt;/a&gt;&lt;br /&gt;
	&lt;a href=&quot;/products/cat:home/sortby:price&quot;&gt;Show some product catagory page&lt;/a&gt;
&lt;?php
	die();
	}

	$page=array_shift($addressStack);
	$params=array();
	foreach ($addressStack as $stack){
		$p=explode(':',$stack);
		if (isset($p[1]))
			$params[$p[0]]=$p[1];
		else
			$params[$p[0]]=null;
	}

	function callPage($page,$params){
		echo &quot;You are in page $page&quot;;
		echo &quot;&lt;br /&gt;Parameters are :&lt;pre&gt;&quot;;
		echo print_r($params,true);
		echo &quot;&lt;/pre&gt;&quot;;
	}

	callPage($page,$params);
</pre>
<p>این کد میتونه به عنوان یه شروع کمکتون کنه. با این فرض که callPage یه تابعه که شما رو به هر صفحه میفرسته. (یعنی بر اساس آرگومان page صفحه درست رو include میکنه . البته مواظب باشید و به نکات امنیتی توجه کنید!)<br />
نکته مهمی که در این مورد وجود داره اینه که حتما، صفحاتی که وجود ندارن رو مدیریت کنید. یعنی توی تابع callPage حتما بررسی کنید که آیا صفحه وجود داره اگه وجود داره فراخوانی بشه اگر نه، خطای ۴۰۴ به درستی ایجاد بشه، چون وجود خطای ۴۰۴ مناسب خودش یکی از اصوله!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/580/seo-friendly-url/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
<li><a href='http://cyberrabbits.net/43/vhosts/' rel='bookmark' title='هاست مجازی بر روی آپاچی'>هاست مجازی بر روی آپاچی</a> <small>خیلی پیش می آید که ما نیاز به چند آدرس...</small></li>
<li><a href='http://cyberrabbits.net/708/echo-vs-print/' rel='bookmark' title='echo vs. print'>echo vs. print</a> <small>حتما دو تا تابع echo و print رو میشناسید. این...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/580/seo-friendly-url/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>HTTP Redirect</title>
		<link>http://cyberrabbits.net/471/http-redirect/</link>
		<comments>http://cyberrabbits.net/471/http-redirect/#comments</comments>
		<pubDate>Sat, 18 Sep 2010 21:50:32 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[امنیت در PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=471</guid>
		<description><![CDATA[یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا برای PHP هستم. این دفعه میخوام یه مشکل کوچیک رو توضیح بدم. خیلی ها معمولا اینکار رو انجام میدن (دست کم کدی که من امروز دیدم اینطوری بود! ) : این کد، بررسی میکنه یه تابع دسترسی به این صفحه رو داره، اگر نه پاسش&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/' rel='bookmark' title='CSRF یا چطور چرخ رو دوباره اختراع کردم'>CSRF یا چطور چرخ رو دوباره اختراع کردم</a> <small>امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد....</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/264/download-with-resume-support-in-php/' rel='bookmark' title='دانلود فایل با قابلیت Resume در PHP'>دانلود فایل با قابلیت Resume در PHP</a> <small>کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا برای PHP هستم. این دفعه میخوام یه مشکل کوچیک رو توضیح بدم. خیلی ها معمولا اینکار رو انجام میدن (دست کم کدی که من امروز دیدم اینطوری بود! ) : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php 

	if (!check_current_user_has_privilege_to_this_page()){
		header(&quot;Location: some_other_page.php&quot;);
	}

	//

	echo &quot;This is very important data!&quot;;
</pre>
<p>این کد، بررسی میکنه یه تابع دسترسی به این صفحه رو داره، اگر نه پاسش میده به یه صفحه دیگه، در غیر اینصورت (یعنی اگه کاربر دسترسی داشت) اطلاعاتی رو که باید نشون بده نشون میده، یا کارهایی که باید انجام بشه رو انجام میده. اطلاعات و یا کارهایی که ما نمیخوایم کاربر عادی بدون رد کردن شرطهای امنیتی مثل رمز ببینه. </p>
<p>ولی اینجا یه مشکل هست :) خیلی هم بزرگه. header() اطلاعات رو میفرسته به طرف کاربر و در طرف کاربر عمل جابجایی (redirect ) انجام میشه. حالا اگه با یه برنامه که درکی از Redirect و اصولا پروتکل HTTP نداره این صفحه باز بشه؟؟ یا بدتر،<a href="https://addons.mozilla.org/en-US/firefox/addon/11787/"> اول این افزونه فایرفاکس رو نصب کنید </a>بعد صفحه رو باز کنید، البته قبلش آدرس صفحه رو توی تنظیمات افزون اضافه کنید، اینجوری:<br />
<div id="attachment_472" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/08/Screenshot-NoRedirect-Settings.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/08/Screenshot-NoRedirect-Settings-300x186.png" alt="تنظیم افزونه NoRedirect برای دور زدن http redirect در یک صفحه خاص" title="Screenshot-NoRedirect Settings" width="300" height="186" class="size-medium wp-image-472" /></a><p class="wp-caption-text">تنظیم افزونه NoRedirect برای دور زدن http redirect در یک صفحه خاص</p></div><br />
 میبینید که صفحه کاملا باز میشه.برای مثال آدرس</p>
<pre class="brush: plain; title: ; notranslate">

http://cyberrabbits.net/non/bigbang/headercheck.php
</pre>
<p> رو چک کنید که این مشکل رو داره، کدش هم اینه : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/*
 * this is an example for redirect problem :)
 * */

 if (true)
	header(&quot;Location: http://google.com&quot;);

  echo &quot;This is very important data, you can not see this!(or you can :) )&quot;;
</pre>
<p>قاعدتا فسمت echo هیچوقت نباید نمایش داده بشه، ولی اون افزونه نصب و فعال باشه دیده میشه :)‌ این یعنی که این روش یه چیزی کم داره.<br />
خوب چاره چیه؟؟<br />
خیلی ساده :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php 

	if (!check_current_user_has_privilege_to_this_page()){
		header(&quot;Location: some_other_page.php&quot;);
		die(); //Very sweet :) little , beautiful function!!
	}

	//

	echo &quot;This is very important data!&quot;;
</pre>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/471/http-redirect/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/' rel='bookmark' title='CSRF یا چطور چرخ رو دوباره اختراع کردم'>CSRF یا چطور چرخ رو دوباره اختراع کردم</a> <small>امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد....</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/264/download-with-resume-support-in-php/' rel='bookmark' title='دانلود فایل با قابلیت Resume در PHP'>دانلود فایل با قابلیت Resume در PHP</a> <small>کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/471/http-redirect/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>یه نکته کوچک در PHP</title>
		<link>http://cyberrabbits.net/564/a-small-tip-in-php/</link>
		<comments>http://cyberrabbits.net/564/a-small-tip-in-php/#comments</comments>
		<pubDate>Thu, 16 Sep 2010 10:50:28 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=564</guid>
		<description><![CDATA[همونطور که میدونید توی PHP برای اپراتور OR دو نسخه وجود داره، وشاید کمتر کسی بدون که اینها دوتا از لحاظ طرز عمل تفاوتی ندارن، ولی اولویت عملیاتی متفاوتی دارن .(چند وقت پیش یه بحثی توی فروم iranphp.org مطرح شد در اینباره.) به عبارتی خیلیها درست نمیتونن نتیجه این کد رو حدس بزنن : پاسخ&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه وجود داره، وشاید کمتر کسی بدون که اینها دوتا از لحاظ طرز عمل تفاوتی ندارن، ولی اولویت عملیاتی متفاوتی دارن .(چند وقت پیش یه بحثی توی<a href="http://forum.iranphp.org/Thread-%D8%AE%D8%B7%D8%A7-%D8%AF%D8%B1-mysql-fetch-array"> فروم iranphp.org</a> مطرح شد در اینباره.) </p>
<p>به عبارتی خیلیها درست نمیتونن نتیجه این کد رو حدس بزنن :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	function someFunc(){
		return true;
	}

	$x=false;

	$y= $x || someFunc();

	$z= $x OR someFunc();

	var_dump($y);
	var_dump($z);
</pre>
<p><span id="more-564"></span><br />
پاسخ صحیح اینه که $y برابر true میشه و $z برابر false!!<br />
حتی این میتونه بدتر بشه، که میگذارمش به عهده تخیلات خودتون!! دلیلش هم سادست. اولویت || از = (و به طور کلی همه اپراتورهای مشابه) بیشتره، و در نتیجه اول اجرا میشه، ولی اولویت OR کمتر از = هستش و اول $x رو به $z نسبت میده، و در صورتی که نتیجه false باشه، فقط تابع someFunc فراخوانی میشه و بازگشتیش هم اصلا حساب نمیشه.<br />
تو حالتی که $x برابر true باشه هم، موضوع خیلی بامزه تره :) توی خط $z= &#8230;. اصلا تابع فراخوانی نمیشه :))<br />
این درباره اپراتورهای AND و &#038;&#038; هم صادقه.<br />
امتحانش کنید. <a href="http://php.net/manual/en/language.operators.precedence.php">جدول اولویت عملیاتی عملگرها در PHP رو ببینید.</a></p>
<p>ممنون از بچه های فروم ایران پی اچ پی&#8230;.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/564/a-small-tip-in-php/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/585/php-simple-note/' rel='bookmark' title='بازم یک نکته کوچک تو PHP'>بازم یک نکته کوچک تو PHP</a> <small>یکی از مشکلاتی که برنامه نویسهای تازه کار با اون...</small></li>
<li><a href='http://cyberrabbits.net/750/php-and-simple-notes/' rel='bookmark' title='بازم PHP و نکات کوچیکش!'>بازم PHP و نکات کوچیکش!</a> <small>یه پرسشنامه برای گرفتن چند تا برنامه نویس PHP طرح...</small></li>
<li><a href='http://cyberrabbits.net/799/apache-speed-up-tips/' rel='bookmark' title='دو نکته برای بالا بردن سرعت &#8211; آپاچی'>دو نکته برای بالا بردن سرعت &#8211; آپاچی</a> <small>از نکاتی که پیشنهاد میشه برای بالا بردن سرعت یه...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/564/a-small-tip-in-php/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>ایجاد مکرر کلید جلسه</title>
		<link>http://cyberrabbits.net/528/session-regenerate-id/</link>
		<comments>http://cyberrabbits.net/528/session-regenerate-id/#comments</comments>
		<pubDate>Sun, 05 Sep 2010 09:38:15 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[امنیت در PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فایرفاکس]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[FireBug]]></category>
		<category><![CDATA[FireCookie]]></category>
		<category><![CDATA[FireFox Addon]]></category>
		<category><![CDATA[Session hijacking]]></category>
		<category><![CDATA[Session stealing]]></category>
		<category><![CDATA[Sessions]]></category>
		<category><![CDATA[افزونه فایرفاکس]]></category>
		<category><![CDATA[فایرباگ]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=528</guid>
		<description><![CDATA[در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین نوشته، یه دوستی کامنت گذاشتن که استفاده از تابع session_regenerate_id میتونه کمک کنه برای جلوگیری از دزدیدن جلسه ؟ برای تست به این چیزها احتیاج هست : اول از همه فایرفاکس دوم فایرباگ سوم FireCookie چهارم یک بروزر دیگه غیر از فایرفاکس، مثلا Opera .&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/142/firebug/' rel='bookmark' title='FireBug'>FireBug</a> <small>شاید اگر FireBug نبود اینقدر FireFox رو دوست نداشتم. سالهاست...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین نوشته، یه دوستی کامنت گذاشتن که استفاده از تابع <a href="http://php.net/manual/en/function.session-regenerate-id.php">session_regenerate_id </a>میتونه کمک کنه برای جلوگیری از دزدیدن جلسه ؟ برای تست به این چیزها احتیاج هست :<br />
اول از همه <a href="http://www.mozilla.com/firefox/">فایرفاکس</a><br />
دوم <a href="http://getfirebug.com/">فایرباگ</a><br />
سوم <a href="https://addons.mozilla.org/en-US/firefox/addon/6683/">FireCookie</a><br />
چهارم یک بروزر دیگه غیر از فایرفاکس، مثلا <a href="http://www.opera.com/">Opera</a> .<br />
کدی مثل این رو ایجاد کنید : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
session_start();
session_regenerate_id();

if (isset($_SESSION['test'])){
	echo $_SESSION['test'];
	echo &quot;&lt;br /&gt;&quot;;
	echo session_id();
}else{
	$_SESSION['test']=$_SERVER['HTTP_USER_AGENT'];
	echo &quot;First time&quot;;
}
</pre>
<p>این کد، رشته مربوط به User Agent رو توی جلسه ذخیره میکنه. بعد از دفعه اول که جلسه ایجاد بشه هر بار کلید جلسه هم نمایش داده میشه و همون رشته ذخیره شده، نمایش کلید جلسه فقط برای اینه که دسترسی به کلید جلسه یک کمی راحت شه  :D<br />
اول این کد رو توی Opera یا هر بروزری غیر از فایرفاکس اجرا کنید. البته یه Refresh نیازه که کلید و محتوا رو نشون بده.<br />
حالا همون صفحه رو توی فایرفاکس باز کنید. دوبار هم Refresh کنید که هم کلید جلسه رو ببینید هم محتوا رو، این یکی با اون یکی فرق میکنه (طبیعیه دیگه!) حالا توو همین صفحه فایرباگ رو فعال کنید، اگه FireCookie نصب باشه یه تب داره به اسم Cookies اونجا برید، روی PHPSESSID کلیک راست کنید و گزینه Edit رو بزنید:<br />
<div id="attachment_529" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Firebug-.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Firebug--300x100.png" alt="صفحه FireCookie" title="صفحه FireCookie" width="300" height="100" class="size-medium wp-image-529" /></a><p class="wp-caption-text">صفحه FireCookie</p></div><br />
بعد تو دیالوگ زیر توی قسمت Value مقداری که الان توی Opera (یا هر Browser دیگه که دوست دارید) نمایش داده میشه رو کپی کنید.قبل از اینکه این مقدار کپی شده رو توی فایرفاکس بگذارید، چند بار Opera رو Refresh کنید که یه کلید جدید ساخته بشه (با کمک اون تابع  session_regenerate_id ) بعد اون کلیدی که مربوط به جلسه قبل بود و کپی کردید رو توی دیالوگ زیر قسمت Value بگذارید (این دیالوگ رو گفتم، با کلیک راست روی کلمه PHPSESSID و انتخاب گزینه Edit میتونید ببینید، قسمت Host و زمان و &#8230; مهم نیست و برای هر کس متفاوته و بستگی داره به هاستی که دارید این کد رو اجرا میکنید )‌ اینطوری مطمئن میشید که دیگه اون کلید جلسه برای Opera از بین رفته (که البته نرفته !) :<br />
<div id="attachment_530" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Edit-Cookie.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Edit-Cookie-300x221.png" alt="ویرایش کوکی" title="ویرایش کوکی" width="300" height="221" class="size-medium wp-image-530" /></a><p class="wp-caption-text">ویرایش کوکی</p></div></p>
<p>تایید کنید و بعد یه Refresh و اونوقته که اینو میبینید :<br />
<div id="attachment_531" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Mozilla-Firefox.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/09/Screenshot-Mozilla-Firefox-300x221.png" alt="فایرفاکس یا اپرا؟؟؟؟؟" title="فایرفاکس یا اپرا؟؟؟؟؟" width="300" height="221" class="size-medium wp-image-531" /></a><p class="wp-caption-text">فایرفاکس یا اپرا؟؟؟؟؟</p></div><br />
یه مشکل دیگه این تابع، اینه که دو بار کوکی مربوط به جلسه رو توی Header میفرسته، Browser های جدید و مدرن مشکلی ندارن با این قضیه ولی مثلا روی بعضی گوشیها قاطی میکنه :)<br />
اگر هم با آرگومان true این تابع رو فراخوانی کنیم، ممکنه مشکل از دست دادن جلسه رو داشته باشیم(توی تست من با Refresh های سریع)، بعلاوه اینکه سرعت رو هم کم میکنه، البته نسبتا.</p>
<p>ممنون از <a href="http://webnevis.net/">پیام</a> واسه اینکه این تابع رو هم گوشزد کردن.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/528/session-regenerate-id/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/142/firebug/' rel='bookmark' title='FireBug'>FireBug</a> <small>شاید اگر FireBug نبود اینقدر FireFox رو دوست نداشتم. سالهاست...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/528/session-regenerate-id/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ذخیره جلسات در پایگاه داده</title>
		<link>http://cyberrabbits.net/485/save-session-in-db/</link>
		<comments>http://cyberrabbits.net/485/save-session-in-db/#comments</comments>
		<pubDate>Fri, 03 Sep 2010 09:28:44 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[امنیت در PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[Session hijacking]]></category>
		<category><![CDATA[Session stealing]]></category>
		<category><![CDATA[Sessions]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=485</guid>
		<description><![CDATA[دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده که فیدبک رو ببینم)‌ به هر صورت، این دفعه میخوام روشی رو توضیح بدم که میشه مشکل ذخیره جلسه رو در فایل سیستم حل کرد. این راه حل آخرین راه حل در این زمینست. اگه تنظیمات سرور درست نباشه، و یه کاربر دیگه روی سیستم&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>دفعه قبل، درباره<a href="http://cyberrabbits.net/447/php-sessions-yes-or-no/"> دزدیدن جلسه</a> صحبت کردم، هنوز منتشر نشده که فیدبک رو ببینم)‌ به هر صورت، این دفعه میخوام روشی رو توضیح بدم که میشه مشکل ذخیره جلسه رو در فایل سیستم حل کرد.</p>
<p>این راه حل آخرین راه حل در این زمینست. اگه تنظیمات سرور درست نباشه، و یه کاربر دیگه روی سیستم شما، بتونه فایلهاتون رو از طریق کاربر خودش باز کنه، خیلی راحت میتونه سورس شما رو بدزده،‌ به دیتابیس شما دسترسی پیدا کنه و &#8230; (تنظیمات دیتابیس رو توی PHP معمولا توی فایل متنی مینویسن، خوب اگه یکی اونو باز کنه و بخونه دیتابیس هم امن نیست :) ) اگه میخواید برنامه خودتون رو منتشر کنید و نمیخواید دیگران به سورسش دسترسی داشته باشن، حتما با یه چیزی مثل <a href="http://www.zend.com/products/guard/">Zend Guard</a> از اون محافظت کنید، و از اون مهمتر،‌قیمت مهمترین فاکتور خرید هاست نیست!!!! فکر میکنم اعتماد و امنیت مهمتر باشه!</p>
<p>شاید بد نباشه که اول راه حل ساده تر رو نشون بدیم.<br />
اولین مساله، اینکه مشخص کنیم آیا واقعا از آخرین دسترسی به این جلسه، یه مدت زمان خاص گذشته یا نه؟ دوم اینکه مشخص کنیم این جلسه واقعا برای همین کاربریه که الان میخواد ازش استفاده کنه؟<br />
این کد رو ببینید :</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	//session_save_path(&quot;../tmp&quot;);
	session_start();

	//Settings
	$diff=10*60; //600 Sec time out
	$salt=&quot;this-is-uniq-hash-for-any-program&quot;;

	$_ip=isset ($_SERVER['HTTP_CLIENT_IP'])?
				$_SERVER['HTTP_CLIENT_IP'] : &quot;UNKNOWN&quot;;
	$_ip.=isset ($_SERVER['HTTP_X_FORWARDED_FOR'])?
				$_SERVER['HTTP_X_FORWARDED_FOR'] : &quot;UNKNOWN&quot;;
	$_ip.=isset ($_SERVER['REMOTE_ADDR'])?
				$_SERVER['REMOTE_ADDR'] : &quot;UNKNOWN&quot;;

	$_agent = isset ($_SERVER['HTTP_USER_AGENT']) ?
				$_SERVER['HTTP_USER_AGENT'] : 'NO USER AGENT';

	$browser_data=$salt.$_ip.$_agent;
	$browser_hash=md5($browser_data);

	$now=time();

	if (isset($_SESSION['last_time']) &amp;&amp; isset($_SESSION['browser_hash'])){
		if (strcasecmp($browser_hash,$_SESSION['browser_hash'])!=0
			|| $now-$_SESSION['last_time']&gt;$diff){
			foreach ($_SESSION as $key =&gt; $value){
				unset($_SESSION[$key]);
			}
			session_destroy();
			//You can pass to login page or whatever :-&quot;
			header(&quot;Location: &quot;.$_SERVER['PHP_SELF']);
			die();	//THIS IS IMPORTANT, never trust user browser,
		}
	}else{
		//this is new session....

		//Old session but without time and browser hash ?
		//Just destroy it, if any, to prevent hijaking
		foreach ($_SESSION as $key =&gt; $value){
			unset($_SESSION[$key]);
		}

		$_SESSION['last_time']=$now;
		$_SESSION['browser_hash']=$browser_hash;
	}
</pre>
<p>این کد رو دقیقا در ابتدای برنامه بگذارید. دقیقا در ابتدای فایل index.php (معمولا) میتونید یه فایل جدا درست کنید این کد رو توش بنویسید و در ابتدای برنامه اینو include کنید. حالا دیگه هم زمان کنترل میشه هم بروزر، گرچه این متد بسیار بسیار سادست :) ولی خیلی هم موثره.<br />
دقیقا اولین خط کد، که کامنت هم شده،‌میتونه این امکان رو به شما بده که جای ذخیره شدن جلسات رو عوض کنید، مثلا بیاریدشون روی پوشه های خودتون(که البته باید یه فولدری باشه که وب سرور بهش دسترسی نداشته باشه، یعنی یا با htaccess یا با هر ابزار دیگه ای کاملا قفلش کرده باشید یا اینکه اصلا خارج از پوشه ریشه باشه. البته این پوشه باید توسط PHP قابل نوشتن باشه که بتونه جلسات رو اونجا ذخیره کنه. اگر نه یکی از بیرون و به راحتی از طریق Browser میتونه فایل رو ببینه)</p>
<p>اما راه دوم، که به شما این امکان رو میده که مثلا جلسات رو توی دیتابیس ذخیره کنید، نه توی فایل. از <a href="http://ir.php.net/manual/en/book.pdo.php">PDO</a> برای دیتابیس استفاده شده.<br />
<span id="more-485"></span><br />
برای این روش PHP یه تابع پیش بینی کرده. تابع <a href="http://php.net/manual/en/function.session-set-save-handler.php">session_set_save_handler</a> که به شما امکان میده که خودتون ذخیره و بازیابی جلسه رو انجام بدید. من اینجا یه سیستم ساده ایجاد میکنم برای کمترین نیازها (البته با قابلیتهایی که کد بالایی هم داره)<br />
تابعی که گفتم، به عنوان آرگومان ۶ تا <a href="http://www.php.net/manual/en/language.pseudo-types.php">callback</a> میگیره، callback نوع خاصی نیست، بلکه اسم توابع دیگه به صورت رشته هستش، میتونه به صورت آرایه باشه اونم در صورتی که بخواید به یک متد از یک کلاس اشاره کنید.<br />
مثلا اگه ما یه تابع داشته باشیم به اسم save ، اونوقت رشته ای مثل &#8220;save&#8221; که دقیقا اسم این تابع هست رو بهش میگیم callback، حالا اگه این تابع مثلا از کلاس X باشه بخوایم مثلا از یه شیئ به اسم $object اسمشو بنویسیم میشه</p>
<pre class="brush: php; title: ; notranslate">
array(&amp;$object,&quot;save&quot;)
</pre>
<p>این یه چیزیه معادل اشاره گر به توابع منتها توی PHP دیگه یه نوع جدید معرفی نشده و با همون رشته و آرایه مساله فیصله داده شده.<br />
خوب، برگردیم به تابعی که گفتیم، این تابع ۶ تا آرگومان از جنس callback میگیره، و در زمانهای خاص این ۶ تا رو فراخوانی میکنه.<br />
اول یه جدول درست کنید به اسم sessions با این ساختار :‌</p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE IF NOT EXISTS `sessions` (
  `id` varchar(100) NOT NULL,
  `data` text NOT NULL,
  `modified` datetime NOT NULL,
  `hash` varchar(60) NOT NULL,
  UNIQUE KEY `id` (`id`)
)
</pre>
<p>حالا باید ۶ تا تابع نوشته بشه، من بنا به دلایلی (منجمله اینکه پیوستگی بیشتر میشه) از یه کلاس استفاده میکنم. یعنی به جای ۶ تا تابع مجزا، ۶ تا تابع مینویسم داخل یک کلاس. ساختار اصلی کلاس، اینه :</p>
<pre class="brush: php; title: ; notranslate">
class MySessionHandler{
	private $time_out; //Time out for session
	private $salt;//Salt, an uniq string

	private $pdo;//PDO object
	private $browser_hash;//Browser hash
</pre>
<p>متغیرهاش رو که شما اینجا می ببینید، سه تای اولی وقت ساختن به کلاس پاس داده میشه ولی چهارمی، توی کلاس ساخته میشه. متد سازنده کلاس رو آخر سر مینویسم. حالا میرسیم به توابع مربوط به جلسه. نکته مهم اینه که این توابع باید همه به صورت public باشن.</p>
<p><strong>Open function</strong><br />
‌تابع اول، وقتی فراخوانی میشه که قراره جلسه آغاز بشه. اسم جلسه و آدرس ذخیره شدن اون بهش پاس داده میشه، آدرس رو که اصلا نیازی نداریم چرا که ما میخوایم توی دیتابیس بنویسیم نه توی فایل. اسم جلسه هم مهم نیست :)‌ دست کم من استفاده ای تو این کلاس براش ندیدم، شما میتونید قبل از آغاز جلسه، این اسم رو با کمک تابع <a href="http://ir.php.net/manual/en/function.session-name.php">session_name</a> عوض کنید، که توصیه میکنم حتما اینکار رو انجام بدید. پیشفرضش PHPSESSID هستش. تابع میشه یه چیزی مثل این :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_open($save_path, $session_name){
		//Initialize your need here, in my case
		// I need nothing.
		return true;
	}
</pre>
<p>این تابع برای من کاری قرار نیست بکنه، فقط اینکه نشون میده جلسه شروع شد.<br />
<strong>Close function </strong><br />
این تابع برعکس قبلیه. گزارش میده که جلسه تموم شد. بازم برای من مهم نیست. البته این تابع آرگومان نداره. اینطوری میشه :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_close(){
		//Just deinitialize your resources
		return true;
	}
</pre>
<p><strong>Write function</strong><br />
خوب رسیدیم به جای مهم، نوشتن . یعنی وقتی جلسه میخواد نوشته بشه. البته PHP یه جا کل اطلاعات جلسه رو میفرسته به این تابع، نه یکی به یکی، به عبارتی تو آخر هر جلسه فقط یه بار این متد فراخوانی میشه وتمام.(دقیقا قبل از تابع close که بالاتر نوشتم)<br />
فرض میکنیم browser_hash قبلا محاسبه شده. اینم بدنه تابع :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_write($id,$data){
		$safe_id=$this-&gt;pdo-&gt;quote($id);
		$safe_data=$this-&gt;pdo-&gt;quote($data);
		$hash=$this-&gt;pdo-&gt;quote($this-&gt;browser_hash);
		$now=date(&quot;Y-m-d H:i:s&quot;);
		$query=&quot;INSERT INTO sessions (id,data,modified,hash) VALUES ($safe_id,$safe_data,'$now',$hash)
			ON DUPLICATE KEY UPDATE data=$safe_data, modified='$now',hash=$hash&quot;;
		try{
			$this-&gt;pdo-&gt;query($query);
		}catch(PDOException $e){
			return false;
		}
		return true;
	}
</pre>
<p>تابع دو تا آرگومان داره. اولی کلید جلسه هستش، دومی هم اطلاعات کل جلسه. ما باید اینو توی دیتابیس بنویسیم. فیلد id رو من به صورت یکتا تعریف کردم، و با توجه به اینکه هر بار و با هر refresh این تابع یه بار باید اطلاعات رو آپدیت کنه،‌ یا باید اول سطر قبلی رو پاک کنیم و سطر جدید رو بنویسیم، یا اینکه نه، خیلی ساده از کوئری <a href="http://dev.mysql.com/doc/refman/5.0/en/insert-on-duplicate.html">INSERT &#8230; ON DUPLICAE KEY UPDATE</a> استفاده کنیم. این کوئری خیلی کار رو راحت میکنه. اگه INSERT با خطای DUPLICATE KEY روبرو بشه، UPDATE رو انجام میده.<br />
دقت کنید که من زمان و اطلاعات بروزر رو هم همزمان توی دیتابیس قرار دادم.</p>
<p><strong>Read function</strong><br />
وقت برداشته :)‌ همه اینها واسه همین تابع هستش. این تابع هم فقط یکبار در ابتدای جلسه و بعد از تابع open فراخوانی میشه. آرگومانش فقط کلید جلسه هستش و باید محتوای جلسه به عنوان بازگشتی برگرده. فقط کافیه که اگه جلسه وجود داره، محتوایی که توی write نوشته شده برگرده اگه وجود نداره یا زمانش گذشته یا بروزرش مشکل داره فقط مقدار خالی رو برگردونه. تابع میشه یه چیزی مثل این :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_read($id){
		$query=&quot;SELECT * FROM sessions WHERE id= &quot;.$this-&gt;pdo-&gt;quote($id);
		try{
			$sth=$this-&gt;pdo-&gt;query($query);
		}catch(PDOException $e){
			return &quot;&quot;;
		}
		//Is there any???
		if ($sth-&gt;rowCount()!=1)
			return &quot;&quot;;
		$data=$sth-&gt;fetch();
		//Now its time to validate ...
		$pdo_time=strtotime($data['modified']);
		$pdo_hash=$data['hash'];

		//Check for time out and browser data
		if (time()-$pdo_time&gt;$this-&gt;time_out || strcasecmp($this-&gt;browser_hash,$pdo_hash)!=0)
			return &quot;&quot;;

		//anything is ok, return data
		return $data['data'];

	}
</pre>
<p>تابع خیلی سادست. اطلاعات رو میخونه، و بررسی میکنه که آیا اصلا اطلاعاتی هست؟ یا اینکه زمانش درسته؟ بروزرش درسته؟ و اگه همه درست بود محتوا رو پس میده.<br />
<strong>Destroy function</strong><br />
این تابع فقط زمانی فراخوانی میشه که شما تابع <a href="http://ir.php.net/manual/en/function.session-destroy.php">session_destroy</a> رو فراخوانی کنید (مثلا با خروج کاربر بهتره این تابع فراخوانی بشه) کلید جلسه بهش ارسال میشه و انتظار داره شما اون جلسه رو از بین ببرید. سادست :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_destroy($id){
		$safe_id=$this-&gt;pdo-&gt;quote($id);
		$query=&quot;DELETE FROM sessions WHERE id=$safe_id&quot;;
		echo $query;
		try{
			$this-&gt;pdo-&gt;query($query);
		}catch(PDOException $e){
			return false;
		}
		//$this-&gt;_my_session_gc($this-&gt;time_out);
		return true;
	}
</pre>
<p><strong>GC function</strong><br />
این تابع، قاعدتا باید هر از چند گاهی یک بار توسط PHP فراخوانی بشه، ولی تو تمام تست های من توی ۵-۶ سال گذشته حتی یکبار هم PHP اونرو فراخوانی نکرده :) ولی خوب من خودم معمولا کد destroy رو یه کم تغییر میدم و آخرش خودم تابع GC رو هم فراخوانی میکنم(خط کامنت شده تابع بالایی). شما هم مختارید که هر کاری دوست دارید انجام بدید. اینم بدنه تابع :</p>
<pre class="brush: php; title: ; notranslate">
	public function _my_session_gc($maxlifetime){
		//You can use your timeout instead of this.
		$date=time()-$maxlifetime;
		$date_str=date(&quot;Y-m-d H:i:s&quot;,$date);
		$query=&quot;DELETE FROM sessions WHERE modified&lt;$date&quot;;
		try{
			$this-&gt;pdo-&gt;query($query);
		}catch(PDOException $e){
			return false;
		}
		return true;
	}
</pre>
<p>GC مخفف Garbage Collector میشه، یه چیزی تو مایه های آشغال جمع کن.<br />
آرگومانش، از تنظیمات PHP هستش، زمانی که توی فایل PHP.ini قابل تغییره، ولی گفتم که، طبق تجربیات من اصولا این تابع فراخوانی نمیشه!!</p>
<p>اما حالا میرسیم به تابع سازنده کلاس که قراره همه کارها رو انجام میده :</p>
<pre class="brush: php; title: ; notranslate">
	function __construct(&amp;$pdo,$time_out=600,$salt=&quot;change this in your code&quot;){
		if (!$pdo)
			die(&quot;PDO must created.&quot;);
		$this-&gt;pdo=$pdo;
		$this-&gt;time_out=$time_out;
		$this-&gt;salt=$salt;
		$this-&gt;_calcHahs();

		session_set_save_handler(array(&amp;$this,&quot;_my_session_open&quot;),
								 array(&amp;$this,&quot;_my_session_close&quot;),
								 array(&amp;$this,&quot;_my_session_read&quot;),
								 array(&amp;$this,&quot;_my_session_write&quot;),
								 array(&amp;$this,&quot;_my_session_destroy&quot;),
								 array(&amp;$this,&quot;_my_session_gc&quot;)
								 );

	}

	private function _calcHahs(){
		$_ip=isset ($_SERVER['HTTP_CLIENT_IP'])?
					$_SERVER['HTTP_CLIENT_IP'] : &quot;UNKNOWN&quot;;
		$_ip.=isset ($_SERVER['HTTP_X_FORWARDED_FOR'])?
					$_SERVER['HTTP_X_FORWARDED_FOR'] : &quot;UNKNOWN&quot;;
		$_ip.=isset ($_SERVER['REMOTE_ADDR'])?
					$_SERVER['REMOTE_ADDR'] : &quot;UNKNOWN&quot;;

		$_agent = isset ($_SERVER['HTTP_USER_AGENT']) ?
					$_SERVER['HTTP_USER_AGENT'] : 'NO USER AGENT';

		$browser_data=$this-&gt;salt.$_ip.$_agent;
		$this-&gt;browser_hash=md5($browser_data);
	}
</pre>
<p>تابع _calcHash هم که برای محاسبه hash مربوط به IP و Browser هستش.</p>
<p>اما نکته‌‍ی مهم آخر، حتما، حتما و حتما در آغاز همه کدهای PHP خودتون،‌ اینکار رو انجام بدید، اگر نه این کد درست کار نمیکنه :</p>
<pre class="brush: php; title: ; notranslate">
	date_default_timezone_set(&quot;Asia/Tehran&quot;);
</pre>
<p><a href="http://ir.php.net/manual/en/timezones.php">البته TimeZone های دیگه رو هم میتونید اینجا ببینید</a>. اینطوری زمان تمام توابع با زمان ایران (یا هر جا شما دوست دارید) تنظیم میشه، خصوصا کار ما که زمان توش خیلی مهمه. طرز استفاده از این کد خیلی راحته :</p>
<pre class="brush: php; title: ; notranslate">
	date_default_timezone_set(&quot;Asia/Tehran&quot;);
	try{
		$pdo=new PDO('mysql:dbname=test;host=127.0.0.1','root','bita123');
	}catch (PDOException $e){
		echo &quot;failed : &quot; . $e-&gt;getMessage();
		die();
	}
	//Default php time out is :
	//$time_out=session_cache_expire()*60;
	$time_out=60*10; //600 sec
	$session=new MySessionHandler($pdo,$time_out,&quot;some-uniq-per-site-string&quot;);
	//Now its ok to play with session :)
	session_start();
	if (isset($_SESSION[&quot;test&quot;]))
		echo $_SESSION[&quot;test&quot;];
	else{
		echo &quot;test is not defined&quot;;
		$_SESSION[&quot;test&quot;]=&quot;Hello, I'm test session's value!&quot;;
	}
</pre>
<p>فقط یه نسخه از این کلاس بسازید و بعد جلسه رو شروع کنید. والسلام. حالا جلسات توی دیتابیس ذخیره میشن که خیلی امنتر از فایله. البته اگه اونم &#8230;. بی خیال بابا!!</p>
<p>&#8211;چقدر نوشتم&#8230;..<br />
&#8211; <a href="http://cyberrabbits.net/wp-content/uploads/2010/09/sess.php_.zip">اینم کد کلاس به صورت کامل</a></p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/485/save-session-in-db/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/485/save-session-in-db/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>جلسات PHP :‌ آره یا نه؟ مساله اینه.</title>
		<link>http://cyberrabbits.net/447/php-sessions-yes-or-no/</link>
		<comments>http://cyberrabbits.net/447/php-sessions-yes-or-no/#comments</comments>
		<pubDate>Thu, 26 Aug 2010 21:13:22 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[امنیت در PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[Session hijacking]]></category>
		<category><![CDATA[Session stealing]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=447</guid>
		<description><![CDATA[مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری نیست. بیشتر آزمایشه و اطلاع رسانی. من چیکار کنم آخه؟ وقتی اینقدر ساده میشه امنیت یه وب سایت رو به مخاطره انداخت! هدف من آموزش این نیست که چطور خرابکاری کنید، هدفم اینه که کمک کنم این مشکل برطرف بشه. بحث اصلی : جلسات معمولا&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/' rel='bookmark' title='CSRF یا چطور چرخ رو دوباره اختراع کردم'>CSRF یا چطور چرخ رو دوباره اختراع کردم</a> <small>امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد....</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><strong>مقدمه</strong><br />
باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری نیست. بیشتر آزمایشه و اطلاع رسانی. من چیکار کنم آخه؟ وقتی اینقدر ساده میشه امنیت یه وب سایت رو به مخاطره انداخت! هدف من آموزش این نیست که چطور خرابکاری کنید، هدفم اینه که کمک کنم این مشکل برطرف بشه.<br />
<strong>بحث اصلی : جلسات</strong><br />
معمولا جلسات، از اون چیزهاییه که تو تمام زبانهای برنامه نویسی تحت وب استفاده میشه. این جلسات، اطلاعاتی هستن که در طرف سرور ذخیره میشه، ولی کلید اونها در طرف کلاینت. این کلید معمولا یه رشته غیر قابل حدسه و به دو صورت رد و بدل میشه.<br />
روش اول از طریق لینکه، یعنی مثلا یه لینکی داریم به این صورت : </p>
<pre class="brush: plain; title: ; notranslate">

http://example.com/?sessid=zGNQE1DlHGFjDZLEfdeNK3rvRC7
</pre>
<p>وقتی این درخواست میرسه به PHP (من کاری به بقیه زبانها ندارم) خودش جلسه با این شماره (zGNQE1DlHGFjDZLEfdeNK3rvRC7) رو پیدا میکنه و بارگذاری میکنه. البته اگه باشه. اگه نباشه هم که خیلی ساده چشماشو میبنده و رد میشه. راه دوم اینه که کلید جلسه از طریق کوکی منتقل شه، یه جورایی محبوب تره این روش، علتشم اینه که لینک خراب نمیشه، لزومی هم نداره هر لینکی تو صفحه هست این اطلاعات بهش الصاق بشه. (شما باید وقت اجرا این کلید جلسه رو بگیرید و هر لینکی که خواستید ایجاد کنید اینو به انتهاش اضافه کنید ولی توی روش کوکی چون این اطلاعات با کوکی رد و بدل میشه نیاز نیست.)<br />
اگه به صورت لینک باشه، دیگه میشه گفت <a href="http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/">CSRF</a> که قبلا دربارش نوشتم پیش نمیاد، اما معایبشو گفتم. </p>
<p>حالا اگه یکی تو یه کامپیوتر دیگه،‌ به هر نحوی به این کلید دست پیدا کنه، در زمانی که شما هنوز آنلاین هستید و جلسه اعتبار داره بیاد و صفحه شما رو،‌ با این کلید باز کنه (یعنی اگه لینکه که با ایجاد لینک شبیه سازی شده و اگه کوکیه با ایجاد کوکی مورد نظر درخواستو شبیه سازی کنه، خیلی ساده میره تو حساب شما! ایجاد کوکی هم با <a href="http://getfirebug.com/">FireBug</a> و <a href="https://addons.mozilla.org/en-US/firefox/addon/6683/">FireCookie</a> مثل آب خوردنه!!)<br />
به همین علته که میگن هر وقت از پای صفحه خودتون بلند شدید، حتما logout کنید. فکر نکنید چون پشت کامپیوتر خودتونید همه چی آرومه!! نه، ممکنه همین الان یه نفر جلسه شما رو از اون طرف دنیا دزدیده باشه و داخل صفحه شما شده باشه!! با این logout جلسه تعطیل میشه و کلیدش بی ارزش میشه.و اینطوری اون آقا/خانم دزد،‌با خارج شدن شما،‌ ناکام میمونه، اما اگه اینکارو نکنید ممکنه تا مدتها همچنان فعال باقی باشه.</p>
<p>این که از وسط راه جلسه دزدیده بشه، فقط و فقط یه راه حل داره اونم استفاده از https هستش. یعنی در حالت عادی و غیر https راهی نیست که از دزدیدن کلید در میانه راه جلوگیری کنیم. به عبارتی ISP شما، و تمام کسانی که در مسیر ارتباط شما به سایت قرار گرفتن  با یه مانیتورینگ ساده میتونن اطلاعات جلسه رو بدزدن. خوب، اینو میتونید با یه هزینه اضافه امن کنید ولی آیا خطر این دزدیده شدن جلسه فقط همین جاست؟ یعنی اگه جلسه سالم به مقصد رسید :D اونوقت همه چی سر جاشه؟ خوب بالاخره رسیدم به اصل مطلب.<br />
<span id="more-447"></span><br />
جواب یه <strong>نه</strong> است. یکم بزرگتر از نه معمولی!! یه سایت معمولی، از یه سرور اشتراکی استفاده میکنه. نمیصرفه برای یه وبلاگ بری و یه VPS بگیری. خوب این یعنی ممکنه روی سرور شما چندین سایت دیگه موجود باشه. حالا یه کاری میکنیم. یه همچین فایل ایجاد میکنیم : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
  var_dump(glob(session_save_path().&quot;/sess_*&quot;));
</pre>
<p>روی سرورتون آپلود کنید، اجراش کنید و مطمئنا شگفت زده میشید.(فرض بر لینوکسه، واسه ویندوز \ بگذارید.) یه سرور در پیت دارم برای کارهای آزمایشی، اونجا حتی بک تیک هم بازه :)‌ وقتی این فایل روش اجرا شد، ۴ مگا فایل شد!! این یعنی چند تا کلید؟؟؟ خدا تا! فقط اگه روی suexec باشید کار نمیکنه.<br />
یه برنامه نوشتم که کل این فایلها رو یکی یکی بخونه، توی سرور ذخیره کنه .کلی طول کشید!!! انقدر که تا آخرش نرفتم و نصفه کاره لغوش کردم، تازه رسیده بود به ۳۰ درصد فایلها.(تازه بیشتر فایلها هم خالی بود، در حقیقت جلساتی که اصلا اطلاعاتی ذخیره نشده بود)<br />
زنده باد <a href="http://unixhelp.ed.ac.uk/CGI/man-cgi?grep">grep</a>. اول گشتم دنبال آدرس ایمیل کلی پیدا کردم. با حال زیاد توش بود. مثلا info@&#8230;&#8230;ir یا آدرسهایی شبیه این. یکی، نام کاربریش هم admin بود و میلشم آدرس سایت داشت.<br />
خوب سوتی داده شده بود. متوجه شدم که فایل مربوط به چه سایتیه، و جالب اینکه زده بودم به خال! جلسه هنوز فعال بود(یا شاید من با دسترسی به فایل باعث شده بودم PHP گول بخوره؟؟)، به راحتی وارد شدم. با <a href="http://cakephp.org/">CakePHP</a> بود. همین برام کافی بود، اومدم بیرون، تازه مثل یه کاربر خوب دکمه خروج رو زدم. یکی که اون موقع اینجا بود، گفت Screen shot بگیر ولی خوب اصلا هدفم این نیست که ثابت کنم.</p>
<p>همون یکی که اینجا بود میگفت دسترسی خوندن رو برای PHP ببندید یا از suexec استفاده کنید. اولی عملا ممکن نیست، PHP باید این فایلها رو بخونه، اگه نتونه بخونه که جلسه رو از کجا بارگذاری کنه؟؟ دومی خوبه، ایده بدی نیست، یعنی PHP شما تو فضای کاربری شما اجرا میشه، پس فایلها برای دیگران قابل خوندن نیست. اما هم کنده و هم رو ویندوز ممکن نیست (میگم ویندوز به درد نمیخوره!) در ثانی همه که این روش رو نمیپسندن(مثل من!)<br />
حالا چی؟ بریم داد بزنیم سر اونکسی که مدیر این فضاست و سرور رو فروخته؟ نه. دست کم این مشکل به راحتی توی PHP قابل حله. راه حل هم suexec و این چیزها نیست. فقط چند تا چیز رو باید رعایت کرد.<br />
<strong><br />
1-</strong> هرگز اطلاعات حساس رو داخل جلسه ذخیره نکنید. حساس اینجا رنج وسیعی داره،‌ من توی اون کاری که کردم ۶۴ تا آدرس ایمیل پیدا کردم (grep گفت،‌من نشمردم :) )،‌ فقط برای حدود ۳۰ درصد فایلها. ایمیل از اون اطلاعات حساسه. آدرس سایت مثل اینکه منو هدایت کرد به هدفم از اطلاعات حساسه (گرچه اگه اونم نبود راحت میتونستم همه سایتها رو که روی سرور بود پیدا کنم) رمز که دیگه واویلا. حتی  hash یا رمز گذاری شده هم نباید داخل جلسه ذخیره بشه. چه اشکالی داره اگه فقط id کاربر در دیتابیس ذخیره بشه و بعد فقط با یه کوئری اضافه اطلاعات هر بار از بانک اطلاعاتی خونده بشه؟؟؟<br />
<strong>۲-</strong>یه امکانی بگذارید که این جلسات بعد از یه مدتی بی استفاده بشن. توی جلسه یه متغیر ذخیره کنید،‌ حاوی آخرین ساعت دسترسی، اگه وقتی دوباره جلسه خواست بارگذاری بشه اون رو چک کنید از مثلا ۱۰ دقیقه بیشتر بود جلسه رو از بین ببرید. PHP اتوماتیک اینکار رو میکنه ولی روی یک سروری دیدم که تنظیم بود روی ۲ ساعت!! نگذاریدش به عهده خود PHP. گاهی هم این کار نمیکنه! چون PHP از طریق آخرین زمان دسترسی میفهمه عمر جلسه چقدره، اگه یکی بیاد و اینو دستکاری کنه چی؟ </p>
<p>&#8211; این حدس منه، ممکنه درست نباشه، ولی همین که من فایلهای جلسه رو دوباره خوندم تاریخ آخرین دسترسی اونها هم تغییر کرد، پس PHP هم گول خورد. باید امتحانش کنم (ولی قول دادم به خودم که فعلا نه!)<br />
علاوه بر این، میتونید مثلا IP کاربر، User Agent مربوط به مرورگرش (همه اطلاعاتی هستن که توی آرایه _SERVER ذخیره میشن) رو هم توی جلسه ذخیره کنید(البته به صورت رمزگذاری شده که اگه کسی محتوای جلسه رو دید به راحتی نتونه شبیه سازیش کنه، یا اینکه خیلی راحت از md5  استفاده کنید، IP و User Agent و یه عبارت الکی که به صورت ثابت باشه رو به یه رشته تبدیل کنید، بعد md5  اون رشته رو توی جلسه ذخیره کنید، وقت بارگذاری دوباره جلسه، دوباره این رشته رو ایجاد کنید و md5  اونو با اونی که توی جلسه هستش مقایسه کنید، اگه درست نبود جلسه رو از بین ببرید.)<br />
اینجوری جلسه تا حدی امن میشه حتی اگه فایلش توسط دیگران دیده بشه البته اگه شماره ۱ رو رعایت کنید. </p>
<p><strong>۴-</strong>شاید ایده بدی نباشه (من نمیپسندم چندان)‌ اطلاعات رو به صورت رمزگذاری شده توی جلسه ذخیره کنید. اطلاعات حساس رو که اصلا تو جلسه نگذارید (مورد ۱ که گفتم) اطلاعات غیر حساس هم رمز گذاری بشه.</p>
<p><strong>۵-</strong>حتما قابلیت خروج برای کاربر بگذارید و مدام تو صفحه ترغیبش کنید که کارش تموم شد خارج بشه نه که بروزر رو ببنده.</p>
<p><strong>سه</strong> نداره؟ میدونم. راه حل نهایی و بهترین راه حل همین ۳ هستش. استفاده از تابع <a href="http://php.net/manual/en/function.session-set-save-handler.php">session_set_save_handler</a> که قصد دارم پست بعدی رو در تکمیل این پست بنویسم و درباره همین تابع باشه.<br />
فعلا کلی کار ریخته رو سرم و همین که نشستم این مسخره بازیها رو انجام دادم و بعدش این پست رو نوشتم خودش کلیه!! </p>
<p><strong>یه نکته هم برای کسانی که سرور ها رو تنظیم میکنن</strong><br />
 تو یه بررسی رو چندین و چند سرور یه چیزی متوجه شدم که میتونه خیلی خطرساز بشه :<br />
۱- پوشه پیشفرض برای ذخیره جلسات استفاده شده، یعنی پوشه tmp سیستم عامل (بسته به ویندوز و یا لینوکس این فرق داره)<br />
۲-کاربر خیلی برنامه ها با کاربر PHP یکیه، یا اینکه تو یه Group هستند، مثلا برنامه ای که برای ارسال میل به کار میره. (تو ویندوز که دیگه بدتر، اصولا PHP به کل پوشه دسترسی داره و کاربر و گروه سرش نمیشه )<br />
ربطشو نفهمیدید؟؟؟ برنامه هایی مثل snedmail (یا معادلهاش تو ویندوز)‌ از همین پوشه tmp استفاده میکنن برای ذخیره اتچمنتها، خیلی برنامه های دیگه هم ممکنه اطلاعات حساسی اینجا ذخیره کنن. PHP که به اینجا دسترسی داشته باشه، میتونه به راحتی اطلاعات اونها رو هم بدزده. یعنی کافیه اون فایلی که بالاتر نوشتم یه کم دستکاری بشه تا &#8220;همه&#8221; فایلها رو نشون بده، نه فقط فایلهای جلسه رو. و این یعنی اینکه اطلاعات به راحتی در دسترس همه هستش.<br />
پیشنهادم اینه که پوشه های آپلود فایل و ذخیره جلسات رو تغییر بدید به یه پوشه اختصاصی که خودتون میسازید، طوری که tmp کاملا بی استفاده بشه برای PHP بعد پوشه tmp رو به صورت خاص برای PHP ممنوع کنید. اگه برنامه های دیگه هم قابلیت تغییر این پوشه رو دارن برای اونها هم عوض کنید این آدرسها رو. اونوقت دیگه هر برنامه دسترسی داره به یه پوشه برای کارهای خودش و دست کم از جانب برنامه های دیگه، خصوصا PHP در خطر نیست. البته، این کار برنامه رو از دست خودش در امان نگه نمیداره و مشکلی که در این پست مطرح شد همچنان به قوت خودش باقیه.<br />
&#8211; <strong>ای کسانی که جوملا نصب میکنید!!!</strong> وقت نصب، برنامه تمام تنظیماتی که شما انجام میدید (از اطلاعات دیتابیس گرفته تا کاربر ادمین&#8230;.)‌ همه توی جلسه ذخیره میشوند و هنگام پایان نصب هم این جلسه رو از بین نمیبره. (نشون به اونکه من خودم تو بررسی های بعدی اون فایل که بالاتر گفتم، یک فایل نصب جوملا هم پیدا کردم که کلی از زمان نصبش گذشته بود ) حتما به هر صورتی که میتونید این جلسه رو از بین ببرید،‌ بهتره کد نصب رو طوری دستکاری کنید که بعد از اتمام کار جلسه کاملا نابود بشه.<br />
&#8211; این پست در راستای روشنگری نوشته شده است و نه آموزش خرابکاری یا به لفظ مصطلح اما غلط :‌ هک. شما مسئول کار خودتان هستید و من مسئول کار خودم!<br />
&#8211; من عذاب وجدان دارم اساسی، همین که وارد سایت این بنده خدا شدم. نمیدونم چیکار کنم، میل کنم براش توضیح بدم؟ همین که دیدم وارد شد مثل آدمیزاد خروج رو زدم!! ولی &#8230;.<br />
&#8211; این پست زمانبندی شده هستش، جدید نیست ولی ترجیح دادم بلافاصله منتشر نشه.<br />
&#8211; به اون بابا ایمیل زدم و معذرت خواهی کردم، ایمیل زد و گفت ثابت کن :) من طراحیم نقص نداره و یه سری فحش هم پشت سرش&#8230;. خوب به هر حال نیازی نیست که ثابت بشه، اصولا مهم هم نیست. الانم با اون فحشها که داد بی حساب شدیم. </p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/447/php-sessions-yes-or-no/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/485/save-session-in-db/' rel='bookmark' title='ذخیره جلسات در پایگاه داده'>ذخیره جلسات در پایگاه داده</a> <small>دفعه قبل، درباره دزدیدن جلسه صحبت کردم، هنوز منتشر نشده...</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/' rel='bookmark' title='CSRF یا چطور چرخ رو دوباره اختراع کردم'>CSRF یا چطور چرخ رو دوباره اختراع کردم</a> <small>امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد....</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/447/php-sessions-yes-or-no/feed/</wfw:commentRss>
		<slash:comments>10</slash:comments>
		</item>
		<item>
		<title>CSRF یا چطور چرخ رو دوباره اختراع کردم</title>
		<link>http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/</link>
		<comments>http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/#comments</comments>
		<pubDate>Fri, 13 Aug 2010 14:31:59 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[امنیت در PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[فوت کوزه گری]]></category>
		<category><![CDATA[Cross-Site Request Forgery]]></category>
		<category><![CDATA[CSRF]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=430</guid>
		<description><![CDATA[امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد. داشتم یه برنامه ساده CRUD مینوشتم، یعنی یه برنامه ای که یه سری فیلد رو توی دیتابیس بنویسه، ویرایش داشته باشه و حذف کنه و در نهایت نشون بده. این برنامه فقط برای نمایش برای یه نفر بود که میخواست یه چیزی یاد بگیره :دی&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>امروز یه اتفاق خیلی ساده، یه درس بزرگ بهم داد. داشتم یه برنامه ساده CRUD مینوشتم، یعنی یه برنامه ای که یه سری فیلد رو توی دیتابیس بنویسه، ویرایش داشته باشه و حذف کنه و در نهایت نشون بده.<br />
این برنامه فقط برای نمایش برای یه نفر بود که میخواست یه چیزی یاد بگیره :دی خیلی ساده.<br />
مساله لینک حذف بود، این لینک :‌</p>
<pre class="brush: plain; title: ; notranslate">

http://localhost/admin.php?del=10
</pre>
<p>در حقیقت این میومد و ردیف شماره ۱۰ رو حذف میکرد. البته توی admin.php بررسی میشد که آیا کاربر اجازه دسترسی داره یا نه. اگه اجازه حذف داشت اونوقت حذف رو انجام میداد. به نظر همه چی درست بود، ولی یه اتفاق باعث شد بفهمم یه جای کار میلنگه.<br />
این لینک رو اتفاقی کپی کردم توی کلیپ برد. خودمم همزمان میخواستم یه جایی تو یه پیغام شخصی فروم، یه عکس بگذارم. منتها اشتباها این لینک کپی شد به جای لینک عکس اصلی. از اونجایی که دقت منم در حد تیم ملیه :)‌ عکس رو با بدون اینکه آدرسش رو چک کنم گذاشتم توی پیغام، دکمه پیشنمایش رو زدم و &#8230;</p>
<p>باقیش معلومه. فیلد ۱۰ حذف شد، و من حتی متوجه نشده بودم تا یه کم بعد رفتم دوباره سراغ برنامه.<br />
خوب این یعنی که با اینکه من امنیت رو کاملا حفظ کرده بودم توی فایل admin.php ولی این امنیت خیلی راحت به هم ریخت با وادار شدن من برای دیدن یه عکس. خوب اگه اینکار رو یه نفر دیگه انجام میداد و یه پیغام حاوی لینک حذف برای من میفرستاد که مثلا به صورت عکس جاسازی شده بود،‌ خیلی راحت به مقصودش میرسید. من اجازه حذف دارم،‌ و فقط کافیه اون لینک یه بار توسط من ویزیت بشه، به هر صورتی. </p>
<p>راه چاره چیه؟؟ اینکه از متد GET استفاده نکنیم.یا اینکه تو صفحه دوم هم یه سوال بپرسیم و بعد از طریق متد پست حذف نهایی رو توی مرحله دوم انجام بدیم، یه کمی به نظر من اعصاب خورد کنه.<br />
البته من یه پیشنهاد هم دارم، که فکر میکنم بد نباشه. یه کلمه رندوم ایجاد بشه تو هر جلسه برای هر کاربر، مثلا به اسم $salt . از هر جلسه به جلسه دیگه متفاوت باشه. به جای لینک بالا، از این لینک استفاده بشه (یا چیزی شبیه اون): </p>
<pre class="brush: plain; title: ; notranslate">

http://localhost/admin.php?del=10&#038;salt=$slat
</pre>
<p>وقت حذف علاوه بر بررسی سطح دسترسی کاربر این &#8220;نمک&#8221; هم بررسی بشه :)‌ اگه اونم با متغییری که تو جلسه ذخیره شده برابر بود کاری که باید انجام بشه،‌انجام بشه.<br />
اینطوری، کسی نمیتونه لینک رو درست کنه. (مگه اینکه یه جوری جلسه مدیر رو بدزده، که خوب در اون صورت دسترسی خود مدیر رو داره و دیگه لازم نیست یه همچین کارهایی رو انجام بده، خودش دسترسی داره و میتونه هر کاری میخواد بکنه!!)</p>
<p>پ.ن : به این روش میگن<a href="http://en.wikipedia.org/wiki/Cross-site_request_forgery"> CSRF یا Cross-Site Request Forgery </a>که جالب اینه که خودم هم میدونستم ولی هیچوقت دقت نکرده بودم یعنی همیشه طور دیگه ای بهش نگاه میکردم، اینکه مثلا یه سایت خطرناک رو به خورد کاربر بدن نه سایتی که اصلا خطری نداره و اتفاقا کاربر اجازه دسترسی بهش رو داره و از دسترسی کاربر سواستفاده بشه. این روش هم من دوباره کشف کردم، قبل از من بازم کشف شده بوده :D و من فقط دوباره چرخ رو اختراع کردم!!<br />
بیشتر درباره این قضیه که بگردید دوباره منابع بهتری رو پیدا میکنید و راه حلهای دقیقتر و بهتری. </p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/447/php-sessions-yes-or-no/' rel='bookmark' title='جلسات PHP :‌ آره یا نه؟ مساله اینه.'>جلسات PHP :‌ آره یا نه؟ مساله اینه.</a> <small>مقدمه باور کنید دست خودم نبود!!! هدفم هم اصلا خرابکاری...</small></li>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/430/crsf-or-how-i-invent-wheel-again/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Mysql Menu قسمت سوم</title>
		<link>http://cyberrabbits.net/411/mysql-menu-part-3/</link>
		<comments>http://cyberrabbits.net/411/mysql-menu-part-3/#comments</comments>
		<pubDate>Tue, 03 Aug 2010 11:32:44 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=411</guid>
		<description><![CDATA[چند وقت پیش در باره منو و طریقه ایجاد آن با mysql نوشتم. منظورم بیشتر روش دومی بود که معرفی کردم. خودم تا همین روزها نیاز نشده بود که از این منو استفاده کنم، تا امروز برای کاری ار همان پست استفاده کردم. (این وبلاگ بیشتر از هر کسی گویا به درد خودم میخورد) میخواستم&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/181/mysql-menu-part2/' rel='bookmark' title='MySQL , Menu قسمت دوم'>MySQL , Menu قسمت دوم</a> <small>این پست رو برای اضافه کردن یه سری قابلیت جدید...</small></li>
<li><a href='http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/' rel='bookmark' title='Menu، MySQL و کلا هر چی با M شروع میشه'>Menu، MySQL و کلا هر چی با M شروع میشه</a> <small>اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از...</small></li>
<li><a href='http://cyberrabbits.net/108/ajax-jquery-php-part2/' rel='bookmark' title='Ajax &#8211; jQuery &#8211; PHP قسمت دوم'>Ajax &#8211; jQuery &#8211; PHP قسمت دوم</a> <small>تو قسمت قبلی یه توضیح مختصر درباره اصل برنامه دادم،‌خیلی...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>چند وقت پیش در باره <a href="http://cyberrabbits.net/1388/11/25/mysql-menu-part2/">منو و طریقه ایجاد آن با mysql نوشتم</a>. منظورم بیشتر روش دومی بود که معرفی کردم. خودم تا همین روزها نیاز نشده بود که از این منو استفاده کنم، تا امروز برای کاری ار همان پست استفاده کردم. (این وبلاگ بیشتر از هر کسی گویا به درد خودم میخورد) میخواستم از همان کوئری ها استفاده کنم و یک ساختار تشکیل شده از ul و li های تو در تو درست کنم. اول سعی کردم اطلاعات را داخل آرایه بریزم که بعد به صورت html دربیاورم که متوجه شدم دارم لقمه دور سر میچرخانم.<br />
با توجه به اینکه ساختار منو (لطفا پست MySQL , Menu قسمت دوم رو حتما بخونید) دقیقا شبیه ساحتار تو در توی html هست میشه از این خاصیت استفاده کرد :)‌ یعنی اونجا همه منو های یه سمت چپ داشتن یه سمت راست، تو html هم هر تگی یه سمت چپ داره یه سمت راست. من میخوام یه ساختار منوی ساده با ul و li به وجود بیارم طوری که :</p>
<ul>
<li>عنصر دور تا دور فقط یه ul داره. </li>
<li>
عناصری که زیر مجموعه ندارن دورشون li میفته
</li>
<li>
عناصری که زیر مجموعه دارن هم li میخوان هم ul .
</li>
<li> اگه آیتمی هیچ زیر منو نداشت اونوقت متن اون آیتم هم نشون داده بشه (شما اینو خودتون میتونید گسترش بدید، یا برای همه منوها آیتم رو نشون بدید، من ترجیح دادم ساده‌تر کار کنم.)
</li>
</ul>
<p>نتیجه خیلی سادست. فقط باید حالات مختلف رو بنویسم.ساختار دیتابیس اینه (مثل مثال قبلی ، فقط فیلد name به صورت unique در اومده برای اینکه توی کد باید به یه چیزی تکیه کرد ;) ) </p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE IF NOT EXISTS `adv_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) NOT NULL,
  `data` varchar(255) NOT NULL,
  `lside` int(11) NOT NULL,
  `rside` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=MyISAM;
</pre>
<p>اینبار میخوام از PDO استفاده کنم برای اتصال به mysql . ایده خیلی سادست. فقط در نظر بگیرید که همونطور که قبلا گفتم (تو پست قبلی در این مورد)‌ هر منو یه چپ داره یه راست.<br />
خوب چی میشه اگه سمت چپ رو شروع تگ در نظر بگیریم و سمت راست رو پایان تگ؟ چی میشه اگه یه آرایه ایجاد کنیم به تعداد دو برابر آیتمها، با ایندکس ۱ تا دوبرابر آیتمها، بعد تو آرایه ایندکس سمت چپ تگ شروع html رو بذاریم (مثلا یه li یا یه ul بعد یه li یا هر چیز دیگه ای) و در ایندکس سمت راست هم دقیقا پایان همون تگ سمت راست رو (مثلا اینبار /li )، فقط کافیه بدونیم که مثلا آیتم اگه زیر مجموعه نداشت فقط li میخواد (که این فقط در صورتیه که سمت چپی یکی کمتر از سمت راستی باشه)  اگه منوی دور تا دوره فقط یه ul میخواد (که خوب این هم سمت چپش همیشه ۱ میشه)‌ و در غیر این صورت ul و li با هم.<br />
خوب این خیلی ساده ایتمهای توی دیتابیس رو تبدیل میکنه به یه ساختار html و کافیه که اینبار فقط این آرایه رو به هم بچسبونیم و در خروجی نشون بدیم.<br />
کد زیر کل این کارها رو به ساده ترین وجهی انجام میده، ولی باز هم جای کار داره، خیلی زیاد هم جای کار داره.<br />
<span id="more-411"></span></p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/*
 * Work on this structure :
CREATE TABLE IF NOT EXISTS `adv_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) NOT NULL,
  `data` varchar(255) NOT NULL,
  `lside` int(11) NOT NULL,
  `rside` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `name` (`name`)
) ENGINE=MyISAMs
 */
class MyMenu {
	private $_pdo;

	public function __construct($pdo){
		$this-&gt;_pdo=$pdo;
	}

	public function addTopMenu($name,$data=''){
		try{
			$query=&quot;SELECT MAX(rside) FROM adv_menu&quot;;
			$sth=$this-&gt;_pdo-&gt;query($query);
			$max=intval($sth-&gt;fetchColumn(0));

			$q_name=$this-&gt;_pdo-&gt;quote($name);
			$q_data=$this-&gt;_pdo-&gt;quote($data);
			$left=$max+1;
			$right=$max+2;
			$query=&quot;INSERT INTO adv_menu (name,data,lside,rside) VALUES ($q_name,$q_data,$left,$right)&quot;;
			return $this-&gt;_pdo-&gt;exec($query);
		}catch (PDOException $e){
			echo &quot;failed : &quot; . $e-&gt;getMessage();
		}
	}

	public function insertInside($root,$name,$data){
		try {
			$query=&quot;SELECT rside FROM adv_menu WHERE name = &quot;.$this-&gt;_pdo-&gt;quote($root);
			$sth=$this-&gt;_pdo-&gt;query($query);
			$lside=intval($sth-&gt;fetchColumn(0));

			if (!$lside) return false; //Fail! no root menu

			//Its time to update...
			$query=&quot;UPDATE adv_menu SET lside=lside+2 WHERE lside&gt;=$lside&quot;;
			$this-&gt;_pdo-&gt;exec($query);
			$query=&quot;UPDATE adv_menu SET rside=rside+2  WHERE rside&gt;=$lside&quot;;
			$this-&gt;_pdo-&gt;exec($query);	

			//Now real insert..
			$rside=$lside+1;
			$q_name=$this-&gt;_pdo-&gt;quote($name);
			$q_data=$this-&gt;_pdo-&gt;quote($data);
			$query=&quot;INSERT INTO adv_menu (name,data,lside,rside) VALUES ($q_name,$q_data,$lside,$rside)&quot;;
			return $this-&gt;_pdo-&gt;exec($query);
		}catch (PDOException $e){
			echo &quot;failed : &quot; . $e-&gt;getMessage();
		}
	}

	public function createMenu($parentTags,$parentClasses,$subParentTags,
						$subParentClass,$itemTags,$itemClass){
		//Parent tag ..
		$parentTagStart=$parentTagEnd='';
		foreach ($parentTags as $id=&gt;$Tag){
			$ClassFull=($parentClasses[$id])?&quot; class='$parentClasses[$id]' &quot;:'';
			$parentTagStart.=&quot;&lt;$Tag$ClassFull&gt;&quot;;
			$parentTagEnd=&quot;&lt;/$Tag&gt;&quot;.$parentTagEnd;
		}
		//Sub parent tag ..
		$subParentTagStart=$subParentTagEnd='';
		foreach ($subParentTags as $id=&gt;$Tag){
			$ClassFull=($subParentClass[$id])?&quot; class='$subParentClass[$id]' &quot;:'';
			$subParentTagStart.=&quot;&lt;$Tag$ClassFull&gt;&quot;;
			$subParentTagEnd=&quot;&lt;/$Tag&gt;&quot;.$subParentTagEnd;
		}

		//Item tag ..
		$itemTagStart=$itemTagEnd='';
		foreach ($itemTags as $id=&gt;$Tag){
			$ClassFull=($itemClass[$id])?&quot; class='$itemClass[$id]' &quot;:'';
			$itemTagStart.=&quot;&lt;$Tag$ClassFull&gt;&quot;;
			$itemTagEnd=&quot;&lt;/$Tag&gt;&quot;.$itemTagEnd;
		}
		$query=&quot;SELECT item.*,(COUNT(parent.name) - 1) AS depth
				FROM adv_menu AS item, adv_menu AS parent
				WHERE (item.lside
				BETWEEN parent.lside
				AND parent.rside)
				GROUP BY item.name
				ORDER BY item.lside&quot;;
		$sth=$this-&gt;_pdo-&gt;query($query);
		$result=array();
		foreach ($sth as $item){
			//Is this a parent?
			if ($item['depth']==0){
				$result[$item['lside']]=$parentTagStart;
				$result[$item['rside']]=$parentTagEnd;
			}elseif (intval($item['lside']+1) &lt; (intval($item['rside']))){//Sub menu but also parent
				$result[$item['lside']]=$subParentTagStart;
				$result[$item['rside']]=$subParentTagEnd;
			}else{//Simple sub menus, use data to create link or more..
				$result[$item['lside']]=$itemTagStart.$item['data'];
				$result[$item['rside']]=$itemTagEnd;
			}

		}

		ksort($result);
		return implode(&quot;\n&quot;,$result);
	}
}

try{
	$pdo=new PDO('mysql:dbname=test;host=127.0.0.1','root','bita123');
}catch (PDOException $e){
	echo &quot;failed : &quot; . $e-&gt;getMessage();
	die();
}
$menu=new MyMenu($pdo);
/*
$menu-&gt;addTopMenu(&quot;Root&quot;,&quot;Root data&quot;);
$menu-&gt;insertInside(&quot;Root&quot;,&quot;Home&quot;,&quot;Home data&quot;);
$menu-&gt;insertInside(&quot;Root&quot;,&quot;Page&quot;,&quot;Page data&quot;);
$menu-&gt;insertInside(&quot;Page&quot;,&quot;PHP&quot;,&quot;PHP data&quot;);
$menu-&gt;insertInside(&quot;PHP&quot;,&quot;GTrans&quot;,&quot;GTrans data&quot;);
$menu-&gt;insertInside(&quot;PHP&quot;,&quot;OS&quot;,&quot;OS data&quot;);
$menu-&gt;insertInside(&quot;Page&quot;,&quot;Delphi&quot;,&quot;Delphi data&quot;);
$menu-&gt;insertInside(&quot;Root&quot;,&quot;Articles&quot;,&quot;Articles data&quot;);
$menu-&gt;insertInside(&quot;Articles&quot;,&quot;SQL&quot;,&quot;SQL data&quot;);
$menu-&gt;insertInside(&quot;Articles&quot;,&quot;Ajax&quot;,&quot;Ajax data&quot;);
$menu-&gt;insertInside(&quot;Ajax&quot;,&quot;Part1&quot;,&quot;Part1 data&quot;);
$menu-&gt;insertInside(&quot;Ajax&quot;,&quot;Part2&quot;,&quot;Part2 data&quot;);

*/
echo $menu-&gt;createMenu(			array('ul'),array('parent'),
								array('li','ul'),array('sub-parent','parent-sub'),
								array('li'),array('item')
						);
</pre>
<p>البته این یه کلاس کامله برای ثبت منو البته قابلیت حذف رو براش ننوشتم چون وقت نبود :)+</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/411/mysql-menu-part-3/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/181/mysql-menu-part2/' rel='bookmark' title='MySQL , Menu قسمت دوم'>MySQL , Menu قسمت دوم</a> <small>این پست رو برای اضافه کردن یه سری قابلیت جدید...</small></li>
<li><a href='http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/' rel='bookmark' title='Menu، MySQL و کلا هر چی با M شروع میشه'>Menu، MySQL و کلا هر چی با M شروع میشه</a> <small>اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از...</small></li>
<li><a href='http://cyberrabbits.net/108/ajax-jquery-php-part2/' rel='bookmark' title='Ajax &#8211; jQuery &#8211; PHP قسمت دوم'>Ajax &#8211; jQuery &#8211; PHP قسمت دوم</a> <small>تو قسمت قبلی یه توضیح مختصر درباره اصل برنامه دادم،‌خیلی...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/411/mysql-menu-part-3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>ارسال mail از طریق Zend Framework</title>
		<link>http://cyberrabbits.net/372/mail-for-zend-framework/</link>
		<comments>http://cyberrabbits.net/372/mail-for-zend-framework/#comments</comments>
		<pubDate>Mon, 12 Jul 2010 20:37:12 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend Framework]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[SendMail]]></category>
		<category><![CDATA[zf]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/1389/04/22/</guid>
		<description><![CDATA[لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری هستن! Zend Framework انتخاب من برای یک فریم ورک برای کار با PHP هستش. اینبار میخوام درباره چگونگی ارسال ایمیل به صورتی که بشه براش قالب ایجاد کرد بنویسم. این کد رو من خیلی وقت نیست که نوشتم. اگه مشکل داشت خوشحال میشم برام بنویسید&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/811/zend-translate-and-gnugettext/' rel='bookmark' title='Zend_Translate و GnuGetText'>Zend_Translate و GnuGetText</a> <small>یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم،...</small></li>
<li><a href='http://cyberrabbits.net/580/seo-friendly-url/' rel='bookmark' title='آدرس دهی درست'>آدرس دهی درست</a> <small>یکی از دغدغه های فعلی طراحان وب، ایجاد لینک های...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><ins datetime="2010-07-12T20:26:51+00:00">لطفا کامنتهای داخل کدها رو هم بخونید!! بعضیاشون واقعا ضروری هستن!</ins></p>
<p>Zend Framework انتخاب من برای یک فریم ورک برای کار با PHP هستش. اینبار میخوام درباره چگونگی ارسال ایمیل به صورتی که بشه براش قالب ایجاد کرد بنویسم. این کد رو من خیلی وقت نیست که نوشتم. اگه مشکل داشت خوشحال میشم برام بنویسید تا بدونم.<br />
برای استفاده باید چند تا کار انجام بدید. اول اینکه توی فایل تنظیمات برنامه خودتون یه سری اطلاعات اضافه کنید. اگه از ini استفاده میکنید اینو به فایل تنظیمات اضافه کنید : </p>
<pre class="brush: plain; title: ; notranslate">
mail.from= &quot;you@yoursite.com&quot;
mail.name= &quot;Your name&quot;
mail.templatePath=APPLICATION_PATH &quot;/../../layouts/mails&quot;
mail.host=imap.yoursite.com
mail.port=25
mail.param.auth=login
mail.param.username=you@yoursite.com
mail.param.password=yourpassword
</pre>
<p>اینو توی بخش مناسب بنویسید مثلا برای development توی بخش development و&#8230; فقط مساله templatePath هست که اونم آدرس رو عوض کنید با آدرسی که میخواید view های مربوط به ایمیل قرار بگیره. (این رو قطعا باید بر اساس پروژه خودتون عوض کنید)<br />
گام بعدی اینه که تنظیمات رو بخونید. اینجوری (معمولا توی bootstrap): </p>
<pre class="brush: php; title: ; notranslate">
//Load yor config CHANGE PATH TO YOUR CONFIG FILE!!!!!!
$config=new Zend_Config_Ini(APPLICATION_PATH . '/../../configs/application.ini',APPLICATION_ENV);
Zend_Registry::set('config',$config);	

//Do your stuff......

//Now its time to setup mail system :D
$config=Zend_Registry::get('config');
$mailParams=$config-&gt;mail-&gt;toArray();
Zend_Registry::set('mail_params',$mailParams);
</pre>
<p>شکه نشید ولی تا الان هیچ کاری انجام نشده :)‌ حالا وقت ایجاد Cybits_Utils_View_Helper_SendMail هستش. توی فولدر library چند تا فولدر باید درست شه :<br />
<span id="more-372"></span><br />
اول Cybits داخلش Utils داخلش View داخلش Helper  . این تو در تو بودن روش کار منه شما میتونید ساده ترش کنید اونوقت اسم کلاس باید عوض شه. یه فایل بسازید به اسم SendMail.php توی پوشه Helper و این کد رو داخلش بگذارید : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/**
 *
 * @author f0rud
 * @version
 */
require_once 'Zend/Loader/PluginLoader.php';
require_once 'Zend/Controller/Action/Helper/Abstract.php';

/**
 * SendMail Action Helper
 *
 * @uses actionHelper Cybits_Mail_View_Helper
 */
class Cybits_Utils_View_Helper_SendMail extends Zend_Controller_Action_Helper_Abstract {
	/**
	 * @var Zend_Loader_PluginLoader
	 */
	public $pluginLoader;

	/**
	 * Constructor: initialize plugin loader
	 *
	 * @return void
	 */
	public function __construct() {
		$this-&gt;pluginLoader = new Zend_Loader_PluginLoader ( );
	}

	/**
	 * Strategy pattern: call helper as broker method
	 */
	public function SendMail($mailParams) {
		if (!isset($mailParams['template']) ||
			!isset($mailParams['to']) ||
			!isset($mailParams['subject']))
			return false;
		//Build a new view
		$view = new Zend_View;
		$params=Zend_Registry::get('mail_params');

		$view-&gt;setScriptPath($params['templatePath']);

		foreach($mailParams as $key=&gt;$value){
			$view-&gt;assign($key,$value);
		}

		$body = $view-&gt;render($mailParams['template']);
		$mail = new Zend_Mail();
		$transport = new Zend_Mail_Transport_Smtp($params['host'], $params['param']);
		Zend_Mail::setDefaultTransport($transport);
		$mail-&gt;setBodyText($body)
			 -&gt;setBodyHtml($body)
		     -&gt;setFrom($params['from'],$params['name'])
		     -&gt;addTo($mailParams['to'])
		     -&gt;setSubject($mailParams['subject']);
		// Try to send the email, or log the exception
		try {
		   $mail-&gt;send();
		   return true;
		}catch (Exception $e){
			//If you setup a working logger uncomment this lines :
			//$logger=Zend_Registry::get('logger');
			//$logger-&gt;log($e-&gt;getMessage(),Zend_Log::ERR);
			return false;
		}

	}
}
</pre>
<p>خوب گام آخر (آخریه قول میدم!!) این رو باید به view هاتون اضافه کنید. توی کدتون معمولا اینکار انجام میشه. اگه نشده توی bootstrap فایلتون این کد رو اضافه کنید :</p>
<pre class="brush: php; title: ; notranslate">
$view= new Zend_View();
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
$view-&gt;addHelperPath('Cybits/Utils/View/Helper/','Cybits_Utils_View_Helper');
//Add other helper here ...

$viewRenderer-&gt;setView($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
</pre>
<p>حالا همه چی آمادست. یه view بسازید توی فولدری که توی تنظیمات معرفی کردید. یادتون باشه این میله، نه html کامل . میتونید تست کنید، خیلی از المانها درست کار میکنن. مثلا من این view رو برای ارسال میل ایجاد کردم : </p>
<pre class="brush: php; title: ; notranslate">
//file name is : recover.phtml
&lt;table style=&quot;border : 1px dashed skyblue;&quot;&gt;
	&lt;tr&gt;
		&lt;th&gt;Forgot password &lt;/th&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;Some one request to reset your password. If that was you, click
		on this link if not , just forget it :)
		&lt;a href=&quot;&lt;?=$this-&gt;recoverLink?&gt;&quot;&gt;&lt;?=$this-&gt;recoverLink?&gt;&lt;/a&gt;
		&lt;/td&gt;
	&lt;/tr&gt;
	&lt;tr&gt;
		&lt;td&gt;&lt;small&gt;&lt;a href=&quot;&lt;?=ZF_HOST?&gt;&quot; title=&quot;Our site&quot;&gt;&lt;?=ZF_HOST?&gt;&lt;/a&gt;&lt;/small&gt;&lt;/td&gt;
	&lt;/tr&gt;
&lt;/table&gt;
</pre>
<p>ثابت ZF_HOST آدرس سایته(من معمولا تو همه پروژه هام همچین ثابتی رو تعریف میکنم به درد میخوره). مثلا http://cyberrabbits.net بعد موقع فراخوانی همین (از داخل Controller ):</p>
<pre class="brush: php; title: ; notranslate">
if ($this-&gt;view-&gt;SendMail(array(
	'template'=&gt;'recover.phtml',
	'to'=&gt;$usermail,
	'subject'=&gt;'Forgot password',
	'recoverLink'=&gt;ZF_HOST.'/user/recover/id/'.$secret
	))) $this-&gt;view-&gt;status='SEND_OK'; //Send OK
else $this-&gt;view-&gt;status='SEND_FAIL';//Send fail
</pre>
<p>اگه داخل view باشید هم میتونید به جای this->view->SendMail بزنید this->SendMail هر آرگومانی هم که میخواید به view مربوط به میل بفرستید خیلی ساده میتونید تو آرگومان بنویسیدش. (متغیر recoverLink رو توی مثال من ببینید) </p>
<p>خداییش این کد تقریبا ۵ ساعت وقت منو گرفته!!! </p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/372/mail-for-zend-framework/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/811/zend-translate-and-gnugettext/' rel='bookmark' title='Zend_Translate و GnuGetText'>Zend_Translate و GnuGetText</a> <small>یکی از اصولی که سعی میکنم هنگام طراحی رعایت کنم،...</small></li>
<li><a href='http://cyberrabbits.net/580/seo-friendly-url/' rel='bookmark' title='آدرس دهی درست'>آدرس دهی درست</a> <small>یکی از دغدغه های فعلی طراحان وب، ایجاد لینک های...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/372/mail-for-zend-framework/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>گراواتار</title>
		<link>http://cyberrabbits.net/313/gravatar/</link>
		<comments>http://cyberrabbits.net/313/gravatar/#comments</comments>
		<pubDate>Mon, 03 May 2010 18:10:52 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[متفرقه]]></category>
		<category><![CDATA[Gravatar]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[PHPClasses]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[xmlrpc]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=313</guid>
		<description><![CDATA[گراواتار سیستمیه که همه ی کسانی که با WordPress سر و کار دارن میشناسنش. این سیستم به شما امکان میده یه آواتار رو ایجاد کنید، و از این آواتار در همه سیستمهایی که از گراواتار پشتیبانی میکنن استفاده کنید. اما گراواتار چطوری کار میکنه؟ خیلی ساده، شما اونجا یه اکانت میسازید و میتونید بیشتر از&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/885/string-array-php/' rel='bookmark' title='رشته ها و آرایه ها PHP'>رشته ها و آرایه ها PHP</a> <small>امروز درگیر یک کد بودم شبیه این : به نظر...</small></li>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><a href="http://gravatar.com">گراواتار</a> سیستمیه که همه ی کسانی که با WordPress سر و کار دارن میشناسنش. این سیستم به شما امکان میده یه آواتار رو ایجاد کنید، و از این آواتار در همه سیستمهایی که از گراواتار پشتیبانی میکنن استفاده کنید. اما گراواتار چطوری کار میکنه؟<br />
خیلی ساده، شما اونجا یه اکانت میسازید و میتونید بیشتر از یک میل رو هم به یه اکانت نسبت بدید و هم میتونید که برای هر ایمیل خودتون یه آواتار جداگانه ایجاد کنید.<br />
گراواتار یه سیستم خیلی ساده داره. شما آدرس ایمیل شخصی رو که میخواید تصویرشو بگیرید رو به حروف کوچک تبدیل میکنید،‌ بعد آدرس ایمیل رو به صورت md5 درمیارید و همین. دیگه آدرس عکس رو دارید، در حقیقت لینک آدرس عکس رو توی PHP میتونید اینجوری بدست بیارید : </p>
<pre class="brush: php; title: ; notranslate">
$email = &quot;someone@somewhere.com&quot;;
$default = &quot;http://www.somewhere.com/homestar.jpg&quot;;
$size = 40;
$rate = 'g';
$grav_url = &quot;http://www.gravatar.com/avatar/&quot; . md5( strtolower( $email ) ) .
&quot;?default=&quot; . urlencode( $default ) .
&quot;&amp;size=&quot; . $size .
&quot;&amp;r=&quot; . $rate;

echo &quot;&lt;img src=\&quot;$grav_url\&quot; alt=\&quot;\&quot; /&gt;&quot;;
</pre>
<p>سه تا مقدار default (که آدرس عکس پیشفرضه شماست که اگه نبود اون عکس نمایش داده بشه) و size که اندازه عکس مورد نظر شماست (که اندازه یه ظلع مربع عکسه) که بیشتر از ۵۱۲ هم نمیتونه باشه. (باشه هم مهم نیست، 512 برمیگردونه)‌ و rate (که میگه سایت شما جزو چه دسته ای هست، G ، PG ،R ،X ) که هر سه اختیاری هستن.<br />
خوب، این روش کلی مزیت داره. مزیتاش یکی همین یه دست بودن آواتار کاربر همه جاست. از نظر ریاضیاتی میشه ثابت کرد تعداد کلماتی که md5 برابر داشته باشن بی نهایته :)‌ &#8211; تعداد حالت های مختلف md5 ، محدوده. یعنی ۲ به توان ۱۲۸ حالت مختلف. ولی تعداد کلمات چند تاست؟ بی نهایت. خوب یعنی بی نهایت کلمه هست که md5 یکسان داشته باشه، ولی خوب چقدر احتمال داره که دو تا ایمیل یک md5 داشته باشن؟ به نظر من یه احتمال نزدیک به صفر &#8211; پس این زیاد مهم نیست. ولی با این روش،‌آدرس لینک نمیتونه ایمیل رو لو بده، و رباتهای مزاحم نمیتونن که از طریق عکسهای گراواتار، آدرس ایمیل رو بگیرن. کاربر هم میتونه یه جا عکس رو عوض کنه و کلی جای دیگه این عکس رو داشته باشه. این سیستم، جدیدا xmlrpc رو هم پیاده سازی کرده که امکانات بیشتری میتونه به شما بده (طراح های WordPress علاقه زیادی به xmlrpc دارن ولی من به شخصه json رو ترجیح میدم که از طریق javascript هم به راحتی قابل دسترسیه. نه این که xmlrpc نیست، ولی json خیلی راحت تره) کلاس PHP هم براش پیاده سازی کردن و توی<a href="http://www.phpclasses.org/package/5700-PHP-Send-requests-to-the-Gravatar-API-about-images.html"> این آدرس</a> هست. فقط من که تا حالا نتونستم بفهمم API Key واسه این xmlrpc رو از کجا باید بگیرم! </p>
<p><ins datetime="2010-05-07T07:21:32+00:00">یه میل زدم برای ساپورت درباره API Key و اونا گفتن که API Key همونه که توی <a href="http://wordpress.com">WordPress.com</a> به شما میدن. همونی که مثلا برای <a href="http://akismet.com/">Akismet</a> هم استفاده میشده. خوب مشکل حل شد :)<br />
</ins><br />
راستی اینم گراواتار من :</p>
<p><img src="http://www.gravatar.com/avatar/f6c5947ff77ff642f370d78017cf04fc?s=80&#038;r=g" alt="گراواتار من " /></p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/313/gravatar/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/885/string-array-php/' rel='bookmark' title='رشته ها و آرایه ها PHP'>رشته ها و آرایه ها PHP</a> <small>امروز درگیر یک کد بودم شبیه این : به نظر...</small></li>
<li><a href='http://cyberrabbits.net/740/regular-expression-part%db%b2/' rel='bookmark' title='عبارات با قاعده در PHP – بخش دوم'>عبارات با قاعده در PHP – بخش دوم</a> <small>توی نوشته قبلی، درباره عبارات با قاعده نوشتم، و حالا...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/313/gravatar/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>دانلود فایل با قابلیت Resume در PHP</title>
		<link>http://cyberrabbits.net/264/download-with-resume-support-in-php/</link>
		<comments>http://cyberrabbits.net/264/download-with-resume-support-in-php/#comments</comments>
		<pubDate>Fri, 09 Apr 2010 20:22:56 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[download manager]]></category>
		<category><![CDATA[webserver]]></category>
		<category><![CDATA[وب سرور]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=264</guid>
		<description><![CDATA[کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌ ایندفعه،‌میخوام یه چیزی بنویسم درباره دانلود (آخرش هم نفهمیدم که این کلمه رو به فارسی چی بگیم!! بارگزاری،‌بارگیری، گرفتن ..)‌ خیلی وقتها میخوایم که یه فایل رو محافظت کنیم که همینجوری دانلود نشه، یعنی حتما اسم کاربری بخواد، یا اینکه مطمئن شیم که فایل حتما&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1068/challenge-accepted/' rel='bookmark' title='از یک دانلود ساده تا یک خود درگیری بزرگ!'>از یک دانلود ساده تا یک خود درگیری بزرگ!</a> <small>یکی از مشکلاتی که لینوکسی همیشه میبینم دربارش بحث میکنن...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><ins datetime="2010-04-10T18:03:56+00:00">کد انتهایی یه مشکل کوچک داشت که اصلاح شد :)‌</ins><br />
ایندفعه،‌میخوام یه چیزی بنویسم درباره دانلود (آخرش هم نفهمیدم که این کلمه رو به فارسی چی بگیم!! بارگزاری،‌بارگیری، گرفتن ..)‌ خیلی وقتها میخوایم که یه فایل رو محافظت کنیم که همینجوری دانلود نشه، یعنی حتما اسم کاربری بخواد، یا اینکه مطمئن شیم که فایل حتما از تو صفحه خودمون دانلود میشه که آمار درست باشه.<br />
یه راه ساده وجود داره، که بیشتر از اون استفاده میشه،‌اونهم به سادگی گذاشتن فایل در یک پوشه خارج از دسترسی مستقیم (مثلا خارج از ریشه وب سرور، یا محافظت شده توسط وب سرور،‌مثلا آپاچی با کمک فایل .htaccess ) و بعد به پس دادن فایل از طریق کد به کاربر. مثلا برای PHP میتونید همچین کدی بنویسید : </p>
<pre class="brush: php; title: ; notranslate">
//Before this point you should check everything
//include user authenticate and any thing else
$result=@readfile('/path/to/file');
if ($result===false)
   //Error :(
else {
   // $result contain byte count
}
</pre>
<p>این روش بد نیست، کاربر نمیتونه لینک مستقیم بگیره، که خوب این معمولا برای خیلی ها مهمه، خصوصا با این وضع وبلاگها و سایتهای ایرانی که متخصص کپی/پیست و گرفتن لینک و گذاشتن به اسم خودشون حتی بدون زحمت آپلود مجدد هستن.<br />
اما یه عیب بزرگ داره،‌این دانلود دیگه قابلیت resume نداره. منم که دقیقا میتونم کاربر دیال آپی رو درک کنم،‌مخصوصا اینکه تو ایران بزرگ شدم!! خوب این شد که نشستم و بررسی کردم که چطوری این مشکل رو هم حل کنم. البته این کارو خیلی وقت پیش انجام دادم، و چون امروز خواهر زاده ام گیر داده بود، ‌پیداش کردم  و یادم افتاد که میشه اینجا بنویسمش،‌و خصوصا اینکه این روزا همش تو فکر اینم که چی بنویسم که ارزش نوشتن داشته باشه .<br />
<span id="more-264"></span><br />
اول باید یه توضیح ساده بدم. اونم اینکه اصلا این روش resume چطور کار میکنه. واسه اینکار سرور باید یه اطلاعاتی رو به صورت header بفرسته به کلاینت در ازای درخواست کلاینت. این کار رو به راحتی میشه انجام داد :</p>
<pre class="brush: php; title: ; notranslate">
	header('Accept-Ranges: bytes');
</pre>
<p>این رو وب سرورها برای هر فایلی که قابلیت resume بخواد داشته باشه میفرستن. البته اگه خود سرور این قابلیت رو داشته باشه. خوب ما هم همینکار رو انجام میدیم یعنی این خط رو به جواب کلاینت اضافه میکنیم، اینجوری حتی اگه خود وب سرور هم این قابلیت رو نداشته باشه ما این قابلیت رو اضافه کردیم (یه بار یه وب سرور مینوشتم، با دلفی و ایندی و خودم این قابلیت رو اونجا اضافه کردم، خیلی سخت نبود :)‌ )<br />
حالا کلاینت وقتی میفهمه که وب سرور این قابلیت رو داره ، علاوه بر آدرس فایل یه سری اطلاعات هم میفرسته.ما کاری با کلاینت نداریم،‌چون تو این حالت کلاینت برنامه دانلود هست، که خوب از بحث ما جداست. این اطلاعات رو میتونید (توی PHP ) از آرایه $_SERVER بخونید. این اطلاعات عبارتند از : </p>
<pre class="brush: php; title: ; notranslate">
	$ranges= $_SERVER['HTTP_RANGE'];
	//Now ranges contain some thing like : Range: bytes=0-500
</pre>
<p>البته توی <a href="http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt">Draft</a> مربوط به این قضیه که من خوندم، که Range میتونه چند تایی باشه که من کاری به اون قضیه ندارم و اصولا هم تا به حال ندیدم تو عمل این چند تایی بودن رو. اما این دو عدد که با یه منها از هم جدا میشن،‌ نشونگر بایتهایی هستند که خواسته شده. علامت منها همیشه باید باشه. اما یکی از دو عدد میتونه نباشه (دو عدد همزمان نمیتونن نباشن) اگه عدد اول نباشه، یعنی یه منها باشه بعد عدد دوم، به معنی درخواست n بایت انتهایی فایل هست، که n میشه همون عدد دوم.<br />
اما اگه عدد دوم نباشه، یعنی یه عدد m بعد یه منها،‌به این معنیه که از بایت m شروع کن تا آخر فایل خواسته شده. یادتون هم باشه که بایتها از صفر شروع میشن، یعنی اولین بایت صفره.<br />
خوب این درخواست که بیاد شما باید پاسخ بدید. پاسخ هم ساده هستش:</p>
<pre class="brush: php; title: ; notranslate">
	header(&quot;HTTP/1.0 206 Partial Content&quot;);
	header(&quot;Status: 206 Partial Content&quot;);
	header('Accept-Ranges: bytes');
</pre>
<p>دو تای اولی میگن که اطلاعاتی که قراره با این اتصال فرستاده بشن،‌ یه تیکه از فایل هستن نه یه فایل کامل. خط سوم هم که بالاتر گفتم. البته یادتون باشه header های دیگه مثلا اینها هم باید باشن : </p>
<pre class="brush: php; title: ; notranslate">
	header('Content-type: ' . $mime);
	header('Content-Disposition: attachment; filename=&quot;' . $filename . '&quot;');
	header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T' , $data_modifed));
</pre>
<p>یا خیلی header های استاندارد دیگه، تو این مثال اولی نوع فایل رو مشخص میکنه مثلا image/jpeg یا application/otect-stream یا &#8230; دومی هم اسم واقعی فایل رو میگه خصوصا اینکه ما داریم فایل رو طوری میفرستیم که آدرسش معلوم نشه، ولی بهتره اسم فایل رو معلوم کنیم که نرم افزار کلاینت یعنی همون دانلود منیجر یا بروزر بفهمه که اسم فایل چیه تا از اسم فایل php مثلا download.php استفاده نکنه.  سومی هم که زمان آخرین دستکاری فایله، و خوب جز اینها باز هم میتونه باشه یه چک بکنید میتونید همه رو تو یه جستجوی ساده توی وب پیدا کنید.<br />
اما حالا باید به کلاینت بگیم که چه بایتهایی رو داریم میفرستیم و چند بایت داریم میفرستیم ، به سه متغییر استفاده شده دقت کنید تو کامنتهای بالای کد نوشتم که هر کدوم چی هست :</p>
<pre class="brush: php; title: ; notranslate">
	//$size : size of file or data (all data not this part)
	//$seek_start : start of data in file, for example in ( Range: bytes=0-500 ) $seek_start=0
	//$seek_end : end of data in file, for example in ( Range: bytes=0-500 ) $seek_end=500
	header(&quot;Content-Range: bytes $seek_start-$seek_end/$size&quot;);
	header(&quot;Content-Length: &quot; . ($seek_end - $seek_start + 1));
</pre>
<p>خوب،‌دیگه header کافیه وقت اطلاعات واقعی هستن که فرستاده بشن. این اطلاعات میتونه هر چی باشه، از دیتابیس باشه، از فایل واقعی باشه یا&#8230; من فرض رو بر فایل واقعی میذارم. خوب ما گفتیم که یه قسمت از فایل رو قراره که بفرستیم نه همه اونو،‌پس وقتشه که شروع کنیم، فایل رو باز کنیم، اون قسمت مورد نظر رو بخونیم، و بعد مستقیم توی خروجی بنویسیم، مثلا با یه echo ساده. البته مشکلی به وجود میاد اونم برای فایلهای بزرگ و رنجهای بزرگ. یعنی مثلا یه فایل ۱ گیگا داری، برنامه دانلود میزنه ۴ قسمت ۲۵۰ مگابایتی درخواست میکنه. خوب اینجا مشکله که کل فایلو یه جا بخونی و بریزی بیرون، چون معمولا PHP برای استفاده از حافظه محدودیت داره. برای رفع این مشکل یه راه هست و اونم اینه که فایل رو تکه تکه بخونی مثل این حالت : </p>
<pre class="brush: php; title: ; notranslate">
	$data_len=$seek_end-$seek_start;
	fseek($file,$seek_start,SEEK_SET);
	$bufsize=2048;

	ignore_user_abort(true);
	@set_time_limit(0);
	while (!(connection_aborted() || connection_status() == 1) &amp;&amp; $data_len &gt; 0){
		if ($data_len &lt; bufsize)
			echo fread($file , $data_len);
		else
			echo fread($file , bufsize);
		$data_len -= $bufsize;
		flush();

	}
</pre>
<p>اون سه تا تابع <a href="http://php.net/manual/en/function.ignore-user-abort.php">ignore_user_abort </a>و <a href="http://php.net/manual/en/function.connection-aborted.php">connection_aborted</a> و  <a href="http://php.net/manual/en/function.connection-status.php">connection_status</a> به ما کمک میکنن که کنترل پایان عمل رو از کاربر بگیریم که برای این مورد اینکار بهترین کاره(یعنی اگه کاربر عمل دانلود رو لغو کنه بلافاصله اسکریپت متوقف نمیشه، ادامه پیدا میکنه تا درست و حسابی متوقف بشه. )‌. از طرفی با <a href="http://php.net/manual/en/function.set-time-limit.php">set_time_limit</a> محدودیت زمان اجرای PHP رو هم از بین میبریم که تو این مورد خیلی مهمه، چون PHP بعد از ۳۰ ثانیه به صورت اتوماتیک متوقف میشه و اگه دانلود زیاد طول بکشه این زیاد جالب نیست. اندازه بافر رو هم ۲۰۴۸ بایت در نظر گرفتم که میشه تغییرش داد. </p>
<p>کد کامل هم میشه این (که البته شما میتونید اونو به صورت یه کلاس دربیارید، من بیشتر قصدم این بود که توضیح بدم همه چیزو نه اینکه یه کلاس کامل بنویسم) </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	date_default_timezone_set('GMT');

	//1- file we want to serve :
	$data_file=&quot;/usr/home/f0rud/Desktop/largefile&quot;;
	$data_size=filesize($data_file);
	$mime='application/otect-stream'; //Mime type of file. to begin download its better to use this.
	$filename=basename($data_file); //Name of file, no path included

	//2- Check for request, is the client support this method?
	if (isset($_SERVER['HTTP_RANGE']) || isset($HTTP_SERVER_VARS['HTTP_RANGE'])){
		$ranges_str=(isset($_SERVER['HTTP_RANGE']))?$_SERVER['HTTP_RANGE']:$HTTP_SERVER_VARS['HTTP_RANGE'];
		$ranges_arr=explode('-', substr($ranges_str , strlen('bytes=')));
		//Now its time to check the ranges
		$ranges_arr[0]=intval($ranges_arr[0]);
		if ((intval($ranges_arr[0])&gt;=intval($ranges_arr[1]) &amp;&amp;
			$ranges_arr[1]!=&quot;&quot; &amp;&amp;
			$ranges_arr[0]!=&quot;&quot; ) ||
			($ranges_arr[1]==&quot;&quot; &amp;&amp; $ranges_arr[0]==&quot;&quot;)){
			//Just serve the file normally request is not valid :(
			$ranges_arr[0]=0;
			$ranges_arr[1]=$data_size;
		}
	} else { //The client dose not request HTTP_RANGE so just use the entire file
		$ranges_arr[0]=0;
		$ranges_arr[1]=$data_size;
	}

	//Now its time to serve file
	$file=fopen($data_file,'rb');

	//I use seek and tell to find the location, since I'm too lazy now
	//You may use some + or - instead of all this :)
	if ($ranges_arr[0]==&quot;&quot;){
		//Status 1 : the first one dose not exist
		fseek($file, - intval($ranges_arr[1]),SEEK_END);
		$seek_start=ftell($file);
		fseek($file, intval($ranges_arr[1]),SEEK_CUR);
		$seek_end=ftell($file);
	}elseif ($ranges_arr[1]==&quot;&quot;){
		//Status 2 : the last one dose not exist
		fseek($file,intval($ranges_arr[0]),SEEK_SET);
		$seek_start=ftell($file);
		fseek($file, $data_size - intval($ranges_arr[1]),SEEK_CUR);
		$seek_end=ftell($file);
	}else{
		//Status 3 : Both are here :)
		fseek($file,intval($ranges_arr[0]),SEEK_SET);
		$seek_start=ftell($file);
		fseek($file,  intval($ranges_arr[1])-intval($ranges_arr[0]),SEEK_CUR);
		$seek_end=ftell($file);
	}

	//Lets send headers 

	header('HTTP/1.0 206 Partial Content');
	header('Status: 206 Partial Content');
	header('Accept-Ranges: bytes');

	header('Content-type: ' . $mime);
	header('Content-Disposition: attachment; filename=&quot;' . $filename . '&quot;');
	header(&quot;Content-Range: bytes $seek_start-$seek_end/$data_size&quot;);
	header(&quot;Content-Length: &quot; . ($seek_end - $seek_start));

	//Finally serve data and done ~!
	$data_len=$seek_end - $seek_start;
	fseek($file,$seek_start,SEEK_SET);
	$bufsize=2048;

	ignore_user_abort(true);
	@set_time_limit(0);
	while (!(connection_aborted() || connection_status() == 1) &amp;&amp; $data_len &gt; 0){
		if ($data_len &lt; $bufsize)
			echo fread($file , $data_len);
		else
			echo fread($file , $bufsize);
		$data_len -= $bufsize;
		flush();

	}

	fclose($file);
?&gt;
</pre>
<p>من اینو با <a href="http://www.freedownloadmanager.org/">FDM</a> و <a href="http://www.downthemall.net/">DownThemAll</a> تست کردم. اگه کسی با نرم افزار دیگه تست کرد و جواب داد همینجا بگه. یه چیز عجیب اینه که <a href="http://www.gnu.org/software/wget/">WGET</a> با این کار نمیکنه مدام در مورد Partial Content خطا میده :)‌ به هر حال .<br />
<ins datetime="2011-01-26T13:49:51+00:00">اصلاحیه برای IE8 </ins><br />
دوستی توی <a href="http://cyberrabbits.net/264/download-with-resume-support-in-php/#comment-334">این کامنت نوشتن</a> که این کد با اینترنت اکسپلورر ۸ مشکل داره و علتش هم این باگ هستش : <a href='http://support.microsoft.com/kb/231296' title='Cannot Download .pdf File with HTTP 1.1 Cache-Control = "no-cache" Directive'>Cannot Download .pdf File with HTTP 1.1 Cache-Control = &#8220;no-cache&#8221; Directive</a> </p>
<p>ایشون به عنوان راه حل گفتن که بایستی این دو خط کد هم به Header پاسخ اضافه بشه به عنوان رفع مشکل :‌</p>
<pre class="brush: php; title: ; notranslate">
header(&quot;Cache-Control: no-cache&quot;);
header(&quot;Pragma: no-cache&quot;);
</pre>
<p>این کد باید بین خطوط ۵۸ تا ۶۰ قرار بگیره. فعلا هنوز امکان امتحانشو ندارم. بعد از بررسی دقیقتر حتما اصلاحش رو خواهم نوشت. فعلا اصلا در شرایط نوشتن کد نیستم. بازم ممنون از این دوست.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/264/download-with-resume-support-in-php/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/1068/challenge-accepted/' rel='bookmark' title='از یک دانلود ساده تا یک خود درگیری بزرگ!'>از یک دانلود ساده تا یک خود درگیری بزرگ!</a> <small>یکی از مشکلاتی که لینوکسی همیشه میبینم دربارش بحث میکنن...</small></li>
<li><a href='http://cyberrabbits.net/403/source-forge-redirector/' rel='bookmark' title='دانلود از SourceForge بدون مشکل'>دانلود از SourceForge بدون مشکل</a> <small>اسکریپت رو اگه نصب کردید دوباره آپدیت کنید، چون sf...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/264/download-with-resume-support-in-php/feed/</wfw:commentRss>
		<slash:comments>11</slash:comments>
		</item>
		<item>
		<title>فوت کوزه گری!</title>
		<link>http://cyberrabbits.net/236/php-tricks-1/</link>
		<comments>http://cyberrabbits.net/236/php-tricks-1/#comments</comments>
		<pubDate>Thu, 04 Mar 2010 20:18:26 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>
		<category><![CDATA[misc]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=236</guid>
		<description><![CDATA[گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت برسه و از کلی دردسر نجاتت بده! یه قابلیت که توی فایلهای PHP هست اینه که اگه فایل PHP شما قرار نیست که ادامه داشته باشه یعنی قرار نیست بعد از اینکه کد تموم شد،‌ادامه فایل به صورت HTML باشه، اونوقت میتونید از گذاشتن علامت&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>گاهی یک فوت کوزه گری رو دونستن میتونه کلی بدادت برسه و از کلی دردسر نجاتت بده! یه قابلیت که توی فایلهای PHP هست اینه که اگه فایل PHP شما قرار نیست که ادامه داشته باشه یعنی قرار نیست بعد از اینکه کد تموم شد،‌ادامه فایل به صورت HTML باشه، اونوقت میتونید از گذاشتن علامت <span style="direction:ltr">?&gt;</span> در انتهای کد خودداری کنید. راستشو بخواید اول که این قابلیت رو دیدم کلا نفهمیدم به چه دردی میخوره یا اگه بیشتر راستشو بخواید به خودم گفتم این قابلیت اصلا احمقانه و به درد نخوره (نتیجه اخلاقی این پست اینه که زود قضاوت نکنید! )‌ ولی امشب فهمیدم که نخیر! اینطورا هم نیست!<br />
قضیه اینطوریه که یه کدی دارم مینویسم که قراره یه تصویر رو از یه فایل بخونه و به خروجی بده (تابع <a href="http://php.net/manual/en/function.readfile.php">readfile</a>() از اون توابعیه که بعضیا کمتر میشناسنش!!)‌ همه چی درست بود (البته در ظاهر)‌ اما فایرفاکس خروجی رو نشون نمیداد. نتیجه رو که بررسی میکردم متوجه شدم که Content-Type هم درست ارسال میشه، ولی نمایش داده نمیشه. بارها و بارها کد رو بررسی کردم و هرچه بیشتر چک کردم کمتر نتیجه گرفتم.<br />
تا بالاخره خروجی رو با یه ویرایشگر باینری (HexEditor ) چک کردم. نتیجه دو تا کاراکتر Space در ابتدای فایل تصویر بود. صفحات دیگه رو هم چک کردم. اونجا ها هم این دو تا کاراکتر اضافه بود، ولی خوب html مشکلی نداره با این فااصله های اضافی. فایلهای include شده رو یکی یکی بررسی کردم و آخر سر هم خرابکار، پروژه <a href="http://cyberrabbits.net/1388/09/28/date-convertor/">jDate</a> خودم بود. توی یکی از فایلها بعد از پایان کد و تگ پایان PHP (یا همون  <span style="direction:ltr">?&gt;</span> ) دو تا فاصله خورده بود. این فایل بعد از آغاز session و ارسال header ها استفاده شده بود و به همین دلیل مشکل خودشو توی اونها نشون نداده بود. جالبتر اینکه یه فایل دیگه هم پیدا کردم که دقیقا این مشکل رو داشت و یه Enter اضافه داشت، که خیلی پیش میاد تو یه ویرایشگر این اتفاق بیفته. تو همه فایلهای دوره ویندوزم هم این مشکل کم و بیش بود :) نتیجه گیری اینکه من از این به بعد کل فایلهایی که با کد PHP تموم میشن رو وقتی بنویسم، خصوصا فایلهایی که قرار نیست خروجی داشته باشن مثل همین کتابخونه ها، به همین صورت و بدون بستن کد PHP مینویسم، توصیه میکنم شما هم همین کارو بکنید. به دردسرش نمیرزه،‌این یه دفعه دقیقا ۴۵ دقیقه وقتمو گرفت!!<br />
نکته دوم هم اینکه این پست اولین پست من با عینکه! دو سه روز پیش عینکی شدم!! :)</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/236/php-tricks-1/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/564/a-small-tip-in-php/' rel='bookmark' title='یه نکته کوچک در PHP'>یه نکته کوچک در PHP</a> <small>همونطور که میدونید توی PHP برای اپراتور OR دو نسخه...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/236/php-tricks-1/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP و توابعی که کمتر استفاده میکنیم</title>
		<link>http://cyberrabbits.net/228/php-some-special-function/</link>
		<comments>http://cyberrabbits.net/228/php-some-special-function/#comments</comments>
		<pubDate>Wed, 24 Feb 2010 16:31:50 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=228</guid>
		<description><![CDATA[چند تا تابع هست که ممکنه شما ندیده باشیدشون. منم یه چند تایی رو میشناختم که هر کدوم یه وقتایی نجاتم داده بودن از کلی کد نویسی زاید. این شد که تصمیم گرفتم یه چند تایی رو معرفی کنم: pack() این تابع یک بار منو نجات داد. PHP برای عملیات بیتی مخصوصا روی فایلها کار&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
<li><a href='http://cyberrabbits.net/1005/archlinux-i/' rel='bookmark' title='سایت کاربران آرچ لینوکس فارسی'>سایت کاربران آرچ لینوکس فارسی</a> <small>بالاخره بعد از یک سال تاخیر، سایت آرچ لینوکس فارسی...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>چند تا تابع هست که ممکنه شما ندیده باشیدشون. منم یه چند تایی رو میشناختم که هر کدوم یه وقتایی نجاتم داده بودن از کلی کد نویسی زاید. این شد که تصمیم گرفتم یه چند تایی رو معرفی کنم:<br />
<a href="http://php.net/manual/en/function.pack.php" title="تابع pack">pack()</a><br />
این تابع یک بار منو نجات داد. PHP برای عملیات بیتی مخصوصا روی فایلها کار چندانی انجام نداده. یعنی تمام توابع برخلاف زبانهای دیگه هستند. مثلا تابع <a href="http://php.net/manual/en/function.fwrite.php">fwrite</a> یه رشته (string )‌رو توی فایل مینویسه و این یعنی یه مشکل :)‌.<br />
یه زمانی میخواستم برنامه ای بنویسم که یه فایلی ایجاد کنه که توی دلفی بخونمش،‌اونجا قضیه برعکسه. توی دلفی باینری یه انتخاب ساده تره. اینجا بود که تابع pack رو پیدا کردم این تابع برای ایجاد یک رشته باینری استفاده میشه. یعنی اگه این رشته رو توی یه فایل بنویسی خیلی راحت میتونی مثلا توی دلفی یا سی اونو از فایل بخونی. مثلا برای ایجاد کردن یه خط حاوی دو تا کاراکتر،‌یه int اینجوری میشه عمل کرد :<br />
<span id="more-228"></span></p>
<pre class="brush: php; title: ; notranslate">
	$bin_str=pack('ccI',65,70,122);
</pre>
<p>همین. لیت فرمتهای پشتیبانی شده توسط این تابع توی صفحه راهنما هست. یه تابع برعکس هم هست، تابع <a href="http://www.php.net/manual/en/function.unpack.php">unpack()</a> که میتونه خیلی مفید باشه. </p>
<p><a href="http://www.php.net/manual/en/function.get-browser.php">get_browser()</a><br />
این تابع،‌بر اساس user-agent داده شده، یه سری اطلاعات رو به شما میده که میتونه مفید باشه. البته برای اینکه این تابع کار کنه نیاز به اینه که فایل browsecap رو توی فایل تنظیمات php اضافه کنید مثلا : </p>
<pre class="brush: plain; title: ; notranslate">
[browscap]
; http://php.net/browscap
browscap = /opt/lampp/share/php_browscap.ini
</pre>
<p>البته فقط معلوم نیست چرا این رو نمیشه از طریق کد انجام داد (یعنی این فقط از طریق فایل ini قابل تغییره نه از طریق apache یا اینکه ini_set )‌بنابر این قبل از اینکه از این تابع استفاده کنید حتما مطمئن بشید که توی سرورتون تنظیمه. طریقه استفاده سادست : </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
echo $_SERVER['HTTP_USER_AGENT'] . &quot;\n\n&quot;;

$browser = get_browser($_SERVER['HTTP_USER_AGENT'], true);
print_r($browser);
?&gt;
</pre>
<p>اینم خروجیش برای من : </p>
<pre class="brush: plain; title: ; notranslate">
Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2) Gecko/20100115 Firefox/3.6 (.NET CLR 3.5.21022) FirePHP/0.4

Array
(
    [browser_name_regex] =&gt; §^mozilla/5\.0 \(x11; .*; .*linux.*; .*; rv:1\.9\.2.*\) gecko/.* firefox/3\.6.*$§
    [browser_name_pattern] =&gt; Mozilla/5.0 (X11; *; *Linux*; *; rv:1.9.2*) Gecko/* Firefox/3.6*
    [parent] =&gt; Firefox 3.6
    [platform] =&gt; Linux
    [browser] =&gt; Firefox
    [version] =&gt; 3.6
    [majorver] =&gt; 3
    [minorver] =&gt; 6
    [frames] =&gt; 1
    [iframes] =&gt; 1
    [tables] =&gt; 1
    [cookies] =&gt; 1
    [javaapplets] =&gt; 1
    1 =&gt; 1
    [cssversion] =&gt; 3
    [supportscss] =&gt; 1
    [alpha] =&gt;
    [beta] =&gt;
    [win16] =&gt;
    [win32] =&gt;
    [win64] =&gt;
    [backgroundsounds] =&gt;
    [cdf] =&gt;
    [vbscript] =&gt;
    [activexcontrols] =&gt;
    [isbanned] =&gt;
    [ismobiledevice] =&gt;
    [issyndicationreader] =&gt;
    [crawler] =&gt;
    [aol] =&gt;
    [aolversion] =&gt; 0
)
</pre>
<p>فعلا حوصله نوشتن بیشتر ندارم، وقتشم نیست. تا دفعه بعد که ادامه اینو بنویسم.</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/228/php-some-special-function/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/528/session-regenerate-id/' rel='bookmark' title='ایجاد مکرر کلید جلسه'>ایجاد مکرر کلید جلسه</a> <small>در مورد جلسات این چند روزه زیاد نوشتم. تو آخرین...</small></li>
<li><a href='http://cyberrabbits.net/471/http-redirect/' rel='bookmark' title='HTTP Redirect'>HTTP Redirect</a> <small>یه چند وقتی هست که مدام درگیر کارهای امنیتی، خصوصا...</small></li>
<li><a href='http://cyberrabbits.net/1005/archlinux-i/' rel='bookmark' title='سایت کاربران آرچ لینوکس فارسی'>سایت کاربران آرچ لینوکس فارسی</a> <small>بالاخره بعد از یک سال تاخیر، سایت آرچ لینوکس فارسی...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/228/php-some-special-function/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>MySQL , Menu قسمت دوم</title>
		<link>http://cyberrabbits.net/181/mysql-menu-part2/</link>
		<comments>http://cyberrabbits.net/181/mysql-menu-part2/#comments</comments>
		<pubDate>Sun, 14 Feb 2010 19:51:36 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=181</guid>
		<description><![CDATA[این پست رو برای اضافه کردن یه سری قابلیت جدید و رفع یه باگ ویرایش کردم. :D قبل از شروع بگم اصل این ایده رو یکی از دوستام داد، اونم از وب خونده بود. منتها من هیچ چیز مدوّنی از این مطلب نتونستم پیدا کنم هنوز (نگشتم اگه راستشو بخواید) خواستم پیشاپیش بگم، چون حقیقتا&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/' rel='bookmark' title='Menu، MySQL و کلا هر چی با M شروع میشه'>Menu، MySQL و کلا هر چی با M شروع میشه</a> <small>اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از...</small></li>
<li><a href='http://cyberrabbits.net/108/ajax-jquery-php-part2/' rel='bookmark' title='Ajax &#8211; jQuery &#8211; PHP قسمت دوم'>Ajax &#8211; jQuery &#8211; PHP قسمت دوم</a> <small>تو قسمت قبلی یه توضیح مختصر درباره اصل برنامه دادم،‌خیلی...</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p><ins datetime="2010-07-16T13:34:26+00:00">این پست رو برای اضافه کردن یه سری قابلیت جدید و رفع یه باگ ویرایش کردم.  :D</ins></p>
<p>قبل از شروع بگم اصل این ایده رو یکی از دوستام داد، اونم از وب خونده بود. منتها من هیچ چیز مدوّنی از این مطلب نتونستم پیدا کنم هنوز (نگشتم اگه راستشو بخواید) خواستم پیشاپیش بگم، چون حقیقتا به این مساله اهمیت میدم، احترام به حق دیگران.<br />
توی پست قبل، روشی رو بررسی کردم که توی اون روش، از یه سری کوئری ساده استفاده میشد که یه جور منو از دیتابیس بارگذاری بشه (منظور نمایش نیست، فقط بارگذاری منو از دیتابیس به آرایه ، از MySQL به PHP ) ولی مشکل اساسی این بود که توی اون روش، زیادی قابلیت انعطافی وجود نداشت، درجه ها هم نمیشد از یه حدی بیشتر باشه و برای درجه های بالاتر باید کوئری تغییر میکرد که اینکار مستلزم زمان و کد بیشتره. </p>
<p>اما این دفعه روش طور دیگه ای هست. به جای روش قبلی، منو رو به این صورت در نظر بگیرید. (چیزی تغییر نکرده، فقط طرز نمایش عوض شده و به جای یه نمودار، یه سری آیتم تو در تو وجود داره)<br />
<div id="attachment_183" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/02/adv-menu-1.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/02/adv-menu-1-300x113.png" alt="menu" title="adv-menu-1" width="300" height="113" class="size-medium wp-image-183" /></a><p class="wp-caption-text">دید جدید منو</p></div><br />
جدولی که این دفعه قراره ساخته بشه، ساختار یه کم متفاوتی داره، به این صورت : </p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE IF NOT EXISTS `adv_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) NOT NULL,
  `data` varchar(255) NOT NULL,
  `lside` int(11) NOT NULL,
  `rside` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;

INSERT INTO `adv_menu` (`id`, `name`, `data`, `lside`, `rside`) VALUES
(1, 'Root', 'Root Data', 1, 24),
(2, 'Home', 'Home Data', 2, 3),
(3, 'Pages', 'Pages Data', 4, 13),
(4, 'Articles', 'Articles Data', 14, 23),
(5, 'PHP', 'PHP Data', 5, 10),
(6, 'Delphi', 'Delphi Data', 11, 12),
(7, 'SQL', 'SQL Data', 15, 16),
(8, 'Ajax', 'Ajax Data', 17, 22),
(9, 'Part2', 'Part2 Data', 18, 19),
(10, 'Part1', 'Part1 Data', 20, 21),
(11, 'OS', 'OS Data', 6, 7),
(12, 'GTrans', 'GTrans Data', 8, 9);
</pre>
<p><span id="more-181"></span><br />
اگه دقت کنید میبینید که اینبار به جای یه منوی پدر دو تا شماره به عنوان چپ و راست در نظر گرفته شده. این شماره ها چی هستن؟ ببینید :<br />
<div id="attachment_182" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/02/adv-menu-2.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/02/adv-menu-2-300x113.png" alt="menu 2" title="adv-menu-2" width="300" height="113" class="size-medium wp-image-182" /></a><p class="wp-caption-text">منو و اعداد چپ و راست</p></div><br />
یعنی اولین و آخرین شماره مربوط هستن به آیتم ریشه و باقی هم اگه دقت کنید خیلی ساده از چپ به راست شماره خوردن.(یه شماره سمت راست هر آیتمه rside و یه شماره هم سمت چپ خورده به اسم lside ) این روش شماره گذاری مزایای زیادی داره که سعی میکنم اونا رو نشون بدم. اول از همه اینکه چطوری میتونید کل آیتمها رو به ترتیب انتخاب کنید (فعلا فقط آیتمها رو همینطوری بدون هیچگونه درجه بندی ولی به ترتیب )</p>
<pre class="brush: sql; title: ; notranslate">
SELECT item.name,item.data
FROM adv_menu AS item, adv_menu AS parent
WHERE item.lside
BETWEEN parent.lside
AND parent.rside
AND parent.id =1
ORDER BY item.lside;
</pre>
<pre class="brush: plain; title: ; notranslate">
+----------+---------------+
| name     | data          |
+----------+---------------+
| Root     | Root Data     |
| Home     | Home Data     |
| Pages    | Pages Data    |
| PHP      | PHP Data      |
| OS       | OS Data       |
| GTrans   | GTrans Data   |
| Delphi   | Delphi Data   |
| Articles | Articles Data |
| SQL      | SQL Data      |
| Ajax     | Ajax Data     |
| Part2    | Part2 Data    |
| Part1    | Part1 Data    |
+----------+---------------+
12 rows in set (0.00 sec)
</pre>
<p>این اطلاعات بدون درجه بندی بدرد نمیخورن. میخوایم «عمق» هر آیتم هم مشخص بشه: </p>
<pre class="brush: sql; title: ; notranslate">
SELECT item.name,item.data,(COUNT(parent.name) - 1) AS depth
FROM adv_menu AS item, adv_menu AS parent
WHERE item.lside
BETWEEN parent.lside
AND parent.rside
GROUP BY item.name
ORDER BY item.lside;
</pre>
<pre class="brush: plain; title: ; notranslate">
+----------+---------------+-------+
| name     | data          | depth |
+----------+---------------+-------+
| Root     | Root Data     |     0 |
| Home     | Home Data     |     1 |
| Pages    | Pages Data    |     1 |
| PHP      | PHP Data      |     2 |
| OS       | OS Data       |     3 |
| GTrans   | GTrans Data   |     3 |
| Delphi   | Delphi Data   |     2 |
| Articles | Articles Data |     1 |
| SQL      | SQL Data      |     2 |
| Ajax     | Ajax Data     |     2 |
| Part2    | Part2 Data    |     3 |
| Part1    | Part1 Data    |     3 |
+----------+---------------+-------+
12 rows in set (0.00 sec)
</pre>
<p>اینجوری نتیجه جالبتره. یعنی خیلی ساده میتونید اونو به آرایه تبدیل کنید، البته با خود کوئری هم میشه کارهایی انجام داد مثلا : </p>
<pre class="brush: sql; title: ; notranslate">
SELECT CONCAT( REPEAT('-', COUNT(parent.name) - 1), item.name) AS name,item.data
FROM adv_menu AS item, adv_menu AS parent
WHERE item.lside
BETWEEN parent.lside
AND parent.rside
GROUP BY item.name
ORDER BY item.lside;
</pre>
<pre class="brush: plain; title: ; notranslate">
+-----------+---------------+
| name      | data          |
+-----------+---------------+
| Root      | Root Data     |
| -Home     | Home Data     |
| -Pages    | Pages Data    |
| --PHP     | PHP Data      |
| ---OS     | OS Data       |
| ---GTrans | GTrans Data   |
| --Delphi  | Delphi Data   |
| -Articles | Articles Data |
| --SQL     | SQL Data      |
| --Ajax    | Ajax Data     |
| ---Part2  | Part2 Data    |
| ---Part1  | Part1 Data    |
+-----------+---------------+
12 rows in set (0.00 sec)
</pre>
<p>نتیجه میتونه کمک کنه برای جاهایی که منوهای ساده میخوایم، مثلا برای یه چیزی مثل منوهای بالای هر صفحه تو ویکی. خوب، شاید مشکل اساسی شما این باشه که اگه بخوایم یه آیتم به این مجموعه اضافه کنیم چکار کنیم؟ مثلا یه آیتم به منوی SQL با محتوای Test. برخلاف دفعه قبلی که خیلی راحت میشد با یه کوئری INSERT ساده یه آیتم جدید به دیتابیس اضافه کرد اینبار باید علاوه بر اون یه کوئری هم آیتمهای بعدی رو به روز کنه. یعنی اگه ما بخوایم یه آیتم به زیر منوی SQL اضافه کنیم اینطوری باید کار انجام بشه (همه قدمها پشت سر هم اورده میشن، به روشی که بشه تو کنسول نوشتشون ولی برای PHP یه کم تغییر لازمه)  :<br />
<ins datetime="2010-07-16T13:29:28+00:00">یک باگ اینجا کشفیده شد توسط خودم، که حلش کردم این کد درسته :دی</ins></p>
<pre class="brush: sql; title: ; notranslate">
SET @pos= (SELECT rside FROM adv_menu WHERE name='SQL');
UPDATE adv_menu  SET lside=lside+2 WHERE lside&gt;=@pos;
UPDATE adv_menu  SET rside=rside+2 WHERE rside&gt;=@pos;
INSERT INTO adv_menu (name,data,lside,rside) VALUES ('Test','Test Data',@pos,@pos+1);
</pre>
<p>و این هم همین کد برایPHP </p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	$result=mysql_query(&quot;SELECT rside FROM adv_menu WHERE name='SQL'&quot;);
	$pos=mysql_fetch_assoc($result);
	$p=$pos['rside'];
	mysql_query(&quot;UPDATE adv_menu  SET  lside=lside+2 WHERE lside&gt;=$p&quot;);
	mysql_query(&quot;UPDATE adv_menu  SET  rside=rside+2 WHERE rside&gt;=$p&quot;);
	mysql_query(&quot;INSERT INTO adv_menu (name,data,lside,rside) VALUES ('Test','Test Data',$p,&quot;.($p+1).&quot;)&quot;);
?&gt;
</pre>
<p>این یعنی اینکه برای اضافه کردن یک آیتم جدید، نیاز داریم به دونستن اینکه اون آیتم قراره توی کدوم آیتم دیگه قرار بگیره، تو این مثال ما میخواستیم آخر باشه ولی خوب راههای دیگه ای هم هست اگه بخوایم اول بیفته یا &#8230; به هر صورت اول باید شماره مربوط به جایی که این آیتم قراره قبلترش ایجاد بشه پیدا کنیم، با کمک همون کوئری اولی (که توی PHP به جای اینکار میتونیم خیلی ساده از یه متغیر PHP استفاده کنیم) بعد با یه UPDATE ساده همه منوهای بعدی رو دو تا شیفت میدیم به جلو و بعد هم در گام آخر، منو رو توی جای خالی به وجود اومده میذاریم. برای حذف هم دقیقا همین کارو برعکس انجام میدیم.<br />
<ins datetime="2010-07-16T13:34:26+00:00">این باگ اینجا هم بود :دی</ins></p>
<pre class="brush: sql; title: ; notranslate">
SET @pos=(SELECT rside FROM adv_menu WHERE name='Test');
DELETE FROM adv_menu WHERE rside=@pos;
UPDATE adv_menu SET  lside=lside-2 WHERE lside &gt; @pos;
UPDATE adv_menu SET rside=rside-2 WHERE rside &gt; @pos;
</pre>
<p>تو این روش، هزینه اضافه و کم کردن منو بیشتره. ولی وقت نمایش منو کار سادست. و اگه توجه کنید اضافه کردن و کم کردن به منو کاری نیست که بخواد هر ثانیه یه بار انجام بشه ولی نمایش ممکنه بارها و بارها در یه ثانیه (حتی یه ثانیه) اتفاق بیفته. علاوه بر اون تعداد درجه های تو در تو نامحدوده. نه مثل قبل و اصلا نیازی به تغییر کوئری نداره. </p>
<p>امروز داشتم مطالعم میکردم که برخوردم به <a href="http://dev.mysql.com/doc/refman/5.0/en/group-by-functions.html#function_group-concat">GROUP_CONCAT</a> دیدم که برای این مساله خیلی میتونه مفید باشه، البته یه کم محدودیت میاره  ببینید : </p>
<pre class="brush: sql; title: ; notranslate">
SELECT item.name,item.data,
(COUNT(parent.name) - 1) AS depth,
GROUP_CONCAT(parent.name ORDER BY parent.lside ASC SEPARATOR '|') as path
FROM adv_menu AS item, adv_menu AS parent
WHERE item.lside BETWEEN parent.lside AND parent.rside
GROUP BY item.name
ORDER BY item.lside;
</pre>
<p>و نتیجه : </p>
<pre class="brush: plain; title: ; notranslate">
+----------+---------------+-------+--------------------------+
| name     | data          | depth | path                     |
+----------+---------------+-------+--------------------------+
| Root     | Root Data     |     0 | Root                     |
| Home     | Home Data     |     1 | Root|Home                |
| Pages    | Pages Data    |     1 | Root|Pages               |
| PHP      | PHP Data      |     2 | Root|Pages|PHP           |
| OS       | OS Data       |     3 | Root|Pages|PHP|OS        |
| GTrans   | GTrans Data   |     3 | Root|Pages|PHP|GTrans    |
| Delphi   | Delphi Data   |     2 | Root|Pages|Delphi        |
| Articles | Articles Data |     1 | Root|Articles            |
| SQL      | SQL Data      |     2 | Root|Articles|SQL        |
| Ajax     | Ajax Data     |     2 | Root|Articles|Ajax       |
| Part2    | Part2 Data    |     3 | Root|Articles|Ajax|Part2 |
| Part1    | Part1 Data    |     3 | Root|Articles|Ajax|Part1 |
+----------+---------------+-------+--------------------------+
</pre>
<p>همونطور که میبینید مسیر هر منو هم کاملا قابل دسترسه به یه جداساز که تو این مثال کاراکتر &#8220;|&#8221; هست. این خیلی میتونه کمک کنه ولی باید یادتون باشه این تابع محدودیت داره (پیشفرضش ۱۰۲۴ کاراکتر) که اگه مسیر بیشتر بشه بدون هیچ اخطاری بریده میشه. به هر حال اینم واسه خودش راهیه :))</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/181/mysql-menu-part2/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/' rel='bookmark' title='Menu، MySQL و کلا هر چی با M شروع میشه'>Menu، MySQL و کلا هر چی با M شروع میشه</a> <small>اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از...</small></li>
<li><a href='http://cyberrabbits.net/108/ajax-jquery-php-part2/' rel='bookmark' title='Ajax &#8211; jQuery &#8211; PHP قسمت دوم'>Ajax &#8211; jQuery &#8211; PHP قسمت دوم</a> <small>تو قسمت قبلی یه توضیح مختصر درباره اصل برنامه دادم،‌خیلی...</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/181/mysql-menu-part2/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Menu، MySQL و کلا هر چی با M شروع میشه</title>
		<link>http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/</link>
		<comments>http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/#comments</comments>
		<pubDate>Fri, 12 Feb 2010 08:34:50 +0000</pubDate>
		<dc:creator>فرود</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[برنامه نویسی]]></category>

		<guid isPermaLink="false">http://cyberrabbits.net/?p=162</guid>
		<description><![CDATA[اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از یک روش ساده استفاده کردم. روشی که خیلی های دیگر هم از آن مطمئنا استفاده میکنند. (باز هم خیلی بی مقدمه شروع کردم !) منظورم از منوهای تو در تو خیلی سادست، منوهای کناری سایتها. مثلا یه چیزی مثل شکل زیر: با این فرض که&#8230;
پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/181/mysql-menu-part2/' rel='bookmark' title='MySQL , Menu قسمت دوم'>MySQL , Menu قسمت دوم</a> <small>این پست رو برای اضافه کردن یه سری قابلیت جدید...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/258/exportcsv-to-mysql/' rel='bookmark' title='انتقال اطلاعات از CSV به MySQL'>انتقال اطلاعات از CSV به MySQL</a> <small>بعضی وقتها یه سری کارهای کوچیک میتونه دردسر ساز باشه....</small></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از یک روش ساده استفاده کردم. روشی که خیلی های دیگر هم از آن مطمئنا استفاده میکنند. (باز هم خیلی بی مقدمه شروع کردم !)<br />
منظورم از منوهای تو در تو خیلی سادست، منوهای کناری سایتها. مثلا یه چیزی مثل شکل زیر:<br />
 <div id="attachment_164" class="wp-caption alignnone" style="width: 310px"><a href="http://cyberrabbits.net/wp-content/uploads/2010/02/menu.png"><img src="http://cyberrabbits.net/wp-content/uploads/2010/02/menu-300x244.png" alt="منوی مورد نظر برای سایت :)" title="menu" width="300" height="244" class="size-medium wp-image-164" /></a><p class="wp-caption-text">منوی مورد نظر</p></div><br />
با این فرض که این منوها قراره از دیتابیس خونده بشن (تو این مورد mysql) و نمایش داده بشن. دیشب یه دوستی هم همین رو پرسید ازم که من اگه باشم چیکار میکنم، و بعد من یه راهی به ذهنم رسید که تا همین الان هم یه کله دارم روش کار میکنم.(البته بگم که یه دوست دیگه هم این ایده رو بهم داد و قرار شد لینکی که اصل ایده از اونجا بوده رو برام بفرسته، ولی این دوست من خیلی بد قوله!!) اول از همه روش قدیمی مو میگم و بعد روش جدیدمو . یعنی قسمت اول روشیه که تا به امروز استفاده میکردم و قسمت دوم روش جدیدیه که مد نظرمه،‌احتمالا اینو تو دو تا پست سرو سامون بدم تا شاید اون لینکی که دوستم گفت رو تا اون موقع پیدا کردم چون من الان فقط طرح کلیشو میدونم و بقیش کار خودمه و شاید اون بهتر بود.</p>
<p>فرض کنید همون شکلی که بالا هست رو میخوام پیاده کنم. جدولی با یه ساختار ساده ایجاد میکنم : </p>
<pre class="brush: sql; title: ; notranslate">
CREATE TABLE IF NOT EXISTS `simple_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) NOT NULL,
  `parent` int(11) NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;
</pre>
<p>این روش،‌خیلی ساده میتونه برای منوهای تو در توی محدود استفاده بشه. مثلا تا ۴ درجه.(مثل همین تصویر اگر که ریشه رو هم یک درجه حساب کنید)و البته قسمت دیتا هم اصلا استفاده ای توی مثال من نداره و مثلا میتونه لینکی باشه که این منو قراره به اون ما رو هدایت بکنه یا یه چیزی شبیه این. تو این مثال، ما زیاد اونو جدی نمیگیریم :)‌<br />
مثلا برای این تصویر اطلاعات مورد نیاز من خواهد بود : </p>
<pre class="brush: sql; title: ; notranslate">
INSERT INTO `simple_menu` (`id`, `name`, `parent`, `data`) VALUES
(1, 'Root', 0, 'Root Data'),
(2, 'Home', 1, 'Home Data'),
(3, 'Pages', 1, 'Pages Data'),
(4, 'Articles', 1, 'Articles Data'),
(5, 'PHP', 3, 'PHP Data'),
(6, 'Delphi', 3, 'Delphi Data'),
(7, 'SQL', 4, 'SQL Data'),
(8, 'Ajax', 4, 'Ajax Data'),
(9, 'OS', 5, 'OS Data'),
(10, 'GTrans', 5, 'GTrans Data'),
(11, 'Part1', 8, 'Part1 Data'),
(12, 'Part2', 8, 'Part2 Data');
</pre>
<p><span id="more-162"></span><br />
خوب، این ساختار دیتابیس و اطلاعاتی که نیاز داریم.یعنی دیتابیس باید حاوی این اطلاعات باشه : </p>
<pre class="brush: plain; title: ; notranslate">
mysql&gt; select * from simple_menu;
+----+----------+--------+---------------+
| id | name     | parent | data          |
+----+----------+--------+---------------+
|  1 | Root     |      0 | Root Data     |
|  2 | Home     |      1 | Home Data     |
|  3 | Pages    |      1 | Pages Data    |
|  4 | Articles |      1 | Articles Data |
|  5 | PHP      |      3 | PHP Data      |
|  6 | Delphi   |      3 | Delphi Data   |
|  7 | SQL      |      4 | SQL Data      |
|  8 | Ajax     |      4 | Ajax Data     |
|  9 | OS       |      5 | OS Data       |
| 10 | GTrans   |      5 | GTrans Data   |
| 11 | Part1    |      8 | Part1 Data    |
| 12 | Part2    |      8 | Part2 Data    |
+----+----------+--------+---------------+
12 rows in set (0.00 sec)
</pre>
<p>حالا اولین کاری که قراره انجام بشه، انتخاب کل این ردیفها، نه به صورت ساده بلکه طوری که فرم منو ها هم مشخص بشه: </p>
<pre class="brush: sql; title: ; notranslate">
SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.parent = 0;
</pre>
<p>که نتیجه هم به این صورت میشه : </p>
<pre class="brush: plain; title: ; notranslate">
+------+----------+--------+--------+-----------+---------------+-------------+-------------+
| sub1 | sub2     | sub3   | sub4   | dta1      | dta2          | dta3        | dta4        |
+------+----------+--------+--------+-----------+---------------+-------------+-------------+
| Root | Home     | NULL   | NULL   | Root Data | Home Data     | NULL        | NULL        |
| Root | Pages    | PHP    | OS     | Root Data | Pages Data    | PHP Data    | OS Data     |
| Root | Pages    | PHP    | GTrans | Root Data | Pages Data    | PHP Data    | GTrans Data |
| Root | Pages    | Delphi | NULL   | Root Data | Pages Data    | Delphi Data | NULL        |
| Root | Articles | SQL    | NULL   | Root Data | Articles Data | SQL Data    | NULL        |
| Root | Articles | Ajax   | Part1  | Root Data | Articles Data | Ajax Data   | Part1 Data  |
| Root | Articles | Ajax   | Part2  | Root Data | Articles Data | Ajax Data   | Part2 Data  |
+------+----------+--------+--------+-----------+---------------+-------------+-------------+
7 rows in set (0.00 sec)
</pre>
<p>خوب این هنوز اونقدرها هم خوب نیست. یعنی ایجاد منو ها یه کم دردسر داره، ولی میشه خیلی راحت این اطلاعات رو گرفت و توی یک آرایه گذاشت، برای PHP میشه اینکارو اینجوری انجام داد (البته با توابع ساده MySQL البته PDO هم اصل روش همینه و البته برای Zend Framework یا هر فریم ورک دیگه ای هم که خوب روش خودش داره، ولی اصل و اساس یکیه ).</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
	if (!extension_loaded('mysql')) die ('Need mysql extenstion');

	$con=@mysql_connect('127.0.0.1','user','pass');
	if (!$con) die ('Connection failed!');
	if (!mysql_select_db('test')) die ('MySQL Database selection failed.');
	$query=&lt;&lt;&lt;END_OF_QUERY
SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.parent = 0;
END_OF_QUERY;
	$resultSet=mysql_query($query);
	if (!$resultSet) die(mysql_error());
	$menu=array();
	while ($row=mysql_fetch_assoc($resultSet)){
		//sub1 can not be NULL and so sub2 (since I want to ;) )
		if (!@is_array($menu[$row['sub1']][$row['sub2']]))
			$menu[$row['sub1']][$row['sub2']]=$row['dta2'];
		if ($row['sub3']){
			if (!is_array($menu[$row['sub1']][$row['sub2']]))
				$menu[$row['sub1']][$row['sub2']]=array();
			if (isset($row['sub4'])){
				if (!@is_array($menu[$row['sub1']][$row['sub2']][$row['sub3']]))
					$menu[$row['sub1']][$row['sub2']][$row['sub3']]=array();
				$menu[$row['sub1']][$row['sub2']][$row['sub3']][$row['sub4']]=$row['dta4'];
			}else{
				$menu[$row['sub1']][$row['sub2']][$row['sub3']]=$row['dta3'];
			}
		}
	}
	//Do whatever you want to do with this array ;) I just want to dump it!
	var_dump($menu);
?&gt;
</pre>
<p> (و برای بیشتر از ۴ درجه هم باید کوئری دست کاری بشه) .<br />
حالا اگه نیاز باشه که مثلا یه مسیر خاص رو پیدا کردن هم زیاد سخت نیست، فقط شرط Where یه کم بایستی تغییر کنه مثل این :</p>
<pre class="brush: sql; title: ; notranslate">
SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.name = 'Root' AND t4.name='OS';
</pre>
<pre class="brush: plain; title: ; notranslate">
+------+-------+------+------+-----------+------------+----------+---------+
| sub1 | sub2  | sub3 | sub4 | dta1      | dta2       | dta3     | dta4    |
+------+-------+------+------+-----------+------------+----------+---------+
| Root | Pages | PHP  | OS   | Root Data | Pages Data | PHP Data | OS Data |
+------+-------+------+------+-----------+------------+----------+---------+
1 row in set (0.00 sec)
</pre>
<p> بهتره روشی باشه که منوهایی که فرزند ندارند رو هم مشخص کنه، این منوها باید امکان کلیک داشته باشن (البته میشه که مثلا منوی بالایی هم کلیک پذیر باششه ولی به نظر من بهتره عادت کاربر عوض نشه، یعنی دلیلی نداره منویی که به عنوان منوی پدر باشه کلیک پذیر باشه )‌</p>
<pre class="brush: sql; title: ; notranslate">
SELECT t1.name FROM
simple_menu AS t1 LEFT JOIN simple_menu as t2
ON t1.id = t2.parent
WHERE t2.id IS NULL;
</pre>
<pre class="brush: plain; title: ; notranslate">
+--------+
| name   |
+--------+
| Home   |
| Delphi |
| SQL    |
| OS     |
| GTrans |
| Part1  |
| Part2  |
+--------+
7 rows in set (0.00 sec)
</pre>
<p>این روش کلی دردسر اضافه داره، و مثلا برای درجه های بالاتر مشکلات همین جوری زیاد تر میشن و &#8230;. کد PHP که دیدید خیلی شلوغه و برای درجه های بالاتر بدتر هم میشه. در هر صورت، دفعه بعد روش بهتری رو مینویسم، و امیدوارم به زودی وقت بشه!</p>
<div class="plus-one-wrap"><g:plusone href="http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/"></g:plusone></div><p>پستهای مرتبط :<ol>
<li><a href='http://cyberrabbits.net/181/mysql-menu-part2/' rel='bookmark' title='MySQL , Menu قسمت دوم'>MySQL , Menu قسمت دوم</a> <small>این پست رو برای اضافه کردن یه سری قابلیت جدید...</small></li>
<li><a href='http://cyberrabbits.net/411/mysql-menu-part-3/' rel='bookmark' title='Mysql Menu قسمت سوم'>Mysql Menu قسمت سوم</a> <small>چند وقت پیش در باره منو و طریقه ایجاد آن...</small></li>
<li><a href='http://cyberrabbits.net/258/exportcsv-to-mysql/' rel='bookmark' title='انتقال اطلاعات از CSV به MySQL'>انتقال اطلاعات از CSV به MySQL</a> <small>بعضی وقتها یه سری کارهای کوچیک میتونه دردسر ساز باشه....</small></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://cyberrabbits.net/162/menu-mysql-and-every-m-thing/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

