دستورزبان توصیهشدهی PHP
توی این پست میخوایم درمورد روشهای استاندارد کدنویسی که بهتره توی کدنویسی رعایت کنید صحبت کنیم. البته این روشها فقط درصورتیکه بخواین توی توسعهی کدایگنایتر مشارکت داشته باشین، الزامی هستن.
قالب فایل
فایلها باید با کدگذاری یونیکد (UTF-8) ذخیره بشن. BOM «نباید» استفاده بشه. برخلاف UTF-16 و UTF-32، هیچ کارکتر Byte Order خاصی برای مشخصکردن فایل UTF-8 نیاز نیست و BOM میتونه یه تأثیر منفی روی ارسال خروجی PHP داشته باشه که باعث میشه برنامه نتونه هدرهای خودش رو تنظیم کنه. همچنین باید از کارکتر پایان خطوط Unix استفاده کنید (LF). در اینجا نحوهی انجام اینکار رو در دو مورد از ادیتورهای معروف بیان میکنیم. روش انجام کار توی ادیتور شما ممکنه متفاوت باشه و باید به مستنداتش مراجعه کنید.
TextMate
- تنظیمات برنامه رو باز کنین
- روی گزینهی Advanced کلیک و سربرگ Saving رو باز کنین
- توی قسمت File Encoding گزینهی UTF-8 (recommended) رو انتخاب کنین
- توی قسمت Line Endings گزینهی LF (recommended) رو انتخاب کنین
- اختیاری: گزینهی Use for existing files as well رو هم انتخاب کنین اگه میخواین کارکترهای انتهای خطوط توی فایلهایی که باز کردین هم با تنظیمات جدید اصلاح بشه
BBEdit
- تنظیمات برنامه رو باز کنین
- از قسمت سمت چپ Text Encoding رو انتخاب کنین
- در قسمت Default text encoding for new documents گزینهی Unicode (UTF-8, no BOM) رو انتخاب کنین
- اختیاری: در قسمت If file's encoding can't be guessed, use گزینهی Unicode (UTF-8, no BOM) رو انتخاب کنین
- از قسمت سمت چپ Text Files رو انتخاب کنین
- در قسمت Default line breaks گزینهی Mac OS X and Unix (LF) رو انتخاب کنین
بستن تگ PHP
بستن تگ PHP ازنظر مفسر PHP اختیاریه. هرچند اگه استفاده بشه، هر کارکتر فضای سفید (مثل Space یا Tab یا Enter و...) که بعدش توسط توسعهدهنده، کاربر، برنامهی ویرایشگر یا FTP تولید شده باشه، ممکنه خروجی نامناسبی ایجاد کنه که موردنظر شما نباشه یا حتی باعث بروز خطای PHP در تغییر هدرها، یا ایجاد صفحات خالی و... بشه. برای جلوگیری از این مشکل، تمام فایلهای PHP باید بدون تگ بستن PHP در انتهای خودشون باشن و انتهای این فایلها باید یک تگ باز PHP وجود داشته باشه که آخرین خطش، یک خط خالیه.
نامگذاری فایلها
اسامی کلاسها باید با ساختار Ucfirst باشن (یعنی حرف اول با حروف بزرگ نوشته بشه) درحالیکه هر فایل دیگری (تنظیمات، ویوها، اسکریپتهای عمومی و...) باید تماماً با حروف کوچک نوشته بشن.
این مثالها اشتباه هستن:
somelibrary.php
someLibrary.php
SOMELIBRARY.php
Some_Library.php
Application_config.php
Application_Config.php
applicationConfig.php
اما این اسامی درست هستن:
Somelibrary.php
Some_library.php
applicationconfig.php
application_config.php
بعلاوه، فایلهای حاوی کلاسها باید با اسم خود کلاس مطابقت داشته باشن. برای مثال اگه کلاسی بهاسم Myclass دارین، فایلش هم باید Myclass.php باشه.
نامگذاری کلاسها و متدها
اسم کلاسها همیشه باید با یک حرف بزرگ شروع بشه و اسامی چندکلمهای باید با خطزیر جدا بشن نه اینکه بصورت CamelCased نوشته بشه. مثال:
class superclass // wrong
class SuperClass // wrong
class Super_class // correct
class Super_class
{
public function __construct()
{
}
}
اسامی متدها باید تماماً با حروف کوچک نوشته بشه و اسمشون باید بهوضوع کارکردشون رو نشون بده و ترجیحاً بصورت فعل باشه. سعی کنید از اسامی خیلی طولانی خودداری کنید. اسامی چندکلمهای باید با خطزیر از هم جدا بشن:
function fileproperties() // Wrong, not descriptive and needs underscore separator
function fileProperties() // Wrong, not descriptive and uses CamelCase
function getfileproperties() // Wrong, It's better! But still missing underscore separator
function getFileProperties() // Uses CamelCase
function get_the_file_properties_from_the_file() // Wrong, it's too long
function get_file_properties() // Correct, it's descriptive, uses underscore separator, and all letters are lowercase
اسامی متغیرها
قواعد نامگذاری متغیرها خیلی شبیه اسامی نامگذاری متدهای کلاسه. متغیرها باید فقط از حروف کوچک استفاده کرده باشن و کلماتشون با خطزیر از هم جدا بشه و اسمشون هم مفهوم و کاربردشون و محتواشون رو منتقل کنه. اسامی خیلی کوتاه و غیر کلمهای فقط باید بعنوان متغیرهای حلقه بکار برن. مثال:
$j = 'foo'; // Wrong, single letter variables should only be used in for() loops
$Str // Wrong, contains uppercase letters
$bufferedText // Wrong, uses CamelCasing, and could be shortened without losing semantic meaning
$groupid // Wrong, multiple words, needs underscore separator
$name_of_last_city_used // Wrong, too long
for ($j = 0; $j < 10; $j++) // Correct
$str // Correct
$buffer // Correct
$group_id // Correct
$last_city // Correct
توضیحات
بطور کلی کد باید بطور گسترده حاوی توضیحات باشه. اینکار نهتنها کمک میکنه چرخهی کاری و هدف کدها برای برنامهنویسان کمتجربهتر توضیح داده بشه، بلکه میتونه وقتی چند ماه بعد به سراغ کدتون میاین تا تغییراتی داخلش ایجاد کنید، ارزش غیرقابلتوصیفی داشته باشه. قالب خاصی برای توضیحات بعنوان اجبار وجود نداره، ولی این ساختار پیشنهاد میشه:
توضیحات بهسبک
DocBlock قبلاز کلاسها، متدها و متغیرها میتونه توسط IDEها شناسایی بشه و توی پنجرهی AutoComplete اونها به نمایش در بیاد:
/**
* Super Class
*
* @package Package Name
* @subpackage Subpackage
* @category Category
* @author Author Name
* @link http://example.com
*/
class Super_class {
/**
* Encodes string for use in XML
*
* @param string $str Input string
* @return string
*/
function xml_encode($str)
/**
* Data for class manipulation
*
* @var array
*/
public $data = array();
از توضیحات تکخطی در داخل کد استفاده کنین و یک خط خالی بین توضیحات طولانی و کدتون قرار بدین:
// break up the string by newlines
$parts = explode("n", $str);
// A longer comment that needs to give greater detail on what is
// occurring and why can use multiple single-line comments. Try to
// keep the width reasonable, around 70 characters is the easiest to
// read. Don't hesitate to link to permanent external resources
// that may provide greater detail:
//
// http://example.com/information_about_som...articular/
$parts = $this->foo($parts);
ثابتها
ثابتها از ساختاری مشابه متغیرها پیروی میکنن، بجز اینکه ثابتها باید همیشه تماماً با حروف بزرگ نوشته بشن. همیشه از ثابتهای داخلی کدایگنایتر (مثل SLASH و LD و RD و PATH_CACHE و...) در زمانهای موردنیاز استفاده کنید. مثال:
myConstant // Wrong, missing underscore separator and not fully uppercase
N // Wrong, no single-letter constants
S_C_VER // Wrong, not descriptive
$str = str_replace('{$foo}', 'bar', $str); // Wrong, should use LD and RD constants
MY_CONSTANT // Correct
NEWLINE // Correct
SUPER_CLASS_VERSION // Correct
$str = str_replace(LD.'foo'.RD, 'bar', $str); // Correct
TRUE و FALSE و NULL
این مقادیر باید همیشه با حروف بزرگ نوشته بشن. مثال:
// Wrong
if ($foo == true)
$bar = false;
function foo($bar = null)
// Correct
if ($foo == TRUE)
$bar = FALSE;
function foo($bar = NULL)
عملگرهای منطقی
استفاده از علمگر یا بصورت || مناسب نیست چون وضوحش در برخی دستگاههای خروجی پایینه (برای مثال شبیه 11 بنظر میرسه). استفاده از && نسبتبه AND ترجیح داده میشه ولی هردو قابلقبول هستن و ضمناً قبل و بعد از ! باید فاصله گذاشته بشه. مثال:
// Wrong
if ($foo || $bar)
if ($foo AND $bar) // okay but not recommended for common syntax highlighting applications
if (!$foo)
if (! is_array($foo))
// Correct
if ($foo OR $bar)
if ($foo && $bar) // recommended
if ( ! $foo)
if ( ! is_array($foo))
مقایسهی مقادیر بازگشتی و تبدیل نوع
برخی از توابع PHP درصورت عدم موفقیت، خروجی FALSE تولید میکنن ولی ممکنه که خروجی معتبری هم با مقدار 0 یا رشتهی خالی داشته باشن که میتونه بعنوان FALSE هم در مقایسههای معمولی که نوع رو بررسی نمیکنن، ارزیابی بشه. در چنین مقایسههایی، صراحتاً مشخصکنین که دنبال چی هستین. موقع بررسی مقادیر بازگشتی و چککردن متغیرهای معمولی خودتون، سختگیری کنین و از === یا ==! استفاده کنین. مثال:
// Wrong, If 'foo' is at the beginning of the string, strpos will return a 0,
// resulting in this conditional evaluating as TRUE
if (strpos($str, 'foo') == FALSE)
// Correct
if (strpos($str, 'foo') === FALSE)
// Wrong
function build_string($str = "")
{
if ($str == "") // uh-oh! What if FALSE or the integer 0 is passed as an argument?
{
}
}
// Correct
function build_string($str = "")
{
if ($str === "")
{
}
}
همچنین اطلاعات
تبدیل نوع رو هم مطالعه کنین که میتونن مفید باشن. تبدیل نوع، میتونه تأثیر نسبتاً متفاوتی با اونچه که قابل انتظاره داشته باشه. وقتی یه متغیر رو بصورت رشته تبدیل میکنین، برای مثال NULL و FALSE تبدیل به رشتهی خالی میشن و 0 (یا سایر اعداد) بصورت رشتهای از ارقام در میان و TRUE هم به رشتهی "1" تبدیل میشه:
$str = (string) $str; // cast $str as a string
کدهای اشکالزدایی
هیچوقت کدهای اشکالزدایی رو توی ارسال نهایی روی سرور رها نکنید، حتی اگه بصورت کامنت (توضیحات) قرار گرفته باشن. مواردی مثل
var_dump() یا
print_r() یا دستورات
die() و
exit() نباید توی کد شما قرار گرفته باشن، مگه اینکه هدفی بجز اشکالزدایی داشته باشن.
فاصلههای خالی در فایلها
هیچ فاصلهی خالی نباید قبلاز بازشدن تگ PHP یا بعد از بستن تگ PHP قرار گرفته باشه. خروجی در بافر قرار میگیره، بنابراین فواصل خالی توی فایل شما ممکنه باعث بشن خروجی قبلاز اینکه کدایگنایتر ارسال خروجی خودش رو شروع کنه، برای مرورگر کاربر ارسال بشه و درنتیجه باعث بروز خطا بشه و کدایگنایتر نتونه هدرهای خودش رو تنظیم کنه.
سازگاری
کدایگنایتر پیشنهاد میکنه از PHP5.6 یا بالاتر استفاده کنین ولی میتونه با PHP5.3.7 هم سازگار باشه. کدتون باید با این نیازمندیها سازگاری داشته باشه و اگه از نسخههای قدیمی استفاده بشه، جایگزین مناسب براش نوشته باشین یا اینکه قابلیت خاصی باشه که به آرامی درصورت عدم سازگاری، غیرفعال بشه بدون اینکه به برنامهی نهایی آسیبی بزنه.
بعلاوه، از توابع داخلی PHP که نیاز به نصب کتابخانههای غیر پیشفرض دارن استفاده نکنین، مگه اینکه کدتون شامل روش جایگزین برای مواقعی که اون کتابخانهها موجود نیست، باشه.
یک فایل بهازای هر کلاس
از فایلهای جداگانه برای هر کلاس استفاده کنین، مگه اینکه کلاسها بهشدت بههم وابسته باشن. یکی از مثالهایی که یک فایل CodeIgniter شامل چند کلاس هست، فایل کتابخانهی Xmlrpc هست.
فضاهای خالی
از Tab برای فاصلههای خالی توی کد استفاده کنید نه Space. اینکار ممکنه بنظر کوچک بیاد ولی استفاده از Tab بجای فاصلههای خالی باعث میشه که توسعهدهندگانی که به کد شما نگاه میکنن، بتونن هرجور دوست دارن توی ویرایشگرشون نمایش محتوا رو سفارشیسازی کنن. ازطرف دیگه، حجم فایل هم (کمی) کاهش پیدا میکنه. ذخیرهسازی یک کارکتر Tab بجای چهار کارکتر Space، فضای کمتری اشغال میکنه.
شکستن خطوط
فایلهای باید با کارکترهای انتهای خطوط Unix ذخیره بشن. اینکار بیشتر برای کسانی که با Windows کار میکنن ممکنه مشکل باشه، ولی درهرحال مطمئن بشین که ویرایشگر متنی شما برای ذخیرهی فایلها با کارکترهای انتهای خط Unix تنظیم شده.
تورفتگی کد
از روش تورفتگی Allman استفاده کنید. در این روش، بجز تعریف کلاسها، در بقیهی موارد آکولادها همیشه در یک خط جداگانه قرار میگیرن و ازنظر تورفتگی، همسطح با دستور والد خودشون خواهند بود. مثال:
// Wrong
function foo($bar) {
// ...
}
foreach ($arr as $key => $val) {
// ...
}
if ($foo == $bar) {
// ...
} else {
// ...
}
for ($i = 0; $i < 10; $i++)
{
for ($j = 0; $j < 10; $j++)
{
// ...
}
}
try {
// ...
}
catch() {
// ...
}
// Correct
function foo($bar)
{
// ...
}
foreach ($arr as $key => $val)
{
// ...
}
if ($foo == $bar)
{
// ...
}
else
{
// ...
}
for ($i = 0; $i < 10; $i++)
{
for ($j = 0; $j < 10; $j++)
{
// ...
}
}
try
{
// ...
}
catch()
{
// ...
}
فاصلههای کروشهها و پرانتزها
بطور کلی، پرانتزها و کروشهها نباید حاوی هیچ فاصلهی اضافهای باشن. تنها استثنا، وجود یک فاصله در جاهایی هست که یک ساختار کنترلی PHP آرگومانهای خودش رو توی پرانتز دریافت میکنه (declare و do-while و elseif و for و foreach و if و switch و while) تا کمک کنه با توابع و متدها اشتباه گرفته نشن و خوانایی افزایش پیدا کنه:
// Wrong
$arr[ $foo ] = 'foo';
// Correct
$arr[$foo] = 'foo'; // no spaces around array keys
// Wrong
function foo ( $bar )
{
}
// Correct
function foo($bar) // no spaces around parenthesis in function declarations
{
}
// Wrong
foreach( $query->result() as $row )
// Correct
foreach ($query->result() as $row) // single space following PHP control structures, but not in interior parenthesis
متن بومیسازیشده
کتابخانههای کدایگنایتر میتونن از مزایای فایلهای زبان مناسب در زمان امکان استفاده کنن. برای مثال:
// Wrong
return "Invalid Selection";
// Correct
return $this->lang->line('invalid_selection');
متدها و متغیرهای خصوصی
متدها و متغیرهایی که فقط بصورت داخلی مورد استفاده قرار میگیرن (مثل توابع کمکی و سودمندی که متدهای عمومی شما برای خلاصهسازی کد ازشون کمک میگیرن)، باید با کارکتر خطزیر شروع بشن. مثال:
public function convert_text()
private function _convert_text()
خطاهای PHP
کد شما باید بدون خطا اجرا بشه و وابسته به پنهانکردن هشدارها و یادآوریها نباشه. برای مثال، هیچوقت متغیری رو که خودتون تعریف نکردین، مستقیماً مورد دسترسی قرار ندین (مثل کلیدهای آرایهی
$_POST) مگه اینکه قبلش وجود اونها رو با
isset() بررسی کرده باشین.
مطمئن بشین که محیط توسعهی شما گزارش خطا رو برای تمام کاربران فعال کرده و نمایش خطاها هم توی محیط PHP فعاله. میتونین این موضوع رو با چنین کدی بررسی کنین:
if (ini_get('display_errors') == 1)
{
exit "Enabled";
}
دربرخی از سرورها display_errors غیرفعاله و شما هم دسترسی به php.ini ندارین. در اغلب مواقع مشابه این وضعیت، میتونین با دستور زیر نمایش خطاها رو فعال کنین:
ini_set('display_errors', 1);
نقل قول:نکته: تنظیمکردن display_errors با دستور ini_set در زمان اجرا، مشابه فعالکردنش ازطریق تنظیمات PHP نیست. برای مثال، هیچ تأثیری روی اسکریپتی که خطاهای مرگبار (Fatal Errors) داشته باشه، نداره.
تگهای کوتاه بازکردن
همیشه در مواقعی که یک سرور خاص، short_open_tags رو فعال نکرده باشه، از تگ کامل PHP برای بازکردن استفاده کنین. برای مثال:
// Wrong
<? echo $foo; ?>
<?=$foo?>
// Correct
<?php echo $foo; ?>
نقل قول:نکته: از نسخهی PHP5.4 بهبعد، همیشه شکل خلاصهشدهی echo قابل استفاده است.
یک دستور در هر خط
هیچوقت چند دستور رو در یک خط ادغام نکنید:
// Wrong
$foo = 'this'; $bar = 'that'; $bat = str_replace($foo, $bar, $bag);
// Correct
$foo = 'this';
$bar = 'that';
$bat = str_replace($foo, $bar, $bag);
رشتهها
همیشه رشتهها رو داخل کوتیشن تک بگذارین، مگه اینکه نیاز به پردازش متغیرها داشته باشین و در چنین شرایطی، متغیرها رو داخل آکولاد قرار بدین تا جلوی تفسیر حریصانهی متغیرها گرفته بشه. همچنین درصورتیکه توی رشته از کارکتر کوتیشن تک استفاده شده باشه، ممکنه که بخواین از کوتیشن جفت برای محصورکردن کل رشته استفاده کنین تا نخواین کوتیشن تک رو مدام خنثیسازی کنین. مثال:
// Wrong
"My String" // no variable parsing, so no use for double quotes
"My string $foo" // needs braces
'SELECT foo FROM bar WHERE baz = 'bag'' // ugly
// Correct
'My String'
"My string {$foo}"
"SELECT foo FROM bar WHERE baz = 'bag'"
پرسوجوهای SQL
کلمات کلیدی SQL رو همیشه با حروف بزرگ بنویسین: SELECT و INSERT و UPDATE و WHERE و AS و JOIN و ON و IN و...
کوئریهای طولانی رو به چند خط بشکنین تا خواناتر بشه. ترجیحاً این شکستگی رو در بین بخشهای کوئری انجام بدین. مثال:
// Wrong, keywords are lowercase and query is too long for
// a single line (... indicates continuation of line)
$query = $this->db->query("select foo, bar, baz, foofoo, foobar as raboof, foobaz from exp_pre_email_addresses
...where foo != 'oof' and baz != 'zab' order by foobaz limit 5, 100");
// Correct
$query = $this->db->query("
SELECT foo, bar, baz, foofoo, foobar AS raboof, foobaz
FROM exp_pre_email_addresses
WHERE
(
foo != 'oof'
AND baz != 'zab'
)
ORDER BY foobaz
LIMIT 5, 100
");
آرگومانهای پیشفرض توابع
هر زمان که امکان داشت، برای توابع خودتون مقادیر پیشفرض تعریف کنین. اینکار جلوی بسیاری از خطاهای PHP رو که بخاطر فراخوانیهای اشتباه و بدون مقدارهای مناسب توسط کاربر اتفاق میفته میگیره و باعث میشه کدتون هم برای کارکردن با مقادیر پیشفرض، کوتاهتر بشه. برای مثال:
function foo($bar = '', $baz = FALSE)