«phpClub» — архив тем ("тредов"), посвящённых изучению PHP и веб-технологий.
someApprentice 2020/02/24 06:35:23  №1613334
>>1612570
>Для мессенджера советую в README добавить скриншоты/гифки, описание основных фич (что тут есть сложного и крутого), будет хорошее демо для собеседований. Перечислить использованные технологии и библиотеки. И подключенных ботов. README сухой и краткий и не очевидно, что за ним сложный интересный проект. А может получиться хорошая реклама твоих навыков, которая выделяет тебя среди других разработчиков.

Я хочу свой стартап закончить. Если не получиться, то оформлю README, в качестве запасного плана.

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

>Имхо, компромисс, выбранный Телеграм - неудачный. Шифровать надо 100% сообщений. Но, конечно, это тебе решать.
Мне нужно подумать об этом.

Приведите пожалуйста аргументы почему нужно шифровать 100% сообщений?

В противовес, здесь есть недостаток - нельзя сделать поиск по сообщениям.

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

И поделюсь сырыми мыслями которые крутятся у меня сейчас на этот счет:

Может быть Телеграм как шифровальный мессенджер неудачен, с этим я согласен, но как мессенджер сам по себе, нужно признать, чрезвычайно удобен. Им пользуются все, и такая популярность достигнута благодаря тому, что его юзабилити привычны пользователю. Что-то не знакомое отталкивает. Нужно повторить подобный трюк и сделать это лучше - вместо самопальных алгоритмов шифрования, который использует телеграм, я уже использую проверенный OpenPGP. Осталось только повторить интерфейс.

Я вижу как предложенный вами способ может сработать. Действительно, на расшифровку одного единственного сообщения, в независимости от его размера, уходят доли секунды. Проблемы начинаются когда сообщений несколько, и их дешифровать одновременно - начинаются перегрузы в памяти и приложение начинает работать медленней (и даже фризить если не использовать Web Worker). И расшифровка сообщений по одному может решить проблему. К тому же, вы предложили как можно вывести сообщения плавно и это может выглядеть симпатично. Я проведу тесты. Но если дешифровка будет проходить всё равно медленно или это будет выглядеть некрасиво, или будут технические проблемы в реализации, придётся отказаться от этого решения.

Пока вопрос:

>- показываем сначала блоки-плейсхолдеры вместо сообщений
Как рассчитать размер плейсхолдера? Сообщения могут быть разного размера, и после дешифровки, если размер плейсхолдера статичный, то будет выглядеть немного "дёргано".


Критический, всё же, момент то что нельзя сделать поиск по сообщениям.


>> Нужно добавить ещё urlname
>
>Это можно назвать login или slug.
Или username. Я только что посмотрел в api телеграма, и это называется так https://core.telegram.org/bots/api#user


>> participant, // Ссылка на собеседника, в случае приватной конференции, для публичных это поле остаётся пустым.
>
>Не лучше ли назвать partner/other_party?
А можно назвать ещё проще и лаконичней - party? Это может подразумевать под собой как раз другую сторону:

https://translate.google.com/?source=osdd#auto/ru/party

>Noun
>③ a person or people forming one side in an agreement or dispute.

>соучастник partner, accomplice, accessory, accessary, party, associate
>участник party, participant, member, partner, participator, partaker
>сторона side, party, hand, part, way, aspect


>> date,
>
>Может, лучше писать send_time?
>
>И еще по схеме БД - а не слишком ли много метаданных в открытом виде? Может, есть смысл хранить метаданные сообщения как JSON и шифровать, и хранить шифротекст в единственной колонке metadata ?

>Может, лучше писать send_time?
А почему нельзя назвать просто и лаконично date? Разве на очевидно что это дата сообщения?

>И еще по схеме БД - а не слишком ли много метаданных в открытом виде? Может, есть смысл хранить метаданные сообщения как JSON и шифровать, и хранить шифротекст в единственной колонке metadata ?
Какие метаданные, помимо даты, вы хотели бы чтобы шифровались?

Что касается даты, как я писал выше, если её зашифровать, то нельзя будет сделать выборку по первым или последним сообщениям.


