Guest User

https://github.com/nsdvw/file-sharing

a guest
Aug 30th, 2015
302
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 19.30 KB | None | 0 0
  1. Я успел проверить не 100% кода, а где-то 70%, но думаю, найденных ошибок и замечаний тебе вполне хватит для начала. Код определенно надо улучшать.
  2.  
  3. > https://github.com/nsdvw/file-sharing/blob/master/db.sql#L25
  4. > CREATE TABLE comment (
  5. Если ты делаешь древовидные комменты, то в таблице стоит сделать ссылку на родительский комментарий как минимум. Она поможет например при необходимости восстановить или перепроверить значения в materialized path. Да и позволит легко определять кто родитель. Ссылка должна быть сделана с внешним ключом разумеется.
  6.  
  7. LoginManager надо сделать нормальным, не статическим классом и синглетоном в Слиме. Нужные ему зависимости (маппер) передавать через конструктор. Копипасту, где ты получаешь отдельный объект PDO прямо в классе, надо убрать.
  8.  
  9. Форму логина, встроенную в шапку, надо переделать так, чтобы с любой страницы она отправляла на отдельную страницу логина, где выводятся ошибки, если они есть (а если нет то после логина делается редирект на исходную страницу).
  10.  
  11. Тогда нам не надо в каждом действии передавать переменные $loginError = '', $loginEmail = '', $loginPassword = '' и не надо их указывать в шаблоне, так как они всегда пустые.
  12.  
  13. У тебя очень много копипасты в index.php, программист должен не копировать код, а думать как этого избежать.
  14.  
  15. > if (LoginManager::login()) {
  16. > $login = true;
  17. Вместо того чтобы это писать в каждом действии, лучше в самом начале передать объект LoginManager в смарти.
  18.  
  19. > $title = 'FileSharing — upload file';
  20. Опять же, название по умолчанию можно передать один раз в начале index.php
  21.  
  22. > $bookmark = 'Upload';
  23. Чтобы это не копипастить, можно их передать через view->appendData один раз в начаел index.php.
  24.  
  25. > $app->post('/login',
  26. > $app->get('/login',
  27. Не вижу смысла делать тут 2 функции ради одной страницы. Лучше объединить их в одну, которая обрабатвает и POST и GET.
  28.  
  29. > $app->get('/logout',
  30. так как логаут изменяет состояние системы, его надо делать через POST.Также, защити-ка его CSRF токеном, для работы с токенами стоит сделать конечно отдельный универсальный класс, который можно подключить к любой форме, с методами сгенерировать токен если надо, получить токен, проверить токен: https://github.com/codedokode/pasta/blob/master/security/xsrf.md
  31.  
  32. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L138
  33. > $uploadError = (isset($_GET['error']))
  34. При ошибке ты не должен редиректить. Ведь в этом случае пользователь не сможет повторно отправлить файл на загрузку нажатием кнопки F5. Соответственно эта проверка тоже не нужна. При ошибке надо просто снова выводить форум загрузки. По этой причине удобно объединить вместе функции / и /upload_file
  35.  
  36. > $file->size = ViewHelper::formatSize($file->size);
  37. Это неправльно. Почему одно и то же поле хранит значение в разных форматах? Как в таком коде ориентироваться, когда непонятно в каком формате там лежит значение? Так быть не должно.
  38.  
  39. > echo json_encode($file);
  40. При отдаче JSON надо выставлять заголовок Content-Type: application/json. Также, ты не должен применять json_encode к объекту, ну сам подумай, это же не массив а объект, как его можно преобразовывать в JSON? Это чревато раскрытием каких-то данных которые пользователь знать не должен, ведь твой код никак не проверяет какие поля можно отдать, а какие нельзя.
  41.  
  42. Этого быть не должно. Лучше сделать из класса массив только с перечисленными полями и его уже преобразовывать в JSON. Метод получения массива можно поместить в класс.
  43.  
  44. > echo 'error';
  45. Это неправильно, ты в одном случае отдаешь JSON, а в другом текстовую строку, как ты отличишь одно от другого? Как ты передаешь в чем именно ошибка? Логично при ошибке тоже отдавать JSON.
  46.  
  47. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L178
  48. > if ($user->hash !== sha1($user->salt . $loginForm->password)) {
  49. не надо копипастить по всему коду метод вычисления хеша, надо сделать отдельную функцию или метод.
  50.  
  51. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L181
  52. > LoginManager::setSession(array('id'=>$user->id, 'hash'=>$user->hash,));
  53. Метод SetSession неправильный, у LoginManager должен быть метод «залогинить пользователя», принимающий на вход например объект User, а не метод принимающий массив неизвестного вида. Не стоит заменять объект на массив. Метод може выглядеть так: $loginManager->autorizeUser($user);
  54.  
  55. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L215
  56. > if (!LoginManager::isLoggedIn()) {
  57. > $author_id = null;
  58. > } else {
  59. > $author_id = $_COOKIE['id'];
  60. > }
  61. Вся работа с авторизационными куками должна быть в классе LoginManager, а не размазана повсему коду. В данном случае логично у класса сделать метод getLoggedUser() который возвращает либо объект User либо null.
  62.  
  63. > $request_method = (isset($_GET['ajax'])) ? 'ajax' : 'direct';
  64. Тут лучше использовать переменную $isAjax с значениями true/false. А то возникает вопрос например почему тут не используются константы вместо строк.
  65.  
  66. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L224
  67. > $app->response->redirect("/?error=$error");
  68. При ошибке не надо никуда редиректить, чтобы пользователь мог отправить форму повторно.
  69.  
  70. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L242
  71. > $app->response->redirect('/view/?upload=ok');
  72. Ты при успешной загрузке редиректишь на страницу в пути к которой не указан id файла. А как пользователь тогда получит ссылку на файл? Логичнее редиректить его на страницу файла.
  73.  
  74. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L294
  75. > $user->fromForm($registerForm);
  76. Это как-то не очень правильно, получается у нас модель пользователя должна знать о форме его редактирования. Это неправильно так как модель ничего не должна знать о внешнем мире и как она редактируется. Вместо этого в модели формы должен быть метод, возвращающий или заполняющий модель пользователя.
  77.  
  78. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L303
  79. > $registerError = $registerForm->errorMessage;
  80. > $registerLogin = $registerForm->login;
  81. > $registerEmail = $registerForm->email;
  82. Вместо того чтобы передавать переменные по отдельности, логичнее передать в смари саму форму.
  83.  
  84. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L415
  85. > $comment->level = Comment::getLevelFromPath($comment->materialized_path);
  86. Вместо этого надо сделать метод getLevel()
  87.  
  88. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L416
  89. > $comment->author_id = $app->userMapper->findById($comment->author_id);
  90. запрашивать записи по одной неэффективно. Надо собрать id авторов, выбрать их одним запросом, присоединить к комментариям. И делать это надо не в index.php, а в маппере.
  91.  
  92. > $app->get('/view/:id',
  93. > $app->post('/view/:id',
  94. думаю, это надо объединить вместе в одну функцию. В любом случае, копипастить огромный кусок кода как у тебя, неправильно.
  95.  
  96. > https://github.com/nsdvw/file-sharing/blob/master/public_html/index.php#L471
  97. > if ($file->isImage()) {
  98. > } elseif ($file->isVideo()) {
  99. тут же всего лишь выбор шаблона для отображения? Тогда это логичнее прямо в шаблоне и делать.
  100.  
  101. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/lib.js#L76
  102. > var previewEl = document.createElement('div');
  103. Вот это тоже не очень удачный код. Фактически у тебя тут по частям создается кусок HTML кода. Но раз нам нужен HTMl код, почему мы его маскируем под JS и размазываем на 30 строк? Сделай в HTML файле шаблон окна превью:
  104.  
  105. <script id="tpl-preview" type="text/x-template">
  106. <div class="a">{name}</div>
  107. <a href="{href}"...
  108. ...
  109. </script>
  110.  
  111. Тогда ты можешь в JS получить текст шаблона, сделать там подстановки функцией replace() и вставить в DOM. Что-то похожее описано например тут: https://learn.javascript.ru/templates
  112.  
  113. В общем HTML-код должен быть в HTML файле. При подстановке не забудь про экранирование спецсимволов а то получим этакий XSS.
  114.  
  115. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/list.js#L3
  116. > for (var i=0; i < files.length; i++) {
  117. > files[i].addEventListener('click', function(){
  118. не надо ставить 100 обработчиков. События всплывают, и достаточно поставить один обработчик на родительский элемент (это кстати разбирается в наших задачах на JS, советую порешать). Описано например тут: https://learn.javascript.ru/event-bubbling
  119.  
  120. > removeClass(files[i], 'file-selected');
  121. > addClass(files[i], 'file-nonselected');
  122. Нет смысла делать 2 класса, достаточно один, для выделенного файла.
  123.  
  124. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/list.js#L15
  125. > removeClass(icons[k], 'file-icon-revert');
  126. Зачем отдельный класс на иконку? Средствами CSS наверно можно ее выделить или что там надо сделать.
  127.  
  128. > currentChild.getAttribute('class').indexOf('file-icon') != -1)
  129. Эта же проверка сработает на класс «some-file-iconic». Она неправильная. Также, ее надо вынести в отдельную функцию hasClass()
  130.  
  131. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/list.js#L28
  132. > var xhr = getXmlHttp();
  133. Отправку аякс-запроса не надо вписывать прямо в код (у тебя там и так гигансткая простыня вышла), надо сделать функцию такого типа:
  134.  
  135. getWithAjax({
  136. url: ...,
  137. dataType: 'ajax',
  138. success: function(result) {
  139. // получает уже разобранный ответ в случае успеха
  140. },
  141. error: function (xhr, errorText) {
  142. // вызывается при ошибке
  143. }
  144. });
  145.  
  146. При работе с аяксом надо проверять HTTP-статус ответа, что он равен 200. Почитай например тут: https://ru.wikipedia.org/wiki/%D0%A1%D0%BF%D0%B8%D1%81%D0%BE%D0%BA_%D0%BA%D0%BE%D0%B4%D0%BE%D0%B2_%D1%81%D0%BE%D1%81%D1%82%D0%BE%D1%8F%D0%BD%D0%B8%D1%8F_HTTP
  147.  
  148. При работе с аяксом надо обрабатывать ошибки и давать пользователю возможность отправить запрос повторно.
  149.  
  150. Также, я не уверен, а нужен ли тут аякс и не проще ли JSON с информацией о файлах внедрить прямо в страницу?
  151.  
  152. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/main.js#L1
  153. > window.onload = function () {
  154. Использовать onload плохая идея так как она ждет загрузки всех ресурсов и картинок и обычно поздно срабатывает. А достаточно хоть одной картинке тормозить при загрузке (например сторонний счетчик какой-нибудь) и она сработает с большой задержкой. Пользователь может уже файл успеет выбрать.
  155.  
  156. В данном случае лучше явно вызвать прямо из шаблона нужную фунцкию:
  157.  
  158. <script>
  159. doSomething();
  160. </script>
  161.  
  162. Или к кнопке приписать в коде <button onclick="doSomething(event)">. По моему это и проще и удобнее и работает с момента появления кнопки на странице.
  163.  
  164. Также, в случае с формой, у нее есть событие submit и ты дложен использовать его. В чем разница между событием submit у формы и click у кнопки отправки формы, и почему надо использовать submit, я оставляю разобраться тебе в качестве домашнего задания.
  165.  
  166. Отправку файла методом POST через аякс стоит сделать отдельной функцией. Вывыод ошиьки отдельной функцией. Проверку поддержки нужных функций в браузере отдельной функцией. Увеличение прогресса отдельной функцией.
  167.  
  168. Вообще, лучше отучайся писать простыни — их тяжело читать и неудобно править.
  169.  
  170. > https://github.com/nsdvw/file-sharing/blob/master/public_html/js/main.js#L45
  171. > var fullWidth = +(progressBox.offsetWidth);
  172. Это ненадежно так как верстальщик может например добавить паддинг и алгоримт сломается. Вместо этого лучше воспользоваться возможностью задавать ширину в процентах.
  173.  
  174. Также, в HTML5 есть элемент для отображения прогресса, может лучше его использовать?
  175.  
  176. Если у тебя jPlayer это отдельная библиотека, лучше наверно не смешивать ее с твоими файлами, а положить в отдельную папку как есть.
  177.  
  178. > https://github.com/nsdvw/file-sharing/blob/master/views/header.tpl#L8
  179. > src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
  180. не подключай скрипты с внешних источников когда можно скопировать файл к себе, иначе твой сайт перестанет открываться при их падении/зависании. Ты можешь увидеть такое в туториалах, но там это делают ради экономии времени, и чтобы не объяснять какой файл куда скопировать. Но у тебя же не тот случай.
  181.  
  182. > https://github.com/nsdvw/file-sharing/blob/master/views/register_form.tpl
  183. Нет escape
  184.  
  185. > https://github.com/nsdvw/file-sharing/blob/master/views/register_form.tpl#L6
  186. > <div id="initial-letter">f</div>
  187. > <div id="logo-text">ile-sharing</div>
  188. Это не нужно, делать отдельный див, так как в CSS есть псевдокласс :first-letter.
  189.  
  190. > https://github.com/nsdvw/file-sharing/blob/master/views/register_form.tpl#L26
  191. > <div class="register-field-input">
  192. > <div>
  193. А нужен ли тут лишний пустой див?
  194.  
  195.  
  196. > https://github.com/nsdvw/file-sharing/blob/master/views/video_player.tpl
  197. > https://github.com/nsdvw/file-sharing/blob/master/views/audio_player.tpl
  198. Эти файлы явно сделаны методом копипасты. от нее надо избавиться.
  199.  
  200. > https://github.com/nsdvw/file-sharing/blob/master/app/Helper/Pager.php
  201. В пейджере не должно быть SQL запросов, вся работа с базой должна быть в мапперах. Боее того, там это и не надо, задача пагинатора рассчитывать номера страниц, а число страниц вполне можно передать снаружи.
Add Comment
Please, Sign In to add comment