راهنمای کدنویسی در PHP

مجتبی پاکزاد کدایگنایتر 3.1.9 در حال تکمیل رایگان

در این پست شیوه‌های کدنویسی در پی‌اچ‌پی توصیف می‌شود که خصوصا هنگامی که می‌خواهید در توسعه فریمورک کدایگنایتر مشارکت داشته باشید باید آن‌ها را رعایت کنید. البته الزامی به رعایت این استایل‌ها در اپلیکیشن کدایگنایتری خود ندارید، هر چند توصیه می‌شود در اپلیکیشن خود نیز آن‌ها را رعایت کنید.

فرمت فایل

فایل‌ها باید با انکودینگ یونیکد (UTF_8) ذخیره شوند. BOM نباید استفاده شود. برخلاف UTF-16 و UTF-32، هیچ ترتیب بایتی برای مشخص کردن یک فایل انکود شده با UTF-8 وجود ندارد، و BOM یک اثر جانبی منفی برای ارسال خروجی در PHP دارد که به اپلیکیشن اجازه نمی‌دهد تا هدرهای خود را سِت کند. علامت پایان خط یونیکس (LF) باید استفاده شود.

در اینجا نحوه اعمال این تنظیمات در برخی از ادیتورهای متداول را شرح می‌دهیم. این دستورالعمل ممکن است برای ادیتور شما متفاوت باشد، داکیومنت ادیتور خود را بررسی کنید.

TextMate

  1. پنجره تنظیمات (Preferences) ادیتور را باز کنید.
  2. بر روی Advanced کلیک کنید و سپس تَب Saving را باز کنید.
  3. از گزینه‌های لیست مقابل File Encoding، گزینه UTF-8 (recommended) را انتخاب کنید.
  4. از گزینه‌های مقابل Line Endings، گزینه LF (recommended) را انتخاب کنید.
  5. اختیاری: تیک کنار چکباکس Use for existing files as well را نیز بزنید، اگر می‌خواهید کاراکترهای پایانی (Line Endings) در فایل‌هایی که در ادیتور باز هستند نیز مطابق با تنظیمات جدید اعمال شود.

BBEdit

  1. پنجره تنظیمات (Preferences) ادیتور را باز کنید.
  2. از سمت چپ Text Encodings را انتخاب کنید.
  3. در قسمت Default text encoding for new documents، گزینه Unicode (UTF-8, no BOM) را انتخاب کنید.
  4. اختیاری: در قسمت If file’s encoding can’t be guessed, use، گزینه Unicode (UTF-8, no BOM) را انتخاب کنید.
  5. از ستون سمت چپ Text Files را انتخاب کنید.
  6. در قسمت Default line breaks، گزینه Mac OS X and Unix (LF) را انتخاب کنید.

تگ پایانی PHP

استفاده از تگ پایانی پی‌اچ‌پی در فایل‌های پی‌اچ‌پی ?> برای پی‌اچ‌پی پارسر اختیاری است. با این حال، اگر از آن استفاده شود، تمام وایت‌اسپیش‌های (whitespace) بعد از تگ پایانی، چه توسط دولوپر وارد شده باشند، چه توسط کاریر، یا حتی یک اپلیکیشن FTP، می‌تواند منجر به خروجی ناخواسته، خطاهای پی‌اچ‌پی شود، و یا اگر خطاهای پی‌اچ‌پی متوقف شوند، منجر به تولید صفحات خالی شود. به همین دلیل، تک پایانی پی‌اچ‌پی باید از انتهای تمام فایل‌های پی‌اچ‌پی حذف شود و به جای آن باید یک خط خالی در انتهای فایل قرار گیرد.

نامگذاری فایل‌ها

فایل‌های کلاس باید به شیوه 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 باشد.

نامگذاری کلاس‌ها و متدها

