Правила, Стандарты и Рекомендации для проектов MstarProject

Материал из MstarProject Manual
Перейти к: навигация, поиск

Содержание

Обзор

Область применения

Этот документ предоставляет указания и ресурсы для разработчиков, работающих на MstarProject.

Цели

Хороший стандарт кодирования важен в любом проекте, и особенно там, где множество разработчиков работают над одним проектом. Наличие стандарта кодирования помогает гарантировать, что код высокого качества, с меньшим количеством ошибок, и легко поддерживается.

Редактирование данных правил

Данный текст, точно также как и сам код - не статичный и будет постоянно редактироваться и дополняться новыми текстами. Если есть необходимость какой-либо пункт изменить, дополнить либо переписать заново - нужно создать новую страницу для пункта, а линку на нее поставить в "discussion". На очередной общей встрече обсудим и примем коллективное решение, что с ним делать Важно, чтобы данные правила не навязывались единолично кем-то, а были коллективным решением.

Форматирование и соглашения по именованию

Отступы и Максимальная длина строки

Для отступа нужно использовать символ табуляции. Длину табуляции каждый может настроить в своей ИДЕ. Рекомендуемая длина строки составляет 100 символов, т.е. разработчики должны стремиться держать код как можно ближе к 100-символьной границе, когда это возможно. Однако более длинные строки также допустимы. Максимальная длина любой строки PHP-кода не должна превышать 130 символов.

Классы

Классы всегда пишутся большими буквами каждое новое слово.

class ManagerMessageMonitor extends Base 
{
//-----------------------------------------------------------------------------------------------
private static $sBasicAction = 'manager_message_monitor';
private $iMinSearchTextLength = 3;
//-----------------------------------------------------------------------------------------------
public function __construct() 
{
}
}

Для классов админ панели вначале идет буква "А":

class ADeliveryType extends Admin {
//-----------------------------------------------------------------------------------------------
public function __construct() 
{
}
}

Имена файлов

Все файлы должны писаться отдельно каждое слово с маленькой буквы в единственном числе и разделяемые подчеркиванием. Данное правило не распространяется на файлы классов и sql объектов в папках /class/ /include/sql/ .


Переменные и имена аргументов в методах

Первая буква - всегда строчная;

i - integer
s - string
b - boolen
a - array
o - object
d - double
in - string строка в котой значения разделены запятыми и  каждое значение экранировано кавычками  (для всавки в sql запрос в оператор in() )

Все остальные слова в имени начинаются с большой буквы

Обоснование Всегда можно легко определить, какие переменные поступили в метод в качестве аргумента.

Пример

class NameOneTwo 
{
 var $sEngine;
 var $aEngine;
 function StartYourEngines($iEngine, &$aEngine) 
 {
  $this->sEngine = $sEngine;
  $this->aEngine = $aEngine;
 }
}

Константы

Константы, редактируемые в файле лежат в одном файле /connect.php

$DB_CONF = array(
'User' => 'root',
'Password' => 'hastaMysql',
//...
);
$GENERAL_CONF

Массив $GENERAL_CONF также доступен в темплейтах и используется как

{if $aGeneralConf.ShowCounter}
Counters Code
{/if}

Все остальные константы проекта хранятся в базе и используются статическим методом

Base::GetConstant($sKey,$sDefaultValue)

Обязательно навзание конснтанты ($sKey) должно идти с префиксом: имя модуля или глобальная переменная. Пример: global:contact_email, manager_order:default_email

Для новых констант ввести префикс модуля с разделитем :

global:date_fornmat
price:lock_table

Базовая валюта

Валюта в таблице currency с айдишником 1 является базовой. Все остальные имеют коефициенты относительно базовой.

Стиль кодирования

Обрамление PHP-кода

<?
 $aCode=array();
?>

Обязательно следить, чтобы ни в конце файла не было лишних строк и символов, ни в конце каждой строки не было лишних пробелов и других символов для исключения перекомитов.

Строки

Одинарные кавычки используются в строках только в том случае, если в строке не может быть вставок. Пример: название поля Base::$aRequest['id']

Конкатенация строк:

$sText.=" some text '".Base::$aRequest['field']."' other text ";

Запрещены всякие вариации типа

" $sString " 
или " {$sString} " 

В темплейтах все сообщения и тексты только через:

{$oLanguage->GetMessage('message_code')}
{$oLanguage->GetText('text_code')}