>> Получить последние 20 Конференций для конкретного пользователя, вместе с последним Сообщением и вместе с Получателем если есть (в случае приватной конференции), и вместе с Получателями в случае с публичной конференции:
>
>Не очень понятно, зачем тут выбирать Получателей? Мы же выбираем только сообщения, адресованные данному пользователю? Я предполагаю, что если пользователь - участник конференции, то все сообщения в ней (не написанные им) адресованы ему. Или это на случай, если пользователь состоит в чате, но как-то забанен в нем? На случай игнора отправителя?
Да, я как раз думал об этом, что получателей не обязательно получать, потому что единственный случай, когда нужно получить их, это когда пользователь сам захочешь посмотреть кто состоит в беседе (публичной конференции).

Это как раз решает проблему с кешированием выше и проблему с сортировкой ниже.


По поводу оптимизации:

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

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

Сейчас у меня есть такие вопросы:

Как тестируются параллельное выполнение запросов?
Если написать скрипт, например на PHP, который будет обращаться к БД, он будет выполнять одну команду за другой, императивно. Нужно что-то, что будет выполнять запросы параллельно. Как это сделать?

>- БД работает хорошо только пока индексы помещаются в память. Размер индексов можно как-то увидеть, не помню как. А если таблица помещается в память - то вообще все чудесно. Хайлоад обычно предполагает, что все влезает в память, кроме может быть редко используемых данных. Помещаются данные в память или нет - очень важный фактор, влияющий на производительность.
Тяжело представить что таблица сообщений или конференций поместиться в память, потому что количество записей, в теории, будет катастрофически велико. Как с этим выйдет на практике?

И ещё я сейчас поймал себя на мысли, что ничего не знаю о том когда данные хранятся в RAM, я всегда думал что записи хранятся на диске. Я погуглил этот вопрос, и создаётся впечатление что PostgreSQL решает автоматически когда хранить данные в RAM а когда на диске, и предпочтительнее в RAM. Это верно?


>- для тестирования было бы здорово сделать генератор, который нагенерирует произвольное число пользователей, конференций, сообщений. По ним прогонять запросы по одному и параллельно для измерения реальной производительности.
А у меня есть подобный генератор, который заполняет базу тремя пользователями (Alice, Bob и Tester) и заполняет по сотне сообщений между ними: (☣ Warning! Code with a smell! ☣) https://github.com/someApprentice/Crypter/blob/master/api/src/Command/PopulateCommand.php

Следовало бы увеличить количество пользователей до сотен и количество сообщений до тысяч?

Будет ли лучше если я вместо выполнения HTTP-запросов создам объекты с помощью Faker и factory-muffin, и выполню все запросы к БД напрямую?
Ответы: >>1619949
Аноним 2019/12/05 14:55:08  №1538413
>>1537819

Смысла особого нету. Проще просто сделать CLI скрипт, при необходимости использующий сторонние библиотеки, например, для работы с БД.

В фреймворках есть возможность писать CLI команды и они могут использовать все возможности фреймворка. Вот пример для Симфони: https://symfony.com/doc/current/components/console.html (заметь, что использовать компонент можно и без Симфони).

>>1537720

Быстро напишу основные замечания, после их исправления посмотрим дальше:

https://github.com/j835/student-list/blob/master/SQLdump.sql#L42
> DEFAULT CHARSET=utf8;

Самостоятельно нагугли различия между utf8 и utf8mb4 в MySQL. Если кратно, utf8 это сильно урезанная версия настоящей UTF-8, не поддерживающую, например, эмодзи.

> studentName
Нет особого смысла дублировать название таблицы (students) в имени поля.

> `studentGender` varchar(8) NOT NULL

Стоит использовать ENUM.