نام کلاس‌ها باید همیشه با یک حرف بزرگ شروع شوند. چندین کلمه در نام کلاس‌ها باید با یک کاراکتر آندراسکور جدا شوند، همچنین نباید به صورت CamelCase نامگذاری شوند.

اشتباه

class superclass
class SuperClass

صحیح

class Super_class {

        public function __construct()
        {

        }
}

متدهای کلاس‌ها باید کاملا با حروف کوچک نامگذاری شوند و نام آن‌ها باید به وضوح عملکرد آن‌ها را نشان دهید و ترجیحا شامل یک فعل باشد. از انتخاب نام‌های طولانی پرهیز کنید. چندین کلمه در نام متد را با کاراکتر آندراسکور از هم جدا کنید.

اشتباه

function fileproperties()               // not descriptive and needs underscore separator
function fileProperties()               // not descriptive and uses CamelCase
function getfileproperties()            // Better!  But still missing underscore separator
function getFileProperties()            // uses CamelCase
function get_the_file_properties_from_the_file()        // wordy

صحیح

function get_file_properties()  // descriptive, underscore separator, and all lowercase letters

اسامی متغییرها

دستورالعمل‌های نامگذاری متغییر، بسیار شبیه به چیزی است که برای متدهای کلاس استفاده می‌شود. متغییرها باید فقط شامل حروف کوچک و آندراسکور به عنوان جداکننده باشند. همچنین نامگذاری به صورت منطقی و به صورتی باشد که هدف و محتوای آن را نمایش دهد. متغییر غیر کلمه‌ای و غیرکوتاه باید فقط به عنوان iterator (یعنی تکرار کننده) در حلقه‌های for() استفاده شوند.

اشتباه

$j = 'foo';             // single letter variables should only be used in for() loops
$Str                    // contains uppercase letters
$bufferedText           // uses CamelCasing, and could be shortened without losing semantic meaning
$groupid                // multiple words, needs underscore separator
$name_of_last_city_used // too long

صحیح

for ($j = 0; $j < 10; $j++)
$str
$buffer
$group_id
$last_city/code>

کامنت‌گذاری

به طور کلی، کد باید به طور گسترده کامنت‌گذاری شود. با این کار نه تنها به توضیح چرخه کار و هدف کد برای برنامه‌نویس‌های تازه‌کار کمک می‌شود، بلکه زمانی که بعد از چندین ماه دوری به سراغ کد خود برمی‌گردید، ارزشمند بودن این کامنت‌ها مشخص خواهد شد. فرمت اجباری برای کامنت‌ها وجود ندارد، اما الگوی زیر توصیه می‌شود.

استایل کامنت‌گذاری DocBlock قبل از تعریف کلاس‌ها، متدها و پراپرتی‌ها می‌آیند، و به همین صورت توسط IDEها قابل تشخیص و استفاده هستند:

/**
 * 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();

از کامنت‌گذاری تک خطی (single line comments) در داخل کد استفاده کنید، بین بلاک‌های کامنت بزرگ و کدها یک خط خالی قرار دهید.

// 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_something/in_particular/

$parts = $this->foo($parts);

ثابت‌ها

ثابت‌ها دستورالعملی مشابه متغییرها دارند، به جز در مورد حروف، که همیشه باید تماما با حروف بزرگ نوشته شوند.همیشه از ثابت‌های کدایگنایتر مانند SLASH و LD و RD و PATH_CACHE و غیره در زمان مناسب استفاده کنید.

اشتباه

myConstant      // missing underscore separator and not fully uppercase
N               // no single-letter constants
S_C_VER         // not descriptive
$str = str_replace('{foo}', 'bar', $str);       // should use LD and RD constants

صحیح

MY_CONSTANT
NEWLINE
SUPER_CLASS_VERSION
$str = str_replace(LD.'foo'.RD, 'bar', $str);

TRUE و FALSE و NULL

کلمات کلیدی TRUE و FALSE و NULL باید همیشه با حروف بزرگ نوشته شوند.

اشتباه

if ($foo == true)
$bar = false;
function foo($bar = null)

صحیح

if ($foo == TRUE)
$bar = FALSE;
function foo($bar = NULL)

عملگرهای منطقی

از عملگر مقایسه‌ای || (یا) استفاده نکنید، زیرا وضوح آن در خروجی برخی دستگاه‌ها کم است (برای نمونه، شبیه 11 دیده می‌شوند، پس بهتر است از OR استفاده شود). استفاده از && نسبت به AND ارجح‌تر است، اما هر دو پذیرفته شده هستند، و همیشه باید یک اسپیس قبل و بعد از آن قرار گیرد.

اشتباه

if ($foo || $bar)
if ($foo AND $bar)  // okay but not recommended for common syntax highlighting applications
if (!$foo)
if (! is_array($foo))

صحیح

if ($foo || $bar)
if ($foo AND $bar)  // okay but not recommended for common syntax highlighting applications
if (!$foo)
if (! is_array($foo))

مقایسه مقادیر بازگشتی و تایپ‌کستینگ

برخی از فانکشن‌های PHP در صورت عدم موفقیت، FALSE برمی‌گردانند، اما ممکن است یک استرینگ خالی ("") یا 0 نیز برگردانند که در مقایسه‌های غیرسخت‌گیرانه معادل FALSE ارزیابی می‌شوند. هنگامی که با این مقادیر در دستورات شرطی روبرو هستید، به صراحت نوع مقدار بازگشتی مورد تظرتان را مشخص کنید، و نه مقداری که ارزیابی آن در نوع غیرسخت‌گیرانه منجر به خروجی‌های غیرمنتظره می‌شود.

برای بررسی سخت‌گیرانه متغییرها و مقادیر بازگشتی، در صورت لزوم از === و !== استفاده کنید.

اشتباه

// 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)

صحیح

if (strpos($str, 'foo') === FALSE)

اشتباه

function build_string($str = "")
{
        if ($str == "") // uh-oh!  What if FALSE or the integer 0 is passed as an argument?
        {

        }
}

صحیح

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 5.6 یا ورژن‌های جدیدتر آن استفاده شود، اما به دلیل اینکه تا پایان سال 2018 حتی پشتیبانی PHP 7.0 نیز به پایان می‌رسد، نیازی به رعایت دستورالعمل سازگاری کدایگنایتر نیست و ترجمه آن نادیده گرفته شده است.

از فانکشن‌های پی‌اچ‌پی که نیاز به نصب لایبرری‌هایی غیر از لایبرری‌های پیش‌فرض دارند استفاده نکنید، مگر اینکه کدتان شامل یه متد جایگزین باشد که مناسب برای زمانی است که فانکشن مورد نظر در دسترس نیست.

یک فایل برای هر کلاس

برای هر کلاس یک فایل جداگانه بسازید، مگر اینکه کلاس‌ها خیلی به هم مرتبط باشند. فایل لایبرری Xmlrpc، یک مثال برای فایلی است که شامل چندین کلاس است.

وایت‌اسپیس

برای وایت‌اسپیس در کدهای خود از تَب به جای اسپیس استفاده کنید. ممکن است کار کم‌اهمیتی به نظر برسد، اما استفاده از تَب به جای اسپیس، امکان این را می‌دهد تا توسعه‌دهنده‌هایی که کدتان را بررسی می‌کنند، بتوانند در هر اپلیکیشنی که برای توسعه استفاده می‌‌کنند، indentation را در هر سطحی که می‌خواهند تنظیم و سفارشی کنند. به عنوان یک مزیت دیگر نیز، نتیجه این‌کار فایل‌های نسبتا فشرده‌تر است، به این دلیل که ذخیره یک کاراکتر تَب نسبت به چهار کاراکتر اسپیس، فضای کمتری را اشغال می‌کند.

لاین‌بریک‌ها

فایل‌ها باید با لاین‌بریک (کاراکتر انتهای خط) یونیکس ذخیره شوند. برای دولوپرهایی که از ویندوز استفاده می‌کنند ممکن است کمی مشکل‌تر به نظر برسد، اما در هر صورت بررسی کنید که در تنظیمات IDE یا ادیتوری که استفاده می‌کنید ذخیره فایل‌ها با لاین‌بریک یونیکس تنظیم شده باشد.

تورفتگی کد

 در کدهایتان از استایل Allman برای قرار دادن تورفتگی استفاده کنید. در این روش به جز هنگام تعریف کلاس‌ها، همیشه در خط بعد از خودشان قرار می‌گیرند و از نظر تورفتگی هم سطح عبارت یا دستور کنترلی (control statement) هستند که کد در آن قرار دارد.

اشتباه

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() {
        // ...
}

صحیح

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()
{
        // ...
}

فاصله‌گذاری پرانتز و براکت

به طور کلی، پرانتزها و براکت‌ها نباید از هیچ اسپیس اضافه‌ای داخل خود داشته باشند. البته یک استثنا نیز وجود دارد، هرگاه ساختارهای کنترلری پی‌اچ‌پی که آرگومان‌هایی را داخل پرانتز می‌پذیرند (declare و do-while و elseif و for و foreach و if و switch و while)، برای متمایز کردنشان از فانکشن‌ها و افزایش خوانایی، یک اسپیس بعد از آن‌ها استفاده می‌شود.

اشتباه

$arr[ $foo ] = 'foo';

صحیح

$arr[$foo] = 'foo'; // no spaces around array keys

اشتباه

function foo ( $bar )
{

}

صحیح

function foo($bar) // no spaces around parenthesis in function declarations
{

}

اشتباه

foreach( $query->result() as $row )

صحیح

foreach ($query->result() as $row) // single space following PHP control structures, but not in interior parenthesis

محلی‌سازی متن

لایبرری‌های کدایگنایتر در هر زمانی که امکان آن وجود داشت باید از فایل‌های زبان استفاده کنند.

اشتباه

return "Invalid Selection";

صحیح

return $this->lang->line('invalid_selection');

متدها و متغییرهای پرایوت

متدها و متغییرهایی که تنها به صورت اینترنالی (داخل کلاس) در دسترس هستند مانند فانکشن‌های کمکی و کاربردی که متدهای پابلیک از آن‌ها برای انتزاع کد (code abstraction) استفاده می‌کنند، باید یک آندراسکور (_) به عنوان پیشوند در ابتدای خود داشته باشند.

public function convert_text()
private function _convert_text()

خطاهای PHP

کد باید فاقد خطا باشد و برای دست یافتن به این پیش‌نیاز، نباید به مخفی کردن warningها و noticeها متکی بود. برای نمونه، هیچگاه متغییری را که خودتان آن را مقداردهی نکرده‌اید (مانند کلیدهای آرایه $_POST) بدون چک کردن آن با isset() مورد دسترسی قرار ندهید.

بررسی کنید که error reporting در محیط توسعه برای همه کاربران و display_errors نیز در محیط پی‌اچ‌پی (php.ini) فعال باشند. با کد زیر می‌توانید از فعال‌بودن این مورد اطمینان مطمئن شوید:

if (ini_get('display_errors') == 1)
{
        exit "Enabled";
}

در برخی از سرورها display_errors غیرفعال است و شما نیز هیچ دسترسی برای تغییر php.ini ندارید. اغلب با کد زیر می‌توانید آن را فعال سازید:

ini_set('display_errors', 1);

توجه

مقداردهی display_errors با استفاده از فانکشن ini_set() در زمان اجرا با فعال کردن آن از طریق php.ini یکسان نیست. به عبارت دیگر، اگر اسکریپت دارای fatal errors باشد، هیچ تاثیری بر آن ندارد.

تگ‌های کوتاه باز کردن

در مورد سرورهایی که short_open_tag در آن‌ها غیرفعال است، همیشه از تگ‌های باز کردن کامل پی‌اچ‌پی استفاده کنید.

اشتباه

<? echo $foo; ?>

توجه

مقداردهی از PHP 5.4 به بعد، تگ <?= همیشه در دسترس است.

هر خط یک دستور

هیچگاه چندین دستور را در یک خط ادغام نکنید:

اشتباه

$foo = 'this'; $bar = 'that'; $bat = str_replace($foo, $bar, $bag);

صحیح

$foo = 'this';
$bar = 'that';
$bat = str_replace($foo, $bar, $bag);

استرینگ‌ها

همیشه استرینگ‌ها را داخل تک کوتیشن (نقل قول تکی یا علامت) قرار دهید، مگر اینکه نیاز به پارس کردن متغییر داخل آن داشته باشید و هرجایی نیاز به پارس کردن متغییر دارید، آن متغییر را داخل آکولاد قرار بدهید تا از Greedy Token Parsing جلوگیری کنید. همچنین اگر می‌خواهید از کاراکترهای تک کوتیشن (نقل قول تکی) در داخل استرینگ استفاده کنید، استرینگ را در داخل دابل کوتیشن قرار دهید، در این صورت نیازی به Escape کردن کاراکترها نخواهید داشت.

اصطلاحات فنی

Greedy Token Parsing : فرض کنید می‌خواهید یک یا چند کاراکتر بلافاصه بعد از متغییر استفاده کنید، مثلا:

$name = Amir;
"My name is $nameali"

همانطور که در مثال بالا مشاهده می‌کنید، استفاده بدون فاصله از ali برای ساخت کلمه Amirali منجر به خطا (Greedy Token Parsing) خواهد شد، زیرا مفسر پی‌اچ‌پی دنبال متغییر $nameali می‌گردد که اصلا وجود ندارد. برای رفع این مشکل باید متغییر داخل کروشه قرار بگیرد.

$name = Amir;
"My name is {$name}ali"

اشتباه

"My String"                                     // no variable parsing, so no use for double quotes
"My string $foo"                                // needs braces
'SELECT foo FROM bar WHERE baz = \'bag\''       // ugly

صحیح

'My String'
"My string {$foo}"
"SELECT foo FROM bar WHERE baz = 'bag'"

کوئری‌های اس‌کیو‌ال

در اس‌کیو‌ال، کلمات کلیدی همیشه به صورت کپیتال نوشته می‌شوند: SELECT و INSERT و UPDATE و WHERE و AS و JOIN و ON و IN و غیره.

کدهای طولانی را به چند خط تفکیک کنید تا خوانایی کدها افزایش یابد، ترجیحا هر شرط یا عبارت دستوری را در یک خط بنویسید.

اشتباه

// 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");

صحیح

$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");

آرگومان‌های پیش‌فرض فانکشن

هر زمان که ممکن بود، مقدار پیش‌فرض برای آرگومان‌های فانکشن در نظر بگیرید. این کار باعث جلوگیری از بروز خطاهای پی‌اچ‌پی می‌شود که در اثر فراخوانی‌های اشتباه رخ می‌دهند، همچنین مقادیر فال‌بَک متداولی (common fallback values) وجود خواهد داشت که می‌توانند موجب کاهش چند خط از کد شوند، این کاهش خطوط کد به این دلیل است که خود پی‌اچ‌پی هرگاه در هنگام فراخوانی یک تابع مقداری به آن پاس داده نشود از مقدار تعریف شده پیش‌فرض استفاده می‌کند و دیگر نیازی نیست که شرطی برای چک کردن و مقداردهی در نظر گرفته شود. مثال:

function foo($bar = '', $baz = FALSE)
منبع
PHP Style Guide