Для админ темплейтов соответственно:

{$oLanguage->GetDMessage('message_code')}
{$oLanguage->GetDText('text_code')}


Для дат скл-ей нужно использовать метод DateFormat::FormatSearch()

if (Base::$aRequest['search_date']) {
$sWhere.=" and ".$sDateField.".post_date>='".DateFormat::FormatSearch(Base::$aRequest['search']['date_from'])."'
 and ".$sDateField.".post_date<='".DateFormat::FormatSearch(Base::$aRequest['search']['date_to'])."'";
}

В скле должно быть поле post_date_model - единый формат даты с единым названием для всех проектов и константой. Дефолтный - дата без времени. По умолчанию поведение календарей и дат в новых проектах from>= 00:00 to <=23:59, если другое не предустановлено.

Для данных пришедших от пользователя в обязательном порядке использовать метод String::FilterRequestData()

$aDirectorySto=String::FilterRequestData(Base::$aRequest['data'] ,
array('name','id_directory_city','description'=>array('is_html'=>true),'address','phone','url','discount','city_region'));
$aDirectorySto['id_user']=Auth::$aUser['id'];
Base::$db->Autoexecute('directory_sto',$aDirectorySto,'INSERT');

Массивы

В качестве разделителя слов для ключей ассоциативного массива используйте underscore ('_').

array(
'id_provider'=>1, 
'name_provider'=>"Name Provider",
)

Классы

Функции и методы

Предустановленные названия

View - табличка (ItemView если в классе несколько объектов)
Preview - просмотр одного (ItemView если в классе несколько объектов)
Get
Set
Add
Edit
Apply
CallParse - колбек
Create
Update
Import
Export
ChangeSelect - обновление выпадающего списка.

Для action обратное правило. Они должны заканчиваться на глагол. Исключение для индекса. И все слова в единственном числе.

Пример:

manager_auction_file_create , manager_auction_response_import, manager_auction_file_view

Новые свойства и методы дописываются в конец списка, если они не связаны с уже существующими. Для удобства чтения стоит грппировать методы и свойства, если между инми есть связь внутри класса.

Управляющие структуры

Из трёх существующих стилей расстановки фигурных скобок допустимы два, причём первому рекомендуется отдавать предпочтение:

открывающая скобка ставится под соответствующим оператором и на одном отступе с ним:

if ($condition)                      while ($condition)
{                                    {
    ...                                     ...
}                                    }

Unix-стиль расстановки фигурных скобок, когда открывающая скобка ставится на одной строке с соответствующим оператором, а закрывающая - на одном отступе с оператором:

if ($condition) {                    while ($condition) {
    ...                                     ...
}                                    }

Преимущество первого стиля заключается не только в психологии. Если вы работаете не в ZendStudio, а на сервере и используете текстовый редактор (например, vi или mc) то проще найти блок если скобки расположены одна под другой.

Пример

if ($bVeryLongCondition && $bSecondVeryLongCondition) //два очень длинных условия
{
  ...
}
elseif (...)
{
  ...
}
else (...)
{
  ...
}

Шаблоны

Предустановленные параметры шаблоны: массив template, bWidthLimit, sTopText, sText, sBottomText

Рефакторинг

Цель рефакторинга должна всегда превышать время по стоимости, потраченное на сам рефакторинг.

После рефакторинга за собой нужно удалять либо комментировать (и потом удалять) ненужные блоки, логику, файлы и таблицы (поля) во избежание захламления проекта ненужными и непонятными структурами.

Особенности верстки

Чекбоксы и радиобатоны обрамлять тегом <label>:

<label for="male">Male</label>
<input type="radio" name="sex" id="male" />

<label for="female">Female
<input type="radio" name="sex" />
</label>

Документация

Коментарии

Формат описания gotchas

Ключевое слово gotcha должно ставиться в самом начале комментария.

Комментарии могут содержаться на нескольких строках, но первая строка должна быть ясным и законченным по смыслу конспектом.

Комментарий должен включать в себя имя автора и дату замечания. Эти сведения содержатся и в документации исходников, но иногда бывает трудно их отыскать. Иногда случается, что потенциально опасный участок кода слишком долго не исправляется. Дата gotcha позволяет определить такие места. Указание на автора gotcha показывает, с кого нужно спросить.

Пример

