Это копия, сохраненная 5 июля 2018 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.
Да, в нашем треде отвечают почти на все вопросы, только не сразу.
Это тред для начинающих. Не написал за свою жизнь ни одной программы и имеешь тройку по математике? Ты наш человек.
Предыдущий тред был тут: >>1152267 (OP) . Остальные треды есть в архиве: https://phpclub.tech/ (там есть поиск, так что можно легко найти обсуждение какой-то задачи или ответы на свой старый пост) или ищутся в гугле по словам "клуб изучающих php" и в архиваче.
Мейлач лежит? Есть запасной тред на доброчане: /s/res/23225.xhtml#i46467
Что самое главное для программиста? Умение аккуратно оформлять код (как, написано во втором посте).
Правила: ведем себя воспитанно, помогаем новичкам, читаем учебники, решаем задачки, постим ссылки на решения, ОП их проверяет и дает советы и замечания. ОП заходит редко, где-то раз в 2-3 дня, у него мало времени, не жди его, решай задачки дальше. ОП отвечает на все вопросы по его задачкам и учебнику, а вот насчет каких-то других вещей - только если останется время. Но в треде немало анонимных экспертов разного уровня, так что вряд ли вопрос останется без ответа.
С чего начать
У нас есть свои уроки по основам PHP, они собраны и выложены по адресу http://codedokode.github.io/phpbook (вас отредиректит на другой домен, не читайте, не сохраняйте, не запоминайте его, он временный). Это учебник для изучающих с нуля, то есть если ты вообще ничего не знаешь, то можно начать с него. Он простой и понятный. Там есть задачи, их нужно решать (чтобы стать программистом, надо писать код — иначе никак). Пости ссылки на решения в тред, мы их проверим, напишем замечания и дадим советы по улучшению. С другой стороны, если этот учебник тебе не нравится, можно читать любой другой. Или официальный мануал. Или все сразу.
Устанавливать пока что ничего не требуется, разве что редактор кода вроде Sublime Text 3, Notepad++, Visual Studio Code, Netbeans PHP или PhpStorm (с ним будет удобнее).
Если не знаешь как решать, запости код, напиши в каком месте остановился и попроси подсказку.
Ты прошел весь учебник? Молодец, но это были лишь основы языка PHP, этого недостаточно. Вот что в идеале надо изучить еще: ООП, как работает веб-сервер, HTML/CSS, SQL, PDO, работа с таблицами в БД, работа с формами, MVC, git, composer, JS, фреймворки, автоматизированное тестирование.
Надо переходить к более серьезным задачкам, которые научат тебя всему этому.
- для начала прочти урок https://github.com/codedokode/pasta/blob/master/soft/web-server.md
- установи Апач + PHP (советы выше и ниже) и читай туториал http://php.net/manual/ru/tutorial.php
- Учи HTML/CSS и SQL, PDO, хотя бы основы
- Далее простая, но полезная задача сделать список студентов, в ней много полезных советов: https://github.com/codedokode/pasta/blob/master/student-list.md
- Более сложная задача сделать файлообменник на микрофреймворке Slim: https://gist.github.com/codedokode/9424217
- Еще более сложная и долгая задача на Yii/Symfony: https://gist.github.com/codedokode/8733007
- После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
- Если ты все решил, переходи к Symfony 3/Doctrine 2
- Почитать про паттерны http://designpatternsphp.readthedocs.org/ru/latest/README.html (если ты не изучил ни одного фреймворка, то это будет рановато), тут с примерами кода http://designpatternsphp.readthedocs.org/ru/latest/README.html . Имей в виду что без примеров использования их учить бесполезно - не поймешь, хочешь увидеть примеры использования паттернов - ковыряй исходники Симфони, например Symfony Forms. Не заучивай паттерны - смотри код и думай, зачем тут они использованы.
Чтобы делать эти задания, тебе надо установить Апач + PHP (можно заодно сразу и MySQL) на компьютер. Вот полезные инструкции:
https://github.com/codedokode/pasta/blob/master/soft/php-install.md
https://github.com/codedokode/pasta/blob/master/soft/apache-install.md
Может тебе понадобится пользоваться командной строкой, вот гайд https://github.com/codedokode/pasta/blob/master/soft/cli.md
Решения задач лучше показать мне, особенно на ООП,так как сам ты вряд ли увидишь все ошибки. Пости свой код на гитхаб и вкидывай ссылку в тред по мере решения. Я прокомментирую и укажу на ошибки.
Параллельно стоит подучивать английский, на первых порах можно без него, но по мере развития придется все чаще сталкиваться с англоязычными статьями, так что лучше не откладывать. Читать можно news.ycombinator.com - это что-то вроде их хабра. Также можно начинать смотреть фильмы и видео на английском.
Также, у нас есть задачи которые позволят тебе изучить или подтянуть до нормального уровня знания JS/HTML/CSS/SQL. Решай их параллельно с задачами выше.
- HTML/CSS: https://github.com/codedokode/pasta/blob/master/html/html.md
- JS: https://gist.github.com/codedokode/ce30e7a036f18f416ae0
- SPA (сложно): https://github.com/codedokode/pasta/blob/master/js/spa.md
- Проверялка решений на JS: http://dkab.github.io/jasmine-tests/
- MySQL: https://github.com/codedokode/pasta/blob/master/db/databases.md
Что почитать
- Мануал по PHP — http://www.php.net/manual/ru/langref.php
- Сайт phptherightway (перевод на русский: http://getjump.me/ru-php-the-right-way/ )
- По PHP: Профессиональное программирование на PHP Джордж Шлосснейгл
- По PHP: Мэтт Зандстра — PHP: Объекты, шаблоны, методики программирования
- JS: learn.javascript.ru
- Про Git: https://git-scm.com/book/ru/v1
- Новости IT на англ. https://news.ycombinator.com/
Оформляй код аккуратно!!! — например пропусти через phpformatter.com . Также, если ты пользуешься IDE вроде PhpStorm, Netbeans, Eclipse, то в них эта опция встроена, подробнее: https://gist.github.com/codedokode/8759492
У ОПа нет аккаунтов и групп вконтакте, в фейсбуке, в твиттере, все "пхп-треды" там поддельные.
Платиновые вопросы
- Почему PHP? Потому что вакансий море, и учить легко.
- Сайт опять упал!!!!! — Не паникуй, а открой http://rghost.ru/6bfCY9lfl и получи личную немного устаревшую оффлайновую копию сайта (можно читать хоть на андроиде без интернета)
- Что надо знать чтобы найти работу - разработчику: PHP, SQL, HTML/CSS, JS, ООП, Git, композер, MVC, фреймворк. Верстальщику - HTML/CSS, JS, jQuery. У нас в треде были люди, которые практически с нуля учились и смогли найти работу.
- Что будут спрашивать на собеседовании если 0 опыта - гонять по теории, по официальному мануалу PHP, давать дурацкие задачки на переворачивание строк, гонять по SQL (транзакции, внешние ключи, напиши запрос), по JS (как сделать анимацию при нажатии кнопки), ну погугли, не ленись
- Можно подробнее про поиск работы, собеседования - нет, ОП писать не будет, но может кто из анонов захочет рассказать. Поищите тред перезвонивших, а также раздел /wrk/
- Сколько времени надо изучать все это? - все зависит от тебя, но не меньше 6-8 месяцев
- Нужен ли ООП, фреймворки, MVC, git, composer? — Да, однозначно. Посмотри любую вакансию.
Если тебе лень выравнивать код руками, закачай его на http://www.phpformatter.com/ и нажми «format». Робот исправит выравнивание и отступы в мгновение ока (да, прогресс не стоит на месте). Если ты используешь мощную IDE вроде PhpStorm, там тоже есть функция форматирования кода.
Горячие клавиши для форматирования кода в разных IDE: https://gist.github.com/codedokode/8759492
Вообще, в PHP долгое время не было единого стандарта оформления кода, все писали как попало и было много бардака, но сейчас дело лучше — есть стандарты PSR-1 и 2. Вот как надо оформлять код:
- переменные и функции пишутся с маленькой буквы, подчеркивание не используется, используется camelCase, пример: $x, $numberOfPeople, printResults()
- Название функции начинается с глагола, в стиле «сделайЧтоТо»
- не знаешь английский? Не беда, в 21 веке есть решение этой проблемы. Не пиши транслитом, открой лучше Гугл Транслейт или slovari.yandex.ru и найди название для переменной там
- в именах классов используется CamelCase, первая буква большая, «_» может использоваться
- мы предпочитаем подстановку переменных вместо конкатенации строк: "I am $age years old" — хорошо, 'I am ' . $age . ' years old' — плохо из-за обилия точек и кавычек
- мы используем для отступов 4 пробела (можно настроить редактор, чтобы при нажатии Tab он вставлял 4 пробела)
Вот ссылка на стандарты, где все это описано подробнее и даны примеры оформления:
PSR-1: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-1-basic-coding-standard.md
PSR-2: https://github.com/samdark/fig-standards-ru/blob/master/accepted/ru/PSR-2-coding-style-guide.md
Напомню всем, что у нас есть архив тредов phpclub.tech . Там есть поиск и можно найти, например, все обсуждения задачи про кредит. Или найти свой старый пост и все ответы к нему.
>Верстальщику - HTML/CSS, JS, jQuery
Я вот не понимаю, что делает верстальщик? Если верстальщик сверстает макет, то всё-равно же придется всё заново переписывать фронетнедеру, который будет на каком-нибудь React'e описывать всё эту верстку.
фикс фронтендеру
фикс всЮ эту верстку
Учебник читал, гуглил, но все равно ничего не понял.
Всегда думал, что фронтендер это и верстальщик тоже. Иначе что за отдельный фронтендер? Чем он будет заниматься? Это прослойка между бекендером и верстальщиком зачастую нахуй не нужная, если пишут не на реакте-вью-ангуляре?
Это деление было уместно лет 10 назад в полсдений раз (ну и на проектах той эпохи), когда клиенты были тонкими, а не толстыми и на них не было иной логики, кроме как логики отображения.
Все кто продолжают тянуть эту лямку деления и сегодня, просто либо не занимаются веб-разработкой, либо живут на легаси-проектах, либо просто идиоты. Потому что сегодня верстка в отрыве от всего остльного не пилится. Точнее, она не имеет никакого смысла, ее все-равно придется натягивать на логику отображения, которое нынче уже представлено не жквери, а компонентами фреймворков\библиотек\веб-компонентами\етк, короче тем, что уже не назовешь простой версткой.
Те же "фронтендеры" что кричат, что они занимаются только логикой отображения, просто ленивые хуеплеты, делающие двойную работу. Сначала верстальщик верстает макет, потом они его переплетают логикой - это дегенратвство. Учитывая то, что значительная часть логики отображения сегодня выносится в тот же css, анимации и прочее. И чтобы заниматься действителньо крупным проектом, нельзя быть в отрыве от верстки. Это все равно, что кричать что бэкендеру не нужно в ьазы данных. Проектировать ему может их не нужно, а дергать эффективно - еще как нужно. И если он в них не умеет, то дальше коленочных поделок пускать его нельзя. То же самое с фронтом.
" Вижу, что почти все написано в процедурном стиле." - а что ты смотрел? Я например постоянно с Симфони сталкиваюсь, а это ООП.
>сразу сделать одно поле и передавать туда сразу весь DI контейнер?
Вот так делать точно не стоит, в чем смысл DI в названии? Почитай комментарии ОПа к задаче о студентах. Если у тебя часто встречаются одинаковые зависимости в разных методах, то можешь их внедрять в конструктор, но лучше, как мне кажется, внедрять зависимости сразу в методы, тогда у тебя точно не будет неиспользуемых объектов. Обычно в крутых контейнерах есть функции вызова нужных тебе методов(например ты разбиваешь юрл и по заранее описанным рутам находишь коллбек), и они сами заполняют методы нужными зависимостями по тайп-хинту или по аннотациям или еще какой-то подпихне
> идея примерно такая: мы заводим переменные $result (результат предыдущих действий), $number (число, которое сейчас набирается) и $op (предыдущая операция). Затем разбираем строку посимвольно (циклом for).
Это есть.
>Если это цифра — добавляем ее к числу $number (умножаем на 10 и прибавляем цифру)
Это есть.
>если это операция — то выполняем предыдущую операцию (которую мы храним в $op) над $result и $number, сохраняем итог в $result, а текущую операцию на будущее сохраняем в op. Если это «=», то выполняем предыдущую операцию и выводим $result
Это тоже есть.
При этом если тут оно хоть что-то выдавало https://ideone.com/Tjfbus, то тут я добавил одну переменную и оно умолкло https://ideone.com/PwGd9r .
Если же перенести echo $result в самый конец https://ideone.com/e6qIvF, то он выдаёт набор цифр 0002222488448842662. Они должны быть в результатах вычисления, это правильные цифры, но и не правильные.
Пытался вот так https://ideone.com/niBIBF, всё равно ничего.
Но при этом я не могу понять, где ошибка.
Если попадается операция, то создаётся временная переменная с предыдущей операцией, а текущая сохраняется на будущее. После выполняется вычисление над $result и $number операции, если же операция =, то вычисляется текущая и выводится результат с прекращением дальнейшего выполнения цикла for.
Если же попадается число, то оно складывается в $number.
Пытался разобрать это https://ideone.com/T0AfO, вообще ничего не понял.
ОП, ответь, пожалуйста, на вопрос - это очень важно. Вопрос без подвоха и не траленк. Ты би или гей?
Ты своей нетактичностью даже меня задел, а я за ОП-а и двор стреляю в упор.
Намек понятен?
не-ОП
1×1 = 1
2×2 = 4
...
9×9 = 81
Результат пока такой https://ideone.com/qawxeR
Я понимаю, почему в строке 1 * 1 не выводится решение, но как сделать так чтобы все работало как задумано, не городя кучи всего снаружи цикла, без костылей так сказать.
Спасибо, добрый человек.
https://ideone.com/JkebMt
Чего такой вариант не устраивает?
for ($a = 1; $a < 10; $a++) {
$sum = $a $a;
echo "$a $a = $sum\n";
}
Разницы нет, но по PSR - со скобками, ради единобразия: https://github.com/php-fig/fig-standards/blob/master/proposed/extended-coding-style-guide.md#4-classes-properties-and-methods
> When instantiating a new class, parenthesis MUST always be present even when there are no arguments passed to the constructor.
Просто раньше это казалось шуткой, а теперь обретает больше смысла.
Наверняка уже какой-нибудь ероха написал ОП-тяну и они уже пару лет как встречаются :(
Например, есть:
$z = 1;
$q = '+';
$x = 3;
echo $z, $q, $x;
Выводит: "1+3". Как сделать, чтобы оно сразу вычислялось и выводило 4?
Это касательно задачи с калькулятором. Можно же числа определить в один массив, а операции в другой, а когда for дойдёт до "=", по очереди вызвать элементы массивов с числами и операциями, так, чтобы они сложились в пример и сразу вычислялись?
ОП - это жнец, который вещает нам из глубин космоса.
Ноу дискасс.
Гугли eval() для крайне опасного быстрохака, но вообще это задача синтаксического анализа, гугли и читай dragonbook %хотя вряд ли ты его осилишь, тупой и ленивый кусок дерьма%
Как я понял, то часто разные контейнеры имплементируют метод singleton(), по которому возвращается один и тот же, единожды созданный объект, а по get() возвращается новенький. Так что да, по get() ты получаешь новый объект.
Хотя в статье ОПа по DI пример куска кода контейнера, который нужно доработать, имеет функционал как раз обратный, там два массива, в одном готовые к созданию сервисы, а в другом уже созданные объекты сервисов, и get() сначала проверяет массив уже созданных объектов.
1. узнать за сколько примерно в комфортных для меня условиях я смогу справиться с такой вот несложной задачкой, сейчас мне кажется что я написал бы часа за 4 не больше всё это.
2. Что бы на гитхабе был такой вот хэллоу ворлд на игнайтере для собеседований. (не то что бы я хотел на нем работать дальше, просто что бы был если спросят за скилл)
3. Освежить в памяти фреймворк и посмотреть подводные камни какие-никакие.
4. У меня ступор и я хоть и хочу каждый день сесть за задачу, но понимаю что сначала надо прочитать кучу теории по местам в которых у меня дыры, а потом уже садиться и писать сразу нормально с исключениями и паттернами хотя бы для бд. А не просто обертка пдо в свой бд-класс и поперли.
Как вам такая безумная идея, имеет смысл?
>>1175880 - программа про вывод чисел текстом
>>1175881 - замечания по верстке отдаленно напоминающего ВК сайта
>>1175884 - https://github.com/7Y2RPXK3ETDCNRDD/webpaint
Если я не проверил чей-то пост - напомните о себе здесь.
1. По get запросу и id юзера - panel.php?id=25 и в самой странице поставить проверки, если юзер на которого мы зашли совпадает с залогиненым юзером, то появляются крутилочки для изменения данных
или 2
Страница panel.php будет без get запроса, и все будет выполнятся внутри нее. Правда тогда вопрос: как заходить на чужие аккаунты ?
3. Как лучше поступить с точки зрения безопасности, проверяя пользователя и его привелегии?
Проверка через $_SESSION я так понял довольно уязвима ?
Короче, как лучше запилить логику личного кабинета ?
Есть вариант с тремя таблицами:
-Пользователи
-События
-События+пользователи(many-to-many)
То есть, чтобы узнать количество непрочитанных уведомлений мне нужно сперва узнать количество всех событий, затем узнать какое количество событий прочитано пользователем, после чего найти их разницу.
Но! Тут есть проблема. Во-первых пользователей очень много, около 20 000, во вторых события тоже будут измеряться тысячами.
Не будет ли тормозить запрос на выборку из третьей таблицы, учитывая, что он будет происходить при каждой загрузке страницы? (Вверху счетчик непрочитанных уведомлений).
Ситуация осложняется тем, что у событий есть права доступа (по нескольким параметрам: регион/представительство/отдел), поэтому запрос на выборку событий будет ещё медленнее.
Ещё есть мысль создать табличку типа:
Айди_пользователя|количество прочитанных уведомлений
Добавлять во второй столбец значение при прочтении уведомлений.
Однако, выглядит этот вариант не очень надежно.
Буду благодарен любым советам и идеям. Спасибо!
Выходит, что русский язык работать не будет и мне нужно руками вызывать $messageFormatter->format($translator->translate('files')) ?
Решил просто порассуждать и поделиться мыслями,строго не бейте, все равно никто больше непишет ничего, а так глядишь дискач на почве указания на ошибки завяжется.
Как я понял ты хочешь сделать свою систему уведомлений скорее как систему обычных сообщений на сайте, но слегка урезанную. Во первых сами юзеры не смогут слать в ней друг другу сообщения, а во вторых сообщения будут генерироваться самим сайтом внутри во время выполнения некоторых функций. (ну пример наверное когда пользователь пополнил баланс на X рублей и ему прилетело сообщение системное: мол вы пополнили баланс на X рублей, теперь он Y рублей, спасибо бла бла) - для персональных оповещений звучит не так уж и плохо.
Допустим у тебя будет табличка в которой связь юзеров и сообщений. Ну добавилась туда 1 связь - и всё ок. Ничего не разрастается. Причем очевидно, по тому что ты хочешь отслеживать прочитанные сообщения у тебя будет у каждого сообщения некий флаг (readen) и в селектах его тоже будет легко учитывать. Даже можно например сделать переодическую отчистку таблицы, с переносом тех сообщений в которых дата создания больше чем пол года и readen=true в отдельную таблицу-архив, из которой всё будет долго и незаметно для юзера селектится когда он в своих уведомлениях кликнет на кнопку: смотреть давнишние, или что-то в таком духе, но нам то главное, что наша основная табличка не будет тормозить для большинства юзеров.
Ок, но что делать когда нужно разослать сообщения всем пользователям? (У нас в каталоге новая хуета-нейм, ознакомьтесь). Ты же не будешь генерить 20000 записей в эту таблицу с тем что бы каждому юзеру пришло подобное? Это же тупо засерание собственной базы и адуха как на этапе инсерта туда, так и на этапе селекта потом каждым юзером из неё. Лучше уж сделать отдельный функционал немного напоминающий новости - ты просто создаешь новость, и в отдельных полях указываешь группы пользователей для которых она видима например. Если у тебя там пользователи не сильно то и бьются по группам, то можно прямо в таблице завести столбцы с булевыми значениями, и типа указывать что-то типа:
everyone=true - новость для всех пользвателей
everyone=false, admin=true - новость для админов.
everyone=false, admin=false, premUser=true - сообщение сообщение только для каких-то там прем юзеров если у тебя такие есть. Ну надеюсь понятно в целом, и конечно же при такой системе придется продумать как и что ты будешь селектить и как ты будешь себя вести в случае добавления новых групп пользователей.
Либо если у тебя все юзеры разбиты на группы с разными ид, то можно в отдельном поле хранить какой-нибудь json массив с перечислением груп пользователей которым новость доступна.
С тем что бы отслеживать кто и что тут прочитал, а кто не прочитал - уже не получится. Но не придется для небольшого сайта писать хайлоад оптимизацию просто для того что бы следить кто что проситал.
Решил просто порассуждать и поделиться мыслями,строго не бейте, все равно никто больше непишет ничего, а так глядишь дискач на почве указания на ошибки завяжется.
Как я понял ты хочешь сделать свою систему уведомлений скорее как систему обычных сообщений на сайте, но слегка урезанную. Во первых сами юзеры не смогут слать в ней друг другу сообщения, а во вторых сообщения будут генерироваться самим сайтом внутри во время выполнения некоторых функций. (ну пример наверное когда пользователь пополнил баланс на X рублей и ему прилетело сообщение системное: мол вы пополнили баланс на X рублей, теперь он Y рублей, спасибо бла бла) - для персональных оповещений звучит не так уж и плохо.
Допустим у тебя будет табличка в которой связь юзеров и сообщений. Ну добавилась туда 1 связь - и всё ок. Ничего не разрастается. Причем очевидно, по тому что ты хочешь отслеживать прочитанные сообщения у тебя будет у каждого сообщения некий флаг (readen) и в селектах его тоже будет легко учитывать. Даже можно например сделать переодическую отчистку таблицы, с переносом тех сообщений в которых дата создания больше чем пол года и readen=true в отдельную таблицу-архив, из которой всё будет долго и незаметно для юзера селектится когда он в своих уведомлениях кликнет на кнопку: смотреть давнишние, или что-то в таком духе, но нам то главное, что наша основная табличка не будет тормозить для большинства юзеров.
Ок, но что делать когда нужно разослать сообщения всем пользователям? (У нас в каталоге новая хуета-нейм, ознакомьтесь). Ты же не будешь генерить 20000 записей в эту таблицу с тем что бы каждому юзеру пришло подобное? Это же тупо засерание собственной базы и адуха как на этапе инсерта туда, так и на этапе селекта потом каждым юзером из неё. Лучше уж сделать отдельный функционал немного напоминающий новости - ты просто создаешь новость, и в отдельных полях указываешь группы пользователей для которых она видима например. Если у тебя там пользователи не сильно то и бьются по группам, то можно прямо в таблице завести столбцы с булевыми значениями, и типа указывать что-то типа:
everyone=true - новость для всех пользвателей
everyone=false, admin=true - новость для админов.
everyone=false, admin=false, premUser=true - сообщение сообщение только для каких-то там прем юзеров если у тебя такие есть. Ну надеюсь понятно в целом, и конечно же при такой системе придется продумать как и что ты будешь селектить и как ты будешь себя вести в случае добавления новых групп пользователей.
Либо если у тебя все юзеры разбиты на группы с разными ид, то можно в отдельном поле хранить какой-нибудь json массив с перечислением груп пользователей которым новость доступна.
С тем что бы отслеживать кто и что тут прочитал, а кто не прочитал - уже не получится. Но не придется для небольшого сайта писать хайлоад оптимизацию просто для того что бы следить кто что проситал.
Бро, спасибо, что потратил время.
Сообщениями это не может считаться и не сработает.
Это скорее похоже на описанные тобой новости, однако, как ты сам написал, отслеживать кто что прочитал не получится. А именно это мне и нужно.
>генерить 20000 записей
Нет, это не нужно. Просто записываешь в связующую табличку id пользователя и id новости, когда пользователь эту новость прочтет.
Тем самым исключаются проблемы с инсертом.
Внимательнее почитал доку - оказалось у Symfony там свой велосипед и Intl они не юзают: http://symfony.com/doc/2.1/book/translation.html#pluralization
Но судя по сорсам - можно сделать свой MessageFormatter: https://github.com/symfony/translation/blob/master/Formatter/MessageFormatter.php
И уже там дёргать \MessageFormatter
Ну смотри, у тебя в любом случае получается таблица, которая потенциально содержит в себе число записей = число юзеров x число новостей. Так что готовься что через некоторое время тебя будет ад и погибель.
Если же тебе нужна именно таблица на бэкэнде для каких-то своих целей, то на ум разве что приходит некая оптимизация, что когда у тебя создается новость, то в неё записывается куча записей из id новости и id каждого юзера, но когда например юзер прочитал оповещение, у тебя из этой таблицы запись с id этого юзера и id оповещения удаляется. То есть в идеале на живом сервисе в теории у тебя юзеры и будут поддерживать чистоту в этой таблице. Но на практике если на сайте скажем 20к юзеров, а реально активных из них скажем 5к, то у тебя всё равно будет таблица в перспективе забита на sum(users) x sum(news) x ~0.75, потому что 75% мертвых юзеров никогда ничего не прочитают, и придется например опять же самому писать дополнительную зачистку, что бы из этой основной таблички в какой-нибудь архив тормозной всё что старше даты X, даже если оно не прочитано в случае лагов.
Кстати это даже интересно звучит, ведь можно написать автоматизацию и повесить на крон, которая будет спрашивать из этой таблички какой-нибудь ряд тестовых селектов, и в случае если у тебя среднее время запроса превышает комфортные 0.1 секунду то например начинать чистку на предмет разгребания части самого старья. Ну и повесить всё это циклично на крон, что бы проверялдось само раз в час/три/день.
Тебе что конкретно нужно, что бы пользователям просто не показывало новости по несколько раз? Тогда может быть есть смысл в фильтрации на клиенте? Ну типа пользователь посмотрел новости, ты в куки посадил id этих новостей. И каждый раз когда у тебя селектятся новости для пользователя, ты через пхп скажем фильтруешь результат и удалеяешь из него те новости id которых у юзера сидят в куках. Правда тогда с разных устройств они будут заново получать новости. Опять же получается дыра.
>>176390
Исключается проблема с большим инсертом при создании новости, но не девается никуда проблема с тем, что у тебя юзеры при просмотре новостей эту табличку сделают очень жирной и выведут из работоспособного состояния. У меня на локалке от миллиона записей вроде уже работа с таблицей перестает быть комфортной. при этом просто разовые запросы, никаких даже тестов о том, что бы слать например <10 запросов в секунду. У тебя если 20к юзеров, и пусть даже в среднем из них 5к будут активно что-то читать, то в среднем получается, что через 200 новостей они уже этот миллион записей создадут, ну пусть для вашего сервера комфортно будет ворочать табличку с 10лямами записей - ок. Но ты же говорил о том, что
>события тоже будут измеряться тысячами.
Тебе явно нужен какой-то хитрый механизм отчистки от старого дерьма.
Ну смотри, у тебя в любом случае получается таблица, которая потенциально содержит в себе число записей = число юзеров x число новостей. Так что готовься что через некоторое время тебя будет ад и погибель.
Если же тебе нужна именно таблица на бэкэнде для каких-то своих целей, то на ум разве что приходит некая оптимизация, что когда у тебя создается новость, то в неё записывается куча записей из id новости и id каждого юзера, но когда например юзер прочитал оповещение, у тебя из этой таблицы запись с id этого юзера и id оповещения удаляется. То есть в идеале на живом сервисе в теории у тебя юзеры и будут поддерживать чистоту в этой таблице. Но на практике если на сайте скажем 20к юзеров, а реально активных из них скажем 5к, то у тебя всё равно будет таблица в перспективе забита на sum(users) x sum(news) x ~0.75, потому что 75% мертвых юзеров никогда ничего не прочитают, и придется например опять же самому писать дополнительную зачистку, что бы из этой основной таблички в какой-нибудь архив тормозной всё что старше даты X, даже если оно не прочитано в случае лагов.
Кстати это даже интересно звучит, ведь можно написать автоматизацию и повесить на крон, которая будет спрашивать из этой таблички какой-нибудь ряд тестовых селектов, и в случае если у тебя среднее время запроса превышает комфортные 0.1 секунду то например начинать чистку на предмет разгребания части самого старья. Ну и повесить всё это циклично на крон, что бы проверялдось само раз в час/три/день.
Тебе что конкретно нужно, что бы пользователям просто не показывало новости по несколько раз? Тогда может быть есть смысл в фильтрации на клиенте? Ну типа пользователь посмотрел новости, ты в куки посадил id этих новостей. И каждый раз когда у тебя селектятся новости для пользователя, ты через пхп скажем фильтруешь результат и удалеяешь из него те новости id которых у юзера сидят в куках. Правда тогда с разных устройств они будут заново получать новости. Опять же получается дыра.
>>176390
Исключается проблема с большим инсертом при создании новости, но не девается никуда проблема с тем, что у тебя юзеры при просмотре новостей эту табличку сделают очень жирной и выведут из работоспособного состояния. У меня на локалке от миллиона записей вроде уже работа с таблицей перестает быть комфортной. при этом просто разовые запросы, никаких даже тестов о том, что бы слать например <10 запросов в секунду. У тебя если 20к юзеров, и пусть даже в среднем из них 5к будут активно что-то читать, то в среднем получается, что через 200 новостей они уже этот миллион записей создадут, ну пусть для вашего сервера комфортно будет ворочать табличку с 10лямами записей - ок. Но ты же говорил о том, что
>события тоже будут измеряться тысячами.
Тебе явно нужен какой-то хитрый механизм отчистки от старого дерьма.
1. В какой конкретно кодировке лучше всего создавать базу?
utf8_general_ci
?
2. Движок по дефолту InnoDB, не трогать?
3. Если про классы в пхп всё понятно, то вот таблицы и названия баз данных сейчас как принято писать? Тоже камелкейсом?
students_list
studentsList
StudentsList
?
student
это таблица, и так понятно что она лист.
Еще есть холивар на тему student или students.
Оно в принципе не важно. Ну лучше знать о таких срачах на случай случайно детонирующих коллег.
- >>1176468 - задача про кредит и задача про вектор https://repl.it/repls/AcclaimedWhirlwindSoftwareengineer
- >>1176469 - почему в задаче про кредит не работает if ($a || $b || $c > $d) ?
- >>1176470 вопрос про ssh agent
- >>1176471 про просмотрщик картинок
- >>1176476 про ООП и https://github.com/mlmn/vector.loc/
Если вы писали в прошлом треде и вам не ответили - напомните о себе тут.
С викториной проблем нет, интересна схема с купоном.
Пока есть 2 идеи - либо записать в базу n купонов и выдавать каждому, кто прошёл весь Вьетнам, выпиливаясь из базы штукой. Либо, сделать как с капчей, если все ответы верные - выдается рандом код\картинка - ноунейм пишет, и вычитается n число от общей суммы.
Что скажете, мамкины прогеры? Если что, база есть.
Сгенерировать в базу. Либо определить упорядоченное (желательно внешне рандомное) множество значений размерностью n. Ща распишу. я ж у мамы программист
Не напрягаясь, заскочил сюда. Мне это в ИДЕ удобней будет или с простым редактором можно обойтись? Или в какой лучше все это дело оборачивать? чтобы чекать БД, формочки и т.д.?
мимоАндройдДжуниор
Оборачиваешь сердце в em, делаешь ресет родных для em стилей, меняешь цвет и шрифт. Остальное там через html entities набираешь, в том числе само сердце, юникодным номером.
Может быть это судьба?
Спасибо
Поставил php7.2 и на ровном месте образовалась ошибка (пикрелейтед), с переменной все ок же! Когда заливаю на хостинг то ошибки нет, подозреваю что там php<7.2
Вот код страницы
https://pastebin.com/Tz9D5kpK
Что посоветуешь анону, который хочет быть веб макокой но никак не выберет стул: пхп или питон? плюсы и минусы, заранее спасибо!
7.2.4
Пхп простой, но ограниченный. На пхп можно делать только сайты и то с вебсокетами например будет уже пляска и простые cli приложения. Нет ни многопоточности, ни нормальной асинхронности, много легаси вроде тысяч функций в глобальном пространстве. Насколько помню всё так же хуёво с юникодом. Скудное разнообразие структур данных.
Питон сложнее, но правильный и по-настоящему универсальный хотя свои проблемы тоже есть, в общем-то это подмножество ещё более широкого набора проблем пхп, это дорога в мир большого программирования и вырастания из макаки.
Если хочется именно вебмакакой быть, учи лучше жс и ебашь фронтэнды. Голова будет болеть от тысяч фреймворков и дебильной слабой типизации в пхп такая же, но хотя бы заработаешь что-то.
Большое спасибо, анон!
В смысле? Я же ей присвоил true в случае успеха и false в случае провала регистрации.
Вынес в начало перед if, и присвоил $errors__test = 0;
Ошибок нет, так будет правильно?
Пхп даже на вебсервер проще положить, он в апаче вообще нативно работает. Можешь сходу прям на голом пхп уже что то клепать, а с питоном так не выйдет, нужна джанга, uswgi и еще костыли на сервак. Но анон >>176863 правильно сказал, я считаю что питон это вклад в будущее, а пхп это быстро и без напрягов срубить бабла уже сейчас.
бамп
Нужно направление, как это всё осуществляется, че куда.
Ничего не мешает, если уже все знаешь в пхп, то можно по вечерам пробовать душить своего питона.
А если только начал?
> я считаю что питон это вклад в будущее, а пхп это быстро и без напрягов срубить бабла уже сейчас.
>Kokoko Kokoko
В последующие лет 5 все будет жить, так что практически нихуя не изменится. Специалисты везде нужны. Очередной флейм разводишь.
>В последующие лет 5 все будет жить
Чего ты порвался то, я не говорил что пхп наебнется вот вот уже. Имелось в виду далекое будущее.
Я к тому, что если надо быстро освоить и идти работать за деньги то это однозначно пхп.
Если острой необходимости в деньгах нет, то изучение питона в далекой перспективе даст свои профиты. Разве я не прав ?
>, то изучение питона в далекой перспективе даст свои профиты. Разве я не прав ?
И почему же?
На далекую перспективу изучаешь питон от корки до корки и погружаешься в мир хайлоада и отсоса хиккующих выблядков.
Мимопроходил
Я не знаю, я просто мимопроходил. Я вообще думаю, что надо всё на начальном уровне изучить РНР, JS, node.js, Pyhton. Чтобы уметь на всем этом тудулист смастерить. А потом выбрать что-то одно, что больше по душе и по жизненным обстоятельствам.
фикс Python
Прямо с языка снял.
Какие долбежки? Весь стек устанавливается тремя командами, или одной через tasksel. Вообще это полезно как минимум потому что в будущем в виртуальной среде ты сможешь тестить свое говно на разных серверах и версиях пых. если захочешь
Например, я создаю в админке новую форму, добавляю туда любые поля, выбираю их тип, задаю правила валидации, возможно назначаю эвенты перед отправкой формы, потом все это дело сохраняется в БД. На фронте эта форма со всеми полями и правилами вытаскивается из БД и генерируется в html/js. Не представляю, как это организовать вообще, чтобы в контроллер прилетали POST-данные, ими заполнять эту динамическую форму, валидировать и сохраняться данные в БД.
Ну я на самом деле потом подумал ещё, тебе ведь всё равно нужно учитывать использовался ли купон, так что наверное только в БД тыщу рандомов писать. Единственное проверяй чтобы в кодах не было мата. FUCKBITCHTWAT
Через [л-ю-н] задаются диапазоны же? Тогда все буквы между ними попадут в поиск.
Из статьи википедии про l33t. Она нужна для изучения функции strtr(), если ты сделал задачу про шифровку, то можно пропустить.
Ну не стукай!
Нуу, я только вкатываюсь, массивы хуесивы и прочее. Заодно мб как прохать пойму немного. Ы ебать.
BUMP
А где по-твоему используются такие методы?
Обычно, когда мы тестируем класс, мы смотрим на него снаружи и "не видим" приватных или защищенных методов. Мы используем этот класс точно так же, как и его использует код. Ведь цель тестирования - что класс будет вести себя так, как ожидается. Не приватные методы класса, а класс в целом.
Плюс, хорошая идея не закладывать в тесты какие-то внутренние особенности класса, так как при рефакторинге это все сломается. Мы должны тестировать публично доступные методы.
Мы пишем тесты по мотивам требований, которые предъявляются к классу и публичным методам. Если метод должен вести себя так-то - мы это и тестируем.
Соответственно, ответ - тестируй публичные методы. Они ведь вызывают тот приватный метод.
Если привести пример - представь что твой класс это кофе-машина. Если ты ее хочешь протестировать, то ты жмешь кнопки и проверяешь, что она выдает кофе. Ты не лезешь под крышку и не меряешь там напряжение. И потому твой тест не перестает работать при замене кофе-машины на другую модель.
Как вариант, можно конечно добавить публичный метод testSomething() но это будет не очень хорошо, так как мы закладываемся на знание внутреннего устройства класса. И этот тест окажется бесполезен после рефакторинга.
Сделалъ. Просто там была задача с "шпионским" шифром, а потом с хакерским. Вот я и не понял зачем делать это дважды.
В сотню раз, это тупой вопрос ведь, гошечка лучшее решение для хай-лоад сервера.
это не тупой вопрос,прост недавно узнал про язык от гугла.
это не тупой вопрос,прост недавно узнал про язык от гугла.так на нем можyо клепать сайты на поток?
Ты не прав, с тестами проблемы возникают у тех, кто не умеет их писать, делает хрупкими, полагается на внутренности тестируемого кода, что приводит к тому, что изменения в систему вносить очень больно. По моим ощущениям юнит-тесты вообще не добавляют сложности, пишутся быстро и проверяют сразу много всего, с приёмочными тестами возиться приходится больше, особенно если на фронте навороченное SPA, но в целом от таких тестов тоже польза есть. Не надо лезть с такими советами, человек может хорошим специалистом стать, а ты тянешь обратно в болото. У нас есть на работе мануальные тестировщики, но они, к примеру, не могут протестировать REST API, а программист может. Ещё мануальных тестировщиков не получится "запускать" так же часто, а тесты можно запускать на каждый чих.
Глупый вопрос. Зачем нужно писать в стиле ООП в вебе, если скрипты "живут" очень малое время (получают данные из БД и рисуют шаблоны)? Можно же просто статическими функциями реализовывать все требования к сайту.
Смысл вопроса: не могу понять, куда ООП "присобачить", в какие файлы и функции, в процессе своего говнокодинга. Да, можно создавать экземпляры классов, но для чего? Можно без него обойтись, не скажу, что проще, но они не нужны, лишнее нагромождение
http://sandbox.onlinephpfunctions.com/code/63cf89da69bab8ce6434556101059c96e78d3b8c
https://ideone.com/aoUwEY
Ps. сделол вот так, без массивов.
$word10 = array_flip($word1);
$word11 = array_rand($word10);
$word20 = array_flip($word2);
$word22 = array_rand($word20);
$finalWord = $word11 . " " . $word22;
echo $finalWord;
Но мне кажется это не очень правильно, к тому же, пытаюсь понять ту ситуацию с выводом сверху.
Решил нахерачить логин форму отсюда: https://www.tutorialspoint.com/php/php_mysql_login.htm. И меня не редиректит на страницу welcome.php, хотя он проверяет по базе данных логин и пароль, то есть конект есть, редиректа нету. Не могу понять что не так, на 10 раз пересмотрел все исходники
>http://sandbox.onlinephpfunctions.com/code/63cf89da69bab8ce6434556101059c96e78d3b8cC
Строка 18: $halfLength == $count. Это условие всегда неверно. Соответственно, скрипт даже не заходит внутрь цикла. Нужно переписать на выполнять пока $i (в твоем случае $count) меньше $halfCount.
28: elseif($halfLength == $count) {
echo "$result";
}
Это условие никогда не проверится. Ты ждешь третьего результата, там где возможно только 2 (true, false).
26: Замени на $result = "Не палиндром"; Выводи $result после цикла.
С этого момента все должно работать.
Как все заработало, время рефакторинга:
$zero = 0;
$minusone = -1;
$count = 0;
Лишние переменные. Достаточно использовать $i. С позициями при копировании символов ты все сделал верно (много анонов тут со старта ошибаются).
Условие в цикле можно сократить до одного if, когда не нужно будет обновлять эти переменные.
На проверку, задача про мошеннические опечатки.
Вариант только с русским текстом:
https://ideone.com/sz1DCU
Вариант с русским и английским текстом: https://ideone.com/IjnjLz
Ситуация: чиню/дополняю сайт на пхп, и вижу: все запросы к бд обернуты минимум в рид локи.
Судя по общему качеству исполнения сайта делаю вывод, что они скорее всего не нужны.
Но какие нужны? Понимаю, довольно абстрактный вопрос. Но судя по объяснениям которые я видел - большинство операций и так лочат всё как требуется.
КАПЧА НЕВАЛИДНА BAD GATEWAY
>>174695 (OP)
Еще на проверку, автоматическое исправление опечаток.
Версия с проверкой на опечатки до и после исправления https://ideone.com/A7cHmQ
Урезанная версия с просто исправлением
https://ideone.com/WzIbPY
Заранее спасибо!
Делать обращения к базе в цикле это плохо или похуй? Антипаттерн или норм?
20 секунд уделил твоему вопросу, сам код не копировал и у себя на компе не запускал.
Так что на вскидку два возможных варианта, предполагая что сам код там рабочий и предельно просто всё там по схеме:
1. Если пост запрос то смотрим что в нем
2. Взяли с пост-запроса логин пароль
3. Спросили у базы строчку where логин и пароль
4. Посмотрели ответ базы, посчитали сколько в нем строк
5. Если строк == 1 (а не 0, ведь если в базе нет юзера с таким логином и паролем, то стало быть что-то неверно), то поставить куку юзернейма и редиректнуть на траницу которая как раз уже требует эту куку.
Во первых как с таким говном работать новичку:
Берешь и построчно вардампишь свой код.
После
>$result = mysqli_query($db,$sql);
пишешь
var_dump($result);
exit;
Смотришь что там в ответе, что коннект есть и всё ок.
Идешь дальше:
Смотришь что там прилетело в:
> $row = mysqli_fetch_array($result,MYSQLI_ASSOC);
var_dump($row);
exit;
...
var_dump($count);
Дальше у тебя будет 2 варианта - либо в $count лежит 0 - значит проблема с базой, потому что нет такого юзера и ты накасячил в самой таблице и пытаешься залогиниться под неверныю юзером или с неверным паролем или еще что-то такое.
Либо в $count лежит единичка и должно бы редиректить, но ничего не работает, тут ты должен знать что пикрил. Значит ты что-то не так скопировал и у тебя какая-нибудь херня пои копипасте или ты забил пробелов перед <?php тегами и прочее.
20 секунд уделил твоему вопросу, сам код не копировал и у себя на компе не запускал.
Так что на вскидку два возможных варианта, предполагая что сам код там рабочий и предельно просто всё там по схеме:
1. Если пост запрос то смотрим что в нем
2. Взяли с пост-запроса логин пароль
3. Спросили у базы строчку where логин и пароль
4. Посмотрели ответ базы, посчитали сколько в нем строк
5. Если строк == 1 (а не 0, ведь если в базе нет юзера с таким логином и паролем, то стало быть что-то неверно), то поставить куку юзернейма и редиректнуть на траницу которая как раз уже требует эту куку.
Во первых как с таким говном работать новичку:
Берешь и построчно вардампишь свой код.
После
>$result = mysqli_query($db,$sql);
пишешь
var_dump($result);
exit;
Смотришь что там в ответе, что коннект есть и всё ок.
Идешь дальше:
Смотришь что там прилетело в:
> $row = mysqli_fetch_array($result,MYSQLI_ASSOC);
var_dump($row);
exit;
...
var_dump($count);
Дальше у тебя будет 2 варианта - либо в $count лежит 0 - значит проблема с базой, потому что нет такого юзера и ты накасячил в самой таблице и пытаешься залогиниться под неверныю юзером или с неверным паролем или еще что-то такое.
Либо в $count лежит единичка и должно бы редиректить, но ничего не работает, тут ты должен знать что пикрил. Значит ты что-то не так скопировал и у тебя какая-нибудь херня пои копипасте или ты забил пробелов перед <?php тегами и прочее.
Не можешь справится со сложным запросом и хочешь селать много мелких? Если можно за 1 запрос вытянуть что-то, то наверное лучше за 1 дернуть. Хотя знаешь, я на самом деле не уверен, иногда лучше потестить например, поделав оба варианта по несколько десятков/сотен/тысяч раз и справнив среднее время за сколько ты в итоге получаешь свои данные.
Сам часто делал в продакшене хуйню уровня: дернуть несколько простых запросов и их уже средствами пхп обработать всё в нужный блок данных, потому что знания мускуля не на таком уровне, и вместо того что бы 4 часа сидеть гуглить и учиться составить этот хитровыебанный запрос с условиями и подзапросами средствами мускуля, проще реально дернуть несколько разных отдельных запросов и их перелопатить вируозно на пхп. К тому же начальник прямым текстом еще на тебя налегает что давай сириусли ебош хуяк-хуяк и в продакшен.
>Не можешь справится со сложным запросом и хочешь селать много мелких?
Нет. У меня есть таблица user_info, куда я вставляю id разных сущностей. Их много - user_agent_id, phone_id, ip_address_id и т.д.
То есть перед тем как вставить запись в эту таблицу, мне нужно получить id всех необходимых сущностей (или если их нет - добавить сущность и потом вернуть её id).
'/\.(\w+$)/iu'
>Какие ты видишь решения проблемы?
Я ещё не освоился с JS, но пока могу сказать, что это нужно разделить либо на отдельные функции, либо разбить на два класса - Helper и Handler, который вешает обработчики (к примеру, Handler.prototype.handleClickOnImg...).
>> var fullsize = $('.fullsize');
>Не лучше ли сохранять ссылку в переменной, а не искать каждый раз этот класс по всему дереву DOM?
>> Этот элемент нужно искать именно каждый раз когда мы делаем клик, потому что, при каждом открытии, он, каждый раз, создаётся новый.
ну все равно, можно сохранять ссылку на него, а не искать в DOM.
Разве эта переменная и так не ссылка на объект jQuery?
>Что-то мне кажется, для добавления обертки надо опять откатываться к яваскрипту. Либо добить решение с флексбоксом. Вот такой вот он, CSS. Предлагай свои идеи.
У меня идеи закончились когда не получилось отцентировать картинку. Мне нравится вариант с высчитом размеров картинки и центрирования с помощью double translate, потому что мы, в любом случае, обращаемся к js чтобы вывести popup - ничего страшного если с его помощью и размеры посчитать.
Так же вот думая об этом, понимаешь зачем в итоге ооп было придумано. Ну не могут просто люди брать и в голове держать программу написанную без какой либо модульности на 300 и более строк кода, где тупо идет поток лапши. Скажем надо что-то где-то переписать и тут же понимаешь как всё плохо с этим всем, когда у тебя из-за одной переменной может в нескольких местах может всё сломаться и нужно весь код из-за такой вот мелочи перелопачивать. А так вроде как всё разбито по модулям, и ты даже можешь с этим легко работать, главное научиться их выделять логически и связывать, а сами микрозадачи решать - это легко и уже давно к этому адаптировался.
Поясни поподробнее схему? Ты когда юзера создаешь, ты всю инфу не в 1 таблицу кладешь, а раскидываешь всякую сопутствующую инфу по типу его телефона и емейла по отдельным таблицам, а юзеру собственно вносишь id для связи? В чем вообще профит? Я искренне не понимаю подобного подхода.
>$halfCount
Ты имел ввиду $halfLength (половина длины текста без пробелов)? $i заменить $count?
>Ты ждешь третьего результата, там где возможно только 2 (true, false).
Но ($halfLength == $count) либо равно, либо не равно?
>>178627
Я вот не могу понять как кто-то решает задачу за час. Я уже месяц сижу на основах пхп из учебника ОПа. Впрочем реального времени на учебу потрачено может неделя от силы. Когда дело доходит до задач дропаю на несколько дней, пока жду тут ответа. От чтения справочника ситуация не меняется, даже наоборот - появляется еще больше вопросов.
Да и задачи я "решил" скопировав решения анонов, которые выкладывали их в тред. Копировал, впрочем не полностью. Используя их как примеры пришло понимание того, как это можно использовать тему урока.
Я придерживаюсь мнения что перед тем как давать задачи, нужно дать пример использования теории на практике. Этот момент в учебнике упущен.
Как было в школьных учебниках: дается теория, формула. Потом идут примеры как их применяют. Потому примеры уже с задачей. Далее идут непосредственно задачи, которые решают самостоятельно.
Очень не хватает примеров применения на практике материала. Я ведь даже предположить как можно, а как нельзя.
Может с таким ущербным майндсетом мне сразу в биореактор, совсем уже заебался
Не имейл и телефон, а разные данные о посещении сайта. Статистика - версия браузера, версия оs, ip адрес, название модели устройства, тип устройства и т.д. Телефон не юзера, а некий, который на сайте юзеру показывается, в зависимости от параметра url. Естественно мне эти данные нужно связать с id юзера.
>ты всю инфу не в 1 таблицу кладешь, а раскидываешь всякую сопутствующую инфу по типу его телефона и емейла по отдельным таблицам, а юзеру собственно вносишь id для связи?
>В чем вообще профит?
В нормализации базы данных.
Вроде довольно реалистичные задачи, вон типа списка студентов, файлообменника etc - дохрена же сайтов примерно такой структуры, вполне реалистично выходит.
Я не про задачи, а про пример применения темы урока на практике. Именно как и куда это можно вставить. Потому что когда я вижу решения анона у меня возникает только одна мысль "а так можно было?", потому как я не знаю как можно и как нельзя.
Вот почему ты теперь мне кажешься высокомерным пидорасом? Еще хотел же приписать к вопросу про твою оптимизацию:
inbifo: не слышал про нормализацию?
Но подумал, что это будет лишнее, а в итоге следовало бы.
сам надумал себе - сам обиделся
Кароче ты парсишь $_SERVER, и например видишь что там юзер зашел с какого-нибудь альфа-фаерфокс 60 версии, идешь значит в таблицу user_agents, смотришь что там ничего нету подобного, заходишь в неё, добавляешь туда себе этот новый неизведанный агент, забираешь его айдишечку, потом херачишь так по нескольким таблицам которые у тебя чисто под такие вот "сущности" выделены и содержат 2 столбца из id и значения, и обойдя в итоге все таблицы и убедившись что везде всё есть ты завозишь наконец с чистой совестью в user_info тупо наконец-то запись с кучкой айдишников? Я правильно понял? У тебя так при каждом обращении юзера к сайту происходит? Ну и я не уловил в итоге, в чем профит такого усложнения перед тем что бы просто занести все нужные тебе куски информации в одну таблицу 1 инсертом за 1 обращение? Инбифо: для нормализации.
>Вот почему ты теперь мне кажешься высокомерным пидорасом?
Пиздец ты ебанутый. Пропало желание тебе отвечать.
Подожду опа.
http://nnm-club.me/forum/viewtopic.php?t=1220587
А можешь пожалуйста написать один или несколько примеров, о чем именно, что должно бы быть написано в уроках?
>Ну и я не уловил в итоге, в чем профит такого усложнения перед тем что бы просто занести все нужные тебе куски информации в одну таблицу 1 инсертом за 1 обращение? Инбифо: для нормализации.
Денормализация ИНОГДА, ДОВОЛЬНО РЕДКО, бывает оправдана, но это явно не тот случай. За денормализованные данные по ебалу бьют в нормальных конторах.
Но если ты индус Кумар Ебалнасрал то, действительно, профиты могут быть неочевидны. Можешь дальше хуячить данные в базу as is.
Дампнул - вывод пикрил. Пробелов не видать - https://pastebin.com/F2CChrQw Еще раз проверил все файлы хотя я до сих пор могу не видеть какую-нибудь очевидную ошибку При успешном логине он крашит body, пикрил. Собсна что делать, по-другому можно редирект сделать? Пробывал локейшен заменить на echo - тот же эффект.
Вспоминается анекодот про макак и бананы. Вот я захожу к вам ща в камеру, кидаю ненормализованную таблицу на пол, и вы на меня набрасываетесь, а пояснить то можешь на конкретном примере? А не высрать: НУ ТАК ПРИНИТА.
Мне вот видится, что нормализация нужна реже чем нужна, потому что в небольших проектах она скорее всего будет только усложнять базу и увеличивать количество кода которое её обслуживает, что нахуй не нужно. И на каком-нибудь условном хайлоаде ты тоже будешь херачить 10 записей в 10 таблиц вместо 1? Может тогда у тебя маня-нормализация и дыры в проектировании базы, а не нормализация?
Жду пояснения на конкретном примере, с обоснованием хотя бы в духе: ну кароче у меня к этой таблице селектов 99%, поэтому тут всё нормализованно и проиндексированно по айдишникам, шоб быстрее, а инсерты 1% оче редко и похуй что там 10 запросов за раз идет.
У слившегося челика как я понял юзер при каждом клике создает в десятки таблиц по запросу только что бы проверить есть ли там то, что юзер с собой принес, потом еще потенциально по десятку инсертов может прилетать туда же, если вся инфа- свежачок, зато нормализацию базы поддерживает)))
А я то думал мы тут ща обсуждать будем и рождать в споре истину.
https://www.youtube.com/watch?v=AQqiUusn04k
Смотри, а попробуй в начале welcome.php
написать перед include('session.php');
написать:
echo 123;
exit;
Вдруг у тебя всё нормально редиректит, просто я смотрю, что в session.php тоже при какой-то ошибке редиректит назад на login.php, вдруг у тебя проблема как раз в том, что прохоидт целый круг редиректов и назад тебя на login.php возвращает.
И не заходит в этот файл получается?
Значит проблема в этих 3 строчках - пикрил 1.
Заходим в мануал: http://php.net/manual/ru/function.session-register.php
Видим корень зла - пикрил 2.
И вообще, напиши ка error_reporting(-1); в начале своего кода - почему еще не сделал и не посмотрел что тебе пишет твой пхп?
Я тупой, возможно. Вставил вот как на пике и самой первой строкой после <?php - никакого эффекта, так же пустой экран.
Я конечно не гуру, даже не нуб.
Но почти любой адекват скажет что этот топик впринципе настолько сильно зависит от обстоятельств что спорить о нём нет смысла без полного их описания.
Нет никаких чётких конвенций.
Поэтому использовать нужно коммон сенс.
Вот пара высосанных из жопы примеров:
Например, если содержимое столбца никогда не используется отдельно от сущности таблицы, скорее всего нет смысла её нормализовать.
Пример: posts [ id, author_id, content ] - нет смысла нормализовать [ content ], он полностью входит в сущность поста и от неё не отрывен.
Но если мы вдруг понимаем, что в ней в итоге придется при этом держать больше одного значения, значит лучше все-же нормализовать.
Пример: posts [ id, authors_ids, content ] - имеет смысл добавить таблицу posts_authors [ post_id, author_id ]. Такое может произойти если вдруг захотелось возможность писать коллективные посты.
Если есть несколько столбцов которые всегда содержат соответствующие значения, логично нормализовать их в отдельную таблицу.
Пример: posts [ id, author_id, content, category_title, category_image ] - логично вынести данные в таблицу categories [ id, title, image ], наша таблица станет такой: posts [ id, author_id, category_id, content ].
Однако, пример: posts [ id, author_id, content, category_title ] - не факт что такая структура требует нормализации.
Предположим, при написании поста автор может вообще что угодно придумать для категории, а поиск постов по ней осуществляется через вайлдкард - в таком случае смысла в нормализации нет.
Если же мы имеем жесткий список категорий, тогда даже если у категории есть только title и она больше нигде не используется в отрыве от постов, все-равно есть смысл вынести её в отдельную таблицу.
Пойдем дальше. Пример: requests [ user_id, ip, agent, date ].
Например, поле ip - содержит четыре числа через точку. Это-же очевидный массив. Почему бы не нормализовать его разделить его на четыре столбца, по одному на октет? А потом вынести их в отдельные таблицы: /0 [ id, octet ], /8 [ id, octet ], /16 [ id, octet], /24 [ id, octet] и получить таблицу request [ user_id, agent, date, /0_id, /8_id, /16_id, /24_id ].
Объяснить почему это не имеет смысла простым человеческим языком и без использования мата мне сложно. Но в общем смысле это сводится к тому что диапазон возможных значений будет равен размеру таблиц в которые ты их выносишь.
Далее, поле agent. Ну тут уж никто не поспорит совешенно очевидно что это массив жостко огранниченных по значениям полей! Первая мысль конечно же разнести его на столбцы [ .., browser, browser_version, os, os_version, device, .. ] и так далее. Потом мы сразу же захотим выделить все эти метаданные в таблицы: browsers [ id, name, version ], osses [ id, name, version ], devices [ id, name, revision, company_id, ... ], companies [ id, title, trademark_id ... ], trademarks [ id, title, image_id, ... ], images [ id, path, svg_path, icon_id, ... ] icons [ id, СТОЙ ЧТО ЖЕ ТЫ ДЕЛАЕШЬ ТЕБЕ ПРОСТО НУЖНО ПОФИЛТРОВАТЬ РЕКВЕСТЫ!!!
Просто берешь и select * from requests where agent like "%firefox%".
Хочешь знать с каких устройств заходит твой юзер? SELECT DISTINCT reqiests.agent ...
Невероятно просто.
У себя в аппликейшене можешь просто держать мап "browser" => "wildcard" / "regex"
Мысль что все виды браузеров, ос и устройств можно просто разнести на жостко категоризируемые данные немного оторвана от реальности, в которой я могу вообще любой мусор напихать в свой user_agent.
Прежде чем это делать нужно очень четко представлять себе конечную цель твоей работы. Возможно, это реально требуется. Но даже в таком случае, почти наверняка тебе не нужно нормализовать эти столбцы в отдельные таблицы, потому что почти наверняка, отдельно от сущности реквеста эти данные тебе не нужны. Берешь какую-то из библиотек по вычислению браузера/ос/чепухи, у нормальных они будут индексированны. В таблицу вставляешь уже индексированные значения. В приложении селектишь по этим-же индексам. Как пример. Если потом захочешь поменять библу, можно замапить новые индексы на старые, или сразу сделать свой мап если живешь в режиме паранойи.
Мимо проходил в надежде стать более лучше одеваться.
Я конечно не гуру, даже не нуб.
Но почти любой адекват скажет что этот топик впринципе настолько сильно зависит от обстоятельств что спорить о нём нет смысла без полного их описания.
Нет никаких чётких конвенций.
Поэтому использовать нужно коммон сенс.
Вот пара высосанных из жопы примеров:
Например, если содержимое столбца никогда не используется отдельно от сущности таблицы, скорее всего нет смысла её нормализовать.
Пример: posts [ id, author_id, content ] - нет смысла нормализовать [ content ], он полностью входит в сущность поста и от неё не отрывен.
Но если мы вдруг понимаем, что в ней в итоге придется при этом держать больше одного значения, значит лучше все-же нормализовать.
Пример: posts [ id, authors_ids, content ] - имеет смысл добавить таблицу posts_authors [ post_id, author_id ]. Такое может произойти если вдруг захотелось возможность писать коллективные посты.
Если есть несколько столбцов которые всегда содержат соответствующие значения, логично нормализовать их в отдельную таблицу.
Пример: posts [ id, author_id, content, category_title, category_image ] - логично вынести данные в таблицу categories [ id, title, image ], наша таблица станет такой: posts [ id, author_id, category_id, content ].
Однако, пример: posts [ id, author_id, content, category_title ] - не факт что такая структура требует нормализации.
Предположим, при написании поста автор может вообще что угодно придумать для категории, а поиск постов по ней осуществляется через вайлдкард - в таком случае смысла в нормализации нет.
Если же мы имеем жесткий список категорий, тогда даже если у категории есть только title и она больше нигде не используется в отрыве от постов, все-равно есть смысл вынести её в отдельную таблицу.
Пойдем дальше. Пример: requests [ user_id, ip, agent, date ].
Например, поле ip - содержит четыре числа через точку. Это-же очевидный массив. Почему бы не нормализовать его разделить его на четыре столбца, по одному на октет? А потом вынести их в отдельные таблицы: /0 [ id, octet ], /8 [ id, octet ], /16 [ id, octet], /24 [ id, octet] и получить таблицу request [ user_id, agent, date, /0_id, /8_id, /16_id, /24_id ].
Объяснить почему это не имеет смысла простым человеческим языком и без использования мата мне сложно. Но в общем смысле это сводится к тому что диапазон возможных значений будет равен размеру таблиц в которые ты их выносишь.
Далее, поле agent. Ну тут уж никто не поспорит совешенно очевидно что это массив жостко огранниченных по значениям полей! Первая мысль конечно же разнести его на столбцы [ .., browser, browser_version, os, os_version, device, .. ] и так далее. Потом мы сразу же захотим выделить все эти метаданные в таблицы: browsers [ id, name, version ], osses [ id, name, version ], devices [ id, name, revision, company_id, ... ], companies [ id, title, trademark_id ... ], trademarks [ id, title, image_id, ... ], images [ id, path, svg_path, icon_id, ... ] icons [ id, СТОЙ ЧТО ЖЕ ТЫ ДЕЛАЕШЬ ТЕБЕ ПРОСТО НУЖНО ПОФИЛТРОВАТЬ РЕКВЕСТЫ!!!
Просто берешь и select * from requests where agent like "%firefox%".
Хочешь знать с каких устройств заходит твой юзер? SELECT DISTINCT reqiests.agent ...
Невероятно просто.
У себя в аппликейшене можешь просто держать мап "browser" => "wildcard" / "regex"
Мысль что все виды браузеров, ос и устройств можно просто разнести на жостко категоризируемые данные немного оторвана от реальности, в которой я могу вообще любой мусор напихать в свой user_agent.
Прежде чем это делать нужно очень четко представлять себе конечную цель твоей работы. Возможно, это реально требуется. Но даже в таком случае, почти наверняка тебе не нужно нормализовать эти столбцы в отдельные таблицы, потому что почти наверняка, отдельно от сущности реквеста эти данные тебе не нужны. Берешь какую-то из библиотек по вычислению браузера/ос/чепухи, у нормальных они будут индексированны. В таблицу вставляешь уже индексированные значения. В приложении селектишь по этим-же индексам. Как пример. Если потом захочешь поменять библу, можно замапить новые индексы на старые, или сразу сделать свой мап если живешь в режиме паранойи.
Мимо проходил в надежде стать более лучше одеваться.
Перед include('config.php);
А вообще, идея в том, что даже если тебе твой пхп и не покажет эрор на использовании этой функции, то ты сам её замени на то, как в мануале показано.
Бляяять, костыль действительно работает. Анон, я бы тебя расцеловал во все места, в которые можно расцеловать. Счастья тебе, здоровья, жену красавицу или трапа красивого, в общем всего всего. Спасибо, что на меня дауна потратил свое время!
>Поэтому использовать нужно коммон сенс.
>Прежде чем это делать нужно очень четко представлять себе конечную цель твоей работы. Возможно, это реально требуется. Но даже в таком случае, почти наверняка тебе не нужно нормализовать эти столбцы в отдельные таблицы, потому что почти наверняка, отдельно от сущности реквеста эти данные тебе не нужны.
Гуд поинт бро, вообще вспоминается цитата одного мудрого анона, что типа есть нуб, который может навасянить простую лапшу, а есть типа обученая макака, которая нахуярит паттернов всяких и прочего говна, потому что так надо, а в итоге какой-нибудь гуру с 10 летним стажем, который точно так же мог бы нахуярить паттернов и прочего говна, выберет тот же вариант что и нуб - просто написать быстро что бы работало и забыть - потому что нахуй это усложнение пока не нужно - потом понадобится - переделает на изи.
Так вот глядя на подобные высеры
>За денормализованные данные по ебалу бьют в нормальных конторах.
Понимаю, что передо мной скорее всего вчерашний долбоеб, которого в шараге научили чему-то и теперь он будет вопреки здравому смыслу хуярить так как его научили везде и всюду. Такие челики никогда не задаются наверное вопросами в духе: интересно а как там вконтакте это сделано или в фейсбуке? Представить даже сложно какие там ухищрения что бы такими данными ворочать.
Вот уж точно орнул, когда представил, что устраиваешься например в фейсбук, и там блядь вся эта ваша бигдата нормализованна и юзер размазан по милиону таблиц, ну а хуле, John - популярное имя, нехуй дублироваться, го отдельную таблицу под имена)))
После того как осилю учебник. Навскидку - тело цикла, например, рассматривать подробнее, что в нем может находиться.
Во первых перестань писать под спойлером - это адово тупо и бесит.
Во вторых если хочешь упражнений на циклы, то вот тебе на вскидку 3 для того что бы лучше разобраться.
1. Напиши кусок кода который умеет считать факториалы.
Пусть выводит факториалы от 1! до 10! например.
2. То же самое но с числами фибоначи.
3. Условия знаешь? Тогда напиши физз-базз:
Напишите программу, которая выводит на экран числа от 1 до 100. При этом вместо чисел, кратных трем, программа должна выводить слово «Fizz», а вместо чисел, кратных пяти — слово «Buzz». Если число кратно и 3, и 5, то программа должна выводить слово «FizzBuzz»
а
>Условие в цикле можно сократить до одного if, когда не нужно будет обновлять эти переменные.
Снова 2 часа перебирания. Почему бы тебе не сказать что это за условия, ибо я, откровенно говоря - идиот или необучаемый.
Я в этом треде уже много насрал - вот и скрываю. Да и кому нужны эти претензии пока я не составил конкретные пояснения что/где/куда? Пока еще рано.
Спасибо за задачи, я и правда не до конца понял как работает цикл. С пониманием остального материала таких проблем не возникало. Просмотр решения других анонов + чтения учебника + учебник ОПа в моем случае сработали.
>>178825
>>178659
>>178190
Видишь - много ненужного флуда. Надо выражать мысль лаконичнее. Горю я сильно от своей ограниченности, анон, контролировать получается не очень.
Применимо к PHP это, например, вот что:
https://github.com/idealo/php-middleware-stack
по крайней мере к этому идет.
вот:
http://sandbox.onlinephpfunctions.com/code/ee8b5691bf732bd63333003f7425003db2fdee7f
>идиот или необучаемый
Терпение, труд и регулярные занятия решат эти проблемы.
Ебать тебя порвало, братишка. Сразу скатился в ad hominem и скобочки подключил с боевыми картиночками.
Но вот только нубас ты, ведь сам в этом расписался:
>>178339
>Сам часто делал в продакшене хуйню уровня
>не можешь справиться со сложными запросами
То есть в базах данных ты хуй простой, но мнение имеешь.
по нему мало инфы
Выбор фреймворка от проекта зависит
для работы на дядю
>С высокомерным пидрилой
Я не он, но ты так говоришь, как будто это плохо. Высокомерным быть лучше, чем тупым и самоуверенным, как ты.
Какие подводные?
До этого работал с yii2
Расстояние между А и В 18 км, первая заправка находится от А на расстоянии 9 км, расстояние от первой до след. заправки 4 км, от второй до B 5 км. Нужно оптимально разместить три новые заправки (помимо этих трех, эти три перемещать нельзя) чтобы минимизировать максимальное расстояние между двумя заправками подряд (на всем маршруте).
Т.е. имеем массив [9,4,5] и количество заправок k = 3, нужно конкретно для этой задачи получить массив [3,3,3,4,2.5,2.5], но как это сделать алгоритмически и для любых данных, ума не приложу..
пс может вопрос тупой, сори, но если кто сталкивался или сообразит хелп плз! Всем мир!
Братик, у тебя почерк неровный. Из твоего поста вытекает, что уже существует всего 2 заправки
Да есть такое, изначально заправки две, затем над добавить еще 3 в оптимальных местах (т.е. k = 3 это новые заправки).
> но почти ничего не работает, нельзя например через фасад модели запрос к бд делать как в ларавеле.
А ты уверен, что эта возможность - это особенность eloquent, а не самого Ларавеля? Я сам не в курсе, если что.
Но вообще, эти фасады - это скорее вредная штука, так как это статические методы, они работают в обход DI. Хотя что-то я сейчас не могу вспомнить, чем именно они вредны.
> Так все фишки eloquent-а работают, но мне кажется эта идея слишком сомнительной из за глобальной переменной в коде.
Ну да, это уже не DI.
>>178805
Смотря в каком контексте. В веб-фреймворках middleware (посредник) - это обертка, в которую можно обернуть процесс обработки запроса. Ну то есть функция, которая получает на вход Request, может что-то с ним сделать, затем вызывает обработчик запроса, получает ответ (Response), опять же может с ним что-то сделать перед тем, как он будет отдан клиенту (браузеру).
Выглядеть это может так:
$middleware = function (Request $req, callable $next): Response
{
...
$response = $next($req);
...
return $response;
};
middleware может быть несколько, и они вкладываются друг в дружку.
Предвосхищая вопрос, зачем это нужно, вот несколько применений:
- ограничение доступа к определенным URL
- сжатие или кеширование ответов по определенным правилам
- шифрование кук: middleware может расшифровывать приходящие куки до обработки запроса, а после зашифровывать установленные куки до отдачи клиенту
- организация сессий
- защита от CSRF
- добавление/обработка каких-то заголовков
> но почти ничего не работает, нельзя например через фасад модели запрос к бд делать как в ларавеле.
А ты уверен, что эта возможность - это особенность eloquent, а не самого Ларавеля? Я сам не в курсе, если что.
Но вообще, эти фасады - это скорее вредная штука, так как это статические методы, они работают в обход DI. Хотя что-то я сейчас не могу вспомнить, чем именно они вредны.
> Так все фишки eloquent-а работают, но мне кажется эта идея слишком сомнительной из за глобальной переменной в коде.
Ну да, это уже не DI.
>>178805
Смотря в каком контексте. В веб-фреймворках middleware (посредник) - это обертка, в которую можно обернуть процесс обработки запроса. Ну то есть функция, которая получает на вход Request, может что-то с ним сделать, затем вызывает обработчик запроса, получает ответ (Response), опять же может с ним что-то сделать перед тем, как он будет отдан клиенту (браузеру).
Выглядеть это может так:
$middleware = function (Request $req, callable $next): Response
{
...
$response = $next($req);
...
return $response;
};
middleware может быть несколько, и они вкладываются друг в дружку.
Предвосхищая вопрос, зачем это нужно, вот несколько применений:
- ограничение доступа к определенным URL
- сжатие или кеширование ответов по определенным правилам
- шифрование кук: middleware может расшифровывать приходящие куки до обработки запроса, а после зашифровывать установленные куки до отдачи клиенту
- организация сессий
- защита от CSRF
- добавление/обработка каких-то заголовков
Передаешь данные о перемещении на сервер по вебсокету, пользователи по какому-нибудь вебсокету подписываются на изменения и получают информацию о перемещении. На сервере нужен демон, который будет поддерживать вебсокет-соединения и ретранслировать сообщения. Ключевые слова: javascript, websocket, Websocket Application Message Protocol, php websocket server, php multiplexor.
>>178813
> го отдельную таблицу под имена
Это ты в шутку или всерьез? Если второе, то ты похоже не понимаешь, в чем суть нормализации, зачем она нужна. Нормализация такого не подразумевает, так как имя само по себе не отдельная от пользователя сущность, а его свойство.
Если что, урок https://github.com/codedokode/pasta/blob/master/db/normalization.md
>>178809
Так а фреймворки не на PHP написаны? Как ты собрался изучать их, если PHP не понимаешь?
>>178804
Про нормализацию я попытался написать простыми словами тут https://github.com/codedokode/pasta/blob/master/db/normalization.md
> Однако, пример: posts [ id, author_id, content, category_title ] - не факт что такая структура требует нормализации.
Лучше сделать, так как 1) если надо обновить название категории, то придется обновлять кучу записей (а там окажется, что оно еще в нескольких таблицах используется) 2) если у категории надо добавить какие-то свойства, то тоже будет неудобно.
> Предположим, при написании поста автор может вообще что угодно придумать для категории, а поиск постов по ней осуществляется через вайлдкард - в таком случае смысла в нормализации нет.
Это называется теги, и их обычно реализуют через нормализованные таблицы, так как завтра захочется получить число постов по каждому тегу и тд. А без нормализации у тебя из-за добавления пробела получается другой тег.
> Например, поле ip - содержит четыре числа через точку. Это-же очевидный массив.
Вообще, конечно, нет. для IP удобно использовать BINARY и в СУБД обычно есть функции преобразования между текстовым и бинарным форматами.
Передаешь данные о перемещении на сервер по вебсокету, пользователи по какому-нибудь вебсокету подписываются на изменения и получают информацию о перемещении. На сервере нужен демон, который будет поддерживать вебсокет-соединения и ретранслировать сообщения. Ключевые слова: javascript, websocket, Websocket Application Message Protocol, php websocket server, php multiplexor.
>>178813
> го отдельную таблицу под имена
Это ты в шутку или всерьез? Если второе, то ты похоже не понимаешь, в чем суть нормализации, зачем она нужна. Нормализация такого не подразумевает, так как имя само по себе не отдельная от пользователя сущность, а его свойство.
Если что, урок https://github.com/codedokode/pasta/blob/master/db/normalization.md
>>178809
Так а фреймворки не на PHP написаны? Как ты собрался изучать их, если PHP не понимаешь?
>>178804
Про нормализацию я попытался написать простыми словами тут https://github.com/codedokode/pasta/blob/master/db/normalization.md
> Однако, пример: posts [ id, author_id, content, category_title ] - не факт что такая структура требует нормализации.
Лучше сделать, так как 1) если надо обновить название категории, то придется обновлять кучу записей (а там окажется, что оно еще в нескольких таблицах используется) 2) если у категории надо добавить какие-то свойства, то тоже будет неудобно.
> Предположим, при написании поста автор может вообще что угодно придумать для категории, а поиск постов по ней осуществляется через вайлдкард - в таком случае смысла в нормализации нет.
Это называется теги, и их обычно реализуют через нормализованные таблицы, так как завтра захочется получить число постов по каждому тегу и тд. А без нормализации у тебя из-за добавления пробела получается другой тег.
> Например, поле ip - содержит четыре числа через точку. Это-же очевидный массив.
Вообще, конечно, нет. для IP удобно использовать BINARY и в СУБД обычно есть функции преобразования между текстовым и бинарным форматами.
Не храни пароли в открытом виде, читай урок https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
После header Location надо завершать скрипт. зачем ты продолжаешь его выполнять и выводишь страницу?
Вместо error_reporting в коде лучше на своем локальном сервере прописать это в php.ini вместе с display_errors=1, но не делай так на боевом сервере.
>>178769
> что нормализация нужна реже чем нужна, потому что в небольших проектах она скорее всего будет только усложнять базу
Наоборот. Отсутствие нормализации усложняет работу с БД. Усложняет таким образом:
- если данные дублируются, то при обновлении надо найти все дубликаты и обновить их. Легко ошибиться и где-то пропустить это. Ну например, мы храним около поста и комментария имя пользователя. Пользователь поменял имя - мы должны везде его обновить. Пользователь забанен - надо искать и скрывать его комментарии.
- если у нас есть внешний ключ, ссылающийся на таблицу, то мы можем вставить только ссылку на существующую в той таблице строку. Если его нет - вставить можно что угодно.
- если данные неатомарные, то с ними неудобно работать, нельзя индексировать
Ну возьмем простой пример. Умник решил хранить теги к посту в одной колонке:
text | tags
Пост 1| php , js
Пост 2| уроки php ,photoshop
Удобно ли тут искать записи с тегом php? Легко ли добавить тег, удалить тег SQL-запросом? Можно ли сделать индекс для быстрого поиска по тегу? Если мы захотим каждому тегу добавить атрибут "скрытый", легко ли это сделать?
Ты сэкономишь какое-то время на изучении нормализации, да, но ты потеряешь его, когда будешь писать более сложный код, находить в нем ошибки, писать скрипты для их исправления. Когда твоим коллегам придется разбираться в базе без внешних ключей. Я видел БД, которые спроектировали люди без знаний нормализации и таких людей конечно подпускать к проектированию баз данных нельзя.
> ну кароче у меня к этой таблице селектов 99%, поэтому тут всё нормализованно и проиндексированно по айдишникам, шоб быстрее,
Ты же не понимаешь, в чем суть нормализации. Она не для того, чтобы ускорить какой-то поиск. А чтобы данные хранить в наиболее удобном для работы с ними виде.
Не храни пароли в открытом виде, читай урок https://github.com/codedokode/pasta/blob/master/security/password-hashing.md
После header Location надо завершать скрипт. зачем ты продолжаешь его выполнять и выводишь страницу?
Вместо error_reporting в коде лучше на своем локальном сервере прописать это в php.ini вместе с display_errors=1, но не делай так на боевом сервере.
>>178769
> что нормализация нужна реже чем нужна, потому что в небольших проектах она скорее всего будет только усложнять базу
Наоборот. Отсутствие нормализации усложняет работу с БД. Усложняет таким образом:
- если данные дублируются, то при обновлении надо найти все дубликаты и обновить их. Легко ошибиться и где-то пропустить это. Ну например, мы храним около поста и комментария имя пользователя. Пользователь поменял имя - мы должны везде его обновить. Пользователь забанен - надо искать и скрывать его комментарии.
- если у нас есть внешний ключ, ссылающийся на таблицу, то мы можем вставить только ссылку на существующую в той таблице строку. Если его нет - вставить можно что угодно.
- если данные неатомарные, то с ними неудобно работать, нельзя индексировать
Ну возьмем простой пример. Умник решил хранить теги к посту в одной колонке:
text | tags
Пост 1| php , js
Пост 2| уроки php ,photoshop
Удобно ли тут искать записи с тегом php? Легко ли добавить тег, удалить тег SQL-запросом? Можно ли сделать индекс для быстрого поиска по тегу? Если мы захотим каждому тегу добавить атрибут "скрытый", легко ли это сделать?
Ты сэкономишь какое-то время на изучении нормализации, да, но ты потеряешь его, когда будешь писать более сложный код, находить в нем ошибки, писать скрипты для их исправления. Когда твоим коллегам придется разбираться в базе без внешних ключей. Я видел БД, которые спроектировали люди без знаний нормализации и таких людей конечно подпускать к проектированию баз данных нельзя.
> ну кароче у меня к этой таблице селектов 99%, поэтому тут всё нормализованно и проиндексированно по айдишникам, шоб быстрее,
Ты же не понимаешь, в чем суть нормализации. Она не для того, чтобы ускорить какой-то поиск. А чтобы данные хранить в наиболее удобном для работы с ними виде.
Зависит от ситуации, если запросов много, то зачастую один большой запрос выгоднее, чем много мелких.
>>178342
> user_agent_id, phone_id, ip_address_id
Ip адрес может быть отдельной сущностью, а может и не быть в зависимости от ситуации. Если у тебя с IP-адресом связаны какие-то данные, например, "репутация", то это отдельная сущность. Вместо искуственного ключа тут можно использовать естественный - сам IP адрес в бинарной форме (BINARY(4)), благо он весит всего 4 байта, а в СУБД есть функции преобразования из текстового в бинарный формат.
То же относится к phone_id и тд.
Нормализация не требует каждое поле выносить в отдельную таблицу.
С другой стороны, рассмотрим такую ситуацию, что пользователь, как правило, пользуется одним и тем же устройством. Тогда, может быть, выгоднее сделать таблицу устройств и таблицу-лог вида
time | user_id | device_id
Урок https://github.com/codedokode/pasta/blob/master/db/normalization.md
>>178660
Ну общая идея такая:
- программа это последовательность команд, после каждой ставится точка с запятой:
команда 1;
команда 2;
- команда - это например echo 2 + 2; или $x = 3 + 3;
- программа по умолчанию выполняется сверху вниз
- блоком называют последовательность команд, заключенных в фигурные скобки:
{
команда 1;
команда 2;
}
блоки используются не сами по себе, а как часть операторов вроде if, for, foreach.
- специальная команда if позволяет выполнять действия в определенном блоке или пропускать блок в зависимости от условия
- циклы for, foreach, while, do позволяют выполнить блок несколько раз подряд в зависимости от условий
- в программе могут содержаться функции, они начинаются со слова function, за которой идет имя:
function x()
{
команда 1;
команда 2;
}
Слово function только создает функцию, но пока не выполняет команды в ней. Также, есть анонимные функции, у них нет имени и потому указатель для их вызова сохраняется в переменную:
$x = function () { ... };
- в программе можно вызвать функцию, указав ее имя и круглые скобки: echo x(); Анонимная функция вызывается через переменную, в которой хранится указатель на нее: echo $x();
Вот краткое описание синтаксиса PHP и того, что можно делать.
Ты бы спрашивал вопросы, если что-то непонятно.
Зависит от ситуации, если запросов много, то зачастую один большой запрос выгоднее, чем много мелких.
>>178342
> user_agent_id, phone_id, ip_address_id
Ip адрес может быть отдельной сущностью, а может и не быть в зависимости от ситуации. Если у тебя с IP-адресом связаны какие-то данные, например, "репутация", то это отдельная сущность. Вместо искуственного ключа тут можно использовать естественный - сам IP адрес в бинарной форме (BINARY(4)), благо он весит всего 4 байта, а в СУБД есть функции преобразования из текстового в бинарный формат.
То же относится к phone_id и тд.
Нормализация не требует каждое поле выносить в отдельную таблицу.
С другой стороны, рассмотрим такую ситуацию, что пользователь, как правило, пользуется одним и тем же устройством. Тогда, может быть, выгоднее сделать таблицу устройств и таблицу-лог вида
time | user_id | device_id
Урок https://github.com/codedokode/pasta/blob/master/db/normalization.md
>>178660
Ну общая идея такая:
- программа это последовательность команд, после каждой ставится точка с запятой:
команда 1;
команда 2;
- команда - это например echo 2 + 2; или $x = 3 + 3;
- программа по умолчанию выполняется сверху вниз
- блоком называют последовательность команд, заключенных в фигурные скобки:
{
команда 1;
команда 2;
}
блоки используются не сами по себе, а как часть операторов вроде if, for, foreach.
- специальная команда if позволяет выполнять действия в определенном блоке или пропускать блок в зависимости от условия
- циклы for, foreach, while, do позволяют выполнить блок несколько раз подряд в зависимости от условий
- в программе могут содержаться функции, они начинаются со слова function, за которой идет имя:
function x()
{
команда 1;
команда 2;
}
Слово function только создает функцию, но пока не выполняет команды в ней. Также, есть анонимные функции, у них нет имени и потому указатель для их вызова сохраняется в переменную:
$x = function () { ... };
- в программе можно вызвать функцию, указав ее имя и круглые скобки: echo x(); Анонимная функция вызывается через переменную, в которой хранится указатель на нее: echo $x();
Вот краткое описание синтаксиса PHP и того, что можно делать.
Ты бы спрашивал вопросы, если что-то непонятно.
Пока ждешь, пробуй смотреть другие задачи, читать урок дальше, поискать информацию сам, а вдруг найдешь понятное объяснение.
>>178190
> $zero = 0;
Это плохое название для переменной, так как далее ты ее увеличиваешь и там уже не zero. Надо писать, что хранится в переменной, для чего она нужна. В данном случае переменная указывает на позицию символа слева и ее можно назвать $leftPosition, $leftPos, $leftCharPos.
> for ($i = 1; $halfLength == $count; $i++) {
Цикл написан с ошибкой. Второй компонент в заголовке - это условие продолжения цикла. Пока оно верно, цикл выполняется, как только становится неверно - цикл завершается. У тебя в начале $count равно нулю, $halfLength - половина длины строки, которая больше нуля. Условие равенства не выполняется, цикл не выполняется ни разу.
Надо писать
$i < $halfCount
то есть, выполнять, пока $i меньше, чем $halfCount.
> } elseif($halfLength == $count) {
Это можно было не делать, а просто поставить echo $result после цикла. Так как цикл должен завершиться, когда перестанет выполняться условие $i < $halfCount, и начнет выполняться код за ним.
>>178627
И еще одна причина появления ООП - это то, что многие вещи удобно представлять именно в виде объектов, у которых есть свойства (поля), и действия над ними (методы).
> $before = checkForTypos($text);
Функция же ничего не возвращает.
> } else {
> echo "Текст успешно исправлен, опечаток больше нет!";
> exit();
exit это плохо, так как ты когда пишешь функцию, не знаешь, кто и когда ее будет вызывать (даже если ты пишешь один, притворись, что не знаешь, и не видишь код снаружи функции). Соответственно ты можешь кому-то этим exit нарушить логику работы программы, код после вызова функции не выполнится. Функция не должна самовольно завершать всю программу.
Также, твоя программа видит в латинских словах опечатки в каждой букве: https://ideone.com/LqT9UN
Надо искать только слова из смеси двух алфавитов.
> Урезанная версия с просто исправлением
Та же проблема, она заменяет все латинские буквы, не глядя, это русское слово или английское.
>>178258
read lock запрещает запись в таблицу, но не запрещает добавление блокировок к ней. Это излишняя мера (она снижает производительность, так как другие скрипты ждут снятия блокировки), но блокировка отдельных строк может быть полезна для согласованного чтения или обновления. Представь такую ситуацию:
- мы банк
- у пользователя есть в нем 2 аккаунта
- нам надо взять деньги с аккаунта 1, аккаунта 2 и найти сумму денег пользователя
Мы пишем код:
$sum1 = getValue("SELECT ... WHERE account = 1");
$sum2 = getValue("SELECT ... WHERE account = 2");
$sum = $sum1 + $sum2;
Корректен ли он?
Ну конечно нет. В промежутке между получением $sum1 и $sum2 могли произойти переводы денег с аккаунта 1 на аккаунт 2 и сумма получится больше фактической. Для предотвращения этого мы можем заблокировать строчку с первым аккаунтом:
BEGIN
SELECT ... WHERE account = 1 FOR SHARE
...
COMMIT
Если мы потом эту сумму хотим записать куда-то, то стоит заблокировать еще и строчку со вторым аккаунтом, чтобы она не менялась в процессе.
Там еще есть опция получения эксклюзивной блокировки на строчку - FOR UPDATE.
Это может пригодиться, например, при использовании materialized path, заблокировать комментарий на время вычисления пути для нового вставляемого.
Теперь второй вопрос. Мы пишем перевод денег со счета 1 на счет 2. Какие тут есть подвохи?
UPDATE ... SET value = value - 100 WHERE account = 1
UPDATE ... SET value = value + 100 WHERE account = 2
Я вижу минимум два:
- скрипт может упасть на полпути, и деньги не дойдут. Или отключится электричество.
- другая транзакция может читать записи между 1-й и 2-й строчкой и увидит меньшую сумму денег, чем есть
Обе этих проблемы решаются заворачиванием в транзакцию:
BEGIN
UPDATE ... SET value = value - 100 WHERE account = 1
UPDATE ... SET value = value + 100 WHERE account = 2
COMMIT
Изменения, вносимые внутри транзакции, не видны другим до выполнения COMMIT.
Теперь вопрос: если же нам надо снять с 2 счетов по 100 тугриков с каждого и положить на третий, то что мы делаем?
Мануалы:
- https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html
- https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-shared-exclusive-locks
- https://ru.wikipedia.org/wiki/Уровень_изолированности_транзакций
> $before = checkForTypos($text);
Функция же ничего не возвращает.
> } else {
> echo "Текст успешно исправлен, опечаток больше нет!";
> exit();
exit это плохо, так как ты когда пишешь функцию, не знаешь, кто и когда ее будет вызывать (даже если ты пишешь один, притворись, что не знаешь, и не видишь код снаружи функции). Соответственно ты можешь кому-то этим exit нарушить логику работы программы, код после вызова функции не выполнится. Функция не должна самовольно завершать всю программу.
Также, твоя программа видит в латинских словах опечатки в каждой букве: https://ideone.com/LqT9UN
Надо искать только слова из смеси двух алфавитов.
> Урезанная версия с просто исправлением
Та же проблема, она заменяет все латинские буквы, не глядя, это русское слово или английское.
>>178258
read lock запрещает запись в таблицу, но не запрещает добавление блокировок к ней. Это излишняя мера (она снижает производительность, так как другие скрипты ждут снятия блокировки), но блокировка отдельных строк может быть полезна для согласованного чтения или обновления. Представь такую ситуацию:
- мы банк
- у пользователя есть в нем 2 аккаунта
- нам надо взять деньги с аккаунта 1, аккаунта 2 и найти сумму денег пользователя
Мы пишем код:
$sum1 = getValue("SELECT ... WHERE account = 1");
$sum2 = getValue("SELECT ... WHERE account = 2");
$sum = $sum1 + $sum2;
Корректен ли он?
Ну конечно нет. В промежутке между получением $sum1 и $sum2 могли произойти переводы денег с аккаунта 1 на аккаунт 2 и сумма получится больше фактической. Для предотвращения этого мы можем заблокировать строчку с первым аккаунтом:
BEGIN
SELECT ... WHERE account = 1 FOR SHARE
...
COMMIT
Если мы потом эту сумму хотим записать куда-то, то стоит заблокировать еще и строчку со вторым аккаунтом, чтобы она не менялась в процессе.
Там еще есть опция получения эксклюзивной блокировки на строчку - FOR UPDATE.
Это может пригодиться, например, при использовании materialized path, заблокировать комментарий на время вычисления пути для нового вставляемого.
Теперь второй вопрос. Мы пишем перевод денег со счета 1 на счет 2. Какие тут есть подвохи?
UPDATE ... SET value = value - 100 WHERE account = 1
UPDATE ... SET value = value + 100 WHERE account = 2
Я вижу минимум два:
- скрипт может упасть на полпути, и деньги не дойдут. Или отключится электричество.
- другая транзакция может читать записи между 1-й и 2-й строчкой и увидит меньшую сумму денег, чем есть
Обе этих проблемы решаются заворачиванием в транзакцию:
BEGIN
UPDATE ... SET value = value - 100 WHERE account = 1
UPDATE ... SET value = value + 100 WHERE account = 2
COMMIT
Изменения, вносимые внутри транзакции, не видны другим до выполнения COMMIT.
Теперь вопрос: если же нам надо снять с 2 счетов по 100 тугриков с каждого и положить на третий, то что мы делаем?
Мануалы:
- https://dev.mysql.com/doc/refman/8.0/en/lock-tables.html
- https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html#innodb-shared-exclusive-locks
- https://ru.wikipedia.org/wiki/Уровень_изолированности_транзакций
> ([а-яёА-ЯЁ]+)?
+ и ? можно заменить на звездочку.
> (([а-яёА-ЯЁ]+)?([a-zA-Z])([а-яёА-ЯЁ]+)?){1,}
Вообще, эта регулярка не требует наличия буквы кириллицы, а вполне совпадет с единственной латинской буквой.
Хотя программа как-то и работает, регулярки, думаю, надо улучшить.
>>178200
Так ты только его и выводишь командой var_dump. Выполняются несколько раз только действия внутри цикла (блока фигурных скобок), а var_dump стоит после и выполняется один раз.
те только через костыли
Смотри как тебе такое универсальное решение
На вход берешь массив сегментов и количество разделений которые нужно добавить.
Делишь 18 (общая длинна) / 5 (общее количество разделителей) = 3.6 - best т.е. в идеальном мире если бы заправки можно было двигать... заодно если у тебя всего один сегмент то ответ уже готов.
Вычисляешь
9 4 5 - наш массив ; 3 - остаток бюджета делителей
5.4 0.4 1.4 - отклонения сегментов от идеального; 2.4 - среднее отклонение
Берешь первый отрезок с самым большим отклонением и делаешь вот что: делишь его по очереди на разное количество сегментов в бюджете и считаешь среднее отклонение. Лучший разрез даст наименьшее отклонение.
4.5 4.5 4 5 ; 2
0.9 0.9 0.4 1.4 ; 0.9
3 3 3 4 5 ; 1
0.6 0.6 0.6 0.4 1.4 ; 0.72
2.25 2.25 2.25 2.25 4 5 ; 0
1.35 1.35 1.35 1.35 0.4 1.4 ; 1.2
Выбираем разрез на 3 части, в бюджете остается еще разрез. Повторим алгоритм, теперь резать будем 5, но так как вариант порезать сегмент всего один (у нас один разрез) то сразу получаем конечный результат:
3, 3, 3, 4, 2.5, 2.5 ; 0
0.6, 0.6, 0.6, 0.4, 1.1, 1.1 ; 0.88
Предположим другие входные данные:
50 22 2 9; 5; ( 10.375 )
Поехали:
25 25 22 2 9; 4; 10.125
16.66 16.66 16.66 22 2 9; 3; 6.7
12.5 12.5 12.5 12.5 22 2 9; 2; 4.26
10 10 10 10 10 22 2 9; 1; 2.9
8.33 8.33 8.33 8.33 8.33 8.33 22 2 9; 0; 3.73
Выбираем вариант с разделением на пять частей (тратим 4 делителя):
10 10 10 10 10 22 2 9; 1; 2.9
Ну думаю уже догадался что мы поделим и что получится.
На вскидку кажется нет узких мест.
Теперь давай попробуем прикинуть алгоритмическую сложность:
Первое: перед каждым разрезом мы проходимся по массиву для поиска самого большого куска, это N. Операцию выбора большого куска придется также повторить до тех пор пока k не кончится - N k, но так как k уменьшается то N log( k )
Второе: чтобы найти лучший разрез мы делим наш кусок на k..1 и каждый раздел расчитываем, это еще раз k, так что k ^ 2, но так как k уменьшается то log ( k ^ 2 )
Вторую операцию мы повторяем столько раз сколько дает первая, т.е.: N log( k ) log ( k ^ 2 )
Алгоритмическая сложность если я правильно понимаю O( N log( k ) log ( k ^ 2 ) )
Надеюсь если будет кто шарящий читать поправит.
Пойду устраиваться вместо тебя
Смотри как тебе такое универсальное решение
На вход берешь массив сегментов и количество разделений которые нужно добавить.
Делишь 18 (общая длинна) / 5 (общее количество разделителей) = 3.6 - best т.е. в идеальном мире если бы заправки можно было двигать... заодно если у тебя всего один сегмент то ответ уже готов.
Вычисляешь
9 4 5 - наш массив ; 3 - остаток бюджета делителей
5.4 0.4 1.4 - отклонения сегментов от идеального; 2.4 - среднее отклонение
Берешь первый отрезок с самым большим отклонением и делаешь вот что: делишь его по очереди на разное количество сегментов в бюджете и считаешь среднее отклонение. Лучший разрез даст наименьшее отклонение.
4.5 4.5 4 5 ; 2
0.9 0.9 0.4 1.4 ; 0.9
3 3 3 4 5 ; 1
0.6 0.6 0.6 0.4 1.4 ; 0.72
2.25 2.25 2.25 2.25 4 5 ; 0
1.35 1.35 1.35 1.35 0.4 1.4 ; 1.2
Выбираем разрез на 3 части, в бюджете остается еще разрез. Повторим алгоритм, теперь резать будем 5, но так как вариант порезать сегмент всего один (у нас один разрез) то сразу получаем конечный результат:
3, 3, 3, 4, 2.5, 2.5 ; 0
0.6, 0.6, 0.6, 0.4, 1.1, 1.1 ; 0.88
Предположим другие входные данные:
50 22 2 9; 5; ( 10.375 )
Поехали:
25 25 22 2 9; 4; 10.125
16.66 16.66 16.66 22 2 9; 3; 6.7
12.5 12.5 12.5 12.5 22 2 9; 2; 4.26
10 10 10 10 10 22 2 9; 1; 2.9
8.33 8.33 8.33 8.33 8.33 8.33 22 2 9; 0; 3.73
Выбираем вариант с разделением на пять частей (тратим 4 делителя):
10 10 10 10 10 22 2 9; 1; 2.9
Ну думаю уже догадался что мы поделим и что получится.
На вскидку кажется нет узких мест.
Теперь давай попробуем прикинуть алгоритмическую сложность:
Первое: перед каждым разрезом мы проходимся по массиву для поиска самого большого куска, это N. Операцию выбора большого куска придется также повторить до тех пор пока k не кончится - N k, но так как k уменьшается то N log( k )
Второе: чтобы найти лучший разрез мы делим наш кусок на k..1 и каждый раздел расчитываем, это еще раз k, так что k ^ 2, но так как k уменьшается то log ( k ^ 2 )
Вторую операцию мы повторяем столько раз сколько дает первая, т.е.: N log( k ) log ( k ^ 2 )
Алгоритмическая сложность если я правильно понимаю O( N log( k ) log ( k ^ 2 ) )
Надеюсь если будет кто шарящий читать поправит.
Пойду устраиваться вместо тебя
А как будут работать локи строк с myisam таблицами? Если я правильно понял там в любом случае при DML запросах она блочится целиком. И заодно суб-вопрос, зафигом там вообще myisam если даже текста нет...
И как думаешь нужен ли мне лок всей таблицы вот для чего:
есть основная допустим main: | id | ... |
и много разных:
main_secondary: | main_id | secondary_id |
Мне нужно удалить запись из основной, предварительно почистив все остальные. Но есть вероятность, что пока я буду их чистить и дойду до удаления основной, в какую-то из них опять что-то добавят.
В то-же время не хотелось бы лочить их все. И не хотелось бы получить висяки.
Я так понимаю, если добавить констрейнты foreign key, то мне просто не даст удалить запись если в процессе кто-то что-то опять добавит, но такой вариант мне тоже не нравится - просто inconvinient для пользователя.
Что если дописать там поле допустим QUEUED_FOR_DELETION, и во всех местах приложения не давать добавлять связи для таких сущностей, а потом когда-нибудь их удалить. Можно было бы конечно вообще не удалять в таком случае но таблица будет расти и место там не резиновое.
У меня как то так вышло: http://sandbox.onlinephpfunctions.com/code/0494c1a1d92e3584993a9514f8e60459c33c57b5
Тут конечно еще нужно допиливать. Надо бы больше тестов. Проверка входящих значений, имена поправить и т.д. Хотя я собой доволен. Когда твой пост прочитал, вообще не понимал как подступиться.
Как же лаконично и алгоритмически более эффективно.
А казалось бы просто представь сегмент как что-то самостоятельное, пиздец я еще тупой, рановато мне пытаться помогать кому-то.
Для чего лучше? Оба варианта хуевы для взаимодействия, что первый долбоеб которому ничего не докажешь, но он окукливается при первой же угрозе и будет тебя скрыто хейтить, иногда попердывая при удобном для него случае, как и второй, который будет стоять на своей неправоте и считать что спор выигран тем, за кем было последнее слово или тот кто громче крикнул.
Вопрос по постгресу. Уменя есть колонка в таблице с варчар данными, которые есть обычный json. Типа:
{"Lat":"123","Lon":"231"}
Суть в том, что мне нужно сделать из неё jsonb, но так, чтобы значения были нумерик, а не текст. Обычный alter, который set type jsonb using col::jsonb переводит в обычный текст (значения в кавычках), а мне нужно от этих кавычек избавиться. Как быть?
Меня почему - то ступор на самых простых вещах, каждый раз. В голову не пришла мысль, что если цикл не ломается - значит все совпало. И что считать не надо, так как уже все считается.
А по поводу условия в цикле - это уже просто невнимательность. Много раз переделывал, "глаз замылился".
http://sandbox.onlinephpfunctions.com/code/ce073838ae8fa5753fef60ff8c5018ca95a65de4
Так верно?
я вообще изучал этот кал. это же фронтэнед, а мы тут учим php sql ООП фреймворки линуху
Покажи как ты сохраняешь
Сделать бекап. Добавить новую колонку типа JSON. Сделать UPDATE и сконвертировать значения из старой колонки в новую. Проверить, что все ок. Дропнуть старую колонку.
Какой посоветуете? Мало ли, может придется использовать сервер в будущем и я уже буду знаком со всеми настройками
Умные люди ресайзят картинки на клиенте.
Я опять выхожу на связь. До меня дошло что я не знаю статус выполнения задач, и как я понял то beanstalkd мне ничего не скажет. Мне теперь надо запилить еще таблицу статуса задач в бд, и к ней при реквесте обращаться? Не ну это уже некрасиво.
на 3 трап?
Это ОП.
Всегда пишется что build successful
Есть идея запилить скрипт, который будет чекать определённый элемент на странице сайта. Простой чек по коду 200 не подходит, потому что сайт может отдавать такой код и при этом, например, быть пустой страницей или если оплата вовремя не внесена была домен всё равно будет доступен только будет редиректить на скрипт хостера.
Норм идея так делать? Сайты мои, поэтому можно не париться, что элемент, наличие которого чекаю, исчезнет.
>>Какие ты видишь решения проблемы?
>Я ещё не освоился с JS, но пока могу сказать, что это нужно разделить либо на отдельные функции, либо разбить на два класса - Helper и Handler, который вешает обработчики (к примеру, Handler.prototype.handleClickOnImg...).
Я ещё подумал, что можно разить ещё на отдельные классы, например PopUp и поместить в него все методы связанные с PopUp'ом. Например, PopUp.handle(); или PopUp.resize() и т.д.
Я только не знаю насколько это будет читабельно https://codepen.io/anon/pen/YLGNRY?editors=0010
Мне лично нравится такое решение.
1) Сразу ставить линупс и привыкать к нему или всякие апачи и на окнах нормально функционируют? Если на окнах придрочусь, не будет такого что в линупсе по другому всё работает? На каких осях кодят во всяких кампаниях, куда меня потенциально могут взять джуном? люблю вас
Ну смотри, с линуксом ты в любом случае столкнешься, так что выбор не между натуралом и геем, а между натуралом и бисексуалом.
С такой точки зрения может даже лучше быть бисексуалом - ты будешь знать много всяких тонкостей о которых натуралы даже не задумываются.
Минус же в том что обычный традиционный секс тебя уже не будет удовлетворять.
Если что, сервак на дебиане 8, апач 2.4, пыха 7.2.
Вопрос к про, задали сегодня на собеседование.
Написать семафор для доступа к абстрактному ресурсу на 7 строк кода PHP.
Я чет тормазнул на этом, вообще все реализации что были в голове и то что я знаю о семафорах подразумевало много больше кода.
Чего от меня хотели?
Сможет кто решить это?
>Чего от меня хотели?
Ну, меня например, неделю назад на улице остановили и сказали сюда иди сюка. Потом избили, прошлись по мне ногами, но не обоссали, а достали свои мужыцкие достоинства и давай трясти ими.
А тебе велели семафор... семафор для доступа... для доступа к абстрактному ресурсу на 7 строк кода... на PHP.
Отбой, разобрался. В апаче поменял, а про nginx забыл.
Может, от тебя хотели просто знание библиотечных функций: http://php.net/manual/en/book.sem.php
В чем проблема? По идее все должно работать правильно =(
Для наглядности увеличил число слогов до 40 https://ideone.com/xxWWWY
Индексы в массивах в php начинаются с 0, а не единицы. Поэтому лучше сделать так
>$random = mt_rand(0, count($letters)-1);
Дайте пожалуйста убер-краткий гайд с примерами для даунского вката в sql на уровне "решить задачу на собеседовании". САМЫЕ основы - как добавлять, удалять, изменять данные знаю. Но вот промежуточные таблицы в запросе, функции всякие, объединения/соединения таблиц, вычитания, переменные в запросе - я вообще ноль. Памахити, братишки.
Так там тесты, а не гайды. Или ты вот это
http://www.sql-tutorial.ru
имел ввиду?
Там дохуя очень, я за полдня не успею прошариться. Так бы на офф-сайте доки, может, читал с гуглопереводчиком, если бы времени было вагон.
На openserver это делалось в разы интуитивней
Бекенд же ведь он разный бывает. Если просто сайты делать, то действительно немного интересного. А так иногда приходится и базы настраивать и проектировать, репликацию, веб-сокеты.
Быстро решительно добавляешь ссылку на сайт на панель закладок. Сайт теперь открывается в один клик.
Я об этом не подумал. Спасибо
А почему во всех туториалах по началу работы с php советуют брать связку apache+MySQL+PHP7, а не OpenServer, где все это есть и работа происходит чисто интуитивно?
Потому что openserver это чисто виндовая хуйня. Следоваетльно чуть более чем полностью бесполезная. Ты когда будешь сайт на хостинг переносить столкнёшься с линухом куда будешь накатывать apache/nginx + mysql + php
Хмм, понятно. А есть какой нибудь туториал по apache? Я имею ввиду вот зачем мне Apache Service monitor? У меня там даже start не горит. Вдруг есть какая то инфа, про которую стоило бы узнать заранее? Был бы благодарен
Накати лучше линух в виртуалку хотя бы и играйся там с установкой и настройкой окружения. Там это всё приятней и быстрее делается, чем в винде.
На винду не случайно запилили все эти комбайны типа опенсервера и xammp ибо устанавливать все эти компоненты по отдельности тот ещё гемор под винду.
Да... Практика в линуксе определенно нужна, но в любом случае пока что в планах - это осилить PHP. Думаю это еще не скоро будет
Хорошо. Спасибо за ответ анончик.
Хотя у меня последний вопрос остался. Как определять, например, что нужно выполнять на js, а что на php?
Cлишком общий вопрос. Зависит от задачи, очевидно. Нельзя ответить коротко и однозначно.
Как в задаче с "Сумма прописью" числам придать женский род?
Хуяришь массив. Цифор всего десять.
>устраиваешься.
Там то все понятно, требования везде примерно одинаковые по шаблону, абстрактное знание php, mysql, html, js , jquery.
А вот как быстро спасти себя от голодной смерти с помощью пхп с помощью фриланса - не известно.
Задачка с выводом email-адресов из текста:
https://ideone.com/0ZiZMs
Вроде я её и решил, но мне не нравится такое решение, ибо я хотел бы, чтоб мне выводились только имэйл адреса, а не то, что получается у меня (у меня тоже адреса, но они делятся по частям регулярного выражения в массив)
Подскажите, как можно вывести только название адреса.
Спасибо!
Шутишь? Он огромный, а в оп-посте самые азы. Ладно, спасибо.
>устроился хуесосом в говноконтору ковырять лендинги
>мам у вас в пхп так скучно, где интересное то?
Чому ты такой тупой?
Кому заняться нечем, можете рейтануть, раскритиковать мой код. Советы тоже норм
Обмен валют - https://ideone.com/GxnpIE
Игра в кубики - https://ideone.com/fpmVXk
Таблица умножения - https://ideone.com/XIc2VN
Процент вклада - https://ideone.com/v9dwvI
Расчет кредита - https://ideone.com/UOy1TU
Средний балл - https://ideone.com/Wu7ve0
Сравнение роста - https://ideone.com/0iNujR
РулетОчка - https://ideone.com/akfHyX
Генератор имён - https://ideone.com/qTV2wH
Шифровка - https://ideone.com/M9ebqb
Люблю вас
А формы от csrf защитил? А как быть с формой, если страница кэшируется? А валидацию с сохранением промежуточных значений в сессию сделал? А что, если для записи данных нужно двадцать таблиц из трёх бд дёрнуть, как вон выше у анона с ёба-нормализованной бд, а клиент хочет сразу ответ получить? И так далее, и так далее
Жаль тот челик с нормализованной бд окуклился и не ответил в чем таки профит его подхода.
Что там критиковать-то, кек, три строчки.
https://ideone.com/2ZrJCa
post-message { margin: 5px; }
так и
blockquote { margin: 1em 5px;}
Не подхватывается, кто-нибудь сталкивался?
Что ты хочешь мне сказать?
И вообще сори, отбой - всё заработало, я забыл галочку в настройках поставить собственно, которая активирует всё это.
Хочу чет на безе GET, как описывалось в пасте про MVC, но загуглить не смог.
Ну так это, берёшь какой-нибудь известный микрофреймворк типа Slim (или Fat Free Framework) и смотришь как там устроен роутинг.
так они все достаточно громоздкие, хотелось бы чет простое. Чисто для понимания как такое написать и собственно написать.
Я бы начал такое вот васянить: допустим хочу что бы мои урлы были вида:
site/controller/method/argument
На примере студентов это бы выглядело:
student.list/Student/show/123
Student - это контроллер тут
show - это метод контроллера
123 - это аргумент который мы ему передаем ( id студента которого мы хотим посмотреть )
Ну и вот тебе надо смотреть в $_SERVER там смотреть в какое-нибудь ["PATH_INFO"]
дергать оттуда все эти куски урла
далее отдавать специальной функции, которая будет как-то так всё это обрабатывать:
$test = new $controller; //в $test ляжет новый объект класса Student;
$test->$method($argument); //тут она вызовет метод show и передаст ему аргумент 123 в примере
Ну и для начала норм как по мне.
Спасибо энивей за помощь и молниеносный отклик, настроил себе окошко двача так, что вообще ничего лишнего и легко и приятно стало читать большие посты на мелком монике, ну и форма ответа теперь внезапно наменого менее мешающая.
>>180475
Скажем выводился жирным. Или тут нужно уже кастомный js подрубать что бы высчитывать подобное? И проще сразу идти писать абу что бы вводили?
Да, нужно скрипт писать, ибо сообщения в самом хтмл ничем не выделяются для каждого юзера, да и с жс ты вряд ли найдешь универсальный способ определения того куска текста, который именно для тебя писался. Потому что я могу так сделать
>1
>2
текст
Могу так
текст
>1
Могу даже те>1кст
Вот пример маломальский того что я хотел бы на пикриле.
Хотя бы сам номер поста который мой - выделяеся жирным, помню пару тредов назад кто-то вкидывал как можно кастом js какой-то через закладки браузера активировать на страницах. Есть идеи как можно по простому и быстро такое налепить?
А, ну это легко сделать наверно. Делаешь массив ТВОИХ ответов с треда, потом смотришь в пунктирных постах ответы и проверяешь со своим массивом, если true то делаешь жирным.
тест, что будет если отвечать самому себе.
Блядь в учебнике же написано, что нужно перекатываться на ideone
Вопрос в том, что как это прикрутить к двачу со стороны клиента. Нашел уже тот пост с костылем, но во первых что-то это в лисе не работает, а во вторых надо же кнопку нажимать, а хочется автоматизировать и забыть.
>>180542
Учебник года 3 назад писался, а на идеоне прост забыли при перекате на новую версию пыхи подключить библиотеку в ini файле, напиши им в поддержку если хочешь. cont^9{actANUSiL<4deonePUNCTUMc9^Vom
Щелкните правой кнопкой на панели закладок (чтобы показать ее в Хроме, используйте Ctrl + Shift + B) или найдите в меню пункт "добавить закладку". В поле "название" введите что хотите, в поле URL введите:
javascript:var t = document.querySelectorAll('.post-wrapper:not(.watched-posts-marker)');Array.prototype.map.call(t, function (el) { var ref = el.querySelectorAll('.ABU-refmap a'); if (!ref.length) { var bg = el.querySelector('.post'); bg.style.backgroundColor = '#fbfb9d'; } });
(проверьте, что на сайте латинские буквы не заменены на кирилицу).
После этого сохраните закладку. Теперь при нажатии на эту кнопку запустится скрипт в контексте страницы и подсветит неотвеченные посты желтым.
>Нашел уже тот пост с костылем, но во первых что-то это в лисе не работает, а во вторых надо же кнопку нажимать, а хочется автоматизировать и забыть.
Переделой
https://ideone.com/l7OcfA
Зачем там implode вообще?
Букмарклет работает только по нажатию кнопки. Также, в него не запихнешь много кода.
Ты можешь сделать расширение к браузеру. Я могу помочь советом, если что-то непонятно. Главный недостаток расширений - ты никогда не знаешь, что они делают с твоими данными, не воруют ли они твои пароли и не следят ли за тобой. Но если ты пишешь расширение сам для себя, то это не проблема.
Расширение пишется на HTML/CSS/JS. Оно может содержать:
- фоновую страницу. Это HTML + JS код, который работает в своем, отдельном окружении (то есть можешь представить, что в браузере открывается невидимая вкладка и в нее загружается твой код и что-то там делает)
- кнопку, добавляемую на панель браузера или в адресную строку (browser action/page action). Она может показывать HTML страницу при нажатии или реагировать на это как-то еще.
- также оно может взаимодействовать с другими вкладками и внедрять в них JS скрипты (content scripts). Эти скрипты могут взаимодействовать с DOM страницы, например, находить посты и что-то с ними делать.
Собственно, тебе хватит простого расширения из кнопки в адресной строке (просто чтобы показать, что оно запущено, жать на нее не надо) и контентного скрипта, который будет автоматически внедряться во все вкладки с двачем.
В расширении делается специальный файл - манифест - который содержит информацию о расширении. Через него ты можешь указать иконку для кнопки, путь к контентному скрипту и URL страниц, в которые его надо внедрять.
Стандартный формат расширений - Chrome Extension. Этот формат разработан Гуглом и позже его адаптировали разработчики всех современных браузеров под названием WebExtension, включая Opera, Firefox и Edge. А я помню, как когда-то делал слои абстракции, чтобы сделать расширение и для chrome, и для firefox.
Я тебе советую не заморачиваться и делать как можно проще, для твоего расширения хватит манифеста, иконки и 1 скрипта.
Я бы советовал для облегчения кода не использовать jQuery, если это не замедляет разработку, так как расширение будет работать только в современных браузерах, где есть querySelector, classList и другие современные DOM методы. Там, где их нет, нет и поддержки таких расширений. Или использовать облегченный аналог jQuery вроде zepto.js (для сайтов, я бы его не рекомендовал, из-за совместимости только с новыми браузерами).
Ссылки:
- пример написания расширения (рус) https://canonium.com/articles/briefly-about-building-a-chrome-
- офиц туториал (англ) https://developer.chrome.com/extensions
- офиц док (англ) https://developer.chrome.com/extensions/devguide
- док. по совместимости с фаерфоксом (англ) https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Porting_a_Google_Chrome_extension
- про content scripts (рус) http://chrome-ext.blogspot.ru/2014/02/content-scripts.html
Задавай вопросы, если что.
Букмарклет работает только по нажатию кнопки. Также, в него не запихнешь много кода.
Ты можешь сделать расширение к браузеру. Я могу помочь советом, если что-то непонятно. Главный недостаток расширений - ты никогда не знаешь, что они делают с твоими данными, не воруют ли они твои пароли и не следят ли за тобой. Но если ты пишешь расширение сам для себя, то это не проблема.
Расширение пишется на HTML/CSS/JS. Оно может содержать:
- фоновую страницу. Это HTML + JS код, который работает в своем, отдельном окружении (то есть можешь представить, что в браузере открывается невидимая вкладка и в нее загружается твой код и что-то там делает)
- кнопку, добавляемую на панель браузера или в адресную строку (browser action/page action). Она может показывать HTML страницу при нажатии или реагировать на это как-то еще.
- также оно может взаимодействовать с другими вкладками и внедрять в них JS скрипты (content scripts). Эти скрипты могут взаимодействовать с DOM страницы, например, находить посты и что-то с ними делать.
Собственно, тебе хватит простого расширения из кнопки в адресной строке (просто чтобы показать, что оно запущено, жать на нее не надо) и контентного скрипта, который будет автоматически внедряться во все вкладки с двачем.
В расширении делается специальный файл - манифест - который содержит информацию о расширении. Через него ты можешь указать иконку для кнопки, путь к контентному скрипту и URL страниц, в которые его надо внедрять.
Стандартный формат расширений - Chrome Extension. Этот формат разработан Гуглом и позже его адаптировали разработчики всех современных браузеров под названием WebExtension, включая Opera, Firefox и Edge. А я помню, как когда-то делал слои абстракции, чтобы сделать расширение и для chrome, и для firefox.
Я тебе советую не заморачиваться и делать как можно проще, для твоего расширения хватит манифеста, иконки и 1 скрипта.
Я бы советовал для облегчения кода не использовать jQuery, если это не замедляет разработку, так как расширение будет работать только в современных браузерах, где есть querySelector, classList и другие современные DOM методы. Там, где их нет, нет и поддержки таких расширений. Или использовать облегченный аналог jQuery вроде zepto.js (для сайтов, я бы его не рекомендовал, из-за совместимости только с новыми браузерами).
Ссылки:
- пример написания расширения (рус) https://canonium.com/articles/briefly-about-building-a-chrome-
- офиц туториал (англ) https://developer.chrome.com/extensions
- офиц док (англ) https://developer.chrome.com/extensions/devguide
- док. по совместимости с фаерфоксом (англ) https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Porting_a_Google_Chrome_extension
- про content scripts (рус) http://chrome-ext.blogspot.ru/2014/02/content-scripts.html
Задавай вопросы, если что.
Как вообще знающие PHP используют CMS, пишете свои модули?
Я собирался начать учить, но немножечко время поджимает, со всех сторон в плане учебы, просто как бы я не прогадал.
Еще спрошу: планирую ставить разные хостинги, на Nginx с Joomla, правда времени наверное много уйдет, кто нибудь занимался этим? Какая база нужна?
Для отделения логики от представления не нужно делать второе приложение на JS (как я понял, у них на сервере 2 приложения - одно на JS/ReactJS, и оно обращается к другому на PHP). И цифры пугающие - 10 запросов к API на страницу.
Подумайте сами, все это можно было сделать на PHP:
- шаблонизаторы - есть, например, twig, они лучше чем React
- API - есть, вызываешь нужные функции напрямую вместо использования REST API и затрат на HTTP. Внутреннее API в 100 раз лучше чем REST.
Что интересно, в фронтенде у них используется еще и Angular.
Такое ощущение, что там просто набралась критическая масса яваскриптщиков, которые не хотят писать на PHP. Или может они тайно грезят о замене бекенда на PHP на что-нибудь другое, кто знает.
Согласен, в Symfony например есть мощный компонент форм, позволяющий избегать дублирования кода на фронте и бекенде, ну и в целом только бекенд писать проще, чем бекенд + фронтенд. Но что делать если нужна интерактивность, например в задаче тестхаб было бы гораздо удобнее, если бы создание/редактирование теста было на одной странице, так же как и прохождение тестов (мне нравится как сделали на Duolingo, Lingualeo - там всё без перезагрузки). C jQuery начинается дикая каша, я пробовал, и цена внедрения функционала растёт с каждым новым изменением. А вот SPA на реакте подошло хорошо и существенно облегчило разработку там, где нам была нужна интерактивность.
но на реакте моднее и реактивнее
зато на жс страницу перезагружать не нужно. чем больше логики переносится на жс, тем меньше ее остается для пхп. скоро пхп будут использовать только для запросов к базе.
Так у них реакт на сервере, как я понял. То есть там сервис на ноде рендерит страницы через реакт, а данные запрашивает с PHP-бекенда. Может в расчете перейти на какой-нибудь Го или Ноду и на бекенде.
Так-то я не спорю, что в SPA это пригодится.
В задаче на тестхаб, по моим ощущениям, хватило бы и jQuer, но если хочется, можно и реакт/ангулар использовать. Для прохождения теста, не думаю, что что-то сложное нужно, там же всего лишь выбрать опцию или ввести текст вопроса.
У них JS на сервере, как я понял. Если в браузере - еще хуже, надо загрузить мегабайт, а то и больше яваскрипта, а потом делать кучу запросов к АПИ, это все не быстро и столько времени теряет, что никакой перезагрузкой не окупишь.
Реакт - для интерактивных приложений скорее.
аяксом эти запросы к апи быстро делаются. а пока данные не получены можно поставить заставку. в итоге получается удобный и красивый фронт. да и проще один раз запрос к апи сделать, получить все данные и обрабатывать их на клиенте, чем перегружать страницу и делать запросы к базе каждый раз.
Ты можешь написать сколь угодно крутой фронтенд, но загрузить HTML с данными всегда быстрее чем загрузить HTML + мегабайт яваскрипта + сделать запрос к API. А если ты используешь REST, то тебе скорее придется делать несколько запросов.
А если произойдет хоть одна ошибка в JS коде, будет просто белая страница. В отличие от классического HTML. Плюс, такой сайт будет наверно плохо индексироваться гуглом, так как ему не понравится, что надо запускать полноценный браузер и ждать пока там все загрузится. Статический HTML парсить быстрее.
Зачем мне смотреть на заставку если я могу смотреть сразу на нужные мне данные? Да, это скорее подходит к эпохе из 2000, но почему меня это должно беспокоить.
Если у тебя тяжелый сайт с кучей рекламы и тебя беспокоит перезагрузка страницы, то можно использовать pjax. Он загружает классическую HTML страницу аяксом и обновляет DOM. Не нужен реакт, не нужно делать REST API. Не нужна перезагрузка страницы. не нужно ничего дорабатывать на сервере. Как тебе?
Допустим есть объект внутри которого лежит другой объект - https://ideone.com/QgS6gL
Но что делать если у меня там массив департмаментов, и в каждом департаменте еще и массив рабочих. Просто массив непоклонируешь особо, там ошибка.
Массив не объект и "клонируется" при копировании:
$a = [];
$b = $a; // независимая копия массива
При этом содержимое массива - объекты - не клонируются. Это надо сделать руками. Ты должен создать новый массив, заполнить его клонами, поместить в поле объекта.
Для понимая, представь что в переменной хранится не объект, а его порядковый номер:
$a = new X; // создаем объект #1 и кладем в $a
$b = $a; // копируем номер #1 в $b
Потому для объектов и нужно клонирование, которое создает новый объект, а по умолчанию ты просто копируешь номер.
То же самое при копировании массива с объектами:
$a = [new X, new X, new X];
// можно представить как $a = [#1, #2, #3];
$b = $a; // создается копия массива, но в ней лежат указатели на те же объекты #1, #2, #3
> как работает __clone
PHP создает неглубокую копию объекта и вызывает на копии метод __clone, который может что-то сделать. В примере кода с $inside:
$clone = clone $base;
Это создает "неглубокий" клон $base, когда поля (включая inside) не клонируются, а копируются обычным образом. И в $clone->inside лежит указатель на тот же объект что и в $base. затем PHP вызывает магический метод __clone, который берет объект из $inside, создает его клон и помещает указатель на него назад в этом поле.
Массив не объект и "клонируется" при копировании:
$a = [];
$b = $a; // независимая копия массива
При этом содержимое массива - объекты - не клонируются. Это надо сделать руками. Ты должен создать новый массив, заполнить его клонами, поместить в поле объекта.
Для понимая, представь что в переменной хранится не объект, а его порядковый номер:
$a = new X; // создаем объект #1 и кладем в $a
$b = $a; // копируем номер #1 в $b
Потому для объектов и нужно клонирование, которое создает новый объект, а по умолчанию ты просто копируешь номер.
То же самое при копировании массива с объектами:
$a = [new X, new X, new X];
// можно представить как $a = [#1, #2, #3];
$b = $a; // создается копия массива, но в ней лежат указатели на те же объекты #1, #2, #3
> как работает __clone
PHP создает неглубокую копию объекта и вызывает на копии метод __clone, который может что-то сделать. В примере кода с $inside:
$clone = clone $base;
Это создает "неглубокий" клон $base, когда поля (включая inside) не клонируются, а копируются обычным образом. И в $clone->inside лежит указатель на тот же объект что и в $base. затем PHP вызывает магический метод __clone, который берет объект из $inside, создает его клон и помещает указатель на него назад в этом поле.
Расскажи нам, что будет при ошибке в JS коде в SPA? По моему так и останется страница с прелоадером. Или например если код написан с использованием ES6, а браузер его не поддерживает - что будет?
Осилил уже с помощью стаковерфлоу вот так вот сделать.
Еще был вариант там с помощью array_map это делать, но чет нахрапом не смог осилить принцип и быстро еще к себе адаптировать.
Рассказал бы стори хоть, что на пхп делал и сколько платили, как докатился до фронтэнда и что теперь там делаешь и что там платят собственно.
Так не нужен, что ты не поленился найти наш тред и сказать об этом? Ничего, проходи, устраивайся поудобнее.
>После нее можно изучать автоматизированное тестирование https://gist.github.com/codedokode/a455bde7d0748c0a351a
да просто маньки пхпшники не нужны уже на фоне такого обилия всяких цмс. манек пхпшников это настолько рассатривает, что они даже делают бложики из велосиедов на всяких уии и зендах вместо того, чтобы взять готовую цмс.
А вот js на острие хайповых технологий щас, все больше логики переносится на жс, приложение начинают различаться лишь интерфейсом тк в плане функциональности все давно изобретено и переизобретено под типовые нужды.
собсно меня всегда и брали пилить фронтенд там, где на бэке пхп (вордпресс или уии), потому что я в довесок знаю похапе.
и вот я пришел сказать, как человек, который в рановй степени учил и пхп и жс, что жс гораздо более востребован и оплачивается.
Такой тупой вопросец. Зачем использовать {скобки} при вписывании переменной в строку вида --- echo "Привет аноны я, {$username}";
Куда устроился в итоге и сколько платят? И что за город?
Ну я изчую пхп. Но я не нацелен на гавно цмс, разве когда я изучу симфони, мне с такими знаниями придется клепать хрень на цмс? Я думал там, интереснее есть задачи для бэкэнда.
Самый простой вариант - пройтись руками и сделать копию HTML файла, но с добавленными пунктами меню, длинными названиями, длинными словами, гигансткими картинками, крошечными картинками. Можно и автоматизировать процесс, сделав скрипт, который например удлиняет все тексты на странице.
Также можно проверить, что если скрыть какой-то блок (сайдбар например), страница не разваливается.
Не забудь тестировать страницу на разной ширине окна браузера, чтобы проверить адаптивность.
Также, ты можешь использовать вставку php-кода в HTML, чтобы скрипт генерировал контент по заданным параметрам.
У меня был скрипт, чтобы в верстке добавлять пункты меню и удлинять тексты, но я не помню, где он.
нахуй не нужны фреймворки, все можно сделать на цмс в пхп.
а на фреймворках любят костылять байтоебы-велосипедчики, в итоге костыляют ту же цмс только долго и плохо
опен сервер поставь и забей на сервер и разбирание в том, что такое апач, хуяч.
Будешь кодить на похапе, постепенно будут возникать вопросы о том, что такое хттп и как сервер работает, тогда появиться смысл разбираться в этом. В ином случае будет все пролетать мимо кассы.
Мимо по своему опыту
внезапно были годные уроки от webformyself
там вся кухня блядь была, давно смотрел, но помню ахуел от того как дохуя можно рассказать по теме
тож устроился джуном на пхп. С сайтами вообще почти не работаю.
Пишу ботов, хуйотов, парсеры, спамеры лол и много всякой подобной хуйни, работа с сайтом часто заключается в том, чтобы я просто набросал какой нибудь интерфейс чтобы кликать можно было и управлять настройками бота
Тебе без ЧПУ?
Хуле там учить тогда? Принимаешь ?page=hyi, на странице пишешь include $_GET['page'].'.php'; сверху/снизу шапка/футер. Обмазывание свистоперделками - дело индивидуальное
подрбней?
Откуда это распространённое мнение, что работа на симфони как-то сильно отличается от остальной работы на PHP? Плохой код пишут везде, я по работе сталкиваюсь с проектами на Laravel/Symfony/Yii и скажу, что всё зависит не от фреймворка, а от разработчиков.
>>181019
Да. https://learn.javascript.ru/ajax
>>181030
> мегабайт яваскрипта
Есть tree shaking, мы будем заморачиваться с этим: https://medium.com/netscape/webpack-3-react-production-build-tips-d20507dba99a
> + сделать запрос к API.
Ну нет же, при первой загрузке страницы данные из бекенда передаются куда-нибудь в window.INITIAL_STATE и уже оттуда подхватываются фреймворком, без дополнительного запроса. Про "несколько запросов" тоже не понял, ничего мне не мешает передавать сразу дерево объектов на фронт.
> Плюс, такой сайт будет наверно плохо индексироваться гуглом,
Ну не всегда нужна индексация, может быть продукт закрытый для определённого круга лиц. И уже 2018-й на дворе, тут пишут, что гугл индексирует SPA: https://medium.com/@l.mugnaini/spa-and-seo-is-googlebot-able-to-render-a-single-page-application-1f74e706ab11
> Не нужен реакт, не нужно делать REST API
А потом бизнес скажет, что нужно API для мобилок (было 2 раза). И дешевле было сразу делать API. И на страницах всё больше логики и jQuery лапша становится всё запутаннее, а пилить свой очередной MVC велосипед не хочется, нужно чтобы новые разработчики быстро вникали в проект.
Проверьте решение - кредит на айпад.
https://ideone.com/HW6Jad
И еще: объясните что происходит с переменной $payed после того, как я ее "вернул" максимально простым языком, пожалуйста.
Есть ли в этом профиты?
Под чем работаете вы?
В принципе пердолинг меня увлекает и я не против, если есть плюсы
> объясните что происходит с переменной $payed после того, как я ее "вернул" максимально простым языком, пожалуйста.
Когда ты выходишь из функции, ты можешь "вернуть" какое-то значение (не переменную! локальные переменные уничтожаются после выхода из функции) с помощью return, например:
return 100;
return "Yes";
return [1, 2, 3];
$x = 1;
return $x;
Важно помнить, что ты возвращаешь не переменную, а берешь ее значение и возвращаешь его. Переменные не возвращаются и не передаются - передаются только значения - числа, строки, массивы. Переменная - это просто хранилище для значения. Надо различать эти 2 понятия.
Значение, которое было указано в return, будет возвращено в место вызова функции. То есть, если ты пишешь
echo x();
И функция x() возвращает 10, то получится echo 10 и это число выведется на экран. Или, если ты напишешь
$y = x();
То результат, который вернет x(), скопируется в переменную $y.
Можно использовать функции и в более сложных выражениях, например:
$a = x() + y() + z();
Здесь происходит вызов 3 функций, их результаты складываются и сохраняются в переменную $a. Или так:
echo x(y());
Здесь вызывается функция y(), ее результат запоминается, затем вызывается функция x(), при этом ей передается результат функции y(), и то, что вернет x(), выводится на экран.
Если в функции нет return, то она возвращает значение по умолчанию - null. Если ты никуда не сохраняешь результат вызова функции, он отбрасывается, например:
w();
Здесь результат вызова функции никак не используется и отбрасывается.
По решению:
Код надо правильно выравнивать. Смотри второй пост треда. Ты можешь либо с самого начала ставить отступы правильно, либо использовать сайт для форматирования, либо писать код в IDE, где есть функция выравнивания кода. В твоем коде отступы нарушены и тяжело понять, где начинается и заканчивается тот же if.
> $credit = 39999;
> $payPerMonth = 5000;
Это можно было сделать аргументами функции, чтобы можно она была универсальнее и можно было делать расчет кредита с любыми параметрами. Это не ошибка, просто так было бы чуть лучше.
if можно было бы заменить на функцию min/max:
выплата = меньшее из (стандартная выплата, остаток кредита);
Расчет программа делает правильно.
> объясните что происходит с переменной $payed после того, как я ее "вернул" максимально простым языком, пожалуйста.
Когда ты выходишь из функции, ты можешь "вернуть" какое-то значение (не переменную! локальные переменные уничтожаются после выхода из функции) с помощью return, например:
return 100;
return "Yes";
return [1, 2, 3];
$x = 1;
return $x;
Важно помнить, что ты возвращаешь не переменную, а берешь ее значение и возвращаешь его. Переменные не возвращаются и не передаются - передаются только значения - числа, строки, массивы. Переменная - это просто хранилище для значения. Надо различать эти 2 понятия.
Значение, которое было указано в return, будет возвращено в место вызова функции. То есть, если ты пишешь
echo x();
И функция x() возвращает 10, то получится echo 10 и это число выведется на экран. Или, если ты напишешь
$y = x();
То результат, который вернет x(), скопируется в переменную $y.
Можно использовать функции и в более сложных выражениях, например:
$a = x() + y() + z();
Здесь происходит вызов 3 функций, их результаты складываются и сохраняются в переменную $a. Или так:
echo x(y());
Здесь вызывается функция y(), ее результат запоминается, затем вызывается функция x(), при этом ей передается результат функции y(), и то, что вернет x(), выводится на экран.
Если в функции нет return, то она возвращает значение по умолчанию - null. Если ты никуда не сохраняешь результат вызова функции, он отбрасывается, например:
w();
Здесь результат вызова функции никак не используется и отбрасывается.
По решению:
Код надо правильно выравнивать. Смотри второй пост треда. Ты можешь либо с самого начала ставить отступы правильно, либо использовать сайт для форматирования, либо писать код в IDE, где есть функция выравнивания кода. В твоем коде отступы нарушены и тяжело понять, где начинается и заканчивается тот же if.
> $credit = 39999;
> $payPerMonth = 5000;
Это можно было сделать аргументами функции, чтобы можно она была универсальнее и можно было делать расчет кредита с любыми параметрами. Это не ошибка, просто так было бы чуть лучше.
if можно было бы заменить на функцию min/max:
выплата = меньшее из (стандартная выплата, остаток кредита);
Расчет программа делает правильно.
> Есть tree shaking, мы будем заморачиваться с этим:
Я пока цифр не видел, так что особо в его эффективность не верю. Но суть и не в этом, зачем загружать мегабайт или полмегабайта яваскрипта ради шаблонизации на клиенте, если можно просто отдать готовый HTML? По моему тут просто хочется не сделать лучше, а любой ценой использовать реакт. Даже ценой переусложнения кода. Ладно, бекенд, его никто не видит, но фронтенд утяжелять-то зачем?
> Ну нет же, при первой загрузке страницы данные из бекенда передаются куда-нибудь в window.INITIAL_STATE и уже оттуда подхватываются фреймворком, без дополнительного запроса. Про "несколько запросов" тоже не понял, ничего мне не мешает передавать сразу дерево объектов на фронт.
Ну так с таким же успехом можно передавать уже отрендеренный HTML. Я напомню, что речь в статье о сайте с вакансиями.
> Про "несколько запросов" тоже не понял,
В REST API обычно данные для вывода каждого блока получаются отдельным запросом. Информация о вакансии - один запрос, список похожих вакансий - второй, топ компаний - третий, рекомендуемые ссылки - четвертый и так далее. То, что ты ничего не понял, говорит скорее всего о том, что ты плохо разбираешься в этой теме.
> Ну не всегда нужна индексация, может быть продукт закрытый для определённого круга лиц
Сайт с вакансиями.
> гугл индексирует SPA
Это требует больше процессорного времени в сравнении со статическим сайтом. Бюджет времени на 1 сайт может быть ограничен. Значит, Гугл будет индексировать меньше страниц или делать это медленее. Плюс, увеличивается вероятность каких-то ошибок. Из-за ошибки JS загрузится белая страница - ухудшение рейтинга. Напомню, что на сервере можно отдать код 5xx и робот повторит попытку позже.
Роботам не удобны SPA и нормально они их индексировать не будут в обозримое время. Робот не додумается нажимать кнопки, двигать слайдеры и тд. SPA, как я понимаю, индексируются в основном за счет сайтмапов, то есть списка ссылок на все страницы сайта.
Да это и не надо. Зачем индексировать текстовый редактор? SPA это приложения, а индексируются для поиска сайты с текстами.
> А потом бизнес скажет, что нужно API для мобилок (было 2 раза).
И для мобильного приложения нужен ровно такой де API, как для сайта (нет). В любом случае, добавить только REST API гораздо быстрее чем заморачиваться со всем, что ты описал.
> а пилить свой очередной MVC велосипед не хочется, нужно чтобы новые разработчики быстро вникали в проект.
А он и не нужен для показа вакансий.
> Есть tree shaking, мы будем заморачиваться с этим:
Я пока цифр не видел, так что особо в его эффективность не верю. Но суть и не в этом, зачем загружать мегабайт или полмегабайта яваскрипта ради шаблонизации на клиенте, если можно просто отдать готовый HTML? По моему тут просто хочется не сделать лучше, а любой ценой использовать реакт. Даже ценой переусложнения кода. Ладно, бекенд, его никто не видит, но фронтенд утяжелять-то зачем?
> Ну нет же, при первой загрузке страницы данные из бекенда передаются куда-нибудь в window.INITIAL_STATE и уже оттуда подхватываются фреймворком, без дополнительного запроса. Про "несколько запросов" тоже не понял, ничего мне не мешает передавать сразу дерево объектов на фронт.
Ну так с таким же успехом можно передавать уже отрендеренный HTML. Я напомню, что речь в статье о сайте с вакансиями.
> Про "несколько запросов" тоже не понял,
В REST API обычно данные для вывода каждого блока получаются отдельным запросом. Информация о вакансии - один запрос, список похожих вакансий - второй, топ компаний - третий, рекомендуемые ссылки - четвертый и так далее. То, что ты ничего не понял, говорит скорее всего о том, что ты плохо разбираешься в этой теме.
> Ну не всегда нужна индексация, может быть продукт закрытый для определённого круга лиц
Сайт с вакансиями.
> гугл индексирует SPA
Это требует больше процессорного времени в сравнении со статическим сайтом. Бюджет времени на 1 сайт может быть ограничен. Значит, Гугл будет индексировать меньше страниц или делать это медленее. Плюс, увеличивается вероятность каких-то ошибок. Из-за ошибки JS загрузится белая страница - ухудшение рейтинга. Напомню, что на сервере можно отдать код 5xx и робот повторит попытку позже.
Роботам не удобны SPA и нормально они их индексировать не будут в обозримое время. Робот не додумается нажимать кнопки, двигать слайдеры и тд. SPA, как я понимаю, индексируются в основном за счет сайтмапов, то есть списка ссылок на все страницы сайта.
Да это и не надо. Зачем индексировать текстовый редактор? SPA это приложения, а индексируются для поиска сайты с текстами.
> А потом бизнес скажет, что нужно API для мобилок (было 2 раза).
И для мобильного приложения нужен ровно такой де API, как для сайта (нет). В любом случае, добавить только REST API гораздо быстрее чем заморачиваться со всем, что ты описал.
> а пилить свой очередной MVC велосипед не хочется, нужно чтобы новые разработчики быстро вникали в проект.
А он и не нужен для показа вакансий.
> include $_GET['page'].'.php';
Не делай так. Это уязвимость, позволяющая запустить любой php-файл на сервере. Значение из page надо проверять по белому списку разрешенных значений.
>>180081
Читаем https://ru.wikipedia.org/wiki/Семафор_(информатика)
Затем гуглим http://php.net/manual/ru/book.sem.php и пишем код на нем. В задаче не сказано, что нельзя его использовать.
Допустим, это не прокатило. Тогда нам нужны 2 вещи:
- где-то нам нужно хранить счетчик, доступный из всех потоков (или скорее процессов). Если речь именно о потоках, то хватит переменной, если о процессах, то нужен файл или примитив ОС (разделяемая память итд)
- для реализации enter()/leave() нам нужен примитив для атомарного изменения счетчика, а также для ожидания, пока счетчик не станет более 0
Так как в задаче про это не написано, просто представим, что такие функции уже есть (atomic_inc(), atomic_dec(), wait()). Если и это не прокатывает, то есть 2 варианта:
- если речь о потоках, то храним счетчик в переменной, а wait() делаем на основе sleep(). Минус - я не уверен, что увеличение переменной атомарно даже через $x++ - есть шанс, что эта операция реализована не атомарно (и кстати, ты понимаешь, что значит "атомарно"). Это сомнительный вариант
- иначе, реализуем их на основе файла, в котором мы храним число и который блокируем на время операции увеличения/уменьшения, чтобы реализовать атомарность:
- http://php.net/manual/en/function.flock.php
Напишешь, или нужна еще помощь? Если напишешь, давай код, я придумаю, как можно протестировать, что он реально работает. Ну или придумай сам.
>>181357
Примитив (в данном случае набор функций) для синхронизации (взаимодействия между) потоков (потоки - это когда несколько кусков кода выполняется параллельно). То есть несколько кусков кода выполняется параллельно и с помощью семафора мы можем заставить один кусок кода ждать, пока другой кусок кода освободит какой-то ресурс, а не набрасываться на него всей толпой.
Почитай вики https://ru.wikipedia.org/wiki/Семафор_(информатика)
>>181356
Не надо так. Лучше уж встроенный в PHP сервер.
> include $_GET['page'].'.php';
Не делай так. Это уязвимость, позволяющая запустить любой php-файл на сервере. Значение из page надо проверять по белому списку разрешенных значений.
>>180081
Читаем https://ru.wikipedia.org/wiki/Семафор_(информатика)
Затем гуглим http://php.net/manual/ru/book.sem.php и пишем код на нем. В задаче не сказано, что нельзя его использовать.
Допустим, это не прокатило. Тогда нам нужны 2 вещи:
- где-то нам нужно хранить счетчик, доступный из всех потоков (или скорее процессов). Если речь именно о потоках, то хватит переменной, если о процессах, то нужен файл или примитив ОС (разделяемая память итд)
- для реализации enter()/leave() нам нужен примитив для атомарного изменения счетчика, а также для ожидания, пока счетчик не станет более 0
Так как в задаче про это не написано, просто представим, что такие функции уже есть (atomic_inc(), atomic_dec(), wait()). Если и это не прокатывает, то есть 2 варианта:
- если речь о потоках, то храним счетчик в переменной, а wait() делаем на основе sleep(). Минус - я не уверен, что увеличение переменной атомарно даже через $x++ - есть шанс, что эта операция реализована не атомарно (и кстати, ты понимаешь, что значит "атомарно"). Это сомнительный вариант
- иначе, реализуем их на основе файла, в котором мы храним число и который блокируем на время операции увеличения/уменьшения, чтобы реализовать атомарность:
- http://php.net/manual/en/function.flock.php
Напишешь, или нужна еще помощь? Если напишешь, давай код, я придумаю, как можно протестировать, что он реально работает. Ну или придумай сам.
>>181357
Примитив (в данном случае набор функций) для синхронизации (взаимодействия между) потоков (потоки - это когда несколько кусков кода выполняется параллельно). То есть несколько кусков кода выполняется параллельно и с помощью семафора мы можем заставить один кусок кода ждать, пока другой кусок кода освободит какой-то ресурс, а не набрасываться на него всей толпой.
Почитай вики https://ru.wikipedia.org/wiki/Семафор_(информатика)
>>181356
Не надо так. Лучше уж встроенный в PHP сервер.
>>181338
Это не так. CMS заточены на создание сайта через админку, из стандартных модулей. Там обычно можно добавлять свои шаблоны и модули. Но если функционал сайта плохо ложится на CMS, проще писать его с нуля, чем пытаться сделать такой супермодуль для CMS.
Твои рассуждения напоминают рассуждения человека, который что-то краем уха слышал, но ничего не понимает в предмете.
>>181169
Сайты вакансий подтверждают твою мысль?
>>180921
Писать кастомные модули, дорабатывать их, писать шаблоны ты не сможешь. Только использовать стандартные.
>>180670
> echo $a = implode(" ", $word[array_rand($word)]);
Здесь не очень понятно, зачем использовать $a = ... если можно писать сразу echo implode(...).
implode склеивает значения из массива в строку. Но $word[array_rand($word)] - это не массив, а строка (одно слово), и склеивать в ней ничего не требуется. implode лишний.
>>180685
тут все верно
>>180564
>>178891
Давай начнем с нормализованного подхода, а потом прикинем, что с нормализацией. Не очень понятно, что имеется в виду по "событиями"? Один из стандартных текстов, который рассылается массе людей? Или произвольный, индивидуальный для каждого текст (Вася поделился 5 фото и получил 3 лайка)?
Допустим, произвольный текст. Тогда получается как-то так:
userId | eventType | time | isRead | text
Тут конечно все выходит немного печально, текст может быть большой, и таблица потому тяжелая. Допустим, текст весит 200 байт (100 русских букв), инты весят 8 байт в x64, получается примерно 230 байт на строку, и всего 20000 польз. x 1000 событий x 230 = 4.6Gb - это только данные. Индексы, впрочем, будут меньше из-за того, что в них не включен текст. MySQL конечно может работать с такой таблицей, но тяжеловато и надо очень тщательно писать запросы. И она ведь расти будет. В такой ситуации можно посоветовать выделить на сервер MySQL заведомо больше памяти, чем весит эта таблица.
Как вариант оптимизации в таком тяжелом случае - разделить таблицу на основную (последняя неделя/месяц) и архивную. А может даже партицировать по месяцам (новую таблицу на каждый месяц, если события происходят постоянно и их так много).
Теперь рассмотрим более простые варианты. Допустим, "событие" одинаково для всех пользователей. Ну например, опубликована новая интересная статья на сайте. Это намного проще. Делаем так:
events: id | time | text
notifications: userId | eventId | isRead
Здесь все не так печально. В таблице notifications строчка весит 8 + 8 + 1 = 17 байт, 17 x 20M = 340M, терпимо. Минус - time у нас в events, и нельзя выбрать записи из notifications отсортированными. Потому придется перенести time туда для оптимизации (индекс по (userId, time) для выборки N последних событий).
Также, если нам не нужны очень старые события, можно по крону их удалять. Большинство пользователей ведь наверно их читать не будет. Или переносить в архивную таблицу.
Наконец, предположим, что нам не нужны прочтенные уведомления. Тогда еще проще - вместо поля isRead можно просто удалять записи. Хотя, тут надо мерять, может выгоднее удалять их отложенно, по крону.
Если события рассылаются всем, то можно еще рассмотреть вариант хранить не список непрочтенных нотификаций, а наоборот, прочтенных.
Наконец, поговорим про "ленты". На некоторых сайтах ты можешь "подписываться" на сущности (например, группы) и получать персонализированные новости из них.
Реализация такой системы "в лоб" непроста. Ну допустим, мы сделаем так:
subscriptions: userId | groupId - кто на что подписан
И при выводе ленты делаем примерно так:
SELECT FROM posts p JOIN subscriptions s ON ... WHERE s.userId = ? ORDER BY p.time DESC;
Это конечно ни в какие индексы не ляжет и уронит твою БД. Потому, можно попробовать другой вариант:
subscriptions: userId | groupId
news: userId | postId | time | isRead - time для оптимизации
При добавлении нового поста в группу ищем, кто на нее подписан, и кидаем им в news ссылку на пост. Если подписчиков много, вставка поста начнет тормозить, и придется делать ее отложенно через крон или gearman. Можно также использовать комбинированный подход - смотреть, сколько подписичков, если мало, вставлять напрямую, если много - отложенно.
Можно еще чуть оптимизировать таблицу news, если убрать поле isRead и для пользователя просто хранить дату последнего открытия ленты новостей.
Тут небольшой минус - в постоянно растущей таблице news. В ней 3 поля по 8 байт + isRead, и на большом числе пользователей и новостей она опять же будет расти. Значит, надо либо ограничить длину ленты, либо срок хранения, либо архивировать старые записи в ней.
Ну и еще небольшая оптимизация: если у пользователя мало подписок (3-5), то таким пользователям можно не использовать таблицу news, а запрашивать новые посты несколькими SELECT. Хотя это может усложнить код.
В любом случае, я советую:
- посмотреть все ограничения, как можно использовать их для оптимизации
- прикинуть объем данных заранее
- сделать прототип, вбить туда заведомо больше данных и протестировать все типичные запросы (вставка, просмотр, получить число непрочтенных уведомлений итд)
> То есть, чтобы узнать количество непрочитанных уведомлений мне нужно сперва узнать количество всех событий, затем узнать какое количество событий прочитано пользователем, после чего найти их разницу.
Число прочтенных можно денормализовать и хранить для каждого пользователя. В твоем подходе только недостаток в том, что таблица прочтенных будет неограниченно расти со временем, если только не удалять старые события.
> Не будет ли тормозить запрос на выборку из третьей таблицы, учитывая, что он будет происходить при каждой загрузке страницы?
Если ему надо обойти 5 строк по индексу - нет. Если сотни - то на большой нагрузке. Если десятки тысяч - то всегда.
> Ситуация осложняется тем, что у событий есть права доступа (по нескольким параметрам: регион/представительство/отдел), поэтому запрос на выборку событий будет ещё медленнее.
Это плохо. Для быстрой выборки запрос должен использовать индекс.
> Добавлять во второй столбец значение при прочтении уведомлений.
Это называется денормализация, но тут есть подвох. У тебя уведомления зависят от региона - значит при смене региона ты должен как-то пересчитать эту цифру.
>>180564
>>178891
Давай начнем с нормализованного подхода, а потом прикинем, что с нормализацией. Не очень понятно, что имеется в виду по "событиями"? Один из стандартных текстов, который рассылается массе людей? Или произвольный, индивидуальный для каждого текст (Вася поделился 5 фото и получил 3 лайка)?
Допустим, произвольный текст. Тогда получается как-то так:
userId | eventType | time | isRead | text
Тут конечно все выходит немного печально, текст может быть большой, и таблица потому тяжелая. Допустим, текст весит 200 байт (100 русских букв), инты весят 8 байт в x64, получается примерно 230 байт на строку, и всего 20000 польз. x 1000 событий x 230 = 4.6Gb - это только данные. Индексы, впрочем, будут меньше из-за того, что в них не включен текст. MySQL конечно может работать с такой таблицей, но тяжеловато и надо очень тщательно писать запросы. И она ведь расти будет. В такой ситуации можно посоветовать выделить на сервер MySQL заведомо больше памяти, чем весит эта таблица.
Как вариант оптимизации в таком тяжелом случае - разделить таблицу на основную (последняя неделя/месяц) и архивную. А может даже партицировать по месяцам (новую таблицу на каждый месяц, если события происходят постоянно и их так много).
Теперь рассмотрим более простые варианты. Допустим, "событие" одинаково для всех пользователей. Ну например, опубликована новая интересная статья на сайте. Это намного проще. Делаем так:
events: id | time | text
notifications: userId | eventId | isRead
Здесь все не так печально. В таблице notifications строчка весит 8 + 8 + 1 = 17 байт, 17 x 20M = 340M, терпимо. Минус - time у нас в events, и нельзя выбрать записи из notifications отсортированными. Потому придется перенести time туда для оптимизации (индекс по (userId, time) для выборки N последних событий).
Также, если нам не нужны очень старые события, можно по крону их удалять. Большинство пользователей ведь наверно их читать не будет. Или переносить в архивную таблицу.
Наконец, предположим, что нам не нужны прочтенные уведомления. Тогда еще проще - вместо поля isRead можно просто удалять записи. Хотя, тут надо мерять, может выгоднее удалять их отложенно, по крону.
Если события рассылаются всем, то можно еще рассмотреть вариант хранить не список непрочтенных нотификаций, а наоборот, прочтенных.
Наконец, поговорим про "ленты". На некоторых сайтах ты можешь "подписываться" на сущности (например, группы) и получать персонализированные новости из них.
Реализация такой системы "в лоб" непроста. Ну допустим, мы сделаем так:
subscriptions: userId | groupId - кто на что подписан
И при выводе ленты делаем примерно так:
SELECT FROM posts p JOIN subscriptions s ON ... WHERE s.userId = ? ORDER BY p.time DESC;
Это конечно ни в какие индексы не ляжет и уронит твою БД. Потому, можно попробовать другой вариант:
subscriptions: userId | groupId
news: userId | postId | time | isRead - time для оптимизации
При добавлении нового поста в группу ищем, кто на нее подписан, и кидаем им в news ссылку на пост. Если подписчиков много, вставка поста начнет тормозить, и придется делать ее отложенно через крон или gearman. Можно также использовать комбинированный подход - смотреть, сколько подписичков, если мало, вставлять напрямую, если много - отложенно.
Можно еще чуть оптимизировать таблицу news, если убрать поле isRead и для пользователя просто хранить дату последнего открытия ленты новостей.
Тут небольшой минус - в постоянно растущей таблице news. В ней 3 поля по 8 байт + isRead, и на большом числе пользователей и новостей она опять же будет расти. Значит, надо либо ограничить длину ленты, либо срок хранения, либо архивировать старые записи в ней.
Ну и еще небольшая оптимизация: если у пользователя мало подписок (3-5), то таким пользователям можно не использовать таблицу news, а запрашивать новые посты несколькими SELECT. Хотя это может усложнить код.
В любом случае, я советую:
- посмотреть все ограничения, как можно использовать их для оптимизации
- прикинуть объем данных заранее
- сделать прототип, вбить туда заведомо больше данных и протестировать все типичные запросы (вставка, просмотр, получить число непрочтенных уведомлений итд)
> То есть, чтобы узнать количество непрочитанных уведомлений мне нужно сперва узнать количество всех событий, затем узнать какое количество событий прочитано пользователем, после чего найти их разницу.
Число прочтенных можно денормализовать и хранить для каждого пользователя. В твоем подходе только недостаток в том, что таблица прочтенных будет неограниченно расти со временем, если только не удалять старые события.
> Не будет ли тормозить запрос на выборку из третьей таблицы, учитывая, что он будет происходить при каждой загрузке страницы?
Если ему надо обойти 5 строк по индексу - нет. Если сотни - то на большой нагрузке. Если десятки тысяч - то всегда.
> Ситуация осложняется тем, что у событий есть права доступа (по нескольким параметрам: регион/представительство/отдел), поэтому запрос на выборку событий будет ещё медленнее.
Это плохо. Для быстрой выборки запрос должен использовать индекс.
> Добавлять во второй столбец значение при прочтении уведомлений.
Это называется денормализация, но тут есть подвох. У тебя уведомления зависят от региона - значит при смене региона ты должен как-то пересчитать эту цифру.
Спасибо, я как раз разбираюсь с фейкером, буду пробовать как лучше в моем случае.
Ещё придумал хранить где-то в сессии количество уведомлений и изначально в счетчике непрочитанных вверху отображать данные из сессии. А уже потом, когда страничка обновится - отправлять аякс запрос на обновление счетчика и данных в сессии.
Тут где то пропал мой пост. Напишу что помню. Вообщем я померял для:
user_table
user_id, name
(20000 rows)
message_table
message_id, text(varcahr(128)), add_time
(5e6+ rows)
user_message_map
user_id(index), message_id, is_read, read_time
(5e6+ rows)
1 ядро (i5 6500) 1 Gb
Мускуль держит:
4000-6000 вставок в секунду. Мессага всем пользователям вставляется 3.7 секунды.
1300 выборок в секунду для count() непрочитанных.
Это для сообщений к юзерам 1<->1.
Для такой базы я бы не давал основным таблицам расти дальше 5е6 записей (дальше производительность резво начинает падать) и не больше 100-150 записей на одного пользователя (иначе нужно мутить хитрую пагинацию внутри БД).
А еще вспомнил. Выбор 1000 непрочитанных при 2e7 записей в БД. 0.5-0.6 секунды.
> 4000-6000 вставок в секунду.
Под нагрузкой эта цифра просядет. Плюс, число пользователей будет только расти в дальнейшем. Но если вставку делать из крона по ночам, то ок.
> Для такой базы я бы не давал основным таблицам расти дальше 5е6 записей (дальше производительность резво начинает падать) и
А в посте упоминалось 20К пользователей и 1000 событий. Тесты надо было делать на соотв. объемах.
> Выбор 1000 непрочитанных
Это малополезный тест, так как вряд ли пользователь захочет читать 1000 сообщений сразу.
> user_id(index), message_id, is_read, read_time
Если ты выбираешь с сортировкой по времени, то индекс надо было делать по (user_id, time)
Кстати есть вопрос. Можно ли в пыхе через echo выводить сразу результат деления/умножения/вычитания/etc, а не создавать отдельную переменную, записывать туда результат вычисления и потом только вставлять её в echo ?
>Я так понял нет смысла кидать сюда задания.
Решенные из учебника ОПа? Есть конечно. Просто у нас тут может быть большой пинг на проверки (иногда и 3 недели для больших задач).
Тут со всем помогут. Если ты конечно не притащишь откровенный фриланс или криминал.
>Можно ли в пыхе через echo выводить ...
Да. Там вообще можно отрываться по полной:
echo '$' . ($x = function($a) {return $a;})(1) (10 * 6) . PHP_EOL;
//1000000
Вот задача
Я просто не совсем понял задачи ОПа. Он указал, что Школьник не может заплатить в месяц больше, чем 5000 рублей. Т.е. школьник чисто гипотетически может может отдать в месяц и 4000 рублей? Иными словами ОП, я так понял, не указал конкретное сумму сколько школьник отдает в банк. Я делал задачу так, что школьник отдает в банк каждый месяц 5000 рублей.
результат все равно не совпадает
Ссыль на код: https://ideone.com/47k5fR
Задача: http://phpbooktest.ga/l1/loops.html
Стоит. Сам долго ругался и пытался в итоге остался на кубунте - годнота, удобно и приятно
Ты переусложнил расчет баланса кредита. Как результат у тебя сначала школьник платит 5000, а потом считается новый баланс. Евросеть конечно, себя так обижать не станет и начислит проценты в 00:00, а деньги примет не раньше 9:00.
В последний месяц, когда баланс кредита меньше 5000 у тебя все равно платит всю сумму, это нужно проверять.
Как перекатиться на убунту если на винде у тебя пубг и прочая стимохуйня? Перекатиться на месяцок что бы отработать консольку и прочее, а потом как возьмут на работу снова установить себе дома лпмповый опен сервер?
дуалбут
Как еще полностью выключить логи на сервере?
В файле https://github.com/Lozy/danted/blob/master/install_debian.sh
(или install_centos.sh в зависимости от ОС) находим строчку
> logoutput: /var/log/sockd.log
И меняем ее на
logoutput: /dev/null
После установки проверяем файл /etc/danted/sockd.conf и убеждаемся, что там прописан /dev/null.
Это отключает логи сокс-демона.
Спасибо. Попробую.
О, как хорошо, что я решил еще подождать ответов, да, ты логичнее.
html:
<b>Фотография</b><input type='file' multiple name='image[]'>
php:
$uploaddir = 'resource/photos/';
for ($i=0; $i<count($_FILES['photo']['name']); $i++)
{
$uploadfile = $uploaddir . basename($_FILES['photo']['name'][$i]);
if (move_uploaded_file($_FILES['photo']['tmp_name'][$i], $uploadfile)) {
echo "Файл успешно загружен.\n";
} else {
echo "Ошибка!\n";
}
}
Раньше загружал только один файл и норм было. А так почему-то не работает
Дуалбут ставится поверх винды и норм.
Заодно рабочее и игровое пространство разграничиваешь.
Только ставь не убунту, лучше дебиан, иначе смысла нет, она как шиндовс, даже с консолькой работать толком не нужно
>даже с консолькой работать толком не нужно
Не нужно но можно, да и главное же не пердолинг ради пердоинга, а изучение среды работа с правами и прочее что нужно для работы разрабом на линуксе. Мне же нужно что бы я при устройстве на работу имел навык сесть за бубунту рабочую и не сосать хуй неделю пока разбираюсь с ней.
Обновил код https://github.com/richBlueElephant/phpClub/blob/master/public/media/js/script.js
Я изначально старался чтобы код был максимально простым, но с расширением он начал становиться более запутанным. Мне не хватает опыта. Нужно закончить задачу с чатом.
Ответьте пожалуйста на пару вопросов выше >>180024 >>178355
Спасибо.
Вот код если что https://repl.it/@2chGray/Palindrom
ideone уёбки не отвечают на моё письмо с просьбой подключить mbstring библиотеку
Всё же советую попробовать что-то другое. Далеко не факт что на работе будет убунта.
А зачем тебе вся длина строки?
Давай порассуждаем. Допустим у тебя четное число символов в строке - тут всё просто и ты должен совершить 2 итерации цикла если у тебя 4 символоа, 3 если 6, 4 если 8 и так далее.
Простое деление на 2 работает для этого.
Далее допустим у тебя нечетная строка, средний символ в такой строке равен сам себе, поэтому в строке скажем из 5 символов тебе достаточно лишь сравнить первые два с последними двумя. Для строки в 5 символов нужно лишь 2 итерации, из 7 сиволов - 3 итерации, и так далее.
Значит тебе надо просто высчитывать длину строки, далее из длины строки с помощью целочисленого деления высчитывать то, сколько итераций тебе нужно для проверки на палиандром. Ща загуглил и в php 7 появилась такая штука как intdiv(); - то что нужно для тебя как раз.
Ну и в теле цикла сравнивать первый с последним, первый + 1 с последним -1 и так далее до окончания цикла.
А по поводу письма, ты же им на английском написал? Есть мнение, что сервис всё же забугорный, хоть для нас у них и русская морда.
первый пик*
Подключать бутстрап через cdn плохая практика. Вдруг какой нибудь ркн заблочит cdn или еще какая то неведомая хуйня произойдет. Файлы лучше иметь у себя на сервере.
ОП, у тебя в уроке какая-то хрень.
>Также, при наследовании есть правило: класс-предок не должен ничего знать о своих наследниках. Он не имеет права обращаться к полям или методам, которые появляются только в наследниках и которых нет в нем самом. Это логично: ведь кто-то сделать еще одного наследника и не добавить туда эти поля.
В тексте про объекты.
http://phpbooktest.ga/l1/pasta.html
Сразу вопрос: как можно создать проверку на актуальность сохраненных кук?
>Сразу вопрос: как можно создать проверку на актуальность сохраненных кук?
if (isset($_COOKIE["your cookie name"])) {
...
}
А если куки сохраняются в отдельном файле и не удаляются автоматически по истечению годности?
А не подскажет ли кто-нибудь, где можно почитать описание такой вот не хитрой конструкции в статическом методе (его части, там несколько подобных конструкций следуют одна за другой):
if ( something true or false) { return; }
Этот метод вызывается через Paamayim Nekudotayim и в принципе понятно, что он делает, он возвращает вычисленное выражение от if всё дальше и дальше накапливая вычисления...Но описания такого подхода, что прос то return; и всё, я нигде не нашёл, подскажите, будьте любезны.
Поясни пожалуйста, почему ты думаешь, что это хрень? Это один из принципов ООП.
Ну смотри:
class Parent
{
public function test()
{
echo $this->something; // обращение к полю, которого нет в классе
}
}
class Child extends Parent
{
public $something = "yes";
}
$child = new Child();
$child->test();
Этот код содержит проблему: мы можем написать $p = new Parent(); $p->test(); и это вызовет ошибку (обращение к несуществущему полю). Мы можем защититься от нее, написав перед class Parent слово abstract и тем самым запретив создавать объекты Parent. Но это все равно оставляет возможность для ошибки:
class OtherChild extends Parent {}
$o = new OtherChild();
$o->test(); // обращение к несуществующему полю
Ты можешь сказать, мол, надо быть внимательным и надо прочитать весь существующий код от начала до конца, разобраться в нем, прежде чем что-то наследовать или вызывать. Но это неправильно. У меня нет времени изучать класс, который ты написал, и разбираться, что он от меня ожидает. Твой подход неэффективен и требует от каждого разработчика тратить время зря на изучение чужого кода.
Я хочу, чтобы неправильный код было писать нельзя в принципе. И ООП это позволяет - при соблюдении определенных правил. Одно из которых приведено в уроке, про обращение только к доступным полям и методам. В языках со статической типизацией (Ява, C#) это правило проверяется при компиляции программы и компилятор выдаст ошибку. В PHP, увы, нет.
По этой причине нельзя обращаться к несуществующим в классе полям или методам. Для того, чтобы сказать "это должно быть дописано в наследнике", есть абстрактные методы. С ними проблема полностью решается:
abstract class Parent
{
abstract public function getSomething(); // это должен реализовать наследник
public function test()
{
echo $this->getSomething();
}
}
class Child extends Parent
{
public function getSomething()
{
return "yes";
}
}
$child = new Child();
$child->test();
Этот код не получится использовать как-то неправильно.
ООП тем и хорош, что позволяет писать код, который в принципе нельзя использовать неправильно. Вот тебе еще пример:
1) ООП позволяет запретить присваивать неправильные значения. Допустим, мы делаем класс ЖурналОценок и в нем есть метод поставитьОценку. Очевидно, оценка ставится Ученику за Урок и может быть от 1 до 5. Мы можем написать такой метод:
class Register
{
public function addScore(Lesson $lesson, Student $student, int $score)
{
if ($score < 1 || $score > 5) {
throw new \InvaldiArgumentException("Score must be 1 to 5, $score given");
}
....
}
}
Как видишь, этот метод не позволяет поставить неправильную оценку.
Ты должен стараться писать код так, чтобы его нельзя было неправильно использовать. И ООП дает возможности для этого:
- закрытие внутренних полей и методов от доступа снаружи с помощью private/protected
- закрытие всех полей от доступа снаружи; разрешаем изменение значений только через методы
- проверка передаваемых значений
- указание в конструкторе всех значений, которые нужны для работы класса
- не полагаемся на то, что методы будут вызываться в определенном порядке
Поясни пожалуйста, почему ты думаешь, что это хрень? Это один из принципов ООП.
Ну смотри:
class Parent
{
public function test()
{
echo $this->something; // обращение к полю, которого нет в классе
}
}
class Child extends Parent
{
public $something = "yes";
}
$child = new Child();
$child->test();
Этот код содержит проблему: мы можем написать $p = new Parent(); $p->test(); и это вызовет ошибку (обращение к несуществущему полю). Мы можем защититься от нее, написав перед class Parent слово abstract и тем самым запретив создавать объекты Parent. Но это все равно оставляет возможность для ошибки:
class OtherChild extends Parent {}
$o = new OtherChild();
$o->test(); // обращение к несуществующему полю
Ты можешь сказать, мол, надо быть внимательным и надо прочитать весь существующий код от начала до конца, разобраться в нем, прежде чем что-то наследовать или вызывать. Но это неправильно. У меня нет времени изучать класс, который ты написал, и разбираться, что он от меня ожидает. Твой подход неэффективен и требует от каждого разработчика тратить время зря на изучение чужого кода.
Я хочу, чтобы неправильный код было писать нельзя в принципе. И ООП это позволяет - при соблюдении определенных правил. Одно из которых приведено в уроке, про обращение только к доступным полям и методам. В языках со статической типизацией (Ява, C#) это правило проверяется при компиляции программы и компилятор выдаст ошибку. В PHP, увы, нет.
По этой причине нельзя обращаться к несуществующим в классе полям или методам. Для того, чтобы сказать "это должно быть дописано в наследнике", есть абстрактные методы. С ними проблема полностью решается:
abstract class Parent
{
abstract public function getSomething(); // это должен реализовать наследник
public function test()
{
echo $this->getSomething();
}
}
class Child extends Parent
{
public function getSomething()
{
return "yes";
}
}
$child = new Child();
$child->test();
Этот код не получится использовать как-то неправильно.
ООП тем и хорош, что позволяет писать код, который в принципе нельзя использовать неправильно. Вот тебе еще пример:
1) ООП позволяет запретить присваивать неправильные значения. Допустим, мы делаем класс ЖурналОценок и в нем есть метод поставитьОценку. Очевидно, оценка ставится Ученику за Урок и может быть от 1 до 5. Мы можем написать такой метод:
class Register
{
public function addScore(Lesson $lesson, Student $student, int $score)
{
if ($score < 1 || $score > 5) {
throw new \InvaldiArgumentException("Score must be 1 to 5, $score given");
}
....
}
}
Как видишь, этот метод не позволяет поставить неправильную оценку.
Ты должен стараться писать код так, чтобы его нельзя было неправильно использовать. И ООП дает возможности для этого:
- закрытие внутренних полей и методов от доступа снаружи с помощью private/protected
- закрытие всех полей от доступа снаружи; разрешаем изменение значений только через методы
- проверка передаваемых значений
- указание в конструкторе всех значений, которые нужны для работы класса
- не полагаемся на то, что методы будут вызываться в определенном порядке
return; это выход из функции без возврата значения. В такой ситуации, если попытаться получить значение, которое вернула функция, ты получишь null:
function test() { return; }
$a = test(); // null
В /s/ в виндотреде наверно быстрей помогут. Я уже не вспомню особенности пердолинга с win7, но помоему твой план не взлетит.
Эх, и на том спасибо
я быдлокодер и я хочу возразить в оправдание своего непрофессионализма.
начну с конца картинки. код пишут для людей, а не для машин. ванька закончивший кружок сисадминства в ПТУ не разберётся в этом. код должен быть простым и интуитивно понятным на уровне shell-скриптинга.
касаемо всех случаев на картинке я не увидел что будет если мы вызовем не GET, а хотя бы POST. не говоря уже о HEAD, не говоря уже о PROPFIND, OPTIONS, и других даже невалидных методах, что если вместо HTTP к нам придёт каша, а веб-сервер её пропустит до PHP-интерпретатора!?
я в своё время писал реализации всех популярных серверов и всегда учитываю эти низкоуровневые ньюансы при написании своего, пусть и быдло-, но -кода.
// более гибкий парсер строки чем умеет сам PHP
мы не сделаем,
// это как в статье про шаблоны -- больше чем умеет сам PHP мы не реализуем.
// поэтому каждую страничку чекаем в каждом if, elseif, else 404
if (strpos($_SERVER['REQUEST_URI'], '/home/') === 0) {
// и уже потом проверяем методы
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
case 'POST':
break; // если GET или POST, мы счастливы
case 'HEAD':
exit; // HEAD не требует отдачи контента, выходим
default:
http_response_code(405);
exit; // если неизвестный метод тоже выходим
}
include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'home.php');
}
мы не ограничиваемся возможностями нашего личного парсера маршрута REQUEST_URI,
мы также задаём действия по-умолчанию для популярных методов GET|POST|HEAD, при случае если потребуется реализовать дополнительный метод, мы это так же учтём и добавим уже для какого-то конкретного контроллера.
а не так, что обрабатываем маршрут, передаём контроллеру, а что там было -- GET|POST|HEAD|другая параша -- хер её знает, кому это важно?
я быдлокодер и я хочу возразить в оправдание своего непрофессионализма.
начну с конца картинки. код пишут для людей, а не для машин. ванька закончивший кружок сисадминства в ПТУ не разберётся в этом. код должен быть простым и интуитивно понятным на уровне shell-скриптинга.
касаемо всех случаев на картинке я не увидел что будет если мы вызовем не GET, а хотя бы POST. не говоря уже о HEAD, не говоря уже о PROPFIND, OPTIONS, и других даже невалидных методах, что если вместо HTTP к нам придёт каша, а веб-сервер её пропустит до PHP-интерпретатора!?
я в своё время писал реализации всех популярных серверов и всегда учитываю эти низкоуровневые ньюансы при написании своего, пусть и быдло-, но -кода.
// более гибкий парсер строки чем умеет сам PHP
мы не сделаем,
// это как в статье про шаблоны -- больше чем умеет сам PHP мы не реализуем.
// поэтому каждую страничку чекаем в каждом if, elseif, else 404
if (strpos($_SERVER['REQUEST_URI'], '/home/') === 0) {
// и уже потом проверяем методы
switch ($_SERVER['REQUEST_METHOD']) {
case 'GET':
case 'POST':
break; // если GET или POST, мы счастливы
case 'HEAD':
exit; // HEAD не требует отдачи контента, выходим
default:
http_response_code(405);
exit; // если неизвестный метод тоже выходим
}
include(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'home.php');
}
мы не ограничиваемся возможностями нашего личного парсера маршрута REQUEST_URI,
мы также задаём действия по-умолчанию для популярных методов GET|POST|HEAD, при случае если потребуется реализовать дополнительный метод, мы это так же учтём и добавим уже для какого-то конкретного контроллера.
а не так, что обрабатываем маршрут, передаём контроллеру, а что там было -- GET|POST|HEAD|другая параша -- хер её знает, кому это важно?
https://codeshare.io/jGzQ9
Вот образец метода, каким-то образом все переменные доходят до конца и отправляются, как аргументы функции wp_mail
Ничего тогда не понимаю, что за магия такая.
Хм, или это потому что до return и не доходит дело, просто происходит вызов функции. Совсем чёт худо дело у меня с соображениями на этот счёт.
>И вот допустим я хочу сайт с адресом .by - мне не обязательно садить его у белорусского хостера?
Тут особенности местного законодательства нужно гуглить. По моему для интернет магазинов (хз что у них считается интернет магазином сейчас) домен должен быть в .by и хостер только местный.
Кажется ты настроен на конструктив, поэтому отвечу развернуто - насколько смогу.
Во первых, картинка - шутка, мем такой есть, поэтому воспринимать её буквально не стоит.
Во вторых, человек хотел именно пример как вообще в принципе делается роутинг, насчет этого чуть дальше.
Насчет сложности: в основном это всё паттерны, уже знакомые тем кто берется их реализовывать, "васяна" скорее всего не возьмут на работу где может такое понадобиться, но даже если возьмут - коли он программист, то разберется в удобоваримое время, чай не энштейны их придумывают.
Насчет методов: так как картинка - шутливый псевдокод, то учтены не все нюансы жизни. Популярные же роутеры, однако, имеют в параметрах тип запроса, как пример, объявление роута в klein:
$klein->respond('GET', '/hello-world', function () {
return 'Hello World!';
});
Так-же, пример с Request и далее подразумевают, что тип запроса и соответствующие данные разбираются этим классом самостоятельно и передаются в контроллер уже в "красивом" виде (третий пример подразумевает использование dependency injection container, где это делается автоматически, четвертый - ioc контейнера на рефлексии).
Перейдем к такому примеру, основанному на твоём (не очень честно потому что я не знаю как бы именно ты это написал):
if (strpos($_SERVER['REQUEST_URI'], '/forum/') === 0) {
_ switch ($_SERVER['REQUEST_METHOD']) {
_ _ case 'GET':
_ _ _ break;
_ _ case 'POST':
_ _ _ break;
_ _ case 'HEAD':
_ _ _ exit();
_ _ default:
_ _ _ http_response_code(405);
_ _ _ exit();
_ }
_ include( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'forum.php' );
}
Теперь давай пройдемся по изъянам.
Пропустили запросы GET/POST - хорошо. Но в самом forum.php тебе в итоге придется опять делать ту-же проверку, чтобы выяснить - постит юзер или хочет прочитать страницу. Если же ты проверишь это сразу, то передать в инклюд данные о проверке сможешь только через статический класс или глоаблки, постепенно превращая код в лапшу.
Если тебе понадобится валидация для HEAD (чтобы для несуществующих разделов ответить кодом 404), тебе придется пихать туда эту логику, хотя она УЖЕ будет в контроллере/где-то еще, т.к. там ТОЖЕ нужна эта валидация.
strpos - сматчит все роуты, где в начале есть /forum/. Скорее всего, в вебе будут ссылки только на реальные разделы. Не считая удаленных тредов например. И тогда тебе придется в контроллере опять проверять есть ли такой тред, рендерить другой шаблон - в общем куча работы по валидации URI в контроллере, который по идее должен лишь раскидать данные в модель и отрендерить шаблон.
Что если ты хочешь иметь подраздел, например /forum/admin/ ? С таким подходом твой URI будет отвечать сразу двумя контролами, и тебе придется опять наворачивать лапшу.
Поэтому есть смысл использовать, например, регулярки. Популярные роутеры имеют свой синтаксис вместо регулярок например. Но суть в том что /\/forum\/(.+\/)/ сматчит только то, что ты действительно хочешь. Это конечно не отменяет промежуточный слой валидации, но дополняет его.
Теперь инклюд. Основная проблема в том что общаться с ним ты можешь только через глобалки или статику. И рано или поздно тебе понадобятся промежуточные слои в логике, и тогда... Жизнь лучше, когда не приходится сталкиваться с такой проблемой.
Однако в общем-то я могу представить почему ты считаешь такой подход жизнеспособным, учитывая сказанное тобой про написание серверов, но на практике в пхп это приведет к спагетти.
Теперь о картинке.
Первый и второй пример очень простые и логика в них похожа на твою.
Третий пример и четвертый пример похожи. Подход, похожий на четвертый используется в очень популярном фреймворке Laravel (не буквально, опять же, а на основе того). Почему он вообще существует? Потому что есть такие люди которые любят тестировать код прежде чем пушить в продакшен. Для этого классы делают на основе интерфейсов; При тестировании какого-то класса, ему вместо реальных зависимостей даются мок-имплементации интерфейсов зависимостей; В продакшене IoC контейнер имеет конфигурацию с указанием реализаций интерфейсов.
Разница с третьим примером в том, что у него вместо названий интерфейсов и рефлексии используются явные присвоения реализаций, со строковыми идентификаторами.
Почему третий "хуже" четвертого? Потому что любой скажет тебе что строковые идентификаторы - зло. По сути, почти то-же самое.
Почему они "лучше" первых двух? Потому что в кавычках. С одной стороны, увеличивается гибкость, всё делается автоматически, уменьшается количество рутины. С другой - ломается тайп-хинтинг, и для меня это отменяет все плюсы например...
Полностью я не смогу охватить эту тему, да и зачем, ведь если ты заинтересуешься - найдешь всё сам. Лишь предложу тебе попробовать написать сайт/сервис с использованием роутера, а роутер написать самому.
Кажется ты настроен на конструктив, поэтому отвечу развернуто - насколько смогу.
Во первых, картинка - шутка, мем такой есть, поэтому воспринимать её буквально не стоит.
Во вторых, человек хотел именно пример как вообще в принципе делается роутинг, насчет этого чуть дальше.
Насчет сложности: в основном это всё паттерны, уже знакомые тем кто берется их реализовывать, "васяна" скорее всего не возьмут на работу где может такое понадобиться, но даже если возьмут - коли он программист, то разберется в удобоваримое время, чай не энштейны их придумывают.
Насчет методов: так как картинка - шутливый псевдокод, то учтены не все нюансы жизни. Популярные же роутеры, однако, имеют в параметрах тип запроса, как пример, объявление роута в klein:
$klein->respond('GET', '/hello-world', function () {
return 'Hello World!';
});
Так-же, пример с Request и далее подразумевают, что тип запроса и соответствующие данные разбираются этим классом самостоятельно и передаются в контроллер уже в "красивом" виде (третий пример подразумевает использование dependency injection container, где это делается автоматически, четвертый - ioc контейнера на рефлексии).
Перейдем к такому примеру, основанному на твоём (не очень честно потому что я не знаю как бы именно ты это написал):
if (strpos($_SERVER['REQUEST_URI'], '/forum/') === 0) {
_ switch ($_SERVER['REQUEST_METHOD']) {
_ _ case 'GET':
_ _ _ break;
_ _ case 'POST':
_ _ _ break;
_ _ case 'HEAD':
_ _ _ exit();
_ _ default:
_ _ _ http_response_code(405);
_ _ _ exit();
_ }
_ include( dirname(__FILE__) . DIRECTORY_SEPARATOR . 'forum.php' );
}
Теперь давай пройдемся по изъянам.
Пропустили запросы GET/POST - хорошо. Но в самом forum.php тебе в итоге придется опять делать ту-же проверку, чтобы выяснить - постит юзер или хочет прочитать страницу. Если же ты проверишь это сразу, то передать в инклюд данные о проверке сможешь только через статический класс или глоаблки, постепенно превращая код в лапшу.
Если тебе понадобится валидация для HEAD (чтобы для несуществующих разделов ответить кодом 404), тебе придется пихать туда эту логику, хотя она УЖЕ будет в контроллере/где-то еще, т.к. там ТОЖЕ нужна эта валидация.
strpos - сматчит все роуты, где в начале есть /forum/. Скорее всего, в вебе будут ссылки только на реальные разделы. Не считая удаленных тредов например. И тогда тебе придется в контроллере опять проверять есть ли такой тред, рендерить другой шаблон - в общем куча работы по валидации URI в контроллере, который по идее должен лишь раскидать данные в модель и отрендерить шаблон.
Что если ты хочешь иметь подраздел, например /forum/admin/ ? С таким подходом твой URI будет отвечать сразу двумя контролами, и тебе придется опять наворачивать лапшу.
Поэтому есть смысл использовать, например, регулярки. Популярные роутеры имеют свой синтаксис вместо регулярок например. Но суть в том что /\/forum\/(.+\/)/ сматчит только то, что ты действительно хочешь. Это конечно не отменяет промежуточный слой валидации, но дополняет его.
Теперь инклюд. Основная проблема в том что общаться с ним ты можешь только через глобалки или статику. И рано или поздно тебе понадобятся промежуточные слои в логике, и тогда... Жизнь лучше, когда не приходится сталкиваться с такой проблемой.
Однако в общем-то я могу представить почему ты считаешь такой подход жизнеспособным, учитывая сказанное тобой про написание серверов, но на практике в пхп это приведет к спагетти.
Теперь о картинке.
Первый и второй пример очень простые и логика в них похожа на твою.
Третий пример и четвертый пример похожи. Подход, похожий на четвертый используется в очень популярном фреймворке Laravel (не буквально, опять же, а на основе того). Почему он вообще существует? Потому что есть такие люди которые любят тестировать код прежде чем пушить в продакшен. Для этого классы делают на основе интерфейсов; При тестировании какого-то класса, ему вместо реальных зависимостей даются мок-имплементации интерфейсов зависимостей; В продакшене IoC контейнер имеет конфигурацию с указанием реализаций интерфейсов.
Разница с третьим примером в том, что у него вместо названий интерфейсов и рефлексии используются явные присвоения реализаций, со строковыми идентификаторами.
Почему третий "хуже" четвертого? Потому что любой скажет тебе что строковые идентификаторы - зло. По сути, почти то-же самое.
Почему они "лучше" первых двух? Потому что в кавычках. С одной стороны, увеличивается гибкость, всё делается автоматически, уменьшается количество рутины. С другой - ломается тайп-хинтинг, и для меня это отменяет все плюсы например...
Полностью я не смогу охватить эту тему, да и зачем, ведь если ты заинтересуешься - найдешь всё сам. Лишь предложу тебе попробовать написать сайт/сервис с использованием роутера, а роутер написать самому.
1. Вывод шаблонов в конструкторе и деструкторе убрал, сделал обычные методы.
2.
>А зачем нужно OrgInfo? Нельзя ли в организации сделать методы для вычислений этих чисел? Просто у меня ощущение, что этот класс заточен только под вывод таблицы, и тогда код getOrgInfo() желательно вынести из компании куда-нибудь, хотя бы в ту же OrgInfo. Или вообще ликвидировать этот метод.
Класс OrgInfo - выпилил, перенес вычисления общих и средних показателей по организации в отдельные методы. Теперь поясню почему я так держался за это с самого начала. Изначально очень удобно и просто было высчитывать всё это за 1 цикл, тупо прокручивая все департаменты в нем 1 раз, и складывая всю инфу в 1 выходной объект, поэтому в итоге это и выросло в такую вот шизу как отдельный класс для хранения этой информации на выходе этого метода. Мне и сейчас решение с вынесением всех вычислений в разные методы не кажется хорошим, просто потому что, во первых они почти все повторяют друг друга и у них отличается только переменная на входе, выходе и название соответственно, а во вторых всё это вместо одного компактного цикла использует каждый раз по циклу на метод и опять же перебирает снова и снова департаменты. В общем я сделал как рекомендовано, но остались сомнения с точки зрения оптимизации.
3. Тайп хинты расставил везде где нашел возможным.
4. try/catch выпилил, самому не нравилась идея оборачивать каждый код с exeptionon'ом внутри в эту конструкцию. А если я например чужой класс ипользую - мне нужно идти смотреть получается нет ли там внутри выброса исключений и на методы внутри которых есть рисовать try/catch каждый раз? Звучит тупо, так что только рад что это всё не нужно.
5.
>> public function promoteStuff(array $promotionList) {
>Я не думаю, что это надо делать в департаменте. Тут от департамента ничего не требуется и работников можно повышать напрямую.
Ок, сделал напрямую повышение, зачем лишние действия совершать.
6.
>По моему было бы гораздо удачнее сделать так:
>if ($employeeSelector->matches($employee)) ...
>Это позволило бы поместить логику отбора в класс-фильтра, где ей и место. Ну и еще есть такой вариант с коллбеком:
Перенес из департамента метод фильтрации сотрудников в сам класс EmployeeSelector, сделал там метод match который принимает на вход сотрудника и выдает булево значение - подходит сотрудник под текущие параметры или нет.
> new EmployeeSelector('Engineer', [1, 2, 3], [true, false]);
>лучше сделать возможность указать "не важно" для ранга. null, например, передать.
При этом сам способ фильтрации почти не изменился, разве что добавил возможность передавать в конструктор null как вариант "не важно"
7.
> return round($pageCost, 3);
>Не надо делать тут round, так как это нужно только для вывода в таблицу и должно быть там, где делается вывод, а не тут. Тут должен быть метод, который возвращает точное значение.
Ок, округление теперь во вьюхах
8.
> $vectorSecondAC = clone $vectorVanilla;
>В клонировании у тебя допущена ошибка. Этот метод не создает глубокую копию компании. он делает клон компании, но в него помещает ссылки на те же самые департаменты с теми же самыми работниками - они по умолчанию не клонируются, а просто копируются ссылки. Изучи магический метод __clone().
Переделал, теперь при клонировании организации должны клонироваться еще и объекты департаменты, а не просто ссылки на них, и при клонировании департамента соответственно объекты сотрудников
9.
> $this->organisation->setTitle("после антикризисных мер #1");
>Это неправильно, ради решения задачи про антикризисные меры добавлять в Компанию поле-комментарий. Это не нужно компании, это нужно только в антикризисных мерах, и это надо делать где-то в другом месте.
А мне это казалось клевым - мы изменили объект - мы отметил куда-то о том что он теперь другой и не нужно ниоткуда выцеплять это из других классов, комментарий идет сразу с объектом.
У нас ведь класс компании и служит не для каких-то там бизнесс-процессов компании, а для по сути отчетов, вот и это туда же.
Убрал, но как в другом месте это выводить или впиливать - не понял.
10.
>Сортировку лучше делать в одной функции, так:
>Также, есть еще такой трюк, вычисляем "вес" и сравниваем его:
Выбрал вариант с вычислением веса, так как он мне понятнее.
11.
>По view: в идеале, надо использовать htmlspecialchars при вставке текста в HTML. Иначе может быть уязвимость XS
Ок, обернул, только ведь это имеет смысл если мы выводим информацию, на которую как-то могут влиять сами юзеры, а так то кто в мою вьюху что подсунет. И вообще сделал обертку которая тупо сокращает длину названия функции, что бы было компактнее и удобнее её исользовать в перемешку с html
С простыми правками надеюсь что пока что всё, а не будет еще больше замечаний над которыми я просижу опять неделю.
Теперь опять к самому главному.
12.
>> abstract protected function setDefaults();
>Это не очень хорошо, так как непонятно, что эта функция должна делать. Это никак не описано и никак не проверяется. Мне непонятно, что в ней надо написать. По моему так лучше сделать функции getStartingSalary(), getStartingCoffee() и тд, с которыми все проще и понятнее.
>> В общем тут у меня проблема и не понимание как из этого слабого места выкрутиться. [про абстрактные методы]
>Сделать абс. методы вроде getStartingSalary():float { return 500; } для каждого значения.
Я вообще не понимаю что тут от меня нужно.
Теперь немного истории. Сначала я так и сделал, что в классе Employee просто сидели подобные абстрактные методы, и просто переопределялись для каждого нового класса сотрудника - это было удобно на этапе решения базовой задачи.
Но потом когда я начал решать антикризисные меры, там вы первых написано:
>Пришло время проверить, соответствует ли твой код принципам ООП? Гибок ли он и легко ли поддается изменениям?
В во вторых в одном методе там есть условие:
>Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров.
И тут становится понятно, что просто ретернить захардкоженные значения уже не работает. Ведь их придется менять, а что бы менять - их нужно где-то хранить (в полях и храним, а значит всё предельно удачно теперь встает на свои места и зменяемов с помощью опять же сеттеров и геттеры не просто хардкод а берут то, что в текущий момент в объекте установлено. Я опять же легко могу кому-угодно что угодно поменять, а как всё это провернуть с методами вроде этого:
>getStartingSalary():float { return 500; }
- я просто НЕ ПОНИМАЮ. Если у меня снова будут в сотруднике такие методы - то как я смогу вот эти вот 800 заменить на 1100 в коде?
13. Так же добавил свистелку в виде методов setTimePoint() и benchMark() - прошу оценить реализацию. Саму идею я уже где-то видел, но вот как обычно называется это под капотом и как раскидано по коду я не ковырял пока.
14.
>Кстати, у нас еще есть задача про Гостиницу. Не хочешь отточить навыки ООП?
Да хочу, лучше было сразу просто условие скинуть.
В итоге 2 главных проблемы так и не решил - OrgInfo ращбил на кучу копипастных методов, которые пусть и удобные, но само их существование мне не нравится и они дублируются и лишь загромождают код, и вообще ничего не понял как нужно сделать абстрактные методы в сотрудниках по типу: getStartingSalary():float { return 500; }
1. Вывод шаблонов в конструкторе и деструкторе убрал, сделал обычные методы.
2.
>А зачем нужно OrgInfo? Нельзя ли в организации сделать методы для вычислений этих чисел? Просто у меня ощущение, что этот класс заточен только под вывод таблицы, и тогда код getOrgInfo() желательно вынести из компании куда-нибудь, хотя бы в ту же OrgInfo. Или вообще ликвидировать этот метод.
Класс OrgInfo - выпилил, перенес вычисления общих и средних показателей по организации в отдельные методы. Теперь поясню почему я так держался за это с самого начала. Изначально очень удобно и просто было высчитывать всё это за 1 цикл, тупо прокручивая все департаменты в нем 1 раз, и складывая всю инфу в 1 выходной объект, поэтому в итоге это и выросло в такую вот шизу как отдельный класс для хранения этой информации на выходе этого метода. Мне и сейчас решение с вынесением всех вычислений в разные методы не кажется хорошим, просто потому что, во первых они почти все повторяют друг друга и у них отличается только переменная на входе, выходе и название соответственно, а во вторых всё это вместо одного компактного цикла использует каждый раз по циклу на метод и опять же перебирает снова и снова департаменты. В общем я сделал как рекомендовано, но остались сомнения с точки зрения оптимизации.
3. Тайп хинты расставил везде где нашел возможным.
4. try/catch выпилил, самому не нравилась идея оборачивать каждый код с exeptionon'ом внутри в эту конструкцию. А если я например чужой класс ипользую - мне нужно идти смотреть получается нет ли там внутри выброса исключений и на методы внутри которых есть рисовать try/catch каждый раз? Звучит тупо, так что только рад что это всё не нужно.
5.
>> public function promoteStuff(array $promotionList) {
>Я не думаю, что это надо делать в департаменте. Тут от департамента ничего не требуется и работников можно повышать напрямую.
Ок, сделал напрямую повышение, зачем лишние действия совершать.
6.
>По моему было бы гораздо удачнее сделать так:
>if ($employeeSelector->matches($employee)) ...
>Это позволило бы поместить логику отбора в класс-фильтра, где ей и место. Ну и еще есть такой вариант с коллбеком:
Перенес из департамента метод фильтрации сотрудников в сам класс EmployeeSelector, сделал там метод match который принимает на вход сотрудника и выдает булево значение - подходит сотрудник под текущие параметры или нет.
> new EmployeeSelector('Engineer', [1, 2, 3], [true, false]);
>лучше сделать возможность указать "не важно" для ранга. null, например, передать.
При этом сам способ фильтрации почти не изменился, разве что добавил возможность передавать в конструктор null как вариант "не важно"
7.
> return round($pageCost, 3);
>Не надо делать тут round, так как это нужно только для вывода в таблицу и должно быть там, где делается вывод, а не тут. Тут должен быть метод, который возвращает точное значение.
Ок, округление теперь во вьюхах
8.
> $vectorSecondAC = clone $vectorVanilla;
>В клонировании у тебя допущена ошибка. Этот метод не создает глубокую копию компании. он делает клон компании, но в него помещает ссылки на те же самые департаменты с теми же самыми работниками - они по умолчанию не клонируются, а просто копируются ссылки. Изучи магический метод __clone().
Переделал, теперь при клонировании организации должны клонироваться еще и объекты департаменты, а не просто ссылки на них, и при клонировании департамента соответственно объекты сотрудников
9.
> $this->organisation->setTitle("после антикризисных мер #1");
>Это неправильно, ради решения задачи про антикризисные меры добавлять в Компанию поле-комментарий. Это не нужно компании, это нужно только в антикризисных мерах, и это надо делать где-то в другом месте.
А мне это казалось клевым - мы изменили объект - мы отметил куда-то о том что он теперь другой и не нужно ниоткуда выцеплять это из других классов, комментарий идет сразу с объектом.
У нас ведь класс компании и служит не для каких-то там бизнесс-процессов компании, а для по сути отчетов, вот и это туда же.
Убрал, но как в другом месте это выводить или впиливать - не понял.
10.
>Сортировку лучше делать в одной функции, так:
>Также, есть еще такой трюк, вычисляем "вес" и сравниваем его:
Выбрал вариант с вычислением веса, так как он мне понятнее.
11.
>По view: в идеале, надо использовать htmlspecialchars при вставке текста в HTML. Иначе может быть уязвимость XS
Ок, обернул, только ведь это имеет смысл если мы выводим информацию, на которую как-то могут влиять сами юзеры, а так то кто в мою вьюху что подсунет. И вообще сделал обертку которая тупо сокращает длину названия функции, что бы было компактнее и удобнее её исользовать в перемешку с html
С простыми правками надеюсь что пока что всё, а не будет еще больше замечаний над которыми я просижу опять неделю.
Теперь опять к самому главному.
12.
>> abstract protected function setDefaults();
>Это не очень хорошо, так как непонятно, что эта функция должна делать. Это никак не описано и никак не проверяется. Мне непонятно, что в ней надо написать. По моему так лучше сделать функции getStartingSalary(), getStartingCoffee() и тд, с которыми все проще и понятнее.
>> В общем тут у меня проблема и не понимание как из этого слабого места выкрутиться. [про абстрактные методы]
>Сделать абс. методы вроде getStartingSalary():float { return 500; } для каждого значения.
Я вообще не понимаю что тут от меня нужно.
Теперь немного истории. Сначала я так и сделал, что в классе Employee просто сидели подобные абстрактные методы, и просто переопределялись для каждого нового класса сотрудника - это было удобно на этапе решения базовой задачи.
Но потом когда я начал решать антикризисные меры, там вы первых написано:
>Пришло время проверить, соответствует ли твой код принципам ООП? Гибок ли он и легко ли поддается изменениям?
В во вторых в одном методе там есть условие:
>Увеличить в целях стимуляции умственной деятельности базовую ставку аналитика с 800 до 1100 тугриков, а количество выпиваемого им кофе с 50 до 75 литров.
И тут становится понятно, что просто ретернить захардкоженные значения уже не работает. Ведь их придется менять, а что бы менять - их нужно где-то хранить (в полях и храним, а значит всё предельно удачно теперь встает на свои места и зменяемов с помощью опять же сеттеров и геттеры не просто хардкод а берут то, что в текущий момент в объекте установлено. Я опять же легко могу кому-угодно что угодно поменять, а как всё это провернуть с методами вроде этого:
>getStartingSalary():float { return 500; }
- я просто НЕ ПОНИМАЮ. Если у меня снова будут в сотруднике такие методы - то как я смогу вот эти вот 800 заменить на 1100 в коде?
13. Так же добавил свистелку в виде методов setTimePoint() и benchMark() - прошу оценить реализацию. Саму идею я уже где-то видел, но вот как обычно называется это под капотом и как раскидано по коду я не ковырял пока.
14.
>Кстати, у нас еще есть задача про Гостиницу. Не хочешь отточить навыки ООП?
Да хочу, лучше было сразу просто условие скинуть.
В итоге 2 главных проблемы так и не решил - OrgInfo ращбил на кучу копипастных методов, которые пусть и удобные, но само их существование мне не нравится и они дублируются и лишь загромождают код, и вообще ничего не понял как нужно сделать абстрактные методы в сотрудниках по типу: getStartingSalary():float { return 500; }
>>182372 - кун
Хахахаххаха, лох ебаный блядь. Зашел в гугол, нашел оф сайт этой либы, скончал ее и подключил в своем проекте. Нахуй ноду с ее ебаным npmч
>Я правильно понял, что на продакшене подразумевается, что надо все это удалить руками? Или может есть какой то флаг для npm, чтобы он это сам это сделал и осталось только нужное.
Обычно используют системы сборки типа webpack для такого.
На продакшене у тебя будет только минифицированный файл. А на деве полезно иметь документацию и не аглифицированные-минифицированные сорцы, чтобы можно было в случае чего обратиться к справке либы прямо из ide своей или посмотреть как там внутри код работает.
Через pecl поставь либу id3 (вроде так называется) и там уже готовых функций...
Допустим у тебя 10 сайтов по 10 сторонних либ в каждом. Ты это руками будешь обновлять? Или при разворачивании проекта на другой машине ты будешь всё это руками качать? Какую-то хуйню пишешь. Вангую у тебя ничего сложнее сайта-визитки не было, где в зависимостях только джиквери.
> Допустим у тебя 10 сайтов по 10 сторонних либ в каждом.
Допусти ты тот самый мудак, который использует фреймворк для всего на свете. По себе не равняй.
Каждый сайт свой набор инструментов, один хуй надо будет читать чэенджлог, что убрали, что добавили. Ну и уж не такая это и частая процедура обновлять раз в год, а то и 2, библиотеки.
Из пальца высасываешь.
Некоторых людей это не беспокоит.
Некоторым даже нравится, да.
Те, кого такая ситуация не устраивает, либо берут чужую практику, либо делают свою. В итоге получается что-то типа React. Вот про него и почитай.
Какие-то у тебя школярские проблемы. Ты хоть бы на рынок труда смотрел и выбирал где больше платят и меньше заебывают, больше свободного времени на развитие. Пхп - это мелкие галеры с потогоной системой. Будешь медленно делать пректы, тебя будут всякую фигню заставлять выполнять. Будешь быстро делать - придется говнокодить и прменять примитивные технологии в ущерб своему развитию.
ну я работаю пхп макакой и не жалуюсь. Меня не интересует метод оценки быдла хде больше плотють я хочу для себя выучить какойто более серьезный язык чем пхп. Пробовал ноду - дно. Взялся вот за пайтон - разочаровуюсь по мере изучения и возникла мысль чем же так хуйов пхп?
>когда дошел до ООП и увидел что объект необходимо постоянно передавать в метод - дропнул
лол ребёнок
Я работаю пхп макакой и жалуюсь на то что продуктовые компании, в которых пхп основной язык разработки по пальцам одной руки пересчитать. На счет быдла: ты конечно выбрал пхп потому, что у тебя серьезный математический (или инженерный) бэкграунд
>продуктовые компании
>язык разработки
чево блядь?
Я не выбирал пхп, я случайно в него вкатился, просто потомучто надо было сделать для себя пару сайтов
А кто ругает то? Самый лучший язык как по мне, вкатился только из-за того что наоборот все любят и язык идет вперед, пока всякие руби адово загнивают
Хз пишу на семерке, всё норм. А вообще ща товарищ попросил сайт ему простой сделать, а я не знаю никаких цмс толком. Думаю вот что надо бы свой первый сайт на вордпрессе сварганить уже как раз.
Вот тут не поспоришь: если делаешь что-то для себя или нанимаешь макак в свою студию - пхп замечательный. Если же сам макака, то вступает в действие колесо сансары из которого не выбраться. Из макаки в следующей жизни человеком не переродишься.
>Если же сам макака, то вступает в действие колесо сансары из которого не выбраться. Из макаки в следующей жизни человеком не переродишься.
Поясни ка немного о чем ты? Готов поспорить, что ты нихуя не понимаешь просто, но предвзят к тому, что мол если ты разработчик на пхп, то у тебя одна лишь дорога - клепать лендосы на цмс.
На любых языках пишется как важный софт так и бесполезное говно.
Так-же как и на любых человеческих языках можно вести полезный диалог, а можно пиздеть без толку.
Пыха хорошая, пыха замечательная. Только вот со временем ты вырастешь и захочешь писать более быстрый софт - и не сможешь, потому что она жестко прикручена к запуску отдельного скрипта на каждый процесс. Захочешь писать более надежный софт и увидишь, что это невозможно, посколько вместо числа или строки в функцию может придти прокисший борщ. Захочешь использовать крутые библиотеки - и помощи ни от языка, ни от сообщества не дождешься. Захочешь нанять кодеров себе в помощь - у тебя в коридоре будет вонь как в конюшне и толпа кидающихся какахами макак-соискателей. Карочи, пыха со временем просто перестает доставлять.
Я и в правду нихуя не понимаю ,но все вакансии пхп либо студии с однодневными проектами на одно рыло (а значит дядя будет присваивать весь твой труд себе, с нулевым вкладом со своей стороны), либо какая-нибудь часть проекта работающая с фронтендом на удаленке/фрилансе (и тут ты должен быть фулстак разрабом). У тебя просто не будет ни времени, ни возможности специализироваться глубоко в чем-нибудь одном, или изучить что-то новое. Дядя специально выбрал php для своего проекта, чтобы было быстро и дешево.
Пишешь на https://www.typescriptlang.org/docs/handbook/classes.html
компилируешь в JS атоматом webpack-ом.
Ссу на жлоба. Наверно, ты еще и алкоголик.
вообще то ты первый начал писать оскорбления вроде "ребенок". так что уебывай отсюда и сагу свою забери
>залогироваться
Хуя ты {'даунёнок' => 'глупнький'})0)
Рвись ниже, пажалуста, веселите меня всем тредом.
По итогу все не работает только в долбаном хроме, а в остальных браузерах все и всегда нормально работает. Кто то знает как это фиксить?
Disable cache в настройках Devtool
В классе есть одно свойство, которое в зависимости от объекта будет генерироваться с помощью той или иной хитровыебанной функции, причем практически ничего похожего между ними не будет. Соответственно, я бы хотел передавать объекту функцию. Или хотя бы иметь возможность сделать нечто типа
$obj->bob = function () {return "YOBA"}
Ты аут? Нахуя ты скрин куска треда кинул?
Не работает. ЧЯДНТ?
Суть в том, что есть класс, который работает с html-кодом. И этот самый html-код может получаться разными способами, которые никак не унифицировать. То есть, у каждого объекта своя версия функции, которая генерирует html. По умолчанию он будет генерироваться по определенному алгоритму из переданных в скрипт параметров, но может быть результатом обращения к API, file_get_contents, генерацией по совершенно другому алгоритму и т. д.
Я ёбаный наркоман и закомментировал run, но раскомментирование не помогает.
$this->tst() в run? Для поля tst можешь сделать хитрый setter, кторый будет выполнять колбэк, мне лень писать
Не знаю, насколько это всрато, но мою задачу решает.
>>183306
Не работает.
Доделал Student List вот ссылка на проверку
https://github.com/levneedscoffee/Student-List
Оп, я никак не могу понять зачем нужен отдельный класс Студент?
>С чего вдруг оно не в $this?
>Объявляет public tst
>Потом в функции вызывает this->tst
Как он по твоему должен в this переселиться?
https://stackoverflow.com/questions/1981375/setting-public-class-variables
На скриншоте всё работает, тебя это не смущает?
Я только въезжаю в ООП, но, по-моему, ты несёшь какую-то хуйню, анон.
$this - используется для доступа к объекту изнутри класса. Я в классе объявляю tst. Объект этого класса тоже будет иметь tst. Чё не так то? Тем более, на скриншоте всё работает.
Вдвойне тем более - ты сам по своей ссылке переходил? Там написано, что да, это работает, но лучше обмазываться инкапсуляцией (или как это называется). Это архитектурный дроч, сейчас вопрос не в этом.
> echo "Ответ $answers[$answer[1]]\n";
Тут слишком сложное выражение. Лучше было в отдельной строчке получить ответ, сохранить его в переменную и далее выводить переменную.
Мануал по подстановке переменных в строки: http://php.net/manual/ru/language.types.string.php#language.types.string.parsing
array_rand возвращает массив только если требуется вернуть более 1 ключа, иначе она возвращает сам ключ, не в массиве.
Первый элемент массива имеет индекс 0, а не 1.
>>174852
Есть 2 подхода - внедрять сервисы по одному либо забить и внедрить сразу контейнер.
>>174967
В контроллерах допустимо внедрять контейнер, так как они все равно не повторно используемые, никто не будет пытаться как-то подменять передаваемые сервисы.
> но лучше, как мне кажется, внедрять зависимости сразу в методы,
Для этого вызывающий код должен это поддерживать, то есть анализировать аргументы методов и подбирать сервисы. Плюс, это не работает, если у тебя для одного класса есть несколько сервисов.
Для отладки кода поставь везде echo, которые показывают, какие места кода выполняются и чему равны переменные в этой точке. Например, если if не выполняется, выведи, чему равны переменные перед ним и увидишь, почему.
> elseif($timeOP == '=') {
Это неправильно, так как $timeOp - это предыдущая (а не текущая) увиденная операция, а после = уже других операций нет и это условие никогда не сработает.
> то он выдаёт набор цифр 0002222488448842662. Они должны быть в результатах вычисления, это правильные цифры, но и не правильные.
Потому что надо сбрасывать number после выполнения операции.
>>175435
Короче можно сделать, если использовать функцию array_rand - подробности в мануале.
Еще короче - если составить массив, в котором каждый элемент - это массив, содержащий варианты слов:
[
['я', 'ты', 'он'],
['видел', 'слышал', 'знал'],
...
];
И обходить его в цикле.
>>175648
Через if/else: если операция это плюс, то сложить; если операция это минус, то вычесть и тд.
> Можно же числа определить в один массив, а операции в другой, а когда for дойдёт до "=", по очереди вызвать элементы массивов с числами и операциями, так, чтобы они сложились в пример и сразу вычислялись?
Можно попробовать. Но тогда наверно проще сделать единый массив и с числами, и с операциями, и идти по нему последовательно.
Для отладки кода поставь везде echo, которые показывают, какие места кода выполняются и чему равны переменные в этой точке. Например, если if не выполняется, выведи, чему равны переменные перед ним и увидишь, почему.
> elseif($timeOP == '=') {
Это неправильно, так как $timeOp - это предыдущая (а не текущая) увиденная операция, а после = уже других операций нет и это условие никогда не сработает.
> то он выдаёт набор цифр 0002222488448842662. Они должны быть в результатах вычисления, это правильные цифры, но и не правильные.
Потому что надо сбрасывать number после выполнения операции.
>>175435
Короче можно сделать, если использовать функцию array_rand - подробности в мануале.
Еще короче - если составить массив, в котором каждый элемент - это массив, содержащий варианты слов:
[
['я', 'ты', 'он'],
['видел', 'слышал', 'знал'],
...
];
И обходить его в цикле.
>>175648
Через if/else: если операция это плюс, то сложить; если операция это минус, то вычесть и тд.
> Можно же числа определить в один массив, а операции в другой, а когда for дойдёт до "=", по очереди вызвать элементы массивов с числами и операциями, так, чтобы они сложились в пример и сразу вычислялись?
Можно попробовать. Но тогда наверно проще сделать единый массив и с числами, и с операциями, и идти по нему последовательно.
Зависит от реализации и от настроек. Обычно можно указать - хотим ли мы каждый раз создавать новый объект или переиспользовать старый.
Если твой объект не становится хуже в процессе использования, то его логично переиспользовать, чтобы не создавать новые. Более того, если объект в себе кеширует какие-то значения, то переиспользование позволяет задействовать этот кеш.
>>175799
Задается это обычно при настройке контейнера. Для каждого сервиса индивидуально.
>>175828
Не советую. Этот фреймворк очень старый и даже по тем временам он выглядел весьма сомнительно. Он переполнен дурными приемами написания кода.
Бери лучше Юи, он по стилю поход, но современнее. Но конечно, для такой простой задачи, как студенты, это наверно перебор и лучше брать микрофреймворк вроде Slim.
> У меня ступор и я хоть и хочу каждый день сесть за задачу, но понимаю что сначала надо прочитать кучу теории по местам в которых у меня дыры, а потом уже садиться и писать сразу нормально с исключениями и паттернами хотя бы для бд.
В задаче про студентов есть подробнейшие комментарии, которые разъясняют все сложные места. Прочти их.
>>175988
Если у тебя можно смотреть только свой профиль, то не надо указывать id. Если можно смореть любой профиль, то надо указывать.
> Проверка через $_SESSION я так понял довольно уязвима ?
А при чем тут вообще сессия?
> По get запросу и id юзера - panel.php?id=25 и в самой странице поставить проверки, если юзер на которого мы зашли совпадает с залогиненым юзером, то появляются крутилочки для изменения данных
Проверять надо не тут. Проверять надо в первую очередь в том обработчике, в котором эти изменения сохраняются. Перед сохранением надо проверить, что у пользователя есть права на изменение.
Зависит от реализации и от настроек. Обычно можно указать - хотим ли мы каждый раз создавать новый объект или переиспользовать старый.
Если твой объект не становится хуже в процессе использования, то его логично переиспользовать, чтобы не создавать новые. Более того, если объект в себе кеширует какие-то значения, то переиспользование позволяет задействовать этот кеш.
>>175799
Задается это обычно при настройке контейнера. Для каждого сервиса индивидуально.
>>175828
Не советую. Этот фреймворк очень старый и даже по тем временам он выглядел весьма сомнительно. Он переполнен дурными приемами написания кода.
Бери лучше Юи, он по стилю поход, но современнее. Но конечно, для такой простой задачи, как студенты, это наверно перебор и лучше брать микрофреймворк вроде Slim.
> У меня ступор и я хоть и хочу каждый день сесть за задачу, но понимаю что сначала надо прочитать кучу теории по местам в которых у меня дыры, а потом уже садиться и писать сразу нормально с исключениями и паттернами хотя бы для бд.
В задаче про студентов есть подробнейшие комментарии, которые разъясняют все сложные места. Прочти их.
>>175988
Если у тебя можно смотреть только свой профиль, то не надо указывать id. Если можно смореть любой профиль, то надо указывать.
> Проверка через $_SESSION я так понял довольно уязвима ?
А при чем тут вообще сессия?
> По get запросу и id юзера - panel.php?id=25 и в самой странице поставить проверки, если юзер на которого мы зашли совпадает с залогиненым юзером, то появляются крутилочки для изменения данных
Проверять надо не тут. Проверять надо в первую очередь в том обработчике, в котором эти изменения сохраняются. Перед сохранением надо проверить, что у пользователя есть права на изменение.
pluralisation в Симфони 2.1 сделан максимально бездарно. Так как transChoice() не предполагает, что может быть несколько слов, зависящих от нескольких чисел. А также там нет поддержки зависимости от пола. Пример:
"{user} (расшарил|расшарила) {pictures} (картинка|картинки|картинок) для {friends} (друга|друзей)"
Что я могу посоветовать:
- посмотреть, можно ли сделать свой наследник сервиса Translator
- в нем сделать вызов Intl с полноценным форматированием + свои методы по вкусу
- если нельзя, сделать свой сервис и использовать его, добавить свои теги для перевода в Твиг
- посмотреть в issues компонента Translation, есть ли там жалобы
- если нет, подробно объяснить недостатки существующей системы (я уже создал issue)
> Но судя по сорсам - можно сделать свой MessageFormatter:
Так и стоит поступить. Не забудь это задокументировать в своем проекте, а то догадаться, что ты подменил форматтер, будет невозможно.
>>176742
Зря. Может, у тебя глаз замылился, а ОП с анонами что-то увидят, что ты сам не видишь.
>>176781
Потому что в браузер надо передавать не обычный текст, а текст в формате HTML.
Чтобы переносы строк нормально работали и в браузере и в ideone (и в консоли), можно использовать для этого \n, а в начале программы поставить
header("Content-Type: text/plain; charset=utf-8");
Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML, и уважать переносы строк в нем (так как в языке HTML перенос строки равносилен пробелу).
Иначе перенос строки будет в исходном коде страницы (его можно увидеть нажав Ctrl + U), но на самой странице его не будет.
>>176853
На хостинге может быть отключен вывод ошибок на экран (правильно), и их надо смотреть в логах.
pluralisation в Симфони 2.1 сделан максимально бездарно. Так как transChoice() не предполагает, что может быть несколько слов, зависящих от нескольких чисел. А также там нет поддержки зависимости от пола. Пример:
"{user} (расшарил|расшарила) {pictures} (картинка|картинки|картинок) для {friends} (друга|друзей)"
Что я могу посоветовать:
- посмотреть, можно ли сделать свой наследник сервиса Translator
- в нем сделать вызов Intl с полноценным форматированием + свои методы по вкусу
- если нельзя, сделать свой сервис и использовать его, добавить свои теги для перевода в Твиг
- посмотреть в issues компонента Translation, есть ли там жалобы
- если нет, подробно объяснить недостатки существующей системы (я уже создал issue)
> Но судя по сорсам - можно сделать свой MessageFormatter:
Так и стоит поступить. Не забудь это задокументировать в своем проекте, а то догадаться, что ты подменил форматтер, будет невозможно.
>>176742
Зря. Может, у тебя глаз замылился, а ОП с анонами что-то увидят, что ты сам не видишь.
>>176781
Потому что в браузер надо передавать не обычный текст, а текст в формате HTML.
Чтобы переносы строк нормально работали и в браузере и в ideone (и в консоли), можно использовать для этого \n, а в начале программы поставить
header("Content-Type: text/plain; charset=utf-8");
Это заставит браузер воспринимать то, что выводит твоя программа, как обычный текст, а не HTML, и уважать переносы строк в нем (так как в языке HTML перенос строки равносилен пробелу).
Иначе перенос строки будет в исходном коде страницы (его можно увидеть нажав Ctrl + U), но на самой странице его не будет.
>>176853
На хостинге может быть отключен вывод ошибок на экран (правильно), и их надо смотреть в логах.
В PHP есть проверка типов и нормальные классы, а в Питоне нет. Код типичного проекта на Питоне ничем не читабельнее PHP, например https://github.com/rg3/youtube-dl/blob/master/youtube_dl/utils.py (обрати внимание на объем файла, количество комментариев и число подчеркиваний).
>>176866
Для админок и не требущих особого дизайна страничек подойдет.
>>176970
Даст понимание, из чего состоит веб-сервер.
>>176999
> з(дела(л|ю|н)
Еще есть "зделаем" и "зделанный".
> (\w+)\sа\s/
Пробелов может быть более одного. После "а" может идти не пробел, а другая запятая. Лучше использовать \b
> '/\b((,)|(\.)|(;)|(:)|(!)|(\?))[^\s]\b/' Хоспаде, ну почему ты не работаешь?! //
А зачем там \b в начале? И у тебя теряется [^\s].
>>177153
Может попробовать что-то сделать на основе Symfony Forms?
>>177318
Спроси на сайтах фриланса.
В PHP есть проверка типов и нормальные классы, а в Питоне нет. Код типичного проекта на Питоне ничем не читабельнее PHP, например https://github.com/rg3/youtube-dl/blob/master/youtube_dl/utils.py (обрати внимание на объем файла, количество комментариев и число подчеркиваний).
>>176866
Для админок и не требущих особого дизайна страничек подойдет.
>>176970
Даст понимание, из чего состоит веб-сервер.
>>176999
> з(дела(л|ю|н)
Еще есть "зделаем" и "зделанный".
> (\w+)\sа\s/
Пробелов может быть более одного. После "а" может идти не пробел, а другая запятая. Лучше использовать \b
> '/\b((,)|(\.)|(;)|(:)|(!)|(\?))[^\s]\b/' Хоспаде, ну почему ты не работаешь?! //
А зачем там \b в начале? И у тебя теряется [^\s].
>>177153
Может попробовать что-то сделать на основе Symfony Forms?
>>177318
Спроси на сайтах фриланса.
Я не знаю, хочешь - попробуй и посмотрим, что выйдет, напишешь о впечатлениях. По задумке, ты начинаешь с того, что выражаешь требования к коду в виде тестов, а потом пишешь сам код.
>>177630
Ок, верно. Для вертикального выравнивания одной строки есть трюк - сделать line-height равной высоте блока.
Ок, верно, хотя стоило бы учесть еще ширину border - он тоже место занимает.
Вместо цифр лучше использовать слова, например:
< ;
Полный список HTML-мнемоник: https://dev.w3.org/html5/html-author/charref
Попробуй там найти свои символы. А то цифры человеку читать очень трудно.
Верно.
Все правильно.
>>177715
Повторение - мать учения.
>>177756
Да, но придется изучить Си, и написать свой веб-фреймворк.
Я не знаю, хочешь - попробуй и посмотрим, что выйдет, напишешь о впечатлениях. По задумке, ты начинаешь с того, что выражаешь требования к коду в виде тестов, а потом пишешь сам код.
>>177630
Ок, верно. Для вертикального выравнивания одной строки есть трюк - сделать line-height равной высоте блока.
Ок, верно, хотя стоило бы учесть еще ширину border - он тоже место занимает.
Вместо цифр лучше использовать слова, например:
< ;
Полный список HTML-мнемоник: https://dev.w3.org/html5/html-author/charref
Попробуй там найти свои символы. А то цифры человеку читать очень трудно.
Верно.
Все правильно.
>>177715
Повторение - мать учения.
>>177756
Да, но придется изучить Си, и написать свой веб-фреймворк.
Во-первых, ООП не надо "присобачивать", если ты не видишь, где он нужен, то не надо использовать.
Во-вторых, странно, что ты не видишь, где можно применить ООП. ООП позволяет представлять всякие сущности в виде объектов, и в вебе этого предостаточно - Пользователь, Пост, Комментарий например - прекрасно представляются в виде объектов. Также в виде объектов можно представлять сервисы, которые что-то делают с этими сущностяями. Ну например, ты можешь сделать класс СервисПостов с методами:
- найтиПостПоid(id)
- найтиВсеПостыПоУсловию(сортировка, offset, limit)
- найтиПостыПоТегу(тег)
- получитьЧислоКомментариевКПосту(пост)
- удалитьПост(пост)
- сохранитьПост(пост)
И так далее.
Пример ООП кода есть например в моем уроке по MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md - изучи его.
> Можно же просто статическими функциями реализовывать все требования к сайту.
Статические методы ничем в принципе не отличаются от обычных функций. И соответсвенно получается такой же спутанный воедино код. Ты можешь почитать урок https://github.com/codedokode/pasta/blob/master/arch/di.md где описаны некоторые недостатки этого подхода.
Главный недостаток - весь код становится намертво спутан и ты не можешь вынести какие-то функции для использования в другом проекте. Не можешь их тестировать.
Пример, у тебя есть метод конвертации валюты:
public static function convertDollarToRouble(float $dollars): float { ... }
В нем идет обращение к внешнему сервису, с которого скачивается текущий курс. А как протестировать этот метод без обращения к тому сервису?
Чтобы иметь возможность подменять ответ от внешнего сервиса, мы должны вынести "скачивальщик курса" из кода функции и сделать его отдельной сущностью: либо анонимной функцией
function ($currency): float {}
либо интерфейсом:
interface RateDownloader()
{
public function downloadRate($currency);
}
Эта функция или объект становятся "зависимостью" метода convertDollarToRouble (зависимость - это то, от чего зависит работа функции). Теперь, когда мы выделили зависимость, мы должны как-то ее передать ("внедрить"). Это можно тоже сделать по-разному, например, передать как аргумент:
convertDollarToRouble(RateDownloader $rateDownloader, float $dollars): float { ... }
Передача как аргумент на практике довольно неудобна, так как тебе придется как-то получать эту зависимость в месте вызова функции. Плюс, зависимостей может стать больше - если ты захочешь кешировать курс, то придется передавать еще и кеш как зависимость. Удобнее внедить зависимость через конструктор:
class Converter
{
public function __construct(RateDownloader $rateDownloader) {}
public function convertDollarToRouble(...) ..;
}
Вот мы и получили ООП с DI. Прочитай урок по DI, там подробнее описано.
Во-первых, ООП не надо "присобачивать", если ты не видишь, где он нужен, то не надо использовать.
Во-вторых, странно, что ты не видишь, где можно применить ООП. ООП позволяет представлять всякие сущности в виде объектов, и в вебе этого предостаточно - Пользователь, Пост, Комментарий например - прекрасно представляются в виде объектов. Также в виде объектов можно представлять сервисы, которые что-то делают с этими сущностяями. Ну например, ты можешь сделать класс СервисПостов с методами:
- найтиПостПоid(id)
- найтиВсеПостыПоУсловию(сортировка, offset, limit)
- найтиПостыПоТегу(тег)
- получитьЧислоКомментариевКПосту(пост)
- удалитьПост(пост)
- сохранитьПост(пост)
И так далее.
Пример ООП кода есть например в моем уроке по MVC: https://github.com/codedokode/pasta/blob/master/arch/mvc.md - изучи его.
> Можно же просто статическими функциями реализовывать все требования к сайту.
Статические методы ничем в принципе не отличаются от обычных функций. И соответсвенно получается такой же спутанный воедино код. Ты можешь почитать урок https://github.com/codedokode/pasta/blob/master/arch/di.md где описаны некоторые недостатки этого подхода.
Главный недостаток - весь код становится намертво спутан и ты не можешь вынести какие-то функции для использования в другом проекте. Не можешь их тестировать.
Пример, у тебя есть метод конвертации валюты:
public static function convertDollarToRouble(float $dollars): float { ... }
В нем идет обращение к внешнему сервису, с которого скачивается текущий курс. А как протестировать этот метод без обращения к тому сервису?
Чтобы иметь возможность подменять ответ от внешнего сервиса, мы должны вынести "скачивальщик курса" из кода функции и сделать его отдельной сущностью: либо анонимной функцией
function ($currency): float {}
либо интерфейсом:
interface RateDownloader()
{
public function downloadRate($currency);
}
Эта функция или объект становятся "зависимостью" метода convertDollarToRouble (зависимость - это то, от чего зависит работа функции). Теперь, когда мы выделили зависимость, мы должны как-то ее передать ("внедрить"). Это можно тоже сделать по-разному, например, передать как аргумент:
convertDollarToRouble(RateDownloader $rateDownloader, float $dollars): float { ... }
Передача как аргумент на практике довольно неудобна, так как тебе придется как-то получать эту зависимость в месте вызова функции. Плюс, зависимостей может стать больше - если ты захочешь кешировать курс, то придется передавать еще и кеш как зависимость. Удобнее внедить зависимость через конструктор:
class Converter
{
public function __construct(RateDownloader $rateDownloader) {}
public function convertDollarToRouble(...) ..;
}
Вот мы и получили ООП с DI. Прочитай урок по DI, там подробнее описано.
>Код типичного проекта на Питоне ничем не читабельнее PHP, например https://github.com/rg3/youtube-dl/blob/master/youtube_dl/utils.py
https://www.youtube.com/watch?v=RE0G2b3-QsA
Или смысла нет какой из них использовать, мол они все одинаковые по сути?
Знаю точно что есть modx - там больше ориентированности на код, нужно все шаблончики и сущности в ручную прописывать, что бы потом юзер смог типовую сущность в админке создать и заполнить например накликиванием. В каком-нибудь wordpress же знаю что для тебе заранее заготовлены некие сущности такие как page, posts ну и так далее, и админка заранее под все это заточена. Там вообще всё сделано так, что простой бложек или сайт о компании из нескольких страничек можно накликать по сути в админке и подцепить поверх понравившуюся тему там же.
Сейчас под WP есть куча писек, которые и интернет-магазин в пару кликов помогают создать. Многие макаки умудряются зарабатывать на создании сайтов никак не залезая в код, на любой чих есть плагины. Другой вопрос, конечно, в том, что если плагина на чих всё-таки нет и нужно слегка что-то изменить, то макак обделается. Да и на производительности это сказывается не очень хорошо, конечно же.
А modx да, он как CMF позиционируется. Еще Drupal вроде ёба в этом смысле, но не знаю, жив ли он еще.
Друпал вроде как жив, там новая версия выкатилась вроде как год назад с ооп-ориентацией. У нас в городе есть галера которая чисто на друпале ориентируется и они периодически проводят даже обучалки что бы новых макакичей рекрутировать.
>Другой вопрос, конечно, в том, что если плагина на чих всё-таки нет и нужно слегка что-то изменить, то макак обделается.
Мне кажется, что это стереотип из той же оперы что и на пхп одни макаки работают и вообще это язык хуевый. Я вот вчера скачал себе wp, и там заходишь в их кодекс и там везде в документации вплетены гайды о том что писать в коде для чего и думаю что многие обучалки тоже расскажут о том как и что писать, так что любая макака спустя пару недель будет просто вынуждена хотя бы в шаблонах покопаться, а там уже и впшные функции надо знать и так далее.
function utf8_strrev($str){
preg_match_all('/./us', $str, $ar);
return implode(array_reverse($ar[0]));
}
Что значит якорь us в регулярки? Это все не ASCII символы?
Как вообще распутывать чужие регулярки.
расшифровывается как: Do Repeat YOLO
Состоит в дублировании кода везде, где это возможно, каждый инстанс должен иметь уникальные изменения.
>Обычно используют системы сборки типа webpack для такого.
А можно как нибудь без вебпака обойтись? У меня из жс-а просто только плеер нужен. Например устанавливать через композер или npm, и затем баш скриптом необходимые для продакшена файлы в публичную папку перетаскивать.
У нас есть статическая страница c подключенным jquery. После заполнения формы мы делаем ajax-запрос с параметрами к php, а оттуда по прошествии некоторого времени приходит ответ выстраиваемый в хтмл таблицу.
Как я понял php-файл при работе с ajax работает сам по себе, без участия браузера пользователя. Если он покинет страницу, то выполнение скрипта на сервере продолжится и без него?
Если моя теория верна даем печеньки посетителю, пусть он уходит. Результат выполнения скрипта мы отправляем в БД с привязкой к кукам посетителя. При возвращении, показываем из БД результат выполнения по кукам. Это вообще возможно?
Ты просто начитался всяких ВЫЕБУ ТВОЙ РОТ ЗА ТАКОЙ СТИЛЬ КОДА и не представляешь, какой ад творится в днищесегменте фриланса. Некоторые петухи вообще на одних конструкторах живут, а ты удивляешься тому, что они сайты на визуальных письках вроде visual composer делают абсолютно не вникая в код.
>Некоторые петухи вообще на одних конструкторах живут.
Сначала подумал что они вместо функций создают под каждую функцию класс, в котором есть только конструктор, и потом уже этим говном оперируют в коде.
Потом таки допер в чем дело.
Наркоманы. Не понимают очевидной шутки. НО БОЛЕЕ ТОГО, УЖЕ МАНЯФАНТАЗИЙ СЕБЕ ПРИДУМАЛИ ГДЕ ОП С ЕРОХОЙ ВСТРЕЧАЕТСЯ!
Привет.
Работаю версталой и хочу двигаться дальше, пришел к тебе Анон за ответов на вопросо, есть в нашем городе ряд вакансий джун пхп, там в требованиях никто не распишет что нужно знать джуну, так вот Анон в этом и вопрос.
Что нужно знать джуну пхп? Какой базис? Что уметь делать?
Для начала: ВСЕ из онлайн-учебника ОП-а - НЕОБХОДИМО
А что плохого в новых фреймворках?
Пацаны, ероха подкатывающий к ОП-тян детектед, ибо очевидно пытается выставить нашу теорию в безмуном свете, что бы замести следы. Типа как когда вор громче всех кричит: "лови вора!".
>Если он покинет страницу, то выполнение скрипта на сервере продолжится и без него?
Да
>Это вообще возможно?
Да
Или взять меня так скажем, под крыло и помочь начать. Хорошо умею в математику, вроде не туп
>1184429
Можно было бы. Я вот начинал и продолжаю без ментора, всё сам, ахах, а было бы гораздо быстрее, если бы можно было спросить кого-то. Иногда тупо не знал как у гугла спросить даже.
>страницы по старому принципу подключаются и везде есть .html
Я могу настроить mod_rewrite так, что все урлы на моём сайте будут иметь вид /page.html а под капотом будет какой-нибудь моднявый Yii.
а, ну хотя, я забыл про .htaccess XD
В JS треде настолько токсичные аноны, что за полтора года и наверное сотню вопросов, я получил только посылы на три буквы. Так что нет.
Мне кажется, что тут так во всех тредах. Только на SO всё хорошо, ибо модерация.
Разбирал свой старый проект и нашёл там такое странное решение. Почему я вообще так сделал - непонятно. Видимо, была причина.
Вопросы как правило связаны со знанием синтаксиса, ооп, шаблонами. Если попадется мудак с матаном головного мозга, то заебет вопросами математического характера. Будет самоутверждаться и потом всем говорить, что ты говно итебя взяли только потому что кто то должен набирать код.
Кадровичка если будет тестировать без представителя отдела разработки - считай завалил вью. Ибо бужет дохуя непонимания с их стороны из-за отсуствия знаний в области ИТ.
Говорят, остались еще пидоры, которые ебут мозги загадками на внимательность и сообразительность. Забыл как это точно называется что то типа тест на когнитивные возможности. Вопрос про бейсбольную биту и мяч за 1 доллар и 10 центов и прочие пидорские загадки.
А, ну будь еще готов к приколам, дадут тестовое задание и ты его должен будешь решать на листочке. Типа напишите нам функцию парсинга входных данных с такими то критериями. Не ссы и говори, чтобы дали комп с ИДЕ для решения, ибо вентел на хую работать в конторе, которая заставляет писать код на листочке.
https://github.com/codedokode/pasta/blob/master/interview-tasks.md
Ну и ты отличай, что PHP-разработка бывает разной, в веб-студиях у тебя будут спрашивать опыт работы с CMS и умение верстать, в нормальных конторах - фреймворки, вопросы по БД по алгоритмам. Вот этот >>184544 в каких-то днищеконторах собеседовался, надеюсь ты в такие не попадёшь.
https://drive.google.com/file/d/0B8VvT_dbtrYMN1NKSUZXRlo4QUk/view
Вот тестовое задание одной фирмы из моей мухосрани. Я на самом деле не пойму даже обязательного задания, просто блять не представляю как это должно выглядеть. в виде таблицы, карточек, еще какого-то говна
Пошел на хуй, пидор. А если серьёзно, то я с этой ебалой сталкивался в середине 00-х и до ~2010 года, потом перекатился в другую сферу, тк заебало. Сейчас уже может все по другому, ибо рыночек порешал и квалификация не так важна. Сечёшь, всегда найдется обезьянка готовая работать чуть ли не за еду.
Будто на пхп не надо каждый год учить фреймворк.
намек на ноду?
>https://drive.google.com/file/d/0B8VvT_dbtrYMN1NKSUZXRlo4QUk/view
Посмотрел, чет дохуя хотят. Эту залупу в полном виде неделю минимум писать.
Это норма?
Плюс важнее, мне кажется, не что, а как. Тот же фронт лучше бы замутить на SPA, а не на голом хтмл с jquery-аякс-запросами. Но причем тут ебаный пхп, лол?
Нет, просто если раньше ты писал говно с минимумом знаний - это был эдакий сайт.
А сейчас это просто паттерн такой KISS называется.
Ну и тоже самое с остальным. Фабрики и прочее.
>мне всё ещё нужны паттерны?
Да.
>много ли сейчас чего вообще делается на чистом похапе? Я думал чаще всего просто юзают фреймворки
И то юзают и то. Тут тебе не JS, где можно вообще фрэймворк выучить без знаний JS, тут надо знать PHP чтобы порой исходники вордпресса править.
Вордпресс - лютейший говнокод.
Если пишешь на фреймворке, то да, он нехуево так задает тебе архитектуру, как правильно прогать. Но лучше таки иметь представление, хотя бы.
Если что-то разрабатывается с нуля, то без паттернов у тебя получится лапша, не поддающаяся дальнейшим улучшениям и хуй пойми как работающая
>Вордпресс - лютейший говнокод.
Пруфы? Ты что, все его исходники отмониторил лол? Да, он ебануться какой тормозной. Но весь PHP такой, разве нет?
>не использует тайп-хинтинг последних версий, неймспейсы, даже ооп внедрено в качестве костыля.
Че еще пизданешь?
Что я такого спизданул, еблан? Я тебя спросил, почему ты считаешь его говнокодом.
>не использует тайп-хинтинг последних версий, неймспейсы, даже ооп внедрено в качестве костыля.
Вот твои еблано-аргументы, значит. Но блять почему если код не июзает неймспейсы, то это сразу говнокод? И блять PHP по дефолту был не строго типизорованым, что же получается PHP по роду своему вынуждает писать говнокод, так выходит?
> PHP по роду своему вынуждает писать говнокод
Да, прикинь?
На языке без строгой типизации очень сложно, если сказать невозможно, что-то большое и сложное написать.
Даже js-дауничи начали что-то подозревать и выкатили typescript.
А еще в пхп есть такие штуки, как fatal error'ы (вместо исключений)(и даже появление исключений не привело к отказу от тонн старого говна, в итоге современные методы кидают исключения, а старые ерроры и предупреждения).
А еще функция в пыхе, ВНЕЗАПНО, из-за его нестрогой типизации может вернуть тебе совершенно любое говно вместо того, что ты ожидаешь.
Например, повсеместно используемая PDO:fetch() вернет тебе FALSE вместо массива, если запись не найдена.
То есть ждешь ты такой массив, а тебе фолс ебаный прилетел в ебало.
Как было бы сделано в норм языке: или пустой массив или исключение.
Но это же похапе.
И такое дерьмище там чуть ли не в каждой стандартной функции.
Хоть и стараются в новых версиях return одного типа делать.
И еще есть вещи, вынуждающие писать на пхп говно-код.
Например, там нет нормальных структур данных. Только массив.
мимо пхп-фрилансер
>повсеместно используемая PDO:fetch() вернет тебе FALSE вместо массива, если запись не найдена. То есть ждешь ты такой массив, а тебе фолс ебаный прилетел в ебало. Как было бы сделано в норм языке: или пустой массив или исключение. Но это же похапе. И такое дерьмище там чуть ли не в каждой стандартной функции. Хоть и стараются в новых версиях return одного типа делать.
Что мещает юзать if (!PDO::fetch()) die()?
Типо это нормально, наоборот более юзабельно, нет?
>там нет нормальных структур данных. Только массив.
Лол что? PHP насколько я знаю позволяет оперировать любыми данными. Там же есть ассоциативные массивы аки словари в питоне. ЧТо же тебе ещё надо?
>Что мещает юзать костыли?
>Вот дед говно ел и ты ешь. Что тебе еще надо? Не жили богато - неча и начинать
Ты дурачек что ли? Какие костыли ебать? Это старинное условие ещё со времен Си.
Если ты используешь нэймспейсы - ты говнокодер. И неважно какой язык. Просто смирись.
Где ты в PHP будешь её использовать? Если ты в многопоточные расчёты хочешь, ты не совсем ту платформу выбрал.
Я вот на Хекслет когда сидел видел лекцию про неймспейсы и нихуя не понял, что это за говно.
Может кто-нибудь объяснить, что это?
Ну в смысле нет? Это интерпретируемый язык. И такой хуйни как у питона -- PyPy, насколько я знаю у PHP нет. Ну и с какого тогда хуя PHP -- не тормоз?
>А еще функция в пыхе, ВНЕЗАПНО, из-за его нестрогой типизации может вернуть тебе совершенно любое говно вместо того, что ты ожидаешь.
>Я написал функцию, а она не работает ряяяя
Про тестирование не слышал?
>На языке без строгой типизации очень сложно, если сказать невозможно, что-то большое и сложное написать.
Джависты не жалуются (Именно Java, а не JS).
>Например, повсеместно используемая PDO:fetch() вернет тебе FALSE вместо массива, если запись не найдена.
Тоесть по твоему она должна вернуть запись если её нет? Наркоман штоле? Правильное поведение.
>Как было бы сделано в норм языке: или пустой массив или исключение.
С чего вдруг? Код то работает. Это не проблемы программы что у тебя руки кривые и ты ищешь то, чего нет. Исключение ты можешь сам повесить.
Короче как я понял ты просто неопытный ньюфаг который не понимает зачем он вообще в программисты понаехал.
Что ещё спизданёшь? Что автотест костыли? Что try catch костыли? if у него костыль. Одна история ахуительнее другой просто.
>Тоесть по твоему она должна вернуть запись если её нет? Наркоман штоле? Правильное поведение.
Нет, он видимо имеет ввиду что если записи нет то функция сама должна раисить исключение. Ленивый он, ебать.
Он не торможной. Если у тебя в коде рекурсия через рекурсию - да. А если говнокода нет, всё отлично работает. МОжешь даже тесты погуглить на последние версии. А то, что вордпресс тупит - ну вообще хз, стандартно на него навешают аддонов, плагинов, потом "Ну это PHP тупит". Ну конечно. Чёж у меня 10 лет сайт висит, даже не упал ни разу. А у вас тупит всё. Руки кривые растут из жопы.
Говно самое настоящее. Вроде как ты используешь переменную Vasya. А какой-то чел уже в своём фрэймворке использует переменную Vasya. Ну вот подрубил ты фрэймворк, и стало у тебя 2 васи. Чтобы конфликта небыло, надо нэймспэйсы использовать. Это типа говнокодерский выход. А на самом деле надо
1 Нормально именовать переменные.
2 Определять область их видимости. Если у тебя счётчик i используется в 1 функции, а видит его вся прога - это очень плохо.
>Он не торможной.
Нет братан, PHP -- нацеленный на веб исключительно тормозной язык(ибо интерпретируемый). Ну в сравении с Си или с Питоном это так.
Лол, а разве такие однотипные переменные не в массивы запихивать или там в объекты?
>Определять область их видимости. Если у тебя счётчик i используется в 1 функции, а видит его вся прога - это очень плохо.
Бля а что ты определить область видимости что мы юзаем? Правильно, класс, функции и неймспейсы блять. Неймспейсы нам помогают определить облать.
Так этож не её обязаность. Странный какой-то. Там же суть в том, чтобы либо вернуть порцию данных, либо сообщить что данных нет. Возвращать массив нулевых значений - это как-то вообще тупость. Вот тебе вернули false, ну сам массив нулевых значений создай. Делов то.
>тормозной язык
Если что, интерпретатор 7 пхп - самый быстрый интерпретатор из существующих, дебил.
>Ну в сравении с Си или с Питоном это так.
Ты видимо на серьёзных проектах с питоном не работал. Питон для веба - тот ещё тормоз. В своё время перевёл контору на PHP с Питона (2013 год примерно), и билинговая система ускорилась в 7 раз.
Лол что? Ну окей может быть я не знаю. Но факт остается фактом PHP интерпретируется. Следовательно это тормоз тот ещё. .
Прочитай про HHVM например, который почему-то медленнее 7 PHP. Хотя казалось бы C++ и все дела.
Нет, банально потому, что когда я с ним работал, никто его не юзал, ибо был он в бета версии. Но веб сервера на C++ и на C в 2007 ещё писал. Годнота. Если не считать что можно сервер положить в даун одном лёгким движением руки.
Сложение строк как конкатенация, но вычитание КАК МАТ ОПЕРАЦИЯ! За что, господи за что?
Каждая переменная - ГЛОБАЛЬНАЯ
>Каждая переменная - ГЛОБАЛЬНАЯ
Это где это такая поебень? В жс? Там же вроде замыкания и вся хуйня
>if у него костыль.
Метод, возвращающий разные типы данных, - это говно.
Маняконструкции, обходящие эту проблему - костыли.
Как ты додумался вообще про if говорить, ты на нулевом уровне абстракции мыслишь?
>Другие языки "Обьяви специально что переменная глобальная"
>JS "Обьяви специально что переменная НЕ глобальная"
>на нулевом уровне абстракции мыслишь?
Ты еблан совсем братуха? О чем ты блять? Ифы это часть языка. Ты типо не юзаешь их когда проектируешь алгоритм? Нулевой уровень абстракции блять. Лол
>Как ты додумался вообще про if говорить
Это ты говоришь. Я поэтому и упомянул, что у тебя увидел и удивился.
>Маняконструкции, обходящие эту проблему - костыли.
Потому-что нет никакой проблемы. То, что ты не способен додуматься, до того что для всех очевидно и само собой - твои проблемы.
- У меня есть сущность Файл(базовая сущность), и наследующие ее Изображение, Видео, и Аудио. (Сделано это для того чтобы рендерить разные шаблоны под видео, аудио и "обычный" файл)
- Создаю конкретные сущности фабрикой на лету.
- Дальше мне нужно отправить в очередь сообщение, если это медиа файл, чтобы потом этот файл расковырять, сконвертировать, да что я захочу.
И вот тут у меня вопрос, я могу все еще свитчом вызывать нужные методы в зависимости от типа сущности, или, как я думал, использовать ПОЛИМОРФИЗМ, сделать интерфейс например Queueable с каким-то handle методом, мои сущности будут его имплементировать, и потом одной строчкой отправлять в очередь сообщение, в котором буду передавать например id, воркер будет вызывать handle метод. Но тут проблема, моя сущность это domain object, в ней не должно быть никакой логики вроде как, да и вообще в любой точке программы мне не нужен метод handle, который по сути выполняется всего один раз после загрузки файла. Не ебать мозг и использовать свитч/ifelse ?
Вы тут совсем поехавшие? Говорить что неймспейсы нинужны - это какой-то новый уровень долбоёбизма.
>>184737
Ещё один с мантрой про "нинужно".
>>184741
>Джависты не жалуются (Именно Java, а не JS).
>Java - самый строготипизированный язык из популярных, с таким уровнем типизации, что пыху снится только во влажных мечтах
Что, мать твою, ты такое несёшь?
Долбоёбов полон тред.
Это не я сказал, а ты придумал.
Речь шла про то, что сам факт того, что метод может вернуть разные типы данных - это хуево.
Затем чел запостил говнокод, который "обходит" эту проблему в виде конструкции if.
На что я ответил, что подобные конструкции (любые, которые пытаются проверять, какой тип данных вернул метод это должно было быть сделано на этапе объявления возвращаемого типа данных этого метода) - костыли.
Затем пришел очередной довен в виде тебя с охуительной "цитатой" собственного сочинения.
>На языке без строгой типизации очень сложно, если сказать невозможно, что-то большое и сложное написать.
Тайпхинты в пхп не решают эту проблему? Кроме переменных можно на любой пердежь поставить тип принимаемых и возвращаемых значений. На переменные и так смысла нет их ставить особо, все равно через конструктор их заполняешь. осталось только перенести весь код в интернете на пхп7 и переписать с тайп хинтами
> Тайпхинты в пхп не решают эту проблему?
Отчасти решают. Тайп-хинтинг - это большой шаг вперёд для php.
Но один хуй, не вся стандартная либа на них переписана.
Да и продвинутых конструкций вида HashMap $map = new HashMap<ArrayList<String>>() один хуй нет, ибо нет ни нужного уровня типизации, ни самих структур данных, лол
>HashMap $map = new HashMap<ArrayList<String>>()
Что-то на эльфийском, не могу прочитать. Можно это как-то реализовать через интерфейсы? Да и вообще, зачем из пхп делать худшую версию джавы?
Я думаю, что имелось ввиду, что функции, от которой ожидаешь массив, логично было бы вернуть массив, а не false.
Если записи не были найдены - возвращается пустой массив.
Я для себя написал простой класс, в который запихнул всю работу с PDO и который автоматически это делает.
Просто заебало каждый раз проверять результат. А если этого не делать, то, передай ты это дело в какой-нибудь foreach, получишь предупреждение о том, что invalid argument suplied for foreach.
А если функция возвращает пустой массив, то таких проблем нет, предупреждений нет. Профит.
Не логичнее возвращать null если записи не были найдены? Отсутствие записей = отсутствие данных = null.
fetchAll кстати возвращает пустой массив, хуй их поймешь.
Походу я не прав. Перечитал своё сообщение и подумал, что ведь действительно правильно возвращать false вместо массива.
Ты когда заказываешь бургер в пиццерии, получаешь ответ о том, что бургеров у них нет, а не пустую коробку, верно? Это правильно поведение.
Да и сувать непонятную переменную в цикл, без проверки, тоже неправильно, наверное.
Хотя, в моём любимом C++, языке со статической типизацией, таких проблем нет, т.к. если функция должна вернуть int, то она его вернёт. Обосрётся, но вернёт int ну или выкинет исключение, но это другая история.
20!8 год на дворе
От кого плюются? Количество языков с таким синтаксисом настолько огромно, что проще перечислить кто не с ткаим синтаксисом. Критика синтаксиса - это просто сразу нахуй. Сразу видно что ты из b к нам потраллить понаехзал, а приграммированием и не занимался ни разу. Синтаксис у него, ахуеть вообще.
почему сразу троль?
>>начать юзать тайпхинтинг
Имею в виду, что будет больше смысла в нём, т.к. сейчас он не даёт особого прироста к производительности.
Кто говорит такую ересь, походу совсем не понимает зачем PHP нужен. JIT он профиты даёт не для веба, зачем он тут? Какую задачу он решает?
Да потому что, PHP интерпретируемый, что по определению уже снижает производительность. JIT-компиляция должна принести трансляцию кода в двоичный, соответственно можно нормально работать с системой, нет посредников. Тут уже будут иметь смысл строготипизированные программы. Потому что сейчас юзать тайпхинтинг смысла не особо-то много.
Задачу повышения производительности медленного слоника, как минимум, внося смысл в тайпхинтинг для php.
Почитай, может поймешь
https://stackoverflow.com/questions/32940170/are-scalar-and-strict-types-in-php7-a-performance-enhancing-feature
Хоть один нормальный человек в треде
>JIT-компиляция
>в двоичный
>сейчас юзать тайпхинтинг смысла не особо-то много
>юзать тайпхинтинг ради маня-производительности, а не для того, чтобы писать более нормальный структурированный код, а не пхп-лапшу
Какие же конченые дауны в этом треде, я просто в ахуе
>что функции, от которой ожидаешь массив, логично было бы вернуть массив, а не false.
Если ты ожидаешь от неё массив, ты чего-то не понимаешь в программировании.
Тайпхинтинг есть в PHP7 и старше.
>заебало каждый раз проверять результат.
Всмысле каждый раз. Написал функцию или вообще в MVC пропустил. 1 раз написал и забыл про это. Ты вообще о чём? Какой каждый раз то?
А не ты ли даун? Компиляция подразумевает трансляцию высокоуровневого кода в машинный язык, для того, чтобы блять твой компьютер понял что делать. А PHP интерпретируемый на данный момент. ПОЧИТАЙ ЧТО ЭТО ТАКОЕ.
>>юзать тайпхинтинг ради маня-производительности, а не для того, чтобы писать более нормальный структурированный код, а не пхп-лапшу
Я и сказал "причин не особо много". Только ради структурированного кода и стоит использовать. Больше тебе он ничего принесет.
Посмотри тесты или проведи их сам. ПОЭТОМУ Я И ОСВЕТИЛ ТЕМУ JIT'а. Который и даст буст к пёрфоменсу, сука.
Вот по-твоему, зачем ВК разработали KittenPHP?? ПОДУМАЙ, КАКУЮ ЗАДАЧУ ОНИ РЕШИЛИ, МММММ?
>даст буст к пёрфоменсу, сука.
Опять какой-то буст. А текущей производительности мало? Какой тебе буст нужен? И так уже быстрее некуда.
Тоесть ты ответить на вопрос не можешь, а про Jit сказал пушо васян из 11 класса тебе говорил что это круто.
У кого проблемы? Где? Пруфы, графики, скрины где именно PHP что-то там вешет. Почему у меня за 10 лет проблем с производительностью базы на 600 миллионов записей и PHP нет, а у залётного анона с laba1.php они вдруг есть?
омг, ты слишком консервативен. То есть, ты считаешь, что дальше некуда? Так? А что если я тебе скажу, что всё еще быстрее работать будет? :3
> JIT он профиты даёт не для веба
В том числе и для веба, почему нет? Если у нас долгоживущее приложение. Или если разработчики смогут сделать кеш для JIT-кода в короткоживущих приложениях.
>>185066
Поясню для любопытствующих анонов.
JIT повышает производительность часто выполняемых функций за счет их компиляции в машинный код, который выполняется напрямую процессором (по умолчанию php-код выполняется интерпретатором, а не процессором). Но тут не все просто, для роста производительности желательно знать тип каждой переменной - в этом случае мы можем вместо общего кода, поддерживающего любой тип данных, сделать "оптимизированную" версию, заточенную под конкретные типы. А без этого выигрыш может быть небольшой, а сама программа будет просто занимать больше памяти.
Именно потому и нужна JIT - just-in-time (а не AOT - ahead-of-time) компиляция - он во время выполнения кода определяет типы значений в переменных, которые нельзя определить до выполнения, только методом статического анализа кода.
Машинный код - это команды, которые "понимает" и выполняет процессор (по факту это просто числа, которые хранятся в памяти и каждое число обозначает отдельную примитивную команду вроде сложения содержимого 2 ячеек памяти). Программы на строго типизированных языках вроде Си, Го, компилируются в машинный код.
Байт-код - это команды, которые "понимает" и выполняет интерпретатор PHP. Обычно PHP-программа превращается в байт-код, и его выполняет интепретатор. AOT или JIT-компиляция - это преобразование программы в машинный код для выполнения на процесссоре напрямую. AOT компиляция делается до выполнения программы, JIT - в процессе выполнения.
Например, если у нас есть PHP-функция:
function x($a, $b) { return $a + $b; }
То не зная типы $a и $b - сложно для нее написать оптимальный машинный код. Так как для сложения целых чисел в машинном коде нужна одна команда, для сложения дробных другая, для сложения массивов - нужно вызвать отдельную подрограмму. В случае, если один из аргументов - строка, ее надо сначала преобразовать в число. Плюс, есть еще варианты, что в переменных может быть null или false/true. И функция выше превращается в такую сложную последовательность команд:
если ($a или $b - массив), то { использовать функцию сложения массивов; }
если ($a - это строка), то { преобразовать ее в число; }
если ($a это null, false или true), { преобразовать их в число; }
если ($b - это строка) { то преобразовать ее в число; }
если ($b это null, false или true), { преобразовать их в число; }
если ($a это int, а $b это float) { преобразовать $a в float; }
если ($b это int, а $a это float) { преобразовать $b в float; }
если ($a и $b - типа float) { использовать операцию сложения дробных чисел; }
если ($a и $b - типа int) { использовать операцию сложения целых чисел; }
То есть один знак "+" раскладывается в длинную последовательность машинных команд. И если мы используем AOT компиляцию, то особого выигрыша не получается - мы выполняем ту же длинную последовательность команд, которую выполнил бы интерпретатор, наткнувшись на оператор "+". Только теперь наша последовательность команд занимает больше места в памяти.
Теперь допустим, у нас есть вариант функции, где указаны типы данных:
function x(int $a, int $b) { return $a + $b; }
Здесь на первый взгляд типы заданы жестко, но это не так. PHP позволяет передать в качестве $a или $b также float, если в нем целое число, или string, строку с целым числом, и автоматически сконвертируем их в int (мануал ). Потому код получается такой:
если $a - это строка, то { попытаться преобразовать ее в int }
иначе если $a - это float, то { попытаться преобразовать ее в int }
иначе если $a не int, то { выдать ошибку }
то же самое для $b
использовать команду сложения целых чисел;
Это лучше, чем в прошлый раз, но не идеально. Указание declare(strict_types=1) в начале файла отключает эту конвертацию и позволяет сократить код до:
если $a не int, то { выдать ошибку }
если $b не int, то { выдать ошибку }
использовать команду сложения целых чисел;
JIT позволяет определить типы переменных. Во время выполнения кода он "запоминает" типы переданных переменных, а потом компилирует оптимизированную под эти типы версию функции в машинном коде. При этом в начале ставится проверка: если переданы переменные других типов, то вместо оптимизированной функции используется медленный, но универсальный неоптимизированный вариант.
JIT не бесплатен. Анализ и компиляция требуют времени. Потому обычно JIT применяется только к "горячим" функциям, которые вызываются больше определенного числа раз. JIT используется, например, в интерпретаторе JS, v8, который используется в Chrome.
> JIT он профиты даёт не для веба
В том числе и для веба, почему нет? Если у нас долгоживущее приложение. Или если разработчики смогут сделать кеш для JIT-кода в короткоживущих приложениях.
>>185066
Поясню для любопытствующих анонов.
JIT повышает производительность часто выполняемых функций за счет их компиляции в машинный код, который выполняется напрямую процессором (по умолчанию php-код выполняется интерпретатором, а не процессором). Но тут не все просто, для роста производительности желательно знать тип каждой переменной - в этом случае мы можем вместо общего кода, поддерживающего любой тип данных, сделать "оптимизированную" версию, заточенную под конкретные типы. А без этого выигрыш может быть небольшой, а сама программа будет просто занимать больше памяти.
Именно потому и нужна JIT - just-in-time (а не AOT - ahead-of-time) компиляция - он во время выполнения кода определяет типы значений в переменных, которые нельзя определить до выполнения, только методом статического анализа кода.
Машинный код - это команды, которые "понимает" и выполняет процессор (по факту это просто числа, которые хранятся в памяти и каждое число обозначает отдельную примитивную команду вроде сложения содержимого 2 ячеек памяти). Программы на строго типизированных языках вроде Си, Го, компилируются в машинный код.
Байт-код - это команды, которые "понимает" и выполняет интерпретатор PHP. Обычно PHP-программа превращается в байт-код, и его выполняет интепретатор. AOT или JIT-компиляция - это преобразование программы в машинный код для выполнения на процесссоре напрямую. AOT компиляция делается до выполнения программы, JIT - в процессе выполнения.
Например, если у нас есть PHP-функция:
function x($a, $b) { return $a + $b; }
То не зная типы $a и $b - сложно для нее написать оптимальный машинный код. Так как для сложения целых чисел в машинном коде нужна одна команда, для сложения дробных другая, для сложения массивов - нужно вызвать отдельную подрограмму. В случае, если один из аргументов - строка, ее надо сначала преобразовать в число. Плюс, есть еще варианты, что в переменных может быть null или false/true. И функция выше превращается в такую сложную последовательность команд:
если ($a или $b - массив), то { использовать функцию сложения массивов; }
если ($a - это строка), то { преобразовать ее в число; }
если ($a это null, false или true), { преобразовать их в число; }
если ($b - это строка) { то преобразовать ее в число; }
если ($b это null, false или true), { преобразовать их в число; }
если ($a это int, а $b это float) { преобразовать $a в float; }
если ($b это int, а $a это float) { преобразовать $b в float; }
если ($a и $b - типа float) { использовать операцию сложения дробных чисел; }
если ($a и $b - типа int) { использовать операцию сложения целых чисел; }
То есть один знак "+" раскладывается в длинную последовательность машинных команд. И если мы используем AOT компиляцию, то особого выигрыша не получается - мы выполняем ту же длинную последовательность команд, которую выполнил бы интерпретатор, наткнувшись на оператор "+". Только теперь наша последовательность команд занимает больше места в памяти.
Теперь допустим, у нас есть вариант функции, где указаны типы данных:
function x(int $a, int $b) { return $a + $b; }
Здесь на первый взгляд типы заданы жестко, но это не так. PHP позволяет передать в качестве $a или $b также float, если в нем целое число, или string, строку с целым числом, и автоматически сконвертируем их в int (мануал ). Потому код получается такой:
если $a - это строка, то { попытаться преобразовать ее в int }
иначе если $a - это float, то { попытаться преобразовать ее в int }
иначе если $a не int, то { выдать ошибку }
то же самое для $b
использовать команду сложения целых чисел;
Это лучше, чем в прошлый раз, но не идеально. Указание declare(strict_types=1) в начале файла отключает эту конвертацию и позволяет сократить код до:
если $a не int, то { выдать ошибку }
если $b не int, то { выдать ошибку }
использовать команду сложения целых чисел;
JIT позволяет определить типы переменных. Во время выполнения кода он "запоминает" типы переданных переменных, а потом компилирует оптимизированную под эти типы версию функции в машинном коде. При этом в начале ставится проверка: если переданы переменные других типов, то вместо оптимизированной функции используется медленный, но универсальный неоптимизированный вариант.
JIT не бесплатен. Анализ и компиляция требуют времени. Потому обычно JIT применяется только к "горячим" функциям, которые вызываются больше определенного числа раз. JIT используется, например, в интерпретаторе JS, v8, который используется в Chrome.
Ну и выкатывайтся тогда в более подходящий тред, а не самоутверждайся за счет обучающихся.
>>185136
Не нервничай.
>>184954
Ну иди и пиши на том языке, который тебе нравится. Алсо, твой язык тоже отстой: https://habr.com/post/315152/
Это тред изучения PHP, а не тред для обсуждения вашей тонкой душевной организации, которой видите ли синтаксис не нравится. Валите на любой другой язык, если вам этот не нравится. Что вы тут забыли, флудеры? Мы тут делом вообще-то занимаемся.
>>184901
Да, логичнее всегда исопльзовать один тип возвращаемого значения. Как вариант - для объектов можно возвращать null. Так проще использовать результат функции, не добавляя лишние ифы.
>>184915
Если ты возвращаешь массив записей, то логичнее пустой массив. Если одну запись - то null.
>>184925
Нет, возвращать false как раз плохая идея так как ты теперь везде обязан будешь писать проверку на false. Лучше возвращать значения только одного типа. Также, можно использовать исключение - оно бы подошло в твоем примере с пиццерией. Так как ожидаемое поведение пиццерии - то, что бургеры она всегда может приготовить.
<figure>
<img>
<figcaption>...</figcaption>
</figure>
Закрепляешь figcaption за счет абс. поз. относительно figure.
>>184824
Нет, тут нужны именно generic types, то есть обобщенные типы вроде "Коллекция объектов типа T" - Collection<T>. В примере выше надо читать так:
ArrayList<String> - список строк (на основе массива)
HashMap<ArrayList<String>> - хеш-таблица, содержащая списки строк.
Без generic тебе придется делать кучу одинаковых классов вроде IntCollection, StringCollection, SomeObjectCollection, итд.
>>184790
> У меня есть сущность Файл(базовая сущность), и наследующие ее Изображение, Видео, и Аудио. (Сделано это для того чтобы рендерить разные шаблоны под видео, аудио и "обычный" файл)
Есть еще альтернативный вариант - сделать сущность Файл с полем media, а в него класть МедиаОбъект - Изображение, Видео или Аудио.
> И вот тут у меня вопрос, я могу все еще свитчом вызывать нужные методы в зависимости от типа сущности, или, как я думал, использовать ПОЛИМОРФИЗМ, сделать интерфейс например Queueable с каким-то handle методом, мои сущности будут его имплементировать
Вот по моим ощущениям, это не очень хорошая идея. У нас есть принцип single responsibility - каждый класс занимается своим делом. Класс File представляет данные о файле в БД и наверно это не его задача, вычислять размер изображения или генерировать превьюшки, или выполнять какие-то фоновые задачи. Это лучше поручить отдельному сервису (а может даже нескольким):
медиаОбъект = сервис->распарситьФайл(путьКФайлу);
превьюшка = сервис->сгенерироватьПревьюшку(видеоФайл); или превьюшка = сервис->сгенерироватьПревьюшку(путьКФайлу);
сервисФоновыхЗадач->запланировать(ЗадачуКонвертацииВидео);
При таком подходе мы можем вызывать функцию анализа в любом месте кода. А при твоем - тебе нужен объект Файл с кучей полей для этого. То есть я предлагаю для чистоты кода изолировать друг от друга отдельно хранение данных о загруженных файлах и анализ медиафайлов. А у тебя все выглядит спутанно и связано друг с другом намертво.
А ты ведь еще пытаешься в класс Файл и засунуть взаимодействие с очередью задач. Зачем? Задача - это отдельная сущность и можно либо: использовать в Задаче switch, либо вообще сделать разные типы задач:
обработатьМедиаФайл(файл) {
если (тип == видеофайл) {
запланировать ЗадачуГенерацииПревьюшки;
запланировать ЗадачуКонвертацииВидео;
} иначе ...
...
}
Задачи можно представить либо объектами, либо функциями.
Наследование имело бы смысл применять, если бы можно было добавлять новые типы файлов, не трогая код. Но это в твоем случае вряд ли возможно. Не стоит использовать наследование просто ради того, чтобы оно было. То есть задавай себе вопрос, а зачем мне это? Какая мне от этого выгода?
<figure>
<img>
<figcaption>...</figcaption>
</figure>
Закрепляешь figcaption за счет абс. поз. относительно figure.
>>184824
Нет, тут нужны именно generic types, то есть обобщенные типы вроде "Коллекция объектов типа T" - Collection<T>. В примере выше надо читать так:
ArrayList<String> - список строк (на основе массива)
HashMap<ArrayList<String>> - хеш-таблица, содержащая списки строк.
Без generic тебе придется делать кучу одинаковых классов вроде IntCollection, StringCollection, SomeObjectCollection, итд.
>>184790
> У меня есть сущность Файл(базовая сущность), и наследующие ее Изображение, Видео, и Аудио. (Сделано это для того чтобы рендерить разные шаблоны под видео, аудио и "обычный" файл)
Есть еще альтернативный вариант - сделать сущность Файл с полем media, а в него класть МедиаОбъект - Изображение, Видео или Аудио.
> И вот тут у меня вопрос, я могу все еще свитчом вызывать нужные методы в зависимости от типа сущности, или, как я думал, использовать ПОЛИМОРФИЗМ, сделать интерфейс например Queueable с каким-то handle методом, мои сущности будут его имплементировать
Вот по моим ощущениям, это не очень хорошая идея. У нас есть принцип single responsibility - каждый класс занимается своим делом. Класс File представляет данные о файле в БД и наверно это не его задача, вычислять размер изображения или генерировать превьюшки, или выполнять какие-то фоновые задачи. Это лучше поручить отдельному сервису (а может даже нескольким):
медиаОбъект = сервис->распарситьФайл(путьКФайлу);
превьюшка = сервис->сгенерироватьПревьюшку(видеоФайл); или превьюшка = сервис->сгенерироватьПревьюшку(путьКФайлу);
сервисФоновыхЗадач->запланировать(ЗадачуКонвертацииВидео);
При таком подходе мы можем вызывать функцию анализа в любом месте кода. А при твоем - тебе нужен объект Файл с кучей полей для этого. То есть я предлагаю для чистоты кода изолировать друг от друга отдельно хранение данных о загруженных файлах и анализ медиафайлов. А у тебя все выглядит спутанно и связано друг с другом намертво.
А ты ведь еще пытаешься в класс Файл и засунуть взаимодействие с очередью задач. Зачем? Задача - это отдельная сущность и можно либо: использовать в Задаче switch, либо вообще сделать разные типы задач:
обработатьМедиаФайл(файл) {
если (тип == видеофайл) {
запланировать ЗадачуГенерацииПревьюшки;
запланировать ЗадачуКонвертацииВидео;
} иначе ...
...
}
Задачи можно представить либо объектами, либо функциями.
Наследование имело бы смысл применять, если бы можно было добавлять новые типы файлов, не трогая код. Но это в твоем случае вряд ли возможно. Не стоит использовать наследование просто ради того, чтобы оно было. То есть задавай себе вопрос, а зачем мне это? Какая мне от этого выгода?
> Ну в сравении с ... Питоном это так.
Питон точно такой же интерпретируемый, только еще медленнее, насколько я знаю.
> Ну в сравении с Си
Да, Си компилируется в машинный код и он быстрый, но ты пробовал на нем писать что-то сложнее сложения матриц в лабораторной работе? Вот смотри, код на PHP:
$userIds = array_unique(array_column($users, 'id'));
ну-ка, сколько времени у тебя займет написать то же самое на Си? Явно в несколько раз больше, чем у меня эту строчку. А ведь еще надо компилировать и отлаживать. Потому код на Си быстрый, но требует очень много времени на написание и отладку, а время работы программистов - это деньги и писать интернет-магазин на Си нереально дорого.
Плюс, в PHP в разы удобнее отладка, пишешь просто var_dump() и смотришь результат, а при исключении видишь нормальный стек трейс.
Конечно, есть компилируемые языки с большей скоростью написания кода - C++, D, Rust, Go - но она все равно ниже чем на PHP, плюс в них все плохо с веб-фреймворками, насколько я знаю.
>>184766
Если ты новичок-теоретик, зачем про производительность рассуждаешь?
>>184762
Вообще, это странно конечно. Не думаю, что он настолько медленнее, наверно что-то в алгоритмах поменялось.
>>184756
Возвращать нужно данные одного типа, чтобы не писать потом ифы.
> Вот тебе вернули false, ну сам массив нулевых значений создай. Делов то.
Там был разговор про другое, про функцию, которая возвращает список записей.
> Ну в сравении с ... Питоном это так.
Питон точно такой же интерпретируемый, только еще медленнее, насколько я знаю.
> Ну в сравении с Си
Да, Си компилируется в машинный код и он быстрый, но ты пробовал на нем писать что-то сложнее сложения матриц в лабораторной работе? Вот смотри, код на PHP:
$userIds = array_unique(array_column($users, 'id'));
ну-ка, сколько времени у тебя займет написать то же самое на Си? Явно в несколько раз больше, чем у меня эту строчку. А ведь еще надо компилировать и отлаживать. Потому код на Си быстрый, но требует очень много времени на написание и отладку, а время работы программистов - это деньги и писать интернет-магазин на Си нереально дорого.
Плюс, в PHP в разы удобнее отладка, пишешь просто var_dump() и смотришь результат, а при исключении видишь нормальный стек трейс.
Конечно, есть компилируемые языки с большей скоростью написания кода - C++, D, Rust, Go - но она все равно ниже чем на PHP, плюс в них все плохо с веб-фреймворками, насколько я знаю.
>>184766
Если ты новичок-теоретик, зачем про производительность рассуждаешь?
>>184762
Вообще, это странно конечно. Не думаю, что он настолько медленнее, наверно что-то в алгоритмах поменялось.
>>184756
Возвращать нужно данные одного типа, чтобы не писать потом ифы.
> Вот тебе вернули false, ну сам массив нулевых значений создай. Делов то.
Там был разговор про другое, про функцию, которая возвращает список записей.
>>184754
curl_exec возвращает строку. И вообще, откуда там возьмется объект? JSON это массивы (списки и словари), в нем полноценных объектов все равно нет.
>>184753
Ты путаешь неймспейс и область видимости переменной. Это не связанные вещи. Неймспейсы это просто способы давать классам и функциям длинные имена вроде
\MegaForum\Entity\Post
Но в коде использовать короткое имя Post. Урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
Область видимости - это где будет видна переменная, например, переменная созданная в функции, видна только внутри нее.
>>184749
Нет, фреймворк или библиотека вообще не должны создавать глобальные переменные. Плюс, на них неймспейсы не распространяются.
> Нормально именовать переменные.
Если ты про классы и функции, то это приводит к тому, что получаются длинные имена вроде Zend_Db_Table_abstract. Неймспейсы позволяют использовать уникальные имена, но при этом в коде исопльзовать более короткие синонимы: https://github.com/codedokode/pasta/blob/master/php/autoload.md
Вот зачем ты спешишь критиковать, не разобравшись в теме?
>>184739
У меня есть простой урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
>>184716
fetch сделан логично: он возвращает или запись или false. Пустой массив как раз нелогично, так как он выглядит как пустая запись из базы данных без единственной ячейки.
>>184754
curl_exec возвращает строку. И вообще, откуда там возьмется объект? JSON это массивы (списки и словари), в нем полноценных объектов все равно нет.
>>184753
Ты путаешь неймспейс и область видимости переменной. Это не связанные вещи. Неймспейсы это просто способы давать классам и функциям длинные имена вроде
\MegaForum\Entity\Post
Но в коде использовать короткое имя Post. Урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
Область видимости - это где будет видна переменная, например, переменная созданная в функции, видна только внутри нее.
>>184749
Нет, фреймворк или библиотека вообще не должны создавать глобальные переменные. Плюс, на них неймспейсы не распространяются.
> Нормально именовать переменные.
Если ты про классы и функции, то это приводит к тому, что получаются длинные имена вроде Zend_Db_Table_abstract. Неймспейсы позволяют использовать уникальные имена, но при этом в коде исопльзовать более короткие синонимы: https://github.com/codedokode/pasta/blob/master/php/autoload.md
Вот зачем ты спешишь критиковать, не разобравшись в теме?
>>184739
У меня есть простой урок https://github.com/codedokode/pasta/blob/master/php/autoload.md
>>184716
fetch сделан логично: он возвращает или запись или false. Пустой массив как раз нелогично, так как он выглядит как пустая запись из базы данных без единственной ячейки.
Первая часть элементарная и решается на Симфони за несколько часов, если ты хорошо с ней знаком. Тут правда подвох в количестве сотрудников - если ты попробуешь загрузить в память 50 000 объектов Доктрины, то скрипт упадет. Ну правильно, надо отсеивать тех, кто поленился изучить особенности работы Доктрины.
> не представляю как это должно выглядеть.
Написано же:
> Создайте веб страницу, которая будет выводить иерархию сотрудников в
древовидной форме.
Заходишь на страницу, а там дерево вроде дерева папок, только с сотрудниками. Вроде такого например https://www.jstree.com/demo/
Что такое дерево: https://ru.wikipedia.org/wiki/Дерево_(структура_данных)
>>184691
> Тот же фронт лучше бы замутить на SPA
Зачем? Ты предлагаешь писать 2 приложения (клиентское и серверное), замедлять время загрузки страницы, и повышать затраты на разработку, ради чего?
>>184538
Не годится, слишком длинно и слишком много копипасты.
Даю хинт. Допустим, тебе надо найти последовательность из слов, не менее 5 слов, где первое слово это "большой", а последнее - "кот". Вот регулярка:
большой\s(\w+\s){5,}кот
Первая часть элементарная и решается на Симфони за несколько часов, если ты хорошо с ней знаком. Тут правда подвох в количестве сотрудников - если ты попробуешь загрузить в память 50 000 объектов Доктрины, то скрипт упадет. Ну правильно, надо отсеивать тех, кто поленился изучить особенности работы Доктрины.
> не представляю как это должно выглядеть.
Написано же:
> Создайте веб страницу, которая будет выводить иерархию сотрудников в
древовидной форме.
Заходишь на страницу, а там дерево вроде дерева папок, только с сотрудниками. Вроде такого например https://www.jstree.com/demo/
Что такое дерево: https://ru.wikipedia.org/wiki/Дерево_(структура_данных)
>>184691
> Тот же фронт лучше бы замутить на SPA
Зачем? Ты предлагаешь писать 2 приложения (клиентское и серверное), замедлять время загрузки страницы, и повышать затраты на разработку, ради чего?
>>184538
Не годится, слишком длинно и слишком много копипасты.
Даю хинт. Допустим, тебе надо найти последовательность из слов, не менее 5 слов, где первое слово это "большой", а последнее - "кот". Вот регулярка:
большой\s(\w+\s){5,}кот
Ты можешь посмотреть исходники phpclub.tech, если интересно: https://github.com/richBlueElephant/phpClub
>>183769
Не факт. При разрыве соединения скрипт может завершиться. Там есть опция, чтобы игнорировать это.
>>184218
Не понятно, о чем речь. Какие еще куки? Просто отркываешь сайт в браузере, если ты залогинен, то актуальные.
>>184213
Я где-то выше писал, требования обычно - умение с первого дня включиться в проект, а для этого надо знать
- SQL
- ООП
- какой-нибудь фреймворк
- git
>>183746
Можно конечно. Вебпак - это склейщик и загрузчик JS/CSS файлов, если тебе он не нужен, то не надо его и использовать.
Конечно, можно копировать и скриптом. Можно на bash, можно на PHP, если хочется кроссплатформенности.
Также, в фреймворках вроде Симфони могут быть свои методы для этого.
>>182636
Ну так проблема, что на dev папка vendor обычно не видна из public. Хотя, ... можно ее отобразить туда симлинком, на дев можно, а на продакшен я не уверен, что это безопасно и что там нет какого-то php скрипта, который можно использовать не по назначению. Лучше не стоит на продакшене так делать.
Ты можешь посмотреть исходники phpclub.tech, если интересно: https://github.com/richBlueElephant/phpClub
>>183769
Не факт. При разрыве соединения скрипт может завершиться. Там есть опция, чтобы игнорировать это.
>>184218
Не понятно, о чем речь. Какие еще куки? Просто отркываешь сайт в браузере, если ты залогинен, то актуальные.
>>184213
Я где-то выше писал, требования обычно - умение с первого дня включиться в проект, а для этого надо знать
- SQL
- ООП
- какой-нибудь фреймворк
- git
>>183746
Можно конечно. Вебпак - это склейщик и загрузчик JS/CSS файлов, если тебе он не нужен, то не надо его и использовать.
Конечно, можно копировать и скриптом. Можно на bash, можно на PHP, если хочется кроссплатформенности.
Также, в фреймворках вроде Симфони могут быть свои методы для этого.
>>182636
Ну так проблема, что на dev папка vendor обычно не видна из public. Хотя, ... можно ее отобразить туда симлинком, на дев можно, а на продакшен я не уверен, что это безопасно и что там нет какого-то php скрипта, который можно использовать не по назначению. Лучше не стоит на продакшене так делать.
Вообще, в менеджерах пакетов вроде bower (и думаю, в плагине bower для компрозера) есть возможность исключать лишние файлы если автор библиотеки это предусмотрел. И в npm тоже.
А так, ничего страшного, если ты вывалишь лишние файлы в public, я думаю, нет, если только там нет PHP скриптов, которые можно использовать в целях атаки на сервер.
Зависимости, я думаю, не особо нужны, по идее там все компилируется автором в один файл, который можно использовать.
Обрати внимание, что там есть 2 варианта - dev и production. Если ты ставишь dev версию, то получишь гору зависимостей, потому npm лучше было вызывать с опцией production. Она описана тут https://docs.npmjs.com/cli/install
Так что варианты такие:
- устанавливать в public как есть
- устанавливать не в public, а потом как-то копировать или создавать симлинки на те файлы, которые нужно сделать доступными
В том же video.js есть файл .npmignore и он по идее должен игнорировать лишние файлы: https://github.com/videojs/video.js/blob/master/.npmignore
>>183674
Строка в памяти хранится как последовательность байтов. При этом в utf-8 1 буква может кодироваться комбинацией из нескольких байтов. Если ты не указываешь флаг u, то PCRE будет считать что у тебя 1-байтовая кодировка и точка будет соответствовать не символу, а одному байту. И он разобьет строку на отдельные байты, перевернет их и поменяет их комбинации и строка перестанет вообще раскодироваться. Флаг u заставляет его интепретировать строку как последовательность utf-8 символов.
Подробнее про байты и кодировки https://github.com/codedokode/pasta/blob/master/cs/strings.md
Ну вот создали HHVM например, создали под него даже мод PHP с типизацией. А он оказался медленнее чем PHP 7, не говоря уже про PHP 7.1. Тоесть вроде и производительность должны были поднять, ну и получили в скорости по сравнению с 5.4, но с другой стороны задачу "Надо выполнять код быстрее" это не решило.
>Если у нас долгоживущее приложение.
Как по мне - PHP для долгоживущих приложений не совсем подходит. Читал множество статей на stack о том что всё равно в районе 2-3 суток случается либо переполнение буфера, либо затыки с процессором.
Думаю, это зависит от того, как писать код. То есть проблема не в PHP, а где-то допущена в коде утечка. Хотя я сам не очень представляю, как ее искать без каких-то инструментов для этого.
Спасибо большое за советы. У меня есть еще один вопросик. Когда, например, видео конвертируется, я хочу быть в курсе этого, чтобы выводить стандартный шаблон для загрузки файла. То есть наверное у записи в бд должна быть колонка в стиле state = (new, proccessing, finished, failed) или что-то в таком духе. Я думал что если я использую бд, то может мне и beanstalk и не нужен (для задачки на обучение), но beanstalk будет стучать в базу только после того как получит новое сообщение, а скрипт на бд будет стучать каждое n время. Хорошая ли практика использовать сервер очередей И базу, чтобы быть в курсе состояния моего файла?
пятерку
Фигурная скобочка в определении классов и методов на той же строке стоит, как, например в if/else.
Это вообще почему так? При помощи чего вы это делаете? Ну не руками же, идеально никто не пишет, а вручную исправлять всё - так себе идея.
>самоутверждайся за счет обучающихся.
Вовсе нет. Но очень многие в треде несут полную хуйню с умным видом, тем самым мешая реально обучающимся.
Ты сука знаешь, чем JIT-компилятор отличается от обычного компилятора?
И нахуя такое счастье кому-то нужно?
> Зачем? Ты предлагаешь писать 2 приложения (клиентское и серверное), замедлять время загрузки страницы, и повышать затраты на разработку, ради чего?
Затем что потом заказчику захочется внести новый функционал, сделать мобильное приложение или ещё что.
А ты не предусмотрел возможностей для роста
NetBeans в 2018-м уже никто не юзает. PHPStorm из коробки нормально форматирует код и умеет ещё кучу всего: https://www.youtube.com/watch?v=pP9jw3fgrCU
> а вручную исправлять всё - так себе идея.
Верно, поэтому придумали PHP CS Fixer
Спасибо
Когда захочется, тогда и сделаем. Не вижу также прямой связи между SPA и мобильным приложением. С большой вероятностью там понадобится отдельный API.
>ты не предусмотрел возможностей для роста
А ещё надо написать версию без js и под ie6. Заказчик может тебя попросить написать, а ты не предусмотрел возможностей для роста.
\Name\Space\Class::...
То есть, когда мы, находясь в одном пространстве имён, пытаемся получить доступ к классам/функциями/чему-угодно из других пространств имён.
Я не особо знаю как работает php внутри, но такой вопрос меня мучает.
И вообще, нормально ли использовать такой способ записи? Или нужно обязательно везде писать use \Name\Space\Class::... ?
Тебе нужно разобраться как запускать процессы в фоне. В линуксе это делается добавлением символа & в конец консольной команды, на винде - не знаю, погугли "windows run process in background": https://superuser.com/a/591084
>>185504
Ты утрируешь, IE6 очень старый. Максимум, что просят в 2018-м - это поддержку IE 9-10. Сейчас заказчикам поголовно нужны SPA, а 0.2% маргиналов без JS никого не интересуют. Такова реальность, не то чтобы мне это нравилось.
>>185781
Если я тебя правильно понял, то нет никакой разницы - использовать use или всегда указывать полный неймспейс. Но использовать use - хорошая практика, которая делает код более лаконичным. Прописывать самому us'ы не нужно - IDE сами неймспейс прописывают.
> И вообще, нормально ли использовать такой способ
При возникновении подобных вопросов можешь просто открыть исходники популярного фреймворка, например Symfony, и посмотреть - делают там так или нет.
Читал про '&' что процесс открывется в том же треде что и сам инстанс php и так-же останется висеть до завершения, со всеми сопутствующими импликейшнами, и поэтому лучше через посредника, демона или что-то еще открывать его в отдельном. Соответствует ли это действительности, или я что-то не так понял?
просто сижу тют в тредже.
а, он же не из пхп запускает
Дополню только, что это сторонняя и неофициальная конфа, созданная кем-то из посетителей треда. Заходите туда на свой страх и риск!
Дополню что в конфе 2-3 сообщения в неделю.
Какие столбцы должны быть в БД в таблице относящейся к файлу?
Как это сделать ?
Использую DiDom
<?php
for ( $i = -40000; $i >= 0; $i = ($i + ($i * 0.03) + 1000) - 5000 )
{
echo $i . ' <br>';
}
Проебал что начал с отрицательного числа, переделал формулу
for ( $i = -40000; $i >= 0; $i = (($i + ($i * 0.03) + (-1000)) + 5000) )
{
echo $i . ' <br>';
}
Все равно выводит пустую страницу.
У тебя большой опыт?
Ну очевидно. В тяжелых фреймворках больше уровней абстракций и прочего говна. Там например путь при наследовании из 10 дочерних классов у какого-нибудь контроллера, потом еще всякие ди-контейнеры, в которые складываются всякие db, конфиг и прочее, объявляется куча переменных и зависимостей с константами, еще всякие десятки инклудов других файлов и прочее, и всё это для того, что бы ты просто мог увидеть стартовую страницу сайта. Самый быстрый скрипт - тот в котором написано только то, что от него требуется, без всяких классов и абстракций. Тупо на базовых типах, функциях и с использованием шустреньких алгоритмов с наименьшей сложностью.
поэтому я пишу на чистом пхп
>и среди них луший по этим показателям слим
а хуй там было
https://habr.com/company/nixsolutions/blog/329718/
>Время выполнения
>сколько нам нужно ожидать, чтобы получить ответ от сервера. Чем ниже это значение, тем лучше для нас
>php 7.1
>slim-3.0 0.662
>никого больше до секунды
ну сам же макнул себя.
там разбор идет по всем показателям
А вообще, этот тест сам по себе мало полезен. Если ты хочешь высокую производительность, бери Си++, Rust, Go, D, а не PHP. Задача фреймворка - облегчать разработку. Фреймворки там разные и может быть, что в более быстром не будет каких-то нужных тебе классов и тебе придется больше писать самому.
кони в вакууме
Потому что ты долбоёб и пишешь хуйню. Покури ещё как работает цикл for в php.
В цикле пишется условие продолжение цикла. Оно проверяется перед каждым шагом. У тебя условие $i >= 0 не выполняется уже на первом шаге и цикл не выполняется ни разу.
но надо не забывать что буквы могут оказаться в обратном порядке например строка "string"
Я так понял, вызываем функции и вводим 2 цифры - это два номера букв в строке, между которым надо отсчитать расстояние.
Далее цикл, который будет длится пока не закончатся все буквы в строках ( узнаем предварительно сколько букв в строке)
Далее, при условии, что если счетчик равен первой цифре, которую мы оказали, мы запоминаем эту цифру, и так же со второй цифрой.
А далее просто от второй отнимаем первую цифру. Это так?
Или стойте, это же БУКВЫ.
Есть переменная со строкой, мы вызываем функцию, куда записываем буквы из этих строк. И делаем тоже самое, только предварительно надо узнать под какой буквой какая цифра. Океей.
Я правильно мыслю? Тогда такой вопрос, скажите пожалуйста - вот допустим есть строка, есть буквы. Как мне узнать, за какой буквой какая цифра? Ну, т.е.
$a = "abvgd";
Я ввел strlen ($а);, знал что в той строке 5 букв, узнал.
Я ввел далее g, и... Погодите. Я запутался.
В общем обьясните, умоляю. Я максимально гуглить старался, но это пока выше моих познаний.
Учусь по базовому курсу для нулевых из шапки.
Не осилил твой поток мысли, но я бы делал по такому алгоритму:
1. Предполагаем из условия, что в строке как начальная, так и конечная буква встречаются только в единичном экземпляре.
2. Берем искомую строку, превращаем её в массив символов как учила оп-тян это намек, что если не знаешь как превратить, то смотри её уроки по строкам http://archive-ipq-co.narod.ru/l1/strings.html
3. Далее перебираем наш массив в цикле, когда нам встречается начальная буква, мы запоминаем её номер в какую-нибудь переменную, например $start, когда встречается наша конечная, её номер тоже запоминаем в другую переменную, например $finish.
4. Высчитываем искомое число отняв $start от $finish.
5. Возвращаем значение.
На примере без кода ты будешь примерно такими значениями оперировать:
$string = 'qwerty';
допустим ищем разницу между w и t
2...
$array = [0=>'q', 1=>'w', 2=>'e', 3=>'r', 4=>'t', 5=>'y'];
3...
$start = 1;
$finish = 4;
4...
$interval = 3;
5...
return 3;
Задача: написать вью каунтер в 2018 году.
Решение: каждый раз при отдаче отрендеренной страницы инкрементим редис по ключу с названием и id объекта. Объединяем всё в тэг, каждый раз считаем сумму и, по достижению суммы всех просмотров, скажем, сотни, ставим таск на инсерт в базу. В базу инсертим просмотры с нужными параметрами, временем и тд в отдельную таблицу, с которой морфичечски соединяем все объекты с вью-каунтерами. При просмотре объекта юзеру отдаём каунт из бд + значение из редиса.
Все правильно делаю? Какие тут могут быть подводные?
А, ещё второй вариант: каждый раз ставить таск с дилеем, скажем, в 5 минут, который собирает просмотры из редиса и заносит в бд. Если таск стоит в очереди, то не ставим. По сути типа крон, но "по запросу".
Куча бесполезного кода для их обработки и формирования.
Ни одного явного доказательства влияния на ранг при поиске.
Полтора человека которые пишут или читают урлы в адресной строке.
Почему же куча кода? Банально добавляешь в таблицу поле slug + делаешь одну универсальную функцию генерации слуга.
Да это чепуха. А вот когда начинается "а вот если у нас такой раздел то пусть будут такие а если этот раздел то в обратном порядке, а в этом разделе кароче вообще по другому вот здорово то а!"...
Ты думаешь меня там кто-то учил? Первый месяц, кстати, бесплатно работал, а следующую прибавку через пол года обещали
Задачка "Клавиша shift" на проверку.
http://sandbox.onlinephpfunctions.com/code/4faccec74928ab79c28fbd437464ebededfb2cff
>А http://phpclub.tech/ временно не работает или проект накрылся?
Нам хостинг отключили, предположительно за NSFW картинки, которые были спаршены с 2ч. Скоро восстановим.
>NSFW
Ого, кто-то за этим следит? Хуясе вахтеры.
У меня проблема! В жаваскрипте:
let x = 0;
setInterval(()=>{ x += (10 / 100 2) }, 1000}
выдает значения 1.2 1.4 а затем бред — 1.599999999 1.79999999998 и т.д.
Штоэта? Как бороться?
Пробовал
x += Number((10 / 100 2).toFixed(2))
но не помогает.
Ну вы поняли, там *
Тебе надо обязательно почитать про тип float и представление в компьютере дробных чисел (стандарт IEEE754).
Представь, что у тебя есть фиксированное число ячеек памяти (например, 10 ячеек), в каждую из которых можно поместить ровно одну цифру. И ты хочешь с помощью этих 10 ячеек представить как можно больший диапазон чисел, включая дробные. Как это можно сделать?
Самое простое - это число с фиксированной запятой. Пусть у нас между 5-й и 6-й ячейкой стоит воображаемая запятая. И если у нас есть последовательность 12345 67890, то мы интерпретируем ее как число
12345,67890
Этот подход имеет свои плюсы (простота), но и минусы: мы не можем представить таким образом числа меньше 0,00001 или больше 99999,99999 (чуть менее 100 000). Для расширения диапазона необходимо выделять больше ячеек памяти. Однако, такой подход использовался в древних калькуляторах и компьютерах. Конечно, там могло быть не 10 ячеек, или запятая могла стоять в другом месте, но принцип тот же.
Потому был придуман другой способ: число с плавающей запятой. Разобьем наши 10 ячеек на 2 группы, допустим группу из 2 и из 7 ячеек, а в оставшуюся 10-ю ячейку мы будем класть знак - плюс или минус:
+ 12 3456789
Назовем первую группу + 12 "экспонентой", а вторую 3456789 - "мантиссой". Пусть перед мантиссой стоит воображаемая запятая. И договоримся, что экспонента показывает, на сколько разрядов надо сдвинуть запятую вправо относительно начального положения (если экспонента отрицательная, со знаком минус, то запятая сдвигается влево, а не вправо). Например, +12 значит, что запятую надо сдвинуть на 12 символов вправо, дополняя число нулями при нехватке цифр:
имеем мантиссу 0,3456789 и экспоненту 12, сдвигаем запятую и получаем итоговое число:
345678900000,0
А в таком числе:
-02 1234567
Запятая сдвигается на 2 символа влево, дополняя пустые места нулями, и получается
0,001234567
Если обозначить экспоненту как E, а мантиссу как M, то мы можем записать правило восстановления числа в виде формулы N = 0,M x (10 ^ E).
При таком подходе минимальное число, которое мы можем представить, будет -99 0000001 = 0, ... 99 нулей ... 0000001, а максимальное x 99 9999999 = 999 999 900 ... и еще 90 нулей. Как видишь, используя те же 10 ячеек, мы смогли представить огромный диапазон чисел, примерно от ~10^-106 (10 в минус 106 степени, очень крошечное число) до ~10^100 (очень огромное число).
Но этот подход имеет и недостаток - так как у нас под мантиссу выделено всего 7 цифр, то оставшиеся цифры теряются. Например, число 123456789 будет сохранено как
+09 1234568
И при восстановлении получится 123456800,0 что отличается на 11 от исходного числа. Сохраняются только несколько первых значащих цифр. То есть наш формат сохраняет числа не точно, а с погрешностью.
Единственное отличие чисел стандарта IEEE754 от описанного мной варианта отличается в том, что там используются двоичные числа (состоящие только из цифр 0 или 1) и 32 ячейки. То есть 32 бита, каждый из которых равен 0 или 1.
Это и ответ на твой вопрос. 1,4 + 0,2 дает 1,6 которое при переводе в двоичную систему счисления представляет бесконечную дробь: 1,6 = 1,1 0011 0011 0011 0011.... Из-за того, что дробь бесконечная, а размер мантиссы конечный - 24 бита, получается не ровно 1,6, а 1,5999999 что отличается от 1,6 совсем немного (примерно на 0,00000001) и в принципе является тем же числом.
Сконвертировать число из 10-ного вида в формат IEEE754: https://www.h-schmidt.net/FloatConverter/IEEE754.html
Почитать про дробные числа:
- https://habr.com/post/112953/
Кстати, из-за этой особенности (погрешность при сохранении) нельзя сравнивать дробные числа с помощью a == b. Не хочешь подумать, над "правильным" алгоритмом сравнения дробных чисел, с учетом того, что они хранятся неточно и в результате операций может накапливаться небольшая погрешность в последнем разряде?
Тебе надо обязательно почитать про тип float и представление в компьютере дробных чисел (стандарт IEEE754).
Представь, что у тебя есть фиксированное число ячеек памяти (например, 10 ячеек), в каждую из которых можно поместить ровно одну цифру. И ты хочешь с помощью этих 10 ячеек представить как можно больший диапазон чисел, включая дробные. Как это можно сделать?
Самое простое - это число с фиксированной запятой. Пусть у нас между 5-й и 6-й ячейкой стоит воображаемая запятая. И если у нас есть последовательность 12345 67890, то мы интерпретируем ее как число
12345,67890
Этот подход имеет свои плюсы (простота), но и минусы: мы не можем представить таким образом числа меньше 0,00001 или больше 99999,99999 (чуть менее 100 000). Для расширения диапазона необходимо выделять больше ячеек памяти. Однако, такой подход использовался в древних калькуляторах и компьютерах. Конечно, там могло быть не 10 ячеек, или запятая могла стоять в другом месте, но принцип тот же.
Потому был придуман другой способ: число с плавающей запятой. Разобьем наши 10 ячеек на 2 группы, допустим группу из 2 и из 7 ячеек, а в оставшуюся 10-ю ячейку мы будем класть знак - плюс или минус:
+ 12 3456789
Назовем первую группу + 12 "экспонентой", а вторую 3456789 - "мантиссой". Пусть перед мантиссой стоит воображаемая запятая. И договоримся, что экспонента показывает, на сколько разрядов надо сдвинуть запятую вправо относительно начального положения (если экспонента отрицательная, со знаком минус, то запятая сдвигается влево, а не вправо). Например, +12 значит, что запятую надо сдвинуть на 12 символов вправо, дополняя число нулями при нехватке цифр:
имеем мантиссу 0,3456789 и экспоненту 12, сдвигаем запятую и получаем итоговое число:
345678900000,0
А в таком числе:
-02 1234567
Запятая сдвигается на 2 символа влево, дополняя пустые места нулями, и получается
0,001234567
Если обозначить экспоненту как E, а мантиссу как M, то мы можем записать правило восстановления числа в виде формулы N = 0,M x (10 ^ E).
При таком подходе минимальное число, которое мы можем представить, будет -99 0000001 = 0, ... 99 нулей ... 0000001, а максимальное x 99 9999999 = 999 999 900 ... и еще 90 нулей. Как видишь, используя те же 10 ячеек, мы смогли представить огромный диапазон чисел, примерно от ~10^-106 (10 в минус 106 степени, очень крошечное число) до ~10^100 (очень огромное число).
Но этот подход имеет и недостаток - так как у нас под мантиссу выделено всего 7 цифр, то оставшиеся цифры теряются. Например, число 123456789 будет сохранено как
+09 1234568
И при восстановлении получится 123456800,0 что отличается на 11 от исходного числа. Сохраняются только несколько первых значащих цифр. То есть наш формат сохраняет числа не точно, а с погрешностью.
Единственное отличие чисел стандарта IEEE754 от описанного мной варианта отличается в том, что там используются двоичные числа (состоящие только из цифр 0 или 1) и 32 ячейки. То есть 32 бита, каждый из которых равен 0 или 1.
Это и ответ на твой вопрос. 1,4 + 0,2 дает 1,6 которое при переводе в двоичную систему счисления представляет бесконечную дробь: 1,6 = 1,1 0011 0011 0011 0011.... Из-за того, что дробь бесконечная, а размер мантиссы конечный - 24 бита, получается не ровно 1,6, а 1,5999999 что отличается от 1,6 совсем немного (примерно на 0,00000001) и в принципе является тем же числом.
Сконвертировать число из 10-ного вида в формат IEEE754: https://www.h-schmidt.net/FloatConverter/IEEE754.html
Почитать про дробные числа:
- https://habr.com/post/112953/
Кстати, из-за этой особенности (погрешность при сохранении) нельзя сравнивать дробные числа с помощью a == b. Не хочешь подумать, над "правильным" алгоритмом сравнения дробных чисел, с учетом того, что они хранятся неточно и в результате операций может накапливаться небольшая погрешность в последнем разряде?
Мне кажется, нет. Было бы правильно создать отдельный метод setQuestion для заполнения массива вопросов, а в него уже передавать вопросы в виде многомерного массива и создать метод getQuestions, который возвращает все массивы. Таким образом, происходит разделение данных и логики (я имею в виду, что данные не вводятся вручную, а передаются в специальную функцию обработчик, который является прослойкой для них. Чем это лучше? Тем, что мы абстрагируемся от способа хранения) и уменьшается количество повторяемого кода.
Но это имхо.
Я ничего не понял, но благодарю сильно за ответ. Тут другое дело пока. Я сейчас делаю по гайду
Основы ООП. Я пытаюсь создать класс с вопросами и ответами, но у меня ничего не получается. https://ideone.com/D3eBq8
Откуда взялся $letter ?
Уже три часа бьюсь над загадкой. То ругается что Notice: Undefined variable: text in M:\Soft\xampp\htdocs\test.php on line 15
на строках где функцией создаю вопросы, то циклом не показывает варианты ответов.
Я долбоеб, ошибка найдена, прошу прощения.
Решил в итоге так:
https://learn.javascript.ru/number#неточные-вычисления
let x = 0;
setInterval(()=>{
x = (+(x + (10 / 100 • 2)).toFixed(2));
}, 1000);
>>188015
Говорили, не нужна математика программировании, ага...
Это конец, всегда еле вытягивал матишу, а она и тут достала, эх.
>Это и ответ на твой вопрос
Вопрос был в том числе "Как бороться?" :) Ответа я не нашел.
>"правильным" алгоритмом сравнения дробных чисел
В голову приходит только превратить обе части в строки и сравнить их. Но тут тоже могут быть какие-нибудь подводные камни.
При обучении программированию и в очень редких случаях математика нужна. На математической основе построено много обучающих задач по программированию. При этом математика выступает в роли чего то сверхпростого и тривиального а сложность в создании логического алгоритма.
Алсо:
>Поэтому в IEEE754 применяется правило округления до четного. Так, 12,5 будет округлено до 12, а 13,5 – до 14.
ОК. А как это обходят в ЯП? Используют округление от нуля?
https://ru.wikipedia.org/wiki/IEEE_754-2008#Округление_до_ближайшего
Раньше казалось, что математические операции и в программировании железобетонны и бесконечно надежны. Это же ужас:
>Деньги нельзя хранить в виде числа с плавающей запятой, т.к. в этом случае нельзя выделить значимые разряды. Если в языке программирования нет типов данных с фиксированной запятой, можно выйти из положения и хранить деньги в виде целого числа, подразумевая копейки (иногда доли копеек)
Можно поподробнее, пожалуйста? Я думаю подразумевается нечто вроде decimal(9,2) из sql? Тут явно указано 2 знака за запятой. А если хранишь все число, то потом сам выделяешь запятую с конца чтобы получить копейки?
Капча намекает на неточность.
Тред не читал, о чем речь не знаю, в почти всех задачах на округление нужно использовать % остаток от деления на 10 с цикличным умножением или делением с последующим округлением на 10
Не знаю, создай что-то вроде обыкновенной фабрики, но той, которая не возвращает объекты, а сохраняет их. Хотя, это больше похоже на шаблон Приспособленец.
interface QuestionFactory
{
public function setQuestion(string $name, int $points, array $answerd);
public function getQuestions() : array;
}
А использовать как-то так:
$factory = new QuestionFactory();
$factory->setQuestion("Вопрос", 666, array("Да", "Нет"));
$factory->setQuestion("Вопрос2", 666, array("Да", "Нет"));
$factory->setQuestion("Вопроc3", 666, array("Да", "Нет"));
$questions = $factory->getQuestions();
foreach($questions as $question) {
echo $question->text;
...
}
Хотя, использовать 3 параметра, это уже эребор и говорит о неправильной архитектуре.
Я, если что, не профи. Сам только познаю дзен. Пускай придет гуру и разрулит всё это.
Ой да зачем тут целую фабрику городить. Это если бы вопросы разных классов были тогда ладно еще.
Щас тут насоветуют начитанные знатоки велосипедеры.
При попытке сделать это через echo "<img src="../upload/$_GET[id].jpg">"; появляется ошибка, мол, что у тебя там за точки стоят придурок.
При попытке сделать это через echo '<img src="../upload/$_GET[id].jpg">'; ошибка исчезает, но уже ищется файл $_GET[id].jpg, т.к. из-за других кавычек переменная читается как текст.
Чо, собсна, делать?
Во-первых, так делать нельзя, $_GET прямо в HTML-код, это уязвимость, читай урок про XSS https://github.com/codedokode/pasta/blob/master/security/xss.md
Ты обязан отфильтровать полученные данные и гарантировать, например, что там только цифры.
Во-вторых, насчет кавычек, почитай правила обработки строк в кавычках в PHP внимательно и целиком http://php.net/manual/ru/language.types.string.php
В третьих, для расширения кругозора почитай еще про шаблоны: https://github.com/codedokode/pasta/blob/master/php/templates.md
Это гораздо лучше чем мучаться с echo и кавычками.
>echo "<img src="../upload/$_GET[id].jpg">";
как вариант обозначить для интерпретатора где точно начинается и заканчивается сложная переменная (массивы и объекты надо обмазывать в {} )
echo "<img src="../upload/{$_GET[id]}.jpg">";
>echo '<img src="../upload/$_GET[id].jpg">';
как вариант сделать через склеивание строк через точку
echo '<img src="../upload/' . $_GET[id]. '.jpg">';
>$_GET[id]
Еще у тебя тут скорее всего ошибка, и нужно будет выводить через
$_GET['id']
Если ты обращаешься к числовому ключу массива, то без кавычек - всё ок:
$test[1]
$test[0]
$test['234]
Но если он сам по себе строка, то через кавычки
$user['name']
$user['age']
...
А еще ты можешь не эхать весь html с помощью пхп, а писать обычный html код, а где нужно уже вставлять переменные с помощью пхп тегов: <?=$var?>
Например:
...
<div class="img>
<img src="../upload/<?=$_GET['id']?>.jpg">
</div>
...
>echo "<img src="../upload/$_GET[id].jpg">";
как вариант обозначить для интерпретатора где точно начинается и заканчивается сложная переменная (массивы и объекты надо обмазывать в {} )
echo "<img src="../upload/{$_GET[id]}.jpg">";
>echo '<img src="../upload/$_GET[id].jpg">';
как вариант сделать через склеивание строк через точку
echo '<img src="../upload/' . $_GET[id]. '.jpg">';
>$_GET[id]
Еще у тебя тут скорее всего ошибка, и нужно будет выводить через
$_GET['id']
Если ты обращаешься к числовому ключу массива, то без кавычек - всё ок:
$test[1]
$test[0]
$test['234]
Но если он сам по себе строка, то через кавычки
$user['name']
$user['age']
...
А еще ты можешь не эхать весь html с помощью пхп, а писать обычный html код, а где нужно уже вставлять переменные с помощью пхп тегов: <?=$var?>
Например:
...
<div class="img>
<img src="../upload/<?=$_GET['id']?>.jpg">
</div>
...
> echo '<img src="../upload/' . $_GET[id]. '.jpg">';
вот этот вариант рабочий.
> echo "<img src="../upload/{$_GET[id]}.jpg">";
а этот не рабочий
Но я бы сделал так:
?> <img src="../<?= $_GET['id'] ?>.jpg" /><?
Просто на практике выводить строку с хтмл часто геморно. Проще закрыть пхп, дальше писать хтмл с микро вставками пхп с выводом переменных, а затем снова открыть пхп.
>А еще ты можешь не эхать весь html с помощью пхп, а писать обычный html код, а где нужно уже вставлять переменные с помощью пхп тегов: <?=$var?>
Вот за это открытие большое человеческое спасибо. И в этой ситуации реально удобно, и ещё в паре мест лишние костыли выкину.
я на SimpleXML делал
> Сама информация в вопросах сохраняется в видимых или скрытых полях и отправляется на сервер как обычная форма. Проверку правильности надо сделать как на клиенте, яваскриптом, так и на сервере. Желательно, чтобы описания полей брались из одного источника, а не дублировались.
Как это сделать, есть какие-то готовые решения? Например для вопросов с одним вариантом ответа мне нужно сделать проверку, что хотя бы у одного из вариантов выставлено is_correct = true, как такую проверку не дублировать на фронте и бекенде? Формы симфони мне только дадут возможность использовать HTML5 валидацию, а как насчёт более сложных случаем? Я читал про JSON Schema, но не нашёл готовых пакетов. Велосипедить не хочу, такой велосипед потянет на отдельный пакет/бандл.
ОП, что посоветуешь для админки?
Если тебе нужно и там и там то не дублировать не получится.
JSON это просто яваскрипт объект. Тоесть ты можешь из пхп выводить хтмл а можешь выводить яваскрипт.
Например
<script>
var question = <?= json_encode($question) ?>;
</script>
Ну собственно выводить можно что угодно, хоть текст, хоть картинки, хоть файлы выдавать, я както пдф на лету генерировал.
Но в твоей задаче сказано что надо хранить данные в полях а не в json, тоесть тебе нужно при выводе страницы сделать hidden инпуты. Например скрытый инпут с номером правильного ответа.
Единственный смысл проверки на клиенте, это юзабилити. Чтобы пользователю быстрей показать результат. Но без такой же проверки на сервере было бы неправильно, т.к. все что на клиенте можно подменить. Хотя в данной задаче было бы нелепо показывать юзеру результат до проверки на сервере и ответа. Поэтому самое юзабильное решение это аякс.
Поясни
Аккуратненько.
<title>
<?php echo $title; ?>
</title>
Но вместо заголовка, содержащего значение переменной, появляется ошибка, мол $title не объявлена, хотя тот же самый <?php echo $title; ?> в самом body работает. Мне кажется, проблема в том, что <title> грузится раньше <body>, в котором объявляется переменная. Можно ли как-то устранить проблему? Объявить переменную не в <body> никак не получится.
Просто если немножко подумать то ты поймешь что переменную нельзя использовать до того как она объявлена. Это было бы не логично.
Хочется конечно взять и..., но я сдержусь, смотри дружище, тебе никто не мешает твой пхп код вставить вообще перед открывающим <html>
<?php
$title='ti';
$var1='pidor';
$closeHtml='</body></html>';
?>
<html>
<head>
....
<?=$title?>
</head>
<body>
<?=$var1?>
...
<?$closeHtml>
но на самом деле это всё залупа и хуйня, потому что лучше делай вот так - сначала в файле index.php, ты собственно пишешь пхп код и собираешь все переменные которые тебе нужны, потом ты пишешь в коде прям include "view.php"; в которой у тебя собственно лежит весь html с вставками переменных из пхп, это ты таким образом на самом простом уровне реализуешь подключение html шаблона в котором будет отделение, как бы выразиться попроще что бы тебе было яснее, фронтэнда от бэкэнда. Или отделение движка от отображения, или бизнес-логики от логики-представления - как тебе удобнее будет, так это и представляй.
Попробуй вот прям ща накидать себе два файла примерно таких index.php, view.php
index.php
<?
$title = "ti-huy";
$name = "naprimer";
$string = "ne pishi translitom v kode, ya delayu eto dla skorosti, no eto zashkvar";
include "view.php";
//end of index.php
view.php
<html>
<head>
<title><?=$title?></title>
</head>
<body>
<?=$string?>
</body>
</html>
и да, как ты мог заметить, вместо
<?php echo $title; ?>
лучше и быстрее использовать
<?=$title?>
Это компактная и удобная запись
Хочется конечно взять и..., но я сдержусь, смотри дружище, тебе никто не мешает твой пхп код вставить вообще перед открывающим <html>
<?php
$title='ti';
$var1='pidor';
$closeHtml='</body></html>';
?>
<html>
<head>
....
<?=$title?>
</head>
<body>
<?=$var1?>
...
<?$closeHtml>
но на самом деле это всё залупа и хуйня, потому что лучше делай вот так - сначала в файле index.php, ты собственно пишешь пхп код и собираешь все переменные которые тебе нужны, потом ты пишешь в коде прям include "view.php"; в которой у тебя собственно лежит весь html с вставками переменных из пхп, это ты таким образом на самом простом уровне реализуешь подключение html шаблона в котором будет отделение, как бы выразиться попроще что бы тебе было яснее, фронтэнда от бэкэнда. Или отделение движка от отображения, или бизнес-логики от логики-представления - как тебе удобнее будет, так это и представляй.
Попробуй вот прям ща накидать себе два файла примерно таких index.php, view.php
index.php
<?
$title = "ti-huy";
$name = "naprimer";
$string = "ne pishi translitom v kode, ya delayu eto dla skorosti, no eto zashkvar";
include "view.php";
//end of index.php
view.php
<html>
<head>
<title><?=$title?></title>
</head>
<body>
<?=$string?>
</body>
</html>
и да, как ты мог заметить, вместо
<?php echo $title; ?>
лучше и быстрее использовать
<?=$title?>
Это компактная и удобная запись
Да, все подтверждаю, лучше разделять, чтоб сначала вся бизнеслогика шла, а потом уже вывод с переменными. Но для начала если совсем нуб, то можно и в одном файле. Вверху логика, внизу вывод всей страницы.
Я год назад телефон разъебал, пусть это и была звонилка за тясячу, но всё равно жалко, он у меня долго был и много где с ним путешествовал, в общем хороший телефон, и до сих пор жалко, похуй что он дешевый, ведь не в цене дело, помню на работе когда ходил срать в толчек бизнес центра играл на нем в змейку, охуенно было отдохнуть от всей этой суеты и на 15 минут отвлечься.
А я не хотел отвлекаться. Я ебашил кислоту прям во время разработки. Код светился так охуенно.
Я тогда эникеем работал, это я ща уже 3 года на пхп хуярю всякое говно и не могу решить до сих пор решить как организовать взаиодействие классов в кошках-мышках и дубрировать ли переменные координат или нет, но вангую это не по моей тупости, а потому что передо мной в этой задаче стоят на самом деле одни из базовых вопросов геймдева, и если об этом думать, то сильно голова закипает, так о чем это я. Эникеем я обслуживал контору из 3 этащей в БЦ и сотней человек штата, и постоянно кому-то было что-то нужно и на ногах был 90% времени поэтому, и вот как раз съебать подальше ото всех было иногда прост необходимо.
Эйбля, представляю, я бы слабительное пил чтоб в параше весь день сидеть.
то же самое будет.
Браузер сам закоментил первую строку xml и проставил критические html теги.
Надо гуглить настройки браузера в этом плане.
К примеру, если я в системе создам переменную окружения FOO='foo', а в PHP задам $_ENV['FOO']='bar'; то какое значение получит консольное приложение запущенное с помощью PHP?
Нашел ответ проверив экспериментально:
Изменение переменной $_ENV - никак не отразилось на изменение переменных окружения.
Изменение глобальных переменных с помощью putenv("FOO=bar"); передалось и в консольное приложение, которое мы вызываем с помощь PHP.
В мануале по этой функции сказано:
>Переменная будет существовать только на время выполнения текущего запроса. По его завершении переменная вернется в изначальное состояние.
Из чего можно сделать вывод, что у каждого "консольного клиента", в том числе и PHP, свой набор переменных окружения.
В моем приложении есть серьёзная проблема.
Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения. Если сообщения были отправлены как раз в этот интервал (т.е. между вызовами скрипта), то эти сообщения не обновятся.
К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
Как можно добиться подобного поведения в PHP? Мне приходит на ум, что это асинхронное получение результатов из БД... То есть, если в БД появились новые сообщения, то скрипт триггерется.
Пхп не предназначен для постоянной работы.
Сделай чтобы при запросе сообщений передавалось не время ответа а время последнего полученного сообщения. Тогда ты сможешь отдать все сообщения после последнего отданного.
Good call
Сделал https://github.com/someApprentice/chat/commit/9bf70b7215bb924f508249fd347be3b6e1b4bfef
Теперь сообщения перестали пропадать
Осталось решить проблему с постоянной нагрузкой, которая меня не так раздражает, и я могу свободно решать другие проблемы
Спасибо
https://jsfiddle.net/e0Lk2a6v/
Скроллбар переместился вверх, но теперь можно скроллить влево-вправо колесиком мышки! Без JS. Без Shift-а.
Можно использовать, например, в элементах с фиксированной высотой-шириной, в которых нужна именно горизонтальная прокрутка.
У меня вот нашлось сразу идеальное место, куда применить. Мб кому полезно будет.
Попрошу Вас объясниться, мистер!
Это ты еще до паралакса не дошел
Уж лучше не джээс сделать - проматывать на ширину блока при прокрутке колёсика. Так неудобно, так как приходится туда-сюда крутить, чтобы на страничку попасть.
>>187793
Задачка Yoda speak на проверку
http://sandbox.onlinephpfunctions.com/code/bd50ad5b1ba78da4cac28247e93a098a77a1713e
К сожалению на ideone функции с приставкой mb_ не хотят исполняться.
В данном случае не обязательно. Нужны если выводишь элемент массива или свойство объекта.
Но лично я ставлю всегда, чтобы было везде одинаково
кнопка "сделать сайт"
https://ideone.com/5QSOQE
На сайте написано: Правильный ответ: чуть больше миллиона накопится примерно за 49 лет, соответственно вкладчику исполнится 65.
Но у меня к 65-летию у него ток 970к, а миллион будет на 66 лет.
А все, нашел ошибку. Пиздец я тупой.
Либо муисам гавно, либо я дебил.
Как может быть что
SELECT .. LIKE 'cat'
находит что нужно а
SELECT .. AGAINST ( 'cat' IN NATURAL LANGUAGE MODE )
или
SELECT .. AGAINST ( '+cat' IN BOOLEAN MODE )
НИЧЕГО?
SELECT .. AGAINST ( '"cat"' IN BOOLEAN MODE )
Ничего.
SELECT .. AGAINST ( 'cat*' IN BOOLEAN MODE )
что угодно кроме 'cat'.
Там где отрабатывает - порой крайне странно ранжирует.
Может он просто не рассчитан на текст из одного-двух слов?
p.s.: какие шансы уебать базу при апгрейде с MDB 5.5 на десятку?
Хотя зачем я сказал про canvas
>Если ты о скриншоте страницы, то это javascript и html5 canvas.
Да, я о странице. Я думал отправить запрос на сервер, получить html и как то сэмулировать браузер, но вот как это сделать, не знаю.
>Симулировать браузер на пхп.
Поверь, оно того не стоит.
Но, возможно есть какойнибуь сервис который это делает. Шлешь туда хтмл, получаешь в ответ картинку, теоритически. Ну для этого curl нужно юзать.
https://www.betaface.com/wpa/
>>189816
Канвас никак не позволяет делать скриншоты страниц. Для этого надо либо запускать управляемый браузер (headless/selenium), либо использовать сторонний сервис создания скриншотов.
>>189784
А ты можешь тут на http://sqlfiddle.com/ воссоздать ситуацию? Создать таблицу с парой-тройкой записей и проверить?
А то так трудно понять.
>>189690
10 000 р будет не в 17, а в 16 лет.
>>189575
Подробно объяснено в мануале http://php.net/manual/ru/language.types.string.php#language.types.string.parsing
Тут они не обязательны, но и вреда особого от них нет.
>>189509
> mb_convert_case($text, MB_CASE_LOWER, $encoding)
Можно просто mb_strtolower
Функция makeFirstletterUppercase читается не очень хорошо из-за обилия скобок. Лучше наверно так:
$firstLetter = mb_substr(...);
...
Кроме \K, можно было еще использовать assertions ( http://php.net/manual/en/regexp.reference.assertions.php ) которые не захватывают символы, вроде (?=[...])
Название переменной $word неудачное, можно подумать, что там всего одно слово, а там массив слов.
В остальном верно.
>>189816
Канвас никак не позволяет делать скриншоты страниц. Для этого надо либо запускать управляемый браузер (headless/selenium), либо использовать сторонний сервис создания скриншотов.
>>189784
А ты можешь тут на http://sqlfiddle.com/ воссоздать ситуацию? Создать таблицу с парой-тройкой записей и проверить?
А то так трудно понять.
>>189690
10 000 р будет не в 17, а в 16 лет.
>>189575
Подробно объяснено в мануале http://php.net/manual/ru/language.types.string.php#language.types.string.parsing
Тут они не обязательны, но и вреда особого от них нет.
>>189509
> mb_convert_case($text, MB_CASE_LOWER, $encoding)
Можно просто mb_strtolower
Функция makeFirstletterUppercase читается не очень хорошо из-за обилия скобок. Лучше наверно так:
$firstLetter = mb_substr(...);
...
Кроме \K, можно было еще использовать assertions ( http://php.net/manual/en/regexp.reference.assertions.php ) которые не захватывают символы, вроде (?=[...])
Название переменной $word неудачное, можно подумать, что там всего одно слово, а там массив слов.
В остальном верно.
Интересное решение. Хотя у меня на тачпаде например и так есть горизонтальная прокрутка, жаль, не все программы ее поддерживают и в линуксе ее нет.
>>189418
Надо смотреть сначала в документацию, потом в код.
Обрати внимание, что при использовании mass assignment из данных пользователя надо контролировать, каким полям можно присваивать значения, а каким - нет, иначе пользователь может изменить какие-то служебные поля: https://laravel.com/docs/5.6/eloquent#mass-assignment
В документации ничего не сказано, посмотрим в код метода create():
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L1514
- https://github.com/illuminate/database/blob/master/Eloquent/Builder.php#L788
Он вызывает $this->newModelInstance($attributes); и затем $instance->save();
которая вызывает model->newInstance($attributes)
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L282
Эта функция передает атрибуты в конструктор, а тот передает их в fill():
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L212
Далее вызывается:
> $key = $this->removeTableFromKey($key);
> ...
> $this->setAttribute($key, $value);
removeTableFromKey() удаляет все, что идет до точки, если она есть: https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L271
setAttribute() описана тут: https://github.com/illuminate/database/blob/master/Eloquent/Concerns/HasAttributes.php#L533
Она сохраняет переданные данные:
> $this->attributes[$key] = $value;
Вернемся к save(). Она вызывает в итоге https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L666
А та уже https://github.com/illuminate/database/blob/master/Query/Builder.php#L2336
В общем, я думаю, что значения элементов массива экранируются. Насчет ключей - на 100% не уверен, что там нет чего-нибудь этакого.
Интересное решение. Хотя у меня на тачпаде например и так есть горизонтальная прокрутка, жаль, не все программы ее поддерживают и в линуксе ее нет.
>>189418
Надо смотреть сначала в документацию, потом в код.
Обрати внимание, что при использовании mass assignment из данных пользователя надо контролировать, каким полям можно присваивать значения, а каким - нет, иначе пользователь может изменить какие-то служебные поля: https://laravel.com/docs/5.6/eloquent#mass-assignment
В документации ничего не сказано, посмотрим в код метода create():
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L1514
- https://github.com/illuminate/database/blob/master/Eloquent/Builder.php#L788
Он вызывает $this->newModelInstance($attributes); и затем $instance->save();
которая вызывает model->newInstance($attributes)
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L282
Эта функция передает атрибуты в конструктор, а тот передает их в fill():
- https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L212
Далее вызывается:
> $key = $this->removeTableFromKey($key);
> ...
> $this->setAttribute($key, $value);
removeTableFromKey() удаляет все, что идет до точки, если она есть: https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L271
setAttribute() описана тут: https://github.com/illuminate/database/blob/master/Eloquent/Concerns/HasAttributes.php#L533
Она сохраняет переданные данные:
> $this->attributes[$key] = $value;
Вернемся к save(). Она вызывает в итоге https://github.com/illuminate/database/blob/master/Eloquent/Model.php#L666
А та уже https://github.com/illuminate/database/blob/master/Query/Builder.php#L2336
В общем, я думаю, что значения элементов массива экранируются. Насчет ключей - на 100% не уверен, что там нет чего-нибудь этакого.
> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.
Гугление находит:
1) https://github.com/BitOne/php-meminfo
2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.
Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686
> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.
Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.
> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.
Таймаут нужен, так как браузер, а также промежуточные прокси или NAT-сервера могут прибивать "подвисшие" соединения, по которым ничего не передается. Потому обычно его ставят в пределах 20-60 секунд.
Опишем задачу: у нас есть один процесс (или поток) на сервере, который обрабатывает соединение от клиента (wait.php) и "висит" в ожидании уведомлений для него или в ожидании таймаута - subscriber. И есть второй процесс/поток, который добавляет новое сообщение в БД и хочет как-то уведомить все ждущие потоки первого типа о появлении нового сообщения - publisher. Такая архитектура еще называется publish/subscribe (pub/sub) - есть те, кто "публикует" сообщения, и есть те, кто "подписывается" на их получение.
Чтобы понять, кто какое уведомление ждет, введем "идентификатор канала" - channelId. Подписчик подписывается на сообщения с определенным channelId. Наша система лишь уведомляет об изменениях, она не передает их содержание, подписчик должен сам извлечь новые сообщения из базы данных.
channelId может, например, просто равняться userId пользователя, которому предназначено сообщение.
Реализуется это в общем за счет межпроцессного (межпоточного) взаимодействия, IPC: https://ru.wikipedia.org/wiki/Межпроцессное_взаимодействие
Это набор "примитивов", которые реализуются по-разному в зависимости от ситуации, используемой ОС и тд. При этом не очень принципиально, о чем идет речь - о потоках, о "зеленых" тредах (асинхронное программирование), о процессах или даже о запущенных на разных машинах процессах. Я буду писать просто "поток".
Если посмотреть на примитивы IPC, то для нашей задачи подошли бы, например, семафоры ( https://ru.wikipedia.org/wiki/Семафор_(информатика) ). Семафор умеет блокироваться до того, как его "отпустят". Вот, как может выглядеть код:
// Создаем доступную всем потокам таблицу семафоров. Она имеет структуру
//
// {
// channel1: [ semaphore1, semaphore2, semaphore3 ],
// channel2: [ semaphore ],
// ....
// }
//
// Каждый подписчик создает и кладет в таблицу свой семафор под нужным ему channelId.
// На одном канале может быть несколько подписчиков.
//
var semaphores = {};
// Функция для потока-подписчика, она ждет публикации
// сообщения с данным channelId неограниченное время
function waitForMessages(channelId) {
// создаем семафор, который блокируется при попытке захвата
var semaphore = initSemaphore(0);
// добавляем его в список
if (!semaphores[channelId]) {
semaphores[channelId] = [];
}
semaphores[channelId].push(semaphore);
// блокируемся до появления события
// (так как семафор имеет значение 0)
semaphore.enter();
// получено новое сообщение, удаляем не нужный более семафор
// из массива
removeFromArray(semaphores[channelId], semaphore);
semaphore.destroy();
}
// Функция для издателя, "будит" все потоки, ждущие сообщения
// с данным channelId
function publishMessage(channelId) {
// Находим все ждущие семафоры и "отпускаем" их
if (!semaphores[channelId]) {
return;
}
semaphores[channelId].forEach(function (semaphore) {
// Отпускаем семафор и позволяем ждущему на нем потоку
// захватить его в enter()
semaphore.leave();
});
}
В этом коде я использовал особенность Node.JS, что она однопоточная и функция выполняется целиком, потому нам не надо беспокоиться о гонках между потоками (например, в процессе выполнения forEach никто не добавляет или удаляет элементы из массива). В других языках могут потребоваться дополнительные блокировки во время работы с разделяемым массивом semaphores.
Также, я не реализовал таймаут ожидания уведомления - оставим это как домашнее задание читателю, например, можно "отпускать" семафор в подписчике примерно таким кодом:
setTimeout(function () {
semaphore.leave();
}, timeout);
Это усложнит код, так как при срабатывании семафора до таймаута желательно уничтожать таймаут, чтобы освободить ресурсы.
Также, приведенный выше код требует, чтобы из всех потоков была доступна переменная semaphores и потому работает только внутри одного процесса Node.JS. Это лишь демонстрация архитектуры pub/sub.
Если ты хочешь реализовать pubsub между разными процессами, или тем более, разными машинами, этот подход не подойдет и проще использовать брокер (брокер = посредник при сделке) - специальный процесс, реализующий паттерн pubsub. Брокер является сервером (например, TCP-сервером), к нему подсоединяются клиенты-подписчики, ждущие новых сообщений, и клиенты-издатели, желающие уведомлять подписчиков.
Например, есть такие варианты:
- pubsub встроен в сервер redis: https://redis.io/topics/pubsub который может служить брокером
- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
- написать свой брокер на Node.JS с использованием готовых библиотек
- написать на Го
- сервер очередей RabbitMQ: https://ru.wikipedia.org/wiki/RabbitMQ - он, кстати, поддерживает HTTP и возможно, подписчик может подсоединяться напрямую с клиента
- сервер очередей ZeroMQ
Есть и другие брокеры. Есть так же более-менее стандартные протоколы MQTT и AMPQ, чтобы для разных брокеров можно было использовать общие клиентские библиотеки.
Ты можешь использовать pubsub внутри одного процесса (например, иметь единый процесс на ReactPHP, который обслуживает все соединения и который внутри содержит брокер, например, встроенный в WAMP) или использовать отдельный, внешний брокер. В этом варианте подписчики соединяются с брокером в ожидании уведомлений, а издатели подсоединяются к брокеру для публикации уведомления.
В случае внешнего брокера, есть опять же 2 варианта:
- браузер подсоеиняется к процессу PHP, который соединяется с брокером
- браузер напрямую подсоединяется к брокеру, если он поддерживает HTTP и такой режим доступа
Браузер может использовать long polling или websocket в зависимости от условий и реализации. Если используется websocket и протокол WAMP, то в него уже встроена возможность подписываться на каналы, и клиентская JS-библиотека с нужными функциями - сервер должен ее только реализовать. Вот пример такой клиентской библиотеки: https://github.com/crossbario/autobahn-js
В плане масштабирования есть такие варианты:
- использовать единый внешний брокер сообщений (готовый или самописный) - это годится для маленьких масштабов.
- использовать много брокеров, составить соответствие между channelId и IP/портом брокера и подсоединяться к нужному - это неограниченно масштабируемая схема.
Надеюсь, я не слишком запутал? Задавай уточняющие вопросы тогда.
> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.
Гугление находит:
1) https://github.com/BitOne/php-meminfo
2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.
Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686
> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.
Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.
> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.
Таймаут нужен, так как браузер, а также промежуточные прокси или NAT-сервера могут прибивать "подвисшие" соединения, по которым ничего не передается. Потому обычно его ставят в пределах 20-60 секунд.
Опишем задачу: у нас есть один процесс (или поток) на сервере, который обрабатывает соединение от клиента (wait.php) и "висит" в ожидании уведомлений для него или в ожидании таймаута - subscriber. И есть второй процесс/поток, который добавляет новое сообщение в БД и хочет как-то уведомить все ждущие потоки первого типа о появлении нового сообщения - publisher. Такая архитектура еще называется publish/subscribe (pub/sub) - есть те, кто "публикует" сообщения, и есть те, кто "подписывается" на их получение.
Чтобы понять, кто какое уведомление ждет, введем "идентификатор канала" - channelId. Подписчик подписывается на сообщения с определенным channelId. Наша система лишь уведомляет об изменениях, она не передает их содержание, подписчик должен сам извлечь новые сообщения из базы данных.
channelId может, например, просто равняться userId пользователя, которому предназначено сообщение.
Реализуется это в общем за счет межпроцессного (межпоточного) взаимодействия, IPC: https://ru.wikipedia.org/wiki/Межпроцессное_взаимодействие
Это набор "примитивов", которые реализуются по-разному в зависимости от ситуации, используемой ОС и тд. При этом не очень принципиально, о чем идет речь - о потоках, о "зеленых" тредах (асинхронное программирование), о процессах или даже о запущенных на разных машинах процессах. Я буду писать просто "поток".
Если посмотреть на примитивы IPC, то для нашей задачи подошли бы, например, семафоры ( https://ru.wikipedia.org/wiki/Семафор_(информатика) ). Семафор умеет блокироваться до того, как его "отпустят". Вот, как может выглядеть код:
// Создаем доступную всем потокам таблицу семафоров. Она имеет структуру
//
// {
// channel1: [ semaphore1, semaphore2, semaphore3 ],
// channel2: [ semaphore ],
// ....
// }
//
// Каждый подписчик создает и кладет в таблицу свой семафор под нужным ему channelId.
// На одном канале может быть несколько подписчиков.
//
var semaphores = {};
// Функция для потока-подписчика, она ждет публикации
// сообщения с данным channelId неограниченное время
function waitForMessages(channelId) {
// создаем семафор, который блокируется при попытке захвата
var semaphore = initSemaphore(0);
// добавляем его в список
if (!semaphores[channelId]) {
semaphores[channelId] = [];
}
semaphores[channelId].push(semaphore);
// блокируемся до появления события
// (так как семафор имеет значение 0)
semaphore.enter();
// получено новое сообщение, удаляем не нужный более семафор
// из массива
removeFromArray(semaphores[channelId], semaphore);
semaphore.destroy();
}
// Функция для издателя, "будит" все потоки, ждущие сообщения
// с данным channelId
function publishMessage(channelId) {
// Находим все ждущие семафоры и "отпускаем" их
if (!semaphores[channelId]) {
return;
}
semaphores[channelId].forEach(function (semaphore) {
// Отпускаем семафор и позволяем ждущему на нем потоку
// захватить его в enter()
semaphore.leave();
});
}
В этом коде я использовал особенность Node.JS, что она однопоточная и функция выполняется целиком, потому нам не надо беспокоиться о гонках между потоками (например, в процессе выполнения forEach никто не добавляет или удаляет элементы из массива). В других языках могут потребоваться дополнительные блокировки во время работы с разделяемым массивом semaphores.
Также, я не реализовал таймаут ожидания уведомления - оставим это как домашнее задание читателю, например, можно "отпускать" семафор в подписчике примерно таким кодом:
setTimeout(function () {
semaphore.leave();
}, timeout);
Это усложнит код, так как при срабатывании семафора до таймаута желательно уничтожать таймаут, чтобы освободить ресурсы.
Также, приведенный выше код требует, чтобы из всех потоков была доступна переменная semaphores и потому работает только внутри одного процесса Node.JS. Это лишь демонстрация архитектуры pub/sub.
Если ты хочешь реализовать pubsub между разными процессами, или тем более, разными машинами, этот подход не подойдет и проще использовать брокер (брокер = посредник при сделке) - специальный процесс, реализующий паттерн pubsub. Брокер является сервером (например, TCP-сервером), к нему подсоединяются клиенты-подписчики, ждущие новых сообщений, и клиенты-издатели, желающие уведомлять подписчиков.
Например, есть такие варианты:
- pubsub встроен в сервер redis: https://redis.io/topics/pubsub который может служить брокером
- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
- написать свой брокер на Node.JS с использованием готовых библиотек
- написать на Го
- сервер очередей RabbitMQ: https://ru.wikipedia.org/wiki/RabbitMQ - он, кстати, поддерживает HTTP и возможно, подписчик может подсоединяться напрямую с клиента
- сервер очередей ZeroMQ
Есть и другие брокеры. Есть так же более-менее стандартные протоколы MQTT и AMPQ, чтобы для разных брокеров можно было использовать общие клиентские библиотеки.
Ты можешь использовать pubsub внутри одного процесса (например, иметь единый процесс на ReactPHP, который обслуживает все соединения и который внутри содержит брокер, например, встроенный в WAMP) или использовать отдельный, внешний брокер. В этом варианте подписчики соединяются с брокером в ожидании уведомлений, а издатели подсоединяются к брокеру для публикации уведомления.
В случае внешнего брокера, есть опять же 2 варианта:
- браузер подсоеиняется к процессу PHP, который соединяется с брокером
- браузер напрямую подсоединяется к брокеру, если он поддерживает HTTP и такой режим доступа
Браузер может использовать long polling или websocket в зависимости от условий и реализации. Если используется websocket и протокол WAMP, то в него уже встроена возможность подписываться на каналы, и клиентская JS-библиотека с нужными функциями - сервер должен ее только реализовать. Вот пример такой клиентской библиотеки: https://github.com/crossbario/autobahn-js
В плане масштабирования есть такие варианты:
- использовать единый внешний брокер сообщений (готовый или самописный) - это годится для маленьких масштабов.
- использовать много брокеров, составить соответствие между channelId и IP/портом брокера и подсоединяться к нужному - это неограниченно масштабируемая схема.
Надеюсь, я не слишком запутал? Задавай уточняющие вопросы тогда.
Вполне себе предназначен - ReactPHP.
>>189406
Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным).
>>189281
Массив $_ENV это лишь копия переменных, доступных в момент запуска PHP скрипта. Его изменение ни на что не влияет. Ты можешь изменить переменные окружения процесса (для текущего процесса, которые будут наследоваться запущенными из него процессами) с помощью putenv(). А получить текущие значения с помощью getenv().
Кстати, в некоторых конфигурациях $_ENV вообще пуст. Его заполнение включается где-то в php.ini.
> Из чего можно сделать вывод, что у каждого "консольного клиента", в том числе и PHP, свой набор переменных окружения.
У каждого процесса в ОС своя копия переменных. Она копируется от родителя в момент создания процесса, но это именно независимая копия.
>>189148
Она унаследует переменные процесса PHP в момент запуска внешней программы. $_ENV ни на что не влияет.
Неапрва. Я когдато гуглил. Канвас нужен чтобы получить достутп к графическому апи html5 на странице, а с его помощью можно сделать снимок внутри произвольной области внутри любого элемента.
>ReactPHP
Ебать извращение.
Это работает с большими ограничениями, если верить статье https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Drawing_DOM_objects_into_a_canvas и не получится это использовать для рендеринга произвольного стороннего сайта.
Пыха изначально разрабатывалась для http.
Тоесть: Запрос -> Выполнение -> Ответ.
Я конечно не знаю всех тонкостей как там оно внутри устроено. Но мне кажется что попытки использовать этот язык под что то большее реализуются с помощью шаманского бубна у костра под циганские танцы и путем внедрения неимоверных костылей.
А так конечно, я даже видел среду разработки десктоп-приложений на php.
>>>1190032
>Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным)
Ебашишь простыни и не видишь что то же самое уже предложено. Мамкин умник.
Тебе именно что кажется. По твоей логике, JS разрабатывался для использования в браузере и на сервере ему делать нечего.
Нет, яваскрипт разрабатывался не для браузера.
Кстати по поводу библиотеки mbstring в ideone, я же им писал две недели назад.
Видимо у них пока нет времени на это. А тебе спасибо за доброе дело!
Благодаря ошибкам на ideone я наконец разобрался с openserver, теперь гораздо удобнее проверять свои поделки. Но судя по треду лучшее решение это установить lamp на сервер и подключаться к нему, что скажете?
Open server достаточно. 7 лет на нем, никаких проблем.
>> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
>
>Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.
>
>Гугление находит:
>
>
>2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.
>
>Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686
Надо было уточнить, что потребление памяти со стороны клиента, т.е. браузера. Которая происходит, как я предполагаю, потому что, с поступлением новой информации из постоянных обновлений, растёт и объем памяти в браузере, который их зачем-то туда сохраняет.
>> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.
>
>Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.
Или таймстамп. Но зачем возвращать время для следующего запроса, в клиенте можно же самому выставить интервал?
>> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
>> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
>
>Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.
Значит нужно поскорей переходить на вебсокет...
>Надеюсь, я не слишком запутал? Задавай уточняющие вопросы тогда.
Прежде чем разобраться межпроцессным взаимодействием, нужно разобраться как работают процессы.
Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?
Если виртуальный сервер, это один единственный процесс, то я могу предположить, что значит уже сам PHP создает "дерево" из нескольких процессов, это так?
В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом. Я представлял, что приложение по цепочке вызывает все зависимости и в итоге становиться одним целым, т.е. я не думал что на каждую зависимость создаётся свой процесс. Неужели это действительно так?
Чтобы понять как работает паттерн pub/sub, я заглянул на вики и там обнаружил, что
>Шаблон издатель-подписчик представляет собой расширение шаблона наблюдатель, в который добавлено описание канала событий (англ. event channel), специально предназначенного для оповещения о событиях.
То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?
>>190032
>Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным).
А клиент не пользуется этим временем, он просто пересылает его обратно PHP. Однако, я вижу аргумент за использование ID вместо даты - так надёжнее.
>> К тому же, ещё есть большая проблема с потреблением памяти, со временем, если оставить приложение открытом, то, спустя какое-то время, вызов скриптов загрузит всю память. У меня дошло до ~1.4Гб за менее чем пол часа.
>
>Нужно искать место утечки памяти. В идеале, должен быть какой-то инструмент, который меряет число выделений/освобождений памяти, находит неосвобожденные куски и пишет стектрейсы, где они были выделены.
>
>Гугление находит:
>
>
>2) Использовать режим trace в xdebug, который пишет в лог каждый вызов функции и потребление памяти в этот момент. Как-то проанализировать лог в поисках функций, при вызове которых потребление только растет.
>
>Что-то еще я нагуглил тут: https://stackoverflow.com/questions/16068301/how-to-find-which-php-script-is-leaking-memory/36288686#36288686
Надо было уточнить, что потребление памяти со стороны клиента, т.е. браузера. Которая происходит, как я предполагаю, потому что, с поступлением новой информации из постоянных обновлений, растёт и объем памяти в браузере, который их зачем-то туда сохраняет.
>> Обновление сообщений и контактов происходит вызовом скрипта c определённым интервалом и возвратом времени когда этот скрипт был вызван, чтобы получать из БД только новые на текущий момент сообщения.
>
>Скрипт на сервере должен возвращать время для следующего запроса. Тут стоит еще помнить, что время может быть разным на клиенте/сервере и лучше использовать в запросе полученное от сервера же время.
Или таймстамп. Но зачем возвращать время для следующего запроса, в клиенте можно же самому выставить интервал?
>> В популярной соц.сете, эта проблема решается следующим образом, добавляется параметр wait и ответ поступает только спустя это время. Но, если собеседник отправляет сообщение, то этот скрипт вызывается мгновенно.
>> Я не понимал как это возможно, пока не написал это сообщение, и мне пришла идея, что когда кто-то отправляет сообщение, то сервер перестает ждать и отправляет ответ.
>
>Это так называемый long polling - когда скрипт "подвисает" до наступления события (или до таймаута). Это вариант для случаев, когда недоступен или не хочется использовать вебсокет. Возможно соцсеть использует его потому, что этот метод более устойчив в случае разрывов связи, но я не уверен, это надо делать тесты. Или может, потому что он кросс-браузерней и не требует вебсокета. Или потому, что не все прокси (в организациях) пропускают вебсокет.
Значит нужно поскорей переходить на вебсокет...
>Надеюсь, я не слишком запутал? Задавай уточняющие вопросы тогда.
Прежде чем разобраться межпроцессным взаимодействием, нужно разобраться как работают процессы.
Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?
Если виртуальный сервер, это один единственный процесс, то я могу предположить, что значит уже сам PHP создает "дерево" из нескольких процессов, это так?
В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом. Я представлял, что приложение по цепочке вызывает все зависимости и в итоге становиться одним целым, т.е. я не думал что на каждую зависимость создаётся свой процесс. Неужели это действительно так?
Чтобы понять как работает паттерн pub/sub, я заглянул на вики и там обнаружил, что
>Шаблон издатель-подписчик представляет собой расширение шаблона наблюдатель, в который добавлено описание канала событий (англ. event channel), специально предназначенного для оповещения о событиях.
То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?
>>190032
>Я бы не советовал вообще вычислять время на сервере PHP, чтобы не сталкиваться с проблемами расхождения времени на клиенте, сервере, особенно если их несколько. Лучше пусть клиент присылает время последнего доступного ему сообщения, или, может, id (чтобы он был уникальным).
А клиент не пользуется этим временем, он просто пересылает его обратно PHP. Однако, я вижу аргумент за использование ID вместо даты - так надёжнее.
Если в браузере - попробуй использовать средства разработчика, вкладка Profiles, опции Take Heap Snapshot и Record Heap Allocations. Такое потребление абсолютно ненормально и скорее всего вызвано ошибками в коде.
Также, чтобы проблема была заметнее, можно попробовать специально надобавлять очень много сообщений.
> Или таймстамп. Но зачем возвращать время для следующего запроса, в клиенте можно же самому выставить интервал?
Тут имеется в виду, не время, когда сделать запрос, а время, начиная с которого искать новые сообщения. Ну то есть в начале клиент например посылает запрос
/new-messages?from=0
получает какие-то сообщения и новое значение для from (или вычисляет его сам из максимального времени у последнего сообщения). И использует это значение в следующем запросе.
> Значит нужно поскорей переходить на вебсокет...
Тут лучше сделать сравнение. Кто знает, может при неустойчивой связи как раз long polling надежнее работает.
> Прежде чем разобраться межпроцессным взаимодействием, нужно разобраться как работают процессы.
Процесс - это, условно, говоря абстрактная сущность, которая соответствует выполняющейся программе и под которую ОС выделяет ресурсы, в первую очередь память и процессорное время. Ты можешь увидеть список процессов командами вроде ps lax или top.
Процесс в ядре имеет уникальный номер (PID = process id, а также TGID = thread group id), который ты можешь увидеть в выводе команды top или ps lax в колонке с заголовком PID. Ресурсы - в первую очередь это изолированная область памяти (плюс еще кое-что по мелочи: набор переменных окружения, таблица открытых файлов и тд). Когда ты запускаешь какую-то команду (например, ls), ядро ОС создает новый "процесс", добавляет его в список процессов, выделяет кусок памяти, мапит (отображает) туда исполняемый файл /bin/ls и создает внутри процесса "поток", который будет выполнять двоичный код из этого файла.
"Поток" (тред, поток выполнения кода) это абстрактная штука, которая может выполнять код. В каждом живом процессе есть минимум один поток. В ядре ОС есть планировщик потоков, который постоянно переключает имеющиеся процессоры между потоками, создавая иллюзию параллельного выполнения всех потоков. Код может порождать новые потоки внутри того же процесса, также, потоки могут завершаться, если они выполнили свой код и больше не нужны. В линуксе потоки еще называются Light Weight Processes, они идентифицируются уникальным номером TID (thread id). Чтобы увидеть отдельные потоки, надо добавить к команде ps lax опцию -T, она выводит идентификатор потока (тот, что TID) под заголовком SPID, а идентификатор процесса под заголовком PID. Путаница вызвана историческими причинами, в первую очередь тем, что современные потоки появлились где-то в районе linux 2.4, а до этого была другая система ( https://unix.stackexchange.com/questions/364660/are-threads-implemented-as-processes-on-linux ).
Мануал по функции clone(), порождающей процессы и потоки в Линуксе, описывает названия TID, PID и TGID: http://man7.org/linux/man-pages/man2/clone.2.html Можно считать, что PID/TGID совпадает с TID самого первого потока в процессе.
> Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?
Ты про HTTP сервер? Это зависит от используемого веб-сервера и ОС. Если ты используешь mod_php, как плагин к Апачу, то по умолчанию под Линуксом Апач создает N процессов-рабочих и 1 процесс-диспетчер, который принимает и раскидывает приходящие HTTP-запросы между рабочими. Он также следит за рабочими, создает новые при росте нагрузки, или при их аварийном завершении, или прибивает ненужные. Код интерпретатора PHP подгружается как библиотека внутрь процесса-рабочего Апача и выполняется внутри него. У Апача есть специальное API, ты пишешь код на Си, компилируешь в библиотеку, подключаешь ее в конфиге и Апач вызывает твой код при обращении к определенному типу файла. Разработчики PHP поставляют интерпретатор в виде плагина к Апачу. Соответственно, при обращении к php-файлу Апач вызывает функцию из плагина, которая выполняет файл как PHP код.
Если ты используешь php-fpm, то там похожая схема, есть главный процесс и несколько процессов-рабочих. Диспетчер раздает запросы рабочим, и они выполняют указанный в запросе PHP-скрипт. Но бывают и другие варианты, например, когда создается несколько рабочих тредов внутри одного процесса.
Наконец, встроенный в PHP веб-сервер однопоточный и он просто в цикле принимает 1 запрос, выполняет указанный скрипт, и дальше ждет поступления следующего запроса.
Такого, что на каждый входящий запрос запускается новый процесс, обычно не делают, это неэффективно. Так когда-то давно было в протоколе CGI - веб-сервер при поступлении запроса просто запускал приложение и оно выводило на стандартный вывод HTML-код, который сервер оборачивал в HTTP-заголовки и отправлял клиенту.
Хотя процесс интепретатора PHP обычно не завершается, но контекст, в котором выполняется PHP-скрипт (список созданных PHP-переменных, функций, классов) обычно очищается после обработки одного HTTP-запроса. То есть приходит HTTP-запрос, создается новый пустой "контекст", php-скрипт работает, подгружает файлы, создает переменные, потом завершается и "контекст" уничтожается и следующий PHP-скрипт запускается в чистом, пустом окружении, как будто предыдущего скрипта и не было.
Проще говоря, в классической схеме твои PHP-скрипты - это лишь обработчики, которые сервер вызывает, когда ему нужно, в таком окружении, которое он настроил.
Если тебе это не нравится, ты можешь написать консольное PHP-приложение, которое само будет веб-сервером: открывает порт, принимает HTTP-запросы, обрабатывает их и отправляет ответы. В такой ситуации "контекст" не будет очищаться и ты можешь из обработчика одного запроса создать переменную, которая будет доступна в обработчике другого. То есть там может быть примерно такой код:
// Открываем TCP-порт
$socket = createListeningSocket(9001);
while (true) {
// Принимаем новое соединение от клиента
$connection = acceptConnection($socket);
handleConnection($connection);
}
Но это будет довольно неэффективно, так как если клиент тупит с отправкой или приемом данных, то твой сервер ждет его и ничего не делает. Ну и такое приложение не способно использовать более 1 ядра процессора. Если ты хочешь писать свой веб-сервер, лучше задействовать ReactPHP, который реализует асинхронную обработку событий.
> В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом
Издателем тут будет скрипт, который добавляет в базу новое сообщение, а подписчиком - скрипт, который ждет новых сообщений в схеме с long polling либо поток, обслуживающий websocket-соединение.
В общем случае это могут быть процессы даже на разных машинах (если у нас много серверов). Ну например, пусть у нас при отправке сообщения вызывается скрипт /send.php, он может выглядеть так:
...
$msg = new Message();
...
saveMessageToDb($msg);
notifyUser($toUser); // эта функция должна как-то вызвать функционал издателя
> Я представлял, что приложение по цепочке вызывает все зависимости и в итоге становиться одним целым, т.е. я не думал что на каждую зависимость создаётся свой процесс. Неужели это действительно так?
То, что ты описываешь, получится, если ты запускаешь свое приложение с помощью встроенного в PHP веб-сервера. Он однопоточный и выполняет все приходящие HTTP-запросы внутри одного процесса по очереди. Но это невыгодно, так как один процесс, если он однопоточный, может использовать максимум одно ядро процессора. А при высоких нагрузках, очевидно, одного ядра недостаточно.
Я описывал архитектуры серверов (не только веб-, а вообще любых работающих по схеме клиент-сервер приложений) вот тут вот: https://gist.github.com/codedokode/ffd520440a970c07c1c6 - есть самые разные варианты.
> То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?
Да. Семафор - это примитив, низкоуровневая сущность. Во многих ОС есть системные вызовы для создания и управления семафорами. pub/sub это штука, которая гораздо на более высоком уровне и она реализуется поверх примитивов ОС. То есть обычно для pub/sub нужна или библиотека или отдельное приложение-брокер. Отдельное приложение-брокер 100% нужно для распределенных систем, так как примитивы IPC работают только внутри одной машины.
Если в браузере - попробуй использовать средства разработчика, вкладка Profiles, опции Take Heap Snapshot и Record Heap Allocations. Такое потребление абсолютно ненормально и скорее всего вызвано ошибками в коде.
Также, чтобы проблема была заметнее, можно попробовать специально надобавлять очень много сообщений.
> Или таймстамп. Но зачем возвращать время для следующего запроса, в клиенте можно же самому выставить интервал?
Тут имеется в виду, не время, когда сделать запрос, а время, начиная с которого искать новые сообщения. Ну то есть в начале клиент например посылает запрос
/new-messages?from=0
получает какие-то сообщения и новое значение для from (или вычисляет его сам из максимального времени у последнего сообщения). И использует это значение в следующем запросе.
> Значит нужно поскорей переходить на вебсокет...
Тут лучше сделать сравнение. Кто знает, может при неустойчивой связи как раз long polling надежнее работает.
> Прежде чем разобраться межпроцессным взаимодействием, нужно разобраться как работают процессы.
Процесс - это, условно, говоря абстрактная сущность, которая соответствует выполняющейся программе и под которую ОС выделяет ресурсы, в первую очередь память и процессорное время. Ты можешь увидеть список процессов командами вроде ps lax или top.
Процесс в ядре имеет уникальный номер (PID = process id, а также TGID = thread group id), который ты можешь увидеть в выводе команды top или ps lax в колонке с заголовком PID. Ресурсы - в первую очередь это изолированная область памяти (плюс еще кое-что по мелочи: набор переменных окружения, таблица открытых файлов и тд). Когда ты запускаешь какую-то команду (например, ls), ядро ОС создает новый "процесс", добавляет его в список процессов, выделяет кусок памяти, мапит (отображает) туда исполняемый файл /bin/ls и создает внутри процесса "поток", который будет выполнять двоичный код из этого файла.
"Поток" (тред, поток выполнения кода) это абстрактная штука, которая может выполнять код. В каждом живом процессе есть минимум один поток. В ядре ОС есть планировщик потоков, который постоянно переключает имеющиеся процессоры между потоками, создавая иллюзию параллельного выполнения всех потоков. Код может порождать новые потоки внутри того же процесса, также, потоки могут завершаться, если они выполнили свой код и больше не нужны. В линуксе потоки еще называются Light Weight Processes, они идентифицируются уникальным номером TID (thread id). Чтобы увидеть отдельные потоки, надо добавить к команде ps lax опцию -T, она выводит идентификатор потока (тот, что TID) под заголовком SPID, а идентификатор процесса под заголовком PID. Путаница вызвана историческими причинами, в первую очередь тем, что современные потоки появлились где-то в районе linux 2.4, а до этого была другая система ( https://unix.stackexchange.com/questions/364660/are-threads-implemented-as-processes-on-linux ).
Мануал по функции clone(), порождающей процессы и потоки в Линуксе, описывает названия TID, PID и TGID: http://man7.org/linux/man-pages/man2/clone.2.html Можно считать, что PID/TGID совпадает с TID самого первого потока в процессе.
> Виртуальный сервер к которому приходят запросы это один единственный процесс или на каждый запрос создается свой собственный?
Ты про HTTP сервер? Это зависит от используемого веб-сервера и ОС. Если ты используешь mod_php, как плагин к Апачу, то по умолчанию под Линуксом Апач создает N процессов-рабочих и 1 процесс-диспетчер, который принимает и раскидывает приходящие HTTP-запросы между рабочими. Он также следит за рабочими, создает новые при росте нагрузки, или при их аварийном завершении, или прибивает ненужные. Код интерпретатора PHP подгружается как библиотека внутрь процесса-рабочего Апача и выполняется внутри него. У Апача есть специальное API, ты пишешь код на Си, компилируешь в библиотеку, подключаешь ее в конфиге и Апач вызывает твой код при обращении к определенному типу файла. Разработчики PHP поставляют интерпретатор в виде плагина к Апачу. Соответственно, при обращении к php-файлу Апач вызывает функцию из плагина, которая выполняет файл как PHP код.
Если ты используешь php-fpm, то там похожая схема, есть главный процесс и несколько процессов-рабочих. Диспетчер раздает запросы рабочим, и они выполняют указанный в запросе PHP-скрипт. Но бывают и другие варианты, например, когда создается несколько рабочих тредов внутри одного процесса.
Наконец, встроенный в PHP веб-сервер однопоточный и он просто в цикле принимает 1 запрос, выполняет указанный скрипт, и дальше ждет поступления следующего запроса.
Такого, что на каждый входящий запрос запускается новый процесс, обычно не делают, это неэффективно. Так когда-то давно было в протоколе CGI - веб-сервер при поступлении запроса просто запускал приложение и оно выводило на стандартный вывод HTML-код, который сервер оборачивал в HTTP-заголовки и отправлял клиенту.
Хотя процесс интепретатора PHP обычно не завершается, но контекст, в котором выполняется PHP-скрипт (список созданных PHP-переменных, функций, классов) обычно очищается после обработки одного HTTP-запроса. То есть приходит HTTP-запрос, создается новый пустой "контекст", php-скрипт работает, подгружает файлы, создает переменные, потом завершается и "контекст" уничтожается и следующий PHP-скрипт запускается в чистом, пустом окружении, как будто предыдущего скрипта и не было.
Проще говоря, в классической схеме твои PHP-скрипты - это лишь обработчики, которые сервер вызывает, когда ему нужно, в таком окружении, которое он настроил.
Если тебе это не нравится, ты можешь написать консольное PHP-приложение, которое само будет веб-сервером: открывает порт, принимает HTTP-запросы, обрабатывает их и отправляет ответы. В такой ситуации "контекст" не будет очищаться и ты можешь из обработчика одного запроса создать переменную, которая будет доступна в обработчике другого. То есть там может быть примерно такой код:
// Открываем TCP-порт
$socket = createListeningSocket(9001);
while (true) {
// Принимаем новое соединение от клиента
$connection = acceptConnection($socket);
handleConnection($connection);
}
Но это будет довольно неэффективно, так как если клиент тупит с отправкой или приемом данных, то твой сервер ждет его и ничего не делает. Ну и такое приложение не способно использовать более 1 ядра процессора. Если ты хочешь писать свой веб-сервер, лучше задействовать ReactPHP, который реализует асинхронную обработку событий.
> В вашей задаче вы описали два процесса publisher и subscriber. Соответственно, эти два процесса должны быть как-то описаны - быть сущностью или, проще говоря, скриптовым файлом
Издателем тут будет скрипт, который добавляет в базу новое сообщение, а подписчиком - скрипт, который ждет новых сообщений в схеме с long polling либо поток, обслуживающий websocket-соединение.
В общем случае это могут быть процессы даже на разных машинах (если у нас много серверов). Ну например, пусть у нас при отправке сообщения вызывается скрипт /send.php, он может выглядеть так:
...
$msg = new Message();
...
saveMessageToDb($msg);
notifyUser($toUser); // эта функция должна как-то вызвать функционал издателя
> Я представлял, что приложение по цепочке вызывает все зависимости и в итоге становиться одним целым, т.е. я не думал что на каждую зависимость создаётся свой процесс. Неужели это действительно так?
То, что ты описываешь, получится, если ты запускаешь свое приложение с помощью встроенного в PHP веб-сервера. Он однопоточный и выполняет все приходящие HTTP-запросы внутри одного процесса по очереди. Но это невыгодно, так как один процесс, если он однопоточный, может использовать максимум одно ядро процессора. А при высоких нагрузках, очевидно, одного ядра недостаточно.
Я описывал архитектуры серверов (не только веб-, а вообще любых работающих по схеме клиент-сервер приложений) вот тут вот: https://gist.github.com/codedokode/ffd520440a970c07c1c6 - есть самые разные варианты.
> То есть описание канала событий это обёртка над методами межпроцессных взаимодействиями, в нашем случае семафором?
Да. Семафор - это примитив, низкоуровневая сущность. Во многих ОС есть системные вызовы для создания и управления семафорами. pub/sub это штука, которая гораздо на более высоком уровне и она реализуется поверх примитивов ОС. То есть обычно для pub/sub нужна или библиотека или отдельное приложение-брокер. Отдельное приложение-брокер 100% нужно для распределенных систем, так как примитивы IPC работают только внутри одной машины.
Я про видео-курсы.
Задачу от ОП-а на список студентов или файлобменник сделай, сразу поймешь как все работает.
Никогда не пиши обработчик в хтмл теге.
Если у тебя тестовый проект из одной страницы, который нахуй никому не нужен, тогда напиши обработчких в теге скрипт.
Если это сайт с несколькими страницамии js скриптами, тогда пиши обработчик в файле скрипта и подключай через src
ЕСТЬ ОЧЕНЬ ДРЕВНИЙ СПРАВОЧНИК ДЛЯ НУБОВ, ЗДЕСЬ ВСЕ ПО ВЕБУ, В ТОМ ЧИСЛЕ ОБЪЯСНЯЕТСЯ ПРИНЦИП РАБОТЫ СЕРВЕРНЫХ СКРИПТОВ. ДОБАВТЕ В ШАПКУ.
https://starcat.dp.ua/doc/wdh/
Обработчик писать не стоит, а вызов функции вполне можно, как мне кажется, если это проблем не приносит. Это же проще и короче. И, кстати, в реакте и ангуларе именно так и делают.
>>190582
Обрачивание в функцию полезно тем, что переменные остаются внутри функции и не "вытекают" наружу. То есть ты не затрешь глобальные переменные, которые мог поставить какой-то другой скрипт.
Насчет вызова в атрибуте onclick - почему бы и нет, я проблем не вижу. Это удобно тем, что можно в один клик в IDE перейти к определению функции.
Есть такой паттерн, который мне не нравится, когда вообще весь JS код выносится в отдельный файл, включая код инициализации. То есть там пишутся функции, а потом блок вроде
$(document).ready(function() {
$('.somebutton').click(...);
...
});
У такого подхода есть плюсы, он во многих уроках и статьях упоминается, но есть и минусы:
- нереально найти обработчик из HTML кода, не понятно даже есть он или нет у данной кнопки
- в файле с JS непонятно, на каких страницах используется тот или иной блок кода, и используется ли он вообще? Никто не решается удалять код, его только дописывают, и в итоге там получается огромная длинная функция инициализации, в которой часть кода работает "вхолостую", и в которой никто не разберется.
- не тратится время на поиск элементов и навешивание обработчиков
- файл JS инициализируется только после события ready, хотя в случае с атрибутами обработчик доступен сразу
Но конечно, это не универсальное решение. Вообще, мне нравится решение, когда в JS мы объявляем только функции, без кода инициализации, а обработчики прописываем либо в атрибутах, либо вызываем функцию инициализации определенного виджета через инлайн-скрипт такого вида
<скрипт>
var widget = $('.js-some-widget');
initSomeWidget(someWidget);
</скрипт>
Этот подход требует загружать скрипты синхронно в шапке (а почему бы и нет, если их немного), но при желании переделывается на асинхронную загрузку скриптов через тот же require.js:
<скрипт>
когдаЗагрузится('some-script.js и jquery', function () {
var widget = $('.js-some-widget');
initSomeWidget(someWidget);
});
Алсо, хоть это и не имеет отношения к вопросу, но мне не нравится jQuery, он не модульный и мне не нравится его интерфейс, например, использование одних и тех же функций как геттеров и сеттеров, скрытие ошибок.
Обработчик писать не стоит, а вызов функции вполне можно, как мне кажется, если это проблем не приносит. Это же проще и короче. И, кстати, в реакте и ангуларе именно так и делают.
>>190582
Обрачивание в функцию полезно тем, что переменные остаются внутри функции и не "вытекают" наружу. То есть ты не затрешь глобальные переменные, которые мог поставить какой-то другой скрипт.
Насчет вызова в атрибуте onclick - почему бы и нет, я проблем не вижу. Это удобно тем, что можно в один клик в IDE перейти к определению функции.
Есть такой паттерн, который мне не нравится, когда вообще весь JS код выносится в отдельный файл, включая код инициализации. То есть там пишутся функции, а потом блок вроде
$(document).ready(function() {
$('.somebutton').click(...);
...
});
У такого подхода есть плюсы, он во многих уроках и статьях упоминается, но есть и минусы:
- нереально найти обработчик из HTML кода, не понятно даже есть он или нет у данной кнопки
- в файле с JS непонятно, на каких страницах используется тот или иной блок кода, и используется ли он вообще? Никто не решается удалять код, его только дописывают, и в итоге там получается огромная длинная функция инициализации, в которой часть кода работает "вхолостую", и в которой никто не разберется.
- не тратится время на поиск элементов и навешивание обработчиков
- файл JS инициализируется только после события ready, хотя в случае с атрибутами обработчик доступен сразу
Но конечно, это не универсальное решение. Вообще, мне нравится решение, когда в JS мы объявляем только функции, без кода инициализации, а обработчики прописываем либо в атрибутах, либо вызываем функцию инициализации определенного виджета через инлайн-скрипт такого вида
<скрипт>
var widget = $('.js-some-widget');
initSomeWidget(someWidget);
</скрипт>
Этот подход требует загружать скрипты синхронно в шапке (а почему бы и нет, если их немного), но при желании переделывается на асинхронную загрузку скриптов через тот же require.js:
<скрипт>
когдаЗагрузится('some-script.js и jquery', function () {
var widget = $('.js-some-widget');
initSomeWidget(someWidget);
});
Алсо, хоть это и не имеет отношения к вопросу, но мне не нравится jQuery, он не модульный и мне не нравится его интерфейс, например, использование одних и тех же функций как геттеров и сеттеров, скрытие ошибок.
Нет вешать обработчик на onclick в теге супер хуево. Это подход 2000 года. Правильно добавлять только через addEventListener.
Написание любого кода вроде onclick в теге в дальнейшем приведет к невообразимой путанице. Исключение очень маленькие одно-двухстраничные проекты.
А ангуляр дермо сделанное для дебилов, чтоб они хоть как то кодить могли.
Каким образом это приводит к путанице?
<button onclick="openSomePopup()"></button>
Вот тут я могу в один клик в IDE перейти к функции. А в твоем подходе, как мне искать обработчик кнопки в горах JS кода?
Аргумент про 2000 год это вообще не аргумент.
Алсо, открой HTML-код Ютуба или своей любимой соцсети и сделай поиск по ' on'.
Хтмл код ютуба не аргумент. Допустим у тебя сайт из 10-20 страниц с кучей js финтифлюшек. Хороший разраб сделал несколько js файлов в ктоторых сгрупировал фукнции и обработчики по смыслу. Потом в проект взяли тебя и ты на одной из страниц вписал обработчик в onclick тега. А через неделю этот обработчик понадобился еще в нескольких местах.
Это во-первых. Во вторых при отладке js в браузере проще выполнять по шагам js файл чем скакать в хтмл мешанине.
Ты отстаиваешь устаревший и менее эфефективный подход.
Ты прочитал мой пост? >>190665
На сайте из 10-20 разделов твой JS файл превратится в большую свалку кода такого вида:
$('.class-1').click(...
....
...
);
if ($('.class2').length) {
$('.class2')....
.....
...
);
if ($(window).widh() > 800) {
...
}
И так еще строк 300.
Этот подход работает только для маленьких сайтов.
И ты не ответил на вопрос, как при таком подходе искать обработчик для какой-то кнопки?
> Во вторых при отладке js в браузере проще выполнять по шагам js файл
При отладке ты просто ставишь брейкпойнт в начале нужной тебе функции и тебе не надо по шагам проходить описанные выше 300 строк инициализации.
> Ты отстаиваешь устаревший и менее эфефективный подход.
Я привел в посте аргументы, почему мой подход лучше. Все твои аргументы сводятся к "устаревший". Кто его объявил устаревшим? Покажи мне стандарт W3C, в котором это написано. Или хотя бы страницу MDN.
Ты прочитал мой пост? >>190665
На сайте из 10-20 разделов твой JS файл превратится в большую свалку кода такого вида:
$('.class-1').click(...
....
...
);
if ($('.class2').length) {
$('.class2')....
.....
...
);
if ($(window).widh() > 800) {
...
}
И так еще строк 300.
Этот подход работает только для маленьких сайтов.
И ты не ответил на вопрос, как при таком подходе искать обработчик для какой-то кнопки?
> Во вторых при отладке js в браузере проще выполнять по шагам js файл
При отладке ты просто ставишь брейкпойнт в начале нужной тебе функции и тебе не надо по шагам проходить описанные выше 300 строк инициализации.
> Ты отстаиваешь устаревший и менее эфефективный подход.
Я привел в посте аргументы, почему мой подход лучше. Все твои аргументы сводятся к "устаревший". Кто его объявил устаревшим? Покажи мне стандарт W3C, в котором это написано. Или хотя бы страницу MDN.
Не ну тут он прав, мне доводилось работать со свалками кода в js файлах, это тот еще гемор.
В целом js файл выглядет аккуратнее чем html js мешанина, наверное поэтому этот подход я считаю лучше.
Но это не особо решает.
Единственное что могу еще добавить в пользу addEventListener, таким способом можно к одной кнопке привязать несколько событий, в html это не возможно.
https://ideone.com/vCJosI
Со стихами смог сделать только через shuffle, иначе первые две строки были абсолютно одинаковыми.
https://ideone.com/IPaXhH
С палиндромом ебался часа 3, написал хуеты, но главное работает.
http://sandbox.onlinephpfunctions.com/code/1eed5a0bfc43968f61079d0dd36a84d9d66c2b2c
Несколько обрабочиков, сорян за неточность.
Да я знал что ты скажешь что можно в один onlcick вписать несколько функций. Но что если обработчики нужно вешать по условию, или даже например удалять в определенных ситуациях? Сам хуй.
Все, нашел ошибку во второй. Шел по улице и понял, где я обосрался.
http://sqlfiddle.com/#!9/beb4c/3
Такой же результат как у меня, хотя тут мускл а не мариа
На иннодб поиске такой проблемы нет, но чтобы мне поставить иннодб фуллтекст придется сначала апдейтнуть марию на 10+ версию.
Я проверил стандартный список стоп-слов муисам и там кошки нет, возможно в той дистрибуции сентос которая на впске стоит свой особый конфиг но я уже не знаю как это быстро чекнуть.
Ок, погуглил мануал: https://dev.mysql.com/doc/refman/8.0/en/fulltext-boolean.html
> The minimum and maximum word length full-text parameters apply to FULLTEXT indexes created using the built-in FULLTEXT parser and MeCab parser plugin. innodb_ft_min_token_size and innodb_ft_max_token_size are used for InnoDB search indexes. ft_min_word_len and ft_max_word_len are used for MyISAM search indexes.
Проверил значение параметра: http://sqlfiddle.com/#!9/beb4c/4
> ft_min_word_len4
Причем, как я понимаю, в случае изменения параметров может потребоваться пересоздать индекс с измененными параметрами, а не только менять их на время поиска.
Кстати, через SET они не меняются.
Сойдет ли для этого decimal длинной 10 и 2 знаками после запятой?
MySQL, если что.
Зависит от того, что твоя система будет делать. Если у тебя тупо каталог с ценой за продукт в рубль.копейка то хватит. Иначе врядли.
Например 1 евроцент это ~ 0.0000014 биткоина.
Для магазов вроде нужно делать decimal 10.4 (это стандарт установленный регулятором) при операциях с деньгами, ну и показывать пользователю 10.2.
Спасибо тебе, поменял настройку и поехало..
Только в итоге все равно задачу решает не лучше like с вайлдкартой :(
У меня есть путь:
D:/Progs/openserver/OSPanel/domains/uppu/files/2018/7413527a15526a76bf354a78520ad2b5ba1a19d5.JPG
Если я ее подставляю в браузер, то файл открывается, а если подставляю эту строку в <a href="..."> или <img src="..." то ничего не работает
Почему не работает?
Это обсолютный путь на диске. Браузер когда такое видит сразу запрашавает файл прямо с жесткого диска не подключаясь к веб-серверу.
Сервер работает с URL. Ссылки и сорцы картинок должны тоже соответсвовать.
Я не знаю настроены ли у тебя виртуальные хосты, но твоя ссылка должна выглядеть как то так:
Если у тебя корень сайта (где лежит твой html файл)
D:/Progs/openserver/OSPanel/domains/uppu/
и в браузере ты октрываешь его через:
http://localhost/
то ссылка на картинку будет:
http://localhost/files/2018/7413527a15526a76bf354a78520ad2b5ba1a19d5.JPG
спасибо
Допустим у меня код автолоадера: пикрил
И файл Student.php с Описанием класса Student лежит соотственно в /Classes/Entities/
Ну и с помощью неймспейса я как бы сообщаю автолоадеру полное имя класса, которое совпадает с папкой в которой он лежит, что бы не хардкодить путь до всех папок с классами в автолоадер, и он находит его.
Так вот - я теперь всегда обязан буду так создавать новый класс?
$a = new Classes\Entities\Student();
Либо например написать где-то
use Classes\Entities\Student;
Что пхп будет воспринято как
use Classes\Entities\Student as Student;
и можно будет писать
$a = new Student();
Это всё всё равно не решает никак проблему автоматизации подгрузки классов, нужно будет либо в ручную постоянно писать use c полным именем класса (в котором зашита по сути папка в которой он лежит), либо написать какой-то отдельный инициализатор, которому скармливать например какой-нибудь массив со всеми классами в приложении, и что бы он за тебя писал в цикле этот самый use Classes\Entities\Student;
Это нормальная практика? На этом всё? Просто сначала автолоадер воспринимался как нечто что само должно находить и подгружать тебе всё что ты захочешь, но в итоге как я посмотрю всё равно он будет подгружать только то, что четко задокументировано самим программистом.
>нужно будет либо в ручную постоянно писать use c полным именем класса
IDE делают это автоматически.
Суть неймспейсов в том что у тебя может быть несколько классов Student. Поэтому всегда надо уточнять.
При чем тут иде? Суть в том, что это должно быть в коде, а дальше уже не важно ты в ручную это пишешь или иде за тебя. Речь о том, что бы вообще обойти написание этих юзов.
>>191585
Да это то понятно, вы не поняли немного что я собственно спрашивал. У меня возник вопрос о том как собственно писать автолоадер когда у тебя гора классов в разных папках.
Пришло на ум три варианта.
1. ты пишешь хитровыебанный древовидный поиск по папкам, в духе:
заглянуть в каждую папку в корне, там есть вызываемый класс? да - подгрузить, нет - в папке есть еще папки? да - посмотреть каждую папку ... и так далее
2. Хардкодишь в автолоадер все возможные пути в которых у тебя могут сидеть классы и пусть пребором ищет по ним.
3. Вот так зашивать путь к классу в неймспейс, и при попытке вызывать класс придется сообщать собственно этот путь и указывать, либо 1 раз писать use Namespace\Classname; где-то что бы потом можно было просто везде потом спокойно вызывать этот самый $obj = new Classname;
Еще кстати вопрос по поводу заглавных букв в названии папок возник.
Лучше в итоге всё всё писать с большой буквы? И неймспейс и названия папок? Вроде и когда мешанина из мелких и больших все работает, но что бы 1 раз уяснить для себя и не тупить больше с этим.
https://github.com/codedokode/pasta/blob/master/php/autoload.md прочитал если что, но вдруг что-то упустил.
>заглянуть в каждую папку в корне, там есть вызываемый класс? да - подгрузить, нет - в папке есть еще папки
сосешь в производительности.
Можно сделать один файл с массивом где каждому классу сопоставлен путь к нему.
Но тебе при создании новых классов нужно лезть туда и вписывать его. Если захочешь переименовать или переместить класс, опять лезть и менять там.
Так что лучше смириться с use и не париться.
К тому же это практика многих языков, не только php. Все так делают.
Автолоадер помогает избежать записи путей к файлам в коде. Ты ведь будешь не только писать свои классы, а еще и использовать сторонние библиотеки. И за счет автолоадера ты убираешь из кода пути к ним, а просто пишешь: "мне нужен класс Symfony\Routing\Router". Не указывая, где находится библиотека.
До автолоадинга делали так: библиотеки клали в отдельную папку вроде /usr/share/pear (pear = php extension and application repository) и прописывали путь к ней в set_include_path(). А в коде писали что-то вроде require 'Net/Util/Dns.php'; для класса с названием Net_Util_Dns из сторонней библиотеки.
Это вариант до сих пор работает, кстати.
В старом варианте тебе надо было указать и имя класса, и расположение файла. В новом, с автозагрузкой, только имя класса. По моему, удобнее.
В некоторых других языках, например, в node.js, модули подключаются по пути относительно текущего файла:
var module = require('some/module');
var object = new module.SomeClass(); (или new module(), если модуль экспортирует единственный класс)
Отличие от PHP в том, что в JS классы и функции не создаются в глобальной области видимости, а только в переменной module. Это позволяет например иметь функции с одинаковыми названиями в разных модулях без использования неймспейсов (неймспейсом по сути становится путь к модулю).
Теперь про неймспейсы. Длинные имена классов были и до них, например, Zend_Db_Table_Abstract. Неймспейсы лишь поменяли _ на \ и позволили за счет use использовать к коде короткое имя и сделать код более читаемым. use по задумке может вставлять в код IDE.
----
Теперь вернемся к твоему примеру.
> Classes\Entities\Student
Classes здесь не нужен. Он ничего не говорит. В начале пути либо пишу название приложения, либо ничего. То есть либо StudentList\Entity\Student, либо Entity\Student. Если классов совсем мало (в пределах 10-20), можно писать просто Student или StudentList\Student. Если все классы находятся в одном неймспейсе, то use писать не придетися.
Почему нужны длинные имена классов? В PHP не может быть 2 класса с совпадающим именем и все приложения и библиотеки должны использовать уникальные имена классов. Отсюда нужда в неймспейсах. Чтобы твой Student не совпал с именем класса в какой-то библиотеке.
Урок, если ты не читал https://github.com/codedokode/pasta/blob/master/php/autoload.md
Автолоадер помогает избежать записи путей к файлам в коде. Ты ведь будешь не только писать свои классы, а еще и использовать сторонние библиотеки. И за счет автолоадера ты убираешь из кода пути к ним, а просто пишешь: "мне нужен класс Symfony\Routing\Router". Не указывая, где находится библиотека.
До автолоадинга делали так: библиотеки клали в отдельную папку вроде /usr/share/pear (pear = php extension and application repository) и прописывали путь к ней в set_include_path(). А в коде писали что-то вроде require 'Net/Util/Dns.php'; для класса с названием Net_Util_Dns из сторонней библиотеки.
Это вариант до сих пор работает, кстати.
В старом варианте тебе надо было указать и имя класса, и расположение файла. В новом, с автозагрузкой, только имя класса. По моему, удобнее.
В некоторых других языках, например, в node.js, модули подключаются по пути относительно текущего файла:
var module = require('some/module');
var object = new module.SomeClass(); (или new module(), если модуль экспортирует единственный класс)
Отличие от PHP в том, что в JS классы и функции не создаются в глобальной области видимости, а только в переменной module. Это позволяет например иметь функции с одинаковыми названиями в разных модулях без использования неймспейсов (неймспейсом по сути становится путь к модулю).
Теперь про неймспейсы. Длинные имена классов были и до них, например, Zend_Db_Table_Abstract. Неймспейсы лишь поменяли _ на \ и позволили за счет use использовать к коде короткое имя и сделать код более читаемым. use по задумке может вставлять в код IDE.
----
Теперь вернемся к твоему примеру.
> Classes\Entities\Student
Classes здесь не нужен. Он ничего не говорит. В начале пути либо пишу название приложения, либо ничего. То есть либо StudentList\Entity\Student, либо Entity\Student. Если классов совсем мало (в пределах 10-20), можно писать просто Student или StudentList\Student. Если все классы находятся в одном неймспейсе, то use писать не придетися.
Почему нужны длинные имена классов? В PHP не может быть 2 класса с совпадающим именем и все приложения и библиотеки должны использовать уникальные имена классов. Отсюда нужда в неймспейсах. Чтобы твой Student не совпал с именем класса в какой-то библиотеке.
Урок, если ты не читал https://github.com/codedokode/pasta/blob/master/php/autoload.md
Такой файл умеет генерировать композер с опцией --optimize-autoloader: https://getcomposer.org/doc/articles/autoloader-optimization.md
Учтите, что обязательно нужен кеш php-кода вроде opcache, чтобы эта оптимизация была эффективна и PHP не перечитывал этот гигантский массив при каждом запуске скрипта.
> On PHP 5.6+, the class map is also cached in opcache which improves the initialization time greatly. If you make sure opcache is enabled, then the class map should load almost instantly and then class loading is fast.
Как и с любой другой оптимизацией, обязательно делайте измерения производительности до и после.
>>191609
> У меня возник вопрос о том как собственно писать автолоадер когда у тебя гора классов в разных папках.
Если ты используешь композер, то он умеет генерировать автозагрузчик и там есть 2 готовых варианта:
1) описать каждую папку, прописав путь к папке и неймспейс находящихся в ней классов
2) заставить композер обойти все файлы и сгенерировать список, какой класс в каком файле (classmap)
> Лучше в итоге всё всё писать с большой буквы? И неймспейс и названия папок?
Да. Имена классов и так пишутся с большой буквы, логично и неймспейсы так же. В папках регистр должен совпадать, так как на линукс файловая система и имена файлов чувствительны к регистру.
>>191542
>я как бы сообщаю автолоадеру полное имя класса, которое совпадает с папкой в которой он лежит
Не полное, а относительное. Потому Classes писать не надо.
> Просто сначала автолоадер воспринимался как нечто что само должно находить и подгружать тебе всё что ты захочешь, но в итоге как я посмотрю всё равно он будет подгружать только то, что четко задокументировано самим программистом.
Правила, как называть и располагать классы (PSR-4) нужны, так как без них каждый начнет придумывать свою систему и ты при работе в команде или с чужим кодом будешь ломать голову, как найти тот или иной класс, куда поместить новый класс? Без правил как раз будет свалка, а с правилами все единообразно.
Посмотри на пример чужого проекта, как там сделаны пути и имена классов:
- совсем маленький проект https://github.com/symfony/filesystem
- проект побольше https://github.com/slimphp/Slim
- https://github.com/twigphp/Twig
Заодно обрати внимание на оформление и все остальное. От тебя хотелось бы в итоге (после проверок и исправления кода) получить код такого же качества или лучше. Разумеется, язык в ридми и комментариях можно использовать русский.
Такой файл умеет генерировать композер с опцией --optimize-autoloader: https://getcomposer.org/doc/articles/autoloader-optimization.md
Учтите, что обязательно нужен кеш php-кода вроде opcache, чтобы эта оптимизация была эффективна и PHP не перечитывал этот гигантский массив при каждом запуске скрипта.
> On PHP 5.6+, the class map is also cached in opcache which improves the initialization time greatly. If you make sure opcache is enabled, then the class map should load almost instantly and then class loading is fast.
Как и с любой другой оптимизацией, обязательно делайте измерения производительности до и после.
>>191609
> У меня возник вопрос о том как собственно писать автолоадер когда у тебя гора классов в разных папках.
Если ты используешь композер, то он умеет генерировать автозагрузчик и там есть 2 готовых варианта:
1) описать каждую папку, прописав путь к папке и неймспейс находящихся в ней классов
2) заставить композер обойти все файлы и сгенерировать список, какой класс в каком файле (classmap)
> Лучше в итоге всё всё писать с большой буквы? И неймспейс и названия папок?
Да. Имена классов и так пишутся с большой буквы, логично и неймспейсы так же. В папках регистр должен совпадать, так как на линукс файловая система и имена файлов чувствительны к регистру.
>>191542
>я как бы сообщаю автолоадеру полное имя класса, которое совпадает с папкой в которой он лежит
Не полное, а относительное. Потому Classes писать не надо.
> Просто сначала автолоадер воспринимался как нечто что само должно находить и подгружать тебе всё что ты захочешь, но в итоге как я посмотрю всё равно он будет подгружать только то, что четко задокументировано самим программистом.
Правила, как называть и располагать классы (PSR-4) нужны, так как без них каждый начнет придумывать свою систему и ты при работе в команде или с чужим кодом будешь ломать голову, как найти тот или иной класс, куда поместить новый класс? Без правил как раз будет свалка, а с правилами все единообразно.
Посмотри на пример чужого проекта, как там сделаны пути и имена классов:
- совсем маленький проект https://github.com/symfony/filesystem
- проект побольше https://github.com/slimphp/Slim
- https://github.com/twigphp/Twig
Заодно обрати внимание на оформление и все остальное. От тебя хотелось бы в итоге (после проверок и исправления кода) получить код такого же качества или лучше. Разумеется, язык в ридми и комментариях можно использовать русский.
В проектах, на которые я выше дал ссылки, правила автозагрузки прописаны в файле composer.json. Там используется автозагрузчик, который генерирует композер на основе правил из этого файла.
>Такой файл умеет генерировать композер с опцией --optimize-autoloader
Ну так одно дело когда это автоматически.
Путь с d:/... указывать неправильно, так как этот путь и диск d: виден только на твоем компьютере. Если на твой сайт зайдет человек с другого компьютера, у него может вообще не быть диска d:, и тем более, там не будет нужной картинки.
В вебе пути указываются относительно корневой папки веб-сервера (так как веб-сервер раздает только файлы внутри нее и не отдает файлы за ее пределами).
Как написал анон выше, надо писать /files/....
Алсо, имена из md5 на мой взгляд это не самая лучшая идея, так как они не воспринимаются человеком, их неудобно перепечатывать и при отладке тебя будет сбивать с толку большое количество таких файлов в папке.
> Почему не работает?
Потому что в src/href указывается URL, и путь d:/... - это не URL. Структура URL описана в моем уроке https://github.com/codedokode/pasta/blob/master/network/urls.md
>>191244
Почему? По идее LIKE использует индекс только при поиске по началу строки, если в начале не стоит % или _, а MATCH - умеет искать слово в любом месте строки по индексу.
>>191131
Наверно с 2-4 знаками после запятой. Не забудь поле для валюты, если у тебя что-то международное.
>>190851
Навешивание обработчика полезно, когда надо передать ему контекст в виде переменных из замыкания или значение this:
var x = ...;
button.addEventListener('click', function (e) {
console.log(x);
});
var someObject = ...;
button.addEventListener('click', e => someObject.handleClick(e);
});
В случае навешивания обработчика снаружи надо использовать addEventListener, а не button.onclick = ..., чтобы не затереть обработчик в атрибуте. Через JS трогать on-атрибуты вообще не стоит.
> Почему не работает?
Потому что в src/href указывается URL, и путь d:/... - это не URL. Структура URL описана в моем уроке https://github.com/codedokode/pasta/blob/master/network/urls.md
>>191244
Почему? По идее LIKE использует индекс только при поиске по началу строки, если в начале не стоит % или _, а MATCH - умеет искать слово в любом месте строки по индексу.
>>191131
Наверно с 2-4 знаками после запятой. Не забудь поле для валюты, если у тебя что-то международное.
>>190851
Навешивание обработчика полезно, когда надо передать ему контекст в виде переменных из замыкания или значение this:
var x = ...;
button.addEventListener('click', function (e) {
console.log(x);
});
var someObject = ...;
button.addEventListener('click', e => someObject.handleClick(e);
});
В случае навешивания обработчика снаружи надо использовать addEventListener, а не button.onclick = ..., чтобы не затереть обработчик в атрибуте. Через JS трогать on-атрибуты вообще не стоит.
В коде ошибка
document.log = log;
Это ни на что не влияет. Глобальные переменные создаются в объекте window, а не document.
>Не полное, а относительное. Потому Classes писать не надо.
Ну это если у меня нет папки Classes в корне в которой и лежат все классы. Если я её уберу из пути, то у меня тупо будет ошибка что по указаному пути ничего не найдено.
Вообще я не понял особо по поводу относительного пути.
Допустим я дампаю __DIR__ - он показывает папку с виртуальным хостом у меня.
Соответственно поэтому я и навасянил . '\\' . себе в путь класса в отличие от примера ОП-а, потому что если это убрать, то не будет хватать одного бекслеша.
В общем пикрилы всё демонстрируют.
Похуй.
this.world.locations = [
new Location(
"Яма арены",
[{text:"в клетку", id:"Клетка арены"}],
[
new creatures.Mouse({}).go("Клетка арены"),
new creatures.Mouse({}).guard(?).follow(?)
]
),
new Location(
"Клетка арены",
[{text:"выйти", id:"Яма арены"}],
[]
)
];
У тебя все классы лежать в папке Classes, поэтому эту папку можно жестко вписать в путь в автолоадере, а классы именовать без этой папки.
Спасибо, наконец-то понял о чем речь.
У тебя еще ошибка: ты должен заменять бекслеши в имени класса на прямые, так как в Linux обратные бекслеши в путях не поддерживаются, а прямые работают во всех ОС.
Classes писать не надо. Неймспейс это не путь к файлу, а группировка классов вместе по назначению. В твоем варианте, если классов всего несколько, логичнее их кинуть в общий неймспейс StudentList. Папку под него можно создавать, можно не создавать.
Я же вроде писал в уроке, что PSR-4 допускает установку правил вроде "Все классы в неймспейсе X лежат в папке Y". То есть, например, "класс StudentList\X" лежит в src/X.php.
Я вообще не советую использовать папку classes, так как в ООП приложении 99% файлов это классы. Просто назови ее src или StudentList.
Думаю, что можно, почему нет?
var mouse1 = new Mouse();
var mouse2 = new Mouse();
mouse2.follow(mouse1);
id не стоит вводить, если и без них все работает. Объекты сами по себе уникальны и не требуют идентификаторов.
Без переменных, как я понимаю, не получится? У меня там намечается 1000 локаций и ~700 "мышек", я потеряюсь в переменных. %%Как и в ID."
Массивы имеют ключи. Используй их вместо id.
>так как веб-сервер раздает только файлы внутри нее и не отдает файлы за ее пределами
В задаче про студентов писалось, что нужно выносить все файлы за корень, и оставить там только css, js и index.php
так где же все-таки должен находиться каталог с файлами загруженными на сервер?
Картинки, которые загружаются на сайт и должны показываться пользователям, должны быть в публичной папке. То, что пользователи видеть не должны, за ее пределами.
Если файл должен быть доступен из браузера то конечно он должен быть в публичной папке по определению.
Одно дело выучить пыху, другое научиться программировать.
Большая часть вакансий основана на пыхе, это самое популярное говно, так еще будет долго.
В базу данных, естественно, всё залетает, но экзепшн все равно выкидывает. 2031 ошибка, как сказано в документации,
это неправильное формирование данных для execute(). Но выглядит всё правильно, вроде как. Ах да, пробовал ключи типа ":name", вместо "name". Не помогло.
Пробовал, все то же, не выходит
>>192395
Не прочитал до конца твой пост. сук Должно работать же. Можешь попробовать использовать ? плейсхолдеры, и передавать массив без ключей, или через BindValue байндить. Но все равно как-то странно что не работает :thinking:
А зачем ты поля таблицы в кавычки берешь? Я бы по ПРИКОЛУ попробовал без них затестить и с ключами :name
спасибо, за попытку помощи. Я НАШЕЛ В ЧЕМ ДЕЛО. У меня строчка была в методе модели, которая кидала записи в базу, которая ссылалась на метод для SELECT-запросов, что и вызывало ошибку формирования данных, мать вашу, как всегда. Я весь stackoverflow перерыл уже XD
Конкретно один пробел " "
Один или больше пробелов " +"
Один любой пробельный символ (табуляция например) "\h"
Один или больше любых пробельных символов "\h+"
Незнаю чо там в задаче, но если в preg_replace заменить "\h+" на " ", то везде будет ровно 1 пробел.
Ну так это, я могу, выучив пыху, идти зарабатывать на хлебушек и попутно вкатываться в ТО САМОЕ программирование, где 30000кккк/наносекунду?
Да. Пара тройка лет работы на пыхе и ты уже понимаешь как не надо писать код. Какую инфу по теории программирования надо знать и т.д. Изучаешь какой нибудь C++, C# или яву, пишешь в резюме что ты ахуевший кодер со стажем и опаньки ты в многоэтажном офисе с бассейном ебешь гусей своей мечты.
Нет это не кайф. Тут нужно ебашить как на заводе, чтобы чего то достичь с нуля.
Ну так ебашить по жизни энивей придется, и я так-то считаю что ебашить головой и иметь в будущем перспективы это намного радужнее чем ебашить лапками и не иметь перспектив.
Все верно. Просто знаю человек сто, которые хотели стать программистами чтоб не работать на заводе и купаться в деньгах, но дико обломились когда головушки заболели.
Во-первых, зачем тебе переменная $exchangeRate, если ты её не используешь?
Во-вторых, строка "$dollars" выведет значение переменной $dollars без знака доллара.
Чтобы вывести со знаком доллара нужно выводить так: "${$dollars}" или так "$ $dollars" или так "$dollars$"
Просто читал гайдик данный в шапке, делал все как там описано
Если для чего то введена переменная, нужно везде вместо её значения подставлять её.
Потому что, если в переменной значение поменяется, то оно должно поменяться везде.
Строчку error_reporting(-1) добавляй после того как убедился что все работает, чтобы видеть ошибки выполнения пока решаешь задачу.
Ну, хули, пока не попробую - не узнаю. А вдруг не обосрусь и через десяток лет буду получать тонны денег и нюхать кокаин с задниц латиноамериканских шлюх.
пол года на кокаине и ты из гениального прогера превращаешься в тупорылого дауна
После полугода на кокаине и умереть не жалко.
>>192973
Спасибо большое. Стоило ещё пояснить точнее - не просто заменить, а именно расставить, чтоб запятая превратилась не в пробел, а в запятую с пробелом.
Там выше у анончика есть пример кода, он использует $1 и $2. Я погуглил, выяснил, что это подмаски. Но как их юзать, так и не разобрался. А тупо копипастить не хочу, хочу понять.
если в своей регулярке какуюто часть ты берешь в скобки, то часть строки соотвествующая этой части в скобках запоминается в $1. $2 - это то что совпало со вторыми скобками и т.д. Часть строки совпавшая со всей регуляркой целиком это $0.
Используя скобки в регулярке ты можешь потом производить замену совпавшей части строки при этом сохраняя конкретные совпавшие подчасти.
Например есть предложение с запятой посередине, ты хочешь заменть запятую на точку с запятой но чтобы все остальное осталось как было:
preg_replace("/(.+),(.+)/", "$1;$2", "слово1, слово2")
В регулярке обрамляется скобочками нужный объект, например /(пидорашка) (любит) пыню/ui
пидорашка это $1
любит $2
а так /((пидорашка) (любит)) пыню/ui
$1 пидорашка любит
$2 пидорашка
$3 любит
Понятно?
Кстати, вместо $1 можно написать так \\1.
Только для запрос-ответ. Все постоянные длительные серверные вычисления лучше делать на с++
Например если помимо игроков сервер должен постоянно просчитывать действия мобов то пхп для этого херовый.
Мобы могут чтото делать независимо от того есть игроки или нет. Нет игроков = нет запросов = нет запусков скрипта. Если вычисления для мобов простые и короткие по времени можно использовать переодические вызовы скриптов кроном.
>изометрия на изометрии и рпг погоняет
Ну так это то что надо. Я много раз хотел чтото подобное, но нихуя не умею рисовать. зато знаю пхп.
>Мобы могут чтото делать независимо от того есть игроки или нет.
Судя по исходникам которые я смотрел, делается так. "Если игрок достиг ячейки карты, запустить действия моба". "Если на карте нет игрока, не использовать функцию." Как-то так.
Да такой вариант возможен. Эмуляция прошедших действий моба только при наблюении. Но мобы это только один пример. Я имел в виду вообще любые процессы которые могут происходить даже если никто не онлайн. В некоторых случаях делать просчеты только когда "кто-то смотрит" не получится из-за большого фактора энтропии.
Например нужно оповестить игрока о том что где то в мире произошло какое то событие, которое зависит от множества факторов. При этом нельзя посадить это на рандом и оповестить нужно именно в момент когда событие произошло. В такой ситуации на сервере должна происходить движуха независимо от запросов игроков.
Интересно, а как с этим в WOW обстоит дело, там вроде по расписанию всё. И все события в основном генерируются именно по времени и заранее описаны скриптами.
В вов все просто. У каждого моба есть четкое расписание действий. Нет энтропии.
Но что если бы мобы могли вести себя рандомно (как в майнкрафте например). Скажим есть у тебя хата, пришли в любой момент криперы проели дыру в стене позврывали все нахуй.
Ну то есть у пхп очень ограниченный предел возможностей в плане игр. Но простую игровую механию сваять нехуй делать.
Побегу делать наброски, если будет получаться что-то больше чем "У меня есть идея", ты же в треде будешь? спишемся.
ага
как обрезать длинную строку, чтобы показывалась начало и конец, а в середине было "..." ?
"Список",
"Список 3"
- то всё ок
но если:
"йцуйываыапыв"
какое-нибудь, то чет сосалово.
Алсо когда стояла просто регулярка:
/^[\w]+$/
То вообще русские символы не пропускало.
Спасибо большое, теперь всё ок. Видимо без u пропускало некоторые русские символы, которые умещаются в 1 байт или что-то такое, поэтому такой рандом был.
В htmlspecialchars или аналоги как описано тут: https://github.com/codedokode/pasta/blob/master/security/xss.md
Некоторые шаблонизаторы поддерживают автоматическое экранирование при выводе.
Но дошел до момента, где нужно создавать некий список. Допустим это название и пункты и вот надо что бы всё это создавалось / редактировалось без перезагрузки страницы. Чет завис на этом моменте, с чего начать гуглить, что бы это воплотить?
Под аякс запросы обычно другой метод контроллера пишут или в тот же всё летит?
Допустим есть у меня /someController/editList/123
Захожу я в него, там во вьюхе список обычный
<ul>
<li></li>
...
<li></li>
</ul>
в каждый <li> обернут по сути элемент из базы, в общем наверное нужно весь этот список превратить в js объект сначала? А дальше с ним манипуляции проводить? И если что бы по заполнению некого поля и клику по кнопке Добавить еще могу представить себе как этот список что-то будет добавляться или по клику какой-нибудь иконки что бы пункт удалялся, то как только устроить редактирование отдельных пунктов чет ума не приложу.
Можно выдавать json объект и строить элементы яваскриптом, а можно выдавать кусок html и заменить яваскриптом содержимое блока.
Все зависит от того нужно ли тебе использовать данные в яваскрипте или нет. Если нет, то html кусок лучше чем json.
Если логика в обработке аякс и обычного запроса сильно не отличается то вполне можно использовать тот же экшн контроллера. Просто ставишь в нем проверку на аякс чтобы не выдавать всю страницу а только часть или json.
Щас увидел что у тебя на странице будет редактирование. В этом случае json нормальное решение, но и тут в принципе можно обойтись без него ведь есть же data аттрибуты у тегов.
Работа на цмс более востребована, обычно это мелкие конторы с низким порогом оплаты. Фреймы как правило используются для более серьезных проектов чем "хатет сайт продават", соответственно в более крупных компаниях и более крупной оплатой.
Стартовать лучше с цмс.
Ну и если стает вопрос пилить обычный сайт (блог, магаз, несколькостраничник) на цмс или фрейме то цмс в этом плане лидирует так как имеет уже готовую админку и огромный набор плагинов.
На фрейме админку придется пилить самому, конечно есть готовые решения в виде очень простых и универсальных админок, но их нужно очень сильно допиливать.
Так что фрейм нужно юзать для особых нестандартных проектов, например таже биржа фриланса или соцсеть.
А не подскажешь какую выбрать цмс. ЧТо бы я мог на удаленке найти работу. Ну я конечно её пощупаю, попробую написать пару модулей. Этого же хватит? Хотя бы что бы в месяц тысяч 7 получать.
В любой цмс нужно уметь делать модули и темы.
да и надо было написать:
$random = mt_rand(1,6);
что бы функция mt_rand сложила в переменную $random число от 1 до 6.
Пасиб, сижу сейчас читаю вот это
https://learn.javascript.ru/ajax-xmlhttprequest
Правда там бэкэнд на ноде написан, но как я понял это не важно. А вот как узнать что у тебя аякс прилетел не обычный запрос?
>Если логика в обработке аякс и обычного запроса сильно не отличается то вполне можно использовать тот же экшн контроллера. Просто ставишь в нем проверку на аякс чтобы не выдавать всю страницу а только часть или json.
как эту проверку в пхп реализовать?
xmlhttprequest добавляет в http запрос какойто особый заголовок, который можно увидеть в пхп в массиве $_SERVER
Как-то так, предыдущие не скинул, ибо делал в другой IDE т.к. IDEOne не работала.
Ты можешь даже присваивать $i произвольные значения внутри цикла. Например чтобы сбросить счетчик, или перескочить несколько итераций сразу.
Там вообще какая-то хуйня с ней творится начинает, может IDE тупит. Сейчас проверю где-нибудь ещё.
Эм... Фиг знает почему, но теперь всё работает. Сделал как ты написал, спасибо!
Вроде бы работает как надо, но вот хуй знает, что делать с этими копейками.
А как в вордпресс и вообще в цмски вкатиться? Я не представляю, как писать модули. И нужны ли ООП, продуманная архитектура, или можно все в трешовом стиле цмс писать. Мне кажется, что аккуратный код плагина особой роли не сыграет, если сам код цмс выглядит как говно.
Судя по архитектуре вордпреса, его клепали умельцы не особо шарящие в высокоуровневом программировании. Там есть несколько классов но в основном все построено на глобальных функциях.
Что касается модулей то тут больше свободы, можешь весь модуль делать на функциях можешь на ооп.
Ну а в катываться начинать надо с чтения документации, желательно официальной, желательно на англиском.
if ($credit < 5000) {
break;
}
}
Если я так сделаю, то прерывание затронет весь цикл ли только конструкцию if?
break и continue влияет только на циклы, независимо от того насколько глубоко внутри цикла он находится.
Нужна помощь с симфони. По ссылке выше говорится, что фронт-контроллер каким-то образом должен обрабатывать каждый запрос, но как заставить его это делать не написаноили я жопой читал. Если фронт-контроллер имеет вот такой код
$uri = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
if ('/index.php' === $uri) {
list_action();
} elseif ('/index.php/show' === $uri && isset($_GET['id'])) {
show_action($_GET['id']);
} else {
header('HTTP/1.1 404 Not Found');
echo '<html><body><h1>Page Not Found</h1></body></html>';
}
как можно перенаправить запрос с лист.пхп сюда?
Этим занимается веб сервер. Процесс называется rewrite.
https://symfony.com/doc/current/setup/web_server_configuration.html
Так блять, вроде работает. Вопрос такой. Я писал расчёт с процентами и комиссией в 2 строки:
$credit = $credit* $percent;
$credit = $credit + $comission;
И у меня был проёб в расчётах на некоторую сумму, с чем это может быть связано? Проёб такой, что айфон обходился в ~55к
Поидее питон более универсальный язык.
Я бы запрос-ответ сделал на пыхе, а все постоянные процессы игры на с++
Вот кто эталонный образец для сайта govnokod.ru. Уж если србрался бек писать для онлайна, то пиши его уеликом на крестах, ибо сокеты там и всякое такое будет безопаснее и быстрее. Пых для бека... ну разве только для блогов и форумов.
Плюсы то что надо.
И это все?
https://www.youtube.com/watch?v=YRHqSsKYDJo
самый внятный видеокурс на русском, который нашёл, правда он по slim2
Нужна помощь, знаю что тут есть мастера не только по бекэнду
оно написано для тех, кто шарит прост. типа ты работал на чем-то похожем раньше.
советую тебе просто смотреть обучающие видео на ютубе, как кто-то делает сайт.
охуеть,а ОП говорил что с него надо начинать,а руководство-гавно.вот у YII оно норм
двачую, сам 3 года назад вкатился в Yii2. сейчас подумываю переходить на ларавел из-за его большей популярности.
да
https://habr.com/company/nixsolutions/blog/329718/
но ради справедливости добавлю что это сферическое тестирование в вакууме
Блять, подловил))
Формы вроде "зделаешь" не учтены, но в общем, решено правильно.
>>178355
Я не очень помню уже, о чем был исходный пост, но просто напишу, какие есть вариант организации JS кода на больших сайтах, какие встречаются проблемы и какие бывают варианты решения.
Начнем с вопроса, как можно инициализировать код и подключать обработчики.
1) "классический" вариант: подключаем скрипты в шапке документа, содержащие функции, а в коде прописываем обработчики в атрибутах вроде <button onclick="..">, которые вызывают эти функции.
Плюс:
- простота и понятность, можно в один клик перейти к функции
- обработчики работают в момент появления элемента на странице (браузер парсит HTML по мере получения и отображает частично загруженную страницу), нет промежутка времени, когда они "мертвые"
- нет проблем с навешиванием обработчиков на динамически доабвляемый на страницу контент
Минус:
- скрипты в шапке блокируют разбор HTML до их загрузку и нешуточно замедляют загрузку страницы на мобильном соединении если их много (с HTTP/2 это чуть улучшилось, но все равно мешает загрузке)
2) вешаем обработчики через функции вроде $(document).ready() в внешних файлах. Этот подход описан довольно давно (статья 2006 года http://alistapart.com/article/behavioralseparation ).
Плюс:
- достигается полное отделение JS от HTML (некоторые вообще против inline-скриптов и атрибутов onxxx)
- это подается под соусом progressive enhancement
- скрипт не требуется загружать заранее в шапке, его можно загрузить асинхронно уже после загрузки страницы
Минус:
- не очевидно, как найти обработчик для кнопки. Это отчасти решается при грамотной организации кода (например, если у каждого виджета или страницы свой управляющий JS-файл и он небольшой, то понятно, что обработчик надо искать в нем)
Особенно неявно все сделано в bootstrap. Там часто наличие определенного класса или атрибута на элементе вызывает появление на нем обработчика. Это очень неочевидная схема.
- код инициализации может разрастаться со временем и часто работать вхолостую (опять же, грамотное разделение на небольшие файлы может помочь)
- не всегда получается навесить обработчик для динамически добавляемого контента (отчасти решается навешиванием обработчика не на каждый элемент, а на уровень выше в DOM)
- кнопка "мертвая" с момента появления до момента загрузки скрипта и срабатывания события docuemnt ready (решение: либо делаем graceful degradation, чтобы до загрузки скрипта кнопка работала без JS, либо скрываем кнопку до инициализации, например, прописав ей класс с visibility: hidden и снимая этот класс при инициализации). Кстати, про "мертвые" кнопки по моему нигде не пишут, неужели я один знаю про эту особенность?
3) смешанные варианты
Например, вариант 1, но с асинхронной загрузкой кода или вариант 2, с инициализацией не во внешнем файле, а в inline-скрипте.
Проблемы, встречающиеся на больших сайтах:
Проблема: большой объем кода. Трудно разобраться, надо грузить много лишнего кода, который не нужен на странице.
Решение: разбиваем код на файлы. Критерии разбиения могут быть разные, например: один класс на файл, один файл на страницу или раздел, один файл на каждый виджет. Главное, чтобы там была какая-то логика.
Например, в идеологии БЭМ мы можем каждый виджет (элемент страницы) делать независимым и класть в свою папку, где лежит его шаблон, его стили, управляющие скрипты: https://ru.bem.info/methodology/filestructure/ . Ну например, мы можем сделать отдельную папку для виджета просмотра и добавления комментариев, после чего легко добавлять его на любую страницу.
Разбиение на компоненты используется и в Angular, и в React, но делать его можно и без этих библиотек.
Проблема: синхронные скрипты блокируют загрузку страницы
Решение: используем progressive enhancement и асинхронную загрузку. То есть загружается просто базовая HTML версия страницы, затем асинхронно грузятся скрипты и обогащают страницу.
Проблема: асинхронно загруженный скрипт не знает, есть ли уже в дереве DOM нужные ему элементы или нет
Решение: либо используем document.ready, либо пишем после кода виджета inline-скрипт, который вызывает фукнцию инициализации явно. Во втором случае элемент инициализируется раньше, чем если бы мы ждали события ready.
Проблема: скрипты могут иметь зависимости и их требуется грузить в определенном порядке. Например, скрипт, использующий jQuery, можно запускать только после загрузки этой библиотеки.
Решение: модули, ES6 модули, AMD, загрузчики вроде require.js. Они позволяют указать зависимости модулей друг от друга.
Проблема: элементы не реагируют на события до загрузки управляющего скрипта
Решение: либо используем graceful degradation (например, ссылка до загрузки скрипта работает как ссылка, а после - как кнопка) либо скрываем элементы. При скрытии желательно использовать visibility: hidden или opcaity, чтобы верстка не прыгала при появлении элемента.
Проблема: в разных скриптах может оказаться функция или переменная с одинаковым именем.
Решение: префиксы в именах функций или модули.
Ответы на вопросы:
> Я ещё не освоился с JS, но пока могу сказать, что это нужно разделить либо на отдельные функции, либо разбить на два класса - Helper и Handler, который вешает обработчики (к примеру, Handler.prototype.handleClickOnImg...).
Не уверен, что в этом есть смысл, разбивать код одного виджета на 2 части.
Кстати, по поводу закрытия при клике на фон - видел такую реализацию, что при наведении на фон (за пределами попапа) курсор превращается в крестик. Чтобы намекнуть, что он закрывает попап. А при наведении на превьюшку курсор можно превращать в лупу.
По коду: https://codepen.io/anon/pen/YLGNRY?editors=0010
Не очень понятно, зачем там строка this.fullsize = $('.fullsize') встречается 2 раза.
> var content = $('<img/>', {class: 'fullsize', src: src});
> $(content).on('load', function() {
Нет ли тут риска, что событие load может произойти до навешивания обработчика? Я не уверен, как все это работает, но по моему src было бы безопаснее задавать после навешивания обработчика.
Нет индикации того, что идет загрузка.
В коде очень глубокая вложенность. 3-4 уровня это максимум.
Это сделано очень неудачно:
> много строк...
> this.visible = true;
> }
> }.bind(this));
Непонятно, к чему вообще относится bind(this). Не надо так писать, огромную функцию и после нее сразу же bind. Вообще, это плохая идея делать гигантские анонимные функции. Ее надо разбить на функции поменьше. Зачем вообще вкладывать несколько функций друг в друга, когда можно их писать просто вертикально друг над другом?
Непонятно, какие методы у объекта публичные, а какие приватные и не должны вызываться снаружи.
По коду https://github.com/richBlueElephant/phpClub/blob/master/public/media/js/script.js
Вот тут видна проблема работающих вхолостую скриптов. На странице может не быть постов, которым требуется превью, а скрипт для них все равно запустится и повесит обработчики.
Также, здесь ставится глобальный обработчик в PostPreview, который будет вызываться при любом движении мыши на странице. Обработчики в атрибутах имеют то преимущество, что они вызываются только при наведении мыши на ссылку. Но конечно имеют и недостатки.
Это конечно скорее проблема плохого дизайна обработчиков событий в браузере, что нам приходится дергать JS скрипт на каждое событие прокрутки или движения мыши. Причем они еще и синхронные - пока обработчик не отработает, браузер ждет и ничего не делает.
Тут в коде та же проблема- огромная вложенность и стремление писать стены кода вместо разделения на функици.
При отправке запроса желательно сделать индикатор. На доброчане, например, яблочко крутится, как я помню, при наведении на ссылку.
Если код состоит из отдельных независимых классов, может стоит разбить его на отдельные файлы.
Если пост есть на странице, возможно не требуется дергать API.
jQuery по умолчанию отключает кеширование в GET-запросе добавлением случайного параметра в URL. Если посты редко меняются, имеет смысл включить кеширование и на сервере отдавать заголовки для безусловного кеширования на небольшое время. А может, еще и поддерживать кеширование через if-modified-since.
В обработчике scroll уменьшение частоты вызовов лучше сделать отдельной функцией, можно даже найти готовую, они называются вроде debounce ( https://davidwalsh.name/javascript-debounce-function ). Это явно стоит вынести в отдельную функцию.
В обработчике попапа, возможно, стоит реагировать на $(window).resize и обновлять позицию попапа, опять же, с применением debounce.
Формы вроде "зделаешь" не учтены, но в общем, решено правильно.
>>178355
Я не очень помню уже, о чем был исходный пост, но просто напишу, какие есть вариант организации JS кода на больших сайтах, какие встречаются проблемы и какие бывают варианты решения.
Начнем с вопроса, как можно инициализировать код и подключать обработчики.
1) "классический" вариант: подключаем скрипты в шапке документа, содержащие функции, а в коде прописываем обработчики в атрибутах вроде <button onclick="..">, которые вызывают эти функции.
Плюс:
- простота и понятность, можно в один клик перейти к функции
- обработчики работают в момент появления элемента на странице (браузер парсит HTML по мере получения и отображает частично загруженную страницу), нет промежутка времени, когда они "мертвые"
- нет проблем с навешиванием обработчиков на динамически доабвляемый на страницу контент
Минус:
- скрипты в шапке блокируют разбор HTML до их загрузку и нешуточно замедляют загрузку страницы на мобильном соединении если их много (с HTTP/2 это чуть улучшилось, но все равно мешает загрузке)
2) вешаем обработчики через функции вроде $(document).ready() в внешних файлах. Этот подход описан довольно давно (статья 2006 года http://alistapart.com/article/behavioralseparation ).
Плюс:
- достигается полное отделение JS от HTML (некоторые вообще против inline-скриптов и атрибутов onxxx)
- это подается под соусом progressive enhancement
- скрипт не требуется загружать заранее в шапке, его можно загрузить асинхронно уже после загрузки страницы
Минус:
- не очевидно, как найти обработчик для кнопки. Это отчасти решается при грамотной организации кода (например, если у каждого виджета или страницы свой управляющий JS-файл и он небольшой, то понятно, что обработчик надо искать в нем)
Особенно неявно все сделано в bootstrap. Там часто наличие определенного класса или атрибута на элементе вызывает появление на нем обработчика. Это очень неочевидная схема.
- код инициализации может разрастаться со временем и часто работать вхолостую (опять же, грамотное разделение на небольшие файлы может помочь)
- не всегда получается навесить обработчик для динамически добавляемого контента (отчасти решается навешиванием обработчика не на каждый элемент, а на уровень выше в DOM)
- кнопка "мертвая" с момента появления до момента загрузки скрипта и срабатывания события docuemnt ready (решение: либо делаем graceful degradation, чтобы до загрузки скрипта кнопка работала без JS, либо скрываем кнопку до инициализации, например, прописав ей класс с visibility: hidden и снимая этот класс при инициализации). Кстати, про "мертвые" кнопки по моему нигде не пишут, неужели я один знаю про эту особенность?
3) смешанные варианты
Например, вариант 1, но с асинхронной загрузкой кода или вариант 2, с инициализацией не во внешнем файле, а в inline-скрипте.
Проблемы, встречающиеся на больших сайтах:
Проблема: большой объем кода. Трудно разобраться, надо грузить много лишнего кода, который не нужен на странице.
Решение: разбиваем код на файлы. Критерии разбиения могут быть разные, например: один класс на файл, один файл на страницу или раздел, один файл на каждый виджет. Главное, чтобы там была какая-то логика.
Например, в идеологии БЭМ мы можем каждый виджет (элемент страницы) делать независимым и класть в свою папку, где лежит его шаблон, его стили, управляющие скрипты: https://ru.bem.info/methodology/filestructure/ . Ну например, мы можем сделать отдельную папку для виджета просмотра и добавления комментариев, после чего легко добавлять его на любую страницу.
Разбиение на компоненты используется и в Angular, и в React, но делать его можно и без этих библиотек.
Проблема: синхронные скрипты блокируют загрузку страницы
Решение: используем progressive enhancement и асинхронную загрузку. То есть загружается просто базовая HTML версия страницы, затем асинхронно грузятся скрипты и обогащают страницу.
Проблема: асинхронно загруженный скрипт не знает, есть ли уже в дереве DOM нужные ему элементы или нет
Решение: либо используем document.ready, либо пишем после кода виджета inline-скрипт, который вызывает фукнцию инициализации явно. Во втором случае элемент инициализируется раньше, чем если бы мы ждали события ready.
Проблема: скрипты могут иметь зависимости и их требуется грузить в определенном порядке. Например, скрипт, использующий jQuery, можно запускать только после загрузки этой библиотеки.
Решение: модули, ES6 модули, AMD, загрузчики вроде require.js. Они позволяют указать зависимости модулей друг от друга.
Проблема: элементы не реагируют на события до загрузки управляющего скрипта
Решение: либо используем graceful degradation (например, ссылка до загрузки скрипта работает как ссылка, а после - как кнопка) либо скрываем элементы. При скрытии желательно использовать visibility: hidden или opcaity, чтобы верстка не прыгала при появлении элемента.
Проблема: в разных скриптах может оказаться функция или переменная с одинаковым именем.
Решение: префиксы в именах функций или модули.
Ответы на вопросы:
> Я ещё не освоился с JS, но пока могу сказать, что это нужно разделить либо на отдельные функции, либо разбить на два класса - Helper и Handler, который вешает обработчики (к примеру, Handler.prototype.handleClickOnImg...).
Не уверен, что в этом есть смысл, разбивать код одного виджета на 2 части.
Кстати, по поводу закрытия при клике на фон - видел такую реализацию, что при наведении на фон (за пределами попапа) курсор превращается в крестик. Чтобы намекнуть, что он закрывает попап. А при наведении на превьюшку курсор можно превращать в лупу.
По коду: https://codepen.io/anon/pen/YLGNRY?editors=0010
Не очень понятно, зачем там строка this.fullsize = $('.fullsize') встречается 2 раза.
> var content = $('<img/>', {class: 'fullsize', src: src});
> $(content).on('load', function() {
Нет ли тут риска, что событие load может произойти до навешивания обработчика? Я не уверен, как все это работает, но по моему src было бы безопаснее задавать после навешивания обработчика.
Нет индикации того, что идет загрузка.
В коде очень глубокая вложенность. 3-4 уровня это максимум.
Это сделано очень неудачно:
> много строк...
> this.visible = true;
> }
> }.bind(this));
Непонятно, к чему вообще относится bind(this). Не надо так писать, огромную функцию и после нее сразу же bind. Вообще, это плохая идея делать гигантские анонимные функции. Ее надо разбить на функции поменьше. Зачем вообще вкладывать несколько функций друг в друга, когда можно их писать просто вертикально друг над другом?
Непонятно, какие методы у объекта публичные, а какие приватные и не должны вызываться снаружи.
По коду https://github.com/richBlueElephant/phpClub/blob/master/public/media/js/script.js
Вот тут видна проблема работающих вхолостую скриптов. На странице может не быть постов, которым требуется превью, а скрипт для них все равно запустится и повесит обработчики.
Также, здесь ставится глобальный обработчик в PostPreview, который будет вызываться при любом движении мыши на странице. Обработчики в атрибутах имеют то преимущество, что они вызываются только при наведении мыши на ссылку. Но конечно имеют и недостатки.
Это конечно скорее проблема плохого дизайна обработчиков событий в браузере, что нам приходится дергать JS скрипт на каждое событие прокрутки или движения мыши. Причем они еще и синхронные - пока обработчик не отработает, браузер ждет и ничего не делает.
Тут в коде та же проблема- огромная вложенность и стремление писать стены кода вместо разделения на функици.
При отправке запроса желательно сделать индикатор. На доброчане, например, яблочко крутится, как я помню, при наведении на ссылку.
Если код состоит из отдельных независимых классов, может стоит разбить его на отдельные файлы.
Если пост есть на странице, возможно не требуется дергать API.
jQuery по умолчанию отключает кеширование в GET-запросе добавлением случайного параметра в URL. Если посты редко меняются, имеет смысл включить кеширование и на сервере отдавать заголовки для безусловного кеширования на небольшое время. А может, еще и поддерживать кеширование через if-modified-since.
В обработчике scroll уменьшение частоты вызовов лучше сделать отдельной функцией, можно даже найти готовую, они называются вроде debounce ( https://davidwalsh.name/javascript-debounce-function ). Это явно стоит вынести в отдельную функцию.
В обработчике попапа, возможно, стоит реагировать на $(window).resize и обновлять позицию попапа, опять же, с применением debounce.
В MyISAM нельзя заблокировать одну строку, только таблицу целиком. Подозреваю, что будет либо ошибка, либо mysql молча проигнорирует требование блокировки.
> Мне нужно удалить запись из основной, предварительно почистив все остальные. Но есть вероятность, что пока я буду их чистить и дойду до удаления основной, в какую-то из них опять что-то добавят.
Нужно обернуть операцию в транзакцию, а может быть, просто связать записи внешними ключами, чтобы при удалении главной записи зависимые от нее удалялись автоматически.
> В то-же время не хотелось бы лочить их все. И не хотелось бы получить висяки.
Это необходимо. Вряд ли у тебя куча транзакций обращается в один момент времени к одной и той же строке.
> Я так понимаю, если добавить констрейнты foreign key, то мне просто не даст удалить запись если в процессе кто-то что-то опять добавит, но такой вариант мне тоже не нравится - просто inconvinient для пользователя.
Там есть разные варианты, например, автоматически удалять зависимые записи при удалении главной.
> Что если дописать там поле допустим QUEUED_FOR_DELETION, и во всех местах приложения не давать добавлять связи для таких сущностей, а потом когда-нибудь их удалить. Можно было бы конечно вообще не удалять в таком случае но таблица будет расти и место там не резиновое.
По моему ты изобретаешь велосипед и это здесь не нужно.
>>179285
Нет, неправильно. Ты не меняешь значения firstletter и lastletter и они всегда одинаковые, на каждом шаге цикла.
>>179478
Апач и нгинкс изучи.
>>179617
По моему это усложнение. Вызов внешней команды это сложнее и медленнее, чем вызов библиотечной функции. Для ресайза в PHP можно найти готовую библиотеку или написать несложный код на gd.
>>179739
Получается да, нужна таблица для хранения прогресса и состояний. Хотя так ли она нужна? Ты же можешь просто в общей таблице файлов хранить, есть ли у файла превьюшка или что ты там генерируешь.
Надо учесть, что задача генерации превьюшки или видео может провалиться. Возможно, стоит предусмотреть несколько попыток конвертации. И ручной запуск переконвертации проблемных файлов.
В MyISAM нельзя заблокировать одну строку, только таблицу целиком. Подозреваю, что будет либо ошибка, либо mysql молча проигнорирует требование блокировки.
> Мне нужно удалить запись из основной, предварительно почистив все остальные. Но есть вероятность, что пока я буду их чистить и дойду до удаления основной, в какую-то из них опять что-то добавят.
Нужно обернуть операцию в транзакцию, а может быть, просто связать записи внешними ключами, чтобы при удалении главной записи зависимые от нее удалялись автоматически.
> В то-же время не хотелось бы лочить их все. И не хотелось бы получить висяки.
Это необходимо. Вряд ли у тебя куча транзакций обращается в один момент времени к одной и той же строке.
> Я так понимаю, если добавить констрейнты foreign key, то мне просто не даст удалить запись если в процессе кто-то что-то опять добавит, но такой вариант мне тоже не нравится - просто inconvinient для пользователя.
Там есть разные варианты, например, автоматически удалять зависимые записи при удалении главной.
> Что если дописать там поле допустим QUEUED_FOR_DELETION, и во всех местах приложения не давать добавлять связи для таких сущностей, а потом когда-нибудь их удалить. Можно было бы конечно вообще не удалять в таком случае но таблица будет расти и место там не резиновое.
По моему ты изобретаешь велосипед и это здесь не нужно.
>>179285
Нет, неправильно. Ты не меняешь значения firstletter и lastletter и они всегда одинаковые, на каждом шаге цикла.
>>179478
Апач и нгинкс изучи.
>>179617
По моему это усложнение. Вызов внешней команды это сложнее и медленнее, чем вызов библиотечной функции. Для ресайза в PHP можно найти готовую библиотеку или написать несложный код на gd.
>>179739
Получается да, нужна таблица для хранения прогресса и состояний. Хотя так ли она нужна? Ты же можешь просто в общей таблице файлов хранить, есть ли у файла превьюшка или что ты там генерируешь.
Надо учесть, что задача генерации превьюшки или видео может провалиться. Возможно, стоит предусмотреть несколько попыток конвертации. И ручной запуск переконвертации проблемных файлов.
Проверь заголовки кеширования, которые отдаются со статикой. Ты уверен, что у тебя не включено принудительное кеширование всего в браузере?
Алсо, эти watch скрипты часто едят процессор, хотя по логике не должны бы.
>>179898
>>182122
Есть готовые сервисы вроде ping-admin, погугли.
Твой вариант годится, но тебе надо его реализовать и поддерживать. Там много проверок - надо проверять дату истечения домена, попадание его в черные списки итд.
>>180220
Не найдет адрес вроде xOJYyzANUS000M.O0PUNCTUMc;GRom
До символа @ могут встретиться символы +, точка, процент и наверно еще куча символов.
Вывыодить надо не массив с кучей лишних элементов, а только список email.
>>180308
> Обмен валют - https://ideone.com/GxnpIE
Верно.
> Игра в кубики - https://ideone.com/fpmVXk
Вместо $humanFRand лучше было сделать humanRand1 - цифра читается лучше, чем S или F.
Решено верно.
> Таблица умножения - https://ideone.com/XIc2VN
Верно.
> Процент вклада - https://ideone.com/v9dwvI
Правильно.
> Расчет кредита - https://ideone.com/UOy1TU
> Можно было через for, в котором сделать проверки if sumCredit < 5000 и т.д.
Так наверно и стоило ради простоты кода. А то тут трудно проверить, правильно ли все считается.
> Средний балл - https://ideone.com/Wu7ve0
Правильно.
> Сравнение роста - https://ideone.com/0iNujR
Верно.
> РулетОчка - https://ideone.com/akfHyX
Правильно.
> Генератор имён - https://ideone.com/qTV2wH
> $name[$i]
Можно просто писать $name[] = ..., чтобы индексы вычислялись автоматически. А так, верно.
> Шифровка - https://ideone.com/M9ebqb
Праивльно.
Проверь заголовки кеширования, которые отдаются со статикой. Ты уверен, что у тебя не включено принудительное кеширование всего в браузере?
Алсо, эти watch скрипты часто едят процессор, хотя по логике не должны бы.
>>179898
>>182122
Есть готовые сервисы вроде ping-admin, погугли.
Твой вариант годится, но тебе надо его реализовать и поддерживать. Там много проверок - надо проверять дату истечения домена, попадание его в черные списки итд.
>>180220
Не найдет адрес вроде xOJYyzANUS000M.O0PUNCTUMc;GRom
До символа @ могут встретиться символы +, точка, процент и наверно еще куча символов.
Вывыодить надо не массив с кучей лишних элементов, а только список email.
>>180308
> Обмен валют - https://ideone.com/GxnpIE
Верно.
> Игра в кубики - https://ideone.com/fpmVXk
Вместо $humanFRand лучше было сделать humanRand1 - цифра читается лучше, чем S или F.
Решено верно.
> Таблица умножения - https://ideone.com/XIc2VN
Верно.
> Процент вклада - https://ideone.com/v9dwvI
Правильно.
> Расчет кредита - https://ideone.com/UOy1TU
> Можно было через for, в котором сделать проверки if sumCredit < 5000 и т.д.
Так наверно и стоило ради простоты кода. А то тут трудно проверить, правильно ли все считается.
> Средний балл - https://ideone.com/Wu7ve0
Правильно.
> Сравнение роста - https://ideone.com/0iNujR
Верно.
> РулетОчка - https://ideone.com/akfHyX
Правильно.
> Генератор имён - https://ideone.com/qTV2wH
> $name[$i]
Можно просто писать $name[] = ..., чтобы индексы вычислялись автоматически. А так, верно.
> Шифровка - https://ideone.com/M9ebqb
Праивльно.
Увы, ideone теперь тоже сломан, перекатывайтесь куда-нибудь еще.
>>181775
Школьник может платить от 0 до 5000 р в месяц. Он платит как можно больше, чтобы быстрее выплатить кредит и меньше платить процентов. То есть он платит меньшее из чисел (5000, остаток долга).
Код выглядит примерно так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
Ответ тогда получится правильный.
Также, там сначала добавляются проценты, а потом школьник платит. У тебя наоборот.
>>182086
Вот кстати по чату, очень бы хотелось, чтобы ты для себя сделал первым приоритетом простой, понятный код. Чтобы в нем было легко разбираться. Мы же в PHP разделяем код на классы, соблюдаем правила оформления, пишем комментарии - хотелось бы и в JS то же самое.
Сторонние скрипты лучше положить в отдельную папку от своих.
>>182092
А какой в этом смысл? Достаточно проверить половину символов.
А так, решено верно.
>>182336
Посмотри в инструментах разработчика (Ctrl + Shift + I), нет ли ошибок на вкладке Network и Console. Не надо гадать.
Увы, ideone теперь тоже сломан, перекатывайтесь куда-нибудь еще.
>>181775
Школьник может платить от 0 до 5000 р в месяц. Он платит как можно больше, чтобы быстрее выплатить кредит и меньше платить процентов. То есть он платит меньшее из чисел (5000, остаток долга).
Код выглядит примерно так:
- прибавляем проценты и комиссию к остатку долга (!не вычитаем ничего пока!)
- если остаток маленький, выплачиваем сколько осталось и уходим
- иначе платим 5000
«Платим» здесь значит уменьшаем долг и увеличиваем общую сумму выплаченного.
Ответ тогда получится правильный.
Также, там сначала добавляются проценты, а потом школьник платит. У тебя наоборот.
>>182086
Вот кстати по чату, очень бы хотелось, чтобы ты для себя сделал первым приоритетом простой, понятный код. Чтобы в нем было легко разбираться. Мы же в PHP разделяем код на классы, соблюдаем правила оформления, пишем комментарии - хотелось бы и в JS то же самое.
Сторонние скрипты лучше положить в отдельную папку от своих.
>>182092
А какой в этом смысл? Достаточно проверить половину символов.
А так, решено верно.
>>182336
Посмотри в инструментах разработчика (Ctrl + Shift + I), нет ли ошибок на вкладке Network и Console. Не надо гадать.
Спасибо за сообщение, что-то я сразу не понял.
>>182378
А зачем их сохранять в файле? непонятно. Используй что-то стандартное, например, сессии или храни данные в БД. Ну или пиши свой код для проверки и обновления данных в файле.
>>182405
Насчет нестандартных методов - некоторые роутеры умеют проверять методы и указывать, для каких роутов какие методы разрешены. Symfony Routing это умеет.
> if (strpos($_SERVER['REQUEST_URI'], '/home/') === 0) {
Это плохой подход, так как у тебя дял страницы есть огромное число URL вроде /home/1, /home/2 и тд. А с точки зрения логики и SEO у каждой страницы должен быть 1 URL.
> case 'HEAD':
> exit; // HEAD не требует отдачи контента, выходим
это неправильная реализация, так как HEAD должен отдать ровно те же заголовки, что и GET, только без тела. У тебя это не реализовано. Например, в твоем коде для страницы с 404 HEAD отдаст код 200, а это неправильно. HEAD проще всего реализовать, делая GET и отрезая от него тело.
Смысл роутера - чтобы не писать лапшу из if, а просто сделать конфиг.
> try/catch выпилил, самому не нравилась идея оборачивать каждый код с exeptionon'ом внутри в эту конструкцию. А если я например чужой класс ипользую - мне нужно идти смотреть получается нет ли там внутри выброса исключений и на методы внутри которых есть рисовать try/catch каждый раз? Звучит тупо, так что только рад что это всё не нужно.
try/catch ты пишешь, только если тебе надо ловить исключение и как-то на него реагировать. Иначе не надо ничего писать. То, что функция выбрасывает исключение, не значит, что ты обязан его ловить. Это дело добровольное.
> Убрал, но как в другом месте это выводить или впиливать - не понял.
Просто прописать во вью либо сделать массив вида ['name' => ..., 'company' => ...] либо такой же объект.
> Ок, обернул, только ведь это имеет смысл если мы выводим информацию, на которую как-то могут влиять сами юзеры, а так то кто в мою вьюху что подсунет
Лучше сразу привыкать делать правильно. Плюс, если у тебя в названии компании будет <s> то без htmlspecialchars оно вставится как тег.
> >> abstract protected function setDefaults();
> Я вообще не понимаю что тут от меня нужно.
У тебя, чтобы объект Employee правильно работал, в нем надо задать зарплату, кофе и тд. Это можно сделать по-разному:
- сделать такие аргументы в конструкторе Employee, чтобы нельзя было создать объект без их задания
- сделать абстрактные методы, чтобы нельзя было объявить класс-наследник без указания зарплаты и потребл. кофе
> И тут становится понятно, что просто ретернить захардкоженные значения уже не работает. Ведь их придется менять, а что бы менять - их нужно где-то хранить
Можно ввести 3 разных понятия:
- стартовая базовая зарплата - неизменна, возвращается абстрактными методами
- текущая базовая зарплата - хранится в поле, меняется. При создании объекта сюда копируется значение стартовой зарплаты.
- итоговая зарплата с учетом надбавок - нигде не хранится, вычисляется методом
Либо можно при создании объекта требовать указать стартовую зарплату в конструкторе.
В твоем коде никак не проверяется, что стартовая зарплата задана. Легко забыть это сделать при написании класса-наследника.
> Так же добавил свистелку в виде методов setTimePoint() и benchMark()
Почитай мануал по microtime - explode давно уже не нужен. Также, а зачем это нужно, если можно с помощью расширения xdebug записать трейс и в нем посмотреть, какие функции когда вызвались и сколько времени заняли? Погугли xdebug tracing.
> В итоге 2 главных проблемы так и не решил - OrgInfo ращбил на кучу копипастных методов, которые пусть и удобные, но само их существование мне не нравится и они дублируются и лишь загромождают код,
Тут наверно ничего не сделать, благо методы крошечные. Сложные классы могут содержать и по 1000, и по 2000 строк, так что ничего. Ну либо придется выносить это в обертку, которой мы передаем компанию и в которой есть методы для вычисления суммы и среднего.
Можно было еще как-то заморочиться с такой штукой, где один метод умеет считать сумму любого показателя:
public function getTotal($type) {
...
}
По коду:
Для методов, которые ничего не возвращают, можно писать тайп-хинт void.
> public function getDepartments(): array {
> if ($this->countDepartments() == 0) {
> throw new Exception('Пустая организация, нечего выводить.');
Я думаю, логичнее возвращать пустой массив. Разве это ошибка - запросить список департаментов, если их пока не добавили?
> $classMatch = (get_class($employee) == $this->getClass());
Может быть лучше instanceof, чтобы искались бы и наследники класса? По правилу подстановки Лисков наследник класса может использоваться вместо него.
> if ($classMatch and $rangMatch and $leaderMatch) {
> return true;
Можно писать return $classMatch && $rangMatch && $leaderMatch;
> if (in_array($employee, $fireList)) {
Тут наверно нужен еще true
> abstract protected function setDefaults();
Недостаток твоего решения тут в том, что этот метод не обязывает нас что-то делать. И не документирует, что он должен делать. Мы можем просто сделать пустой метод или забыть что-то в нем задать. Потому тут лучше было бы сделать 3 отдельных абстрактных метода вроде
abstract protected function getDefalutSalary(): float
В этом случае все понятнее, и невозможно обойтись без указания зарплаты.
> new EmployeeSelector('Engineer', null, null);
имя класса лучше писать как Engineer::class - тогда при опечатке будет выдана ошибка.
> $engineersList = $employeeSelector->filterEmployees($dep->getEmployees());
Тут еще можно было сделать вариант $dep->findEmployees($selector); но твой вариант тоже годится конечно.
Для селектора, если у тебя мало условий поиска, можно передавать их в конструктор, но когда условий много, удобнее их задавать через методы, например:
$selector = new Selector;
$selector->setMinRang(2);
$selector->setMinExperience(2);
$selector->setSalaryRange(100, 500);
Также, есть еще интересный вариант, когда селектор выражается анонимной функцией, возвращающей true/false:
$list = $dep->findEmployees(function (Employee $e) {
return $e->getRank() == 2 && $e instanceof Engineer;
});
Если в PHP добавят стрелочные функции ( https://wiki.php.net/rfc/arrow_functions ), этот код будет еще короче:
$list = $dep->findEmployees(fn($e) => $e->getRank() == 2 && $e instanceof Engineer);
Более того, в объектах есть метод __invoke, который позволяет "вызывать" их как функцию: http://php.net/manual/ru/language.oop5.magic.php#object.invoke
Если твой Selector реализует этот метод, то его можно будет передавать в ту же самую функцию вместо анонимной функции.
В общем, сделано верно. Надеюсь, ты смог увидеть преимущества ООП в этой задаче, как ООП позволяет организовать большой и сложный код, разбив его на отдельные классы. Если хочется еще потренироваться с ООП, могу посоветовать задачу про Гостиницу: https://phpclub.tech/pr/res/1082507.html#1097078
> try/catch выпилил, самому не нравилась идея оборачивать каждый код с exeptionon'ом внутри в эту конструкцию. А если я например чужой класс ипользую - мне нужно идти смотреть получается нет ли там внутри выброса исключений и на методы внутри которых есть рисовать try/catch каждый раз? Звучит тупо, так что только рад что это всё не нужно.
try/catch ты пишешь, только если тебе надо ловить исключение и как-то на него реагировать. Иначе не надо ничего писать. То, что функция выбрасывает исключение, не значит, что ты обязан его ловить. Это дело добровольное.
> Убрал, но как в другом месте это выводить или впиливать - не понял.
Просто прописать во вью либо сделать массив вида ['name' => ..., 'company' => ...] либо такой же объект.
> Ок, обернул, только ведь это имеет смысл если мы выводим информацию, на которую как-то могут влиять сами юзеры, а так то кто в мою вьюху что подсунет
Лучше сразу привыкать делать правильно. Плюс, если у тебя в названии компании будет <s> то без htmlspecialchars оно вставится как тег.
> >> abstract protected function setDefaults();
> Я вообще не понимаю что тут от меня нужно.
У тебя, чтобы объект Employee правильно работал, в нем надо задать зарплату, кофе и тд. Это можно сделать по-разному:
- сделать такие аргументы в конструкторе Employee, чтобы нельзя было создать объект без их задания
- сделать абстрактные методы, чтобы нельзя было объявить класс-наследник без указания зарплаты и потребл. кофе
> И тут становится понятно, что просто ретернить захардкоженные значения уже не работает. Ведь их придется менять, а что бы менять - их нужно где-то хранить
Можно ввести 3 разных понятия:
- стартовая базовая зарплата - неизменна, возвращается абстрактными методами
- текущая базовая зарплата - хранится в поле, меняется. При создании объекта сюда копируется значение стартовой зарплаты.
- итоговая зарплата с учетом надбавок - нигде не хранится, вычисляется методом
Либо можно при создании объекта требовать указать стартовую зарплату в конструкторе.
В твоем коде никак не проверяется, что стартовая зарплата задана. Легко забыть это сделать при написании класса-наследника.
> Так же добавил свистелку в виде методов setTimePoint() и benchMark()
Почитай мануал по microtime - explode давно уже не нужен. Также, а зачем это нужно, если можно с помощью расширения xdebug записать трейс и в нем посмотреть, какие функции когда вызвались и сколько времени заняли? Погугли xdebug tracing.
> В итоге 2 главных проблемы так и не решил - OrgInfo ращбил на кучу копипастных методов, которые пусть и удобные, но само их существование мне не нравится и они дублируются и лишь загромождают код,
Тут наверно ничего не сделать, благо методы крошечные. Сложные классы могут содержать и по 1000, и по 2000 строк, так что ничего. Ну либо придется выносить это в обертку, которой мы передаем компанию и в которой есть методы для вычисления суммы и среднего.
Можно было еще как-то заморочиться с такой штукой, где один метод умеет считать сумму любого показателя:
public function getTotal($type) {
...
}
По коду:
Для методов, которые ничего не возвращают, можно писать тайп-хинт void.
> public function getDepartments(): array {
> if ($this->countDepartments() == 0) {
> throw new Exception('Пустая организация, нечего выводить.');
Я думаю, логичнее возвращать пустой массив. Разве это ошибка - запросить список департаментов, если их пока не добавили?
> $classMatch = (get_class($employee) == $this->getClass());
Может быть лучше instanceof, чтобы искались бы и наследники класса? По правилу подстановки Лисков наследник класса может использоваться вместо него.
> if ($classMatch and $rangMatch and $leaderMatch) {
> return true;
Можно писать return $classMatch && $rangMatch && $leaderMatch;
> if (in_array($employee, $fireList)) {
Тут наверно нужен еще true
> abstract protected function setDefaults();
Недостаток твоего решения тут в том, что этот метод не обязывает нас что-то делать. И не документирует, что он должен делать. Мы можем просто сделать пустой метод или забыть что-то в нем задать. Потому тут лучше было бы сделать 3 отдельных абстрактных метода вроде
abstract protected function getDefalutSalary(): float
В этом случае все понятнее, и невозможно обойтись без указания зарплаты.
> new EmployeeSelector('Engineer', null, null);
имя класса лучше писать как Engineer::class - тогда при опечатке будет выдана ошибка.
> $engineersList = $employeeSelector->filterEmployees($dep->getEmployees());
Тут еще можно было сделать вариант $dep->findEmployees($selector); но твой вариант тоже годится конечно.
Для селектора, если у тебя мало условий поиска, можно передавать их в конструктор, но когда условий много, удобнее их задавать через методы, например:
$selector = new Selector;
$selector->setMinRang(2);
$selector->setMinExperience(2);
$selector->setSalaryRange(100, 500);
Также, есть еще интересный вариант, когда селектор выражается анонимной функцией, возвращающей true/false:
$list = $dep->findEmployees(function (Employee $e) {
return $e->getRank() == 2 && $e instanceof Engineer;
});
Если в PHP добавят стрелочные функции ( https://wiki.php.net/rfc/arrow_functions ), этот код будет еще короче:
$list = $dep->findEmployees(fn($e) => $e->getRank() == 2 && $e instanceof Engineer);
Более того, в объектах есть метод __invoke, который позволяет "вызывать" их как функцию: http://php.net/manual/ru/language.oop5.magic.php#object.invoke
Если твой Selector реализует этот метод, то его можно будет передавать в ту же самую функцию вместо анонимной функции.
В общем, сделано верно. Надеюсь, ты смог увидеть преимущества ООП в этой задаче, как ООП позволяет организовать большой и сложный код, разбив его на отдельные классы. Если хочется еще потренироваться с ООП, могу посоветовать задачу про Гостиницу: https://phpclub.tech/pr/res/1082507.html#1097078
А какие проверки тебе нужны? Я не понимаю вопрос.
Могу только дать ссылку на определение слова авторизация: https://ru.wikipedia.org/wiki/Авторизация
>>182659
Библиотека getId3 по моему.
>>182598
Библиотекам часто нужны зависимости. Удобнее по моему иметь небольшой списочек библиотек в файле, чем вручную их все качать, искать их зависимости, при обновлении все это заново делать итд
В чем недостаток bower? То, что ты его не осилил, это не аргумент.
>>182722
Ну так JS код надо точно так же организовать, как PHP: отделить логику от интерфейса, разбить на функции, прокомментировать, соблюдать стиль написания кода.
У тебя явно можно разнести логику загрузчика файла и логику отображения. Например:
var form = $('#upload-form');
form.on('submit', function () {
lockSubmitButton(form);
showProgress();
uploader.uploadForm(form, function (response) {
unlockSubmitButton(form);
hideProgress();
showMessage('Success');
}, function (reason) {
unlockSubmitButton(form);
hideProgress();
showMessage('Error: '+ reason);
});
});
Или то же, но с промисами:
form.on('submit', function () {
lockSubmitButton(form);
showProgress();
var response = uploader.uploadForm(form);
response.success(function (response) {
unlockSubmitButton(form);
hideProgress();
showMessage('Success');
});
response.error(function (reason) {
unlockSubmitButton(form);
hideProgress();
showMessage('Error: '+ reason);
});
});
Чтобы не ездить по нодам, возьми библиотеку для DOM traversing или jQuery.
>>182742
Простую форму загрузки файла можно сделать без React. Более того, React довольно тяжелый и там по моему Hello World под мегабайт весит.
>>183002
Typescript неплохая вещь, но к вопросу он мало отношения имеет. Да и не очень удобно, постоянно перекомпилировать вместо того, чтобы просто обновить страницу.
А какие проверки тебе нужны? Я не понимаю вопрос.
Могу только дать ссылку на определение слова авторизация: https://ru.wikipedia.org/wiki/Авторизация
>>182659
Библиотека getId3 по моему.
>>182598
Библиотекам часто нужны зависимости. Удобнее по моему иметь небольшой списочек библиотек в файле, чем вручную их все качать, искать их зависимости, при обновлении все это заново делать итд
В чем недостаток bower? То, что ты его не осилил, это не аргумент.
>>182722
Ну так JS код надо точно так же организовать, как PHP: отделить логику от интерфейса, разбить на функции, прокомментировать, соблюдать стиль написания кода.
У тебя явно можно разнести логику загрузчика файла и логику отображения. Например:
var form = $('#upload-form');
form.on('submit', function () {
lockSubmitButton(form);
showProgress();
uploader.uploadForm(form, function (response) {
unlockSubmitButton(form);
hideProgress();
showMessage('Success');
}, function (reason) {
unlockSubmitButton(form);
hideProgress();
showMessage('Error: '+ reason);
});
});
Или то же, но с промисами:
form.on('submit', function () {
lockSubmitButton(form);
showProgress();
var response = uploader.uploadForm(form);
response.success(function (response) {
unlockSubmitButton(form);
hideProgress();
showMessage('Success');
});
response.error(function (reason) {
unlockSubmitButton(form);
hideProgress();
showMessage('Error: '+ reason);
});
});
Чтобы не ездить по нодам, возьми библиотеку для DOM traversing или jQuery.
>>182742
Простую форму загрузки файла можно сделать без React. Более того, React довольно тяжелый и там по моему Hello World под мегабайт весит.
>>183002
Typescript неплохая вещь, но к вопросу он мало отношения имеет. Да и не очень удобно, постоянно перекомпилировать вместо того, чтобы просто обновить страницу.
Хром подчиняется правилам, заданным в HTTP заголовкам ответа. Кто-то криворуко настроил эти заголовки, а виноват Хром.
>>183287
Вызывать функцию надо как call_user_func($this->x, ...) и тогда все будет работать. Но судя по твоему описанию, у тебя может быть неудачная архитектура и ты лепишь костыли вместо решения проблемы.
> Суть в том, что есть класс, который работает с html-кодом. И этот самый html-код может получаться разными способами, которые никак не унифицировать. То есть, у каждого объекта своя версия функции, которая генерирует html.
Это только подтверждает мою мысль.
/[а-яёА-ЯЁ]?[a-zA-Z][а-яёА-ЯЁ]?/ui
Как сделать, чтобы она находила латинскую букву с обязательной кириллической буквой рядом или с обоих сторон?
Выглядит как хуй, но спасибо.
в Б меня обоссали за попытку пилить сайт на чистом языке
Работая в компании мне приходилась иметь дело с самописными двиками.
Незная чистого языка ты постоянно будешь биться об нипонимание что делает тот или иной код в недрах фрейма.
>>190415
Прошу прощения за столь долгий ответ - пришлось набираться сил чтобы погрузитья глубже в изучение, но, каким-то чудом, я, вроде, всё понял. Даже вопросов нет, почти.
Чтобы убедиться в правельности своих знаний, я преведу пример, что я собираюсь сделать. Пожалуйста, сделайте замечания, если у вас такие возникнут.
Технологии, которые я буду использовать:
Redis Pub/Sub https://redis.io/topics/pubsub
phpredis https://github.com/phpredis/phpredis
ReactPHP EventLoop (для реализации таймаута ожидания) https://reactphp.org/event-loop/
subscribe.php
<?php
$redis = new Redis();
$redis->open(...); // соеденяемся
// Каналами может быть всё что угодно - id переписок, пользователей, всё на что мы хотим подписаться
// В нашем случае можно опустить только до сообщений для простоты
$channels = [...];
$message = new Message();
$loop = React\EventLoop\Factory::create();
$loop->addTimer($interval = 30, function () use ($redis) {
//unsubscribe
$redis->close();
//возвращаем пустое сообщение
});
$loop->run();
$message = $redis->subscribe($channels, function($r, $c, $m) use ($message) {
return $message->set($m);
});
//возвращаем получившиеся сообщение (либо пустое, либо то что вернул брокер)
return $message;
?>
publish.php
<?php
$database = new Database(...);
$redis = new Redis();
$redis->open(...);
$channels = [...];
$message = new Message($_POST['message']);
$message = $database->add($message);
foreach ($channels as $channel) {
$redis->publish($channel, $message); // перевод объекта Сообщения в строку опустим для простоты
}
// возвращаем сообщение назад в случае успеха
return $message;
?>
Получилось довольно всё просто, не смотря на такую трудную для меня задачу.
Однако, у меня, всё же, появились вопросы пока я писал этот пост, и я вынжден попрасить вас ответить на них.
https://github.com/phpredis/phpredis#subscribe
Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция? Должно быть, ассинхронная. То есть, её возврат и присваиванее в переменную $message, в моём коде, не произойдет, если никакое сообщение так и не прийдёт?
Если это так, то можно сначала подписаться, а потом уже поставить таймайут ожидания.
https://github.com/phpredis/phpredis#publish
Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?
Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.
Как я и раньше, брал в пример поведение одной популярной соц.сети, так и сейчас хотелось бы рассмотреть одно из её поведений: Когда происходит одно из уведомлений (не важно, приходит ли сообщение или собеседник набирает сообщение, или кто-то подписывается) всегда выполняется один и тот же запрос.
Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
А что вы думаете?
И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?
Спасибо большое.
>>190415
Прошу прощения за столь долгий ответ - пришлось набираться сил чтобы погрузитья глубже в изучение, но, каким-то чудом, я, вроде, всё понял. Даже вопросов нет, почти.
Чтобы убедиться в правельности своих знаний, я преведу пример, что я собираюсь сделать. Пожалуйста, сделайте замечания, если у вас такие возникнут.
Технологии, которые я буду использовать:
Redis Pub/Sub https://redis.io/topics/pubsub
phpredis https://github.com/phpredis/phpredis
ReactPHP EventLoop (для реализации таймаута ожидания) https://reactphp.org/event-loop/
subscribe.php
<?php
$redis = new Redis();
$redis->open(...); // соеденяемся
// Каналами может быть всё что угодно - id переписок, пользователей, всё на что мы хотим подписаться
// В нашем случае можно опустить только до сообщений для простоты
$channels = [...];
$message = new Message();
$loop = React\EventLoop\Factory::create();
$loop->addTimer($interval = 30, function () use ($redis) {
//unsubscribe
$redis->close();
//возвращаем пустое сообщение
});
$loop->run();
$message = $redis->subscribe($channels, function($r, $c, $m) use ($message) {
return $message->set($m);
});
//возвращаем получившиеся сообщение (либо пустое, либо то что вернул брокер)
return $message;
?>
publish.php
<?php
$database = new Database(...);
$redis = new Redis();
$redis->open(...);
$channels = [...];
$message = new Message($_POST['message']);
$message = $database->add($message);
foreach ($channels as $channel) {
$redis->publish($channel, $message); // перевод объекта Сообщения в строку опустим для простоты
}
// возвращаем сообщение назад в случае успеха
return $message;
?>
Получилось довольно всё просто, не смотря на такую трудную для меня задачу.
Однако, у меня, всё же, появились вопросы пока я писал этот пост, и я вынжден попрасить вас ответить на них.
https://github.com/phpredis/phpredis#subscribe
Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция? Должно быть, ассинхронная. То есть, её возврат и присваиванее в переменную $message, в моём коде, не произойдет, если никакое сообщение так и не прийдёт?
Если это так, то можно сначала подписаться, а потом уже поставить таймайут ожидания.
https://github.com/phpredis/phpredis#publish
Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?
Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.
Как я и раньше, брал в пример поведение одной популярной соц.сети, так и сейчас хотелось бы рассмотреть одно из её поведений: Когда происходит одно из уведомлений (не важно, приходит ли сообщение или собеседник набирает сообщение, или кто-то подписывается) всегда выполняется один и тот же запрос.
Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
А что вы думаете?
И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?
Спасибо большое.
Всё равно, что учить некий диалект русского языка не зная русского языка. Разумеется в том же блатняке дохуя абстракций и даже более-менее зная язык всё равно не поймёшь особо, но тебе русский нужен всё равно, иначе будешь как макака.
А какая разница? Книга - плохой пересказ документации. Просто авторы докатали пару глав для новый buzzwords: psr, nginx, fpm и тд для переиздачи, а так все тот же треш из 2004 года. Нет смысла возится с этим толмудом. Лучше сразу топить задачу про студентов из ОП поста и все с ней связанное.
>Лучше сразу топить задачу про студентов из ОП поста и все с ней связанное.
А если я программирую первый раз в жизни? Стоит ли проходить основы из оп-учебника?
Основы SQL я вроде знаю, что это огромная язык позволяющий ебашить двумерные таблицы (может и трёхмерные, хуй знает, читал пару лет назад) и генерировать всякие запросы с фильтрами для получения нужных данных из базы. А вот ООП... сука, где? Я знаю, что его придумал какой-то мужик, но написал ли он о нём гайд? Да и опять на этом ебучем английском читать.
>ебола управляемая через
проебалась где-то между. Эта хуйня конечно полезна, когда тебя зовут помочь, ибо тыжпрограммист, а там в 1С нужно ебашить отчёты.
Да знаю я. Шапки от начала и до конца зачитывать — золотое правило в любом тематическом разделе.
Конечно. ОП поясняет за основы.
неОП Если примерно то от 6 месяцев до года в обычном темпе для обычного человека. Есть конечно истории как чуваки за 2-3 месяца полностью вкатывались, но лично я не умею грамотно декомпозировать проблему, приходиться медленно учиться на своих ошибках
Я и 5 покупал, и 7 купил. Валяются ;-)
Котеров слишком заумно пишет. Излишняя, чрезмерная, надменная подача информации. Вроде как живое общение с читателем, на равных.
Правда в самом гачале книги авторы пишут, что книга для уже знающих азы программирования, в том числе писавшие на любых других ЯП.
Для новичка в мире программирования лучше начать с какой-нибудь онлайн обучалки. Потом уже переходить на чтиво сего талмуда.
А старцы из мира программирования хейтят Котерова и его книги. Говорят, мол, говнокода много в примерах, нелогично он выстроен и несвязанно, нет единого стиля, используются устаревшие решения и прочее.
Сам не читал, слышал мнение где-то (тут же на дваче, кажется).
У меня есть знакомый, который пол года пытался научится в группе, а потом три года сидя дома. Но так и не стал кодером. Потому что учился на расслабоне, например включает видос и валяется с попкорном а там чтото приятно бубнят. Или читает текст без вдумывания, инфа проходит через мозг и не осознается. То есть он думает что если водить глазами по буквам то чему то можно научиться.
Так что тут все зависит от упорства памяти и внимательности.
> говнокода много в примерах
Там ещё беда в том, что примеры кода были написаны в эпоху рнр 5.0.21(!!!) и из 5 книги перккочевали в 7, но с небольшими поправками под современные реалии. И все бы ничего, но стиль примеров действительно остался из середины 00-х.
Там столько исходников, что переписывоть все под 7 рнр у них заняло кучу времени.
Лично мне почему 7 понравилась, тем что в книге они уже пишут про композер, как некоторые технологии юзать. Мне как любителю писать скрипты для души это ценная инфа.
В js с появлением ноды появились всякие бабели и прочие штуки типа вебпака. Была бы полезна некая книга с описанием что и для чего, конечно же в полном цикле с изучением js. Я вот лично из-за объема не собранного вместе материала теряюсь. По отдельности нде то выискивать и читать не очень хочется. Хотелось бы чтобы доходчиво в 1 книге расписали чо к чему.
По обстоятельствам. Я не проф программист, пишу исклбчительно дома для души. Жс юзаю по мере, но в основном библы, так как с нэтив надо слишком дохуя нюансов этой пердольноц затычки знать.
Не слушай >>197036 этого. Зная js + jquery легче начать работать чем с js + vue.
Фреймы это уже более высокий уровень, туда легко идти тем у кого уже есть какой то опыт в более простых задачах.
Так что если ты только вкатываешься то делай упор на js + jquery. В большинстве вебкомпаний этого достаточно.
Нахуй пошел. Я дело советую.
Допустим ты полнуй нуб только вкатился только начал искать работу. Тебе ничего кроме вордпреса не доверят. И скажи мне нахуй там vue ?
Азаххахаха а нахуй на ворде жкуэри? Что ты несешь дебил? Ты ни дня не работая даешь советы) лол кек чебурек. Катись колбаской сукин сын.
Ебать ты даунЖ, ворпрес и жквери не раздельны и у меня 6 лет стажа как на говнокоде так и на фреймах, так шо сосни и жалуйся мамке дите.
>>197047
Вот ты и сдулся петух ебаный. Вся твоя гнилая суть проявилась. Хуета чсвшная. Поясняю тебе за щеку. Ты еще бы мутулз посоветовал или иной древгий нахуй ненужный фреймворк для изучения, лалка сосалка ты дурная. Дура безтрусая ты, я тебе еще раз повторяю, ты окромя как в /pr нигде строчки не писал.
Идиота кусок, что ты собрался в WP на jQuery писать? Ты совсем ебанат натрия что ли?
Да любые свистелки пирделки блять. Любая js логика. Любые манипуляции со страницей после загрузки. Да тот же аякс блять.
Всё с тобой ясно. Ты понятия не имеешь, о чём ты пишешь. Другой анон правильно про тебя написал, что ты кроме как в /pr нигде больше строчки кода не написал.
мимо другой анон
Нужно знать чистый js. А остальное под проект уже учить, но вообще если что от еще хочется, то ангуляр, вуе или реакт. Так?
Так
Спасибо. Понял.
Да в идеале под проект, но хуйня в том что тебе, так как ты нуб, ничего сложного не доверят. А в простых проектах нужен jquery.
КОД https://pastebin.com/MBXEwHGJ
Ошибка и бд на пике
написано же, не то что надо передал в mysqli_real_escape_string
Ты меня на шизика то не тяни. Я тебе не билли миллиган, не традаю я множественными личностями.
Хоть в чём-то с тобой солидарен, но ты всё же не мимикрируй под программиста. Человек пришёл за помощью, а ты его траллить начал.
Я все верно сказал. Если ты чего то не понимаешь, не списывай это на траллинг.
я с этого начинал
Ты должен знать правила работы серверной части в веб приложениях и веб сервисах. Где клиент запрашивает не страницы как это происходит в обычных веб сайтах, а идет чисто технический обмен данными.
Основные отличия это формат ответа и авторизация.
Обычно в ответ приходит json, jsonp или xml. Авторизация не всегда на основе кукисов а чаще на основе токенов.
Изучи статью википедии про принципы REST, и разбери пару примеров API, например API Яндекс Диска и API Youtube. Этого хватит.
Макака освоила месяца за 4, но тогда в погромировании никаких знаний не было от слова совсем.
> Макаке еще нужно освоить SQL
Казалось, что это просто. На самом деле нет?
JS пипец сложный. Я чот вообще года два не могу в него. Хотя за плечами PHP (fullstack), Delphi, C++, Swift, даже 20 лет как мёртвый PROLOGUE, даже руби. А работаю я C# макакой! А вот JS не могу. Какой-то он нелогичный весь. Вот не поддаётся он анализу. Смотрю на код макакенов, вижу прямо лютый говнокод. А им норм. Даже какие-то конференции устраивают дже хвастают друг перед другом. Вообще не могу понять этот движ. Для меня те кто освоил JS - вообще не люди, это какие-то эльфы сверхразумы.
JS выглядит не так ровно как код других языков. А еще объект это функция и класс и конструктор объектов. Наследование через анал жирафа.
Но когда я вкатился в js я понял что на нем мне писать удобней и быстрей чем на чем-то другом. С++, С#, php, sql, верстка кун
У него есть некоторое количество роялей в шкафу, которые нужно просто держать в голове и все проверять, когда появляется вероятность этого рояля.
А откуда он знает "4" число это или символ и что тебе надо.
Просто надо читануть как что и во что конвертируется автоматом.
Ух ты, какой замечательный хурр дурр!
8 джойнов. Вспомнил как меня Админ центробанка хотел отпиздить за применение 3 джойнов в 2008 году. Потому-что серваки нагружаю. А база там с миллиардами записей.
Ну ты помни что такое возможно. Сегодня ты магаз делаешь, а зватра положил финансовую систему области.
Да я в курсе. Когда работаешь в системе финансовой области и получаешь в разы больше денег, так что можешь спокойно втулить в несколько раз больше таблиц и делать еще кучу всякой сложной херни для оптимизации.
Сразу же замечание по интерфейсу - плохо смешивать в интерфейсе 2 разных языка (русский и английский), это выглядит, как будто проект начали переводить, но не доделали. Лучше все делать на одном языке.
В httpd.conf желательно было добавить комментарии, указывающие, что там надо поменять.
В репозиторий не надо добавлять config.json, так как он у каждого члена команды разный, они будут перезаписывать конфиги друг друга и потому этот файл надо убрать из репозитория и добавить в .gitignore. Понятно, что ты делаешь проект один, но полезно учиться работать в команде. А так, даже если кто-то тебе пулл-реквест захочет с исправлениями предложить, он твой config.json перезапишет своим.
path в конфиге можно было не указывать, так как путь к папке со скриптом легко определить по php-константе __DIR__.
В базе данных нужно соблюдать единый стиль наименования полей. У тебя student_id и groupNum написаны в разном стиле. Да и не стоило сокращать, пиши лучше groupNumber.
В таблице желательно использовать комментарии, например, в поле password желательно указать, что там хранится, какой вид хеша.
composer.lock не надо добавлять в gitignore для приложений. Его лучше добавить в репозиторий. Он содержит точные версии библиотек, которые использует разработчик и которые гарантированно работают. Соответственно тот, кто разворачивает проект, установит эти проверенные версии.
> ini_set('display_errors', 1);
Это лучше бы не писать в коде, так как эта настройка должна быть отключена на продакшене. Ее лучше задавать в php.ini
> $string = file_get_contents('/var/www/project/config.json');
Тут лучше не прописывать путь, а использовать __DIR__ .
У тебя в коде во многих местах прописан этот путь и из-за этого твой проект нельзя развернуть в другой папке. Это очень неудобно. Вместо того, чтобы 20 раз прописывать путь, его нужно было определить в boostrap-скрипте, загрузить по нему конфиги и передать в те классы, которым путь или конфиг нужен.
> "StudentList\\Controllers\\": "app/controllers/",
Тут конечно проще было назвать папки точно так же, как и неймспейс, а не прописывать каждую папку отдельно.
> https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/FrontController.php
> $uriControllerName = explode("?",$uri)[0];
Лучше использовать parse_url для этого.
> if(!empty($_SERVER['REQUEST_URI'])){
> $uri = trim($_SERVER['REQUEST_URI'], '/');
> }
Зачем тут !empty? Если этого элемента нет, то у тебя не будет создана переменная $uri и дальше пойдут ошибки.
> if(file_exists($path)){
> include_once($path);
> }else{
> FrontController::http404();
> }
> $controllerName= 'StudentList\\Controllers\\'.$controllerName;
Ты тут при ошибке 404 выдаешь заголовок 404, но не завершаешь на этом скрипт, а продолжаешь выполнять.
> https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/MainController.php
> const CONTROLLER_VIEW = 'mainView.html';
Нет особого смысла использовать тут константу.
> private function pagination($pageNum, $search, $sort)
Имена функций принято начинать с глагола, сделайЧтоТо().
Пагинатор лучше передавать напрямую в шаблон, чем извлекать из него отдельные переменные и передавать их.
А так, у тебя в контроллере какие-то непонятные массивы куда-то передаются и преобразуются и тяжело в этом разбираться. Код должен быть простой, например:
пагинатор = создатьПагинатор();
студенты = сервисСтудентов->найтиВсехСтудентов(страница);
и так далее. Без оперирования сложными массивами с непонятными полями.
> $values = $paginator->returnPageValues($pageNum, $search, $sort);
Плохое название для переменной, так как values ничего не значит. Любая переменная хранит какое-то значение.
> $this->wrapSearchStr($val, $search);
Это костыль, который вызовет баги. Ты должен подсвечивать поисковую фразу только в определенных местах (в таблице), а не пытаться заменять ее везде. Может, ты заменишь ее в какой-то переменной, которая вообще не выводится никуда, а используется как-то по другому.
> "arr" => $val["arr"],
Непонятно, что значит val, и что значит arr. Переменным и полям надо давать понятные названия, чтобы код было легко читать.
> https://github.com/levneedscoffee/Student-List/blob/master/app/services/Paginator.php
Пагинатор сделан, на мой взгляд, неудачно. В ООП есть принцип разделения ответственности - каждый класс отвечает за что-то свое. Соответственно, можно разделить ответственность между классом, получающим данные студентов и классом, рассчитывающим параметры пагинации. Чтобы получился универсальный пагинатор, который можно использовать с чем угодно. Ты же в пагинаторе смешал расчет пагинации и получение данных и твой пагинатор в других ситуациях использовать нельзя. Он годится только для работы с таблицей студентов.
> $db = new StudentDataGateway(new DatabaseMySql());
Зависимости лучше внедрять с использованием DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Тем более, что у тебя там подключен pimple.
> returnIterableArr
Лучше было назвать getAllPageNumbers() или getVisiblePageNumbers().
Вместо 0 логичнее возвращать пустой массив.
> public function returnPageValues($page, $search=null, $sort = null)
У результатов функций и их аргументов надо расставлять тайп-хинты: http://php.net/manual/ru/functions.arguments.php#functions.arguments.type-declaration
Они делают код понятнее и защищают от ошибок.
https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/Controller.php#L22
Плохо, что твиг-функция сама что-то берет из $_GET. Если смотреть на место вызова, это ведь не очевидно, что она может откуда-то сама брать данные. Не надо так делать, все, что нужно функции, лучше передавать ей явно.
https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/EditController.php#L33
Здесь после отправки заголовка для редиректа выполнение скрипта продолжается зачем-то.
> $val->validate($studentObject, $userEmail);
> $errors = $val->returnErrors();
Во многих случаях значения лучше сразу возвращать через return, а не таким косвенным образом. Это делает код более надежным и не позволяет вызвать методы в неправильном порядке (не позволяет вызвать returnErrors до валидации, например).
Если надо вернуть несколько значений, то тут есть варианты:
- вернуть нумерованный массив: list($errors, $values) = validate(....);
- вернуть ассоциативный массив
- вернуть объект с несколькими полями или методами
- сделать отдельно метод преобразования значений, а отдельно метод валидации
Еще, плохо что ты вызываешь рендеринг одного и того же шаблона, передавая туда разные наборы переменных. Как можно надежно писать код, если ты не знаешь, будет такая переменная передана или нет?
Удалять пользователя при смене email - плохая идея. В дальнейшем могут появляться новые таблицы, которые ссылаются на таблицу пользователей. При смене email эти связи у тебя будут потеряны. Также, если в таблицу добавить поля, которых нет в форме, то они тоже будут потеряны при смене email. Лучше использовать id в качестве идентификатора.
Также, как я понимаю, у тебя можно редактировть данные пользователя, если в куках есть его email. Это не дает никакой защиты и позволяет редактировать данные любого пользователя, если ты знаешь email, который часто не является секретной информацией.
Редактирование лучше делать не созданием нового объекта, а загрузкой объекта из БД, изменением его полей и сохранением в БД - так надежнее. Например, если в объекте есть поля, которых нет в форме, то их значения не потеряются.
В конфиге роутера лучше писать полные имена классов и методов, а не сокращенные. В этом случае они ищутся поиском (если мы хотим узнать, где используется класс или метод), а также в IDE можно перейти к методу по клику.
https://github.com/levneedscoffee/Student-List/blob/master/app/entity/User.php
Здесь очень странно, что объект, представляющий пользователя, сам лезет куда-то в базу и сам себя сохраняет. Логичнее было бы в UserDataGateway добавить методы для этого. А то не очень понятно, в чем разделение ответственности между User и UserDataGateway и не занимаются ли они одним и тем же.
https://github.com/levneedscoffee/Student-List/blob/master/app/models/DataInterface.php
В таких случаях надо писать комментарии, так как непонятно, зачем этот интерфейс и какой характеристике класса он соответствует.
Сразу же замечание по интерфейсу - плохо смешивать в интерфейсе 2 разных языка (русский и английский), это выглядит, как будто проект начали переводить, но не доделали. Лучше все делать на одном языке.
В httpd.conf желательно было добавить комментарии, указывающие, что там надо поменять.
В репозиторий не надо добавлять config.json, так как он у каждого члена команды разный, они будут перезаписывать конфиги друг друга и потому этот файл надо убрать из репозитория и добавить в .gitignore. Понятно, что ты делаешь проект один, но полезно учиться работать в команде. А так, даже если кто-то тебе пулл-реквест захочет с исправлениями предложить, он твой config.json перезапишет своим.
path в конфиге можно было не указывать, так как путь к папке со скриптом легко определить по php-константе __DIR__.
В базе данных нужно соблюдать единый стиль наименования полей. У тебя student_id и groupNum написаны в разном стиле. Да и не стоило сокращать, пиши лучше groupNumber.
В таблице желательно использовать комментарии, например, в поле password желательно указать, что там хранится, какой вид хеша.
composer.lock не надо добавлять в gitignore для приложений. Его лучше добавить в репозиторий. Он содержит точные версии библиотек, которые использует разработчик и которые гарантированно работают. Соответственно тот, кто разворачивает проект, установит эти проверенные версии.
> ini_set('display_errors', 1);
Это лучше бы не писать в коде, так как эта настройка должна быть отключена на продакшене. Ее лучше задавать в php.ini
> $string = file_get_contents('/var/www/project/config.json');
Тут лучше не прописывать путь, а использовать __DIR__ .
У тебя в коде во многих местах прописан этот путь и из-за этого твой проект нельзя развернуть в другой папке. Это очень неудобно. Вместо того, чтобы 20 раз прописывать путь, его нужно было определить в boostrap-скрипте, загрузить по нему конфиги и передать в те классы, которым путь или конфиг нужен.
> "StudentList\\Controllers\\": "app/controllers/",
Тут конечно проще было назвать папки точно так же, как и неймспейс, а не прописывать каждую папку отдельно.
> https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/FrontController.php
> $uriControllerName = explode("?",$uri)[0];
Лучше использовать parse_url для этого.
> if(!empty($_SERVER['REQUEST_URI'])){
> $uri = trim($_SERVER['REQUEST_URI'], '/');
> }
Зачем тут !empty? Если этого элемента нет, то у тебя не будет создана переменная $uri и дальше пойдут ошибки.
> if(file_exists($path)){
> include_once($path);
> }else{
> FrontController::http404();
> }
> $controllerName= 'StudentList\\Controllers\\'.$controllerName;
Ты тут при ошибке 404 выдаешь заголовок 404, но не завершаешь на этом скрипт, а продолжаешь выполнять.
> https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/MainController.php
> const CONTROLLER_VIEW = 'mainView.html';
Нет особого смысла использовать тут константу.
> private function pagination($pageNum, $search, $sort)
Имена функций принято начинать с глагола, сделайЧтоТо().
Пагинатор лучше передавать напрямую в шаблон, чем извлекать из него отдельные переменные и передавать их.
А так, у тебя в контроллере какие-то непонятные массивы куда-то передаются и преобразуются и тяжело в этом разбираться. Код должен быть простой, например:
пагинатор = создатьПагинатор();
студенты = сервисСтудентов->найтиВсехСтудентов(страница);
и так далее. Без оперирования сложными массивами с непонятными полями.
> $values = $paginator->returnPageValues($pageNum, $search, $sort);
Плохое название для переменной, так как values ничего не значит. Любая переменная хранит какое-то значение.
> $this->wrapSearchStr($val, $search);
Это костыль, который вызовет баги. Ты должен подсвечивать поисковую фразу только в определенных местах (в таблице), а не пытаться заменять ее везде. Может, ты заменишь ее в какой-то переменной, которая вообще не выводится никуда, а используется как-то по другому.
> "arr" => $val["arr"],
Непонятно, что значит val, и что значит arr. Переменным и полям надо давать понятные названия, чтобы код было легко читать.
> https://github.com/levneedscoffee/Student-List/blob/master/app/services/Paginator.php
Пагинатор сделан, на мой взгляд, неудачно. В ООП есть принцип разделения ответственности - каждый класс отвечает за что-то свое. Соответственно, можно разделить ответственность между классом, получающим данные студентов и классом, рассчитывающим параметры пагинации. Чтобы получился универсальный пагинатор, который можно использовать с чем угодно. Ты же в пагинаторе смешал расчет пагинации и получение данных и твой пагинатор в других ситуациях использовать нельзя. Он годится только для работы с таблицей студентов.
> $db = new StudentDataGateway(new DatabaseMySql());
Зависимости лучше внедрять с использованием DI: https://github.com/codedokode/pasta/blob/master/arch/di.md
Тем более, что у тебя там подключен pimple.
> returnIterableArr
Лучше было назвать getAllPageNumbers() или getVisiblePageNumbers().
Вместо 0 логичнее возвращать пустой массив.
> public function returnPageValues($page, $search=null, $sort = null)
У результатов функций и их аргументов надо расставлять тайп-хинты: http://php.net/manual/ru/functions.arguments.php#functions.arguments.type-declaration
Они делают код понятнее и защищают от ошибок.
https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/Controller.php#L22
Плохо, что твиг-функция сама что-то берет из $_GET. Если смотреть на место вызова, это ведь не очевидно, что она может откуда-то сама брать данные. Не надо так делать, все, что нужно функции, лучше передавать ей явно.
https://github.com/levneedscoffee/Student-List/blob/master/app/controllers/EditController.php#L33
Здесь после отправки заголовка для редиректа выполнение скрипта продолжается зачем-то.
> $val->validate($studentObject, $userEmail);
> $errors = $val->returnErrors();
Во многих случаях значения лучше сразу возвращать через return, а не таким косвенным образом. Это делает код более надежным и не позволяет вызвать методы в неправильном порядке (не позволяет вызвать returnErrors до валидации, например).
Если надо вернуть несколько значений, то тут есть варианты:
- вернуть нумерованный массив: list($errors, $values) = validate(....);
- вернуть ассоциативный массив
- вернуть объект с несколькими полями или методами
- сделать отдельно метод преобразования значений, а отдельно метод валидации
Еще, плохо что ты вызываешь рендеринг одного и того же шаблона, передавая туда разные наборы переменных. Как можно надежно писать код, если ты не знаешь, будет такая переменная передана или нет?
Удалять пользователя при смене email - плохая идея. В дальнейшем могут появляться новые таблицы, которые ссылаются на таблицу пользователей. При смене email эти связи у тебя будут потеряны. Также, если в таблицу добавить поля, которых нет в форме, то они тоже будут потеряны при смене email. Лучше использовать id в качестве идентификатора.
Также, как я понимаю, у тебя можно редактировть данные пользователя, если в куках есть его email. Это не дает никакой защиты и позволяет редактировать данные любого пользователя, если ты знаешь email, который часто не является секретной информацией.
Редактирование лучше делать не созданием нового объекта, а загрузкой объекта из БД, изменением его полей и сохранением в БД - так надежнее. Например, если в объекте есть поля, которых нет в форме, то их значения не потеряются.
В конфиге роутера лучше писать полные имена классов и методов, а не сокращенные. В этом случае они ищутся поиском (если мы хотим узнать, где используется класс или метод), а также в IDE можно перейти к методу по клику.
https://github.com/levneedscoffee/Student-List/blob/master/app/entity/User.php
Здесь очень странно, что объект, представляющий пользователя, сам лезет куда-то в базу и сам себя сохраняет. Логичнее было бы в UserDataGateway добавить методы для этого. А то не очень понятно, в чем разделение ответственности между User и UserDataGateway и не занимаются ли они одним и тем же.
https://github.com/levneedscoffee/Student-List/blob/master/app/models/DataInterface.php
В таких случаях надо писать комментарии, так как непонятно, зачем этот интерфейс и какой характеристике класса он соответствует.
https://github.com/levneedscoffee/Student-List/blob/master/app/models/DatabaseMySql.php
У тебя во многих местах идет чтение JSON конфига. Это дублирование кода и это надо вынести отдельно. Удобнее всего применить здесь DI и требовать передавать параметры соединения в класс DatabaseMySql. Хотя тогда становится непонятно, зачем он вообще нужен.
> } catch (\PDOException $e) {
> echo "Connection failed: " . $e->getMessage();
Это неправильно. Зачем ты выводишь пользователю подробности о параметрах соединения с БД (которые он не поймет), и при этом не пишешь их в лог, где их мог бы увидеть разработчик? Почитай урок про исключения, как их лучше обрабатывать: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
> https://github.com/levneedscoffee/Student-List/blob/master/app/models/StudentDataGateway.php
> public function __construct(DataInterface $obj)
Почему не передавать сюда сразу объект PDO? Зачем класс-посредник?
https://github.com/levneedscoffee/Student-List/blob/master/app/models/StudentDataGateway.php#L23
> $stmt = $this->pdo->query('SELECT * FROM students ORDER by '.$sort.' LIMIT '.$page.','.$limit);
Нет защиты от SQL инъекции.
> return $stmt->fetch()[0];
Есть метод в PDO для получения только одного значения.
https://github.com/levneedscoffee/Student-List/blob/master/app/services/Security.php#L18
> if(isset($_COOKIE['token']) && isset($_POST['token']) && $_COOKIE['token'] === $_POST['token']){
Если кука есть, но пустая (''), и в форме пустой токен, то проверка пройдет. Проверка не должна срабатывать, если токены пустые.
> if (!preg_match("/^[А-ЯЁA-Z][-а-яёa-zА-ЯЁA-Z\\s]{1,".$strLength."}$/u", $name)) {
Вместо подстановки переменной в регулярку было бы лучше просто проверить длину через mb_strlen. А то появляется вопрос, а нет ли тут возможности инъекции произвольного выражения в регулярку и не создаст ли это новой уязвимости?
> public function validateEmail($email)
В email до знака @ может встретиться намного больше символов, например: минус, плюс, процент.
Статьи по теме валидации email:
- https://habr.com/post/274985/
- https://habr.com/post/55820/
Зачастую проще проверять email по упрощенной модели:
- есть знак @
- нет пробелов
- есть точка справа, и вокруг точки есть хотя бы еще 1 символ (технически домен в email может быть и без точки, но такие домены не доступны в глобальном интернете, а только во внутренних сетях)
Вроде как такая модель используется в браузере для поля с типом email.
> const MAX_points = 300;
Константы пишут большими буквами.
> const MIN_BIRTHDAY = 1985-01-01;
Дискриминация по возрасту
> https://github.com/levneedscoffee/Student-List/blob/master/app/view/mainView.html
> <td>{{ row.name|raw }}</td>
Что с защитой от XSS? Ты ведь отключил экранирование.
https://github.com/levneedscoffee/Student-List/blob/master/app/view/editView.html
Тут желательно настроить HTML5 валидацию для проверки данных до отправки.
> <input class="form-control" type="text" name="email"
В HTML5 есть тип поля email. Изучи типы полей input в HTML5.
https://github.com/levneedscoffee/Student-List/blob/master/app/view/registerView.html
По яваскрипт-коду:
- на время отправки запроса желательно блокировать форму или кнопку отправки
- перед отправкой запроса стоит очистить поле с информацией об ошибках, так как оно более неактуально и может сбить с толку пользователя
- желательно показать, что идет отправка запроса
Про это написано в моем уроке про использование аякса: https://github.com/codedokode/pasta/blob/master/js/ajax.md
Так, вообще, сделано хорошо для первого раза, но код, конечно, стоит доработать.
https://github.com/levneedscoffee/Student-List/blob/master/app/models/DatabaseMySql.php
У тебя во многих местах идет чтение JSON конфига. Это дублирование кода и это надо вынести отдельно. Удобнее всего применить здесь DI и требовать передавать параметры соединения в класс DatabaseMySql. Хотя тогда становится непонятно, зачем он вообще нужен.
> } catch (\PDOException $e) {
> echo "Connection failed: " . $e->getMessage();
Это неправильно. Зачем ты выводишь пользователю подробности о параметрах соединения с БД (которые он не поймет), и при этом не пишешь их в лог, где их мог бы увидеть разработчик? Почитай урок про исключения, как их лучше обрабатывать: https://github.com/codedokode/pasta/blob/master/php/exceptions.md
> https://github.com/levneedscoffee/Student-List/blob/master/app/models/StudentDataGateway.php
> public function __construct(DataInterface $obj)
Почему не передавать сюда сразу объект PDO? Зачем класс-посредник?
https://github.com/levneedscoffee/Student-List/blob/master/app/models/StudentDataGateway.php#L23
> $stmt = $this->pdo->query('SELECT * FROM students ORDER by '.$sort.' LIMIT '.$page.','.$limit);
Нет защиты от SQL инъекции.
> return $stmt->fetch()[0];
Есть метод в PDO для получения только одного значения.
https://github.com/levneedscoffee/Student-List/blob/master/app/services/Security.php#L18
> if(isset($_COOKIE['token']) && isset($_POST['token']) && $_COOKIE['token'] === $_POST['token']){
Если кука есть, но пустая (''), и в форме пустой токен, то проверка пройдет. Проверка не должна срабатывать, если токены пустые.
> if (!preg_match("/^[А-ЯЁA-Z][-а-яёa-zА-ЯЁA-Z\\s]{1,".$strLength."}$/u", $name)) {
Вместо подстановки переменной в регулярку было бы лучше просто проверить длину через mb_strlen. А то появляется вопрос, а нет ли тут возможности инъекции произвольного выражения в регулярку и не создаст ли это новой уязвимости?
> public function validateEmail($email)
В email до знака @ может встретиться намного больше символов, например: минус, плюс, процент.
Статьи по теме валидации email:
- https://habr.com/post/274985/
- https://habr.com/post/55820/
Зачастую проще проверять email по упрощенной модели:
- есть знак @
- нет пробелов
- есть точка справа, и вокруг точки есть хотя бы еще 1 символ (технически домен в email может быть и без точки, но такие домены не доступны в глобальном интернете, а только во внутренних сетях)
Вроде как такая модель используется в браузере для поля с типом email.
> const MAX_points = 300;
Константы пишут большими буквами.
> const MIN_BIRTHDAY = 1985-01-01;
Дискриминация по возрасту
> https://github.com/levneedscoffee/Student-List/blob/master/app/view/mainView.html
> <td>{{ row.name|raw }}</td>
Что с защитой от XSS? Ты ведь отключил экранирование.
https://github.com/levneedscoffee/Student-List/blob/master/app/view/editView.html
Тут желательно настроить HTML5 валидацию для проверки данных до отправки.
> <input class="form-control" type="text" name="email"
В HTML5 есть тип поля email. Изучи типы полей input в HTML5.
https://github.com/levneedscoffee/Student-List/blob/master/app/view/registerView.html
По яваскрипт-коду:
- на время отправки запроса желательно блокировать форму или кнопку отправки
- перед отправкой запроса стоит очистить поле с информацией об ошибках, так как оно более неактуально и может сбить с толку пользователя
- желательно показать, что идет отправка запроса
Про это написано в моем уроке про использование аякса: https://github.com/codedokode/pasta/blob/master/js/ajax.md
Так, вообще, сделано хорошо для первого раза, но код, конечно, стоит доработать.
Мне тоже непонятно. думаю, что особо нет смысла. В JS в новых браузерах есть FormData для отправки данных форм аяксом, и туда можно прикладывать файлы.
>>185260
Получается да, надо где-то хранить состояние. Еще ведь конвертация может провалитсья и надо будет запустить ее повторно.
БД и сервер очередей выполняют разные задачи. БД - хранит данные. Сервер очередей - уведомляет о появлении новой задачи в БД.
>>185334
Думаю, надо залезть в настройки форматирования и поправить их.
>>185854
Тут есть подвохи. В Линуксе после завершения процесса его родитель получает уведомление и информацию - код выхода, потребленные ресурсы итд. Если ты запустил дочерний процесс, а потом внезапно умер, то после завершения дочернего процесса информацию получать будет некому и в системе может остаться "зомби" - данные от умершего процесса.
В этом плане конечно из-под веб скрипта лучше длительные процессы не запускать.
Плюс, непонятно, как ты в такой модели будешь контролировать число запущенных процессов? Их же можно много назапускать и они будут мешать друг другу и тормозить. Как отслеживать, что они упали с ошибкой, как сохранить информацию об ошибке?
Плюс, в случае с очередью задач ты можешь сделать систему распределенной - на одном сервере обслуживать запросы, на другом - выполнять фоновые задачи. Масштабирование!
> Читал про '&' что процесс открывется в том же треде что и сам инстанс php
Это неправда.
>>186007
А подумай, какую информацию ты хотел бы хранить о файле.
>>186281
Не знаю. Ищи какие-то отличительные признаки.
Мне тоже непонятно. думаю, что особо нет смысла. В JS в новых браузерах есть FormData для отправки данных форм аяксом, и туда можно прикладывать файлы.
>>185260
Получается да, надо где-то хранить состояние. Еще ведь конвертация может провалитсья и надо будет запустить ее повторно.
БД и сервер очередей выполняют разные задачи. БД - хранит данные. Сервер очередей - уведомляет о появлении новой задачи в БД.
>>185334
Думаю, надо залезть в настройки форматирования и поправить их.
>>185854
Тут есть подвохи. В Линуксе после завершения процесса его родитель получает уведомление и информацию - код выхода, потребленные ресурсы итд. Если ты запустил дочерний процесс, а потом внезапно умер, то после завершения дочернего процесса информацию получать будет некому и в системе может остаться "зомби" - данные от умершего процесса.
В этом плане конечно из-под веб скрипта лучше длительные процессы не запускать.
Плюс, непонятно, как ты в такой модели будешь контролировать число запущенных процессов? Их же можно много назапускать и они будут мешать друг другу и тормозить. Как отслеживать, что они упали с ошибкой, как сохранить информацию об ошибке?
Плюс, в случае с очередью задач ты можешь сделать систему распределенной - на одном сервере обслуживать запросы, на другом - выполнять фоновые задачи. Масштабирование!
> Читал про '&' что процесс открывется в том же треде что и сам инстанс php
Это неправда.
>>186007
А подумай, какую информацию ты хотел бы хранить о файле.
>>186281
Не знаю. Ищи какие-то отличительные признаки.
Надо смотреть документацию по созданию тем.
>>186941
Довольно далеки. Если ты будешь учиться в нашем треде, то тебе наша информация пригодится, так как мы изучаем ООП и Яву тебе будет проще понять.
>>187366
Есть функция поиска символа в строке. Находишь позицию одного символа, позицию второго и вычитаешь их друг из друга. Обрабатываешь отрицательные значения.
Только используй mb-функции, так как strlen не работает с кириллицей.
>>187491
Если у тебя маленькая посещаемость, то можно фигачить все прямо в базу. Если большая то почитай статью от мейл ру https://habr.com/company/mailru/blog/206494/ (текст куда-то потерялся, копия: http://savepearlharbor.com/?p=206494 )
Так да, общий принцип в том, что мы храним в базе немного устаревшие данные, новые показы фигачим куда-нибудь в редис в очередь с персистентностью (сохранением на диск), и по крону раз в N минут аггрегируем и обновляем показатели в БД.
Единтсвенное, надо проработать схему, которая будет устойчива к отказам. Условно говоря, твой PHP скрипт обновления базы может упасть в любом месте и надо сделать так, чтобы данные все равно оставались бы в согласованном виде.
> каждый раз ставить таск с дилеем, скажем, в 5 минут, который собирает просмотры из редиса и заносит в бд. Если таск стоит в очереди, то не ставим
Имхо проще использовать крон.
>>187793
>>196705
> return mb_strtoupper(mb_substr($text, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr(mb_convert_case($text, MB_CASE_LOWER, 'UTF-8'), 1, mb_strlen($text), 'UTF-8');
Очень длинно и тяжело читать, надо вынести часть выражения в переменные и разбить на несколько действий.
Сделано верно.
Надо смотреть документацию по созданию тем.
>>186941
Довольно далеки. Если ты будешь учиться в нашем треде, то тебе наша информация пригодится, так как мы изучаем ООП и Яву тебе будет проще понять.
>>187366
Есть функция поиска символа в строке. Находишь позицию одного символа, позицию второго и вычитаешь их друг из друга. Обрабатываешь отрицательные значения.
Только используй mb-функции, так как strlen не работает с кириллицей.
>>187491
Если у тебя маленькая посещаемость, то можно фигачить все прямо в базу. Если большая то почитай статью от мейл ру https://habr.com/company/mailru/blog/206494/ (текст куда-то потерялся, копия: http://savepearlharbor.com/?p=206494 )
Так да, общий принцип в том, что мы храним в базе немного устаревшие данные, новые показы фигачим куда-нибудь в редис в очередь с персистентностью (сохранением на диск), и по крону раз в N минут аггрегируем и обновляем показатели в БД.
Единтсвенное, надо проработать схему, которая будет устойчива к отказам. Условно говоря, твой PHP скрипт обновления базы может упасть в любом месте и надо сделать так, чтобы данные все равно оставались бы в согласованном виде.
> каждый раз ставить таск с дилеем, скажем, в 5 минут, который собирает просмотры из редиса и заносит в бд. Если таск стоит в очереди, то не ставим
Имхо проще использовать крон.
>>187793
>>196705
> return mb_strtoupper(mb_substr($text, 0, 1, 'UTF-8'), 'UTF-8') . mb_substr(mb_convert_case($text, MB_CASE_LOWER, 'UTF-8'), 1, mb_strlen($text), 'UTF-8');
Очень длинно и тяжело читать, надо вынести часть выражения в переменные и разбить на несколько действий.
Сделано верно.
Сравнение дробных чисел делается путем их вычитания и сравнения разницы с малым числом:
function isEqual(a, b)
{
var diff = Math.abs(a - b);
var epsilon = ...; // маленькое число
return diff < epsilon;
}
Как выбрать epsilon? Зависит от ситуации. Если ты сравниваешь вес товаров, можно взять например 1 грамм. Если напряжение - берешь с учетом погрешности вольтметра.
В общем случае, epsilon вычисляется исходя из самих чисел и известной погрешности формата float. Этой теме посвящен отдельный сайт: http://floating-point-gui.de/errors/comparison/
> Вопрос был в том числе "Как бороться?" :) Ответа я не нашел.
Использовать правильные алгоритмы сравнения чисел с учетом погрешности. То есть надо понимать, что точно хранятся только первые N знаков числа и исходить из этого.
> В голову приходит только превратить обе части в строки и сравнить их. Но тут тоже могут быть какие-нибудь подводные камни.
Это никак не решает проблему так как в этом случае '1.99999999' != '2'.
>>188215
> ОК. А как это обходят в ЯП? Используют округление от нуля?
В ЯП применяется округление по тем правилам, которые заданы в языке. То есть см. мануал.
> Можно поподробнее, пожалуйста?
Да, можно хранить деньги как int в копейках или сотых копейки. То есть 3 рубля = 30000 сотых копейки. Преобразовывать туда-обратно.
Если использовать float, у тебя балансы не будут сходиться.
Сравнение дробных чисел делается путем их вычитания и сравнения разницы с малым числом:
function isEqual(a, b)
{
var diff = Math.abs(a - b);
var epsilon = ...; // маленькое число
return diff < epsilon;
}
Как выбрать epsilon? Зависит от ситуации. Если ты сравниваешь вес товаров, можно взять например 1 грамм. Если напряжение - берешь с учетом погрешности вольтметра.
В общем случае, epsilon вычисляется исходя из самих чисел и известной погрешности формата float. Этой теме посвящен отдельный сайт: http://floating-point-gui.de/errors/comparison/
> Вопрос был в том числе "Как бороться?" :) Ответа я не нашел.
Использовать правильные алгоритмы сравнения чисел с учетом погрешности. То есть надо понимать, что точно хранятся только первые N знаков числа и исходить из этого.
> В голову приходит только превратить обе части в строки и сравнить их. Но тут тоже могут быть какие-нибудь подводные камни.
Это никак не решает проблему так как в этом случае '1.99999999' != '2'.
>>188215
> ОК. А как это обходят в ЯП? Используют округление от нуля?
В ЯП применяется округление по тем правилам, которые заданы в языке. То есть см. мануал.
> Можно поподробнее, пожалуйста?
Да, можно хранить деньги как int в копейках или сотых копейки. То есть 3 рубля = 30000 сотых копейки. Преобразовывать туда-обратно.
Если использовать float, у тебя балансы не будут сходиться.
Да, верно.
>>188255
Ты переусложнил. Ты мог бы сделать объект "коллекция вопросов", QuestionsCollection, но я не вижу особого смысла его делать, когда можно просто взять массив.
Ни фабрика, ни "приспособленец" не имеют к этому отношения и предназначены для других целей.
Твой класс не является фабрикой, так как фабрика - это класс, отвечающий за создание объектов. Используется, если мы хотим дать пользователю возможность влиять на то, как в нашем коде создаются объекты.
>>188528
> Формы симфони мне только дадут возможность использовать HTML5 валидацию, а как насчёт более сложных случаем?
Надо искать или писать библиотеку, конвертирующую правила Симфони в правила для JS-валидатора. Задача нетривиальная, так как даже диалекты регулярок в JS и PHP в мелочах различаются.
Варианты реализации разные. Давай начнем с самой формулировки задачи:
- есть форма редактирования теста
- надо на клиенте динамически добавлять и удалять вопросы
- надо переставлять вопросы местами
- надо менять тип вопросов, в зависимости от типа там доступны разные поля
- надо добавлять и удалять варианты вопросов
- в идеале еще и валидировать на клиенте
Первый вариант - делать на обычном JS. То есть сделать шаблон вопроса, и при нажатии "добавить вопрос" на JS создавать новый элемент DOM на основе шаблона и вставлять в страницу. При перетаскивании - переставлять местами элементы DOM (есть готовые плагины для jQuery) и менять порядковые номера в скрытых полях.
Переключение типов вопросов можно сделать по аналогии с табами - просто сделать несколько форм, но показывать только одну.
Получается такая огромная форма, которую можно модицифировать динамически.
При желании можно добавить сохранение по таймеру на сервер, и аякс-отправку данных для валидации без перезагрузки страницы.
Второй вариант - делать мини-приложение (мини-SPA) для редактирования теста. Выбор богатый: Angular, knockout, React, vue.js. Скорее всего, получится решение потяжелее и посложнее, но зато будет больше возможностей. В соответствие с идеологией MVC ты делаешь модель данных, которая хранит информацию о редактируемом тесте, и с помощью view отображаешь эту модель на странице в виде формы. При изменениях в форме ты меняешь модель данных и view обновляет представление теста на экране.
В такой схеме ты можешь писать как клиентскую валидацию для модели данных, так и аякс-валидацию через отправку модели на сервер. Ну и опять же, периодическое сохранение тоже можно прикрутить.
Да, верно.
>>188255
Ты переусложнил. Ты мог бы сделать объект "коллекция вопросов", QuestionsCollection, но я не вижу особого смысла его делать, когда можно просто взять массив.
Ни фабрика, ни "приспособленец" не имеют к этому отношения и предназначены для других целей.
Твой класс не является фабрикой, так как фабрика - это класс, отвечающий за создание объектов. Используется, если мы хотим дать пользователю возможность влиять на то, как в нашем коде создаются объекты.
>>188528
> Формы симфони мне только дадут возможность использовать HTML5 валидацию, а как насчёт более сложных случаем?
Надо искать или писать библиотеку, конвертирующую правила Симфони в правила для JS-валидатора. Задача нетривиальная, так как даже диалекты регулярок в JS и PHP в мелочах различаются.
Варианты реализации разные. Давай начнем с самой формулировки задачи:
- есть форма редактирования теста
- надо на клиенте динамически добавлять и удалять вопросы
- надо переставлять вопросы местами
- надо менять тип вопросов, в зависимости от типа там доступны разные поля
- надо добавлять и удалять варианты вопросов
- в идеале еще и валидировать на клиенте
Первый вариант - делать на обычном JS. То есть сделать шаблон вопроса, и при нажатии "добавить вопрос" на JS создавать новый элемент DOM на основе шаблона и вставлять в страницу. При перетаскивании - переставлять местами элементы DOM (есть готовые плагины для jQuery) и менять порядковые номера в скрытых полях.
Переключение типов вопросов можно сделать по аналогии с табами - просто сделать несколько форм, но показывать только одну.
Получается такая огромная форма, которую можно модицифировать динамически.
При желании можно добавить сохранение по таймеру на сервер, и аякс-отправку данных для валидации без перезагрузки страницы.
Второй вариант - делать мини-приложение (мини-SPA) для редактирования теста. Выбор богатый: Angular, knockout, React, vue.js. Скорее всего, получится решение потяжелее и посложнее, но зато будет больше возможностей. В соответствие с идеологией MVC ты делаешь модель данных, которая хранит информацию о редактируемом тесте, и с помощью view отображаешь эту модель на странице в виде формы. При изменениях в форме ты меняешь модель данных и view обновляет представление теста на экране.
В такой схеме ты можешь писать как клиентскую валидацию для модели данных, так и аякс-валидацию через отправку модели на сервер. Ну и опять же, периодическое сохранение тоже можно прикрутить.
Что-то он древний такой...
>>190839
тут можно было бы избавиться от дублирования кода в ветках if, если сделать так:
текущая выплата = меньшее из (5000, остаток долга);
По стихам - надо было случайные индексы генерировать внутри цикла, а не один раз до цикла.
> палиндром
> http://sandbox.onlinephpfunctions.com/code/1eed5a0bfc43968f61079d0dd36a84d9d66c2b2c
Много переменных с неудачными названиями. Вместо countOne лучше было написать leftLetter или letter1.
Не очень удачно, что $i начинается с -1.
>>191135
Быстро не объяснить. Обычно используют куки или сессии. Например, в куках можно хранить id пользователя и специальный код, индивидуальный для каждого пользователя, для защиты от подделки.
>>192517
Если тебя интересует механизм переключения, то он такой. Чтобы у кнопки было состояние, мы используем элементы формы - чекбоксы или радиокнопки, у которых оно есть. Скрываем их с экрана, чтобы не мешались. Поскольку кликнуть по скрытому элементу трудно, то добавляем лейблы (label) - тут 2 варианта:
- либо привязать label к input по id
- либо засунуть input внутрь label, тогда id не нужен и не надо следить за их уникальностью
- также есть еще вариант как-то сделать инпут прозрачным, растянуть на размер кнопки и повесить над ней, чтобы обойтись без label
label позиционируем так, чтобы его размер охватывал всю кнопку. Можно, например, кнопку как раз и сделать этим тегом.
После этого, остается только как-то отображать изменение состояния инпута в виде "нажатости" кнопки. Для этого мы помещаем какой-то элемент, спан или див, рядом с инпутом и пишем ему стили, которые зависят от состояния инпута:
.buttons input:checked + .buttons__bg { background: red; }
Вот и все.
Дальше остается только поработать с клавиатурной навигацией.
Что-то он древний такой...
>>190839
тут можно было бы избавиться от дублирования кода в ветках if, если сделать так:
текущая выплата = меньшее из (5000, остаток долга);
По стихам - надо было случайные индексы генерировать внутри цикла, а не один раз до цикла.
> палиндром
> http://sandbox.onlinephpfunctions.com/code/1eed5a0bfc43968f61079d0dd36a84d9d66c2b2c
Много переменных с неудачными названиями. Вместо countOne лучше было написать leftLetter или letter1.
Не очень удачно, что $i начинается с -1.
>>191135
Быстро не объяснить. Обычно используют куки или сессии. Например, в куках можно хранить id пользователя и специальный код, индивидуальный для каждого пользователя, для защиты от подделки.
>>192517
Если тебя интересует механизм переключения, то он такой. Чтобы у кнопки было состояние, мы используем элементы формы - чекбоксы или радиокнопки, у которых оно есть. Скрываем их с экрана, чтобы не мешались. Поскольку кликнуть по скрытому элементу трудно, то добавляем лейблы (label) - тут 2 варианта:
- либо привязать label к input по id
- либо засунуть input внутрь label, тогда id не нужен и не надо следить за их уникальностью
- также есть еще вариант как-то сделать инпут прозрачным, растянуть на размер кнопки и повесить над ней, чтобы обойтись без label
label позиционируем так, чтобы его размер охватывал всю кнопку. Можно, например, кнопку как раз и сделать этим тегом.
После этого, остается только как-то отображать изменение состояния инпута в виде "нажатости" кнопки. Для этого мы помещаем какой-то элемент, спан или див, рядом с инпутом и пишем ему стили, которые зависят от состояния инпута:
.buttons input:checked + .buttons__bg { background: red; }
Вот и все.
Дальше остается только поработать с клавиатурной навигацией.
Это вопросы к администрации учреждения.
>>193839
http://twig-extensions.readthedocs.io/en/latest/text.html - это не годится? Если нет, то писать функцию для Твига самому (в Твиг можно добавлять функции).
>>193882
Без u PCRE думает, что буквы занимают ровно 1 байт и воспринимают 1 букву кириллицы как 2 обычные недобуквы. И от этого все ломается. Например, [аб] воспринимается как набор из 4 недобукв [а1а2б1б2] (а1 и а2 - это обозначение байтов, которыми кодируется буква "а") и совпадает со всем попало.
\w вообще без флага u кириллицу может не видеть (еще бы, если она занимает 2 байта, а \w ищет один).
Допустим есть посты. У постов есть всякие стандартные атрибуты как id, author_id, time, body и т д.
Далее у постов могут быть картинки, допустим как на дваче, до 8 картинок к посту. Картинки лежат соответственно в отдельной таблице и там id, post_id, file_name например.
Далее есть теги, теги тоже лежат в отдельной таблице: id, tag_name
Ну и так как тегов у поста может быть много, и 1 тег может быть у нескольких постов, то есть еще таблица связей вида: id?, tag_id, post_id где свалка many_to_many организована.
Что бы еще такого приплести, допустим у постов есть комментарии - пусть просто нечто вроде урезанных постов без картинок и тегов, но у комментариев есть авторы, то есть в таблице с комментариями всё выглядит как-то так: id, post_id, author_id, body.
Теперь собственно вопрос, желательно для опытных ребят, как бы вы организовали запросы в такую базу? Начать можно с простого варианта, где скажем нужно заселектить 1 пост. Изи можно приджойнить к нему автора, а сопутствующие вещи такие как картинки, теги и комментарии+авторов комментариев можно параллельно запросить отдельными запросами, вместо того что бы писать монстр запрос, который я даже не знаю как будет выглядеть.
Но что делать, когда у нужен допустим список постов какого-нибудь автора со всей вот этой сопутствующей мишурой (теги, картинки, ответы), тут уже просто невозможно выкрутиться подобным способом: не будешь же потом в цикле для каждого поста слать по 3 дополнительных селекта? Если у тебя постов скажем много?
Как с подобными вещами работают? Как вообще с такой базой подружиться?
Для списка постов все лучше делать одним большим запросом.
Самая длительная операция - установление соединения с бд.
Вторая по длительности передача запроса и получение ответа.
Выполнение запроса базой даже с кучей джоинов, сортировок и т.д. обычно происходит быстрее.
Для каждого запроса, кроме непосредственно выборки данных бд делает парсинг текста запроса, анализ наличия индексов и кеша, определение наиболее рационального способа выборки. Уверен там дохрена еще всякого говна.
Если это высоконагруженный новостной портал, то просто делаются дополнительные таблицы, базы данных, сервера баз данных, с кешем наиболее востребованных записей.
Например первые 5 страниц постов храняться в основновной и дополнительной базе.
Связи с картинками можно вообще не хранить в базе а просто условится что картинки поста с id 382 будут лежать в папке 382 которая лежит в папке соответсвующей дате создания поста. В крайнем случае в таблице постов можно добавить поле с количеством картинок у каждого поста, чтобы знать есть ли они и сколько их.
Как подобные запросы делаются? Там картинки и прочие теги делаются подзапросом и возвращаются склеенными и просто джойнятся в итоге к основному?
>>198378
>Связи с картинками можно вообще не хранить в базе а просто условится что картинки поста с id 382 будут лежать в папке 382 которая лежит в папке соответсвующей дате создания поста.
Да, спасибо за свежую идею, да только на каком-нибудь дваче с кучей постов и малым количеством просмотров каждого вряд ли имеет смысл. На сайте где 1 новость в день и десятки тысяч просмотров - офк отлично зайдет.
Если только джоинами то инфа о кадом посте будет дублироваться для каждой картинки, и при разборе ответа чтобы преобразовать это все в объекты тебе надобудет кучу циклов городить которые для разной хуйни выбирают разные поля игнорируя те что дублируются.
Можно поиграться с GROUP BY и CONCAT попробовать вместо нескольких строк с одним и тем же постом получить одну с дополнительным поле содержащим названия картинок через запятую.
С тегами таже ситуация.
Как то мне довелось детально изучить внутреннию механику построенния запросов для связанных таблиц в yii2.
Допустим с помощью объектов с настроенными связями я строю запрос, который ты описал, для списка постов с картинками, тегами, коментами, и прочим говном.
Естественно в итоге я ожидал что фреймер сгененриет большой запрос с кучей джоинов. Но нетут то было. Этот проказник сделал отдельные запросы, по одному для каждой таблицы, вот таким образом.
Сначала были извлечены записи постов. Затем фрейм собрал их id и выбрал все картинки которые принадлежат этим постам по условию WHERE `image`.`post_id` IN (.....).
Затем все эти картинки были перебраны в цикле и растасованы по нужным постам.
Затем точно также один запрос на все теги которые связаны только с выбранными постами.
Так же с комментами.
Почему то только сейчас вспомнил про этот вариант.
> Под аякс запросы обычно другой метод контроллера пишут или в тот же всё летит?
Если он делает что-то другое, то конечно другой.
> в каждый <li> обернут по сути элемент из базы, в общем наверное нужно весь этот список превратить в js объект сначала?
А зачем?
> то как только устроить редактирование отдельных пунктов чет ума не приложу.
Ну самый просто вариант - поместить список внутрь формы и сделать название каждого пункта инпутом (при желании можно через CSS оформить все так, чтобы он выглядел как обычный текст, а только при клике выглядел как инпут). При удалении добавлять в форму скрытое поле с id удаленного пункта. При добавлении - опять же, добавлять в форму новый инпут.
При нажатии кнопки "Сохранить" отправлять всю форму аяксом на сервер. Элементарно же. Желательно также подсвечивать измененные, но еще не сохраненные поля.
Дальше можно усложнять. Например, можно не отправлять форму целиком по нажатию кнопки, а автоматически отправлять данные при добавлении, удалении или изменении единичного поля. Но это создает сложности: что, если произошла ошибка при отправке запроса? Нужно выводить кнопку для повторения попытки, нужно где-то хранить список неотправленных данных и тд.
По моему, тут все реализуется на простом jQuery, без использования SPA-фреймворков.
>>193973
А что сложного в CMS? CMS это программы для разработки и управления сайтом преимущественно через админку. Иногда к этому добавляется необходисость дописывать кастомный код - "темы оформления" (шаблоны) и "плагины" (для реализации отсуствующего функционала).
Если ты в фреймворках разбираешься, ты CMS освоишь легко и быстро. Без проблем сможешь написать тему или плагин, используя документацию. Найди любой курс "как сделать сайт на CMS x" и увидишь, что там все довольно просто.
> Вон учишь цмс и хоть работа какая-никакая.
Так без знания программирования ты вряд ли будешь конкурентноспособен, так как споткнешься при необходиомсти что-то делать в коде или отлаживать баг. Людей, которые могут зайти в админку и нажать кнопку, и без тебя предостаточно. А со знанием программирования - CMS осваивается легко и ты получаешь преимущество перед нажимателями кнопок.
Алсо, ты на hh.ru заходил? Я набрал "php удаленно", выбрал "Вся Россия, без опыта", и там 30 вакансий вываливается: https://hh.ru/search/vacancy?text=php&schedule=remote&clusters=true&enable_snippets=true&experience=noExperience&from=cluster_experience
А если не ставить галочку "без опыта", то вообще 300. Плюс, можно еще на фрилансе поискать задачи.
> Под аякс запросы обычно другой метод контроллера пишут или в тот же всё летит?
Если он делает что-то другое, то конечно другой.
> в каждый <li> обернут по сути элемент из базы, в общем наверное нужно весь этот список превратить в js объект сначала?
А зачем?
> то как только устроить редактирование отдельных пунктов чет ума не приложу.
Ну самый просто вариант - поместить список внутрь формы и сделать название каждого пункта инпутом (при желании можно через CSS оформить все так, чтобы он выглядел как обычный текст, а только при клике выглядел как инпут). При удалении добавлять в форму скрытое поле с id удаленного пункта. При добавлении - опять же, добавлять в форму новый инпут.
При нажатии кнопки "Сохранить" отправлять всю форму аяксом на сервер. Элементарно же. Желательно также подсвечивать измененные, но еще не сохраненные поля.
Дальше можно усложнять. Например, можно не отправлять форму целиком по нажатию кнопки, а автоматически отправлять данные при добавлении, удалении или изменении единичного поля. Но это создает сложности: что, если произошла ошибка при отправке запроса? Нужно выводить кнопку для повторения попытки, нужно где-то хранить список неотправленных данных и тд.
По моему, тут все реализуется на простом jQuery, без использования SPA-фреймворков.
>>193973
А что сложного в CMS? CMS это программы для разработки и управления сайтом преимущественно через админку. Иногда к этому добавляется необходисость дописывать кастомный код - "темы оформления" (шаблоны) и "плагины" (для реализации отсуствующего функционала).
Если ты в фреймворках разбираешься, ты CMS освоишь легко и быстро. Без проблем сможешь написать тему или плагин, используя документацию. Найди любой курс "как сделать сайт на CMS x" и увидишь, что там все довольно просто.
> Вон учишь цмс и хоть работа какая-никакая.
Так без знания программирования ты вряд ли будешь конкурентноспособен, так как споткнешься при необходиомсти что-то делать в коде или отлаживать баг. Людей, которые могут зайти в админку и нажать кнопку, и без тебя предостаточно. А со знанием программирования - CMS осваивается легко и ты получаешь преимущество перед нажимателями кнопок.
Алсо, ты на hh.ru заходил? Я набрал "php удаленно", выбрал "Вся Россия, без опыта", и там 30 вакансий вываливается: https://hh.ru/search/vacancy?text=php&schedule=remote&clusters=true&enable_snippets=true&experience=noExperience&from=cluster_experience
А если не ставить галочку "без опыта", то вообще 300. Плюс, можно еще на фрилансе поискать задачи.
Тут можно без json, просто форму сделать и отправлять аяксом. Формально это соответствует поставленным требованиям.
>>194055
Изучи список вакансий и задач на фрилансе.
>>194199
Лучше всего указывать это явно, например параметром в URL. Не стоит полагаться на заголовок, так как это потом усложнит отладку. Явное (указание, что тебе нужен аякс-ответ) лучше неявного.
>>194353
Не добавляет. Это делает библиотека jQuery, например. И лучше явно указывать в URL, что тебе нужен аякс.
>>194546
Там еще есть условие, что если у обоих даблы, то это тоже ничья. В остальном верно.
>>194616
Не требуется писать $x = $i, ты можешь сразу использовать $i.
> можно ли сделать всё так, чтобы $i в условиях цикла и $i в теле цикла влияли друг на друга только в одностороннем порядке.
Нет, если я правильно понял. $i это просто обычная переменная и никакой защиты от ее изменения нету. Если ты поменяешь $i внутри цикла, это естественно повлияет на его выполнение. Потому $i стоит использовать "только для чтения", то есть не изменять ее значение внутри цикла.
Тут можно без json, просто форму сделать и отправлять аяксом. Формально это соответствует поставленным требованиям.
>>194055
Изучи список вакансий и задач на фрилансе.
>>194199
Лучше всего указывать это явно, например параметром в URL. Не стоит полагаться на заголовок, так как это потом усложнит отладку. Явное (указание, что тебе нужен аякс-ответ) лучше неявного.
>>194353
Не добавляет. Это делает библиотека jQuery, например. И лучше явно указывать в URL, что тебе нужен аякс.
>>194546
Там еще есть условие, что если у обоих даблы, то это тоже ничья. В остальном верно.
>>194616
Не требуется писать $x = $i, ты можешь сразу использовать $i.
> можно ли сделать всё так, чтобы $i в условиях цикла и $i в теле цикла влияли друг на друга только в одностороннем порядке.
Нет, если я правильно понял. $i это просто обычная переменная и никакой защиты от ее изменения нету. Если ты поменяешь $i внутри цикла, это естественно повлияет на его выполнение. Потому $i стоит использовать "только для чтения", то есть не изменять ее значение внутри цикла.
Но это усложнит понимание логики работы кода и лучше такого избегать.
>>194754
Можно при выводе значения использовать round() для округления, смотри мануал по этой функции.
>>194860
По идее надо прочесть документацию и писать так, как она рекомендует, но на практике ты будешь сталкиваться больше с трешем.
>>194861
Это их выбор, может быть ради упрощения понимания или снижения порога входа. Вообще ты, как разработчик, должен бы не спешить сразу отвергать такой подход, а сравнивать, находить плюсы или минусы.
Может, конечно, это и результат какого-то неприятия ООП среди разработчиков. Такой фанатизм, если он есть, не очень хорошая вещь.
Давай для примера разберем какую-нибудь функцию внутреннего API для плагинов. Ну, например, wp_repote_post, которая отправляет POST-запрос: https://codex.wordpress.org/Function_Reference/wp_remote_post
Мы видим, что там активно используются массивы как аргументы и как результат функции. Хорошо, что формат этих массивов хотя бы описан в документации. Причем, функция не выбрасывет исключения, но может вместо массива вернуть объект WP_Error (а вот и объекты появились), потому после ее вызова мы должны будем сами написать проверку. К сожалению, WP_Error не реализует интерфейс Throwable и потому не может быть использован как объект-исключение в конструкции throw.
$result = wp_remote_post('https://example.com', [...]);
if (is_wp_error($result)) {
throw new \Exception($result->get_error_message());
}
$contentType = wp_remote_retrieve_header($result, 'content-type');
Вот, как это могло бы выглядеть в ООП-стиле:
$httpClient = new HttpClient();
$response = $httpClient->post('https://example.com', [...]);
$contentType = $response->headers->get('Content-Type');
Мне конечно симпатичнее второй вариант, тем более, что он дает меньше возможностей для ошибок. Ну например, ты не можешь вызывать метод $response->headers->get до выполнения запроса, так как у тебя нет объекта $response. Ну то есть само собой очевидно, что ты сначала должен сделать запрос, а потом что-то делать с результатом.
А в Вордпрессе такого нету, функция wp_remote_retrieve_header доступна для вызова в любой момент, и только изучением документации ты можешь понять, что ей нужен результат вызова другой функции.
Если мы возьмем более классическое API, то оно документировано куда как хуже. Вот, например, древняя функция get_posts(): https://developer.wordpress.org/reference/functions/get_posts/
В документации написано, что она возвращает массив, но не описано, что в нем содержится. То есть она возвращает массив непонятно чего. Ну и много ссылок на класс WP_Query, видимо, рекомендуется использовать его.
Если мы посмотрим примеры использования в get_posts() и в WP_Query, то увидим, что они полагаются на использование глобальных переменных и глобального состояния:
// The Query
$query1 = new WP_Query( $args );
// The Loop
while ( $query1->have_posts() ) {
$query1->the_post();
echo '<li>' . get_the_title() . '</li>';
}
На мой взгляд, тут неочевидно, откуда get_the_title() берет данные. Гораздо лучше было бы
foreach ($query1->getPosts() as $post) {
echo get_the_title($post);
}
Как плюс, могу отметить, что в коде WP соблюдается определенный стиль, много комемнтариев. Есть документация. То есть при написании небольшого плагина будет вполне комфортно.
Но это усложнит понимание логики работы кода и лучше такого избегать.
>>194754
Можно при выводе значения использовать round() для округления, смотри мануал по этой функции.
>>194860
По идее надо прочесть документацию и писать так, как она рекомендует, но на практике ты будешь сталкиваться больше с трешем.
>>194861
Это их выбор, может быть ради упрощения понимания или снижения порога входа. Вообще ты, как разработчик, должен бы не спешить сразу отвергать такой подход, а сравнивать, находить плюсы или минусы.
Может, конечно, это и результат какого-то неприятия ООП среди разработчиков. Такой фанатизм, если он есть, не очень хорошая вещь.
Давай для примера разберем какую-нибудь функцию внутреннего API для плагинов. Ну, например, wp_repote_post, которая отправляет POST-запрос: https://codex.wordpress.org/Function_Reference/wp_remote_post
Мы видим, что там активно используются массивы как аргументы и как результат функции. Хорошо, что формат этих массивов хотя бы описан в документации. Причем, функция не выбрасывет исключения, но может вместо массива вернуть объект WP_Error (а вот и объекты появились), потому после ее вызова мы должны будем сами написать проверку. К сожалению, WP_Error не реализует интерфейс Throwable и потому не может быть использован как объект-исключение в конструкции throw.
$result = wp_remote_post('https://example.com', [...]);
if (is_wp_error($result)) {
throw new \Exception($result->get_error_message());
}
$contentType = wp_remote_retrieve_header($result, 'content-type');
Вот, как это могло бы выглядеть в ООП-стиле:
$httpClient = new HttpClient();
$response = $httpClient->post('https://example.com', [...]);
$contentType = $response->headers->get('Content-Type');
Мне конечно симпатичнее второй вариант, тем более, что он дает меньше возможностей для ошибок. Ну например, ты не можешь вызывать метод $response->headers->get до выполнения запроса, так как у тебя нет объекта $response. Ну то есть само собой очевидно, что ты сначала должен сделать запрос, а потом что-то делать с результатом.
А в Вордпрессе такого нету, функция wp_remote_retrieve_header доступна для вызова в любой момент, и только изучением документации ты можешь понять, что ей нужен результат вызова другой функции.
Если мы возьмем более классическое API, то оно документировано куда как хуже. Вот, например, древняя функция get_posts(): https://developer.wordpress.org/reference/functions/get_posts/
В документации написано, что она возвращает массив, но не описано, что в нем содержится. То есть она возвращает массив непонятно чего. Ну и много ссылок на класс WP_Query, видимо, рекомендуется использовать его.
Если мы посмотрим примеры использования в get_posts() и в WP_Query, то увидим, что они полагаются на использование глобальных переменных и глобального состояния:
// The Query
$query1 = new WP_Query( $args );
// The Loop
while ( $query1->have_posts() ) {
$query1->the_post();
echo '<li>' . get_the_title() . '</li>';
}
На мой взгляд, тут неочевидно, откуда get_the_title() берет данные. Гораздо лучше было бы
foreach ($query1->getPosts() as $post) {
echo get_the_title($post);
}
Как плюс, могу отметить, что в коде WP соблюдается определенный стиль, много комемнтариев. Есть документация. То есть при написании небольшого плагина будет вполне комфортно.
Зависит от веб-сервера. В Апаче это делается с помощью mod_rewrite, в нгинксе с помощью указания в конфиге правил роутинга, в встроенном в PHP веб-сервере - с помощью роутинг-скрипта.
>>195721
Ответ должен быть 61270. Скорее всего, проблема в этой лишней строке
$credit = $credit * $percent;
>>196298
А что именно непонятно? Оно наверно предполагает наличие каких-то начальных знаний.
Вот например я тут читаю https://www.slimframework.com/docs/
> At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That’s it.
Вроде как довольно понятно.
>>193739
Да. Если ты захочешь сделать "долгоживущее" приложение, которое обрабатывает много запросов не умирая, то есть ReactPHP для этого.
>>196494
Почитай статью в википедии про принципы REST. Изучи документацию по примерам REST API - API яндекс Диска и API Youtube. Тогда ты будешь лучше ориентироваться.
> Мне нужно сделать по сылке доступ к методу, который отдает json или что то подобное?
Формулируешь плохо. Тебе надо сделать на сервере обработчик, который возвращает ответ в формате вроде JSON (или XML например) по определенному URL.
>>1196527
>>196529
Во-первых, ресайзить с 1000 до 150 - плохая идея, так как пользователь зря грузит огромную картинку. Ресайзят обычно в небольших пределах.
Во-вторых, изучи такие свойства: max-width, max-height у картинок, background-cover, object-cover. И может тут в посте про методы центрирования что-то полезное есть: https://2ch.hk/pr/res/1152267.html#1172048 (М)
Зависит от веб-сервера. В Апаче это делается с помощью mod_rewrite, в нгинксе с помощью указания в конфиге правил роутинга, в встроенном в PHP веб-сервере - с помощью роутинг-скрипта.
>>195721
Ответ должен быть 61270. Скорее всего, проблема в этой лишней строке
$credit = $credit * $percent;
>>196298
А что именно непонятно? Оно наверно предполагает наличие каких-то начальных знаний.
Вот например я тут читаю https://www.slimframework.com/docs/
> At its core, Slim is a dispatcher that receives an HTTP request, invokes an appropriate callback routine, and returns an HTTP response. That’s it.
Вроде как довольно понятно.
>>193739
Да. Если ты захочешь сделать "долгоживущее" приложение, которое обрабатывает много запросов не умирая, то есть ReactPHP для этого.
>>196494
Почитай статью в википедии про принципы REST. Изучи документацию по примерам REST API - API яндекс Диска и API Youtube. Тогда ты будешь лучше ориентироваться.
> Мне нужно сделать по сылке доступ к методу, который отдает json или что то подобное?
Формулируешь плохо. Тебе надо сделать на сервере обработчик, который возвращает ответ в формате вроде JSON (или XML например) по определенному URL.
>>1196527
>>196529
Во-первых, ресайзить с 1000 до 150 - плохая идея, так как пользователь зря грузит огромную картинку. Ресайзят обычно в небольших пределах.
Во-вторых, изучи такие свойства: max-width, max-height у картинок, background-cover, object-cover. И может тут в посте про методы центрирования что-то полезное есть: https://2ch.hk/pr/res/1152267.html#1172048 (М)
Упрощение:
(рус)(лат)(рус)?|(лат)(рус)
>>196805
Это неправда. Фреймворки нельзя изучать, не зная языка, в том числе концепций ООП.
>>196856
Обрати внимание, что эта библиотека не использует исключения и ты должен проверять результат каждого вызова, при ошибке сам получать подробности ошибки. Игнорировать ошибки, естественно, не дело.
Насчет метода subscribe - я не уверен, что тут подойдет ReactPHP. Разве метод subscribe() работает асинхронно? Увы, документация по phpredis это никак не освещает (если у тебя есть время, можешь допилить ее), примеров использования я тоже в репозитории не нашел. Как я понимаю, клиент redis при выдаче команды SUBSCRIBE начинает просто ждать сообщений от redis, блокируя тред. То есть вызов $redis->subscribe() блокирующий. И до $loop->run выполнение просто не дойдет.
Для таймаута, судя по https://github.com/phpredis/phpredis/issues/634 можно использовать опцию задания таймаута.
Возможно, правильнее использовать pubsub не для передачи самих сообщений, а для передачи уведомления о том, что что-то изменилось. А за обновлениями лезть в БД.
Также, нужна защита от ошибок. Что, если твой PHP скрипт получил из redis сообщение и умер? Клиент отправляет новый запрос, но сообщение-то уже забрано из redis. Защититься от этого можно такой логикой:
- проверить изменения в БД, если есть, вернуть клиенту
- ждать событий в redis с таймаутом
- проверить изменения в БД, если есть, вернуть клиенту
То есть проектируй свои скрипты исходя из того, что любая операция может не удасться и они могут упасть на любой строчке.
> Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция?
Думаю, нет. Вот ее код:
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2491
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis_commands.c#L755 (генерирует текст команды subscribe)
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L272 (обрабатывает ответ на нее)
Код не особо понятный, но я вижу там
> / Multibulk response, {[pattern], type, channel, payload } /
> while(1) {
> if (!redis_sock_read_multibulk_reply_zval(
> INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
> ....
> if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)
> ==FAILURE) ...
> }
То есть блокирующий цикл.
> Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?
Судя по коду
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2477
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L1040
> if(ret > LONG_MAX) { / overflow /
> RETVAL_STRINGL(response + 1, response_len - 1);
> } else {
> RETVAL_LONG((long)ret);
> }
> ...
> if (IS_ATOMIC(redis_sock)) {
> RETVAL_FALSE;
Есть такие варианты:
- целое число (long - это 32-битное целое)
- строка с числом, если оно большое
- false
Что это за число - надо смотреть документацию по PUBLISH в redis, что она вернет.
> Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
Приемлемо. Есть и много других форматов конечно - BSON, Protocol Buffers и тд.
> К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.
Да, только к моменту получения оно может устареть в сравнении с копией в базе.
> Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
> А что вы думаете?
Не знаю. Зависит от ситуации наверно.
> И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?
Надо учитывать возможность устаревания данных или их потерь.
Упрощение:
(рус)(лат)(рус)?|(лат)(рус)
>>196805
Это неправда. Фреймворки нельзя изучать, не зная языка, в том числе концепций ООП.
>>196856
Обрати внимание, что эта библиотека не использует исключения и ты должен проверять результат каждого вызова, при ошибке сам получать подробности ошибки. Игнорировать ошибки, естественно, не дело.
Насчет метода subscribe - я не уверен, что тут подойдет ReactPHP. Разве метод subscribe() работает асинхронно? Увы, документация по phpredis это никак не освещает (если у тебя есть время, можешь допилить ее), примеров использования я тоже в репозитории не нашел. Как я понимаю, клиент redis при выдаче команды SUBSCRIBE начинает просто ждать сообщений от redis, блокируя тред. То есть вызов $redis->subscribe() блокирующий. И до $loop->run выполнение просто не дойдет.
Для таймаута, судя по https://github.com/phpredis/phpredis/issues/634 можно использовать опцию задания таймаута.
Возможно, правильнее использовать pubsub не для передачи самих сообщений, а для передачи уведомления о том, что что-то изменилось. А за обновлениями лезть в БД.
Также, нужна защита от ошибок. Что, если твой PHP скрипт получил из redis сообщение и умер? Клиент отправляет новый запрос, но сообщение-то уже забрано из redis. Защититься от этого можно такой логикой:
- проверить изменения в БД, если есть, вернуть клиенту
- ждать событий в redis с таймаутом
- проверить изменения в БД, если есть, вернуть клиенту
То есть проектируй свои скрипты исходя из того, что любая операция может не удасться и они могут упасть на любой строчке.
> Я не знаю языка C, поэтому я не могу разобраться, ассинхронная ли это функция?
Думаю, нет. Вот ее код:
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2491
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis_commands.c#L755 (генерирует текст команды subscribe)
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L272 (обрабатывает ответ на нее)
Код не особо понятный, но я вижу там
> / Multibulk response, {[pattern], type, channel, payload } /
> while(1) {
> if (!redis_sock_read_multibulk_reply_zval(
> INTERNAL_FUNCTION_PARAM_PASSTHRU, redis_sock, &z_resp)) break;
> ....
> if(zend_call_function(&(sctx->cb), &(sctx->cb_cache) TSRMLS_CC)
> ==FAILURE) ...
> }
То есть блокирующий цикл.
> Опять же, из-за незнания C, я не знаю, что эта функция вернет? Как всегда, true/false в случае успеха/неудачи?
Судя по коду
- https://github.com/phpredis/phpredis/blob/4533920fde608e12fe763cf2c977ed711c77e120/redis.c#L2477
- https://github.com/phpredis/phpredis/blob/2828c2f187900a5ddc450ce857b467d9e0b483ac/library.c#L1040
> if(ret > LONG_MAX) { / overflow /
> RETVAL_STRINGL(response + 1, response_len - 1);
> } else {
> RETVAL_LONG((long)ret);
> }
> ...
> if (IS_ATOMIC(redis_sock)) {
> RETVAL_FALSE;
Есть такие варианты:
- целое число (long - это 32-битное целое)
- строка с числом, если оно большое
- false
Что это за число - надо смотреть документацию по PUBLISH в redis, что она вернет.
> Приемлимо для обмена сообщений между клиентами Redis использовать строку в виде данных json? Или есть какая-то другая договрённость для обмена большого количества информации?
Приемлемо. Есть и много других форматов конечно - BSON, Protocol Buffers и тд.
> К примеру, в моём коде, я хотел опубликовать весь объект Сообщения, включая id, само сообщение, дату и так далее.
Да, только к моменту получения оно может устареть в сравнении с копией в базе.
> Мне кажется, это эффективно - меньше дёрганий на обращение к БД на обнавление сообщений/контактов/проверки набора сообщений.
> А что вы думаете?
Не знаю. Зависит от ситуации наверно.
> И кстати, в моём примере, при подписке, я не где не обращаюсь к БД, а жду что ко мне придёт уже готовое сообщение. В этом нет ничего плохого?
Надо учитывать возможность устаревания данных или их потерь.
Не читал.
>>197592
Используй точку для склеивания строк. Или конструкции вроде \xff внутри строки.
>>198177
Трудно поверить. Ты наверно просто основы пропустил и берешься сразу за сложное. Порешай-ка наши задачи на JS из ОП-поста, может быть поможет разобраться.
>>198191
Джойны сами по себе не страшно. Страшно - это если СУБД приходится обходить огромные таблицы полным перебором и сопоставлять строки в них вместо того, чтобы выбрать пару строк по ключу. Пример не страшных джойнов:
SELECT comments.*
FROM comments
JOIN posts ON ...
JOIN users ON comments.author_id = users.id
JOIN users AS post_author ON post_author.id = posts.author_id
LEFT JOIN tags_to_posts ...
WHERE comment.date BETWEEN x AND y
Тут все просто: СУБД выбирает комментарии по дате (в идеале по индексу), и затем к ним приджойнивает посты, авторов и тд. Все будет работать относительно быстро.
Пример страшных джойнов:
SELECT products.*
FROM products
JOIN products_colors ...
JOIN products_categories ...
WHERE products_colors.color_id = ?
AND products_categories.category_id = ?
AND products.price < 1000
Здесь СУБД придется выбрать сначала огромный список id товаров определенного цвета, потом огромный список товаров определенной категории, потом пересечь их, если она умеет это делать, потом приджойнить огромные список товаров и отсеять их по цене.
Не читал.
>>197592
Используй точку для склеивания строк. Или конструкции вроде \xff внутри строки.
>>198177
Трудно поверить. Ты наверно просто основы пропустил и берешься сразу за сложное. Порешай-ка наши задачи на JS из ОП-поста, может быть поможет разобраться.
>>198191
Джойны сами по себе не страшно. Страшно - это если СУБД приходится обходить огромные таблицы полным перебором и сопоставлять строки в них вместо того, чтобы выбрать пару строк по ключу. Пример не страшных джойнов:
SELECT comments.*
FROM comments
JOIN posts ON ...
JOIN users ON comments.author_id = users.id
JOIN users AS post_author ON post_author.id = posts.author_id
LEFT JOIN tags_to_posts ...
WHERE comment.date BETWEEN x AND y
Тут все просто: СУБД выбирает комментарии по дате (в идеале по индексу), и затем к ним приджойнивает посты, авторов и тд. Все будет работать относительно быстро.
Пример страшных джойнов:
SELECT products.*
FROM products
JOIN products_colors ...
JOIN products_categories ...
WHERE products_colors.color_id = ?
AND products_categories.category_id = ?
AND products.price < 1000
Здесь СУБД придется выбрать сначала огромный список id товаров определенного цвета, потом огромный список товаров определенной категории, потом пересечь их, если она умеет это делать, потом приджойнить огромные список товаров и отсеять их по цене.
> Теперь собственно вопрос, желательно для опытных ребят, как бы вы организовали запросы в такую базу?
Самый простой (но не самый быстрый) вариант - использовать ORM вроде Доктрины. Смотри, как я выбираю пост по id:
$id = 123;
$post = $em->find(Post::class, $id);
printf("Название: %s, Автор: %s, Комментариев: %s\n",
$post->getTitle(),
$post->getAuthor()->getName(),
count($post->getComments())
);
$tagNames = array_map($post->getTags(), function ($tag) { return $tag->getTitle(); });
printf("Теги: %s\n", implode(', ', $tagNames));
В SQL ты можешь сделать это через несколько запросов или одним запросом с джойнами:
SELECT p.title, a.name, COUNT(DISTINCT com.id) AS commentCount,
GROUP_CONCAT(DISTINCT tag.title) AS tags
FROM posts p ON ...
JOIN users a ON ...
LEFT JOIN comments com ON ...
...
GROUP BY p.id
> Но что делать, когда у нужен допустим список постов какого-нибудь автора со всей вот этой сопутствующей мишурой (теги, картинки, ответы), тут уже просто невозможно выкрутиться подобным способом: не будешь же потом в цикле для каждого поста слать по 3 дополнительных селекта? Если у тебя постов скажем много?
Можно сделать один запрос с джойнами и группировкой. Потом второй для выборки всех картинок. Третий для выборки всех комментов и тд. Зачем делать запросы в цикле, когда можно получать данные сразу по многим постам одним запросом?
SELECT FROM images WHERE post_id IN (1,2,3,4,5);
>>198376
Вообще, нет. Запросы бывают тяжелые, зависит от ситуации.
>>198378
> Связи с картинками можно вообще не хранить в базе а просто условится что картинки поста с id 382 будут лежать в папке 382 которая лежит в папке соответсвующей дате создания поста.
Это имеет недостатки:
- надо проверять, есть ли картинка или нет, через file_exists()
- при замене картинки имя не меняется и браузер может использовать старую кешированную картинку, если используется HTTP кеширование
- нельзя быстро получить размеры картинки, информацию о размерах и параметрах превьюшек
- если ты хранишь картинки в CDN, то быстро проверить их наличие и параметры вообще нельзя, надо делать запрос к CDN
- нельзя проверить, все ли картинки на месте
На практике, почти всегда, если нагрузки не гигантские, выгоднее всегда хранить информацию о картинках и превьюшках в БД. Это решает описанные выше проблемы.
>>198561
Удобнее делать групповые запросы. Первый запрос - выбрать все посты, второй - все картинки к ним. третий - все комментарии итд.
> Теперь собственно вопрос, желательно для опытных ребят, как бы вы организовали запросы в такую базу?
Самый простой (но не самый быстрый) вариант - использовать ORM вроде Доктрины. Смотри, как я выбираю пост по id:
$id = 123;
$post = $em->find(Post::class, $id);
printf("Название: %s, Автор: %s, Комментариев: %s\n",
$post->getTitle(),
$post->getAuthor()->getName(),
count($post->getComments())
);
$tagNames = array_map($post->getTags(), function ($tag) { return $tag->getTitle(); });
printf("Теги: %s\n", implode(', ', $tagNames));
В SQL ты можешь сделать это через несколько запросов или одним запросом с джойнами:
SELECT p.title, a.name, COUNT(DISTINCT com.id) AS commentCount,
GROUP_CONCAT(DISTINCT tag.title) AS tags
FROM posts p ON ...
JOIN users a ON ...
LEFT JOIN comments com ON ...
...
GROUP BY p.id
> Но что делать, когда у нужен допустим список постов какого-нибудь автора со всей вот этой сопутствующей мишурой (теги, картинки, ответы), тут уже просто невозможно выкрутиться подобным способом: не будешь же потом в цикле для каждого поста слать по 3 дополнительных селекта? Если у тебя постов скажем много?
Можно сделать один запрос с джойнами и группировкой. Потом второй для выборки всех картинок. Третий для выборки всех комментов и тд. Зачем делать запросы в цикле, когда можно получать данные сразу по многим постам одним запросом?
SELECT FROM images WHERE post_id IN (1,2,3,4,5);
>>198376
Вообще, нет. Запросы бывают тяжелые, зависит от ситуации.
>>198378
> Связи с картинками можно вообще не хранить в базе а просто условится что картинки поста с id 382 будут лежать в папке 382 которая лежит в папке соответсвующей дате создания поста.
Это имеет недостатки:
- надо проверять, есть ли картинка или нет, через file_exists()
- при замене картинки имя не меняется и браузер может использовать старую кешированную картинку, если используется HTTP кеширование
- нельзя быстро получить размеры картинки, информацию о размерах и параметрах превьюшек
- если ты хранишь картинки в CDN, то быстро проверить их наличие и параметры вообще нельзя, надо делать запрос к CDN
- нельзя проверить, все ли картинки на месте
На практике, почти всегда, если нагрузки не гигантские, выгоднее всегда хранить информацию о картинках и превьюшках в БД. Это решает описанные выше проблемы.
>>198561
Удобнее делать групповые запросы. Первый запрос - выбрать все посты, второй - все картинки к ним. третий - все комментарии итд.
Я сделал такую:
>$regexp = '/^8([()\s-]([0-9]{10}))$/';
И конечно же ничего не работает, не понимаю, как быть.
В примерах всё только с началом кода и концом, а тут надо именно чтоб между числами были эти символы.
Вообще есть ли есть какая либо задача есть ли какой-то определенный алгортим как делать какой-то большой проект?
Этот вопрос меня волнует, потому что я почти всегда начинаю делать заранее не планируя, вот просто начинаю с чего угодно и по мере потребностей дописываю все что нужно, но сталкиваюсь с такой проблемой, что не видя целой картины и хорошо все не запланировав я сталкиваюсь с множеством подводных камней, но и "увидеть все сразу" у меня тоже не получается, мне кажется, что это приходит с опытом.
Как вообще правильно планировать разработку и может какую-то хорошую литературу подскажете?
http://sandbox.onlinephpfunctions.com/code/420e0d6e51382e49251f9e1411329c06f51c505f
И просто исправление текста:
http://sandbox.onlinephpfunctions.com/code/516d35fe23ee0c43c6038af65693aa25e1bd231e
Задачки отсюда, есличе
https://phpbooktest2.ga/l1/finals.html
%%походу все-таки нужна%
анон - >>199160
хотя нет, по интерфейсу видно, что учитель может редактировать отметки с разных предметов, получается у каждой группы свой учитель, но это уже не для вуза, а для начальной школы получается приложение
То что phpredis работает не асинхронно, это вызывает сомнения в использовании этой библиотеки. Да и ваши справедливые замечания, по поводу документации и обработки ошибок, подливают масла в огонь. Слишком много у неё недостатков. Дальше только будет больше проблем с ней.
Подумав над этим, я решил написать что-то подобное на ReactPHP, без участия Redis:
subscribe.php
<?php
// Каналы опустим для простоты (если они вообще нужны)
$database = new Database(...);
$message = new Message();
// Проверяем изменения
$message = $database->check(...);
// Если сообщение пустое ждем событий
if (empty(array_filter(get_object_vars($object)))) {
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server('127.0.0.1:8080', $loop);
$socket->on('connection', function (ConnectionInterface $c) {
$c->on('data', function ($data) use ($c) {
// пришло уведомление, проверяем сообщение в БД и возвращаем его
$message = $database->check(...);
$c->close();
});
});
$loop->addTimer($interval = 30, function () use ($socket) {
$socket->close(); // unsubscribe
});
$loop->run()
}
return $message; // дойдет ли выполнение досюда до окончания таймера?
?>
publish.php
<?php
$database = new Database(...);
$message = new Message($_POST['message']);
$message = $database->add($message);
$loop = React\EventLoop\Factory::create();
$connector = new Connector($loop);
// обработка Reject опущена для простоты
$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $c) {
$c->write(...); // передаем либо уведомление, либо само сообщение целиком
$c->end();
});
$loop->run();
// возвращаем сообщение назад в случае успеха
return $message;
?>
Пара очень быстрых вопросов:
Когда создается сервер, ему передается адрес, который он будет слушать, и при соединении конектора, передается адрес, по которому оно создается. Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?
И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
>- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?
То что phpredis работает не асинхронно, это вызывает сомнения в использовании этой библиотеки. Да и ваши справедливые замечания, по поводу документации и обработки ошибок, подливают масла в огонь. Слишком много у неё недостатков. Дальше только будет больше проблем с ней.
Подумав над этим, я решил написать что-то подобное на ReactPHP, без участия Redis:
subscribe.php
<?php
// Каналы опустим для простоты (если они вообще нужны)
$database = new Database(...);
$message = new Message();
// Проверяем изменения
$message = $database->check(...);
// Если сообщение пустое ждем событий
if (empty(array_filter(get_object_vars($object)))) {
$loop = React\EventLoop\Factory::create();
$socket = new React\Socket\Server('127.0.0.1:8080', $loop);
$socket->on('connection', function (ConnectionInterface $c) {
$c->on('data', function ($data) use ($c) {
// пришло уведомление, проверяем сообщение в БД и возвращаем его
$message = $database->check(...);
$c->close();
});
});
$loop->addTimer($interval = 30, function () use ($socket) {
$socket->close(); // unsubscribe
});
$loop->run()
}
return $message; // дойдет ли выполнение досюда до окончания таймера?
?>
publish.php
<?php
$database = new Database(...);
$message = new Message($_POST['message']);
$message = $database->add($message);
$loop = React\EventLoop\Factory::create();
$connector = new Connector($loop);
// обработка Reject опущена для простоты
$connector->connect('127.0.0.1:8080')->then(function (ConnectionInterface $c) {
$c->write(...); // передаем либо уведомление, либо само сообщение целиком
$c->end();
});
$loop->run();
// возвращаем сообщение назад в случае успеха
return $message;
?>
Пара очень быстрых вопросов:
Когда создается сервер, ему передается адрес, который он будет слушать, и при соединении конектора, передается адрес, по которому оно создается. Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?
И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
>- самописный брокер на ReactPHP, в котором есть поддержка WAMP (Websocket App. Messaging Protocol), в котором есть в том числе и подписки, а также поддержка и websocket, и long polling. Это работает внутри одного процесса PHP.
Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?
Сначала нужно определить с какими данными оперирует пользователь.
Данные могут быть входящими и производными.
Затем делишь данные на группы (данные студента, данные предмета и т.д.)
Каждая группа данных это своя таблица, свой класс. А дальше определяешь в каком классе какие будут методы просто по смыслу.
Я всегда с админки начинаю. Работаю на yii2.
Делаю общие модели данных с общей логикой, затем от них параллельно модели админки и модели фронта.
Сначала это выглядело круто охуенно, но потом я понял что почти во всех проектах этого не нужно было делать, так как модели ничем не отличались.
С другой стороны в любой момент может понадобится добавить такое отличие.
Типичная ошибка многих программистов - делать интерфейс прямым отображением базы данных. Это отдельные вещи, интерфейс делается так, как удобнее пользователю, БД - как удобнее разработчику.
Для ввода названия предмета мы используем поле ввода, так как набрать несколько букв быстрее, чем искать пункт в выпадающем списке. При этом, можно реализовать автодополнение для этого поля, а можно объединить выпадающий список с ручным вводом. А на стороне сервера мы конечно храним предметы в нормализованной БД.
>>199160
Есть разные версии, с чего начинать. Я предпочитаю начинать с модели данных, какие у нас есть данные, как они связаны.
Тут по сути надо сделать 2 приложения:
- серверное, управляющее данными и предоставляющее API для этого (по сути это будет MVC почти без V).
- клиентское, обеспечивающее отображение данных и их изменение пользователем (а тут уже MVC)
Самый сложный момент тут синхронизация данных. На сервере есть БД, на клиенте есть копия части БД, и данные в них могут независимо изменяться. Как обеспечить их согласованность?
Для начала можно попробовать обойтись без этого и просто на каждый чих делать запросы на сервер.
> Вообще есть ли есть какая либо задача есть ли какой-то определенный алгортим как делать какой-то большой проект?
Изучать, как делают другие. В вебе ничего уникального нет и все давно придумано.
Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?
Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).
То есть у тебя не может быть больше одного подписчика. Это не pubsub.
Как я понимаю, реализовать pubsub напрямую на примитивах ОС для IPC (вроде сокетов, которые ты пытался использовать) сложно, если вообще возможно. Проще реализовать его внутри процесса-брокера, и сделать возможность присоединения клиентов-издателей и подписчиков к нему. Я где-то в предыдущем посте предлагал даже пример кода на семафорах: https://phpclub.tech/pr/res/1174695.html#1190031 - сейчас я подумал, что в асинхронном однопоточном коде (Node.JS или ReactPHP) семафоры ведь и не нужны (так как один поток), можно без них это сделать, примерно так (пример сильно упрощен, убрана обработка сетевых ошибок, отсоединений клиентов итд):
// Мы делаем словарь, в котором индексом является номер канала,
// а содержимым - массив сокетов, заинтересованных в сообщениях.
// При поступлении сообщения в канал оно ретранслируется всем
// ждущим его сокетам клиентов.
//
// channelId: [ socket1, socket2, socket3 ]
//
// При отсоединении клиента надо удалять его сокет, это не сделано.
var listeners = {};
// Вызывается из клиента-подписчика, добавляет коллбек в массив коллбеков
// при появлении сообщения в канале оно посылается по TCP соединению
// подписчику
function subscribeForMessages(clientSocket, channelId) {
listeners[channelId].push(clientSocket);
}
// Вызывается из клиента-издателя, отправляет сообщение всем
// подписчикам канала
function publishMessage(channelId, message) {
var clients = sockets[channelId] || [];
// делаем копию для защиты от добавления/удаления
// сокетов в массив в процессе расылки
clients = clients.slice();
clients.forEach(function (socket) {
// В теории, если клиент не принимает сообщения, то они
// буферизуются и буфер будет расти неограниченно. Защита
// от этого не предусмотрена.
socket.write(message);
});
}
// Поднимаем TCP-сервер. Когда приходит соединение, мы читаем из
// него команду в текстовом виде. Подписчик посылает команду вида
// subscribe 1234, а издатель - publish 1234 message
//
// Если подписчик подписался на канал, ему в сокет приходят сообщения
// от издателей как строки текста. Отписка не предусмотрена.
//
// Нагло стырено с https://gist.github.com/creationix/707146
var net = require('net');
var serverSocket = net.createServer(function (socket) {
var lineReader = carrier.carry(socket);
lineReader.on('line', function(line) {
// Пришла строка от клиента
var [_, command, channelId, _, message] = /^(\w+)\s+(\d+)(\s+\S.*)?$/.exec(line);
if (command == 'subscribe') {
subscribeForMessages(socket, channelId);
} elseif (command == 'publish') {
publishMessage(channelId, message);
} else {
console.log("Invalid command: " + line);
}
});
socket.on('error', ...);
socket.on('close', ...);
});
serverSocket.on('error', ...);
serverSocket.listen(9001, '0.0.0.0');
Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.
Тут разбирается реализация брокера в redis: https://making.pusher.com/redis-pubsub-under-the-hood/
> // дойдет ли выполнение досюда до окончания таймера?
Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.
> Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?
Да, если серверов несколько, то нужно "балансировать" клиентов между ними. Это можно сделать несколькими способами:
- на клиенте: клиент имеет массив адресов серверов и выбирает один случайно для соединения
- на сервере: делаем прокси, к которому подсоединяется клиент и который далее проксирует его соединение к одному из серверов
> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".
> Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?
Можно.
Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?
Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).
То есть у тебя не может быть больше одного подписчика. Это не pubsub.
Как я понимаю, реализовать pubsub напрямую на примитивах ОС для IPC (вроде сокетов, которые ты пытался использовать) сложно, если вообще возможно. Проще реализовать его внутри процесса-брокера, и сделать возможность присоединения клиентов-издателей и подписчиков к нему. Я где-то в предыдущем посте предлагал даже пример кода на семафорах: https://phpclub.tech/pr/res/1174695.html#1190031 - сейчас я подумал, что в асинхронном однопоточном коде (Node.JS или ReactPHP) семафоры ведь и не нужны (так как один поток), можно без них это сделать, примерно так (пример сильно упрощен, убрана обработка сетевых ошибок, отсоединений клиентов итд):
// Мы делаем словарь, в котором индексом является номер канала,
// а содержимым - массив сокетов, заинтересованных в сообщениях.
// При поступлении сообщения в канал оно ретранслируется всем
// ждущим его сокетам клиентов.
//
// channelId: [ socket1, socket2, socket3 ]
//
// При отсоединении клиента надо удалять его сокет, это не сделано.
var listeners = {};
// Вызывается из клиента-подписчика, добавляет коллбек в массив коллбеков
// при появлении сообщения в канале оно посылается по TCP соединению
// подписчику
function subscribeForMessages(clientSocket, channelId) {
listeners[channelId].push(clientSocket);
}
// Вызывается из клиента-издателя, отправляет сообщение всем
// подписчикам канала
function publishMessage(channelId, message) {
var clients = sockets[channelId] || [];
// делаем копию для защиты от добавления/удаления
// сокетов в массив в процессе расылки
clients = clients.slice();
clients.forEach(function (socket) {
// В теории, если клиент не принимает сообщения, то они
// буферизуются и буфер будет расти неограниченно. Защита
// от этого не предусмотрена.
socket.write(message);
});
}
// Поднимаем TCP-сервер. Когда приходит соединение, мы читаем из
// него команду в текстовом виде. Подписчик посылает команду вида
// subscribe 1234, а издатель - publish 1234 message
//
// Если подписчик подписался на канал, ему в сокет приходят сообщения
// от издателей как строки текста. Отписка не предусмотрена.
//
// Нагло стырено с https://gist.github.com/creationix/707146
var net = require('net');
var serverSocket = net.createServer(function (socket) {
var lineReader = carrier.carry(socket);
lineReader.on('line', function(line) {
// Пришла строка от клиента
var [_, command, channelId, _, message] = /^(\w+)\s+(\d+)(\s+\S.*)?$/.exec(line);
if (command == 'subscribe') {
subscribeForMessages(socket, channelId);
} elseif (command == 'publish') {
publishMessage(channelId, message);
} else {
console.log("Invalid command: " + line);
}
});
socket.on('error', ...);
socket.on('close', ...);
});
serverSocket.on('error', ...);
serverSocket.listen(9001, '0.0.0.0');
Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.
Тут разбирается реализация брокера в redis: https://making.pusher.com/redis-pubsub-under-the-hood/
> // дойдет ли выполнение досюда до окончания таймера?
Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.
> Т.е. для нескольких серверов, сначала нужно иметь роутер(?), который будет распределять соединения, как это делается при шардинге БД?
Да, если серверов несколько, то нужно "балансировать" клиентов между ними. Это можно сделать несколькими способами:
- на клиенте: клиент имеет массив адресов серверов и выбирает один случайно для соединения
- на сервере: делаем прокси, к которому подсоединяется клиент и который далее проксирует его соединение к одному из серверов
> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".
> Такой брокер уже написан кем-то другим и указан на странице ReactPHP - https://github.com/voryx/Thruway . Только документация совсем не написана. Может попробовать придумать что-нибудь на нём?
Можно.
Тут админка не особо нужна.
>>199113
>>199123
Не, это плохое решение. ЛУчше сделать так:
- пишем выражение вида "ровно одна цифра, за ней любое число минусов, скобок, пробелов"
- обрачиваем это выражение в (...){10}
>>199174
> $fixText
> $fixPunctuation
> $addDot =
тут лучше использовать одну и ту же переменную ($sentence), а не заводить новую на каждом шаге
> $wordsSeparation
лучше просто words
> $createNewSentence
newSentence
А так, верно.
> \\s{2,99}
Лучше \\s{2,} - так нельзя?
> $reg
Непонятное название.
А так, решено верно.
>>199216
lesson - это занятие в конкретный день или в общем название предмета? Наверно еще стоит сделать таблицу предметов.
Выглядит верно.
Тут админка не особо нужна.
>>199113
>>199123
Не, это плохое решение. ЛУчше сделать так:
- пишем выражение вида "ровно одна цифра, за ней любое число минусов, скобок, пробелов"
- обрачиваем это выражение в (...){10}
>>199174
> $fixText
> $fixPunctuation
> $addDot =
тут лучше использовать одну и ту же переменную ($sentence), а не заводить новую на каждом шаге
> $wordsSeparation
лучше просто words
> $createNewSentence
newSentence
А так, верно.
> \\s{2,99}
Лучше \\s{2,} - так нельзя?
> $reg
Непонятное название.
А так, решено верно.
>>199216
lesson - это занятие в конкретный день или в общем название предмета? Наверно еще стоит сделать таблицу предметов.
Выглядит верно.
Ну по идее преподаватель может вести разные предметы же. И у разных групп.
Вообще, эту задачу пока никто не сделал. Если вдруг ты ее сделаешь, и исправишь все мои замечания, думаю, это можно будет смело на любом собеседовании показывать.
>>199332
Вообще, немного странная идея. По идее модель соответствует одной сущности и не может быть 2 модели для одной и той же сущности. Точнее, не очень понятно, в чем смысл.
Если я кого-то пропустил, то, пожалуйста, напомните о себе в новом треде.
Благодарю!
>тут лучше использовать одну и ту же переменную ($sentence), а не заводить новую на каждом шаге
Да, тоже хотел, но где-то читал жопой и не узнал, как их объединить. Подозреваю, что это самые основы, но если можно, то ткните, пожалуйста.
>Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?
Я смотрел некоторые клиенты предлагаемые самим редисом (https://redis.io/clients#php) и предлагаемый ReactPHP (https://github.com/nrk/predis-async). Все они плохо задокументированы. Я разрываюсь между изучением их кода и написанием своего собственного брокера.
Изучу и то и то, так буду точно уверен что из этого выбрать.
Библиотеки на которые я обращу внимание:
https://github.com/clue/php-redis-react
https://github.com/shumkov/rediska //может быть не асинхронная
https://github.com/nrk/predis-async // заброшена с 2016
https://github.com/voryx/Thruway
>Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).
Да, я предполагал, что поторопившись с ответом я могу допустить ошибки, но очень уж хотелось побыстрей вам ответить.
Больше не буду торопиться и буду больше думать над кодом.
>Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.
>В качестве клиента используется telnet или netcat.
Это в рамках примера или действительно лучше использовать консольные приложения? В рабочем приложении я бы написал свой клиент.
>> // дойдет ли выполнение досюда до окончания таймера?
>Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.
Я имел ввиду, что при задавании таймера сначала выполнится код под ним, а потом, по истечению самого таймера, его код. То есть:
setTimeout($t = 3, function() {echo "TIMEOUT"});
echo "AFTER TIMEOUT";
Выдаст результат сначала "AFTER TIMOUT", а потом уже "TIMEOUT".
Т.е. если мы вызовем return после таймаута, то он вернет что-то до его истечения?
>> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
>Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".
Я плохо знаю низкоуровневые устройства сети, но разве сервер слушает все адреса в интернете?
https://reactphp.org/socket/#server
>As above, the $uri parameter can consist of only a port, in which case the server will default to listening on the localhost address 127.0.0.1, which means it will not be reachable from outside of this system.
>which means it will not be reachable from outside of this system.
То есть, если я задам, например, $uri = "google.com:8080", то сервер будет до бесконечности слушать этот адрес, пока google сам не создаст соединение по адресу сервера (что никогда не произойдет). В чем смысл слушать адрес, а не только свой порт, если от клиента зависит создастся ли соединение или нет?
Или это нужно чтобы соединение устанавливалось только когда указанные адреса в сервере и клиенте совпадают? Если подумать, то это имеет смысл, чтобы злоумышленник не создал свое соединение, и не стал присылать свои уведомления... Всё равно ничего не понятно.
Спасибо
>Я погуглил "redis reactphp" и вижу, что есть асинхронные клиенты для редиса. Ты их не смотрел?
Я смотрел некоторые клиенты предлагаемые самим редисом (https://redis.io/clients#php) и предлагаемый ReactPHP (https://github.com/nrk/predis-async). Все они плохо задокументированы. Я разрываюсь между изучением их кода и написанием своего собственного брокера.
Изучу и то и то, так буду точно уверен что из этого выбрать.
Библиотеки на которые я обращу внимание:
https://github.com/clue/php-redis-react
https://github.com/shumkov/rediska //может быть не асинхронная
https://github.com/nrk/predis-async // заброшена с 2016
https://github.com/voryx/Thruway
>Ты, как я вижу, попытался написать свою реализауию pubsub, но она явно не работающая. Ты в subscribe.php открываешь порт на прослушивание на 30 секунд. Но порт в один момент времени может слушать только один процесс (есть возможность открыть порт нескольким процессам, если указать опцию расшаривания порта в linux, но тогда соединение получит один случайно выбранный из этих процессов, а не все).
Да, я предполагал, что поторопившись с ответом я могу допустить ошибки, но очень уж хотелось побыстрей вам ответить.
Больше не буду торопиться и буду больше думать над кодом.
>Если ты так хочешь написать свой pubSub брокер, можешь переписать этот пример на PHP и допилить до работающего состояния. В качестве клиента используется telnet или netcat.
>В качестве клиента используется telnet или netcat.
Это в рамках примера или действительно лучше использовать консольные приложения? В рабочем приложении я бы написал свой клиент.
>> // дойдет ли выполнение досюда до окончания таймера?
>Не знаю, вроде как event loop должен завершиться при закрытии всех сокетов и истечению всех таймаутов. Если нет, то можно (надежнее так) в таймауте явно вызвать команду завершения event loop.
Я имел ввиду, что при задавании таймера сначала выполнится код под ним, а потом, по истечению самого таймера, его код. То есть:
setTimeout($t = 3, function() {echo "TIMEOUT"});
echo "AFTER TIMEOUT";
Выдаст результат сначала "AFTER TIMOUT", а потом уже "TIMEOUT".
Т.е. если мы вызовем return после таймаута, то он вернет что-то до его истечения?
>> И зачем при соединении указывать адрес? Неужели не достаточно того что сервер уже слушает его?
>Так сервер это один процесс, а клиент - другой. Как клиент узнает, к кому подсоединяться? Твой вопрос звучит как "а зачем в браузере вводить адрес сайта в адресной строке, разве не достаточно что где-то есть сервер и он слушает порт".
Я плохо знаю низкоуровневые устройства сети, но разве сервер слушает все адреса в интернете?
https://reactphp.org/socket/#server
>As above, the $uri parameter can consist of only a port, in which case the server will default to listening on the localhost address 127.0.0.1, which means it will not be reachable from outside of this system.
>which means it will not be reachable from outside of this system.
То есть, если я задам, например, $uri = "google.com:8080", то сервер будет до бесконечности слушать этот адрес, пока google сам не создаст соединение по адресу сервера (что никогда не произойдет). В чем смысл слушать адрес, а не только свой порт, если от клиента зависит создастся ли соединение или нет?
Или это нужно чтобы соединение устанавливалось только когда указанные адреса в сервере и клиенте совпадают? Если подумать, то это имеет смысл, чтобы злоумышленник не создал свое соединение, и не стал присылать свои уведомления... Всё равно ничего не понятно.
Спасибо
Оппушка и все.
Насколько сильно нужно знать алгоритмы и матеш на самом деле? Нужно ли быть одаренным гением или хватает базовых знаний?
И как на данный момент обстоят дела с вакансиями PHP программиста. На superjob нашел только 4 вакансии, которые не требовали опыт работы. Все, лавочка закрыта?
Блять, если больше, то возвращает 0.
Поддерживаю реквест
Это копия, сохраненная 5 июля 2018 года.
Скачать тред: только с превью, с превью и прикрепленными файлами.
Второй вариант может долго скачиваться. Файлы будут только в живых или недавно утонувших тредах. Подробнее
Если вам полезен архив М.Двача, пожертвуйте на оплату сервера.