Содержание
Здравствуйте, уважаемые пользователи! В этом посте хотел бы поделиться своим личным опытом «наступления на грабли» во время разработки веб-приложений на языке программирования PHP. Текст будет интересен тем, кто хочет посмотреть на путь девелопера от «зеленого самоучки» до «профессионального разработчика», а также начинающим программистам, которые не против узнать о том, «как не нужно делать» и почему. Возможно даже кто-то вспомнит себя и улыбнётся. Возможно, кто-то просто улыбнётся с моего старого кода.
Все мое ознакомление с веб-разработкой начиналось в далеком 2009-м году с распечатанных непонятных и сложных справочников и руководств, а также ковыряния кода (очень плохого, кстати) open source CMS. Позже я начал использовать ООП и заботиться о «правильности», а ещё позже — замечательный фреймворк Yii, поэтому весь «хороший» код в посте будет демонстрироваться именно с использованием этого фреймворка.
1. Дублирование кода
Сейчас, смотря на свой код минувших лет, я понимаю, что это были действительно ужасные времена. У меня дублировалось практически всё. Функции писались редко, а один и тот же код можно было увидеть в трех-четырех (а то и больше) местах. Почему я так делал? Ну естественно потому что я был неопытным. Это раз. И потому что считал (глупо считал), что написание функции — это трата времени и что гораздо проще просто cкопировать одно и тоже несколько раз.
Что было раньше:
$sql = 'SELECT `id` FROM `days` WHERE `sta`=1'; $r=mysql_query($sql,$conn); $row=mysql_fetch_row($r); $curday=$row[0]; if (($curday>=3 && $curday<=15) || ($curday>=45 && $curday<=55) || ($curday>=90 && $curday<=105)) { $on_off=1; }else{ $on_off=0; }
Что есть сейчас:
$curDay = BasicFunctions::getCurrentDay(); $transferStatus = BasicFunctions::getTransferStatus($curDay); if ($transferStatus == 1) { ... }
Позже я понял, зачем делать «хорошо»:
Самая важная причина создания метода — снижение сложности программы. Методы создаются для того, чтобы можно было скрыть информацию и не думать о ней. Другие причины создания методов — минимизация объема кода, облегчение сопровождения программы и снижение числа ошибок.
Ну и конечно же, методы позволяют выполнять оптимизацию кода в одном месте, а не в нескольких. Они облегчают профилирование кода, направленное на определение неэффективных фрагментов. Если код централизован в методе, его оптимизация повысит быстродействие всех фрагментов, в которых этот метод вызывается как непосредственно, так и косвенно.
2. Пересекание PHP и HTML
Да, я не использовал патернов проектирования. Да, я не использовал популярногоMVC. И это было второй моей фатальной ошибкой. Почему я так делал? Я почему-то не думал о том, что код когда-то может быть переписан/дописан/изменен. Всё «хардкорилось» чисто с расчетом на сиюминутную выгоду.
Что было раньше:
echo '<br /><br /><center><img src="img/cap.jpeg" alt="Капитан" title="Капитан"> <b>Капитан</b></center>'; $sql = 'SELECT * FROM `capitans` WHERE `club`='.$_SESSION['my_club']; $r=mysql_query($sql,$conn); $row=mysql_fetch_row($r); $idp=$row[2]; if (!isset($row[2]) || $row[2]=="") { echo '<center>Капитан команды не назначен</center><br>'; }else{ $sql = 'SELECT * FROM `players` WHERE `id`='.$idp; $r=mysql_query($sql,$conn); $row=mysql_fetch_row($r); .... }
Что есть сейчас:
$capitan = Capitans::model()->findByAttributes(array('club' => $user->club)); $this->render('team', array( ... 'capitan' => $capitan, ... )); ---------------------------------------- <?php if ($capitan == $item->pl_id) { ?> <i class="cap" title="Капитан" alt="Капитан"></i> <?php } ?>
Позже я понял, зачем делать «хорошо»:
Использования паттерна проектирования (при веб-разработке чаще всего это MVC) снижает сложность разработки за счёт готовых абстракций для решения целого ряда проблем. Например, логические части приложения разделяются на автономные области. Например, в том же MVC модель данных, пользовательский интерфейс и взаимодействие с пользователем разделены на три отдельных компонента. Зачем? Затем, чтобы модификация одного из компонентов оказывала минимальное воздействие на остальные.
3. Не использование фреймворка
Я года два писал всё сам и не использовал фреймворков. Я считал, что это что-то очень сложное и на изучение «этого» у меня нет времени. Есть люди, которые принципиально не используют фреймворки, например, потому что они считают, что фреймворк — это open source, который доступен каждому (т. е. каждый может просмотреть его код и найти узкие места). Эти люди, которые так считают, пишут свои фреймворки, которые используют в следующих проектах. Лично я считаю, что сейчас есть TOP-3 (или TOP-5) тех фреймворков, которые проверены на популярных проектах и отточены до максимума, поэтому резон писать что-то свое стремится к нулю. Если вы так не считаете, проголосуйте в конце поста.
Что было раньше (авторизация):
if (!isset($_POST['login']) || strlen($_POST['login'])<3) { $errorsArray[] = 'Не введён логин или он слишком короткий'; $sum=$sum+1; } if (!isset($_POST['pass']) || strlen($_POST['pass'])<3) { $errorsArray[] = 'Не введён пароль или он слишком короткий'; $sum=$sum+1; } if (!preg_match("/^([a-zA-Z0-9]+)$/", $_POST['login'])) { $errorsArray[] = 'В поле "логин" присутствуют недопустимые символы'; $sum=$sum+1; } if (!preg_match("/^([a-zA-Z0-9]+)$/", $_POST['pass'])) { $errorsArray[] = 'В поле "пароль" присутствуют недопустимые символы'; $sum=$sum+1; } $sql = 'SELECT * FROM `users` WHERE `login`="'.$_POST['login'].'"'; $r=mysql_query($sql,$conn); $row=mysql_fetch_row($r); if ($row[1]!==$_POST['login'] || $row[2]!==md5($_POST['pass'])) { $errorsArray[] = 'Неверный логин или пароль'; $sum=$sum+1; }
Что есть сейчас (авторизация):
public function actionLogin() { $model=new LoginForm; if(isset($_POST['LoginForm'])) { $model->attributes=$_POST['LoginForm']; // validate user input and redirect to the previous page if valid if($model->validate() && $model->login()) $this->redirect(Yii::app()->user->returnUrl); } // render form $this->render('login',array('model'=>$model)); }
Позже я понял, зачем нужно использовать фреймворк:
Любой фреймворк — это, в первую очередь, набор уже готовых к использованию стандартов, которые используют, тратя намного меньше времени, разработчики. Кроме этого, вокруг популярных open sourse фреймворков собирается серьезное сообщество разработчиков. Что это значит? А это значит то, что будет легко подключить или заменить человека в команде работающей над проектом. Плюс, комьюнити создает массу готовых сторонних решений.
4. Классы? Не, не слышал
Классы не использовались вообще. Хотя, при описанных выше «ляпах», о каких классах может идти речь :)
Что было раньше:
include_once("inc.php"); include_once("head.php"); include_once("system.php"); if (isset($_SESSION['login']) && isset($_SESSION['password'])) { if (!isset($_GET['id']) || $_GET['id']=="") { ... } }
Что есть сейчас (класс с набором базовых функций, которые используются по всему приложению):
/** * Class BasicFunctions * Same functions for all controllers */ class BasicFunctions { ... /** * Get info about last registered users * @ param type $limit - limit * @ return array - users */ public static function getLastRegUsers ($limit) { $criteria=new CDbCriteria; $criteria->limit = $limit; $criteria->order = 'user_id DESC'; $users = Users::model()->findAll($criteria); return $users; } ... }
Позже я понял, зачем делать «хорошо»:
Самая важная причина создания класса — это снижение сложности. Класс создается для сокрытия информации, чтобы о ней можно было не думать (да, точно так же не думать, как и в случае с методом). Вы сможете забыть о деталях и использовать класс, не зная о его внутренней работе. Другие причины создания классов — минимизация объема кода, облегчение сопровождения программы и снижение числа ошибок (да, точно так же, как и в случае с методом).
5. Не использование LIMIT’ов, JOIN’ов, запросы в циклах
Если бы сейчас существовала инквизиция, то на огонь попадали бы все начинающие программисты, которые занимаются такой ересью :) Это, наверное, самый смертный грех программиста. Такими действиями я просто убивал быстродействие своих приложений. А, как вы знаете, быстродействие — это один из основных факторов, которые влияют на то, вернется ли второй раз к вам пользователь или нет.
Что было раньше (случайный код):
$sqlo = 'SELECT `id`, `pos` FROM `players`'; $ro=mysql_query($sqlo); while($rowo=mysql_fetch_row($ro)) { if ((int)$rowo[0]==(int)$ex2[0]){ $r_tmp_2 = mysql_query("SELECT `pos` FROM `pos` WHERE `id`=".$rowo[1].""); $row_tmp_2=mysql_fetch_row($r_tmp_2); ... } }
Что есть сейчас (случайный код):
if ($addNewBuild = Yii::app()->request->getQuery('addNewBuild')) { $building = Buildings::model()->findByPk($addNewBuild); // Ищем первую запись по Primary Key и возвращаем её, если она найдена if ($building) { ... }else{ ... } }
Позже я понял, зачем делать «хорошо»:
Вы должны заботиться о быстродействии своих приложений. Вы должны заботиться о правильности написания SQL-запросов. Именно SQL-запросы составляют большую часть приложения и именно они в основном влияют на быстродействие. Использование LIMIT’ов помогает избежать проходов по всем записям таблицы, а использование JOIN’ов помогает избежать циклических запросов.
Вот такая вот история. Всем спасибо за интерес, внимание, и улыбки (надеюсь, они у вас были). Если вам этот пост понравился, в следующем расскажу о том, как я «вышел» на фреймворки и на Yii в частности.