// :TODO: apstar 20090319: возможны проблемы со скоростью работы
// Нам точно понадобится хэш-таблица, но пока
// мы используем линейный поиск.
// :KLUDGE: tmh 981225: потенциально небезопасное приведение типа
// Приведение понадобилось для восстановления производного типа. Возможно
// здесь понадобится виртуальный метод или шаблон.

Автор класса или соавтор

Если класс создается новый - нужно писать свое авторство. Если класс дополняется - нужно дописывать себя, чтобы другие участники могли знать, кто писал тот или иной таск

/**
* @author Dima Valegov
* @author Aleksandr Starovoyt
*/

Репозиторий

Формат репозитория:

project_name/trunk - основная ветка разработки
project_name/branch - место под создание веток разработки
project_name/tag - место для сохранения меток на завершенные этапы, если такое необходимо.

Обычно любой проект использует два репозитория, свой проектный и базовый, обновляемый через екстерналы. Комиты в базовый репозиторий не должны ломать старую логику работы, поэтому очень важно следить за этим и тщательно тестировать изменяемый код, так как после комита с багом ломаются все проекты, использующие данную версию базового репозитория.

Обязательно каждый комит должен быть с значимым комментарием на английском языке. Есть такие типы комитов: added:, changed:, deleted:, fixed: . Каджое изменения сопровождаеться заданием в джире ( за еденичными ислючениями) с короткими ключами (PT-25, MP-2, MD-12). К типу комитов необходимо добавить ключ задания.
Примеры комитов:

added:PT-25 add new functional 
changed:MP-10 change structure 
fixed:MD-12 method add to cart

Если комит меняет уже работающую на продакшине логику или может поломать уже работающий функционал - нужно комитить в ветку, назвав ее id_branched_from_name_of_branch. После тестирования нужно смерджить в транк, если все успешно - удалить ветку, если не успешно - ревертить комит в транк с ветки и продолжить комит в самой ветке. И так далее до тех пор, пока функционал не будет перенесен в транк. Ветки за собой нужно удалять.

Нельзя править файлы на сервере, кроме конфигов и тех, которые лежат в игноре. Только локально комит - и апдейт на сервере. Если на сервере остается незакомиченый файл - это баг, причем вылезет он очень непредсказыемо и непонятно, что произошло, так как сырцы просто ламаются. Особенно это касается index.php, .htaccess, etc.

Базы данных, таблицы, поля

База обновляется на сервере в тот момент, как какой либо разработчик добавляет либо меняет таблицу или поле. То актуальная база данных всегда лежит на сервере. Также при добавлении или удалении таблицы из базы необходимо отредактировать файл /cron/local/backup.sh - дампер базы проекта.

Все таблицы, поля и свойства полей подчиняются правилам создания файлов, то есть единственное число и разделение подчеркиванием. Pripary Key, если такой нужен в таблице - всегда называется id Reference Key должен называться по правилу id_reference_table. Для референс полей могут быть исключения, когда ключ ссылается на несколько таблиц (id_section) либо же на одну с разным значением (id_customer, id_manager - для таблицы user)

По возможности нужно использовать минимальный набор индексных полей, которые должны быть минимальной длинны (лучше всего целочисленные). Перечисления можно использовать, если в них будут статически 2-3 значения (m,g или cart,order). В остальных случаях нужно использовать дополнительные таблицы c минимальным набором полей смотри пример.
Пример:

CREATE TABLE `sample_table` (
 `id` int(11) NOT NULL auto_increment,
 `login` varchar(15) default NULL,
 `password` varchar(50) NOT NULL default ,
 `email` varchar(50) NOT NULL default ,
 `name` varchar(50) NOT NULL,
 `description` text NOT NULL,
 `visible` int(11) NOT NULL default '0',
 `num` int(11) NOT NULL default '0',
 `code_changed` varchar(50) NULL,
 `price` double(10,2) NOT NULL default '0',
 `number` int(11) NOT NULL default '1',
 `weight` double(10,3) default '0.00',
 `is_provider_payed` int(11) NOT NULL default '0',
 `volume_delivery_cost` double(10,2) default '0.00',
 PRIMARY KEY  (`id`),
 UNIQUE KEY `login` (`login`)
) ENGINE=InnoDb DEFAULT CHARSET=utf8;

Для полей вида visible, is_custom, is_featured нужно использовать integer(11)

