Joomla! 编码规范
PHP 代码
关于
编码规范
客户端语法风格指南
附录
注意
Joomla! CMS 从 4.2.0 版本开始,将其自身的编码规范切换为 PSR-12(之后切换为 PER 编码风格)编码规范。
本文档适用于 Joomla! < 4.2.0,其中部分内容仍然适用于 4.2.0 及更高版本。本文档将很快更新以反映 PSR-12 编码规范。
语言结构
PHP 代码标签
始终使用完整的 <?php ... ?>
来分隔 PHP 代码,而不是 <? ... ?>
简写形式。这是在不同操作系统和设置上包含 PHP 代码最便携的方式。
对于仅包含 PHP 代码的文件,不应包含结束标签(?>
)。PHP 不需要它。省略它可以防止意外将尾随空格注入输出,这可能会在 Joomla 会话中引入错误(请参阅 PHP 手册中关于 指令分隔 的内容)。
文件应始终以一个空行结束。
常规
根据 PSR-2 关键字和 True/False/Null
PHP 关键字 必须小写。
PHP 常量true
、false
和null
必须小写。
包含代码
在任何无条件包含文件的地方,使用 require_once
。在任何有条件包含文件的地方(例如,工厂方法),使用 include_once
。这两者都可以确保文件只包含一次。它们共享相同的filelist,因此您无需担心混合使用它们。使用 require_once
包含的文件不会被 include_once
再次包含。
注意
include_once
和require_once
是 PHP 语言语句,而不是函数。正确的格式是
require_once JPATH_COMPONENT . '/helpers/helper.php';
您不应将文件名括在括号中。
与 E_STRICT 兼容的 PHP 代码
从 Joomla 1.6 版本开始,以及适用于所有版本的 Joomla 平台,都需要遵循 PHP 5.3+ 支持的面向对象编程实践。Joomla 致力于逐步使源代码符合 E_STRICT。
全局变量
不应使用全局变量。请使用静态类属性或常量代替全局变量,遵循 OOP 和工厂模式。
错误抑制
应避免使用 @
进行错误抑制,并且仅限于在没有其他方法或解决方法可用时使用。
控制结构(通用代码)
对于所有控制结构,关键字和开括号之间都有一个空格,然后在开括号之后或闭括号之前都没有空格。这样做是为了区分控制关键字和函数名称。所有控制结构都必须在其花括号内包含其逻辑。
对于所有控制结构,例如 if
、else
、do
、for
、foreach
、try
、catch
、switch
和 while
,关键字都以换行符开头,并且每个花括号都放在新行上。
感叹号 !
,在条件中使用的逻辑运算符 not
,在感叹号前后不应有空格,如示例所示。
一个 if-else 示例
if ($test)
{
echo 'True';
}
// Comments can go here.
// Note that "elseif" as one word is used.
elseif ($test === false)
{
echo 'Really false';
}
elseif (!$condition)
{
echo 'Not Condition';
}
else
{
echo 'A white lie';
}
如果控制结构跨越多行,则所有行都必须缩进一个制表符,并且闭花括号必须与最后一行参数位于同一行。
if ($test1
&& $test2)
{
echo 'True';
}
一个 do-while 示例
do
{
$i++;
}
while ($i < 10);
一个 for 示例
for ($i = 0; $i < $n; $i++)
{
echo 'Increment = ' . $i;
}
一个 foreach 示例
foreach ($rows as $index => $row)
{
echo 'Index = ' . $index . ', Value = ' . $row;
}
一个 while 示例
while (!$done)
{
$done = true;
}
一个 switch 示例
使用 switch
语句时,case
关键字会缩进。假设在 case 中代码的缩进,break
语句以换行符开头。
switch ($value)
{
case 'a':
echo 'A';
break;
default:
echo 'I give up';
break;
}
一个 try catch 示例
try
{
$table->bind($data);
}
catch (RuntimeException $e)
{
throw new Exception($e->getMessage(), 500, $e);
}
混合语言用法(例如,在布局文件处)
对于布局文件以及我们混合使用 PHP 和 HTML 的所有文件(view/tmpl
和 layout
文件夹中的所有 PHP 文件),我们还将每一行都包装在 <?php ... ?>
块中,并使用控制结构的替代语法。这应该使代码更容易阅读,并使其更容易移动代码块而不会因缺少 <?php ... ?>
标签而导致致命错误。
控制结构示例
一个 if-else 示例
<?php if ($test) : ?>
<?php $var = 'True'; ?>
<?php elseif ($test === false) : ?>
<?php $var = 'Really false'; ?>
<?php else : ?>
<?php $var = 'A white lie'; ?>
<?php endif; ?>
引用
使用引用时,引用运算符之前应该有一个空格,并且在它与函数或变量名称之间没有空格。
例如
$ref1 = &$this->sql;
注意
在 PHP 5 中,对象不需要引用运算符。所有对象都通过引用处理。
连接空格
连接运算符('.')前后始终应该有一个空格。例如
$id = 1;
echo JRoute::_('index.php?option=com_foo&task=foo.edit&id=' . (int) $id);
如果连接运算符是行上的第一个或最后一个字符,则不需要这两个空格。例如
$id = 1
echo JRoute::_(
'index.php?option=com_foo&task=foo.edit&id=' . (int) $id
. '&layout=special'
);
数组
数组中的赋值(=>
运算符)可以使用空格对齐。将数组定义拆分为多行时,最后一个值也应该有一个尾随逗号。这是有效的 PHP 语法,有助于将代码差异降到最低。Joomla 3 倾向于使用 array()
以向后兼容 5.3.10,Joomla 4.0.0 及更高版本默认情况下应使用短数组语法 []
。(短数组语法是在 PHP 5.4 中引入的)。
例如
$options = [
'foo' => 'foo',
'spam' => 'spam',
];
代码注释
解释代码的内联注释遵循 C (/* … */
) 和 C++ 单行 (// ...
) 注释的约定。C 样式块通常仅限于文件、类和函数的文档标头。C++ 样式通常用于添加代码备注。强烈建议使用代码备注,以帮助其他人,包括您未来的自己,了解代码的目的。始终在代码执行特别复杂的运算时提供备注。
PHP 文件中不允许使用 Perl/shell 样式注释(#
)。
当然,代码块可以使用任何合适的格式注释掉以进行调试,但在提交补丁以回馈核心代码之前,应将其删除。
例如,不要包含诸如以下的功能提交
// Must fix this code up one day.
//$code = broken($fixme);
有关内联代码注释的更多详细信息,请参阅 内联代码注释 章节。
注释 Docblocks
文件、类、类属性、方法和函数中的 PHP 和 Javascript 代码的文档标头(称为 docblocks)遵循类似于 JavaDoc 或 phpDOC 的约定。
这些“DocBlocks”借鉴了 PEAR 标准,但有一些针对 Joomla 和 Joomla 平台的特定变化。
有关 DocBlocks 注释的更多详细信息,请参阅 DocBlocks 注释 章节。
函数调用
函数的调用在函数名称和开括号之间没有空格,在开括号和第一个参数之间没有空格;每个参数(如果存在)之间的逗号后有一个空格,最后一个参数和闭括号之间没有空格。等号前应该有空格,并且等号后正好有一个空格。允许跨多行进行制表符对齐。
// An isolated function call.
$foo = bar($var1, $var2);
// Multiple aligned function calls.
$short = bar('short');
$medium = bar('medium');
$long = bar('long');
函数定义
函数定义以新行开头,函数名称和开括号之间没有空格。此外,开花括号和闭花括号也放在新行上。指定返回值的行之前应有一个空行。
函数定义必须包含符合本文档注释部分的文档注释。有关 DocBlocks 函数注释的更多详细信息,请参阅 DocBlocks 注释 章节。
/**
* A utility function.
*
* @param string $path The library path in dot notation.
*
* @return void
*
* @since 1.6
*/
function jimport($path)
{
// Body of method.
}
如果函数定义跨越多行,则所有行都必须缩进一个制表符,并且闭花括号必须与最后一行参数位于同一行。
function fooBar($param1, $param2,
$param3, $param4)
{
// Body of method.
}
闭包/匿名函数
闭包/匿名函数在闭包/匿名函数的名称和开括号之间应该有一个空格。方法签名没有空格。
$fieldIds = array_map(
function ($f)
{
return $f->id;
},
$fields
);
类定义
类定义以新行开头,开花括号和闭花括号也放在新行上。类方法必须遵循函数定义的准则。属性和方法必须遵循 OOP 标准并进行适当声明(根据需要使用 public、protected、private 和 static)。
类定义、属性和方法都必须提供 DocBlock,符合以下部分。
有关 DocBlocks 类注释的更多详细信息,请参阅 DocBlocks 注释 章节。
类属性 DocBlocks
有关类属性 DocBlocks 的更多详细信息,请参阅 DocBlocks 注释 章节。
类方法 DocBlocks
类方法的 DocBlock 遵循与 PHP 函数相同的约定。
有关 DocBlocks 类方法注释的更多详细信息,请参阅 DocBlocks 注释 章节。
类定义示例
/**
* A utility class.
*
* @package Joomla.Platform
* @subpackage XBase
* @since 1.6
*/
class JClass extends JObject
{
/**
* Human readable name
*
* @var string
* @since 1.6
*/
public $name;
/**
* Method to get the name of the class.
*
* @param string $case Optionally return in upper/lower case.
*
* @return boolean True if successfully loaded, false otherwise.
*
* @since 1.6
*/
public function getName($case = null)
{
// Body of method.
return $this->name;
}
}
命名约定
类
类应该使用描述性的名称。尽可能避免使用缩写。类名称应该始终以大写字母开头,并使用驼峰命名法编写,即使使用传统的大写首字母缩写词(例如 XML、HTML)。一个例外是 Joomla 平台类,它们必须以大写“J”开头,下一个字母也必须是大写。
例如
- JHtmlHelper
- JXmlParser
- JModel
函数和方法
函数和方法的命名应该使用“驼峰式命名法”(也称为“驼峰大小写”或“小驼峰式命名法”)。名称的第一个字母小写,每个新“单词”开头的字母大写。Joomla 框架中的函数必须以小写字母 'j' 开头。
例如
- connect();
- getData();
- buildSomeWidget();
- jImport();
- jDoSomething();
私有类成员(表示仅打算在其声明的同一类中使用的类成员)前面带有单个下划线。属性应以下划线格式编写(即,用下划线分隔的逻辑单词),并且应全部小写。
例如
class JFooHelper
{
protected $field_name = null;
private $_status = null;
protected function sort()
{
// Code goes here
}
}
命名空间
命名空间的格式应遵循以下流程。首先是文件文档块,然后是文件所在的命名空间。如果需要,命名空间后面跟着 defined
检查。最后,使用 use
关键字导入的类。所有命名空间导入都必须按字母顺序排列。
/**
* @package Joomla.Administrator
* @subpackage mod_quickicon
*
* @copyright Copyright (C) 2005 - 2018 Open Source Matters, Inc. All rights reserved.
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
namespace Joomla\Module\Quickicon\Administrator\Helper;
defined('_JEXEC') or die;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Plugin\PluginHelper;
use Joomla\CMS\Router\Route;
use Joomla\Module\Quickicon\Administrator\Event\QuickIconsEvent;
常量
常量应始终全部大写,并使用下划线分隔单词。常量名称前缀应为其所属类/包的大写名称。例如,JError
类使用的常量都以 JERROR_
开头。
普通变量和类属性
普通变量遵循与函数相同的约定。
类变量应设置为 null 或其他适当的默认值。
异常处理
异常应用于错误处理。
以下部分概述了如何语义化地使用 SPL 异常。
逻辑异常
当 API 的使用方式存在明确问题时,将抛出 LogicException。例如,如果依赖项失败(您尝试操作尚未加载的对象)。
以下子类也可以在适当的情况下使用
BadFunctionCallException
如果回调引用未定义的函数或缺少某些参数,则可以抛出此异常。例如,如果 is_callable()
或类似函数在某个函数上失败。
BadMethodCallException
如果回调引用未定义的方法或缺少某些参数,则可以抛出此异常。例如,is_callable()
或类似函数在类方法上失败。另一个示例可能是,如果传递给魔术调用方法的参数丢失。
InvalidArgumentException
如果输入无效,则可以抛出此异常。
DomainException
此异常类似于 InvalidArgumentException,但如果值不符合定义的有效数据域,则可以抛出此异常。例如,尝试加载类型为“mongodb”的数据库驱动程序,但该驱动程序在 API 中不可用。
LengthException
如果对参数的长度检查失败,则可以抛出此异常。例如,文件签名不是特定数量的字符。
OutOfRangeException
此异常的实际应用很少,但在请求非法索引时可以抛出。
运行时异常
当某些外部实体或环境导致超出您控制范围的问题时(前提是输入有效),将抛出 RuntimeException。此异常是无法明确确定错误原因时的默认情况。例如,您尝试连接到数据库,但数据库不可用(服务器宕机等)。另一个示例可能是 SQL 查询失败。
UnexpectedValueException
当遇到意外结果时,应使用此类型的异常。例如,函数调用返回字符串而不是预期的布尔值。
OutOfBoundsException
此异常的实际应用很少,但如果某个值不是有效的键,则可能会抛出。
OverflowException
此异常的实际应用很少,但当您将元素添加到已满的容器中时可能会抛出。
RangeException
此异常的实际应用很少,但可能会抛出以指示程序执行期间的范围错误。通常,这意味着存在算术错误,而不是下溢/溢出。这是 DomainException 的运行时版本。
UnderflowException
此异常的实际应用很少,但在尝试从空容器中删除元素时可能会抛出。
记录异常
每个函数或方法都必须使用 @throws 标签注释它抛出的异常类型以及抛出的任何下游异常类型。每种异常类型只需注释一次。无需描述。
SQL 查询
SQL 关键字应大写,而所有其他标识符(显然除了带引号的文本)应小写。
所有表名都应使用 #__
前缀来访问 Joomla 内容并允许应用用户定义的数据库前缀。查询还应使用 JDatabaseQuery API。表永远不应该使用静态前缀,例如 jos_
。
要查询我们的数据源,我们可以调用许多 JDatabaseQuery 方法;这些方法封装了数据源的查询语言(在大多数情况下是 SQL),隐藏了特定于查询的语法,并提高了开发人员源代码的可移植性。
使用查询链将多个查询方法一个接一个地连接起来,每个方法都返回一个可以支持下一个方法的对象,这提高了可读性并简化了生成的代码。自从引入 Joomla 框架以来,“查询链”现在已成为构建数据库查询的推荐方法。
表名和表列名应始终用 quoteName()
方法括起来,以转义表名和表列。
查询中检查的字段值应始终用 quote()
方法括起来,以在将其传递给数据库之前转义该值。查询中检查的整数字段值也应类型转换为 (int)
。
// Get the database connector.
$db = JFactory::getDbo();
// Get the query from the database connector.
$query = $db->getQuery(true);
// Build the query programatically (example with chaining)
// Note: You can use the qn alias for the quoteName method.
$query->select($db->qn('u.*'))
->from($db->qn('#__users', 'u'));
// Tell the database connector what query to run.
$db->setQuery($query);
// Invoke the query or data retrieval helper.
$users = $db->loadObjectList();
更长的链式示例
// Using chaining when possible.
$query->select($db->quoteName(array('user_id', 'profile_key', 'profile_value', 'ordering')))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('profile_key') . ' LIKE '. $db->quote('\'custom.%\''))
->order('ordering ASC');
使用 Select 数组和整数字段值的类型转换的链式示例
$query = $db->getQuery(true)
->select(array(
$db->quoteName('profile_key'),
$db->quoteName('profile_value'),
))
->from($db->quoteName('#__user_profiles'))
->where($db->quoteName('user_id') . ' = ' . (int) $userId)
->order($db->quoteName('ordering'));