> studentCookie` varchar(255) NOT NULL
Нужен комментарий к полю.

В README ничего не написано про конфиг и его настройку.

> Registration.php
> table.php
Нужно называть файлы единообразно.

Вместо 3 автозагрузчиков стоит сделать одну функцию, перебирающую пути. А в идеале - освоить композер и сгенерировать автозагрузчик им.

> $newUser = !isset($_COOKIE['student']);
Логику определения залогиненности надо не размазывать по коду, а оформить в функцию или метод.

В таблице стоит сделать индикацию того, какая выбрана сортировка и по какой колонке. Также, желательно при смене сортировки, страницы, параметров поиска менять URL страницы, а при загрузке страницы, наоборот, брать параметры показа из URL. Это позволит делиться ссылкой на конкретный фрагмент таблицы. Поищи HTML5 history и событие popstate, чтобы узнать, как это сделать. И протестируй тщательно обновление страницы, навигацию вперед/назад, закрытие и открытие вкладки обратно, если решишь делать.

> href=<?php echo "/css/bootstrap.min.css"?>>
Непонятно, зачем тут echo

https://github.com/j835/student-list/blob/master/public/js/mainTableHandler.js#L23
Тут ты по сути вынес кусок шаблона в JS. Это затрудняет правку шаблонов. Было бы лучше сделать "шаблон" одной кнопки в коде (тег HTML5 template: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template ), а в JS уже клонировать его нужное число раз.

Либо поместить шаблон пагинации в тело страницы, и подключить готовый шаблонизатор в JS коде.

> fetch('ajax/view.php', {

Нет обработки ошибок, индикатора занятости, почитай урок https://github.com/codedokode/pasta/blob/master/js/ajax.md

Также, для получения данных логично использовать GET, а не POST. Также, получение данных через fetch() надо вынести в функцию, а не копипастить несколько раз.

> body: "query=" + searchInput.value,

Данные в application/x-www-form-urlencoded надо экранировать с помощью encodeURIComponent, читай урок https://github.com/codedokode/pasta/blob/master/network/urls.md

Также, мне кажется, JS файлы удобнее называть точно так же, как контроллер и шаблон.

> let headers = document.querySelectorAll('th');
Плохой селектор. Что, если на страницу добавят еще одну таблицу? Надо привязываться к ячейкам конкретной, а не любой таблицы.

Код в public/ajax логичнее оформить как контроллеры с роутингом и с шаблонами, а то у тебя получается две архитектуры зачем-то сделано.

https://github.com/j835/student-list/blob/master/public/ajax/view.php#L11
> $orderBy = 0;
> $desc = 0;
Странные значения по умолчанию.

> echo 'Ничего не найдено';
Это должно быть в шаблоне.

ajax/view.php и ajax/search.php можно объединить в один контроллер, они почти одинаковые.

В поиске неправильно работает пагинация и нет сообщения об отсутствии результатов. Нет кнопки отмены поиска.

> $qorder = mysqli_real_escape_string($this->connection, $order);
В Mysqli есть подготовленные запросы с параметрами, советую использовать их.

В mysqli ты не обрабатываешь и не логгируешь ошибки. Надо либо включить использование исключений, либо проверять каждую mysqli функцию на ошибки, как тут: https://www.php.net/manual/ru/mysqli.examples-basic.php

> $result = mysqli_fetch_assoc($result);
> return $result['COUNT(*)'];
есть функция получения одного значения сразу.

Поиск можно реализовать на уровне БД. В твоем коде поиска стоит не писать все длинной стеной, а разделять на функции, например, можно выделить функцию match($student, $query): bool

В функциях и методах надо использовать тайп-хинты.

Стоит разделить отдельно работу с БД и отдельно валидацию. Непонятно, почему они помещены в один класс. Из валидации убрать экранирование данных. Экранирование должно делаться в той же функции, что делает запрос, чтобы было видно что данные экранированы.

> $student->setSurname(mb_strtoupper(mb_substr($surname, 0, 1)) . mb_strtolower(mb_substr($surname, 1)));
Изменение регистра первой буквы надо вынести в отдельную функцию, а не писать код длинной стеной.

Пол надо сделать константами в классе Student (GENDER_MALE).

Желательно, чтобы функция валдидации возвращала причину ошибки.

HTML-код из класса Printer надо перенести в шаблон. Функция сортировки там, наверно, не нужна.
Ответы: >>1538462
Аноним 2019/10/07 21:39:27  №1491406
bug2.png (23, 633x158)
158x633
bug3.png (35, 602x365)
365x602
>>1490764

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

Вместо "выберите файл..." логичнее писать "нажмите, чтобы выбрать файл (или открыть диалог выбора файла) или перетащите файл сюда".

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

Ты пишешь "размер 107.9 KB", но в России числа пишутся через запятую, а "килобайт" как "Кб". Нелогично смешивать российский и западный стиль в одной фразе.

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

Еще один баг - кривая дата загрузки на пикрелейтеде.

В списке файлов у файла есть крестик, но нет подсказки при наведении на него и непонятно, что он делает.

Выпадающее окно "фильтры" сделано неудачно, так как оно визуально не отделено от остальной страницы и сливается с ней. Непонятно, что это попап. Тебе надо бы сделать ему тень или четкие границы, чтобы было видно, что это попап над страницей, а не ее часть. Списки тоже нарисованы неудачно:

- справа нет линий (картинка bug3.png)
- непонятно, что значит пунктирная линия и что сплошная. Лучше было не выпендриваться и выделять выбранный пункт подсветкой. Также, имхо, толстые линии неудачно смотрятся.
- кнопка Фильтры не становится выделенной, когда попап раскрыт
- если открыть фильтры, а затем кликнуть в поле поиска, они не скрываются. Если сделать поиск, то попа фильтров перекрывает надпись "ничего не найдено". Нужно скрывать фильтры при перемещении фокуса табом или клике за их пределы.

Мне кажется, не надо было изобретать свои нестандартные списки, а надо было сделать что-нибудь похожее на существующие списки. В плане дизайна, раскрытие окна фильтров требует лишний клик и я бы предпочел просто панель такого типа:

Показать: [ мои файлы | все файлы ] Сортировка: [ дата ^ ]

То есть думай не только о красивости, но и об удобстве. Раскрывающаяся панель экономит место (которого вообще-то достаточно), но взамен требует лишние 2 клика на раскрытие/скрытие и не позволяет увидеть, что спрятано, без клика по ней.

Не очень понятно, зачем кнопка "найти", если поиск работает без нее.

Нет ссылки на страницу одного файла, а как тут поделиться ссылкой-то? Прямой ссылкой на файл делиться?

В попапе "Удалить этот файл" кнопки слишком маленькие в сравнении с остальным сайтом, выбиваются из стиля. Цвет обводки дает слишком маленькую разницу, лучше было сделать одну кнопку залитую цветом целиком (например, "Да" залить красным, а "Нет" сделать нейтральной). Плохо, когда у тебя каждая кнопка в своем стиле, лучше иметь несколько стилей и везде их использовать, чтобы все смотрелось единообразно.

Не выполняются правила работы с аяксом: https://github.com/codedokode/pasta/blob/master/js/ajax.md

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

Протестировал ли ты поддержку картинок с анимацией (gif), с полупрозрачностью (png)? Хотя, вроде как вижу такие картинки.
Ответы: >>1491422
Аноним 2019/08/06 09:11:54  №1448828
>>1448812

Заголовок таблицы делается ссылкой, содержащей нужные параметры. Вот старый пост, где это обсуждалось: https://phpclub.tech/pr/res/1331378.html#1351739

Еще можно поискать в архиве по словам "ссылка сортировка", может что-то полезное найдешь? https://phpclub.tech/search/?q=%D1%81%D1%81%D1%8B%D0%BB%D0%BA%D0%B0+%D1%81%D0%BE%D1%80%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%BA%D0%B0

Аякс ту не нужен, хотя с ним можно было бы сделать сортировку без перезагрузки страницы. Аякс желательно прикручивать поверх обычных ссылок (по принципу progressive enhancement), чтобы по-прежнему можно было скопировать и сохранить ссылку на страницу в определенном состоянии. Есть (была) библиотека pajax , которая большую часть работы берет на себя.

Задавай уточняющие вопросы, если что-то непонятно.
Ответы: >>1448829
Аноним 2019/08/06 09:08:20  №1448826
>>1448823
>Сортировка делается по ссылке
Понял, спасибо
Аноним 2019/08/06 08:53:24  №1448823
>>1448812
На этом задании джаваскрипт вообще не нужен. Только вёрстка и бэкенд. Сортировка делается по ссылке.
Просто посмотри как у других сделано.
Ответы: >>1448826
Аноним 2019/08/06 08:32:43  №1448812
Аношки, тоже взялся за студентов, как анины выше, но пока читаю условие и обдумываю. Возник вопрос:
>Список абитуриентов — выводит имя, фамилию, номер группы, число баллов. Выводятся по 50 человек на страницу, сортировка по любому полю делается кликом на заголовок колонки таблицы
а как вообще это реализуется? Мне это делать джаваСкриптом/джиКверри, типа перестраивать ячейки таблицы по клику, или силами пшп-скрипта - по клику отправлять новую ссылку на эту же страницу с новыми параметрами сортировки и перезагружать результат?
Как обычно это делается? Я с аяксом пока не очень знаком
Ответы: >>1448823 >>1448828
Аноним 2019/06/03 05:50:24  №1411154
>>1408224

По моему, ты вместо содержимого файла отдаешь просто его название в теле ответа. А надо отдавать содержимое.

Если ты используешь класс ответа из PSR-7, то там есть метод withBody, в который передается "поток", соответствующий файлу.

Также, обрати внимание на то, что надо передавать имена и пути к файлам по-разному:

- в заголовок Content-Disposition - только имя и символы ASCII без кириллицы. Есть также новый стандарт, который позволяет дополнительно к латинице передавать имя в utf-8 для новых браузеров. Изучить его было бы полезно.
- в X-Send-File - надо смотреть документацию, может полный путь от корня, а может относительно какой-то папки

Также, я не понял, зачем ты вызываешь $file->save(). Что ты хочешь сохранить в БД?

Синтаксис у тебя нормальный. Но я бы добавил отступ там, где длинная колбаса вызовов переносится. Я сам не очень люблю method chaining и предпочитаю обычный вызов методов.

>>1408250

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

>>1407003

Сортировку проще всего сделать ссылкой. Например, ссылкой вида:

/list?search=Ivan&sort=-name&page=3

Про генерацию таких ссылок было написано в старых тредах, например:

- https://phpclub.tech/pr/res/1331378.html#1351739
Аноним 2019/05/28 16:53:09  №1408166
>>1407629

А ты вообще понимаешь, чем POST отличается от GET? Формально методы определены в одном из RFC: https://tools.ietf.org/html/rfc7231#section-4

> The GET method requests transfer of a current selected representation for the target resource. GET is the primary mechanism of information retrieval and the focus of almost all performance optimizations.
Hence, when people speak of retrieving some identifiable information via HTTP, they are generally referring to making a GET request.

> The POST method requests that the target resource process the representation enclosed in the request according to the resource's own specific semantics.

Обычно GET используется для получения каких-то данных, а POST - для сохранения каких-то изменений на сервер. Очевидно, что для получения списка логичнее всего использовать именно GET. Например:

/students?search=Ivan&sort=-name&page=2

Я не очень понимаю, зачем тут POST вообще. Если ты хочешь использовать форму, то формы можно отправлять методом GET.

Как написали выше, GET имеет другие преимущества:

- возможность сохранить или переслать ссылку на результат
- возможность перемещаться назад/вперед по истории
- возможность кешировать ответ (тут, впрочем, это вряд ли нужно)

Обычно сортировку просто делают через ссылки в заголовке. Но, конечно, возможны другие варианты, например:

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

Аноним 2019/05/26 22:17:17  №1407133
>>1407119
Я просто заголовки таблицы делал ссылками с параметрами, ссылки эти функцией создаются, чтобы в шаблоне логики не было, параметры такие; по какому столбцу сортирую и в каком порядке, если не выбран никакой или выбран другой заголовок, то asc, если этот выбран был уже, то desc. Ну и поиск текущий тоже в параметры надо вставить, чтобы сортировка не дропалась.
Ответы: >>1407312