Кодировка для таблиц и полей должна наследоваться от проекта. Если есть разнобой в общей логике кодировок - это баг и его нужно исправлять немедленно и уведомить остальных разработчиков, чтобы проверили и обновили у себя базу с сервера.

При склейке нескольких таблиц .* должно в запросе стоять только для значимой таблицы, все остальные поля должны быть перечислены вконце запроса со своим названием, если оно пересекается с предидущими. Пример:

select u.*, uc.name as customer_name
from user as u
inner join user_customer as uc on uc.id_user=u.id

Группировка префиксных таблиц

В конфиге для форума лучше вместо $table_prefix = 'phpbb_'; указать $table_prefix = 'phpbb__';

Использование транзакций

По возможность метод, который последовательно делает несколько обновлений в базе данных нужно использовать транзакции. Старт и Енд для транзакции должен быть в одном методе.

Удаление записей по крону

Если в таблице много данных и они часто уаляются - во избежания повышенной нагрузки на сервер нужно использовать поле visible = 1,0, NULL и дописывать в крон чистильщик свою таблицу. Используется только для частных случаев и т.н. "неудаляемых" записей (к примеру, таблица юзерс. NULL - удаленная запись, только она остается в базе навсегда)

Метод Db::GetAssoc

Для ассоциативных массивов всегда использовать эту прослойку. В самой скл функции если нужны варианты линейного или множественного массива в саму функцию добавлять aData['single'] или aData['multiple']. По умолчанию если не задан параметр будет использовано 'single'.
пример оформления:

function SqlAssocProviderRegionCall($aData) {

	if ($aData['all']) {
		$sWhere.=" ";
	} else {
		$sWhere.=" and pr.visible=1";
	}

	if ($aData['order']) {
		$sOrder=$aData['order'];
	} else {
		$sOrder=" order by pr.name ";
	}
	
	if ($aData['multiple']) {
		$sField.=", field1, field2 .....";
	}

	$sSql="
 	select  id, name"
	.$sField.	
	"from provider_region as pr
	where 1=1
	".$sWhere
	. $sOrder;

 	return $sSql;
}

Соединение таблиц

Для увеличения быстродействия присоеденять таблицы необохдимо по полю тип у которого будет integer и обязательно будет индекс у присоединяемой таблицы

SELECT ua. * , cg. * , uc. * , u. * , cg.name AS customer_group_name, pr.name AS region_name, uum.login AS manager_login, 
ua.amount AS   current_account_amount  
FROM user u 
INNER JOIN user_customer uc ON u.id = uc.id_user 
INNER JOIN user_account ua ON u.id = ua.id_user 
INNER JOIN customer_group cg ON cg.id = uc.id_customer_group 
LEFT JOIN partner_region pr ON uc.id_partner_region = pr.id 
INNER JOIN user_manager um ON uc.id_manager = um.id_user 
INNER JOIN user uum ON um.id_user = uum.id 
WHERE 1 =1 
GROUP BY u.id

Пустой параметр для SQL

Для исключения бага получения некорректной выборки из таблицы нужно использовать проверку ну пустоту и передавать -1 в запрос. Пример:

'id'=> ( Base::$aRequest['id'] ? Base::$aRequest['id']:'-1' ),

Удаление критических данных

Если данные критические для построение отчетов или выборок за текущий или предидущий периоды, то необходимо в функционале запретить их удаление. Для этого на основе поля visible (-1 архив,0,1) провести архивинование данных. Используя базовый метод архивинование. По умолчанию критическим данными являються пользователи (заказчики, поставщики, менеджеры).


Добавление, удаление и изменение таблиц и полей проекта

Каждый запрос на изменение схемы данных на сервере должен в обязательном порядке копироваться в соответственный таск по проекту с видимой ролью "Developers". Необходимо для возможности поиска и анализа по всем изменениям структуры данных проекта.

Deployment

Поля в базе обновлять всегда при локальном изменении. Если локальная база не соответствует удаленной - брать всегда удаленную и переписывать поверх своей. Новые таблицы нужно записать в файл дампера "/cron/local/backup.sh"

Перед тем, как комитить в транк репозитория - нужно подготовить таблицы. Если структура данных не совместима с вновь выкладываемой - максимально сузить промежуток обновления исходников и базы.

При изменении статического контента сайта дописывать номер текущего ревижина, чтобы исключить кеш в броузерах старых посетителей:

