Обязательно к прочтению перед работой.
Полное соблюдение PSR (0, 1, 2)
Автоматический исправитель кодстайла под эти стандарты ТУТ, так же есть в папке _tools/codestyle
Правило бойскаута: оставлять место после себя чище чем оно было до твоего визита. Т.е. переписывайте код который не соответствует стандартам и правилам хорошего тона.
$_data = array (
'addon' => $addon_scheme->getId(),
'priority' => $addon_scheme->getPriority(),
);
db_query("REPLACE INTO ?:addon_descriptions ?e", array(
'lang_code' => $translation['lang_code'],
'addon' => $addon_scheme->getId(),
'name' => $translation['value'],
'description' => $translation['description']
));
return array(
'status' => Response::STATUS_OK,
'data' => array(
'settings' => $result,
'search' => $params
)
);
В нижнем регистре. Разделитель подчёркивание.
Обязательно осмысленное именование. Никаких $tmp, $t, $k, $v и т.д.
Полностью в верхнем регистре, разделитель подчёркивание (_)
- При обращении к элементу массива по ключу заключать имя ключа в одинарные кавычки:
- Все строковые переменные, не содержащие в себе других переменных, заключать в одинарные кавычки:
- Если в строке должна быть переменная, то строка берется в двойные кавычки:
- Комментарии пишутся только на английском языке. Для комментирования кода кода внутри функции/в контроллере использовать двойной слеш //;
- Использования perl style(#) не допускается;
- Не пишите коментарий который дублирует то что и так выражено кодом. Помните правило, что лучше код без комментариев, чем код с ложными и неактуальными комментариями;
- Будьте точны и кратки.
Плохо:
if (!empty($tabs)) {
// If tabs not empty do:
// Enable/disable tabs for addon
ProductTabs::instance()->updateAddonTabStatus($addon, $new_status);
Хорошо:
// Check options type. We need to apply only Selectbox, radiogroup and checkbox modifiers
if (empty($orig_options)) {
// ...
}
Обязателен для всех новых функций.
Описание аттрибутов возможных в doc-комментарии тыц 1, тыц 2
Если функция не возвращает значение, то правильно будет НЕ ПИСАТЬ @return вообще.
$tabs = $addon_scheme->getSections();
if (!empty($tabs)) {
// ...
foreach($tabs as $tab_index => $tab) {
$section_tab_id = Settings::instance()->updateSection(array(
'parent_id' => $addon_section_id,
'edition_type' => $tab['edition_type'],
'name' => $tab['id'],
'position' => $tab_index * 10,
'type' => isset($tab['separate']) ? Settings::SEPARATE_TAB_SECTION : Settings::TAB_SECTION,
));
// Import translations for tab
if (!empty($section_tab_id)) {
$settings = $addon_scheme->getSettings($tab['id']);
foreach ($settings as $k => $setting) {
// ...
}
}
}
}
Имена функций полностью в нижнем регистре. начинатсья должны с префикса fn_, либо с db_
/**
* Returns addon's setting variants (similar to fn_get_settings_variants)
*
* @deprecated deprecated since version 3.0
* @param string $addon Addon name to get option for
* @param string $option_name Option name
* @param string $lang_code 2-letter language code (e.g. 'EN', 'RU', etc.)
* @return array Variants list
*/
function fn_get_addon_option_variants($addon, $option_name, $lang_code = CART_LANGUAGE)
{
//...
}
/**
* Execute query and format result as associative array with column names as keys
*
* @param string $query unparsed query
* @param mixed ... unlimited number of variables for placeholders
* @return array structured data
*/
function db_get_array($query)
{
//...
}
Если у параметров есть дефолтные значения либо, они по смыслу не являются основными то их необходимо объединять в один параметр $extra. Таким образом, в функцию будут передаваться только основным параметры плюс массив экстра.
Вот как выглядела функция прежде:
function fn_get_product_data($product_id, &$auth, $lang_code = CART_LANGUAGE, $field_list = '', $get_add_pairs = true, $get_main_pair = true, $get_taxes = true, $get_qty_discounts = false, $preview = false, $features = true, $skip_company_condition = false)
function fn_get_product_data($product_id, &$auth, $extra)
{
// extra default values
$extra_default = array(
'lang_code' => CART_LANGUAGE,
'field_list' => '',
'get_add_pairs' => true,
'get_main_pair' => true
'get_taxes' => true,
'get_qty_discounts' = false,
'preview' = false,
'get_features' = true
)
$extra = fn_array_merge($extra_default, $extra);
Следует понимать разницу между $params & $extra. Первая использоуется в основном при поиске, и содержит перечень атрибутов и условий для поиска. В то время как $extra агрегирует избыточные параметры. Основная идея для чего это было сделано - облегчить и улучшить стиль передачи всех пришедших в функцию параметров в хуки.
Если какой-либо кусок кода встречается в двух и более местах в контроллере/функции, то код выносится в отдельную функцию в ядро (fn.[тут по смыслу].php).
Все функции должны что-то возвращать!(true/false or $variable) (правило не распространяется на методы классов)
Функция по возможности должна иметь только одну точку выхода. Использование двух и более точек выхода допускается лишь в случае, если этим достигается низкий порог дальнейшей условности, в простейшем случае для экономии ресурсов (например функция fn_apply_exceptions_rules в fn.catalog.php).
С прописной буквы в CamelCase.
class Api
{
class ClassLoader
{
Полностью в верхнем регистре, разделитель подчёркивание (_)
class Api
{
/**
* Key of resource name in _REQUEST
*
* @const REST_PATH_PARAM_NAME
*/
const DEFAULT_REQUEST_FORMAT = 'text/plain';
Полностью в нижнем регистре, разделитель подчёркивание.
Закрытые и приватные свойства ДОЛЖНЫ начинаться с подчёркивания.
class Api
/**
* Current request data
*
* @var Request $_request
*/
private $_request = null;
/**
* Sample var
*
* @var array $_request
*/
private $_sample_var = array();
Со строчной буквы в camelCase. Закрытые и приватные методы ДОЛЖНЫ начинаться с подчёркивания.
class SomeClass
{
/**
* Creates a new ClassLoader that loads classes of the
* specified namespace.
*
* @param string $include_path Path to namespace
*/
public function __construct($include_path = null)
{
// ...
}
/**
* Gets request method name (GET|POST|PUT|DELETE) from current http request
*
* @return string Request method name
*/
private function _getMethodFromRequestHeaders()
{
// ...
}
Начиная с версии 3.1.1 используются прстранства имён.
Tygh -- название пространства имён ядра магазина.
Все классы ядра должны входить в это пространство имён. Если несколько классов относятся по смыслу к одному функционалу, то нужно выделять их в отдельное подспространство, как например классы блок менеджера (Tyqh\BlockManager) и Api (Tyqh\Api)
Объявляется пространство имён так:
namespace Tygh;
Подпространство имён:
namespace Tygh\BlockManager;
Все функции, классы, констаны и т.д объявленные в этом пространстве имён будут доступны из глобального пространства или из другого только в случае указания этого пространства.
Например имеем такой файл:
namespace My\Name;
class MyClass {}
function myfunction() {}
const MYCONST = 1;
$a = new MyClass; // тут всё ок
В другом файле мы должны указывать пространство имён, если онне принадлежит тому же
$c = new \My\Name\MyClass; // Так работает.
$c = new MyClass; // Так не работает.
В третем файле попробуем тоже самое только с use
use My\Name;
$c = new \My\Name\MyClass; // Так работает.
$c = new MyClass; // И так работает.
Пространтсво имён, как и директива use действует только на один файл в котором объявлено. На те файлы которые подключаются с помощью include и reqire, действие не распространяется.
В каждом файле, в котором используются классы обязательно писать в начале директиву use, которая определяет какие пространства имён используются. В случае совпадения названий классов, требуется писать алиасы. Использовать полное имя класса вместе с пространством имён как следствие не требуется и нежелательно.
use Tygh\Registry;
use Tygh\Settings;
use Tygh\Addons\SchemesManager as AddonSchemesManager;
use Tygh\BlockManager\SchemesManager as BlockSchemesManager;
use Tygh\BlockManager\ProductTabs;
use Tygh\BlockManager\Location;
use Tygh\BlockManager\Exim;
Обязательно группировать use диррективы друг с другом.
Директива use это аналог reauire (include). Она добавляет каждому используему в файле имени класса то пространство имён в котором он находится. Она не подключает файл.
Все классы подключаются автоматически из тех путей которые добавлены в ClassLoader. По этому без крайней необходимости не стоит подключать классы вручную (reauire или include).
Все дирерктории аддонов по умолчанию в этот путь включаются, если аддон включен и установлен.
Для того чтобы добавить специфическую папку нужно выполнить вот эту строку:
Registry::get('class_loader')->addIncludePath('\\путь\\до\\папки\\с\\классами');
Дирректория с аддоном может содержать как свои пространства имён так и классы расширяющие Tygh. Можно увидеть на примере аддона StoreImport.
При выполнении вставки в базу данных использовать плейсхолдеры для значения полей. Для вставки данных, не перечислять поля вручную, а использовать массив с плейсхолдером ?e или ?u!
$a = array(
'col1' => $some_var,
'col2' => CONST,
'col3' => 'text'
);
db_query('INSERT INTO ?:mytable ?e', $a);
Перчень плейсхолдеров (placeholders) можно найти в девелоперской документации database-standards Обязательно разделять строки более 120. Автоперенос строк в данном случае не спасает потому как переносит как ему вздумается, в результате я бы не сказал что код от этого становиться понятнее.
Запрос необхомо разделять следующим образом (кавычки и точки должны жестко соблюдаться):
$partner_balances = db_get_hash_array(
"SELECT pa.partner_id, u.user_login, u.firstname, u.lastname, u.email, SUM(amount) as amount"
. " FROM ?:aff_partner_actions as pa"
. " LEFT JOIN ?:users as u ON pa.partner_id = u.user_id"
. " LEFT JOIN ?:aff_partner_profiles as pp ON pa.partner_id = pp.user_id"
. " LEFT JOIN ?:affiliate_plans as ap ON ap.plan_id = pp.plan_id AND ap.plan_id2 = pp.plan_id2"
. " AND ap.plan_id3 = pp.plan_id3"
. " WHERE pa.approved = 'Y' AND payout_id = 0 ?p ?p"
. " ORDER BY $sorting $limit",
'partner_id', $condition, $group
);
Закрывающая скобка обязатально переносится на новую строку! Таким образом мы выделяем нашу многострочную структуру в единый блок, что облегчает чтение кода.
- Никогда не нужно использовать собаку @ перед переменными или функциями - это плохой стиль программирования, ведущий к большому количеству ошибок. Например в php5 (а другой мы и не используем), передавая переменную с собакой по ссылке - на самом деле по ссылке она не передается, а просто копируется.
- Нельзя допускать появление любых ошибок, выдаваемых php-интерпретатором - warning, notices и т.п. Несуществующие переменные, неправильные типы переменных и т.п. должны обрабатываться в коде
- Не использовать функцию current() для получения значения массива во всех случаях (например, есть у вас массив, вы знаете что в нем одно значение и хотите это значение получить - в этом случае надо использовать reset), кроме того когда вам ИМЕННО нужно получить текущее значение (хотя в принципе данный прием использовать у нас негде, т.е. все массивы перебираются foreach'ами). Причина, по которой не надо использовать эту функцию в том, что разные версии php по-разному выставляют внутренний указатель почему-то.
- Добавлять функцию в ядро без хуков и описания.
- Использовать HTTP_REFERER, если вам нужно отредиректиться туда, откуда пришли - передавайте redirect_url
- Минимальная версия PHP теперь 5.3
- Используем PSR стандарты (пока не полностью)
- Используем пространства имён
- Стараемся по возможности выносить всё в классы
- acme
- app - *папка application. агрегирует код магазина. Не должа быть доступна извне, кроме payments8
- addons
- sample_addon - пример аддона. Повторяет директорию app/ ядра
- controllers
- databse файлы БД TODO: перенести в var
- schemas
- Tygh
- SampleAddon - папка с классами определёнными в пространстве имён SampleAddon аддона
- lib
- sample_addon - пример аддона. Повторяет директорию app/ ядра
- controllers
- admin
- common
- customer
- functions
- payments
- schemas
- shippings
- Tygh - классы из пространства имён ядра
- lib
- addons
- design
- images
- install
- js
- addons
- sample_addon - папка с JS файлами аддона
- lib
- tygh
- addons
- stores
- var
- Вместо Registry::get('view') и глобальной перемнной $view используем Registry::getView()
- Вместо Registry::get('ajax') и глобальной перемнной $ajax используем Registry::getAjax()