Resource::Get()->Add('/css/main.css',2);
Resource::Get()->Add('/libp/jquery/jquery.tools.min.js');
Resource::Get()->Add('/css/main.css',4);

Для включению статических файлов в новых проектах нужно использовать новый метод, который отрабатывает повторы и включает последнюю версию статического ресурса, если вставки в нескольких местах кода. Старые вставки в шаблоны по возможности рефакторить.

Запуск предсказуемых нагружаемых скриптов в "бизнес время" 9-18 с ограничением по времени выполнения 7 мин для всех программистов и всех проектов. Нарушения этого правила заказчиком фиксируются субъективно несколько раз в задание - после выносится вопрос о смене хостинг пакета.

Таск менеджмент - JIRA

Процедура обработки таска такая: по приоритетному проекту выбираешь из списка самый приоритетный таск, ставишь ему Старт прогрес - это говорит всем вотчерам, что таск отправлен в работу. После того, как таск сделан - обязательно комитишь и ставишь комент с указанием кода таска (PM-203) и тестиуешь новый функционал на продакшин сервере. Затем пишешь комент и описание того, что было сделано в данном таске и где можно посмотреть результаты работы. Если нужно - пишешь мануал для заказчика по работе с новым функционалом. И только после этого ресолвишь таск и выставляешь часы по нему с кратким коментом того, за что эти часы.

Обязательно у таска должна быть информация о компоненте проекта, к которому таск принадлежит и к какой версии этот таск относится. Если таск большой и содержит в себе несколько больших частей - обязательно для этих частей нужно создавать сабтаски и отчитываться по времени по ним.

За часы по таскам нужно отчитываться в тот день, в который время потрачено.

Оформление коментариев

Если это баг, или простой таск писать, то необоходимо сделать описание того, что сделано. Если это задание или добавление нового функционала, то необходимо сделать скриншот, с описательными данными, которые будут понятны не только программисту (руководителю) но и заказчику. Минимальные требования: показать на скриншоте какие обьекты добавились, и (если необходимо или на скриншоте не видно) описать кратко новый функционал.

Создание контента сайта

Шаблоны

Используеттся новый смартивский стиль {$aObject.field_code} вместо устаревшего [$object.field_code].

Переводы сообщений и текстов

Для кодов переводимых сообщений и текстов рекомендуется использовать английские символы в нижнем регистре. Коды в данном случае - регистронезависимые, то есть в базу вставляется символы код всегда в нижнем регистре для избежания дубликатов.

Для мультиязычных джаваскриптовых переводов используется конструкция буфера:

Base::$aMessageJavascript = array(
"vin_request_ok_auth_user"=> Language::GetMessage("Our manager contact with you soon"),
);

В коде джаваскрипта вызывается как

window.alert($("#vin_request_ok_auth_user").val()+"\n");

Регламенты

При создании нового проекта

Во время создания пула заданий по установке и настройке нового проекта добавляется задание
"Регламентные работы", в рамках которого ответственный за внедрение нового проекта должен выполнить следующее:
- установить модуль "Регламентные работы" на сайт, если он не установлен
- после привязки дизайна, обновления таблиц базы и модулей нового сайта перенести обновления кода в SVN
- перенести обновленную базу и поднять обновленный код на сервере, где будет располагаться сайт
- подключить последнюю версию текдока и картинок к сайту
- выполнить через модуль "регламентные работы" обновление брендов, моделей авто, синхронизировать переводы с сайтом irbis

При переносе проекта на сервер заказчика

Человек, перестраивающий DNS сайта с нашего сервера на сервер заказчика создает задание в jira "переезжающего" сайта проекта
"Регламент - сайт изменил местоположение" на ответственного по проекту.

Ответственный выполняет такие действия: - передает список крон заданий заказчику или сам их настраивает на новом сервере по согласованию с заказчиком;
- уведомляет заказчика о необходимости бекапирования базы сайта и сам настраивает новое бекапирование с согласия заказчика. Новое бекапирование выполняется на сервер заказчика, на новый сервер Рига (с добавлением доступа к нему от заказчика) и один из серверов бекапов (Лондон, Варшава). - на текущем сервере останавливает кроны проекта;
- архивирует соурс сайта с переносом архива в папку бекапа;
- удаляет сайт с /var/www/ и базу, оставляя архив сайта и последний архив базы на сервере где был сайт.